02
Netcode
Explaining how fighting games use delay-based and rollback netcode
October 16, 2019
Infil

Rollback Netcode

Since a game’s choice of netcode can never magically change the distance between a player and their opponent or prevent networks from dropping or delaying information, you may wonder how one netcode strategy could be drastically better than any other. The key lies in how the netcode handles uncertainty.

When there is no information from the remote player, delay-based netcode needs to pause and wait, as described in detail on the previous page. Rollback’s main strength is that it never waits for missing input from the opponent. Instead, rollback netcode continues to run the game normally. All inputs from the local player are processed immediately, as if it was offline. Then, when input from the remote player comes in a few frames later, rollback fixes its mistakes by correcting the past. It does this in such a clever way that the local player may not even notice a large percentage of network instability, and they can play through any remaining instances with confidence that their inputs are always handled consistently.

But let’s start by taking a look at what a “rollback" even is.


Act Now, Apologize Later

When remote inputs are missing, rollback netcode will continue to simulate the game. But, when those inputs eventually come in, the game will have advanced past the time when the opponent pressed that button, and the game will have already shown a different result to the screen. To fix this, rollback netcode will rewind the simulation, apply the correct input, and show the new result to the player immediately.

In the example below, both the local player and the remote player press medium punch on frame 1, but let’s say the opponent’s input is delayed over the network and reaches us three frames later on frame 4. Like offline play, the local player’s move immediately begins animating on their screen, and the game happily continues along assuming the opponent has not done anything. On frame 4, input from the opponent, marked as being pressed on frame 1, finally arrives. This means what we’ve shown to the player in the last 3 frames is not what really happened, and the game must fix the past by performing some calculations in the background.

First, the game state gets reloaded to what it was before frame 1 -- that is to say, it “rolls back" to the frame where the input needs to be applied, a few frames in the past. Then, the inputs from the remote player (as well as any inputs the local player had pressed during past frames) are applied, and the game re-simulates multiple frames forward to reach the present frame once again.

The remote Jago player presses MP on frame 1. When it reaches us, we're already on frame 4, so we have to rewind to frame 1, get Jago to press MP, then re-simulate back to frame 4 very quickly. The orange filter and "ghosted" version of Jago indicate background calculations that the player never sees. At 30% speed, the local player just sees Jago's MP appear a few frames into its startup.

All of this happens instantly, in 1 game frame; the local player does not ever see the game perform these steps individually. Instead, all they see is the game state they thought was correct (but was false) get immediately replaced with the actually correct game state. Depending on what’s happening in the game, the characters might suddenly jump around a little bit, and in general, animations for the remote player’s moves will tend to appear a few frames already into their startup when they are shown to the local player.

Jago attacks with his overhead. The video shows the move with 0, 1, 2, or 3 frames of rollback right when Jago attacks, which shaves that same number of frames off the beginning of the move's startup. Even at 30% speed, it's hard to tell the difference.

To put it another way, rollback allows each client to temporarily break the lockstep model -- the game may be showing slightly different things to each player, depending on the connection quality and what’s happening at the time of the rollbacks. However, the game will always correct itself to be the same for both players whenever the inputs have been received a few frames later.

It’s worth reiterating this important property of rollback netcode; the local player’s inputs are always shown immediately and can never be undone. If you press a button on frame 4, that information is immediately processed by your game and your move will instantly come out on your screen. If any rollback occurs when you pressed that button, it is always correctly re-applied during the rollback calculation. There is also no way your input can be invalidated or “eaten" by network lag, which can occur in delay-based frameworks when the game is waiting for remote input and is unresponsive for both players. Therefore, a player can feel confident that the buttons they press will be executed regardless of the network quality, greatly enhancing the consistency of online play.

As an added bonus, when a game encounters network trouble, there will only ever be rollbacks in the immediate time around the spike, making it a very localized approach. In delay-based solutions, the game may choose to inflate the input delay for many seconds, causing long-lasting effects far away from the time of trouble, even though the network may have smoothed out its issues by then. Rollback is particularly great for lossy connections, like WiFi for this reason.

This is the core concept of rollback. But we can do better.


The Basics of Prediction

In the example above, when input was missing for the remote player, we simply assumed they were inputting nothing so that the game had something to simulate. This is a pretty bad assumption in general because players are rarely doing nothing, and the visual states shown to the player right before a rollback would almost always be wildly incorrect. Turns out, we can predict what the opponent is doing with an uncanny degree of accuracy.

