Understanding Network Movement Handling rrrrrrrrrrr....

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
Linaxys
Posts: 47
Joined: Tue Feb 24, 2009 10:46 pm

Understanding Network Movement Handling rrrrrrrrrrr....

Post by Linaxys »

Hello,
I am trying to handle a player's movement, for a 3D Chat.
I have thought of something like this : http://img16.imageshack.us/img16/5431/schemat.png

Now the client sends his position every 1 second, with his timeElapsed value.

timeDelta is (currentTime - lastTime) / 1000.
The client calculates it's position locally like this :

Code: Select all

if (keyState & PacketClass::PK_FORWARD) {
	pos += movedir * timeDelta * 50.f;
} else if (keyState & PacketClass::PK_BACK) {
	pos -= movedir * timeDelta * 50.f;
}
if (keyState & PacketClass::PK_RIGHT) {
	relativeRotation.Y += 25.f * timeDelta;
} else if (keyState & PacketClass::PK_LEFT) {
	relativeRotation.Y -= 25.f * timeDelta;
}

if (timeToUpdate < Engine->getTime()) {
	PacketClass velPck(PacketClass::PT_PLAYER_POSITION_UPDATE);
	velPck.WriteFloat(Cynum->timeDelta);
	velPck.WriteFloat(pos.X);
	velPck.WriteFloat(pos.Y);
	velPck.WriteFloat(pos.Z);
	velPck.WriteFloat(relativeRotation.Y);
	Client->sendPacket(&velPck);
	timeToUpdate = Engine->getTime() + 1000;
}
Now, the server receives the position, with the client's timeElapsed var, it calculates locally the player's position and sends it back.

timeStamp is the timeElapsed sent by the client, inside the newDelta var.

Code: Select all

f32 newDelta = TPacket->ReadFloat();
f32 pX = TPacket->ReadFloat();
f32 pY = TPacket->ReadFloat();
f32 pZ = TPacket->ReadFloat();
f32 rY = TPacket->ReadFloat();

timeStamp = newDelta;

//
// Server loop, calculating player's position, from the keys he is pressing down.
//

if (keyState & PacketClass::PK_FORWARD) {
	vector3df rot = node->getRotation();
	vector3df pos = node->getPosition();
	pos += rot.rotationToDirection() * 50.f * timeStamp;
	node->setPosition(pos);
} else if (keyState & PacketClass::PK_BACK) {
	vector3df rot = node->getRotation();
	vector3df pos = node->getPosition();
	pos -= rot.rotationToDirection() * 50.f * timeStamp;
	node->setPosition(pos);
}

if (keyState & PacketClass::PK_RIGHT) {
	vector3df rot = node->getRotation();
	rot.Y += 25.f * timeStamp;
	node->setRotation(rot);
} else if (keyState & PacketClass::PK_LEFT) {
	vector3df rot = node->getRotation();
	rot.Y -= 25.f * timeStamp;
	node->setRotation(rot);
}

if (positionUpdateTime < Server->getTime()) {
	// Time to send the new player's position.
	PacketClass upPck(PacketClass::PT_PLAYER_POSITION_UPDATE);
	upPck.WriteInt(ID);
	upPck.WriteFloat(timeStamp);
	upPck.WriteFloat(node->getPosition().X);
	upPck.WriteFloat(node->getPosition().Y);
	upPck.WriteFloat(node->getPosition().Z);
	upPck.WriteFloat(node->getRotation().Y);
	Server->broadcastPacket(&upPck);

	positionUpdateTime = Server->getTime() + 1000;
}
Then... What happens...

As a client, I press on the forward key for about... 100ms,
and the server tells that I am at X : 0, Y : 15, Z : 54874.4875221.............

But my camera was at X : 0, Y : 15, Z : 8.52000.......

Can anyone tell me what I am doing wrong please ?
Thanks.
Cloudef
Posts: 123
Joined: Wed Dec 03, 2008 9:02 pm
Location: Finland

