DoubleDeez / MDFramework

A multiplayer C# game framework for Godot 3.4 Mono.
https://discord.gg/UH49eHK
MIT License
77 stars 13 forks source link

GameSynchronizer ping between peers #51

Closed Beider closed 4 years ago

Beider commented 4 years ago

In the scope of #49 it is intended to implement the ability to disable server relay. Currently the GameSynchronizer has clients sending ping requests to each other, of course these bounce through the server anyway since there is no P2P communication.

Since these packets go through the server anyway we might as well not have the clients ping eachother. The reason they do ping each other is because we use the highest ping in the game to decide what the Remote Offset should be for the GameClock.

In short the remote offset decides how many game ticks behind remote signals are executed, 1 remote offset is 1 physics frame, ie. roughly 16 milliseconds. So if remote offset is 10 then all remote nodes are running 160 milliseconds behind for that client, this is to ensure we get a smooth behavior from all remote nodes so the data will be there before it is needed. Currently the remote offset is actually adjusted extra high because ping is for an entire roundtrip, half of ping time would be the minimum time you need to get remote signals. In addition we have an additional buffer added on top of the ping (20 % I believe).

So what do we do to solve this in the GameSynchronizer?

    private void OnPingTimerTimeout(Timer timer, int PeerId)
    {
        // Check if peer is still active
        if (!InternalPingList.ContainsKey(PeerId) || !MDStatics.IsNetworkActive())
        {
            MDLog.Trace(LOG_CAT, "Peer {0} has disconnected, stopping ping", PeerId);
            timer.Stop();
            timer.RemoveAndFree();
            return;
        }

        // Send ping request
        if (MDStatics.IsClient() || GameClock == null)
        {
            RpcId(PeerId, nameof(RequestPing), OS.GetTicksMsec());
        }
        else
        {
            uint ping = (uint)GetPlayerPing(PeerId);
            uint estimate = GetPlayerTicksMsec(PeerId) + ping;
            RpcId(PeerId, nameof(RequestPing), OS.GetTicksMsec(), estimate, GameClock.GetTickAtTimeOffset(ping));
        }
    }

If you look at this method you can see that clients send other peers ping requests, this should be removed (just disable the ping timer for clients and remove the MDStatics.IsClient()). Then instead the server should calculate what the "highest ping" should be for each client and send it to the client. The highest ping would be <clients ping>+<highest ping of remaining peers>.

Eg. P1 => Ping : 100 P2 => Ping : 150 P3 => Ping : 200

In this scenario maximum ping would be P1 => P1 + P3 = 300 P2 => P2 + P3 = 350 P3 => P3 + P2 = 350

This information could be sent along with normal ping packets and then we just need to ensure if you are a client when you are asked for max ping in GetMaxPlayerPing() we return this value.