I implement prediction in my game as follows (I'm probably doing something extremely stupid):
struct InputPacket
{
private byte _id = 0;
bool forward; // > 0
bool forward_zero; // == 0
bool right; // > 0
bool right_zero; // == 0
float pitch;
float yaw;
public InputPacket(byte id, int forward, int strafe, float pitch, float yaw, bool jumpDown)
{
_id = id;
this.forward = forward > 0;
this.forward_zero = forward == 0;
this.right = strafe > 0;
this.right_zero = strafe == 0;
this.jumping = jumpDown;
this.pitch = pitch;
this.yaw = yaw;
}
public Packet CreatePacket()
{
// creates a packet...
}
}
vector3 pastpositions[256];
mainLoop() // 60 FPS
{
if(shouldUpdate) // input is sampled at 30 FPS
{
input.sample();
int forward = 0;
int right = 0;
if(input.forward.down)
forward++;
if(input.back.down)
forward--;
if(input.strafe_right.down)
strafe++;
if(input.strafe_left.down)
strafe--;
// set player input
playerObj.Forward = forward;
playerObj.Strafe = right;
// set player direction
playerObj.Yaw = camera.yaw;
playerObj.Pitch = camera.pitch;
playerObj.ForwardMove = camera.forwardmove;
playerObj.RightMove = camera.rightmove;
}
// goes before because some values may be changed
if(shouldUpdate)
sendMessage(new InputPacket(...).CreatePacket());
// exact same code as the server
playerObj.Update(elapsedTime);
if(shouldUpdate)
{
pastpositions[counter++] = playerObj.Position;
}
}
receivePos(byte id, Vector3 pos)
{
if(pastpositions[id] != pos)
{
// client will interpolate position to match this
playerObj.ServerPos = pos;
}
}
This works pretty well, except every single time, the prediction is off from 0 to at most, 1 (units). The error increases with ping, and I get around 0.3 units off at 50ms.
After reading the Valve documentation though, it seems that they have it right most of the time. I strongly suspect that what's causing the error for me is that the timing on the server/client may be different.
For example, say the client starts moving at time t = 0, and stops moving at time t = 1. The client thinks it has moved for 1t, and advances the position as it should. However, due to network latency, the first packet may arrive at t=1, and the packet that says "I stopped moving" may arrive at t = 2.25. In the server's eyes, the client moved 1.25 seconds, and thus an error has been produced.
I noticed that Valve includes a duration in their packet, but wouldn't this make the prediction off if the FPS is variable? Seeing as many players don't have a constant FPS, it boggles my mind as to how they rarely get prediction errors.
Even if I were to use the time of the last frame (as Valve states they do), how could I split that time between the player updates? As you can see, my update is sampled at 30 FPS, and my player is updated at 60 FPS, and therefore, I have to somehow intelligently split the time between the extra frames. I guess what worries me the most is that a changing FPS will royally screw over all my guesswork.
Thanks in advance!