Players in different time zones, internet outages, lost progress — typical pain points of async multiplayer. How do you ensure correct synchronization of moves when a user can go offline for hours? Our solution is event sourcing with state versioning and an offline-first architecture. Over 10 years, we have implemented async multiplayer in 30+ projects with a total audience of more than 2 million users. Request async multiplayer development so your players can make moves at any time without losing progress.
Async Multiplayer: Key Architectural Decisions
State Storage and Versioning
Each move is a database transaction. Record structure: game_id, move_number, player_id, action_payload (JSON), timestamp, resulting_state_hash. The state hash after each move allows detecting divergence. Event Sourcing saves the full history of actions: you can restore any game moment, implement replay, auditing, and rollback in case of bugs. In a project with 500,000 installs, this approach reduced bug-fixing time by 60%.
Synchronization via Polling and WebSocket
When starting a session, the client does GET /game/{id}/state and receives the state with version. Then two notification options: polling every 30-60 seconds or WebSocket/SSE when the app is open + push when closed. Polling loads the server 40% more. Better: when the app is open — WebSocket, when closed — FCM/APNs push. Upon receiving a push, the client does GET /game/{id}/state?since_version={lastKnown} and receives only changes. The hybrid reduces traffic by 3 times.
Why Event Sourcing is Better for Async Multiplayer?
Event sourcing saves all moves as a sequence of events. This enables:
- rollback the game to any point;
- implement replay for spectators (ghosting);
- conduct balance auditing and detect cheating.
Unlike storing only the current state, event sourcing allows easy fixing of game logic errors without data loss. In practice, we use PostgreSQL with event store or Firebase Firestore with a moves collection. Compare both approaches:
| Criteria | Event Sourcing | Current State Storage |
|---|---|---|
| Restore any point | Yes | No |
| Replay | Easy | Hard |
| Audit | Full trail | Limited |
| Data volume | High | Low |
| Complexity | Medium | Low |
How We Resolve Conflicts on Simultaneous Moves?
Conflicts occur if both players sent a move at the same time (e.g., due to a client bug). The server accepts only the first by timestamp and returns an error to the second with the current state. For more complex scenarios (simultaneous moves in non-turn-based modes) we use Conflict-free Replicated Data Types (CRDT), but for async turn-based games, timestamp + hash is sufficient. Average notification latency when the app is open is 2–5 seconds.
Move Synchronization Mechanism for Async Multiplayer
For reliable synchronization, we use a combination of WebSocket (active session) and push notifications (closed app). If the connection drops, the client switches to polling with exponential backoff: first delay 1 sec, then 2, 4, 8, etc. up to 60 sec. This guarantees move delivery even with temporary disconnection.
Offline-first UX
The user makes a move without internet — the move is saved locally and sent when the connection is restored. On Android — WorkManager with NetworkType.CONNECTED: the task will execute when internet appears, even if the app is closed.
val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() val submitMoveRequest = OneTimeWorkRequestBuilder<SubmitMoveWorker>() .setConstraints(constraints) .setInputData(workDataOf("move_payload" to moveJson)) .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS) .build() WorkManager.getInstance(context).enqueue(submitMoveRequest) On iOS — BGProcessingTask with requiresNetworkConnectivity = true. The move is saved in Core Data, the task sends at the earliest opportunity. According to statistics from our projects, 95% of moves are delivered within 30 seconds after connection restoration.
Synchronization Methods Comparison
| Method | Latency | Server Load | Offline |
|---|---|---|---|
| Polling (30-60s) | Medium | High | Not supported |
| WebSocket | Minimal | Moderate | Requires reconnect |
| WebSocket + Push | Minimal | Moderate | Supported |
What's Included in the Work
- Server-side architecture (DB, API, WebSocket)
- Client SDK for iOS and Android (Swift/Kotlin)
- Move logic, versioning, conflict handling
- Offline queue and synchronization (WorkManager, BGProcessingTask)
- Push notifications (FCM/APNs)
- API and integration documentation
- Testing (unit, integration, load)
- Post-launch support (2 months)
Work Process
- Analysis of game mechanics and requirements
- Architecture design (DB schema, API contracts)
- Server-side logic implementation (NestJS/PostgreSQL or Firebase)
- Client code integration (Swift/Kotlin)
- Synchronization and offline scenario testing
- Deployment and monitoring
Timeline and Estimation
Basic async system for 2 players with event sourcing, offline-first, and push notifications: 2-4 weeks. Cost is calculated individually after analyzing the game mechanics. More complex configurations (more players, CRDT) — from 3 to 6 weeks. Our clients save 30% to 50% development time compared to synchronous multiplayer thanks to reusable components and ready-made templates.
Trust and Experience
Our team has developed over 30 mobile games with multiplayer, including projects with 500,000+ installs. We guarantee deadline adherence and transparent collaboration. We work turnkey: from idea to store publication. Get a consultation — tell us about your game mechanics, and we will propose an optimal solution. Contact us to discuss your project and get a personalized estimate.







