Skip to content

Instantly share code, notes, and snippets.

@mattgrayisok
Last active January 11, 2016 00:01
Show Gist options
  • Save mattgrayisok/1e4779dce43a98df5e62 to your computer and use it in GitHub Desktop.
Save mattgrayisok/1e4779dce43a98df5e62 to your computer and use it in GitHub Desktop.
Some notes on MMO game engine
Server creates world and starts physics clock
Client creates world, starts physics clock and connects to server
Server receives connection request, creates client representation with a physics tick offset set to current tick
Server begins sending world state objects back to client
Client uses world state objects to populate world including positions (excluding the player themselves)
Client should be using a render clock which is faster than the physics clock:
- The client will always be painting frames that we do not have a state for (extrapolate from existing states?)
- Or we buffer a few states so we're always displaying accurate info but we'll be showing the recent past (can interpolate frames from these values)
How do we share as much logic between client and server as possible?
Control states get tagged and sent straight to the server - no need to buffer on client side or anything
We need to buffer world states a small amount on the client side and report the current position 100ms in the past. Each request to get current state attempts to figure out the current time on the server with a certain ms offset. We find the two states surrounding this offset and then interpolate a position.
On the server side offset the user inputs by 5 (for some reason...)
Values:
- Client interpolation offset (ms) - The moment that is being rendered on the client is this far behind current time
- Server state history (ms) - The amount of world state histories to keep on the server, used for going backwards in time to predict shooting people
- Client player state history (ms) - The amount of player state histories to keep on the client, to use when double checking the player position against the world state
- Client world state history (ms) - The amount of world state histories to keep on the client, to be used by the interpolation offset
Client API:
Gather input state - function called by library, defined by game
getWorldState() - gets a world state for the current time containing all objects, interpolates, extrapolates etc - ready to pass to renderer, called inside render loop
sendMessage(msg) - For sending arbitrary messages
receivedMessage(msg)
gameloop(inputstate) - Called by the engine to allow custom logic to apply input state to current player, should return a new client side player state
reconcilePlayerState(newState, stateTime) - Returns a new player state. Pass in the player state as delivered by the server and the time for that state. Function compares this to the player state history and returns a new player state for the current time shifted towards the correct position
Server API:
Process input state - function called by library, passes in input state and controlled object
getWorldStateForPlayerAtTime(player, time) - Called by the game to get a world state for when a player fired a weapon
sendMessage(player, msg) - For sending arbitrary messages
receivedMessage(player, msg)
gameLoop - Implemented by game, returns a new worldstate
How do we figure out how to mess with world states in a generic way?
The engine doesn't need to know about which object is the current player? But then that means we need to do the player state history (for reconciliation) outside of the engine which is naff. Could solve this by having a method on the client for reconcilePlayerState(newState) when each world state arrives. This would need to be exactly the same format as the return from client side gameloop() so that the engine can compare
A lot of this requires a method of comparing two states and interpolating between them then returning an updated copy. Will need to figure that out!
Maybe the worldstate format is fixed as an array of objects all of which have uuids. Then the engine can loop over them and compare sub elements of the full world. Then any controllable ids can also be registered with the engine maybe
Or maybe different object types can register their interpolation and extrapolation methodswith the engine. That way they can be organised nicely. If a method hasn't been registered for a type the engine just chooses the closest one
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment