Multiplayer movements

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
User avatar
superLED
Chaos Rift Junior
Chaos Rift Junior
Posts: 303
Joined: Sun Nov 21, 2010 10:56 am
Current Project: Engine
Favorite Gaming Platforms: N64
Programming Language of Choice: C++, PHP
Location: Norway

Multiplayer movements

Post by superLED »

This is a topic that have made me avoid going further with network programming.
I simply don't know how to send the information in an effective way.

Let's say I have two players, and I want to send the information about player1 to the player2. Do I constantly send the placement (x and y coordinate) when he is moving? Or do I only send the keyboard inputs?
Or do I do something completely different?
And the same with which state in the animation the player is in. Do I constantly send that information too?

I have tried to constantly send the coordinates to the other player, but it slows up after a while, since the server can't keep up.
I have also tried to only send information when a key is pressed down. That tends to slow down as well
(I am moving my player1, but in the other window, that player's speed is like 80% of the original speed, because the server can't send the information as fast as the client sends information to itself)

It is a 2D 'game' with only two squares - one for each player.
Right now, I'm only dealing with x and y position.
MattSmith
ES Beta Backer
ES Beta Backer
Posts: 7
Joined: Fri Apr 06, 2012 5:33 pm

Re: Multiplayer movements

Post by MattSmith »

Well, it depends what kind of multiplayer you are developing.

If you'd like to do mmo then it would be near impossible to send all the packets for every player to every other player and so forth. This is why you'd send the data to the server. And the server sends the necessary data back.

With a 2 player multiplayer I'd just make player 1 the server and send the data back and forth.

However, In both cases, I'd suggest making words into single digits and put a common format to each packet. For example:

Code: Select all

player1.pos=(10,10);
to words in packet:

Code: Select all

player1.pos.10.10
to numbers:

Code: Select all

1.0.10.10
and so forth for player2

Code: Select all

player2.pos=(10,10);
to words in packet:

Code: Select all

player2.pos.10.10
to numbers:

Code: Select all

2.0.10.10
or for a different piece of info about player 1:

Code: Select all

1.1. Any data here.. I suggest making it also into single bytes, or as small as possible. 

Hope this helped. Any more questions feel free to ask.

EDIT::
Also, forgot, only send packets when you need too. And don't send too many at one point in time. You'll find it'll bog everything down. Only send packets if it is necessary.
My name really is Matt Smith, I'm not anon.
User avatar
k1net1k
Chaos Rift Maniac
Chaos Rift Maniac
Posts: 563
Joined: Sun Nov 07, 2010 2:58 pm
Contact:

Re: Multiplayer movements

Post by k1net1k »

My only suggestion would be to send a representative key state to the server. Eg if the player is currently holding down UP and LEFT and Shooting you might send a byte like
[10101000]
[up,down,left,right,action1,action2,etc,etc]
server then processes each of these per player, per gamecycle/gameloop, and then sends out updates to each player who will need to be notified of these changes.

If player A moves up a bit, send out player A's new position to all players in a certain radius.
User avatar
Teser0ck
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 35
Joined: Mon Aug 29, 2011 12:59 pm
Current Project: Platformer game, Map editor, Particle system
Programming Language of Choice: C++
Location: Czech Republic

Re: Multiplayer movements

Post by Teser0ck »

A lot of games sends structures instead of formats, it looks like this :

Code: Select all

struct PlayerStartedMoving
{
     char type;// without this server couldn't recognize what structure are we sending.
     char dir;
}
struct PlayerStopped
{
     char type;
     short XPos;
     short YPos;
}

...
if( player_pressed_a_key_and_is_able_to_move )
{
     PlayerStartedMoving SendPacket;
     SendPacket.type = (char)1;
     SendPacket.dir = player.GetDir();

     send( socket, reinterpret_cast<char*>( &SendPacket ), sizeof( SendPacket ), 0 );
}
// and more
The server would work pretty much the same, but when receiving structures, it would first check the first byte of received data to find out what structure is it receiving, like this :

