Add client desync log to server desync log output

desync-debugging
Jonathan G Rennison 5 years ago
parent 8722445e7f
commit 0f897eefe2

@ -552,7 +552,7 @@ bool CrashLog::MakeCrashLog() const
* information like paths to the console. * information like paths to the console.
* @return true when everything is made successfully. * @return true when everything is made successfully.
*/ */
bool CrashLog::MakeDesyncCrashLog() const bool CrashLog::MakeDesyncCrashLog(const std::string *log_in, std::string *log_out) const
{ {
char filename[MAX_PATH]; char filename[MAX_PATH];
char buffer[65536 * 2]; char buffer[65536 * 2];
@ -566,7 +566,14 @@ bool CrashLog::MakeDesyncCrashLog() const
strftime(name_buffer_date, lastof(name_buffer) - name_buffer_date, "%Y%m%dT%H%M%SZ", gmtime(&cur_time)); strftime(name_buffer_date, lastof(name_buffer) - name_buffer_date, "%Y%m%dT%H%M%SZ", gmtime(&cur_time));
printf("Desync encountered (%s), generating desync log...\n", mode); printf("Desync encountered (%s), generating desync log...\n", mode);
this->FillDesyncCrashLog(buffer, lastof(buffer)); char *b = this->FillDesyncCrashLog(buffer, lastof(buffer));
if (log_in && !log_in->empty()) {
b = strecpy(b, "\n", lastof(buffer), true);
b = strecpy(b, log_in->c_str(), lastof(buffer), true);
}
if (log_out) log_out->assign(buffer);
bool bret = this->WriteCrashLog(buffer, filename, lastof(filename), name_buffer); bool bret = this->WriteCrashLog(buffer, filename, lastof(filename), name_buffer);
if (bret) { if (bret) {

@ -108,7 +108,7 @@ public:
bool WriteScreenshot(char *filename, const char *filename_last, const char *name = "crash") const; bool WriteScreenshot(char *filename, const char *filename_last, const char *name = "crash") const;
bool MakeCrashLog() const; bool MakeCrashLog() const;
bool MakeDesyncCrashLog() const; bool MakeDesyncCrashLog(const std::string *log_in, std::string *log_out) const;
/** /**
* Initialiser for crash logs; do the appropriate things so crashes are * Initialiser for crash logs; do the appropriate things so crashes are
@ -117,7 +117,7 @@ public:
*/ */
static void InitialiseCrashLog(); static void InitialiseCrashLog();
static void DesyncCrashLog(); static void DesyncCrashLog(const std::string *log_in, std::string *log_out);
static void SetErrorMessage(const char *message); static void SetErrorMessage(const char *message);
static void AfterCrashLogCleanup(); static void AfterCrashLogCleanup();

@ -52,6 +52,9 @@ typedef unsigned long in_addr_t;
# define ioctlsocket ioctl # define ioctlsocket ioctl
# define closesocket close # define closesocket close
# define GET_LAST_ERROR() (errno) # define GET_LAST_ERROR() (errno)
# define SD_RECEIVE SHUT_RD
# define SD_SEND SHUT_WR
# define SD_BOTH SHUT_RDWR
/* Need this for FIONREAD on solaris */ /* Need this for FIONREAD on solaris */
# define BSD_COMP # define BSD_COMP
@ -92,6 +95,9 @@ typedef unsigned long in_addr_t;
# define ioctlsocket ioctl # define ioctlsocket ioctl
# define closesocket close # define closesocket close
# define GET_LAST_ERROR() (sock_errno()) # define GET_LAST_ERROR() (sock_errno())
# define SD_RECEIVE SHUT_RD
# define SD_SEND SHUT_WR
# define SD_BOTH SHUT_RDWR
/* Includes needed for OS/2 systems */ /* Includes needed for OS/2 systems */
# include <types.h> # include <types.h>
@ -156,6 +162,21 @@ static inline bool SetNonBlocking(SOCKET d)
return ioctlsocket(d, FIONBIO, &nonblocking) == 0; return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
} }
/**
* Try to set the socket into blocking mode.
* @param d The socket to set the blocking more for.
* @return True if setting the blocking mode succeeded, otherwise false.
*/
static inline bool SetBlocking(SOCKET d)
{
#ifdef _WIN32
u_long nonblocking = 0;
#else
int nonblocking = 0;
#endif
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
}
/** /**
* Try to set the socket to not delay sending. * Try to set the socket to not delay sending.
* @param d The socket to disable the delaying for. * @param d The socket to disable the delaying for.
@ -169,6 +190,32 @@ static inline bool SetNoDelay(SOCKET d)
return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) == 0; return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) == 0;
} }
/**
* Try to shutdown the socket in one or both directions.
* @param d The socket to disable the delaying for.
* @param read Whether to shutdown the read direction.
* @param write Whether to shutdown the write direction.
* @param linger_timeout The socket linger timeout.
* @return True if successful
*/
static inline bool ShutdownSocket(SOCKET d, bool read, bool write, uint linger_timeout)
{
if (!read && !write) return true;
#ifdef _WIN32
LINGER ln = { 1U, (uint16) linger_timeout };
#else
struct linger ln = { 1, (int) linger_timeout };
#endif
setsockopt(d, SOL_SOCKET, SO_LINGER, (const char*)&ln, sizeof(ln));
int how = SD_BOTH;
if (!read) how = SD_SEND;
if (!write) how = SD_RECEIVE;
return shutdown(d, how) == 0;
}
/* Make sure these structures have the size we expect them to be */ /* Make sure these structures have the size we expect them to be */
assert_compile(sizeof(in_addr) == 4); ///< IPv4 addresses should be 4 bytes. assert_compile(sizeof(in_addr) == 4); ///< IPv4 addresses should be 4 bytes.
assert_compile(sizeof(in6_addr) == 16); ///< IPv6 addresses should be 16 bytes. assert_compile(sizeof(in6_addr) == 16); ///< IPv6 addresses should be 16 bytes.

@ -97,6 +97,7 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet *p)
case PACKET_CLIENT_SET_NAME: return this->Receive_CLIENT_SET_NAME(p); case PACKET_CLIENT_SET_NAME: return this->Receive_CLIENT_SET_NAME(p);
case PACKET_CLIENT_QUIT: return this->Receive_CLIENT_QUIT(p); case PACKET_CLIENT_QUIT: return this->Receive_CLIENT_QUIT(p);
case PACKET_CLIENT_ERROR: return this->Receive_CLIENT_ERROR(p); case PACKET_CLIENT_ERROR: return this->Receive_CLIENT_ERROR(p);
case PACKET_CLIENT_DESYNC_LOG: return this->Receive_CLIENT_DESYNC_LOG(p);
case PACKET_SERVER_QUIT: return this->Receive_SERVER_QUIT(p); case PACKET_SERVER_QUIT: return this->Receive_SERVER_QUIT(p);
case PACKET_SERVER_ERROR_QUIT: return this->Receive_SERVER_ERROR_QUIT(p); case PACKET_SERVER_ERROR_QUIT: return this->Receive_SERVER_ERROR_QUIT(p);
case PACKET_SERVER_SHUTDOWN: return this->Receive_SERVER_SHUTDOWN(p); case PACKET_SERVER_SHUTDOWN: return this->Receive_SERVER_SHUTDOWN(p);
@ -183,6 +184,7 @@ NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_PASSWORD(Packet *
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SET_NAME); } NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SET_NAME); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_QUIT); } NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_QUIT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_ERROR); } NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_ERROR); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_DESYNC_LOG(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_DESYNC_LOG); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_QUIT); } NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_QUIT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR_QUIT); } NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR_QUIT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SHUTDOWN(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_SHUTDOWN); } NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SHUTDOWN(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_SHUTDOWN); }

