diff --git a/.gitignore b/.gitignore index b0e5b8c..3b998ef 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,8 @@ Debug/ *.sdf *.map *.opensdf +*.pdb +*.sbr *.suo *.tlog *.ipch diff --git a/Sources/Engine/Classes/PlayerEntity.es b/Sources/Engine/Classes/PlayerEntity.es index ef534b7..b53f8de 100644 --- a/Sources/Engine/Classes/PlayerEntity.es +++ b/Sources/Engine/Classes/PlayerEntity.es @@ -111,7 +111,9 @@ functions: // provide info for GameAgent enumeration export virtual void GetGameAgentPlayerInfo( INDEX iPlayer, CTString &strOut) { }; - + // provide info for MSLegacy enumeration + export virtual void GetMSLegacyPlayerInf( INDEX iPlayer, CTString &strOut) { }; + // create a checksum value for sync-check export void ChecksumForSync(ULONG &ulCRC, INDEX iExtensiveSyncCheck) { diff --git a/Sources/Engine/GameAgent/GameAgent.cpp b/Sources/Engine/GameAgent/GameAgent.cpp index 156c289..b104756 100644 --- a/Sources/Engine/GameAgent/GameAgent.cpp +++ b/Sources/Engine/GameAgent/GameAgent.cpp @@ -17,21 +17,92 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include - #include - #include #include #include - #include #include #include - #include - #include +#include + +#define MSPORT 28900 +#define BUFFSZ 8192 +#define BUFFSZSTR 4096 + +#define PCK "\\gamename\\%s" \ + "\\enctype\\%d" \ + "\\validate\\%s" \ + "\\final\\" \ + "\\queryid\\1.1" \ + "\\list\\cmp" \ + "\\gamename\\%s" \ + "\\gamever\\1.05" \ + "%s%s" \ + "\\final\\" + +#define PCKQUERY "\\gamename\\%s" \ + "\\gamever\\%s" \ + "\\location\\%s" \ + "\\hostname\\%s" \ + "\\hostport\\%hu" \ + "\\mapname\\%s" \ + "\\gametype\\%s" \ + "\\activemod\\" \ + "\\numplayers\\%d" \ + "\\maxplayers\\%d" \ + "\\gamemode\\openplaying" \ + "\\difficulty\\Normal" \ + "\\friendlyfire\\%d" \ + "\\weaponsstay\\%d" \ + "\\ammosstay\\%d" \ + "\\healthandarmorstays\\%d" \ + "\\allowhealth\\%d" \ + "\\allowarmor\\%d" \ + "\\infinitearmor\\%d" \ + "\\respawninplace\\%d" \ + "\\password\\0" \ + "\\vipplayers\\1" + +#define PCKINFO "\\hostname\\%s" \ + "\\hostport\\%hu" \ + "\\mapname\\%s" \ + "\\gametype\\%s" \ + "\\numplayers\\%d" \ + "\\maxplayers\\%d" \ + "\\gamemode\\openplaying" \ + "\\final\\" \ + "\\queryid\\8.1" + +#define PCKBASIC "\\gamename\\%s" \ + "\\gamever\\%s" \ + "\\location\\EU" \ + "\\final\\" \ + "\\queryid\\1.1" + + +#define CHK_BUFFSTRLEN if((iLen < 0) || (iLen > BUFFSZSTR)) { \ + CPrintF("\n" \ + "Error: the used buffer is smaller than how much needed (%d < %d)\n" \ + "\n", iLen, BUFFSZSTR); \ + if(cMsstring) free (cMsstring); \ + closesocket(_sock); \ + WSACleanup(); \ + } + +#define CLEANMSSRUFF1 closesocket(_sock); \ + WSACleanup(); + +#define CLEANMSSRUFF2 if(cResponse) free (cResponse); \ + closesocket(_sock); \ + WSACleanup(); + +#define SERIOUSSAMKEY "AKbna4\0" +#define SERIOUSSAMSTR "serioussamse" + #pragma comment(lib, "wsock32.lib") WSADATA* _wsaData = NULL; @@ -42,15 +113,27 @@ sockaddr_in* _sinLocal = NULL; sockaddr_in _sinFrom; CHAR* _szBuffer = NULL; +CHAR* _szIPPortBuffer = NULL; +INT _iIPPortBufferLen = 0; +CHAR* _szIPPortBufferLocal = NULL; +INT _iIPPortBufferLocalLen = 0; BOOL _bServer = FALSE; -static BOOL _bInitialized = FALSE; +BOOL _bInitialized = FALSE; +BOOL _bActivated = FALSE; +BOOL _bActivatedLocal = FALSE; TIME _tmLastHeartbeat = 0; CDynamicStackArray ga_asrRequests; -extern CTString ga_strServer = "master1.croteam.org"; +//extern CTString ga_strServer = "master1.croteam.org"; +CTString ga_strServer = "master1.42amsterdam.net"; +//extern CTString ga_strMSLegacy = "master1.croteam.org"; +CTString ga_strMSLegacy = "42amsterdam.net"; + +BOOL ga_bMSLegacy = TRUE; +//BOOL ga_bMSLegacy = FALSE; void _uninitWinsock(); void _initializeWinsock(void) @@ -66,7 +149,7 @@ void _initializeWinsock(void) if(_szBuffer != NULL) { delete[] _szBuffer; } - _szBuffer = new char[1400]; + _szBuffer = new char[2050]; // start WSA if(WSAStartup(MAKEWORD(2, 2), _wsaData) != 0) { @@ -76,8 +159,12 @@ void _initializeWinsock(void) } // get the host IP - hostent* phe = gethostbyname(ga_strServer); - + hostent* phe; + if(!ga_bMSLegacy) { + phe = gethostbyname(ga_strServer); + } else { + phe = gethostbyname(ga_strMSLegacy); + } // if we couldn't resolve the hostname if(phe == NULL) { // report and stop @@ -90,7 +177,11 @@ void _initializeWinsock(void) _sin = new sockaddr_in; _sin->sin_family = AF_INET; _sin->sin_addr.s_addr = *(ULONG*)phe->h_addr_list[0]; - _sin->sin_port = htons(9005); + if(!ga_bMSLegacy) { + _sin->sin_port = htons(9005); + } else { + _sin->sin_port = htons(27900); + } // create the socket _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); @@ -149,7 +240,7 @@ void _sendPacket(const char* szBuffer) int _recvPacket() { int fromLength = sizeof(_sinFrom); - return recvfrom(_socket, _szBuffer, 1024, 0, (sockaddr*)&_sinFrom, &fromLength); + return recvfrom(_socket, _szBuffer, 2048, 0, (sockaddr*)&_sinFrom, &fromLength); } CTString _getGameModeName(INDEX iGameMode) @@ -173,14 +264,18 @@ const CSessionProperties* _getSP() void _sendHeartbeat(INDEX iChallenge) { CTString strPacket; - strPacket.PrintF("0;challenge;%d;players;%d;maxplayers;%d;level;%s;gametype;%s;version;%s;product;%s", - iChallenge, - _pNetwork->ga_srvServer.GetPlayersCount(), - _pNetwork->ga_sesSessionState.ses_ctMaxPlayers, - _pNetwork->ga_World.wo_strName, - _getGameModeName(_getSP()->sp_gmGameMode), - _SE_VER_STRING, - _pShell->GetString("sam_strGameName")); + if (!ga_bMSLegacy) { + strPacket.PrintF("0;challenge;%d;players;%d;maxplayers;%d;level;%s;gametype;%s;version;%s;product;%s", + iChallenge, + _pNetwork->ga_srvServer.GetPlayersCount(), + _pNetwork->ga_sesSessionState.ses_ctMaxPlayers, + _pNetwork->ga_World.wo_strName, + _getGameModeName(_getSP()->sp_gmGameMode), + _SE_VER_STRING, + _pShell->GetString("sam_strGameName")); + } else { + strPacket.PrintF("\\heartbeat\\%hu\\gamename\\serioussamse", (_pShell->GetINDEX("net_iPort") + 1)); + } _sendPacket(strPacket); _tmLastHeartbeat = _pTimer->GetRealTimeTick(); } @@ -209,7 +304,14 @@ extern void GameAgent_ServerInit(void) // join _bServer = TRUE; _bInitialized = TRUE; - _sendPacket("q"); + + if (!ga_bMSLegacy) { + _sendPacket("q"); + } else { + CTString strPacket; + strPacket.PrintF("\\heartbeat\\%hu\\gamename\\serioussamse", (_pShell->GetINDEX("net_iPort") + 1)); + _sendPacket(strPacket); + } } /// Let GameAgent know that the server has stopped. @@ -217,7 +319,14 @@ extern void GameAgent_ServerEnd(void) { if (!_bInitialized) { return; - } + } + + if (ga_bMSLegacy) { + CTString strPacket; + strPacket.PrintF("\\heartbeat\\%hu\\gamename\\serioussamse\\statechanged", (_pShell->GetINDEX("net_iPort") + 1)); + _sendPacket(strPacket); + } + _uninitWinsock(); _bInitialized = FALSE; } @@ -231,6 +340,7 @@ extern void GameAgent_ServerUpdate(void) int iLen = _recvPacket(); if(iLen > 0) { + if (!ga_bMSLegacy) { // check the received packet ID switch(_szBuffer[0]) { case 1: // server join response @@ -270,7 +380,7 @@ extern void GameAgent_ServerUpdate(void) plt.plt_penPlayerEntity->GetGameAgentPlayerInfo(plb.plb_Index, strPlayer); // if we don't have enough space left for the next player - if(strlen(strPacket) + strlen(strPlayer) > 1024) { + if(strlen(strPacket) + strlen(strPlayer) > 2048) { // send the packet _sendPacketTo(strPacket, &_sinFrom); strPacket = ""; @@ -293,52 +403,439 @@ extern void GameAgent_ServerUpdate(void) _sendPacketTo(strPacket, &_sinFrom); break; } - } - } + } + } else { - // send a heartbeat every 150 seconds - if(_pTimer->GetRealTimeTick() - _tmLastHeartbeat >= 150.0f) { + _szBuffer[iLen] = 0; + char *sPch1 = NULL, *sPch2 = NULL, *sPch3 = NULL, *sPch4 = NULL; + sPch1 = strstr(_szBuffer, "\\status\\"); + sPch2 = strstr(_szBuffer, "\\info\\"); + sPch3 = strstr(_szBuffer, "\\basic\\"); + sPch4 = strstr(_szBuffer, "\\players\\"); + if(sPch1) { + CTString strPacket; + CTString strLocation; + strLocation = _pShell->GetString("net_strLocalHost"); + if ( strLocation == ""){ + strLocation = "Heartland"; + } + strPacket.PrintF( PCKQUERY, + _pShell->GetString("sam_strGameName"), + _SE_VER_STRING, + //_pShell->GetString("net_strLocalHost"), + strLocation, + _pShell->GetString("gam_strSessionName"), + _pShell->GetINDEX("net_iPort"), + _pNetwork->ga_World.wo_strName, + _getGameModeName(_getSP()->sp_gmGameMode), + _pNetwork->ga_srvServer.GetPlayersCount(), + _pNetwork->ga_sesSessionState.ses_ctMaxPlayers, + _pShell->GetINDEX("gam_bFriendlyFire"), + _pShell->GetINDEX("gam_bWeaponsStay"), + _pShell->GetINDEX("gam_bAmmoStays"), + _pShell->GetINDEX("gam_bHealthArmorStays"), + _pShell->GetINDEX("gam_bAllowHealth"), + _pShell->GetINDEX("gam_bAllowArmor"), + _pShell->GetINDEX("gam_bInfiniteAmmo"), + _pShell->GetINDEX("gam_bRespawnInPlace")); + + for(INDEX i=0; i<_pNetwork->ga_srvServer.GetPlayersCount(); i++) { + CPlayerBuffer &plb = _pNetwork->ga_srvServer.srv_aplbPlayers[i]; + CPlayerTarget &plt = _pNetwork->ga_sesSessionState.ses_apltPlayers[i]; + if(plt.plt_bActive) { + CTString strPlayer; + plt.plt_penPlayerEntity->GetMSLegacyPlayerInf(plb.plb_Index, strPlayer); + + // if we don't have enough space left for the next player + if(strlen(strPacket) + strlen(strPlayer) > 2048) { + // send the packet + _sendPacketTo(strPacket, &_sinFrom); + strPacket = ""; + } + strPacket += strPlayer; + } + } + + strPacket += "\\final\\\\queryid\\333.1"; + _sendPacketTo(strPacket, &_sinFrom); + + } else if (sPch2){ + + CTString strPacket; + strPacket.PrintF( PCKINFO, + _pShell->GetString("gam_strSessionName"), + _pShell->GetINDEX("net_iPort"), + _pNetwork->ga_World.wo_strName, + _getGameModeName(_getSP()->sp_gmGameMode), + _pNetwork->ga_srvServer.GetPlayersCount(), + _pNetwork->ga_sesSessionState.ses_ctMaxPlayers); + _sendPacketTo(strPacket, &_sinFrom); + + } else if (sPch3){ + + CTString strPacket; + CTString strLocation; + strLocation = _pShell->GetString("net_strLocalHost"); + if ( strLocation == ""){ + strLocation = "Heartland"; + } + strPacket.PrintF( PCKBASIC, + _pShell->GetString("sam_strGameName"), + _SE_VER_STRING, + //_pShell->GetString("net_strLocalHost")); + strLocation); + _sendPacketTo(strPacket, &_sinFrom); + + } else if (sPch4){ + + // send the player status response + CTString strPacket; + strPacket = ""; + for(INDEX i=0; i<_pNetwork->ga_srvServer.GetPlayersCount(); i++) { + CPlayerBuffer &plb = _pNetwork->ga_srvServer.srv_aplbPlayers[i]; + CPlayerTarget &plt = _pNetwork->ga_sesSessionState.ses_apltPlayers[i]; + if(plt.plt_bActive) { + CTString strPlayer; + plt.plt_penPlayerEntity->GetMSLegacyPlayerInf(plb.plb_Index, strPlayer); + + // if we don't have enough space left for the next player + if(strlen(strPacket) + strlen(strPlayer) > 2048) { + // send the packet + _sendPacketTo(strPacket, &_sinFrom); + strPacket = ""; + } + + strPacket += strPlayer; + } + } + + strPacket += "\\final\\\\queryid\\6.1"; + _sendPacketTo(strPacket, &_sinFrom); + + } else { + CPrintF("Unknown query server response!\n"); + return; + } + } + } + + // send a heartbeat every 150 seconds + if(_pTimer->GetRealTimeTick() - _tmLastHeartbeat >= 150.0f) { _sendHeartbeat(0); - } + } } /// Notify GameAgent that the server state has changed. extern void GameAgent_ServerStateChanged(void) { - if (_bInitialized) { + if (!_bInitialized) { + return; + } + if (!ga_bMSLegacy) { _sendPacket("u"); + } else { + CTString strPacket; + strPacket.PrintF("\\heartbeat\\%hu\\gamename\\serioussamse\\statechanged", (_pShell->GetINDEX("net_iPort") + 1)); + _sendPacket(strPacket); } } /// Request serverlist enumeration. extern void GameAgent_EnumTrigger(BOOL bInternet) { - if (!_bInitialized) { + + if ( _pNetwork->ga_bEnumerationChange ) { return; } - // make sure that there are no requests still stuck in buffer - ga_asrRequests.Clear(); + + if ( !bInternet && ga_bMSLegacy) { + // make sure that there are no requests still stuck in buffer + ga_asrRequests.Clear(); + // we're not a server + _bServer = FALSE; + _pNetwork->ga_strEnumerationStatus = "."; + + WORD _wsaRequested; + WSADATA wsaData; + PHOSTENT _phHostinfo; + ULONG _uIP,*_pchIP = &_uIP; + USHORT _uPort,*_pchPort = &_uPort; + INT _iLen; + char _cName[256],*_pch,_strFinal[8] = {0}; - // we're not a server - _bServer = FALSE; + struct in_addr addr; - // send enumeration packet to masterserver - _sendPacket("e"); - _setStatus(""); + // make the buffer that we'll use for packet reading + if(_szIPPortBufferLocal != NULL) { + return; + } + _szIPPortBufferLocal = new char[1024]; + + // start WSA + _wsaRequested = MAKEWORD( 2, 2 ); + if( WSAStartup(_wsaRequested, &wsaData) != 0) { + CPrintF("Error initializing winsock!\n"); + if(_szIPPortBufferLocal != NULL) { + delete[] _szIPPortBufferLocal; + } + _szIPPortBufferLocal = NULL; + _uninitWinsock(); + _bInitialized = FALSE; + _pNetwork->ga_bEnumerationChange = FALSE; + _pNetwork->ga_strEnumerationStatus = ""; + WSACleanup(); + return; + } + + _pch = _szIPPortBufferLocal; + _iLen = 0; + strcpy(_strFinal,"\\final\\"); + + if( gethostname ( _cName, sizeof(_cName)) == 0) + { + if((_phHostinfo = gethostbyname(_cName)) != NULL) + { + int _iCount = 0; + while(_phHostinfo->h_addr_list[_iCount]) + { + addr.s_addr = *(u_long *) _phHostinfo->h_addr_list[_iCount]; + _uIP = htonl(addr.s_addr); + + for (UINT uPort = 25601; uPort < 25622; ++uPort){ + _uPort = htons(uPort); + memcpy(_pch,_pchIP,4); + _pch +=4; + _iLen +=4; + memcpy(_pch,_pchPort,2); + _pch +=2; + _iLen +=2; + } + ++_iCount; + } + memcpy(_pch,_strFinal, 7); + _pch +=7; + _iLen +=7; + _pch[_iLen] = 0x00; + } + } + _iIPPortBufferLocalLen = _iLen; + + _bActivatedLocal = TRUE; + _bInitialized = TRUE; + _initializeWinsock(); + return; + + } else { + + if (!ga_bMSLegacy) { + // make sure that there are no requests still stuck in buffer + ga_asrRequests.Clear(); + // we're not a server + _bServer = FALSE; + // Initialization + _bInitialized = TRUE; + // send enumeration packet to masterserver + _sendPacket("e"); + _setStatus("."); + } + else + { /* MSLegacy */ + // make sure that there are no requests still stuck in buffer + ga_asrRequests.Clear(); + // we're not a server + _bServer = FALSE; + _pNetwork->ga_strEnumerationStatus = "."; + + struct sockaddr_in peer; + + SOCKET _sock = NULL; + u_int uiMSIP; + int iErr, + iLen, + iDynsz, + iEnctype = 0; + u_short usMSport = MSPORT; + + u_char ucGamekey[] = {SERIOUSSAMKEY}, + ucGamestr[] = {SERIOUSSAMSTR}, + *ucSec = NULL, + *ucKey = NULL; + + char *cFilter = "", + *cWhere = "", + cMS[128] = {0}, + *cResponse = NULL, + *cMsstring = NULL, + *cSec = NULL; + + + strcpy(cMS,ga_strMSLegacy); + + WSADATA wsadata; + if(WSAStartup(MAKEWORD(2,2), &wsadata) != 0) { + CPrintF("Error initializing winsock!\n"); + return; + } + +/* Open a socket and connect to the Master server */ + + peer.sin_addr.s_addr = uiMSIP = resolv(cMS); + peer.sin_port = htons(usMSport); + peer.sin_family = AF_INET; + + _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(_sock < 0) { + CPrintF("Error creating TCP socket!\n"); + WSACleanup(); + return; + } + if(connect(_sock, (struct sockaddr *)&peer, sizeof(peer)) < 0) { + CPrintF("Error connecting to TCP socket!\n"); + CLEANMSSRUFF1; + return; + } + +/* Allocate memory for a buffer and get a pointer to it */ + + cResponse = (char*) malloc(BUFFSZSTR + 1); + if(!cResponse) { + CPrintF("Error initializing memory buffer!\n"); + CLEANMSSRUFF1; + return; + } + +/* Reading response from Master Server - returns the string with the secret key */ + + iLen = 0; + iErr = recv(_sock, (char*)cResponse + iLen, BUFFSZSTR - iLen, 0); + if(iErr < 0) { + CPrintF("Error reading from TCP socket!\n"); + CLEANMSSRUFF2; + return; + } + + iLen += iErr; + cResponse[iLen] = 0x00; + +/* Allocate memory for a buffer and get a pointer to it */ + + ucSec = (u_char*) malloc(BUFFSZSTR + 1); + if(!ucSec) { + CPrintF("Error initializing memory buffer!\n"); + CLEANMSSRUFF2; + return; + } + memcpy ( ucSec, cResponse, BUFFSZSTR); + ucSec[iLen] = 0x00; + +/* Geting the secret key from a string */ + + cSec = strstr(cResponse, "\\secure\\"); + if(!cSec) { + CPrintF("Not valid master server response!\n"); + CLEANMSSRUFF2; + return; + } else { + ucSec += 15; + +/* Creating a key for authentication (Validate key) */ + + ucKey = gsseckey(ucSec, ucGamekey, iEnctype); + } + ucSec -= 15; + if(cResponse) free (cResponse); + if(ucSec) free (ucSec); + +/* Generate a string for the response (to Master Server) with the specified (Validate ucKey) */ + + cMsstring = (char*) malloc(BUFFSZSTR + 1); + if(!cMsstring) { + CPrintF("Not valid master server response!\n"); + CLEANMSSRUFF1; + return; + } + + iLen = _snprintf( + cMsstring, + BUFFSZSTR, + PCK, + ucGamestr, + iEnctype, + ucKey, + ucGamestr, + cWhere, + cFilter); + +/* Check the buffer */ + + CHK_BUFFSTRLEN; + +/* The string sent to master server */ + + if(send(_sock,cMsstring, iLen, 0) < 0){ + CPrintF("Error reading from TCP socket!\n"); + if(cMsstring) free (cMsstring); + CLEANMSSRUFF1; + return; + } + if(cMsstring) free (cMsstring); + + /* Allocate memory for a buffer and get a pointer to it */ + + if(_szIPPortBuffer ) { + CLEANMSSRUFF1; + return; + }; + + _szIPPortBuffer = (char*) malloc(BUFFSZ + 1); + if(!_szIPPortBuffer) { + CPrintF("Error reading from TCP socket!\n"); + CLEANMSSRUFF1; + return; + } + iDynsz = BUFFSZ; + + +/* The received encoded data after sending the string (Validate key) */ + + iLen = 0; + while((iErr = recv(_sock, _szIPPortBuffer + iLen, iDynsz - iLen, 0)) > 0) { + iLen += iErr; + if(iLen >= iDynsz) { + iDynsz += BUFFSZ; + _szIPPortBuffer = (char*)realloc(_szIPPortBuffer, iDynsz); + if(!_szIPPortBuffer) { + CPrintF("Error reallocation memory buffer!\n"); + if(_szIPPortBuffer) free (_szIPPortBuffer); + CLEANMSSRUFF1; + return; + } + } + } + CLEANMSSRUFF1; + _iIPPortBufferLen = iLen; + + _bActivated = TRUE; + _bInitialized = TRUE; + _initializeWinsock(); + + } + } } /// GameAgent client update for enumerations. extern void GameAgent_EnumUpdate(void) { + if((_socket == NULL) || (!_bInitialized)) { return; } - int iLen = _recvPacket(); - if(iLen != -1) { + if (!ga_bMSLegacy) { + int iLen = _recvPacket(); + if(iLen != -1) { // null terminate the buffer _szBuffer[iLen] = 0; - switch(_szBuffer[0]) { case 's': { @@ -349,6 +846,8 @@ extern void GameAgent_EnumUpdate(void) UBYTE bFourth; USHORT iPort; }; + _pNetwork->ga_strEnumerationStatus = ""; + sIPPort* pServers = (sIPPort*)(_szBuffer + 1); while(iLen - ((CHAR*)pServers - _szBuffer) >= sizeof(sIPPort)) { sIPPort ip = *pServers; @@ -478,13 +977,448 @@ extern void GameAgent_EnumUpdate(void) break; } } + } else { + /* MSLegacy */ + if(_bActivated) { + HANDLE _hThread; + DWORD _dwThreadId; + + _hThread = CreateThread(NULL, 0, _MS_Thread, 0, 0, &_dwThreadId); + if (_hThread != NULL) { + CloseHandle(_hThread); + } + _bActivated = FALSE; + } + if(_bActivatedLocal) { + HANDLE _hThread; + DWORD _dwThreadId; + + _hThread = CreateThread(NULL, 0, _LocalNet_Thread, 0, 0, &_dwThreadId); + if (_hThread != NULL) { + CloseHandle(_hThread); + } + _bActivatedLocal = FALSE; + } + } } /// Cancel the GameAgent serverlist enumeration. extern void GameAgent_EnumCancel(void) { if (_bInitialized) { + CPrintF("...GameAgent_EnumCancel!\n"); ga_asrRequests.Clear(); _uninitWinsock(); } } + +DWORD WINAPI _MS_Thread(LPVOID lpParam) { + SOCKET _sockudp = NULL; + struct _sIPPort { + UBYTE bFirst; + UBYTE bSecond; + UBYTE bThird; + UBYTE bFourth; + USHORT iPort; + }; + + _setStatus(""); + _sockudp = socket(AF_INET, SOCK_DGRAM, 0); + if (_sockudp == INVALID_SOCKET){ + WSACleanup(); + return -1; + } + + _sIPPort* pServerIP = (_sIPPort*)(_szIPPortBuffer); + while(_iIPPortBufferLen >= 6) { + if(!strncmp((char *)pServerIP, "\\final\\", 7)) { + break; + } + + _sIPPort ip = *pServerIP; + + CTString strIP; + strIP.PrintF("%d.%d.%d.%d", ip.bFirst, ip.bSecond, ip.bThird, ip.bFourth); + + sockaddr_in sinServer; + sinServer.sin_family = AF_INET; + sinServer.sin_addr.s_addr = inet_addr(strIP); + sinServer.sin_port = ip.iPort; + + // insert server status request into container + CServerRequest &sreq = ga_asrRequests.Push(); + sreq.sr_ulAddress = sinServer.sin_addr.s_addr; + sreq.sr_iPort = sinServer.sin_port; + sreq.sr_tmRequestTime = _pTimer->GetHighPrecisionTimer().GetMilliseconds(); + + // send packet to server + sendto(_sockudp,"\\status\\",8,0, + (sockaddr *) &sinServer, sizeof(sinServer)); + + sockaddr_in _sinClient; + int _iClientLength = sizeof(_sinClient); + + fd_set readfds_udp; // declare a read set + struct timeval timeout_udp; // declare a timeval for our timer + int iRet = -1; + + FD_ZERO(&readfds_udp); // zero out the read set + FD_SET(_sockudp, &readfds_udp); // add socket to the read set + timeout_udp.tv_sec = 0; // timeout = 0 seconds + timeout_udp.tv_usec = 50000; // timeout += 0.05 seconds + int _iN = select(_sockudp + 1, &readfds_udp, NULL, NULL, &timeout_udp); + if (_iN > 0) { + /** do recvfrom stuff **/ + iRet = recvfrom(_sockudp, _szBuffer, 2048, 0, (sockaddr*)&_sinClient, &_iClientLength); + FD_CLR(_sockudp, &readfds_udp); + if(iRet != -1 && iRet > 100 && iRet != SOCKET_ERROR) { + // null terminate the buffer + _szBuffer[iRet] = 0; + char *sPch = NULL; + sPch = strstr(_szBuffer, "\\gamename\\serioussamse\\"); + if(!sPch) { + CPrintF("Unknown query server response!\n"); + return -1; + } else { + + CTString strPlayers; + CTString strMaxPlayers; + CTString strLevel; + CTString strGameType; + CTString strVersion; + CTString strGameName; + CTString strSessionName; + + CTString strGamePort; + CTString strServerLocation; + CTString strGameMode; + CTString strActiveMod; + + CHAR* pszPacket = _szBuffer + 1; // we do +1 because the first character is always '\', which we don't care about. + + BOOL bReadValue = FALSE; + CTString strKey; + CTString strValue; + + while(*pszPacket != 0) { + switch(*pszPacket) { + case '\\': + if(strKey != "gamemode") { + if(bReadValue) { + // we're done reading the value, check which key it was + if(strKey == "gamename") { + strGameName = strValue; + } else if(strKey == "gamever") { + strVersion = strValue; + } else if(strKey == "location") { + strServerLocation = strValue; + } else if(strKey == "hostname") { + strSessionName = strValue; + } else if(strKey == "hostport") { + strGamePort = strValue; + } else if(strKey == "mapname") { + strLevel = strValue; + } else if(strKey == "gametype") { + strGameType = strValue; + } else if(strKey == "activemod") { + strActiveMod = strValue; + } else if(strKey == "numplayers") { + strPlayers = strValue; + } else if(strKey == "maxplayers") { + strMaxPlayers = strValue; + } else { + //CPrintF("Unknown GameAgent parameter key '%s'!", strKey); + } + // reset temporary holders + strKey = ""; + strValue = ""; + } + } + bReadValue = !bReadValue; + break; + + default: + // read into the value or into the key, depending where we are + if(bReadValue) { + strValue.InsertChar(strlen(strValue), *pszPacket); + } else { + strKey.InsertChar(strlen(strKey), *pszPacket); + } + break; + } + // move to next character + pszPacket++; + } + + // check if we still have a maxplayers to back up + if(strKey == "gamemode") { + strGameMode = strValue; + } + if(strActiveMod != "") { + strGameName = strActiveMod; + } + + long long tmPing = -1; + // find the request in the request array + for(INDEX i=0; iGetHighPrecisionTimer().GetMilliseconds() - req.sr_tmRequestTime; + ga_asrRequests.Delete(&req); + break; + } + } + + if(tmPing > 0 && tmPing < 2500000) { + // insert the server into the serverlist + CNetworkSession &ns = *new CNetworkSession; + _pNetwork->ga_lhEnumeratedSessions.AddTail(ns.ns_lnNode); + + // add the server to the serverlist + ns.ns_strSession = strSessionName; + ns.ns_strAddress = inet_ntoa(_sinClient.sin_addr) + CTString(":") + CTString(0, "%d", htons(_sinClient.sin_port) - 1); + ns.ns_tmPing = (tmPing / 1000.0f); + ns.ns_strWorld = strLevel; + ns.ns_ctPlayers = atoi(strPlayers); + ns.ns_ctMaxPlayers = atoi(strMaxPlayers); + ns.ns_strGameType = strGameType; + ns.ns_strMod = strGameName; + ns.ns_strVer = strVersion; + } + } + } else { + // find the request in the request array + for(INDEX i=0; iga_bEnumerationChange = FALSE; + WSACleanup(); + return 0; +} + +DWORD WINAPI _LocalNet_Thread(LPVOID lpParam) { + SOCKET _sockudp = NULL; + struct _sIPPort { + UBYTE bFirst; + UBYTE bSecond; + UBYTE bThird; + UBYTE bFourth; + USHORT iPort; + }; + + _sockudp = socket(AF_INET, SOCK_DGRAM, 0); + if (_sockudp == INVALID_SOCKET){ + WSACleanup(); + _pNetwork->ga_strEnumerationStatus = ""; + if(_szIPPortBufferLocal != NULL) { + delete[] _szIPPortBufferLocal; + } + _szIPPortBufferLocal = NULL; + return -1; + } + + _sIPPort* pServerIP = (_sIPPort*)(_szIPPortBufferLocal); + while(_iIPPortBufferLocalLen >= 6) { + if(!strncmp((char *)pServerIP, "\\final\\", 7)) { + break; + } + + _sIPPort ip = *pServerIP; + + CTString strIP; + strIP.PrintF("%d.%d.%d.%d", ip.bFourth, ip.bThird, ip.bSecond, ip.bFirst); + + sockaddr_in sinServer; + sinServer.sin_family = AF_INET; + sinServer.sin_addr.s_addr = inet_addr(strIP); + sinServer.sin_port = ip.iPort; + + // insert server status request into container + CServerRequest &sreq = ga_asrRequests.Push(); + sreq.sr_ulAddress = sinServer.sin_addr.s_addr; + sreq.sr_iPort = sinServer.sin_port; + sreq.sr_tmRequestTime = _pTimer->GetHighPrecisionTimer().GetMilliseconds(); + + // send packet to server + sendto(_sockudp,"\\status\\",8,0, + (sockaddr *) &sinServer, sizeof(sinServer)); + + sockaddr_in _sinClient; + int _iClientLength = sizeof(_sinClient); + + fd_set readfds_udp; // declare a read set + struct timeval timeout_udp; // declare a timeval for our timer + int iRet = -1; + + FD_ZERO(&readfds_udp); // zero out the read set + FD_SET(_sockudp, &readfds_udp); // add socket to the read set + timeout_udp.tv_sec = 0; // timeout = 0 seconds + timeout_udp.tv_usec = 50000; // timeout += 0.05 seconds + int _iN = select(_sockudp + 1, &readfds_udp, NULL, NULL, &timeout_udp); + if (_iN > 0) { + /** do recvfrom stuff **/ + iRet = recvfrom(_sockudp, _szBuffer, 2048, 0, (sockaddr*)&_sinClient, &_iClientLength); + FD_CLR(_sockudp, &readfds_udp); + if(iRet != -1 && iRet > 100 && iRet != SOCKET_ERROR) { + // null terminate the buffer + _szBuffer[iRet] = 0; + char *sPch = NULL; + sPch = strstr(_szBuffer, "\\gamename\\serioussamse\\"); + if(!sPch) { + CPrintF("Unknown query server response!\n"); + if(_szIPPortBufferLocal != NULL) { + delete[] _szIPPortBufferLocal; + } + _szIPPortBufferLocal = NULL; + WSACleanup(); + return -1; + } else { + + CTString strPlayers; + CTString strMaxPlayers; + CTString strLevel; + CTString strGameType; + CTString strVersion; + CTString strGameName; + CTString strSessionName; + + CTString strGamePort; + CTString strServerLocation; + CTString strGameMode; + CTString strActiveMod; + + CHAR* pszPacket = _szBuffer + 1; // we do +1 because the first character is always '\', which we don't care about. + + BOOL bReadValue = FALSE; + CTString strKey; + CTString strValue; + + while(*pszPacket != 0) { + switch(*pszPacket) { + case '\\': + if(strKey != "gamemode") { + if(bReadValue) { + // we're done reading the value, check which key it was + if(strKey == "gamename") { + strGameName = strValue; + } else if(strKey == "gamever") { + strVersion = strValue; + } else if(strKey == "location") { + strServerLocation = strValue; + } else if(strKey == "hostname") { + strSessionName = strValue; + } else if(strKey == "hostport") { + strGamePort = strValue; + } else if(strKey == "mapname") { + strLevel = strValue; + } else if(strKey == "gametype") { + strGameType = strValue; + } else if(strKey == "activemod") { + strActiveMod = strValue; + } else if(strKey == "numplayers") { + strPlayers = strValue; + } else if(strKey == "maxplayers") { + strMaxPlayers = strValue; + } else { + //CPrintF("Unknown GameAgent parameter key '%s'!", strKey); + } + // reset temporary holders + strKey = ""; + strValue = ""; + } + } + bReadValue = !bReadValue; + break; + + default: + // read into the value or into the key, depending where we are + if(bReadValue) { + strValue.InsertChar(strlen(strValue), *pszPacket); + } else { + strKey.InsertChar(strlen(strKey), *pszPacket); + } + break; + } + // move to next character + pszPacket++; + } + + // check if we still have a maxplayers to back up + if(strKey == "gamemode") { + strGameMode = strValue; + } + if(strActiveMod != "") { + strGameName = strActiveMod; + } + + long long tmPing = -1; + // find the request in the request array + for(INDEX i=0; iGetHighPrecisionTimer().GetMilliseconds() - req.sr_tmRequestTime; + ga_asrRequests.Delete(&req); + break; + } + } + + if(tmPing > 0 && tmPing < 2500000) { + // insert the server into the serverlist + _pNetwork->ga_strEnumerationStatus = ""; + CNetworkSession &ns = *new CNetworkSession; + _pNetwork->ga_lhEnumeratedSessions.AddTail(ns.ns_lnNode); + + // add the server to the serverlist + ns.ns_strSession = strSessionName; + ns.ns_strAddress = inet_ntoa(_sinClient.sin_addr) + CTString(":") + CTString(0, "%d", htons(_sinClient.sin_port) - 1); + ns.ns_tmPing = (tmPing / 1000.0f); + ns.ns_strWorld = strLevel; + ns.ns_ctPlayers = atoi(strPlayers); + ns.ns_ctMaxPlayers = atoi(strMaxPlayers); + ns.ns_strGameType = strGameType; + ns.ns_strMod = strGameName; + ns.ns_strVer = strVersion; + } + } + } else { + // find the request in the request array + for(INDEX i=0; iga_bEnumerationChange = FALSE; + _pNetwork->ga_strEnumerationStatus = ""; + WSACleanup(); + return 0; +} \ No newline at end of file diff --git a/Sources/Engine/GameAgent/GameAgent.h b/Sources/Engine/GameAgent/GameAgent.h index 0b79030..835e49a 100644 --- a/Sources/Engine/GameAgent/GameAgent.h +++ b/Sources/Engine/GameAgent/GameAgent.h @@ -20,6 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif extern CTString ga_strServer; +extern CTString ga_strMSLegacy; +extern BOOL ga_bMSLegacy; /// Initialize GameAgent. extern void GameAgent_ServerInit(void); @@ -36,6 +38,10 @@ extern void GameAgent_EnumTrigger(BOOL bInternet); extern void GameAgent_EnumUpdate(void); /// Cancel the GameAgent serverlist enumeration. extern void GameAgent_EnumCancel(void); +/// +DWORD WINAPI _MS_Thread(LPVOID lpParam); +/// +DWORD WINAPI _LocalNet_Thread(LPVOID lpParam); /// Server request structure. Primarily used for getting server pings. class CServerRequest { diff --git a/Sources/Engine/GameAgent/MSLegacy.h b/Sources/Engine/GameAgent/MSLegacy.h new file mode 100644 index 0000000..4defdfb --- /dev/null +++ b/Sources/Engine/GameAgent/MSLegacy.h @@ -0,0 +1,185 @@ +/* +GSMSALG 0.3.3 +by Luigi Auriemma +e-mail: aluigi@autistici.org +web: aluigi.org + + +INTRODUCTION +============ +With the name Gsmsalg I define the challenge-response algorithm needed +to query the master servers that use the Gamespy "secure" protocol (like +master.gamespy.com for example). +This algorithm is not only used for this type of query but also in other +situations like the so called "Gamespy Firewall Probe Packet" and the +master server hearbeat that is the challenge string sent by the master +servers of the games that use the Gamespy SDK when game servers want to +be included in the online servers list (UDP port 27900). + + +HOW TO USE +========== +The function needs 4 parameters: +- dst: the destination buffer that will contain the calculated + response. Its length is 4/3 of the challenge size so if the + challenge is 6 bytes long, the response will be 8 bytes long + plus the final NULL byte which is required (to be sure of the + allocated space use 89 bytes or "((len * 4) / 3) + 3") + if this parameter is NULL the function will allocate the + memory for a new one automatically +- src: the source buffer containing the challenge string received + from the server. +- key: the gamekey or any other text string used as algorithm's + key, usually it is the gamekey but "might" be another thing + in some cases. Each game has its unique Gamespy gamekey which + are available here: + http://aluigi.org/papers/gslist.cfg +- enctype: are supported 0 (plain-text used in old games, heartbeat + challenge respond, enctypeX and more), 1 (Gamespy3D) and 2 + (old Gamespy Arcade or something else). + +The return value is a pointer to the destination buffer. + + +EXAMPLE +======= + #include "MSLegacy.h" + + char *dest; + dest = gsseckey( + NULL, // dest buffer, NULL for auto allocation + "ABCDEF", // the challenge received from the server + "kbeafe", // kbeafe of Doom 3 and enctype set to 0 + 0); // enctype 0 + + +LICENSE +======= + Copyright 2004,2005,2006,2007,2008 Luigi Auriemma + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + http://www.gnu.org/licenses/gpl.txt +*/ + +#ifdef PRAGMA_ONCE + #pragma once +#endif + +/* function gsvalfunc */ +unsigned char gsvalfunc(u_char reg) { + if(reg < 0x1a) return u_char (reg + 'A'); + if(reg < 0x34) return u_char (reg + 'G'); + if(reg < 0x3e) return u_char (reg - 4); + if(reg == 0x3e) return u_char('+'); + if(reg == 0x3f) return u_char ('/'); + return u_char(0); +} + +/* function gsseckey */ +unsigned char *gsseckey(u_char *secure, u_char *key, int enctype) { + static u_char validate[9]; + u_char secbuf[7], + buff[256], + *ptr, + *ptrval, + *sec, + *k, + tmp1, + tmp2, + ebx, + i, + ecx, + edx, + edi; + + i = 0; + ptr = buff; + do { *ptr++ = i++; } while(i); /* 256 times */ + + ptr = buff; + k = (unsigned char*) memcpy(secbuf, key, 6); /* good if key is not NULLed */ + k[6] = edx = i = 0; + do { /* 256 times */ + if(!*k) k = secbuf; + edx = *ptr + edx + *k; + /* don't use the XOR exchange optimization!!! */ + /* ptrval is used only for faster code */ + ptrval = buff + edx; + tmp1 = *ptr; + *ptr = *ptrval; + *ptrval = tmp1; + ptr++; k++; i++; + } while(i); + + sec = (unsigned char *) memcpy(secbuf, secure, 6); + sec[6] = edi = ebx = 0; + do { /* 6 times */ + edi = edi + *sec + 1; + ecx = ebx + buff[edi]; + ebx = ecx; + /* don't use the XOR exchange optimization!!! */ + /* ptr and ptrval are used only for faster code */ + ptr = buff + edi; + ptrval = buff + ebx; + tmp1 = *ptr; + *ptr = *ptrval; + *ptrval = tmp1; + ecx = tmp1 + *ptr; + *sec++ ^= buff[ecx]; + } while(*sec); + + if(enctype == 2) { + ptr = key; + sec = secbuf; + do { /* 6 times */ + *sec++ ^= *ptr++; + } while(*sec); + } + + sec = secbuf; + ptrval = validate; + for(i = 0; i < 2; i++) { + tmp1 = *sec++; + tmp2 = *sec++; + *ptrval++ = gsvalfunc(tmp1 >> 2); + *ptrval++ = gsvalfunc(((tmp1 & 3) << 4) + (tmp2 >> 4)); + tmp1 = *sec++; + *ptrval++ = gsvalfunc(((tmp2 & 0xf) << 2) + (tmp1 >> 6)); + *ptrval++ = gsvalfunc(tmp1 & 0x3f); + } + *ptrval = 0x00; + + return(validate); +} + +/* function resolv */ +u_int resolv(char *host) { + struct hostent *hp; + u_int host_ip; + + host_ip = inet_addr(host); + if(host_ip == INADDR_NONE) { + hp = gethostbyname(host); + if(!hp) { + return (NULL); + } else host_ip = *(u_int *)(hp->h_addr); + } + return(host_ip); +} + +/* end functions */ + + diff --git a/Sources/Engine/Network/Network.cpp b/Sources/Engine/Network/Network.cpp index 8b29797..724960d 100644 --- a/Sources/Engine/Network/Network.cpp +++ b/Sources/Engine/Network/Network.cpp @@ -331,7 +331,7 @@ static void NetworkInfo(void) for(INDEX iplb=0; iplb<_pNetwork->ga_srvServer.srv_aplbPlayers.Count(); iplb++) { CPlayerBuffer &plb = _pNetwork->ga_srvServer.srv_aplbPlayers[iplb]; if (plb.plb_Active) { - CPrintF(" %2d(%2d):'%s'@client%2d: (%dact)\n", + CPrintF(" %2d(%2d):'%s'@client%2d: (%dact)\n", iplb, plb.plb_Index, plb.plb_pcCharacter.GetNameForPrinting(), plb.plb_iClient, plb.plb_abReceived.GetCount()); } @@ -341,7 +341,7 @@ static void NetworkInfo(void) CSessionSocket &sso = _pNetwork->ga_srvServer.srv_assoSessions[iSession]; if (sso.sso_bActive) { CPrintF(" %2d:'%s'\n", iSession, _cmiComm.Server_GetClientName(iSession)), - CPrintF(" buffer: %dblk=%dk\n", + CPrintF(" buffer: %dblk=%dk\n", sso.sso_nsBuffer.GetUsedBlocks(), sso.sso_nsBuffer.GetUsedMemory()/1024); CPrintF(" state:"); @@ -359,7 +359,7 @@ static void NetworkInfo(void) CPrintF(" not a server\n"); } CPrintF("Session state:\n"); - CPrintF(" buffer: (%dblk)%dk\n", + CPrintF(" buffer: (%dblk)%dk\n", _pNetwork->ga_sesSessionState.ses_nsGameStream.GetUsedBlocks(), _pNetwork->ga_sesSessionState.ses_nsGameStream.GetUsedMemory()/1024); CPrintF(" last processed tick: %g\n", _pNetwork->ga_sesSessionState.ses_tmLastProcessedTick); @@ -377,11 +377,11 @@ static void NetworkInfo(void) } - if (TIMER_PROFILING) { - CTString strNetProfile; - _pfNetworkProfile.Report(strNetProfile); - CPrintF(strNetProfile); - } + if (TIMER_PROFILING) { + CTString strNetProfile; + _pfNetworkProfile.Report(strNetProfile); + CPrintF(strNetProfile); + } } static void ListPlayers(void) @@ -432,7 +432,7 @@ static void KickByName(const CTString &strName, const CTString &strReason) if (!_pNetwork->IsServer()) { CPrintF( TRANS("Only server can kick people!\n")); return; - } + } for(INDEX iplb=0; iplb<_pNetwork->ga_srvServer.srv_aplbPlayers.Count(); iplb++) { CPlayerBuffer &plb = _pNetwork->ga_srvServer.srv_aplbPlayers[iplb]; if (plb.plb_Active && plb.plb_pcCharacter.GetNameForPrinting().Undecorated().Matches(strName)) { @@ -462,8 +462,8 @@ static void StockInfo(void) INDEX ctCachedShadows=0, ctDynamicShadows=0, ctFlatShadows=0; SLONG slStaticMemory=0, slDynamicMemory=0, slUploadMemory=0; SLONG slShdBytes=0, slSlackMemory=0, slFlatMemory=0; - INDEX ct256=0, ct128=0, ct64=0, ct32=0, ct16=0; - SLONG sl256Memory=0, sl128Memory=0, sl64Memory=0, sl32Memory=0, sl16Memory=0; + INDEX ct256=0, ct128=0, ct64=0, ct32=0, ct16=0; + SLONG sl256Memory=0, sl128Memory=0, sl64Memory=0, sl32Memory=0, sl16Memory=0; if( _pGfx!=NULL) { @@ -533,15 +533,15 @@ static void StockInfo(void) // report shadow layers and world geometry memory usage FOREACHINDYNAMICARRAY( pwo->wo_baBrushes.ba_abrBrushes, CBrush3D, itbr) // for all brush entities in the world - { + { // skip brush without entity - if( itbr->br_penEntity==NULL) continue; + if( itbr->br_penEntity==NULL) continue; // for each mip - FOREACHINLIST( CBrushMip, bm_lnInBrush, itbr->br_lhBrushMips, itbm) - { + FOREACHINLIST( CBrushMip, bm_lnInBrush, itbr->br_lhBrushMips, itbm) + { // for each sector in the brush mip - FOREACHINDYNAMICARRAY( itbm->bm_abscSectors, CBrushSector, itbsc) + FOREACHINDYNAMICARRAY( itbm->bm_abscSectors, CBrushSector, itbsc) { // add sector class memory usage to polygons memory ctSectors++; @@ -564,7 +564,7 @@ static void StockInfo(void) // for each polygon in sector ctPolys += itbsc->bsc_abpoPolygons.Count(); - FOREACHINSTATICARRAY( itbsc->bsc_abpoPolygons, CBrushPolygon, itbpo) { + FOREACHINSTATICARRAY( itbsc->bsc_abpoPolygons, CBrushPolygon, itbpo) { CBrushPolygon &bpo = *itbpo; slPlyBytes += bpo.GetUsedMemory(); // count in the shadow layers (if any) @@ -588,7 +588,7 @@ static void StockInfo(void) const FLOAT fAstBytes = dToMB * _pAnimSetStock->CalculateUsedMemory(); const FLOAT fShaBytes = dToMB * _pShaderStock->CalculateUsedMemory(); const FLOAT fSkaBytes = dToMB * _pSkeletonStock->CalculateUsedMemory(); - + CPrintF("\nStock information:\n"); CPrintF(" Textures: %5d (%5.2f MB)\n", _pTextureStock->GetTotalCount(), fTexBytes); CPrintF(" ShadowMaps: %5d (%5.2f MB)\n", ctCachedShadows, slShdBytes*dToMB); @@ -747,7 +747,7 @@ void CNetworkLibrary::Init(const CTString &strGameID) _pShell->DeclareSymbol("user void RemIPMask(CTString);", &RemIPMask); _pShell->DeclareSymbol("user void AddNameMask(CTString);", &AddNameMask); _pShell->DeclareSymbol("user void RemNameMask(CTString);", &RemNameMask); - + _pShell->DeclareSymbol("user FLOAT dem_tmTimer;", &ga_fDemoTimer); _pShell->DeclareSymbol("user FLOAT dem_fSyncRate;", &ga_fDemoSyncRate); @@ -854,7 +854,7 @@ void CNetworkLibrary::Init(const CTString &strGameID) _pShell->DeclareSymbol("user FLOAT phy_fCollisionCacheAhead;", &phy_fCollisionCacheAhead); _pShell->DeclareSymbol("user FLOAT phy_fCollisionCacheAround;", &phy_fCollisionCacheAround); - + _pShell->DeclareSymbol("persistent user INDEX inp_iKeyboardReadingMethod;", &inp_iKeyboardReadingMethod); _pShell->DeclareSymbol("persistent user INDEX inp_bAllowMouseAcceleration;", &inp_bAllowMouseAcceleration); _pShell->DeclareSymbol("persistent user FLOAT inp_fMouseSensitivity;", &inp_fMouseSensitivity); @@ -874,7 +874,7 @@ void CNetworkLibrary::Init(const CTString &strGameID) _pShell->DeclareSymbol("persistent user FLOAT inp_f2ndMousePrecisionFactor;", &inp_f2ndMousePrecisionFactor); _pShell->DeclareSymbol("persistent user FLOAT inp_f2ndMousePrecisionThreshold;", &inp_f2ndMousePrecisionThreshold); _pShell->DeclareSymbol("persistent user FLOAT inp_f2ndMousePrecisionTimeout;", &inp_f2ndMousePrecisionTimeout); - + _pShell->DeclareSymbol("persistent user INDEX inp_bMsgDebugger;", &inp_bMsgDebugger); _pShell->DeclareSymbol("persistent user INDEX inp_iMButton4Up;", &inp_iMButton4Up); _pShell->DeclareSymbol("persistent user INDEX inp_iMButton4Dn;", &inp_iMButton4Dn); @@ -887,6 +887,8 @@ void CNetworkLibrary::Init(const CTString &strGameID) _pShell->DeclareSymbol("persistent user INDEX wed_bUseGenericTextureReplacement;", &wed_bUseGenericTextureReplacement); _pShell->DeclareSymbol("user CTString ga_strServer;", &ga_strServer); + _pShell->DeclareSymbol("user CTString ga_strMSLegacy;", &ga_strMSLegacy); + _pShell->DeclareSymbol("user INDEX ga_bMSLegacy;", &ga_bMSLegacy); _pShell->DeclareSymbol("INDEX pwoCurrentWorld;", &_pwoCurrentWorld); } @@ -971,7 +973,7 @@ void CNetworkLibrary::AutoAdjustSettings(void) * remember to keep this routine up to date with CNetworkLibrary::Read() */ void CNetworkLibrary::StartPeerToPeer_t(const CTString &strSessionName, - const CTFileName &fnmWorld, ULONG ulSpawnFlags, + const CTFileName &fnmWorld, ULONG ulSpawnFlags, INDEX ctMaxPlayers, BOOL bWaitAllPlayers, void *pvSessionProperties) // throw char * { @@ -1096,7 +1098,7 @@ void CNetworkLibrary::Save_t(const CTFileName &fnmGame) // throw char * if (!ga_IsServer) { throw TRANS("Cannot save game - not a server!\n"); } - + // create the file CTFileStream strmFile; strmFile.Create_t(fnmGame); @@ -1154,7 +1156,7 @@ void CNetworkLibrary::Load_t(const CTFileName &fnmGame) // throw char * // if starting in network if (_cmiComm.IsNetworkEnabled()) { // make default state data for creating deltas - MakeDefaultState(ga_fnmWorld, ga_sesSessionState.ses_ulSpawnFlags, + MakeDefaultState(ga_fnmWorld, ga_sesSessionState.ses_ulSpawnFlags, ga_aubProperties); } // players will be connected later @@ -1619,7 +1621,7 @@ void CNetworkLibrary::ChangeLevel_internal(void) // destroy all entities that will cross level ga_World.DestroyEntities(senToCross); - + // if should remember old levels if (ga_bNextRemember) { // remember current level @@ -1684,7 +1686,7 @@ void CNetworkLibrary::ChangeLevel_internal(void) // copy entities from temporary world into new one CEntitySelection senCrossed; - ga_World.CopyEntities(wldTemp, senInTemp, + ga_World.CopyEntities(wldTemp, senInTemp, senCrossed, CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0))); // restore pointers to entities for all active player targets @@ -1803,7 +1805,7 @@ static void SendAdminResponse(ULONG ulAdr, UWORD uwPort, ULONG ulCode, const CTS strLine.OnlyFirstLine(); str.RemovePrefix(strLine); str.DeleteChar(0); - if (strLine.Length()>0) { + if (strLine.Length()>0) { CNetworkMessage nm(MSG_EXTRA); nm<SendBroadcast(nm, ulAdr, uwPort); @@ -2326,7 +2328,7 @@ void CNetworkLibrary::AddNetGraphValue(enum NetGraphEntryType nget, FLOAT fLaten // make default state for a network game extern void NET_MakeDefaultState_t( - const CTFileName &fnmWorld, ULONG ulSpawnFlags, void *pvSessionProperties, + const CTFileName &fnmWorld, ULONG ulSpawnFlags, void *pvSessionProperties, CTStream &strmState) // throw char * { // mute all sounds @@ -2427,7 +2429,7 @@ void CNetworkLibrary::GameInactive(void) FOREVER { CNetworkMessage nmReceived; -//_cmiComm.Broadcast_Update(); +// _cmiComm.Broadcast_Update(); ULONG ulFrom; UWORD uwPort; BOOL bHasMsg = ReceiveBroadcast(nmReceived, ulFrom, uwPort); @@ -2437,11 +2439,11 @@ void CNetworkLibrary::GameInactive(void) break; } - /* This is handled by GameAgent. + /* This is handled by GameAgent. // if requesting enumeration and this is server and enumeration is allowed if (nmReceived.GetType()==MSG_REQ_ENUMSERVERS - && IsServer() + && IsServer() && (ser_bEnumeration && ga_sesSessionState.ses_ctMaxPlayers>1)) { // create response CNetworkMessage nmEnum(MSG_SERVERINFO); @@ -2470,7 +2472,7 @@ void CNetworkLibrary::GameInactive(void) void CNetworkLibrary::InitCRCGather(void) { CRCT_ResetActiveList(); - CRCT_bGatherCRCs = TRUE; + CRCT_bGatherCRCs = TRUE; CRCT_AddFile_t(CTString("Classes\\Player.ecl")); } diff --git a/Sources/Engine/Network/Network.h b/Sources/Engine/Network/Network.h index db99d68..a755fad 100644 --- a/Sources/Engine/Network/Network.h +++ b/Sources/Engine/Network/Network.h @@ -76,7 +76,8 @@ public: INDEX ns_ctMaxPlayers; // max number of players CTString ns_strGameType; // game type CTString ns_strMod; // active mod - CTString ns_strVer; // version + CTString ns_strVer; // version + public: void Copy(const CNetworkSession &nsOriginal); diff --git a/Sources/EntitiesMP/Player.es b/Sources/EntitiesMP/Player.es index 8ea3ed6..d305a03 100644 --- a/Sources/EntitiesMP/Player.es +++ b/Sources/EntitiesMP/Player.es @@ -1956,7 +1956,24 @@ functions: strKey.PrintF("ping_%d\x02%d\x03", iPlayer, INDEX(ceil(en_tmPing*1000.0f))); strOut+=strKey; }; - + + // provide info for MSLegacy enumeration + void GetMSLegacyPlayerInf( INDEX iPlayer, CTString &strOut) + { + CTString strKey; + strKey.PrintF("\\player_%d\\%s", iPlayer, (const char*)GetPlayerName()); + strOut+=strKey; + if (GetSP()->sp_bUseFrags) { + strKey.PrintF("\\frags_%d\\%d", iPlayer, m_psLevelStats.ps_iKills); + strOut+=strKey; + } else { + strKey.PrintF("\\frags_%d\\%d", iPlayer, m_psLevelStats.ps_iScore); + strOut+=strKey; + } + strKey.PrintF("\\ping_%d\\%d", iPlayer, INDEX(ceil(en_tmPing*1000.0f))); + strOut+=strKey; + }; + // check if message is in inbox BOOL HasMessage( const CTFileName &fnmMessage) { diff --git a/Sources/SeriousSam/SeriousSam.cpp b/Sources/SeriousSam/SeriousSam.cpp index f8cb5a6..d18f4d5 100644 --- a/Sources/SeriousSam/SeriousSam.cpp +++ b/Sources/SeriousSam/SeriousSam.cpp @@ -115,7 +115,7 @@ extern CTString sam_strModName = TRANS("- O P E N S O U R C E -"); #if _SE_DEMO extern CTString sam_strFirstLevel = "Levels\\KarnakDemo.wld"; #else - extern CTString sam_strFirstLevel = "Levels\\LevelsMP\\1_0_InTheLastEpisode.wld.wld"; + extern CTString sam_strFirstLevel = "Levels\\LevelsMP\\1_0_InTheLastEpisode.wld"; #endif extern CTString sam_strIntroLevel = "Levels\\LevelsMP\\Intro.wld"; extern CTString sam_strGameName = "serioussamse"; @@ -514,8 +514,8 @@ BOOL Init( HINSTANCE hInstance, int nCmdShow, CTString strCmdLine) LoadAndForceTexture(_toLogoEAX, _ptoLogoEAX, CTFILENAME("Textures\\Logo\\LogoEAX.tex")); // !! NOTE !! Re-enable these to allow mod support. - //LoadStringVar(CTString("Data\\Var\\Sam_Version.var"), sam_strVersion); - //LoadStringVar(CTString("Data\\Var\\ModName.var"), sam_strModName); + LoadStringVar(CTString("Data\\Var\\Sam_Version.var"), sam_strVersion); + LoadStringVar(CTString("Data\\Var\\ModName.var"), sam_strModName); CPrintF(TRANS("Serious Sam version: %s\n"), sam_strVersion); CPrintF(TRANS("Active mod: %s\n"), sam_strModName); InitializeMenus(); @@ -612,6 +612,7 @@ void End(void) pvpViewPort = NULL; pdpNormal = NULL; } + CloseMainWindow(); MainWindow_End(); DestroyMenus(); @@ -1199,15 +1200,37 @@ int SubMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int _pInput->DisableInput(); _pGame->StopGame(); + + if (_fnmModToLoad!="") { + + char strCmd [64] = {0}; + char strParam [128] = {0}; + STARTUPINFOA cif; + ZeroMemory(&cif,sizeof(STARTUPINFOA)); + PROCESS_INFORMATION pi; + + strcpy_s(strCmd,"SeriousSam.exe"); + strcpy_s(strParam," +game "); + strcat_s(strParam,_fnmModToLoad.FileName()); + if (_strModServerJoin!="") { + strcat_s(strParam," +connect "); + strcat_s(strParam,_strModServerJoin); + strcat_s(strParam," +quickjoin"); + } + if (CreateProcessA(strCmd,strParam,NULL,NULL,FALSE,CREATE_DEFAULT_ERROR_MODE,NULL,NULL,&cif,&pi) == FALSE) + { + MessageBox(0, L"error launching the Mod!\n", L"Serious Sam", MB_OK|MB_ICONERROR); + } + } // invoke quit screen if needed if( _bQuitScreen && _fnmModToLoad=="") QuitScreenLoop(); - + End(); - return TRUE; } +/* void CheckModReload(void) { if (_fnmModToLoad!="") { @@ -1225,9 +1248,10 @@ void CheckModReload(void) argv[5] = "+quickjoin"; argv[6] = NULL; } + _execv(strCommand, argv); } -} +}*/ void CheckTeaser(void) { @@ -1254,8 +1278,8 @@ int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, CTSTREAM_BEGIN { iResult = SubMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } CTSTREAM_END; - - CheckModReload(); + + //CheckModReload(); CheckTeaser();