Code: Select all

char tempType;
recv( ..., (char*)tempType, 1, .. );// size is 1, because char is only 1 byte long

     if( tempType == (int)1 )
     {
          PlayerStartedMoving RecvPacket;
          .... // receive the rest of data into the packet
     }
As k1net1k said, you should send only important actions, so when player started moving to the left, you would send PlayerStartedMoving struct, where dir = 3 ( let's say it's left ). When he stopped it's better to send current position, so you can be sure there's no deviation.
MattSmith
ES Beta Backer
ES Beta Backer
Posts: 7
Joined: Fri Apr 06, 2012 5:33 pm

Re: Multiplayer movements

Post by MattSmith »

Teser0ck wrote:A lot of games sends structures instead of formats, it looks like this :

Code: Select all

struct PlayerStartedMoving
{
     char type;// without this server couldn't recognize what structure are we sending.
     char dir;
}
struct PlayerStopped
{
     char type;
     short XPos;
     short YPos;
}

...
if( player_pressed_a_key_and_is_able_to_move )
{
     PlayerStartedMoving SendPacket;
     SendPacket.type = (char)1;
     SendPacket.dir = player.GetDir();

     send( socket, reinterpret_cast<char*>( &SendPacket ), sizeof( SendPacket ), 0 );
}
// and more
The server would work pretty much the same, but when receiving structures, it would first check the first byte of received data to find out what structure is it receiving, like this :

Code: Select all

char tempType;
recv( ..., (char*)tempType, 1, .. );// size is 1, because char is only 1 byte long

     if( tempType == (int)1 )
     {
          PlayerStartedMoving RecvPacket;
          .... // receive the rest of data into the packet
     }
As k1net1k said, you should send only important actions, so when player started moving to the left, you would send PlayerStartedMoving struct, where dir = 3 ( let's say it's left ). When he stopped it's better to send current position, so you can be sure there's no deviation.
I agree with this, same path I was trying to explain.
My name really is Matt Smith, I'm not anon.
User avatar
LeonBlade
Chaos Rift Demigod
Chaos Rift Demigod
Posts: 1314
Joined: Thu Jan 22, 2009 12:22 am
Current Project: Trying to make my first engine in C++ using OGL
Favorite Gaming Platforms: PS3
Programming Language of Choice: C++
Location: Blossvale, NY

Re: Multiplayer movements

Post by LeonBlade »

If this is just two player, sending either delta changes or absolute values on moving shouldn't be too much for your code to handle.
With networking, you always want to try and figure out how you can cut down the amount of data being sent out and also limit the amount of times data is being sent out as well.

For example, that's why on a chatting site for example, webcam data might try to resample data in order to cut down the amount of data being sent through. This will allow to only send major changes to cut down on the amount of data coming through for each packet, and total packets in general.

First thing you can do with player movements and other things in games when sending any type of data would obviously be to serialize it. You can create one main Packet class that will serialize your data in and out and then parse it for whoever needs to receive it. When the packet comes in you can easily parse out the data and say, okay this packet ID is 3 so that means it was a player move packet and send it off to be processed by the client.

Another thing is obviously the server knows where the player is, so if anything is off screen you can easily just ignore it. In a 2D game this is easy enough depending on how much control over the screen you give the client. In a 3D game, you can either process packets from things withen a certain radius from your player, or perhaps certain sectors/zones/chunks surrounding you.

If you feel that you're sending too much through, and the server is being bogged down, you might want to try to do a form of "resample" for most packets while trying to cut down on jitter. By that, I mean, you can just send a packet that the player has started moving in a direction and ignore all changes that are only slight movements of X units.

In a senario where player A moves left on screen, you might be thinking oh great every game frame I'm going to update that and that's X packets over X time. But, instead if you only send out that they've moved in this direction you can send one packet when they start moving and another when they've stopped.

