← Home

Designing for Multiplayer Before You Need It

@date=2026-01-09
@tags=gamedev, jewel-defender

gamers-fist-bump-after-winning-game.jpg

Very often when indies are making a game, they aim to focus on local multiplayer first and punt on making an online multiplayer version of it. This is reasonable—you want to get the game out the door, and networking is hard.

The problem is that local-only architecture quietly bakes in assumptions that are hostile to networking: shared state, immediate input, tight coupling between gameplay logic and rendering, and logic running “wherever it’s convenient.” Once those assumptions exist, adding a server later is not an additive change—it’s a rewrite.

One way to bridge this gap is to wire the game as if it were multiplayer without actually implementing online multiplayer yet, and let that shape your code. For some concrete context, I’m doing this right now in Jewel Defender.

Treat the Game as Client–Server From Day One

Even though Jewel Defender is currently a local multiplayer game, I treat it as if it is always client–server.

The server code lives inside the app. When a local game starts, the app spins up a server instance and creates a room. Clients then “connect” to it, even though everything is running in the same process.

The important part is not where the server runs, but that it exists as a concept and owns the world.

A Fake Network Is Still a Network

I have a network receiver layer that can receive input from two sources:

Both use the same function signatures and message formats.

The local network is not a real network. It is just a function call that delivers the same messages that would normally be sent over the wire—player controls, join/leave events, etc. The remote network will eventually send those same messages across an actual connection.

From the caller’s perspective, there is no special case. You send input through “the network,” and something on the other side processes it.

Physics and AI Live After the Network Boundary

All physics, AI, and game rules run only after input passes through the network interface.

The server processes the input and produces world updates. Those updates are then fed back to client-side code exactly as if they came from a remote server.

This enforces a hard boundary:

Even locally, nothing is allowed to “just reach in” and mutate game state.

Minimal Networking, Intentional Constraints

Right now I am deliberately not solving advanced multiplayer problems:

That is all fine. Those are hard problems and orthogonal to basic architecture.

What this approach buys me is that when I do add online multiplayer, the work will be strictly about networking concerns—latency, reliability, reconciliation—not about untangling gameplay logic that was never designed to cross a network boundary.

That is the real win.