Skip to content

Rollback

You can adjust the ping value in the config.json file to simulate a bad internet connection.

Set the ping to 150, it will inject a delay of 150ms to the packets sent between the debug server and the game clients. The RTT for the connections will be 300ms now.

img

Play the game again, the input delay should be very noticeable. You'll add Rollback to your game to make it more responsive.

IResotrable

First, you will create a new class that implements IRestorable interface.

A Rollback enabled game needs to rollback the game states to a confirmed frame from the server. The FrameSyncEngine will take care of the game states stored in the IFrameSyncData components. You need to provide the FrameSyncEngine with a IRestorable object to restore any states that are not stored in a IFrameSyncData component.

public class MyRestorable : IRestorable
{
    PSnapshot2D snapshot2D;

    public int frameNumber { get; }

    public MyRestorable(int frameNumber)
    {
        // 1
        snapshot2D = Parallel2D.Snapshot();
        this.frameNumber = frameNumber;
    }

    public void Clear()
    {
        // 2
        Parallel2D.DestroySnapshot(snapshot2D);
    }

    public void Restore()
    {
        // 3
        Parallel2D.Restore(snapshot2D);
    }
}

In // 1, you create a snapshot of the Parallel physics world. The created MyRestorable will be passed to the FrameSyncEngine.

In // 2, The FrameSyncEngine will call the Clear() method when it does not need to rollback to the frame for a IRestorable. You will destory the snapshot to free the memory in the Parallel physics world.

In // 3, The FrameSyncEngine will call the Restore() method when it wants to rollback to the frame for a IRestorable. You will use the snapshot to restore the physics state of the Parallel physics world.

Next, add a public field rollback to the MyFrameSyncAgent script to enable/disable Rollback in the inspector.

public bool rollback = false;

OnEngineWillExportEvent

Make the following change to the OnFrameSyncEngineCreated method.

public override void OnFrameSyncEngineCreated(FrameSyncEngine engine)
{
    // 1
    FrameSyncInputSetting[] inputSettings = new FrameSyncInputSetting[2];

    // 2
    inputSettings[0] = FrameSyncInputSetting.CompressedFloatInput(
                                                           "y",
                                                           Fix64.FromDivision(-1, 1),
                                                           Fix64.FromDivision(1, 1),
                                                           Fix64.FromDivision(1, 10),
                                                           Fix64.zero);

    // 3
    inputSettings[1] = FrameSyncInputSetting.TriggerInput("ready");

    // 4
    FrameSyncInputConfig inputConfig = new FrameSyncInputConfig(inputSettings);
    engine.SetFrameSyncInputConfig(inputConfig);

    // 5
    parallelPhysics = FindObjectOfType<ParallelPhysicsController2D>();
    parallelPhysics.autoUpdate = false;

    // 6
    engine.OnEngineWillSimulateEvent += FrameSyncEngineWillSimulate;

    // 7
    if(!offline)
    {
        engine.SetNetworkIO(FrameSyncClient.Instance.frameSyncIO);

        // 8
        if(rollback)
        {
            engine.OnEngineWillExportEvent += FrameSyncEngineWillExport;
        }
    }
}

In // 8, we add our handler to the OnEngineWillExportEvent if rollback is enabled;

IRestorable FrameSyncEngineWillExport(int frameNumber)
{
    MyRestorable restorable = new MyRestorable(frameNumber);
    return restorable;
}

You create an IRestorable in the event handler and returned it to the FrameSyncEngine.

Enable Rollback

Make the following change to the OnFrameSyncGameCreated method

public override void OnFrameSyncGameCreated(FrameSyncGame game, FrameSyncReplay replay)
{
    if (offline)
    {
        // 1
        game.type = FrameSyncGameType.Offline;

        // 2
        player1 = game.CreateOfflineGamePlayer();
        player2 = game.CreateOfflineGamePlayer();

        // 3
        MyGameSettings gameSettings = new MyGameSettings();
        gameSettings.player1ID = player1.PlayerID;
        gameSettings.player2ID = player2.PlayerID;

        // 4
        game.userData = gameSettings;
    }
    else
    {
        // 5
        game.type = FrameSyncGameType.Online;

        // 6
        game.SetPlayerDataProvider(FrameSyncClient.Instance.playerDataProvider);
        game.CreateOnlinePlayers();

        // 7
        game.CreateGameUserData<MyGameSettings>();

        //todo: game settings will be created in the matchmaking stage
        MyGameSettings gameSettings = new MyGameSettings();
        gameSettings.player1ID = 1;
        gameSettings.player2ID = 2;
        game.userData = gameSettings;

        // 8
        game.clientSidePrediction = clientSidePrediction;
    }
}

In // 8, you enabled clientSidePrediction on your game.

Play the game, the input delay should be reduced a lot now.

Prediction Modifier

You can use the Prediction Modifier to control the extrapolation for the other players. By default, the FrameSyncEngine just reuses the last received inputs to simulate other players for the prediction frames.

Make the following change to the OnFrameSyncEngineCreated method.

// 2
inputSettings[0] = FrameSyncInputSetting.CompressedFloatInput(
                                                        "y",
                                                        Fix64.FromDivision(-1, 1),
                                                        Fix64.FromDivision(1, 1),
                                                        Fix64.FromDivision(1, 10),
                                                        Fix64.zero,
                                                        PredictionModifier);

In // 2, you set the PredictionModifier method as the Prediction Modifier of the y input.

Fix64 PredictionModifier(Fix64 value)
{
    return value * Fix64.half;
}

The PredictionModifier method is very simply, the y input is decayed by 50%.

The PredictionModifier method is called for every prediction frame, and the output of frame n will be used as the input for frame n+1. So if the initial value of y is 1, the predicted values of y in the predicted frames will be 0.5, 0.25, 0.125...