The server must tell each client what the world looks like — efficiently. Learn snapshots, delta compression against what the client already has, and interest management so you only send what matters.
The simplest replication is the snapshot: each tick (or send tick) the server captures the relevant world state — every entity’s position, health, and so on — and sends it to clients, who replace their view with it. Snapshots are robust to packet loss because each is complete: a dropped one is simply superseded by the next. The downside is size, which the next techniques address.
def make_snapshot(world, tick):
return {
"tick": tick,
"entities": {
e.id: {"x": e.x, "y": e.y, "hp": e.hp, "state": e.anim}
for e in world.entities
},
}
# each send tick: server broadcasts a snapshot; clients REPLACE their world view.
# robust to loss: a dropped snapshot is just superseded by the next one
# (no resend needed) -> this is why snapshots pair perfectly with UDP.
# problem: sending EVERY entity EVERY tick is a lot of bandwidth (next: deltas).Most of the world does not change between snapshots, so sending the full state every tick wastes bandwidth. Delta compression sends only what changed since a snapshot the client has already acknowledged: the server diffs the current state against that baseline and transmits the difference. This dramatically shrinks packets — often by an order of magnitude — and is the standard way authoritative servers replicate state affordably.
def make_delta(current, baseline):
# send ONLY fields that changed since the client's acknowledged baseline
delta = {"base_tick": baseline["tick"], "tick": current["tick"], "changed": {}}
for eid, ent in current["entities"].items():
old = baseline["entities"].get(eid)
if old != ent:
# (real engines diff per-FIELD, not per-entity, for max savings)
delta["changed"][eid] = ent
# removed entities:
delta["removed"] = [eid for eid in baseline["entities"] if eid not in current["entities"]]
return delta
# the server keeps the last snapshot each client ACKed as the baseline, and
# sends diffs against it -> often ~10x smaller than full snapshots.In a large world a client does not need to know about entities on the other side of the map — they are not visible and cannot affect the player. Interest management (relevancy / area-of-interest) sends each client only the entities near or relevant to it, using a spatial structure (a grid or the quadtree/BVH from the game physics course) to find them. This is what lets an MMO with thousands of entities send each player a tiny, relevant slice.
def relevant_entities(world, player, radius=50):
# only replicate entities within the player's area of interest
r2 = radius * radius
return [e for e in world.entities
if (e.x - player.x) ** 2 + (e.y - player.y) ** 2 <= r2]
# (production: use a spatial grid / quadtree — Game Physics course — not a full scan)
# each client gets a snapshot of only ITS relevant set:
def snapshot_for(world, player):
return make_snapshot_of(relevant_entities(world, player))
# turns "send all N entities to all N players" (O(N²)) into "send each player
# their nearby slice" -> how MMOs with thousands of entities stay affordable.
# bonus: a client can't see (or cheat with) info about entities it wasn't sent.Replication is a loop: the server sends state, clients acknowledge the latest snapshot they received, and the server uses those acks to pick each client’s delta baseline. Because state is re-sent every tick, a lost packet self-heals — the client is simply corrected by the next snapshot. This ack-driven, self-correcting flow is what keeps every client eventually consistent with the authoritative world despite constant packet loss.
The replication loop (per client):
server: send delta( current_state, baseline = last snapshot THIS client ACKed )
client: apply it -> ACK the tick it just received
server: on ack, advance that client's baseline to the acked snapshot
packet lost? the client's ACK doesn't advance -> next delta is computed
against an OLDER baseline (bigger, but correct). State SELF-HEALS: the next
snapshot supersedes whatever was missed. No per-entity retransmit needed.
Result: EVENTUAL CONSISTENCY — every client converges to the authoritative
world despite continuous packet loss. Robust by design.