One problem with this is if you miss a packet for stopping then they'll be flying away across the world and maybe jump back later on when they update again, it's all a big pain. Instead, if you're okay with sending data in intervals, you can try to update every few seconds or every few whatever units to try and cut down on packets and not worrying about missing an important packet like stopping.

Just jumping back to the packet structure for a moment to explain this next part, let's say your packet structure looks like this:

Code: Select all

[packet_id] [arguments ...]
And let's say that movement might look like this:

Code: Select all

[packet_id] [entity_id] [x] [y]
So for this example, let's say that packet ID for movement is 3 and our entity ID is 2, alright? So, right now we can guess how many bytes this might be. You might say four bytes, that's possible, but you have to think about your min and max values. If you're okay with only having 256 possible entities, then one byte for entity_id is fine, but if you want to exceed that, you're going to have to increase the size. Two bytes will give you 65,535 possible entities, that might be too much or enough or whatever. You could borrow bits from the packet_id if you feel that you wont be needing 256 different IDs. If you're only going to use 16 then you can use 4 bits from the first byte for the ID from 0000 to 1111 (0 - 15) and then the other 4 bits can be part of the next argument.

In the case of entity IDs, you have 16 sets of 256 by using the 4 bits as a set index. This will result in giving you 4,096 possible entities instead of 256, but remember you sacrifice the amount of packet IDs you can have. Of course, you don't HAVE to just take half of the first byte, you can use maybe 5 or 6 bits for the ID and then leave yourself with 2 or 3 bits for the second half. Maybe you only want to sacrifice 2 bits from the packet ID to give you a total of 63 packets and then give you 4 sets of the next byte which means 1,024 possible entities. Or, maybe you don't care about that too much, and you're okay with using two bytes for entities, whatever works best for you.

Now, let's say though that you do want to keep your X and Y down. If you have a huge coordinate system, you're going to be screwed. You could use two bytes if you have only 65,535 units to move in on the screen in your map or level or whatever you're on.

You could either use a chunk system where you can limit yourself to 256 units per chunk then add a byte before X and Y for what chunk your in, but then you again limit yourself to 256 chunks which might be good enough for you, but maybe you dont want to worry about how many chunks you can have, or maybe you're going to have two bytes for chunks and that's two bytes too many!

The fix for that would be to do something like have a delta move packet and an absolute move packet. The delta move packet can just update the relative movement X and Y based on the current position you're in. This saves you from adding the chunk byte in, keeping your packet down to only 4 bytes for movement instead of 5 or more. If your player has moved more than 256 units in X and or Y, then you can do an absolute position which can take the chunk and the X and Y. What you would be aiming for with this is assuming that if your player teleported or something then you could use the absolute chunk move, but if you're just walking along side them, you can use delta/relative move packets because they're only going to be moving so much per update anyways.

Anyways, I think you get the idea of what I'm saying here. Hopefully this helps, I know some of these concepts were explained already by other people in here, but I figured I would go over them again and explain a few other things as well.

I hope this helps.
There's no place like ~/
User avatar
superLED
Chaos Rift Junior
Chaos Rift Junior
Posts: 303
Joined: Sun Nov 21, 2010 10:56 am
Current Project: Engine
Favorite Gaming Platforms: N64
Programming Language of Choice: C++, PHP
Location: Norway

Re: Multiplayer movements

Post by superLED »

Woah, it was a long reading session, but I got a lot of insight of what I am supposed to do. I really appreciate your time spent replying.
I am not sure how I'm going to send structs over the net. I have only been sending strings for now, but I think I will be able to send structs over the net after a little research.
(I am using SDL_net for the communication)

As a training project, I have started with a small game that's kind of a "board game" where you move from tiles to tiles (press 'Up' once, and it will move you automatically to the tile abow), like in the Pokemon games.
So there won't be much movement data send to the server and forward to the client.

While working on this project, I will constantly try to optimize the network part of my game engine, and get some good habits.
Post Reply