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

  1. Only timestamp when needed: Timestamps add overhead. Use them for time-critical data.

  2. Don’t timestamp reliable ordered packets: They may be delayed significantly, making timestamps less useful.

  3. Validate timestamps: Reject packets with timestamps too far in the future.

  4. 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