mirror of https://github.com/OpenTTD/OpenTTD
(svn r19622) [1.0] -Backport from trunk:
- Fix: Desync when a command is received and in the queue while a client starts joining, i.e. save the game state. This can happen in two ways: with frame_freq > 1 a command received in a previous frame might not be executed yet or when a command is received in the same frame as the join but before the savegame is made. In both cases the joining client would not get all commands to get in-sync with the server (and the other clients) (r19620) - Fix: [desync debug] Minor fixes; "join" pause could cause a crash as some command data was not properly initialised, state was logged multiple times (r19619, r19617, r19602)release/1.0
parent
83cca3a75a
commit
9ed75d3ab7
|
@ -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<CommandPacket>(1);
|
||||
cp = CallocT<CommandPacket>(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)) {
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue