GMS 2 Multiplayer desynchronization


I've been trying to fix a synchronization problem with my multiplayer characters for more than a week.

The time between sending the movement information and processing the information for all clients is so high that the character teleports back with each movement, which is very embarrassing.

I started looking for the culprit, assuming in the first place that my server in C# was taking too long to process the information received and then send it back to everyone. Unfortunately, after a few tests, the server does not take more than 2000 ticks (0.2 ms) to do its job.
Then I thought to myself that the TCP protocol was not adapted, and that it was too long (a little surprising for just two packets, but nvm...). I created a GMS2 light project for movements with the same C# server. As a result, the movements are impeccable.

By elimination, only the client remains. I discovered the amazing profiling tool that allows me to know the execution time. Unfortunately, the execution time is very low, and should not cause such significant desynchronizations.

The way the movements work is as follows: at each change of direction, I send the x and y coordinates to the server as well as the direction (in int). The server sends this information to all players on the map. In short, for a movement, there are only 2 TCP packets: the first to start the movement, the second to stop it.

oClient - Async Networking
var read_buffer = ds_map_find_value(async_load, "buffer");
var constant = buffer_read(read_buffer, buffer_u16);
switch (constant)
    // Player Movement Message
    case 3002:
        var tempUID = buffer_read(read_buffer, buffer_u32);
        var dir = buffer_read(read_buffer, buffer_u32);
        var tempX = buffer_read(read_buffer, buffer_u32);
        var tempY = buffer_read(read_buffer, buffer_u32);
        oCharacter.dir = dir;
        oCharacter.x = tempX;
        oCharacter.y = tempY;

oCharacter - Step
if (!animInProgress)
    if (dir == 1 && !place_meeting(x, y - walkSpeed, oCollision))
        y -= walkSpeed;
    else if (dir == 2 && !place_meeting(x, y + walkSpeed, oCollision))
        y += walkSpeed;
    else if (dir == 3 && !place_meeting(x + walkSpeed, y, oCollision))
        x += walkSpeed;
    else if (dir == 4 && !place_meeting(x - walkSpeed, y, oCollision))
        x -= walkSpeed;
    else if (dir == 5)
        animInProgress = true;
        oldSprite = sprite_index;
        image_speed = walkSpeed / 1.5;
        image_index = 0;
        sprite_index = spriteAnim;

    if (dir != 5 && dir != olderDir)
        image_speed = walkSpeed / 3;
        switch (dir)
            case 0: inMovement = false; image_speed = 0; image_index = 0; break;
            case 1: inMovement = true; sprite_index = SMAdvTop; spriteAnim = SMAdvTopSword; break;
            case 2: inMovement = true; sprite_index = SMAdvBottom; spriteAnim = SMAdvBottomSword; break;
            case 3: inMovement = true; sprite_index = SMAdvRight; spriteAnim = SMAdvRightSword; break;
            case 4: inMovement = true; sprite_index = SMAdvLeft; spriteAnim = SMAdvLeftSword; break;

olderDir = dir;

I don't have any other ideas about my problem, so I hope you'll have something to suggest to me. ^^''


Phil Strahl

Hey Tom!
It appears like the information to stop the character arrives one frame late. A crude way of solving this would be to buffer the network coordinates and delay the client's game by one frame also so that they match up? Another thing, though: I strongly suggest to switch to UDP packets as with TCP/IP you never know when a package will arrive (only that it does). With UDP packets can get lost, so that would means to constantly fire a stream of packets with a player's coordinates out and depending on the number of players/entities this might become slow as well. A solution to that would be sending out packets with absolute coordinates for everything maybe once a second and the packets in between are just position/state changes.

I know, that's all pretty vague but maybe you find something in it that helps.


The secret is to bundle. Record the movement 3-6 frames and then send it to the client that will then replay the frames. And I agree. UDP is the way.