When input is missing, what rollback solutions actually do is duplicate the last known input from the remote player for the current missing frame. If they were holding down-back, the game predicts they will continue to hold down-back. If they were holding a button, the game assumes they are probably still holding that button. The game then runs with these predicted inputs for the remote player instead.

(Note: for the purposes of keeping these example videos as easy to understand as possible, we’ll temporarily assume that network transmission time is instant and whenever there are missing frames, it’s because the network got briefly overloaded. Don’t worry, the concept still works perfectly fine with network delay, but the videos would have been a little too confusing)

Inputs for remote Fulgore are lost after frame 3, but in a rollback model, the game duplicates the last known input to keep the game moving forward. Predicted frames are colored orange, and the last known input is indicated by the orange caret under frame 3's input box.

When the actual, correct remote inputs arrive a few frames later, the game needs to decide how to use them. If the inputs were incorrect, we perform a rollback, as previously described -- the game rewinds to a previous game state and uses the new inputs to simulate (and re-predict) forward. But if these inputs are exactly as predicted (for example, the player did actually continue to hold down-back), no rollback needs to happen; the game that was shown to the player during the prediction period was correct! The game simply updates what it knows to be the last correct input from the remote player, and the local player doesn’t know anything went wrong. When the game correctly predicts the inputs, rollback ensures the game feels perfect, even if there was network trouble.

Inputs for remote Fulgore are, again, lost after frame 3. The inputs eventually come flooding in on frame 8, and Fulgore did indeed hold forward, the same as our prediction. The game quickly verifies this is true and keeps going without even needing to roll back.

Prediction is a relatively basic addition to the paradigm, but it has a huge impact. Rolling back and re-simulating the game only if the remote player did something unexpected has a lot of nuanced advantages that we need to discuss further. It does a lot more to hide the effects of bad connections than you might initially think.


Why Prediction is So Good

Rollback’s version of “prediction" is not as advanced as you might expect if you play other genres, like first-person shooters. As stated, all it really does is assume the remote player has not changed their inputs from the last time it received information from the network.

As it turns out, this is an extremely reasonable assumption if you break down how often players actually change their inputs. Consider a match in a Street Fighter-like game where players are walking back and forth; let’s assume a player changes their walking direction around 5 times per second, which is a good ballpark even for especially active players. This means we actually only need inputs from the remote player on 5 of the 60 frames in a given second (a staggeringly low 8% of the time). The player’s inputs for the other 55 frames can be predicted with a high degree of accuracy just by assuming they are the same as the previous frame.

And this is typically during the most active part of a match. If you have knocked your opponent down and are trying to pressure them, it’s not uncommon for a defensive player to simply hold down-back for 30, 60, or 120 consecutive frames. If your opponent jumps, they are airborne for around 45 frames and are likely to only press 1 button during that entire time frame. Why do we need to wait for the network to tell us this, if the frame-by-frame prediction that your opponent will continue to hold down-back, or continue to not press a button, is correct 95% of the time?

What this means is, if there is a network spike during any of these moments where remote input was unchanged, it is completely invisible to both players. Even in dodgy connections where information may arrive quite late many times per second, a pretty large percentage of these delays will randomly coincide with moments where the remote player was not changing their inputs. A delay-based game would be forced to pause and wait here, but because a rollback game verifies the inputs were correct when the information arrives and doesn’t need to change anything, the connection appears completely perfect.


Rollback Cares About Game State

There are even more ways rollback hides bad connections. Unlike delay-based solutions, which could have problems any time during a match, rollback approaches only show a visual change if the new inputs from the remote player have changed the game state.

Let’s explain with a video example. Here, your remote opponent playing Jago whiffs a heavy punch on frame 2, and your game receives this input over the network just fine and begins animating the move. Right after pressing heavy punch, there is a huge network spike and all information for the next 5 frames does not arrive on time. As explained, rollback doesn’t care; it will continue to simulate the game anyway -- in this case, it will assume Jago inputs nothing, as they had released their finger off the heavy punch button on the last known frame.

Six frames in the future on frame 8, all the information for these missing 5 frames floods in, and as it turns out, the opponent was mashing buttons. This was not what we predicted, so we have to rewind the game 5 frames to the start of the heavy punch, re-simulate these 5 frames with the correct input, and show the player the real game state.

