diff --git a/src/network/network.cpp b/src/network/network.cpp index 87c7fe955a..dd5bf2c8dc 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -1078,7 +1078,14 @@ void NetworkGameLoop() if (_network_server) { /* Log the sync state to check for in-syncedness of replays. */ - if (_date_fract == 0) DEBUG(desync, 1, "sync: %08x; %02x; %08x; %08x", _date, _date_fract, _random.state[0], _random.state[1]); + if (_date_fract == 0) { + /* We don't want to log multiple times if paused. */ + static Date last_log; + if (last_log != _date) { + DEBUG(desync, 1, "sync: %08x; %02x; %08x; %08x", _date, _date_fract, _random.state[0], _random.state[1]); + last_log = _date; + } + } #ifdef DEBUG_DUMP_COMMANDS /* Loading of the debug commands from -ddesync>=1 */ @@ -1086,22 +1093,34 @@ void NetworkGameLoop() static Date next_date = 0; static uint32 next_date_fract; static CommandPacket *cp = NULL; + static bool check_sync_state = false; + static uint32 sync_state[2]; if (f == NULL && next_date == 0) { DEBUG(net, 0, "Cannot open commands.log"); next_date = 1; } while (f != NULL && !feof(f)) { - if (cp != NULL && _date == next_date && _date_fract == next_date_fract) { - _current_company = cp->company; - bool ret = DoCommandP(cp->tile, cp->p1, cp->p2, cp->cmd, NULL, cp->text); - DEBUG(net, 0, "injecting: %08x; %02x; %02x; %06x; %08x; %08x; %08x; \"%s\" (%s) -> %i", _date, _date_fract, (int)_current_company, cp->tile, cp->p1, cp->p2, cp->cmd, cp->text, GetCommandName(cp->cmd), (int)ret); - assert(ret); - free(cp); - cp = NULL; + if (_date == next_date && _date_fract == next_date_fract) { + if (cp != NULL) { + NetworkSend_Command(cp->tile, cp->p1, cp->p2, cp->cmd & ~CMD_FLAGS_MASK, NULL, cp->text, cp->company); + DEBUG(net, 0, "injecting: %08x; %02x; %02x; %06x; %08x; %08x; %08x; \"%s\" (%s)", _date, _date_fract, (int)_current_company, cp->tile, cp->p1, cp->p2, cp->cmd, cp->text, GetCommandName(cp->cmd)); + free(cp); + cp = NULL; + } + if (check_sync_state) { + if (sync_state[0] == _random.state[0] && sync_state[1] == _random.state[1]) { + DEBUG(net, 0, "sync check: %08x; %02x; match", _date, _date_fract); + } else { + DEBUG(net, 0, "sync check: %08x; %02x; mismatch expected {%08x, %08x}, got {%08x, %08x}", + _date, _date_fract, sync_state[0], sync_state[1], _random.state[0], _random.state[1]); + NOT_REACHED(); + } + check_sync_state = false; + } } - if (cp != NULL) break; + if (cp != NULL || check_sync_state) break; char buff[4096]; if (fgets(buff, lengthof(buff), f) == NULL) break; @@ -1128,12 +1147,23 @@ void NetworkGameLoop() int ret = sscanf(p + 6, "%x; %x", &next_date, &next_date_fract); assert(ret == 2); DEBUG(net, 0, "injecting pause for join at %08x:%02x; please join when paused", next_date, next_date_fract); - cp = MallocT(1); + cp = CallocT(1); cp->company = COMPANY_SPECTATOR; cp->cmd = CMD_PAUSE; cp->p1 = PM_PAUSED_NORMAL; cp->p2 = 1; _ddc_fastforward = false; + } else if (strncmp(p, "sync: ", 6) == 0) { + int ret = sscanf(p + 6, "%x; %x; %x; %x", &next_date, &next_date_fract, &sync_state[0], &sync_state[1]); + assert(ret == 4); + check_sync_state = true; + } else if (strncmp(p, "msg: ", 5) == 0 || strncmp(p, "client: ", 8) == 0 || + strncmp(p, "load: ", 6) == 0 || strncmp(p, "save: ", 6) == 0) { + /* A message that is not very important to the log playback, but part of the log. */ + } else { + /* Can't parse a line; what's wrong here? */ + DEBUG(net, 0, "trying to parse: %s", p); + NOT_REACHED(); } } if (f != NULL && feof(f)) { diff --git a/src/network/network_command.cpp b/src/network/network_command.cpp index 15a67b957e..96b400aa3e 100644 --- a/src/network/network_command.cpp +++ b/src/network/network_command.cpp @@ -127,6 +127,24 @@ void NetworkSend_Command(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, Comma SEND_COMMAND(PACKET_CLIENT_COMMAND)(&c); } +/** + * Sync our local command queue to the command queue of the given + * socket. This is needed for the case where we receive a command + * before saving the game for a joining client, but without the + * execution of those commands. Not syncing those commands means + * that the client will never get them and as such will be in a + * desynced state from the time it started with joining. + * @param cs The client to sync the queue to. + */ +void NetworkSyncCommandQueue(NetworkClientSocket *cs) +{ + for (CommandPacket *p = _local_command_queue; p != NULL; p = p->next) { + CommandPacket c = *p; + c.callback = 0; + NetworkAddCommandQueue(c, cs); + } +} + /** * Execute all commands on the local command queue that ought to be executed this frame. */ diff --git a/src/network/network_internal.h b/src/network/network_internal.h index ef1030a9ea..3131cc10e4 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -163,6 +163,7 @@ struct CommandPacket : CommandContainer { void NetworkAddCommandQueue(CommandPacket cp, NetworkClientSocket *cs = NULL); void NetworkExecuteLocalCommandQueue(); void NetworkFreeLocalCommandQueue(); +void NetworkSyncCommandQueue(NetworkClientSocket *cs); /* from network.c */ NetworkRecvStatus NetworkCloseClient(NetworkClientSocket *cs, NetworkRecvStatus status); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index f5fd7fcde3..2421ac31e1 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -336,6 +336,7 @@ DEF_SERVER_SEND_COMMAND(PACKET_SERVER_MAP) sent_packets = 4; // We start with trying 4 packets + NetworkSyncCommandQueue(cs); cs->status = STATUS_MAP; /* Mark the start of download */ cs->last_frame = _frame_counter;