RAFlash brings RetroAchievements-style achievements to Adobe Flash games.
RetroAchievements.org retrofits achievements onto retro games by watching memory addresses. This doesn't work for Flash because dynamic memory allocation means addresses aren't stable between sessions.
For ActionScript 2 games, most game state is attached to the "stage" object hierarchy. Instead of reading raw memory, RAFlash uses a DSL to traverse the stage:
stage.player.health == 0
stage.level.coins >= 10
Building a new Flash Player is impractical, so RAFlash uses the official Adobe Flash Player with a custom "firmware" SWF that loads games inside itself, allowing it to introspect game state.
Flash's sandbox prevents SWFs from making HTTP requests, but sockets are allowed. RAFlash uses a Deno program as a bridge to the outside world.
RAEngine (Deno)
↕ socket
Adobe Flash Player → AVM2Firmware.swf (Haxe/AS3)
↓ embeds
AVM1Firmware.swf (MTASC/AS2)
↓ loads
game.swf
The AVM1/AVM2 split exists because only AS2 code can properly introspect AS2 game stages, but AS3 is needed for socket communication.
- RAEngine (
RAEngine/): Deno/TypeScript. Core logic, socket server, condition evaluation, launches Flash Player and UI windows. - AVM2Firmware (
AVM2Firmware/): Haxe → SWF. Outer shell with socket client, directory browser UI, loads games. - AVM1Firmware (
AVM1Firmware/): AS2 via MTASC. Wrapper for AS2 games, introspects stage, reports values to AVM2Firmware. - RADisplay (
RADisplay/): Vue/Vite. Achievement editor UI, opened via F12 in Flash Player.
- Run
make runto build and launch - RAEngine starts Flash Player with AVM2Firmware
- Select a game from the directory listing
- AS2 games load inside AVM1Firmware wrapper
- Press F12 → opens RADisplay for editing achievements
- Define achievements as logical conditions using the stage traversal DSL
- RAEngine evaluates conditions against live game state
- When all conditions are true → achievement unlocks
make # Build all components
make run # Build and run
make clean # Clean build artifacts- Negative IDs are used for local/unpromoted entities (intentional, not a bug)
- The formula Lexer/Parser in RAEngine handles the stage traversal DSL