1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-24 15:24:30 +01:00

Checksum sprites occasionally in multiplayer

This creates a checksum (SHA1) every so often on server and sends this
value together with PRNG seed for client to check it has still not
desynced.

It's useful to detect a desync early on, as PRNG seeds may remain
unchanged for some more time, while damage may have already been caused.
This commit is contained in:
Michał Janiszewski
2016-07-24 21:32:57 +02:00
parent d8d39c1ade
commit 1fe5fc56c0
6 changed files with 107 additions and 2 deletions

View File

@@ -586,7 +586,10 @@ bool Network::CheckSRAND(uint32 tick, uint32 srand0)
if (tick == server_srand0_tick) {
server_srand0_tick = 0;
if (srand0 != server_srand0) {
// Check that the server and client sprite hashes match
const bool sprites_mismatch = server_sprite_hash[0] != '\0' && strcmp(sprite_checksum(), server_sprite_hash);
// Check PRNG values and sprite hashes, if exist
if ((srand0 != server_srand0) || sprites_mismatch) {
return false;
}
}
@@ -1081,6 +1084,22 @@ void Network::Server_Send_TICK()
last_tick_sent_time = SDL_GetTicks();
std::unique_ptr<NetworkPacket> packet = std::move(NetworkPacket::Allocate());
*packet << (uint32)NETWORK_COMMAND_TICK << (uint32)gCurrentTicks << (uint32)gScenarioSrand0;
uint32 flags = 0;
// Simple counter which limits how often a sprite checksum gets sent.
// This can get somewhat expensive, so we don't want to push it every from in release,
// but debug version can check more often.
static int checksum_counter = 0;
checksum_counter++;
if (checksum_counter >= 100) {
checksum_counter = 0;
flags |= NETWORK_TICK_FLAG_CHECKSUMS;
}
// Send flags always, so we can understand packet structure on the other end,
// and allow for some expansion.
*packet << flags;
if (flags & NETWORK_TICK_FLAG_CHECKSUMS) {
packet->WriteString(sprite_checksum());
}
SendPacketToClients(*packet);
}
@@ -1739,10 +1758,23 @@ void Network::Server_Handle_GAMECMD(NetworkConnection& connection, NetworkPacket
void Network::Client_Handle_TICK(NetworkConnection& connection, NetworkPacket& packet)
{
uint32 srand0;
packet >> server_tick >> srand0;
uint32 flags;
// Note: older server version may not advertise flags at all.
// NetworkPacket will return 0, if trying to read past end of buffer,
// so flags == 0 is expected in such cases.
packet >> server_tick >> srand0 >> flags;
if (server_srand0_tick == 0) {
server_srand0 = srand0;
server_srand0_tick = server_tick;
server_sprite_hash[0] = '\0';
if (flags & NETWORK_TICK_FLAG_CHECKSUMS)
{
const char* text = packet.ReadString();
if (text != nullptr)
{
safe_strcpy(server_sprite_hash, text, sizeof(server_sprite_hash));
}
}
}
}