Building an MMO Backend: 7 Years, 10 Iterations, One Seamless World
Technical deep dive into Photon’s MMO concept car
For 7 years we have been quietly building an MMO backend at Photon. Time to talk about it. It is our “concept car”: not a product, not an SDK, but real tech running today.
Two eras of MMO architecture
MMO backends went through two big architectural eras.
Old style (2000s). You pick a region, then a server or realm, usually lore-named (WoW: Stormscale, Cairne, Blackrock). Your character is bound to that realm, and a “server” is literal: one machine or cluster, one world, one community. Gameplay is a shared overworld plus instanced dungeons per group. Not universal (Star Wars: Galaxies never instanced, Asheron’s Call took its own routes), but the common pattern.
Key references: World of Warcraft, Star Wars: Galaxies, Asheron’s Call 1 & 2, Lineage 1 & 2, Guild Wars 1, Warhammer Online: Age of Reckoning, Dark Age of Camelot.
New style (2010+). You still pick a region, but at login you are dynamically zoned into a copy of the zone your character is in, shared with other players from the same region, with several clones of each zone running at once. Moving through the world transports you between instances. Groups, quests and raids are adapted for this more fluid model.
Key references: World of Warcraft (later versions), Final Fantasy XIV, Elder Scrolls Online, Guild Wars 2, New World.
Both models still make sense. What no longer makes sense is the code underneath them. The original backends were built for the hardware, network stacks and operational practices of their time. They do not survive contact with modern cloud infrastructure, and they cannot be meaningfully reused. Any serious MMO today needs a modern backend, whichever world model it runs.
So we built one that supports both. Realm layout or fluid overworld is a design decision on top of our architecture, not an architecture decision. Expose a fixed set of nodes as a classic realm, or run a fluid overworld: both work on the same stack.
The core idea: a seamless world across machines
The simulation is spread across as many nodes as the game needs. Nodes spin up and shut down at runtime to follow load or to grow the world. Transitions between nodes are invisible to the player. Cross-node interaction is part of the core design, not an edge case bolted on later.
In our demo, a space game, two star systems run on two physical machines. You can fly from one to the other in a straight line. Nothing happens on screen. No loading screen, no portal, no hint that you just changed servers. You can fight across the boundary. Server-side physics collisions work across the boundary. The client never learns that multiple machines exist.
This is the hard part of MMO backends, and it is the part we demonstrate live rather than claim.
In the demo video we are flying from one server to another, with a completely seamless transition – we can see the target server and all npcs/actions on it as we are flying towards it from the start, when we get closer the atmosphere switches and then the actual server transfers (can see at the top Zone: guid and the top-right zone info), we then instantly jump back to our origin server using the gate instead.
Server-authoritative, with prediction
Clients send input, the server decides outcomes. For a persistent world at this scale, that is the model that holds up: one authority no client can argue with. Client-side prediction hides the latency, developed under a simulated 100 ms ping from day one.
The cost of server authority is latency, so the client predicts its own ship locally and corrects when the authoritative result arrives. Remote objects arrive as snapshots and are interpolated. A shared clock keeps the gateway and all simulation nodes on one time base, which keeps prediction and authoritative simulation aligned.
We develop under a simulated 100 ms ping. Not LAN conditions, not localhost. If it feels right at 100 ms, it feels right for real players.
Replication: only what is near you, only what changed
Each player connects to a gateway that owns the connection for the whole session. The gateway does area-of-interest management per player and sends state updates 30 times per second: only objects relevant to that player, and only the fields that changed since the last update.
Game traffic runs on UDP with a custom protocol, split into a reliable channel for messages that must arrive and an unreliable channel for the position stream.
Simulation: everything is an entity
Each node runs a 30 Hz entity-component simulation. Ships, asteroids, gates, loot: all data, processed by small systems each tick. NPCs and players are the same kind of object to the simulation, so AI factions run on exactly the same machinery as human players.
World coordinates are 64-bit doubles. That is a deliberate large-world decision: planet-scale distances live in one continuous coordinate space, without floating-origin tricks.
Persistence without touching the database
Simulation nodes never write to the database directly. A central persistence service keeps live entities in an in-memory cache, serves them on request, and flushes changes to durable storage on a schedule. The database behind it is never exposed to the rest of the system, so it stays replaceable.
The language question: 10 iterations to Go
We tried this in more languages than we like to admit. Roughly ten iterations over the years, and the server language question was settled by elimination:
C and C++ gave us control and paid for it with slow iteration and error-prone code. Rust was too hard to train people on and, at the time, not mature enough around networking and async. C# looked comfortable until the .NET garbage collector met sustained simulation load: unreliably long pauses, exactly where you cannot afford them.
Go won on the combination: fast iteration times, a very good async and task model, easy to train engineers on, and a garbage collector that is genuinely fast under our load profile.
The client side is a C++ library, because C++ is what links into every game engine. Server and client implement the same wire format by hand, once in Go, once in C++. That is a real maintenance cost, and we pay it deliberately, for control and performance in the hot path.
Our demo client runs in Godot. The same library links into Unreal, Unity, or anything else.
Simulation highlights:
- Full Go server (very quick iteration times)
- Ark ECS (MIT)
- Custom Physics engine (Boxes/Spheres/Convex Hulls)
Server highlights:
- Custom built high throughput internal message relay for cross node communication
- Guaranteed ordering and delivery of all messages (ensures no player actions or entity <> entity messages are lost)
- Clients only sees a single consistent world, is unaware of distributed simulation
What this is and is not
This is a concept car: real tech, running today, built to show what is possible. It is not a product and not an SDK.
We believe an MMO backend only becomes a product after a real game has shipped on it. The gaps that matter, live operations, migrations, failure recovery under real player load, only surface in production. So instead of launching a box of parts, we want to build one great game with one great team, and harden the stack where it counts.
If you are a tech director, lead engineer or studio head and want to go deeper: talk to me.
Questions from the thread
The response brought some genuinely good questions, so I picked up a few of the more interesting ones here. These may well drive further deep dives down the line. And to be clear: this is our approach, not the only one. As mentioned earlier, we went through many iterations and changes to arrive at it.
Is client-side prediction not fragile to changes in game mechanics over time?
Not the way it is built, because the client and server do not run separate implementations of the mechanics:
- Shared model. In the demo the flight model is written in C and invoked independently from both the client (C++) and the server (Go).
- Local responsiveness. The client gets instant local feedback and control, and has access to the static objects, convex hulls and other moving entities in the world.
- Server has the final say. The server is always authoritative.
The reason it stays robust to mechanical changes is that the flight model is one shared implementation, not two parallel ones. Change it once and both sides move together, so there is no client-versus-server drift to maintain.
Go makes this straightforward: you can embed C directly and link it, so the flight model lives in one small file, around 100 lines, that both sides call into.
A minimal version of the interop looks like this. The C header (hello.h):
#include <stdio.h>
void hello() {
printf("Hello from C in another file");
}
And calling it from Go:
package main
// #include "hello.h"
import "C"
func main() {
// let's call it
C.hello()
}
This is also part of why the server is in Go. Interop with C is trivial, and in gamedev you always end up linking some native library eventually, so a server language that calls C without friction removes a whole class of glue work. The shared flight model is the direct payoff.
How is combat resolved across a node boundary, when the two entities are authoritative on different nodes?
The mechanism has three parts:
- Overlap region. Neighbouring nodes share an overlap zone, and each node predicts and reconciles the entities it does not own.
- Exactly-once guarantee. A layer on top ensures every operation on an entity is executed exactly once and never lost, including during a seamless transfer.
- Single source of truth. Clients never see the node-predicted state, only the state from the authoritative node.
So the overlap is a server-side continuity mechanism, not something the client can disagree about.
Why use two languages, Go on the server and C++ on the client? Isn’t a split stack a liability?
Less than it looks, for an MMO specifically:
- Small shared surface. An MMO is almost entirely server-authoritative by necessity, cheating makes anything else untenable, so there is little that has to stay in sync between server and client.
- One implementation, not two. The part that is shared, like the flight model, is a single implementation both sides call into rather than two parallel versions.
- What Go buys. Development speed, easy deployment, a concurrency model that fits per-shard simulation, and trivial C interop.
Running this same architecture in C++ on the server would cost far more in time and resources than the language boundary ever does.
Interested in building a large-scale online world or discussing whether this architecture could work for your project? Contact our team to tell us what you are building and talk through the technical requirements. Since this is not an off-the-shelf SDK, the best starting point is a direct conversation. You can also explore the Photon platform and getting-started resources, read the Photon Fusion documentation, or find our community and support options.
















































