Networking Lag from host to player but not the other way around

Hi, it’s me again and I’m having another problem but this time its networking. I implemented networking from a tutorial from a German dude that was making an MMORPG with monogame. I’m using TCP and NetworkStream.

Player:

    async Task HandleUserInput(ClientTCP ctcp, Lily[] lilies)
    {
        if (Keyboard.GetState().IsKeyDown(Keys.Up) || Keyboard.GetState().IsKeyDown(Keys.W))
        {
            await Task.Run(async () =>
            {
                positionRectangle.Y -= speed;
                await ctcp.SendMove("Y", -speed);
                Main.moveCount++;
            });
        }....



    public async Task SendMove(string direction, int signed)
    {
        Main.logger.Info("SendMove was called");
        PacketBuffer buffer = new PacketBuffer();
        buffer.AddInteger((int)ClientPackets.CMove);
        buffer.AddString(direction);
        buffer.AddInteger(signed);
        await SendData(buffer.ToArray());
        buffer.Dispose();
    }

    public static async Task SendData(byte[] data, int index)
    {
        try
        {
            if (Clients[index].myStream != null)
            {
                PacketBuffer buffer = new PacketBuffer();
                buffer.AddBytes(data);
                await Clients[index].myStream.WriteAsync(buffer.ToArray(), 0, buffer.ToArray().Length);
                await Clients[index].myStream.FlushAsync();
                buffer.Dispose();
                Program.moveCount++;
                Console.WriteLine(Program.moveCount);
            }
        }
        catch (Exception)
        {
            throw;
        }
    }

After the data is sent to the server, the server then transfers to all other connected players.

    void HandleMove(int index, byte[] data)
    {
        //Console.WriteLine($"{index} HandleMove");
        PacketBuffer buffer = new PacketBuffer();
        buffer.AddBytes(data);
        buffer.GetInteger();

        //Console.WriteLine($"Player #{index} did:{buffer.GetString()} {buffer.GetInteger()}");
        buffer.AddInteger(index);

        for (int i = 0; i < ServerTCP.Clients.Length; i++)
        {
            if (ServerTCP.Clients[i].IP == null || ServerTCP.Clients[i].Index == index)
                continue;
            _ = ServerTCP.Clients[i].SendMove(buffer, i);

        }
        buffer.Dispose();
    }

Don’t know exactly what I was thinking when writing this method but I’ll just leave it as is for now…(will delete asap)

    public async Task SendMove(PacketBuffer buffer, int clientIndex)
    {
        await ServerTCP.SendData(buffer.ToArray(), clientIndex);

    }

    public static async Task SendData(byte[] data, int index)
    {
        try
        {
            if (Clients[index].myStream != null)
            {
                PacketBuffer buffer = new PacketBuffer();
                buffer.AddBytes(data);
                await Clients[index].myStream.WriteAsync(buffer.ToArray(), 0, buffer.ToArray().Length);
                await Clients[index].myStream.FlushAsync();
                buffer.Dispose();
                Program.moveCount++;
                Console.WriteLine(Program.moveCount);
            }
        }
        catch (Exception)
        {
            throw;
        }
    }

Now the connected clients handle movement data

    async void HandleMove(byte[] data)
    {


        await Task.Run(() =>
        {
            Main.logger.Info("HandleMove was called");
            PacketBuffer buffer = new PacketBuffer();
            buffer.AddBytes(data);
            buffer.GetInteger();
            string direction = buffer.GetString();
            int signed = buffer.GetInteger();
            int playerId = buffer.GetInteger();
            Main.moveCount++;
            if (direction == "X")
            {
                MainGame.players[playerId].cursor.positionRectangle.X += signed;
            }
            else if (direction == "Y")
            {
                MainGame.players[playerId].cursor.positionRectangle.Y += signed;
            }

            buffer.Dispose();
        });
    }

In the code, I log how many times movement occurred with the Main.moveCount variable.
The problem then arises that if the host made 100 move calls the joined player will only receive a small percentage (15- 30 calls) whilst if the joined player moves, the host will receive most of all calls (99.9%). What gives?

When I playtest, I use my home PC and my laptop, which is tethered to my phone for 4G internet (just to make the networking that more realistic).

Can anyone help me with this dilemma?

This can have some 300~ms delay… it’s like playing a game between someone in Europe and far eastern Asia…

That doesn’t make sense, the movement is simultaneous, just the packets are not sent and I don’t know why… if the player who joins moves (4G laptop), the host gets all of the data… It should lag here as well

TCP is maybe not what you want for that type of game. TCP will resend as long as the data arrives, so there should be no data loss at all (but this can increase the latency enormously) unless it detects a connection loss (because of timeout) - also the packets are ordered - so as long as packet 5 is not delivered, packet 6 is not processed.

not sure if that’s exactly happening in your case, but I doubt you will be satisfied at all with TCP in your case - unless you do some tremendous amounts of netcode optimizations (packing, etc)

It’s really hard to debug asyncronous netcode …

There was this thing I forgot the name, interpolation? predicting player movement?, keeps the action going instead of a player standing still…

Here’s a search result:
https://www.youtube.com/results?search_query=game+networking+predicting+movement

You should definitely read this when making a game with networking:
https://www.gabrielgambetta.com/client-server-game-architecture.html
https://www.gabrielgambetta.com/client-side-prediction-server-reconciliation.html
https://www.gabrielgambetta.com/entity-interpolation.html
https://www.gabrielgambetta.com/lag-compensation.html

Thank you for the links. I didn’t think that user prediction would be an issue since the move network data was not that complex, just a simple Y + 5 or a X - 10

Well … all of the above…
And you’re 'await’ing a lot, thus, making your code sequential again (when you iterate in your server-code to send the message to your clients for example, the method you call in that loop is async but awaits the send-request every time it gets called… no concurrency there, imo).
Also: Libraries like lidgren, as that one’s almost certainly referenced in some of the links the others provided, provide nearly-secure transfer layers over UDP reducing the latency.
And you shouldn’t generate objects (Packetbuffer) over and over again in your game-loop (has nothing to do with your stated problem here) because it triggers garbage collection… Rather re-use the objects.
Ah… yeah … that’s all just from the top of my head… and I really think that prediction and correction of that prediction is over-the-top for your example :slight_smile: This should work just fine… My lock-step interval is 3 or 4 times a second (the ‘server’ waits to synchronize player-input) and I don’t have prediction-correction implemented and there is no lag noticeable at all (tested with 8 players).

Thank you again for your reply. I will give lidgren a try. I haven’t tried anything async which I will do now and if that fails, I will switch to lidgren.

EDIT_________
I just finished watching some of the unity networking tutorials and even tho they were doing different things, there was 1 thing they all had in common, and that is “send interval”, they all send their positions at a desired interval whereas I send data every CPU tick when the player is moving… will check if limiting this helps.

Another thing in noticed is that whenever I move and send data over the internet, Windows 10’s Antimalware Service Executable fires up like crazy, what’s up with that? How do game devs disable this and let windows 10 know that this traffic is ok?