• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GameMaker Multiplayer desynchronization

T

TomLaVachette

Guest
Hello,

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
Code:
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;
          
        break;
    }
    .........

oCharacter - Step
Code:
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;
    }

    //Animation
    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. ^^''

Thanks.
 

Phil Strahl

Member
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-any-Key

Member
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.
 
Top