Defining state dependencies directly in the routine declarations is an
incredibly powerful approach. You are essentially building a lightweight
Entity Component System (ECS) mixed with a GraphQL-style data fetcher.
Instead of serializing and piping a massive 10MB world state into every minor
routine, the middleware acts as a selective lens.
1. Routine Declarations (State Contracts)
If a routine registers itself with a strict contract, the middleware only
extracts and provides exactly what is needed. This enforces the principle of
least privilege and drastically reduces execution latency.
A registration manifest for a MarketPurchase routine could look like this:
{
"routineId": "process_purchases",
"requires": ["players.inventory", "players.resourcePoints",
"market.prices"],
"mutates": ["players.inventory", "players.resourcePoints"]
}
The middleware proxy would parse this, slice the relevant data out of the
master state, inject it into the sandboxed JS environment, andcruciallyknow
exactly which fields to merge back into the master state when the routine
returns its output. If a buggy routine tries to modify players.health when it
only declared players.inventory, the middleware simply discards the
unauthorized mutation.
2. Disk and Network Drive Serialization
For a turn-based game, relying on disk or network drive serialization (like
NFS, EFS, or a shared volume) is a highly practical choice. It eliminates the
need to manage a separate database cluster, and your game saves literally
become standalone .json or .msgpack files that you can easily back up, version
control, or email to someone.
To make disk persistence bulletproof in a REST environment, you will need to
account for a few mechanics:
* Atomic Writes: Never overwrite the active state file directly. If the
server crashes halfway through writing a 2MB JSON file, your game state is
corrupted. The middleware must write the new state to a temporary file (e.g.,
match_104.tmp), and once the write is complete, perform an atomic rename
operation to replace match_104.json.
* File Locking (Concurrency): Since REST is stateless and concurrent, two
players might submit their turn intents at the exact same millisecond. Your
proxy needs a mutex or file-locking mechanism to ensure it processes Player
A's intent, saves the file, and then loads the new file to process Player B's
intent.
* Event Sourcing (Optional but fun): Instead of saving the entire state every
turn, you could just append the players' turn actions to an append-only log
file (e.g., 1. Castro buys Van, 2. Mao moves to X). To load the game, the
middleware just runs all the logged actions through the initialization
routine. This makes disk writes microscopically small and gives you an
automatic "replay match" feature.
If you go the disk-serialization route, are you planning to have the
middleware wait for all players to submit their intents before generating a
single "turn resolution" file, or will the game react asynchronously as each
player interacts?