The common wisdom is that blockchain games can't feel good. Block times are too slow, gas is too expensive, and the UX of signing every move would kill any game. Xplode proves this wrong — every move is on-chain, and players don't notice.
Here's how.
The latency problem
A real-time game needs to feel responsive. "Responsive" in UX research means under 100ms from input to feedback — above that, users perceive lag. For a competitive game, the real target is 50ms.
Standard Solana has 400ms block times. Even with optimistic UI updates (showing the result before confirmation), the game state can diverge from chain state for nearly half a second. For a minesweeper game where you're clicking cells, that's just about tolerable. For anything more competitive, it's broken.
Ephemeral rollups
MagicBlock's ephemeral rollups run a separate validator network specifically for gaming state. The key properties:
- 1ms block times — fast enough that on-chain confirmation feels instantaneous
- Full Solana compatibility — same programs, same account model, same signatures
- Automatic settlement — the ephemeral rollup state settles back to Solana mainnet periodically and on game end
The game state lives on the ephemeral rollup during a session. When the game ends, the final state (who won, how much they won) settles to mainnet, where the payout happens from the escrow.
The Xplode architecture
The critical path is: player clicks → WebSocket message → game server → move validation → on-chain record → confirmation → all clients update.
We budget this as:
- WebSocket round trip: ~10ms (LAN), ~30ms (cross-region)
- Game server move validation: under 1ms
- Ephemeral rollup confirmation: ~5ms
- Server → all clients broadcast: ~2ms
Total: ~18ms on LAN, ~40ms cross-region. Inside the 50ms budget.
The game server is Rust (tokio). Move validation is synchronous and allocation-free on the hot path — we pre-allocate game state and validate against it in place. The only async work is the on-chain submission and the broadcast.
What's actually hard
The happy path is easy. The hard part is what happens when the ephemeral rollup lags, when a player's connection drops mid-game, or when both players claim the game ended differently.
We handle this with a state machine that has explicit timeout transitions. If on-chain confirmation doesn't arrive within 200ms, the game server treats the move as unconfirmed and retries. If retries fail, the game is paused and both players are notified. Funds stay in escrow until the chain agrees.
Provably fair randomness is the other hard problem. Mine placement has to be random (so neither player can predict the grid) but verifiable (so neither player can claim the server cheated). The solution is a commit-reveal: the server commits to a hash of the seed at game start, places mines using the seed, and reveals the seed at game end. Players can verify the commitment on-chain.
The subtle issue: the server knows the mine positions throughout the game, which creates an information asymmetry. For Xplode (minesweeper), this doesn't matter — the server has no incentive to cheat individual moves. For games with real player strategy, you'd want VRF-based randomness instead.
What I'd do differently
The game server and wallet server are separate processes communicating over HTTP. This made development easier (separate deployment, independent scaling) but adds a round trip on every bet placement and payout. A single process with async task separation would be faster and simpler.
The Redis schema also grew organically and has some duplication. A second pass would consolidate session state into fewer keys with atomic operations instead of the current multi-key updates.