Timestamping¶
Timestamps allow synchronization of game events across the network, accounting for latency.
Why Use Timestamps¶
Without timestamps, you can’t know when an event actually occurred on the remote system. With timestamps, you can:
Interpolate/extrapolate entity positions
Synchronize game events (e.g., explosions)
Implement lag compensation
Calculate packet age
How Timestamps Work¶
MafiaNet automatically converts timestamps to the receiver’s local time using clock synchronization. When you read a timestamp, it’s already in your local time frame.
Sending Timestamped Packets¶
Timestamps must be the first data in the packet:
MafiaNet::BitStream bs;
// Timestamp must come first
bs.Write((MafiaNet::MessageID)ID_TIMESTAMP);
bs.Write(MafiaNet::GetTime());
// Then your message ID and data
bs.Write((MafiaNet::MessageID)ID_PLAYER_POSITION);
bs.Write(position.x);
bs.Write(position.y);
bs.Write(position.z);
peer->Send(&bs, HIGH_PRIORITY, UNRELIABLE_SEQUENCED, 0, addr, true);
Receiving Timestamped Packets¶
MafiaNet::BitStream bs(packet->data, packet->length, false);
MafiaNet::MessageID firstByte;
bs.Read(firstByte);
MafiaNet::Time timestamp = 0;
bool hasTimestamp = (firstByte == ID_TIMESTAMP);
if (hasTimestamp) {
bs.Read(timestamp);
bs.Read(firstByte); // Read actual message ID
}
// Process based on message type
switch (firstByte) {
case ID_PLAYER_POSITION: {
float x, y, z;
bs.Read(x);
bs.Read(y);
bs.Read(z);
if (hasTimestamp) {
// Calculate how old this position is
MafiaNet::Time age = MafiaNet::GetTime() - timestamp;
// Extrapolate position based on age
ExtrapolatePosition(x, y, z, velocity, age);
}
break;
}
}
Clock Synchronization¶
MafiaNet synchronizes clocks automatically using ping packets. The synchronization is approximate (within a few milliseconds).
Get the clock offset to a remote system:
// Offset from your clock to remote clock
MafiaNet::Time offset = peer->GetClockDifferential(remoteAddress);
Practical Example: Position Interpolation¶
struct NetworkedEntity {
MafiaNet::Time lastUpdateTime;
float x, y, z;
float velX, velY, velZ;
void OnNetworkUpdate(float newX, float newY, float newZ,
float newVelX, float newVelY, float newVelZ,
MafiaNet::Time packetTime) {
// Calculate packet age
MafiaNet::Time age = MafiaNet::GetTime() - packetTime;
// Extrapolate position to current time
float ageSeconds = age / 1000.0f;
x = newX + newVelX * ageSeconds;
y = newY + newVelY * ageSeconds;
z = newZ + newVelZ * ageSeconds;
velX = newVelX;
velY = newVelY;
velZ = newVelZ;
lastUpdateTime = MafiaNet::GetTime();
}
void Update(float deltaTime) {
// Continue extrapolating between updates
x += velX * deltaTime;
y += velY * deltaTime;
z += velZ * deltaTime;
}
};
Lag Compensation Example¶
For a shooting game with lag compensation:
void OnShootReceived(MafiaNet::RakNetGUID shooter,
float aimX, float aimY, float aimZ,
MafiaNet::Time shootTime) {
// Calculate when the shot was fired
MafiaNet::Time age = MafiaNet::GetTime() - shootTime;
// Rewind game state to that time
GameState historicState = GetHistoricState(age);
// Perform hit detection at that point in time
if (CheckHit(shooter, aimX, aimY, aimZ, historicState)) {
ApplyDamage(historicState.hitPlayer);
}
}
Best Practices¶
Only timestamp when needed: Timestamps add overhead. Use them for time-critical data.
Don’t timestamp reliable ordered packets: They may be delayed significantly, making timestamps less useful.
Validate timestamps: Reject packets with timestamps too far in the future.
Use for interpolation: Timestamps are most valuable for smooth movement.
// Validate timestamp isn't in the future (with tolerance)
MafiaNet::Time now = MafiaNet::GetTime();
if (timestamp > now + 1000) { // More than 1 second in future
// Suspicious - possible cheating or clock issue
return;
}
See Also¶
Creating Packets - Adding timestamps to packets
Reliability Types - When to use timestamps