Post by Cloudef »

Are you trying to send your client's timer/delta to server?
They "can't" be synchorized afaik your Client timer is different than Server unless you start them on same time on same machine.
Also IMO its not good idea to send positions from client, but from server so everyone has the real position when the positions get updated.

Tho sending client position can still be done if you don't send it to other clients because this allows cheating. But sending client absolute position is usually just waste of bandwidth.

Anyways, there is many ways to code movement in FPS game, but what i would do is sent the Client state everytime it changes and do sametime the client prediction (Apply the states virtually, move the client even you haven't got answer from server) and if server does not accept the new state revert back to where you sent the states.

Then when you get the Real position packet from server you should start looking for interpolation in google to get smooth transition from your current position to real position. Also there is many techniques when and how to send the real position.

If client hasn't moved why brother sending? Or the state change was only minor then why send again? Also should you send in some tick rate? Or just when character has finished processing the state?

The number 1 rules are
1) Don't trust client.
2) Cheat as possible in coding to make things smooth and almost "real", as the positions in every client will not be exactly same as in other clients, but this is only a minor issue as long as it does not affect the gameplay.

But after reading your post again, i think i'm wrong what you are trying to actually do, sorry :cry:
Linaxys
Posts: 47
Joined: Tue Feb 24, 2009 10:46 pm

Post by Linaxys »

Hello Cloudef,
Thanks for your reply.

I don't still get it... I think the server calculates faster than the client, since it uses a NULL driver and doesn't render anything at all.

Let's suppose that the client has a timeDelta of 0.092222 and the server has a timeDelta of 0.001.

The client can predict it's own movement with it's own timeDelta.
But the server should put him far far away...

I though of simply forcing the client's timeDelta of 0.001 but will the game become FPS-dependant, since the timeDelta won't be related to the game's elapsed time each loop...

Well, I got some questions that confuses me.

:arrow: What interval should I use for client's position/velocity update ? 1 sec ? 100ms ? 500ms ?

:arrow: Should my server just send the client's velocity instead it's fixed position ?

My velocity calculation looks like this :

Code: Select all

// Pressing FORWARD KEY.
if (forwardKeyDown) {
     if (vel.Z < 100.f) {
          vel.Z += 200.f * timeDelta;
     } else {
          vel.Z = 100.f;
     }
} else {
     if (vel.Z > 0.0f) {
          vel.Z -= 400.f * timeDelta;
          if (vel.Z < 0.0f)
               vel.Z = 0.0f;
     } else if (vel.Z < 0.0f) {
          vel.Z += 400.f * timeDelta;
          if (vel.Z > 0.0f)
               vel.Z = 0.0f;
     }
}
Would the server use it's own timeDelta or the client's timeDelta previously sent ?

Thanks a lot, I'd really like to understand it once forever.
Cloudef
Posts: 123
Joined: Wed Dec 03, 2008 9:02 pm
Location: Finland

Post by Cloudef »

You use server own delta, for server. And client delta for client. That's it, that simple :P
You just have to predict the movement so it would be "right" position in server and client.
wITTus
Posts: 167
Joined: Tue Jun 24, 2008 7:41 pm
Location: Germany

Post by wITTus »

Linaxys wrote:What interval should I use for client's position/velocity update ? 1 sec ? 100ms ? 500ms ?
Interval = 1000.0f / UpdatesPerSecond;

Make UpdatesPerSecond modifiable at runtime and experiment with it.
Linaxys wrote:Should my server just send the client's velocity instead it's fixed position ?
I send fixed positions and interpolate between them. I really don't know what the delta is good for, but many people do it that way. Check these links out:

Source Multiplayer Networking
Lag Compensation
Networking with Physic Engines and Smooth Movement Thread

