SimpleRollback demo - Roblox - not copylocked!
The most recent version can be found here
You can use script sync etc to get it in
Disclaimer: this has serious bugs and inefficiencies, and I do not recommend it for production use. This topic will only be of interest to people working with server authority.
This release is also LGPL licensed. If you base your game off my code you are obligated to open source any of my code you have used, including modifications and improvements to the original files. This license does not apply to code from Serde which will use their license.
I’ve been working on a rollback netcode framework recently, after being inspired by server authority. This code is a little different, as it does only gets world state from server if we have a missing frame, rather than using world state to interpolate. We only use player inputs, ever.
The framework works as an injected interface. If you need the animations because of anim permissions, you can find the keyframe sequences in workspace.AnimRig
Now, for usage.
The “Rollback” folder in ReplicatedStorage defines the framework. Don’t modify this, unless you want to make bug fixes and improvements.
“User” is where we will actually make the framework work. There are a lot of extra files here you don’t need to worry about I have included for bug testing.
Client and Server are the main runner scripts. They instantiate their client and server runners.
They inject RunnerInterface
RunnerInterface is just a table of functions packaged as a module. These functions are then called by the Runner classes.
It must export Config and simulate to work.
The rest are optional, but useful functions.
getLocalPlayerInputs is required if you wish to pass any local inputs that need modification first e.g. camera relative movement.
InputSerDes is important and overrides the default network serialisation (nonexistent). I included the serde module (I do not own this module, please abide by the respective license) and reference code to automatically pack input frames based on the config template. I might make this a default.
Config
The important thing to modify here is playerInputTemplate
If you try to access input not defined in playerInputTemplate, you will create endless mispredicts.
You must set the default template values for input here.
maxPingMs is the size of the static buffer of inputs the server gets before repropagating them. Inputs sent after this will be voided by the server.
You probably shouldn’t touch the rest unless you are confident.
In the example, you will see RunnerInterface provides functions from UpdateSim and UpdateWorld
The .update function in UpdateSim controls how the game simulation is processed. You must only modify worldState here. This is where the “game” happens.
The functions in UpdateWorld are purely for view and you must not modify anything passed in here.
-
simPostRender is called after every simulation step (post rollback corrections). It is always called every frame, so it is potentially expensive.
-
simRender is called even after a rollback reprediction. It is also called before simPostRender.
-
frameRender is called after each Heartbeat and that will depend on the scheduler. Use this for parametric functions- e.g. animations.
I welcome any and all contributions and discussion! I hope server authority obsoletes this little project.
Issues/TODO
- Performance can start stalling due to too many deepcopies. I am thinking checkpointing world state every X frames should work well here. I also haven’t made sure each deepcopy is actually necessary.
- Proper optimisation and code quality pass
- Better rendering code to handle cases where mispredicted futures call creation/deletion too often. These are expensive!
- A lua physics engine, because Roblox won’t let us call StepPhysics outside of plugin context
- Intentional connect/disconnect for the demo code
- Instantiation and Actors. The server code can likely be cleanly multithreaded with Actors, as it never renders anything. Client code might not be possible.
- Interpolated rendering for the demo place
- Non predicted properties, to prevent extrapolative rubberbanding if not desired
Update 10
- Fixed the pesky bug with desync! It was because I was accepting old server packets at exactly the frame they get simulated, but due to a logic bug, never simulating, only resending.
- Simple check to make sure client cannot send frames from the future and pollute state
- simRender renamed to postSimRender, simRender is now called any time a sim render happens e.g. resimulating, or post sim.
Update 11
- Injects Connected for all active players, hopefully keeping them active. Might introduce desyncs. Will need investigation.