Perfectly replicating player movement across a network link is a nontrivial task, especially when it comes to first person shooters and similar games, where one keypress results in direct movement of the player's character. Several goals have to be reached:
- the controlling player should not feel any lag
- players should see smooth movement for everyone else
- the server must keep the authority over all player's positions
- use as little bandwidth as possible
Zoidcom provides a special replicator that aims for these goals: ZCom_Replicate_Movement. This class implements client side prediction, dead reckoning/extrapolation, interpolation, movement correction and local overrides.
This class provides quite a bit of functionality, but it also requires a few things from the application:
- Client side prediction allows players to move their characters without waiting for server acknowledgement, completely hiding lag.
- Dead reckoning/extrapolation estimates other player's positions and movement based on prior network data, thus reducing the need for high frequency updates.
- Interpolation computes intermediate values between two network updates so that other player's movements look smooth.
- Movement correction allows the server to correct a player's position.
- Local overrides allow clients to override the position and velocity of objects temporarily. This is needed so that objects don't get extrapolated through walls.
There are three kind of nodes involved:
- game objects must be able to update their physics state independent from each other
- game objects must be able to process user input without updating the physics state
- Owner is the the node on the player's machine.
- Authority is the node on the server.
- Proxies are the nodes on all other player's machines.
The movement replication is done by ZCom_Replicate_Movement. An instance of this replicator has to be registered with the player character's node. The abstract class ZCom_MoveUpdateListener needs to be implemented and registered with the replicator, so that it can notify the game object about the events inputUpdated(), correctionReceived() and two others.
The following is an order of events that occurs when a player moves. O, A and P stand for owner, authority and proxy respectively. U stands for code executed in the user application, i.e. things the user application has to do, whereas Z stands for tasks done by Zoidcom.
- OU The owner captures user input, for example the state of the up/down/left/right buttons.
- OU The owner encodes the input into a bitstream. The format doesn't matter.
- OU The owner calls ZCom_Replicate_Movement::updateInput() and passes the current position and the input bitstream to it.
- OU The owner does NOT apply the input to the object, yet.
- OZ A subsequent call to ZCom_processOutput() will send the input bitstream and the position to the authority node.
- OZ(*) On the owner's system, ZCom_MoveUpdateListener::inputSent() is called by Zoidcom. This is a callback method and the only parameter is the bitstream that contains the input from above.
- OU The owner unpacks this bitstream and applies the input to the local object (Client side prediction).
- AZ The authority node will receive the input bitstream.
- AZ The authority calls the ZCom_MoveUpdateListener::inputUpdated() callback. The input bitstream is given as parameter here, as well as a timestamp from the owner.
- AU(**) Inside the callback, the authority object's physics state must be advanced by the difference between the last timestamp and the current one.
- AU The replicator is informed about the new physics state with ZCom_Replicate_Movement::updateState()
- AU AFTER the physics state has been advanced, the input bitstream is applied to the object.
- AZ The authority compares the owner's position to it's own. If the difference is too big, a correction is sent to the owner.
- AZ The authority will update the proxies.
- OZ If authority has sent a correction to owner, owner will get the callback ZCom_MoveUpdateListener::correctionReceived() called.
- OU In the callback, owner sets it's physics state to the corrected state.
- OU Then owner reapplies all unacked input bitstreams that have been sent to authority, using ZCom_Replicate_Movement::getNextInputHistoryEntry()
(*) The owner receives a copy of it's own input for processing as soon as Zoidcom has sent the input update to the authority. This ensures that owner and authority stay in sync. Owner is not allowed to use the input before it has been sent to the server.
(**) The authority updates it's physics state in lockstep mode. That means, that the authority object is physically updated ONLY when new input is received from the owner. That is necessary to keep owner and authority in perfect sync.
The fact, that the server object only gets updated when new input is received, makes it necessary to take additional steps if the server object is to be displayed (in a smooth way). I suggest not displaying server objects at all, and instead run client code in addition to the server code if there is a player playing on the server. Ie if a player hosts a game, his machine becomes server and client at the same time. Instead of implementing special cases for that in the code, just instantiate client and server in the same process, and let the client connect to the server through a local socket.
A complete implementation of the above can be found in ex07_movement.
This file is part of the documentation for Zoidcom. Documentation copyright © 2004-2008 by Jörg Rüppel. Generated on Sat Aug 16 15:26:51 2008 for Zoidcom by 1.4.6-NO