Winsock - laggy movements

closed account (2NywAqkS)
I've made a nice multi-client server game using Winsock however the movements are quite laggy. My current set-up is:

At regular intervals the server send out the
info of all clients to all the clients.
e.g coords, direction, state.
|
The clients receive the data and update their
own data and draw it.
|
On every key press and release the clients
send to the server data on which keys are
being pressed
|
The server uses the client keyboard information
to move the players and detect collisions and stuff

Obviously this leads to a bit of lag but what seems to happen is when you press
a key the character moves a bit then after a delay starts moving at at regular intervals as if the movement was in a WM_KEYDOWN case. Although this is the
problem I would mainly like replies on, general feedback is appreciated. I've uploaded some of the source code I believe is relevant;

---------Server---------
This is how it updates at regular intervals:
1
2
3
4
5
6
if (GetCurrentTime() - lfTime > 100/6)
		{
			for (int i = 0; i < playerVector.size(); i++) if (!playerVector[i].dead) playerVector[i].Calculate(i);
			UpdateClient();
			lfTime = GetCurrentTime();
		}

The update client function:
1
2
3
4
5
6
7
8
9
10
11
12
13
void UpdateClient()
{
	for (unsigned int i = 0; i < playerVector.size(); i++)
	{
		std::stringstream stream;
		stream << playerVector.size();
		for (unsigned int j = 0; j < playerVector.size(); j++)
		{
			stream << "," << j << "," << playerVector[j].x << "," << playerVector[j].y << "," << playerVector[j].dir << "," << playerVector[j].dead << "|";
		}
		send(playerVector[i].socket, stream.str().c_str(), stream.str().length()+1, 0);
	}
}

in FD_READ this is how it updates itself:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void UpdateServer()
{
	for(int n=0;n<=nMaxClients;n++)
	{
		char szIncoming[1024];
		ZeroMemory(szIncoming,sizeof(szIncoming));
		int inDataLength = recv(playerVector[n].socket,(char*)szIncoming, sizeof(szIncoming),0);
		if(inDataLength!=-1)
		{
			char * comma1 = strchr(szIncoming,',');
			int ID = atoi(szIncoming);
			char *buffer = (comma1 + 1);
			for(unsigned int i = 0; i < 256; i++) playerVector[ID].keys[i] = (buffer[i]=='1');
		}
	}
}

How it calculates movement:
1
2
3
4
5
6
7
8
9
10
11
12
13
// Move
	if (keys['W'] || keys[VK_UP])y += 1;
	if (keys['S'] || keys[VK_DOWN]) y -= 1;
	if (keys['A'] || keys[VK_LEFT])
	{
		x -= 2;
		dir = 0;
	}
	if (keys['D'] || keys[VK_RIGHT])
	{
		x += 2;
		dir = 1;
	}

---------Client---------
When it updates the server on new key states:
1
2
3
4
5
6
7
8
9
10
11
12
13
case WM_KEYDOWN:
	  {
		  keys[wParam] = true;
		  UpdateServer();
		  return 0;
	  }

  case WM_KEYUP:
	  {
		  keys[wParam] = false;
		  UpdateServer();
		  return 0;
	  }

How it updates the server:
1
2
3
4
5
6
7
void UpdateServer()
{
	std::stringstream stream;
	stream << ID << ",";
	for (int i = 0; i < 256; i++) stream << keys[i];
	send(Socket, stream.str().c_str(), stream.str().length()+1, 0);
}

In FD_READ it updates itself like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
char * srlIncoming = szIncoming;
		playerVector.resize(atoi(srlIncoming), Player());
		for (unsigned int i = 0; i < playerVector.size(); i++)
		{
			char * comma1 = strchr(srlIncoming,',');
			char * comma2 = strchr(comma1 + 1,',');
			char * comma3 = strchr(comma2 + 1,',');
			char * comma4 = strchr(comma3 + 1,',');
			char * comma5 = strchr(comma4 + 1,',');

			int n = atoi(comma1 + 1);
			playerVector[n].id = n;
			playerVector[n].x = atof(comma2 + 1);
			playerVector[n].y = atof(comma3 + 1);
			playerVector[n].dir = atof(comma4 + 1);
			playerVector[n].dead = atof(comma5 + 1);
			srlIncoming = strchr(srlIncoming,'|') + 1;
		}


Thanks for taking the time to read this. If you need any more source just ask. On an unrelated not how would I deal with something like bullets in this set-up?

Thanks,
Rowan.
Last edited on
The way your game is designed, there will always be lag. You might think it's a good idea to run a single instance of the game on the server and have the clients just do IO, but this doesn't work in practice.

Each client needs to run an instance of the game just like the server. The clients still send their keypresses (or higher-level actions) to the server, but they also use the keys to process their instances of the game.

Every so often, the server sends a copy of the game's state to all the clients. There are many ways that the clients can handle this. The simplest is just to move all entities to their "correct" positions, but this will cause things to jump around whenever packets arrive and stand still in between.

The easiest "correct" implementation is for the client to assume that other players continue to hold the keys the server sent to them until the next packets arrive. For example, if player 2 was holding the W key when it contacted the server, player 1 will see him moving forward.
closed account (2NywAqkS)
Thanks for the reply, sorry mine was a slow one. Okay, so, I've set it up so the server updates the players with commands & coords and in the meantime they guess based on the latest commands. The server is also working out the positions... only, the server is wrong. It only seems to calculate the new position on every update from a client. which happens at the WM_KEYDOWN (and UP) msg.

1
2
3
4
5
6
7
8
9
10
// Client
case WM_KEYDOWN:
	  {
		  if (wParam == 'W' || wParam == VK_UP) com = UPp;
		  if (wParam == 'S' || wParam == VK_DOWN) com = DOWNp;
		  if (wParam == 'A' || wParam == VK_LEFT) com = LEFTp;
		  if (wParam == 'D' || wParam == VK_RIGHT) com = RIGHTp;
		  UpdateServer();
		  return 0;
	  }


This leads to problems because, as you probably know, WM_KEYDOWN doesn't happen at regular intervals. So essentially the server calculates at a different rate to the clients leading to lots of jumpiness.

Why does the server only calculate on receiving a msg? I thought with this it should update 60 times a second.

1
2
3
4
5
6
7
8
9
10
11
12
// Server
while(GetMessage(&msg,NULL,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		if (GetCurrentTime() - lfTime > 100/6)
		{
			for (int i = 0; i < playerVector.size(); i++) if (!playerVector[i].dead) playerVector[i].Calculate(i);
			UpdateClient();
			lfTime = GetCurrentTime();
		}
	}


thanks, again.
Last edited on
Topic archived. No new replies allowed.