This is really hard stuff IMO. Make sure to put a lot of debug code in it. :twisted:
Generated Documentation for BlindSide's irrNetLite.
"When I heard birds chirping, I knew I didn't have much time left before my mind would go." - clinko
Linaxys
Posts: 47
Joined: Tue Feb 24, 2009 10:46 pm

Post by Linaxys »

Hmm, okay thanks guys, I see clearlier now... :)
Linaxys
Posts: 47
Joined: Tue Feb 24, 2009 10:46 pm

Post by Linaxys »

Hmmm another thing that I don't understand.

You know, I am at position (0,0,50). The server tells me that I am at position (0,0,70).

So, to make a very smooth movement, I apply a velocity at each update, very small so it doesn't look like a teleport.

When I receive the UPDATE packet with my new coordinates, I set my camera like this :

Code: Select all

Camera->velocityFix = vector3df(newPosX - oldPosX, newPosY - oldPosY, newPosZ - oldPosZ);
And this is my camera loop :

Code: Select all

if (velocityFix.X != 0.f || velocityFix.Y != 0.f || velocityFix.Z != 0.f) {
		if (velocityFix.X < 0.0f) {
			velocityFix.X += 5.f * timeDelta;
			if (velocityFix.X > 0.0f)
				velocityFix.X = 0.0f;
		} else if (velocityFix.X > 0.0f) {
			velocityFix.X -= 5.f * timeDelta;
			if (velocityFix.X < 0.0f)
				velocityFix.X = 0.0f;
		}
		if (velocityFix.Y < 0.0f) {
			velocityFix.Y += 5.f * timeDelta;
			if (velocityFix.Y > 0.0f)
				velocityFix.Y = 0.0f;
		} else if (velocityFix.Y > 0.0f) {
			velocityFix.Y -= 5.f * timeDelta;
			if (velocityFix.Y < 0.0f)
				velocityFix.Y = 0.0f;
		}
		if (velocityFix.Z < 0.0f) {
			velocityFix.Z += 5.f * timeDelta;
			if (velocityFix.Z > 0.0f)
				velocityFix.Z = 0.0f;
		} else if (velocityFix.Z > 0.0f) {
			velocityFix.Z -= 5.f * timeDelta;
			if (velocityFix.Z < 0.0f)
				velocityFix.Z = 0.0f;
		}
		pos += velocityFix * timeDelta;
}
But unfortunately, it goes totally wrong...
I mean, when I move, I receive the update one second later.
I come back directly to the last position I was (0,0,50) and I move slowly to (0,0,59), one second later, I come back to (0,0,50), and I move slowly to (0,0,65)... Until I move slowly to (0,0,70)...

Can anyone help me please ?

Thanks !
Linaxys
Posts: 47
Joined: Tue Feb 24, 2009 10:46 pm

Post by Linaxys »

Alright, I've just found how to fix it.
http://irrlicht.sourceforge.net/phpBB2/ ... terpolated

Can someone give me a hint for the rotation smoothing please ?

Thanks.
teamAlpha
Posts: 68
Joined: Sun Mar 08, 2009 4:14 pm

Post by teamAlpha »

Im no expert at math/3d graphics/physics/network etc , but i've been wondering ...can it be done like this :
?


Code: Select all

client.send(ENUM_INC);
client.send(ENUM_X);
client.send(delta)
client.send(X)
client.send(Y)
client.send(Z)
Then the server interprets it as :

Code: Select all

r = server.read

switch(r)
case ENUM_INC
{
	read() - next = axis type
	switch axis
		increase axis x by velocity
	keep reading
	
	now apply delta time to position...
}
To handle rotation :

Code: Select all

client.send(ENUM_INC);
client.send(ENUM_RX);
client.send(delta)
client.send(X)
client.send(Y)
client.send(Z)

Code: Select all

r = server.read

switch(r)
case ENUM_INC
{
	read() - next = axis type
	switch axis
		increase axis x by velocity
	keep reading
	
	now apply delta time to rotation...
}
Alpha::AppleBoy
Post Reply