@ -118,6 +118,7 @@ enum PacketGameType {
PACKET_SERVER_QUIT, ///< A server tells that a client has quit. PACKET_SERVER_QUIT, ///< A server tells that a client has quit.
PACKET_CLIENT_ERROR, ///< A client reports an error to the server. PACKET_CLIENT_ERROR, ///< A client reports an error to the server.
PACKET_SERVER_ERROR_QUIT, ///< A server tells that a client has hit an error and did quit. PACKET_SERVER_ERROR_QUIT, ///< A server tells that a client has hit an error and did quit.
PACKET_CLIENT_DESYNC_LOG, ///< A client reports a desync log
PACKET_END, ///< Must ALWAYS be on the end of this list!! (period) PACKET_END, ///< Must ALWAYS be on the end of this list!! (period)
}; };
@ -420,6 +421,7 @@ protected:
* @param p The packet that was just received. * @param p The packet that was just received.
*/ */
virtual NetworkRecvStatus Receive_CLIENT_ERROR(Packet *p); virtual NetworkRecvStatus Receive_CLIENT_ERROR(Packet *p);
virtual NetworkRecvStatus Receive_CLIENT_DESYNC_LOG(Packet *p);
/** /**
* Notification that a client left the game: * Notification that a client left the game:

@ -169,16 +169,22 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
*/ */
if (this->sock == INVALID_SOCKET) return status; if (this->sock == INVALID_SOCKET) return status;
DEBUG(net, 1, "Closed client connection %d", this->client_id); DEBUG(net, 1, "Shutting down client connection %d", this->client_id);
SetBlocking(this->sock);
this->SendPackets(true); this->SendPackets(true);
ShutdownSocket(this->sock, false, true, 2);
/* Wait a number of ticks so our leave message can reach the server. /* Wait a number of ticks so our leave message can reach the server.
* This is especially needed for Windows servers as they seem to get * This is especially needed for Windows servers as they seem to get
* the "socket is closed" message before receiving our leave message, * the "socket is closed" message before receiving our leave message,
* which would trigger the server to close the connection as well. */ * which would trigger the server to close the connection as well. */
CSleep(3 * MILLISECONDS_PER_TICK); CSleep(3 * MILLISECONDS_PER_TICK);
DEBUG(net, 1, "Shutdown client connection %d", this->client_id);
delete this->GetInfo(); delete this->GetInfo();
delete this; delete this;
@ -218,6 +224,8 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
SendError(errorno); SendError(errorno);
} }
this->SendPackets();
ClientNetworkEmergencySave(); ClientNetworkEmergencySave();
_switch_mode = SM_MENU; _switch_mode = SM_MENU;
@ -276,9 +284,11 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
NetworkError(STR_NETWORK_ERROR_DESYNC); NetworkError(STR_NETWORK_ERROR_DESYNC);
DEBUG(desync, 1, "sync_err: %08x; %02x", _date, _date_fract); DEBUG(desync, 1, "sync_err: %08x; %02x", _date, _date_fract);
DEBUG(net, 0, "Sync error detected!"); DEBUG(net, 0, "Sync error detected!");
my_client->ClientError(NETWORK_RECV_STATUS_DESYNC);
CrashLog::DesyncCrashLog(); std::string desync_log;
CrashLog::DesyncCrashLog(nullptr, &desync_log);
my_client->SendDesyncLog(desync_log);
my_client->ClientError(NETWORK_RECV_STATUS_DESYNC);
return false; return false;
} }
@ -463,6 +473,21 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendError(NetworkErrorCode err
return NETWORK_RECV_STATUS_OKAY; return NETWORK_RECV_STATUS_OKAY;
} }
/** Send an error-packet over the network */
NetworkRecvStatus ClientNetworkGameSocketHandler::SendDesyncLog(const std::string &log)
{
for (size_t offset = 0; offset < log.size();) {
Packet *p = new Packet(PACKET_CLIENT_DESYNC_LOG);
size_t size = min<size_t>(log.size() - offset, SEND_MTU - 2 - p->size);
p->Send_uint16(size);
p->Send_binary(log.data() + offset, size);
my_client->SendPacket(p);
offset += size;
}
return NETWORK_RECV_STATUS_OKAY;
}
/** /**
* Tell the server that we like to change the password of the company. * Tell the server that we like to change the password of the company.
* @param password The new password. * @param password The new password.

@ -84,6 +84,7 @@ public:
static NetworkRecvStatus SendJoin(); static NetworkRecvStatus SendJoin();
static NetworkRecvStatus SendCommand(const CommandPacket *cp); static NetworkRecvStatus SendCommand(const CommandPacket *cp);
static NetworkRecvStatus SendError(NetworkErrorCode errorno); static NetworkRecvStatus SendError(NetworkErrorCode errorno);
static NetworkRecvStatus SendDesyncLog(const std::string &log);
static NetworkRecvStatus SendQuit(); static NetworkRecvStatus SendQuit();
static NetworkRecvStatus SendAck(); static NetworkRecvStatus SendAck();

@ -1160,7 +1160,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p
NetworkAdminClientError(this->client_id, errorno); NetworkAdminClientError(this->client_id, errorno);
if (errorno == NETWORK_ERROR_DESYNC) { if (errorno == NETWORK_ERROR_DESYNC) {
CrashLog::DesyncCrashLog(); CrashLog::DesyncCrashLog(&(this->desync_log), nullptr);
// have the server and all clients run some sanity checks // have the server and all clients run some sanity checks
NetworkSendCommand(0, 0, 0, CMD_DESYNC_CHECK, NULL, NULL, _local_company); NetworkSendCommand(0, 0, 0, CMD_DESYNC_CHECK, NULL, NULL, _local_company);
@ -1169,6 +1169,16 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p
return this->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST); return this->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST);
} }
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_DESYNC_LOG(Packet *p)
{
uint size = p->Recv_uint16();
this->desync_log.resize(this->desync_log.size() + size);
p->Recv_binary(const_cast<char *>(this->desync_log.data() + this->desync_log.size() - size), size);
DEBUG(net, 2, "Received %u bytes of client desync log", size);
this->receive_limit += p->size;
return NETWORK_RECV_STATUS_OKAY;
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p) NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p)
{ {
/* The client wants to leave. Display this and report it to the other /* The client wants to leave. Display this and report it to the other

@ -36,6 +36,7 @@ protected:
NetworkRecvStatus Receive_CLIENT_SET_NAME(Packet *p) override; NetworkRecvStatus Receive_CLIENT_SET_NAME(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_QUIT(Packet *p) override; NetworkRecvStatus Receive_CLIENT_QUIT(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_ERROR(Packet *p) override; NetworkRecvStatus Receive_CLIENT_ERROR(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_DESYNC_LOG(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_RCON(Packet *p) override; NetworkRecvStatus Receive_CLIENT_RCON(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_NEWGRFS_CHECKED(Packet *p) override; NetworkRecvStatus Receive_CLIENT_NEWGRFS_CHECKED(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_MOVE(Packet *p) override; NetworkRecvStatus Receive_CLIENT_MOVE(Packet *p) override;
@ -73,6 +74,8 @@ public:
struct PacketWriter *savegame; ///< Writer used to write the savegame. struct PacketWriter *savegame; ///< Writer used to write the savegame.
NetworkAddress client_address; ///< IP-address of the client (so he can be banned) NetworkAddress client_address; ///< IP-address of the client (so he can be banned)
std::string desync_log;
ServerNetworkGameSocketHandler(SOCKET s); ServerNetworkGameSocketHandler(SOCKET s);
~ServerNetworkGameSocketHandler(); ~ServerNetworkGameSocketHandler();

@ -267,8 +267,8 @@ void CDECL HandleCrash(int signum)
} }
} }
/* static */ void CrashLog::DesyncCrashLog() /* static */ void CrashLog::DesyncCrashLog(const std::string *log_in, std::string *log_out)
{ {
CrashLogOSX log(CrashLogOSX::DesyncTag{}); CrashLogOSX log(CrashLogOSX::DesyncTag{});
log.MakeDesyncCrashLog(); log.MakeDesyncCrashLog(log_in, log_out);
} }

@ -182,8 +182,8 @@ static void CDECL HandleCrash(int signum)
} }
} }
/* static */ void CrashLog::DesyncCrashLog() /* static */ void CrashLog::DesyncCrashLog(const std::string *log_in, std::string *log_out)
{ {
CrashLogUnix log(0); CrashLogUnix log(0);
log.MakeDesyncCrashLog(); log.MakeDesyncCrashLog(log_in, log_out);
} }

@ -633,10 +633,10 @@ static void CDECL CustomAbort(int signal)
SetUnhandledExceptionFilter(ExceptionHandler); SetUnhandledExceptionFilter(ExceptionHandler);
} }
/* static */ void CrashLog::DesyncCrashLog() /* static */ void CrashLog::DesyncCrashLog(const std::string *log_in, std::string *log_out)
{ {
CrashLogWindows log(nullptr); CrashLogWindows log(nullptr);
log.MakeDesyncCrashLog(); log.MakeDesyncCrashLog(log_in, log_out);
} }
/* The crash log GUI */ /* The crash log GUI */

Loading…
Cancel
Save