Tuesday, December 24, 2019

Let's talk about ROLLBACKS! Part 2.

This is a short article about Rollbacks in Fighting Games...

Why one player's chosen delay doesn't affect the other player!

One of the advantages of rollbacks is that each player can choose their own input delay.

Now, some people seem to think that if Player A chooses delay 0 and Player B chooses a higher delay like 6, A's delay 0 choice will affect B's experience, giving them more rollbacks or some junk. Or that B's choice will affect A's game.

Lemme explain why that doesn't have to be the case, right quick.

(The unit of time doesn't matter, it could be frames, seconds, cups-of-coffee, fortnights...I'll just call it "time".)

Player A and B are fighting each other.  Player A chose delay 0, and Player B chose delay 4.
The game is at time 10.
Both players provide an input, let's say Player A presses Up and Player B presses Down.
BOTH OF THOSE INPUTS are treated as that player's input for time 10.  B's chosen delay doesn't change this!
The inputs are sent across the network, etc etc.  When Game A gets B's input, Game A rolls back, applies the input, and proceeds as normal.  When Game B gets A's input, Game B rolls back, applies the input, and proceeds as normal.

THE RESULT IS IDENTICAL ON BOTH SIDES.  The game receives data at the same time and processes it the same way for both players, regardless of chosen delay.


So then, what does choosing a delay really do?!


Player B chose delay 4, meaning they are telling the game to delay their inputs by 4 [units of time].

The game can do this two ways!

The first way, the way delay-based netcode works, is by saving a list of inputs and applying B's input from time 10 at time 14.  Presto, it's 4 frames delayed.

The second way, the way only rollback netcode can do it, is by reading the input at time 10 but showing the player the game at time 6, the game state from four [units of time] ago(Rollback netcode can do this because it already saves the game state at each tick, since that is necessary in order to make rollbacks work.)

The result is the same - Player B's inputs are delayed by 4 frames relative to the game that Player B sees.  It doesn't matter if the inputs are held for 4 ticks, or the game is held for 4 ticks, the presentation is indistinguishable.

Here's a game, at time 10:
-------------------------------------------------
 Time |                    |                    |
-------------------------------------------------
   1  |                    |                    |
-------------------------------------------------
   2  |                    |                    |
-------------------------------------------------
   3  |                    |                    |
-------------------------------------------------
   4  |                    |                    |
-------------------------------------------------
   5  |                    |                    |
-------------------------------------------------
   6  |                    | Player B sees this |
-------------------------------------------------
   7  |                    |                    |
-------------------------------------------------
   8  |                    |                    |
-------------------------------------------------
   9  |                    |                    |
-------------------------------------------------
  10  | Player A sees this |                    | <- CURRENT
-------------------------------------------------

Both players provide the input for time 10, but because Player B's delay is set higher, they're seeing the game at an earlier state.


Then why would I choose higher delay at all?


As I've covered before, the reason for choosing a higher delay is that you will get fewer rollbacks if you choose a delay that's close to the actual internet lag between players.  With Player B's game 4 ticks behind, if the late inputs arrive for Player B at times 8/9/10, they will not see any rollbacks when the game "adjusts the past" after time 7.

At a higher delay your game will feel laggier but look smoother, and at a lower delay (preferably zero!) the response time will be instant but you'll see rollbacks more often.


Again - if the game is properly constructed, neither player's delay choice affects the other player's experience.


[I've revised this section because people got really focused on it.  Sigh.]
GGPO handles this problem by making Player B's game run at time 10-minus-4 while Player A is at time 10, and storing the extra inputs for Player B as "future" inputs.  This is not exactly the same thing, but it's technically the same thing, and it produces the same result. 
It's easier to think about conceptually if you imagine both games in sync.  If you were going to develop your own rollback system, it's easier to just render the old gamestate and not worry about the skewed-sync part or having to ever store future inputs.  GGPO is smarter than that.  :^)