Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiplayer game movement synchronization

I'm working on a multiplayer game and i'm having problem with synchronizing players.

When a player presses one of the move keys (W,A,S,D) then the client sends a packet about the pressed button, the server sets the velocity according to the pressed key and sends back to all nearby players the new velocity.

When the player release the key then the clients sends a packet, server sets the player velocity to 0,0 and sends the position and velocity to all nearby players.

So the problem is when i release the key, then most of the time the player jumps back.

How could i fix this?

i'm using socket.io.

Client side:

   socket.on('positionEntity', function (data) {
        console.log((data.x - entities[data.id].x)+" "+(data.y - entities[data.id].y));
        entities[data.id].setPosition(data);
    });

    $(document).keyup(function(e) {
        if (e.keyCode == 87) {
            keys.W = false;
            socket.emit("stopMove", {dir: 0, time: Date.now()});
        }

        if (e.keyCode == 65) {
            keys.A = false;
            socket.emit("stopMove", {dir: 1, time: Date.now()});
        }

        if (e.keyCode == 83) {
            keys.S = false;
            socket.emit("stopMove", {dir: 2, time: Date.now()});
        }

        if (e.keyCode == 68) {
            keys.D = false;
            socket.emit("stopMove", {dir: 3, time: Date.now()});
        }
    });

    $(document).keydown(function(e) {
        if (e.keyCode == 87 && !keys.W) {
            keys.W = true;
            socket.emit("startMove", {dir: 0, time: Date.now()});
        }

        if (e.keyCode == 65 && !keys.A) {
            keys.A = true;
            socket.emit("startMove", {dir: 1, time: Date.now()});
        }

        if (e.keyCode == 83 && !keys.S) {
            keys.S = true;
            socket.emit("startMove", {dir: 2, time: Date.now()});
        }

        if (e.keyCode == 68 && !keys.D) {
            keys.D = true;
            socket.emit("startMove", {dir: 3, time: Date.now()});
        }
    });

Server side:

socket.on('startMove', function(data) {
    if (data.dir == 0) socket.player.setMotionY(-5);
    if (data.dir == 1) socket.player.setMotionX(-5);    
    if (data.dir == 2) socket.player.setMotionY(5);
    if (data.dir == 3) socket.player.setMotionX(5);

    io.sockets.emit("positionEntity", socket.player.serializePosition());
});

socket.on('stopMove', function(dir) {
    socket.player.setMotionX(0);
    socket.player.setMotionY(0);

    io.sockets.emit("positionEntity", socket.player.serializePosition());
});
like image 222
Dávid Szabó Avatar asked Aug 07 '13 22:08

Dávid Szabó


People also ask

How do multiplayer games sync their state?

The client sends input to the server at T0 to emulate the game state at T1, so the client can then render the game without having to wait for the state update from the server, which only arrives at T3. This approach only works if the following takes place: The game state updates logic are deterministic, ie.

How do multiplayer games send data?

Packets and Protocols: Online games communicate through a packet-switched network, like the Internet, where communications are broken up into small data units, called packets, which are then transmitted through the network from the sender and reassembled on the other side by the receiver.

How do video games stay in sync?

Clients are only connected to each other through the server itself, which operates as a relay between them. Collections of data are sent in discrete chunks to and from each client to keep them updated as the game unfolds.

How multiplayer game works on server side?

A game server (also sometimes referred to as a host) is a server which is the authoritative source of events in a multiplayer video game. The server transmits enough data about its internal state to allow its connected clients to maintain their own accurate version of the game world for display to players.


1 Answers

This is a very complex task you are working on and something I've done as part of a pet project ;)

You're working on a client-server architecture game so the server is final authority on game logic and decisions. They way you are currently handling rendering will make sudden changes in velocity and direction apparent due to latency (as you have noticed!)

The trick is to buffer movement information of remote players so you always render player with a slight delay. I kept things primitive in my project and used only positional data, not acceleration or velocity. Example, when player A moves on his machine a command is not instantly sent to receive acknowledgement, he moves and on the next tick of my networking send loop (10 ticks per second) his position is fired to the server who updates all clients in the vicinity with this new position. These clients have a buffer for each "remote" player which stores each position for a time (100 milliseconds) before rendering the update. In this way the client is rendered with a slight delay but I can interpolate (smooth the transition of the sprite/model) between each positional co-ordinate to achieve smooth motion with the illusion of velocity and acceleration.

A BASIC interpolation function for client code. This system only queued two updates for each remote player at most, where index 0 in the update array was the older of two. So index 0 might be the remote player position 0ms and index 1 is the remote player position at 100ms.

interpolate: function() {
  var timeDifference = new Date().getTime() - this.serverUpdates[1].time;
  // Percentage of time passed since update was received (I use 100ms gaps)
  var interPercent = (timeDifference) / 100;

  // Latest updates (index 1 is the newest state)
  var s1 = this.serverUpdates[0],
    s2 = this.serverUpdates[1];

  // Need to lerp between values provided in latest update and older one
  var p = (new Vector3(s2.position)).subtract(new Vector3(s1.position));
  p = p.timesScalar(interPercent);

  // New position is the older lerped toward newer position where lerp 
  //percentage is the time passed 
  this.position = new Vector3(s1.position).add(p);

  // Now update rotation in a smooth manner
  var rotationDifference = (s2.rotation - s1.rotation);
  if (rotationDifference && rotationDifference != 0) {
    this.rotation = s1.rotation + (rotationDifference * interPercent);
  }
},

In that code I was receiving updates that were approx 100ms apart, so at time 0 position is s1 and time 100ms s2 is the position. So if 50ms have passed since we received s2 then the entity is 50% between the two positions. This was fine for my need but may not work out in other types of games or might need tweaking.

These resources are an excellent start to explain networked games and dealing with latency, you'll be amazed at the difference implementing interpolation and extrapolation will have on your game smoothness in remote clients.

http://gafferongames.com/networking-for-game-programmers/what-every-programmer-needs-to-know-about-game-networking/

https://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/Server_In-game_Protocol_Design_and_Optimization <- REALLY GOOD!

https://developer.valvesoftware.com/wiki/Lag_compensation

Good luck :)

like image 129
Evan Shortiss Avatar answered Sep 29 '22 20:09

Evan Shortiss