Jago presses HP, but after that, inputs are lost for a bit. Rollback simulates forward anyway. When they come in, turns out Jago was mashing buttons; it doesn't match our prediction, so we must rollback and re-simulate. But the end result is identical, so the players don't know there was any network trouble at all.

But because the opponent was whiffing a move, the game’s rules say they aren’t allowed to control their character right now. These new inputs have changed nothing about the game state! Even though the game rolled back and re-simulated multiple frames, the end result was the same as what was shown to the screen, and once again, the lag is completely invisible to both players.

As in the previous section, think about all the times during a match that your opponent’s inputs can’t change the game state. This varies by game, but usually includes times when they are knocked down, being comboed, in block stun, when any move is starting up, recovering or whiffing, and many others. The number of frames these actions take to complete is not short, either; hard knockdowns in Street Fighter games can take 60 frames, heavy attacks whiffing could take 30 frames or more, and combos can last for 10 or more seconds! As long as the game has correctly registered the beginning of these actions, you are completely immune to lag until the opponent can change the game state again, even though the game might be rolling back furiously during these periods.

Putting this together with the prediction model, you can begin to see just how often lag gets “absorbed" during a fight. The connection problems haven’t gone away, but rollback solutions do the best job of just simply ignoring them as often as they can, and they can make even the laggiest connections feel extremely playable. In Killer Instinct’s case, it even allows online sets to be played between Singapore and the eastern US over connections that approach 200ms ping, which would be unbearable with any delay-based netcode.


Can We Split the Difference?

In real world situations, inputs always take time to send over the network, which means every input will always arrive after the frame it was intended for. You may have correctly deduced that this means every single button press or change of direction your opponent does will always cause at least a slight rollback, as long as the input changes the state.

Turns out, we can eliminate this by combining delay-based and rollback solutions. In fact, every modern fighting game that uses rollback is built on a delay-based framework, with the important caveat that the delay is fixed at the start, and never changes. If you pick a suitably small delay and keep it fixed, it should give enough time for “many" of your opponent’s inputs to reach you without needing to trigger a rollback on every state change. If there are network spikes, rather than increasing input delay or pausing and waiting, instead rollback takes over for any input that takes longer than the chosen delay. As long as the delay is small and consistent, players will quickly adapt and many will not even notice a difference from offline play.

What number to set this delay at is up to the discretion of the developer. Some games, like 3rd Strike Online Edition, Darkstalkers Resurrection, Skullgirls and Samurai Shodown V Special, allow the user to set their own delay, usually between 0 and 8 frames. Other games, like Killer Instinct and Injustice 2, universally choose the number for all players (3 frames, in the case of these games) and provide no option to change it.

Skullgirls lets each user manually choose how many frames of input delay they would like. Rollback only kicks in if the network delay is bigger than this number.

Both methods have their strengths and weaknesses. Allowing the user to choose their own delay means they can factor in the strength of the connection and adjust the delay to match. But it also means that players who don’t understand how rollback works, or what this setting controls, are much more likely to choose a setting poorly suited for the connection and experience. Forcing the delay to always be consistent may mean some matches have slightly more delay than is theoretically necessary, but it gains a measure of consistency across all connections. Some developers may feel that asking the player to adjust their reactions to different (although stable) delays across different connections has more drawbacks than just always using a well-chosen setting that will work for the vast majority of connections.


A Summary of Rollback's Benefits

Rollback largely solves the consistency issue that plagues pure delay-based solutions. The local player is not affected by network trouble, which leads to their inputs always being treated the same way; if you try to perform a specific knockdown setup, or you have a combo that needs very tight timing, rollback always lets you execute it the same way regardless of lag. If a network spike occurs, it only impacts the present moment of the match, whereas a delay-based game might inflate the input delay for many seconds.

Large portions of network instability or delay are simply made invisible to both players when using rollback netcode. Every time the remote player cannot change what’s happening to their character, or every time they choose to repeat the action (or non-action) of their previous frame, the rollback system will produce no visual change, and any networking hiccup during that time vanishes. This also impacts things like fireballs thrown by your opponent; once they have been thrown, the opponent can do nothing to change their trajectory, so their path across the screen is immune to rollbacks and network issues, letting you block or jump over them with offline timing.

Rollback is the best solution we have for hiding lag in fighting games. But now it’s time to talk about what it takes to get rollback netcode into a game.

Back to the blog index.

Fightin' Words