How To Make Rollback Clients start on the same frame?

Hello, I’ve been working with the GGPO prediction and rollback model with coherence. I’ve followed the example posted here: https://docs.coherence.io/v/0.8/authority/input-prediction-and-rollback

I’ve noticed some desync and set up the debugger. It looks like the first frame on each client is not the same. I believe this is what causing my issues.

Here is an example from the debug: The first Client detects the other player and starts the simulation at 86278258334

The second client gets both client joined messages, and logs its first frame as
86278258343

This is bad right? If clients don’t agree with what is happening on specific frames, I’d be rolling back to the wrong frame.

I posted a snippet of my input simulation script. I have more if need be

    // called by BOTH clients when a client joins, creates a controller on each side for the new player 
    protected override void OnClientJoined(CoherenceClientConnection client)
    {
        SimulationEnabled = AllClients.Count == 2;
        NetworkedController newController = client.GameObject.GetComponent<NetworkedController>();
        newController.Initalized += BeginSimulation;
        newController.PlayerID = AllClients.Count-1;
        ServiceLocator.ControllerService.AddController(newController);
        // if both clients have joined, the last joining client sends a random seed to both sides to use
        /*if (AllClients.Count == 2 && coherenceBridge.ClientConnections.GetMine() == client)
        {
            Debug.Log("I'm player setting seed for both clients");
            client.SendClientMessage<NetworkedController>("SetRandomSeed", Coherence.MessageTarget.All, Random.Range(0,1000));
        }
        */
        if(SimulationEnabled)
        {
            Random.InitState(0);
            BeginSimulation();
        }
    }

    // starts simulation, but only called after the local client has signaled that they have been initalized
    // currently initalization is only used for sharing a seed. But for future reference this can be used to wait for character choice/stage choice etc.
    public void BeginSimulation()
    {
        # if COHERENCE_INPUT_DEBUG
        Debugger.FramesToKeep = 1000;
        #endif
        // Lets us rejoin the same simulation without restarting the app.
        SimulationEnabled = true;
        manager.StartRoundMultiplayer();
        StateStore.Clear();
    }
    protected override void OnPauseChange(bool isPaused)
    {
        Debug.Log($"paused {isPaused}");
        //PauseScreen.SetActive(isPaused);
    }

I’ve been looking through the forums and documentation but have to get a clear answer. I hope this isn’t off-topic or has been answered already. Thank you!

Excuse the previous code was the result of some quick experimentation and was not accurate to the build that caused the problem. This is the code I’ve been using instead


    // called by BOTH clients when a client joins, creates a controller on each side for the new player 
    protected override void OnClientJoined(CoherenceClientConnection client)
    {
        NetworkedController newController = client.GameObject.GetComponent<NetworkedController>();
        newController.PlayerID = AllClients.Count-1;
        ServiceLocator.ControllerService.AddController(newController);
        if(AllClients.Count == 2)
        {
            Random.InitState(0);
            BeginSimulation();
        }
    }

    // called when both clients have joined
    public void BeginSimulation()
    {
        # if COHERENCE_INPUT_DEBUG
        Debugger.FramesToKeep = 1000;
        #endif
        // Lets us rejoin the same simulation without restarting the app.
        SimulationEnabled = true;
        manager.StartRoundMultiplayer();
        StateStore.Clear();
    }

Hey @the0dox!

The documentation you’ve linked to is an old one (it’s for v0.8). Here’s the new version of it.

It’s important to make sure you’re using the latest SDK version if possible (v1.2.4 as of today). There have been some important fixes to the input system since v0.8.

The desync you’re experiencing is likely due to the clients not starting their simulations on the same frame, as you correctly identified. This is highlighted as a potential issue in the latest documentation:

.

While coherence doesn’t provide built-in support for synchronizing the starting frame, you can implement it yourself. One simple approach involves having one player (e.g., the host) determine a future starting frame and communicate it to all other players using a command.

For instance, if player A (the host) is currently on frame 100, they could send a SetSimulationStartFrame command with a value of 300 to all other players. This should give the command ample time to reach all clients before they reach the designated start frame. Each client, upon reaching frame 300 locally, would then set SimulationEnabled = true.

The delay between the current frame and the start frame should account for potential network latency between players.

Hope this helps resolve your desync issue!

Feel free to ask if you have any more questions or need further assistance!

This is great thank you for the comprehensive response. I have just one more follow up question, is there a link to the full updated documentation? I’ve been using the coherence manual:

but I was wondering if there were other resources for learning coherence

Sure thing!

The link is correct now - this is the latest documentation.

As for other resources, you can find some through the documentation as well: