/** * @brief clientside network receiver * * Plugin for the CGame network code in Game.cpp. * @return the number of bytes read from the network stream */ int CSyncDebugger::ClientReceived(const unsigned char* inbuf) { int length = 0; switch (inbuf[0]) { case NETMSG_SD_CHKREQUEST: if (gs->frameNum != *(int*)&inbuf[1]) { logger.AddLine("Client: received checksum request for frame %d instead of %d", *(int*)&inbuf[1], gs->frameNum); } else { disable_history = true; // no more additions to the history until we're done may_enable_history = false; ClientSendChecksumResponse(); logger.AddLine("Client: checksum response sent"); } length = 5; break; case NETMSG_SD_BLKREQUEST: if (*(unsigned short*)&inbuf[1] >= HISTORY_SIZE) { logger.AddLine("Client: invalid block number %d in block request", *(unsigned short*)&inbuf[1]); } else { ClientSendBlockResponse(*(unsigned short*)&inbuf[1]); logger.AddLine("Client: block response sent for block %d", *(unsigned short*)&inbuf[1]); // simple progress indication logOutput.Print("[SD] Client: %d / %d", *(unsigned short*)&inbuf[3], *(unsigned short*)&inbuf[5]); } length = 7; break; case NETMSG_SD_RESET: logger.CloseSession(); logOutput.Print("[SD] Client: Done!"); // disable_history = false; may_enable_history = true; if (gu->autoQuit) { logOutput.Print("[SD] Client: Automatical quit enforced from commandline"); globalQuit = true; } length = 1; break; default: logOutput.Print("[SD] Client: invalid msg"); length = 0; break; } return length; }
/** * @brief clientside network receiver * * Plugin for the CGame network code in Game.cpp. * @return the number of bytes read from the network stream */ bool CSyncDebugger::ClientReceived(const unsigned char* inbuf) { bool syncDebugPacket = false; switch (inbuf[0]) { case NETMSG_SD_CHKREQUEST: if (gs->frameNum != *(int*)&inbuf[1]) { logger.AddLine("Client: received checksum request for frame %d instead of %d", *(int*)&inbuf[1], gs->frameNum); } else { disable_history = true; // no more additions to the history until we're done may_enable_history = false; ClientSendChecksumResponse(); logger.AddLine("Client: checksum response sent"); } syncDebugPacket = true; break; case NETMSG_SD_BLKREQUEST: if (*(unsigned short*)&inbuf[1] >= HISTORY_SIZE) { logger.AddLine("Client: invalid block number %d in block request", *(unsigned short*)&inbuf[1]); } else { ClientSendBlockResponse(*(unsigned short*)&inbuf[1]); logger.AddLine("Client: block response sent for block %d", *(unsigned short*)&inbuf[1]); // simple progress indication logOutput.Print("[SD] Client: %d / %d", *(unsigned short*)&inbuf[3], *(unsigned short*)&inbuf[5]); } syncDebugPacket = true; break; case NETMSG_SD_RESET: logger.CloseSession(); logOutput.Print("[SD] Client: Done!"); // disable_history = false; may_enable_history = true; syncDebugPacket = true; break; } logger.FlushBuffer(); return syncDebugPacket; }
/** * @brief sixth step after desync * * Called by server once all blockResponses are received. It dumps a backtrace * to the logger for every checksum mismatch in the block which was out of * sync. The backtraces are passed to the logger in a fairly simple form * consisting basically only of hexadecimal addresses. The logger class * resolves those to function, filename & line number. */ void CSyncDebugger::ServerDumpStack() { // first calculate start iterator... unsigned posInHistory = (unsigned)(players[0].remoteFlop % (HISTORY_SIZE * BLOCK_SIZE)); logger.AddLine("Server: position in history: %u", posInHistory); unsigned blockNr = posInHistory / BLOCK_SIZE; unsigned virtualBlockNr = 0; // block nr in remoteHistory (which skips unchanged blocks) for (; virtualBlockNr < requestedBlocks.size() && requestedBlocks[virtualBlockNr] != blockNr; ++virtualBlockNr) {} unsigned virtualPosInHistory = (virtualBlockNr * BLOCK_SIZE) + (posInHistory % BLOCK_SIZE) + 1; unsigned virtualHistorySize = players[0].remoteHistory.size(); if (virtualBlockNr >= requestedBlocks.size()) virtualPosInHistory = virtualHistorySize; unsigned ndif = 0; // number of differences assert(virtualPosInHistory <= virtualHistorySize); // we make a pool of backtraces (to merge identical ones) unsigned curBacktrace = 0; std::map<unsigned, unsigned> checksumToIndex; std::map<unsigned, unsigned> indexToHistPos; // then loop from virtualPosInHistory to virtualHistorySize and from 0 to virtualPosInHistory. for (unsigned i = virtualPosInHistory, c = 0; c < virtualHistorySize; ++i, ++c) { unsigned correctChecksum = 0; if (i == virtualHistorySize) i = 0; bool err = false; for (int j = 0; j < playerHandler->ActivePlayers(); ++j) { if (correctChecksum && players[j].remoteHistory[i] != correctChecksum) { if (historybt) { virtualBlockNr = i / BLOCK_SIZE; blockNr = requestedBlocks[virtualBlockNr]; unsigned histPos = blockNr * BLOCK_SIZE + i % BLOCK_SIZE; unsigned checksum = GetBacktraceChecksum(histPos); std::map<unsigned, unsigned>::iterator it = checksumToIndex.find(checksum); if (it == checksumToIndex.end()) { ++curBacktrace; checksumToIndex[checksum] = curBacktrace; indexToHistPos[curBacktrace] = histPos; } logger.AddLine("Server: chk %08X instead of %08X frame %06u, (value=%08x %15.8e) backtrace %u in \"%s\"", players[j].remoteHistory[i], correctChecksum, historybt[histPos].frameNum, historybt[histPos].data, *(float*)&historybt[histPos].data, checksumToIndex[checksum], historybt[histPos].op); } else { logger.AddLine("Server: chk %08X instead of %08X", players[j].remoteHistory[i], correctChecksum); } err = true; } else { correctChecksum = players[j].remoteHistory[i]; } } if (err) { ++ndif; } } if (ndif) logger.AddLine("Server: chks: %d equal, %d not equal", virtualHistorySize - ndif, ndif); else // This is impossible (internal error). // Server first finds there are differing blocks, then all checksums equal?? // Turns out this can happen if the checksum function is weak. logger.AddLine("Server: huh, all checksums equal?!? (INTERNAL ERROR)"); //cleanup for (playerVec::iterator it = players.begin(); it != players.end(); ++it) it->remoteHistory.clear(); if (historybt) { // output backtraces we collected earlier this function for (std::map<unsigned, unsigned>::iterator it = indexToHistPos.begin(); it != indexToHistPos.end(); ++it) { logger.AddLine("Server: === Backtrace %u ===", it->first); Backtrace(it->second, "Server: "); } } // and reset net->Send(CBaseNetProtocol::Get().SendSdReset()); logger.AddLine("Server: Done!"); logger.CloseSession(); logOutput.Print("[SD] Server: Done!"); }