Trending
Opinion: How will Project 2025 impact game developers?
The Heritage Foundation's manifesto for the possible next administration could do great harm to many, including large portions of the game development community.
Featured Blog | This community-written post highlights the best of what the game industry has to offer. Read more like it on the Game Developer Blogs or learn how to Submit Your Own Blog Post
Let's look into how we can implement input buffering for a multiplayer Unity game, which gives us more control over how we utilize network bandwidth.
In this article, we will implement a way of reducing the frequency at which clients send inputs to the server, in the same way that SyncVars are capped at 10 updates per second. We will do this by buffering inputs and sending them out at a given frequency. This way, we get transmission rate throttling for both inbound and outbound data, and more control over how we utilize network bandwidth.
Because of the way we split our networking code into smaller components in the previous article, it should be relatively easier for us to add our changes without having to wrangle through too much code.
If you haven't already, you can download the source code for this article. Also included in the download are the project folders for the upcoming articles in this series, so if you want to, you can skip ahead and see how the full entity interpolation implementation looks like.
In Unity Networking, clients send input information to the server using Commands. Without buffering, what we did instead was call the corresponding Command every time input is made, potentially causing the client to call Commands on the server during every frame.
With input buffering, we take a different approach. When input is generated, instead of calling the corresponding Command immediately, we place the input in a buffer:
// in Awake (): List<Vector2> inputBuffer = new List<Vector2> (); // in FixedUpdate (): Vector2 input = /* poll input device here */; inputBuffer.Add (input);
Immediately after we buffer the captured input, we check to see if the buffer is full. If it is, we call the appropriate Command and send the buffered inputs to the server.
// Since FixedUpdate runs at 50 times a second, // we set inputBufferSize to 5 so that we can send // our inputs to the server at 10 times a second. public int inputBufferSize = 5; // back to FixedUpdate (): if (inputBuffer.Count < player.inputBufferSize) return; player.CmdMove (inputBuffer.ToArray ()); // don't forget to make room for new inputs! inputBuffer.Clear ();
Since we're now sending multiple inputs at a time to the server, we have to change our Command's function signature so that it can accept arrays:
[Command(channel=0)] public void CmdMove (Vector2[] inputs) { server.Move (inputs); }
Once we receive the inputs on the server, we can apply them all in one go by iterating on the inputs array (similar to how we do client side prediction and server reconciliation):
public void Move (Vector2[] inputs) { CubeState serverState = player.serverState; foreach (Vector2 input in inputs) { serverState = CubeState.Move (serverState, input); } player.serverState = serverState; }
And that does it for buffering!
When controlling your own player object, client prediction does a very good job of making sure that the experience always stays smooth and responsive. However, by introducing input buffering, other players will not see you in the same way that you see yourself! Since inputs are received at a low frequency and applied in batches, your movement will probably look choppy. You won't necessarily be slower; in fact you would probably move at the same speed as everyone else. However, visually it will seem that you're being rendered at a lower frame rate. This is expected - once we add interpolation in, your movement should become smooth again.
Our current implementation has a flaw that cheaters can take advantage of. Can you spot it?
What happens if the client sends more than 5 inputs in a single Command? What if it sends 10, or 50, or more? When the server receives the batched inputs, it accepts and applies all of them at the same time, regardless of the number of inputs received. This allows the client to make more moves than it should be allowed to in a given time frame. The server could potentially let you take 100 steps when you would normally be allowed to take only 5 steps, giving you an unfair advantage over other players. In multiplayer games, this is called a "speed hack," and we will look into how we can prevent this in the next article.
To see all of this in action on your own machine, be sure to download the project folder for this article! Thank you for reading, and until next time.
Read more about:
Featured BlogsYou May Also Like