DEV Community

Cover image for How I Built a Lightweight Turn-Based Relay Server with Java 25 Virtual Threads And Reconnect Handling
Nenad Nikolić
Nenad Nikolić

Posted on • Originally published at turnkit.dev

How I Built a Lightweight Turn-Based Relay Server with Java 25 Virtual Threads And Reconnect Handling

Turn-based multiplayer is deceptively hard. Here's how I designed a simple, fast, and cost-effective relay backend that auto reconnects dropped clients and keeps egress + resource usage low.

Building reliable turn-based multiplayer is painful. Enforcing turns, hiding hands/decks, handling votes, and especially managing disconnects and crashes can eat weeks of dev time.
I wanted something lean that "just works" for Unity and other clients without massive infrastructure costs. So I built TurnKit Relay around a few core principles:

Forward-only design (no complex state rollback)
Java 25 virtual threads for massive concurrency with low overhead
Move-based delta reconnects instead of full resyncs
Aggressive egress and memory optimization

The result? A relay that handles temporary drops and full game crashes gracefully while staying extremely cheap to run at scale.

Most real-time servers struggle with reconnects because they either

  • Resend the entire game state (high bandwidth)
  • Or require complex session management

I chose a simpler path: everything is forward-only. The server only replays missed moves since the client's last acknowledged move number.

Core Technical Choices

  • Java 25 Virtual Threads
    Virtual threads made it possible to handle thousands of concurrent matches with near native thread per connection simplicity, without the overhead of traditional thread pools or reactive complexity. This keeps the code readable while scaling horizontally very cheaply.

  • Forward Only + Move-Based Deltas
    On reconnect, the client sends RECONNECT { lastMoveNumber }. The server replays only the missed OnMoveMade events. No full state snapshots, no diffing engines — just sequential moves. This dramatically cuts egress costs and simplifies the backend.

  • Other Optimizations for Low Egress & High Density
    Server managed lists with data masking (hands/decks stay hidden server side). Minimal JSON payloads. Early termination of stale matches.

These choices let a single modest instance support many more simultaneous matches than a typical WebSocket heavy setup.

If you're building a turn-based game and tired of wrestling with matchmaking, turns, hidden state, and reconnect logic, TurnKit handles the hard parts out of the box.

The biggest win wasn't just the performance, it was how much simpler the client code became. Developers only need to handle a single callback and save two small values for crash recovery if app crashed, if connection dropped it automatically reconnects and resyncs.

For full client implementation details (Unity + generic clients), check the official docs: https://turnkit.dev/docs/client-reconnection

I'm actively improving TurnKit and feedback is always welcome.

Top comments (0)