Serious-Engine/Sources/Engine/GameAgent/GameAgent.cpp
2016-04-12 04:10:43 +03:00

1513 lines
46 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
#include <Engine/StdH.h>
#include <Engine/Engine.h>
#include <Engine/CurrentVersion.h>
#include <Engine/Entities/Entity.h>
#include <Engine/Base/Shell.h>
#include <Engine/Base/Console.h>
#include <Engine/Base/CTString.h>
#include <Engine/Network/Server.h>
#include <Engine/Network/Network.h>
#include <Engine/Network/SessionState.h>
#include <GameMP/SessionProperties.h>
#include <Engine/GameAgent/GameAgent.h>
#if defined(PLATFORM_WIN32)
#pragma comment(lib, "wsock32.lib")
WSADATA* _wsaData = NULL;
typedef int socklen_t;
#else
#include <fcntl.h>
#include <netdb.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket close
typedef int SOCKET;
typedef struct hostent HOSTENT, *PHOSTENT;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;
#define WSAGetLastError() (INDEX) errno
#define WSACleanup()
#endif
#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"
#include <Engine/GameAgent/MSLegacy.h>
SOCKET _socket = INVALID_SOCKET;
sockaddr_in* _sin = NULL;
sockaddr_in* _sinLocal = NULL;
sockaddr_in _sinFrom;
CHAR* _szBuffer = NULL;
CHAR* _szIPPortBuffer = NULL;
LONG _iIPPortBufferLen = 0;
CHAR* _szIPPortBufferLocal = NULL;
LONG _iIPPortBufferLocalLen = 0;
BOOL _bServer = FALSE;
BOOL _bInitialized = FALSE;
BOOL _bActivated = FALSE;
BOOL _bActivatedLocal = FALSE;
TIME _tmLastHeartbeat = 0;
CDynamicStackArray<CServerRequest> ga_asrRequests;
//CTString ga_strServer = "master1.croteam.org";
CTString ga_strServer = "master1.42amsterdam.net";
//CTString ga_strMSLegacy = "master1.croteam.org";
CTString ga_strMSLegacy = "42amsterdam.net";
BOOL ga_bMSLegacy = TRUE;
//BOOL ga_bMSLegacy = FALSE;
void _uninitWinsock();
void _initializeWinsock(void)
{
#ifdef PLATFORM_WIN32
if(_wsaData != NULL && _socket != INVALID_SOCKET) {
return;
}
_wsaData = new WSADATA;
_socket = INVALID_SOCKET;
// start WSA
if(WSAStartup(MAKEWORD(2, 2), _wsaData) != 0) {
CPrintF("Error initializing winsock!\n");
_uninitWinsock();
return;
}
#endif
// make the buffer that we'll use for packet reading
if(_szBuffer != NULL) {
delete[] _szBuffer;
}
_szBuffer = new char[2050];
// get the host IP
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
CPrintF("Couldn't resolve GameAgent server %s.\n", (const char *) ga_strServer);
_uninitWinsock();
return;
}
// create the socket destination address
_sin = new sockaddr_in;
_sin->sin_family = AF_INET;
_sin->sin_addr.s_addr = *(ULONG*)phe->h_addr_list[0];
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);
if (_socket == INVALID_SOCKET) {
CPrintF("Error creating GameAgent socket!\n");
_uninitWinsock();
return;
}
// if we're a server
if(_bServer) {
// create the local socket source address
_sinLocal = new sockaddr_in;
_sinLocal->sin_family = AF_INET;
_sinLocal->sin_addr.s_addr = inet_addr("0.0.0.0");
_sinLocal->sin_port = htons(_pShell->GetINDEX("net_iPort") + 1);
// bind the socket
bind(_socket, (sockaddr*)_sinLocal, sizeof(*_sinLocal));
}
// set the socket to be nonblocking
#ifdef PLATFORM_WIN32
DWORD dwNonBlocking = 1;
if(ioctlsocket(_socket, FIONBIO, &dwNonBlocking) != 0) {
CPrintF("Error setting socket to nonblocking!\n");
_uninitWinsock();
return;
}
#else
int flags = fcntl(_socket, F_GETFL);
int failed = flags;
if (failed != -1) {
flags |= O_NONBLOCK;
failed = fcntl(_socket, F_SETFL, flags);
}
if (failed == -1) {
CPrintF("Error setting socket to nonblocking!\n");
_uninitWinsock();
return;
}
#endif
}
void _uninitWinsock()
{
if (_socket != INVALID_SOCKET) {
closesocket(_socket);
_socket = INVALID_SOCKET;
}
#ifdef PLATFORM_WIN32
if(_wsaData != NULL) {
delete _wsaData;
_wsaData = NULL;
}
#endif
}
void _sendPacketTo(const char* pubBuffer, INDEX iLen, sockaddr_in* sin)
{
sendto(_socket, pubBuffer, iLen, 0, (sockaddr*)sin, sizeof(sockaddr_in));
}
void _sendPacketTo(const char* szBuffer, sockaddr_in* addsin)
{
sendto(_socket, szBuffer, strlen(szBuffer), 0, (sockaddr*)addsin, sizeof(sockaddr_in));
}
void _sendPacket(const char* pubBuffer, INDEX iLen)
{
_initializeWinsock();
_sendPacketTo(pubBuffer, iLen, _sin);
}
void _sendPacket(const char* szBuffer)
{
_initializeWinsock();
_sendPacketTo(szBuffer, _sin);
}
int _recvPacket()
{
socklen_t fromLength = sizeof(_sinFrom);
return recvfrom(_socket, _szBuffer, 2048, 0, (sockaddr*)&_sinFrom, &fromLength);
}
CTString _getGameModeName(INDEX iGameMode)
{
// get function that will provide us the info about gametype
CShellSymbol *pss = _pShell->GetSymbol("GetGameTypeNameSS", /*bDeclaredOnly=*/ TRUE);
if(pss == NULL) {
return "";
}
CTString (*pFunc)(INDEX) = (CTString (*)(INDEX))pss->ss_pvValue;
return pFunc(iGameMode);
}
const CSessionProperties* _getSP()
{
return ((const CSessionProperties *)_pNetwork->GetSessionProperties());
}
void _sendHeartbeat(INDEX iChallenge)
{
CTString strPacket;
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,
(const char *) _pNetwork->ga_World.wo_strName,
(const char *) _getGameModeName(_getSP()->sp_gmGameMode),
_SE_VER_STRING,
(const char *) _pShell->GetString("sam_strGameName"));
} else {
strPacket.PrintF("\\heartbeat\\%hu\\gamename\\serioussamse", (_pShell->GetINDEX("net_iPort") + 1));
}
_sendPacket(strPacket);
_tmLastHeartbeat = _pTimer->GetRealTimeTick();
}
static void _setStatus(const CTString &strStatus)
{
_pNetwork->ga_bEnumerationChange = TRUE;
_pNetwork->ga_strEnumerationStatus = strStatus;
}
CServerRequest::CServerRequest(void)
{
Clear();
}
CServerRequest::~CServerRequest(void) { }
void CServerRequest::Clear(void)
{
sr_ulAddress = 0;
sr_iPort = 0;
sr_tmRequestTime = 0;
}
/// Initialize GameAgent.
extern void GameAgent_ServerInit(void)
{
// join
_bServer = TRUE;
_bInitialized = TRUE;
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.
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;
}
/// GameAgent server update call which responds to enumeration pings and sends pings to masterserver.
extern void GameAgent_ServerUpdate(void)
{
if((_socket == INVALID_SOCKET) || (!_bInitialized)) {
return;
}
int iLen = _recvPacket();
if(iLen > 0) {
if (!ga_bMSLegacy) {
// check the received packet ID
switch(_szBuffer[0]) {
case 1: // server join response
{
int iChallenge = *(INDEX*)(_szBuffer + 1);
// send the challenge
_sendHeartbeat(iChallenge);
break;
}
case 2: // server status request
{
// send the status response
CTString strPacket;
strPacket.PrintF("0;players;%d;maxplayers;%d;level;%s;gametype;%s;version;%s;gamename;%s;sessionname;%s",
_pNetwork->ga_srvServer.GetPlayersCount(),
_pNetwork->ga_sesSessionState.ses_ctMaxPlayers,
(const char *) _pNetwork->ga_World.wo_strName,
(const char *) _getGameModeName(_getSP()->sp_gmGameMode),
_SE_VER_STRING,
(const char *) _pShell->GetString("sam_strGameName"),
(const char *) _pShell->GetString("gam_strSessionName"));
_sendPacketTo(strPacket, &_sinFrom);
break;
}
case 3: // player status request
{
// send the player status response
CTString strPacket;
strPacket.PrintF("\x01players\x02%d\x03", _pNetwork->ga_srvServer.GetPlayersCount());
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->GetGameAgentPlayerInfo(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 += "\x04";
_sendPacketTo(strPacket, &_sinFrom);
break;
}
case 4: // ping
{
// just send back 1 byte and the amount of players in the server (this could be useful in some cases for external scripts)
CTString strPacket;
strPacket.PrintF("\x04%d", _pNetwork->ga_srvServer.GetPlayersCount());
_sendPacketTo(strPacket, &_sinFrom);
break;
}
}
} else {
_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,
(const char *) _pShell->GetString("sam_strGameName"),
_SE_VER_STRING,
//(const char *) _pShell->GetString("net_strLocalHost"),
(const char *) strLocation,
(const char *) _pShell->GetString("gam_strSessionName"),
_pShell->GetINDEX("net_iPort"),
(const char *) _pNetwork->ga_World.wo_strName,
(const char *) _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,
(const char *) _pShell->GetString("gam_strSessionName"),
_pShell->GetINDEX("net_iPort"),
(const char *) _pNetwork->ga_World.wo_strName,
(const char *) _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,
(const char *) _pShell->GetString("sam_strGameName"),
_SE_VER_STRING,
//(const char *) _pShell->GetString("net_strLocalHost"));
(const char *) 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) {
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 ( _pNetwork->ga_bEnumerationChange ) {
return;
}
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 = ".";
PHOSTENT _phHostinfo;
ULONG _uIP,*_pchIP = &_uIP;
USHORT _uPort,*_pchPort = &_uPort;
LONG _iLen;
char _cName[256],*_pch,_strFinal[8] = {0};
struct in_addr addr;
// make the buffer that we'll use for packet reading
if(_szIPPortBufferLocal != NULL) {
return;
}
_szIPPortBufferLocal = new char[1024];
#ifdef PLATFORM_WIN32
// start WSA
WSADATA wsaData;
const WORD _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;
}
#endif
_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 = INVALID_SOCKET;
u_int uiMSIP;
int iErr,
iLen,
iDynsz,
iEnctype = 0;
u_short usMSport = MSPORT;
u_char ucGamekey[] = {SERIOUSSAMKEY},
ucGamestr[] = {SERIOUSSAMSTR},
*ucSec = NULL,
*ucKey = NULL;
const char *cFilter = "";
const char *cWhere = "";
char cMS[128] = {0};
char *cResponse = NULL;
char *cMsstring = NULL;
char *cSec = NULL;
strcpy(cMS,ga_strMSLegacy);
#if PLATFORM_WIN32
WSADATA wsadata;
if(WSAStartup(MAKEWORD(2,2), &wsadata) != 0) {
CPrintF("Error initializing winsock!\n");
return;
}
#endif
/* 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 == INVALID_SOCKET) || (!_bInitialized)) {
return;
}
if (!ga_bMSLegacy) {
int iLen = _recvPacket();
if(iLen != -1) {
// null terminate the buffer
_szBuffer[iLen] = 0;
switch(_szBuffer[0]) {
case 's':
{
// !!! FIXME: serialize this and byteswap it. --ryan.
#pragma pack(1)
struct sIPPort {
UBYTE bFirst;
UBYTE bSecond;
UBYTE bThird;
UBYTE bFourth;
USHORT iPort;
};
#pragma pack()
_pNetwork->ga_strEnumerationStatus = "";
sIPPort* pServers = (sIPPort*)(_szBuffer + 1);
while(iLen - ((CHAR*)pServers - _szBuffer) >= sizeof(sIPPort)) {
sIPPort ip = *pServers;
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 = htons(ip.iPort + 1);
// 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
_sendPacketTo("\x02", &sinServer);
pServers++;
}
}
break;
case '0':
{
CTString strPlayers;
CTString strMaxPlayers;
CTString strLevel;
CTString strGameType;
CTString strVersion;
CTString strGameName;
CTString strSessionName;
CHAR* pszPacket = _szBuffer + 2; // we do +2 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 != "sessionname") {
if(bReadValue) {
// we're done reading the value, check which key it was
if(strKey == "players") {
strPlayers = strValue;
} else if(strKey == "maxplayers") {
strMaxPlayers = strValue;
} else if(strKey == "level") {
strLevel = strValue;
} else if(strKey == "gametype") {
strGameType = strValue;
} else if(strKey == "version") {
strVersion = strValue;
} else if(strKey == "gamename") {
strGameName = strValue;
} else {
CPrintF("Unknown GameAgent parameter key '%s'!", (const char *) 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 sessionname to back up
if(strKey == "sessionname") {
strSessionName = strValue;
}
// insert the server into the serverlist
CNetworkSession &ns = *new CNetworkSession;
_pNetwork->ga_lhEnumeratedSessions.AddTail(ns.ns_lnNode);
long long tmPing = -1;
// find the request in the request array
for(INDEX i=0; i<ga_asrRequests.Count(); i++) {
CServerRequest &req = ga_asrRequests[i];
if(req.sr_ulAddress == _sinFrom.sin_addr.s_addr && req.sr_iPort == _sinFrom.sin_port) {
tmPing = _pTimer->GetHighPrecisionTimer().GetMilliseconds() - req.sr_tmRequestTime;
ga_asrRequests.Delete(&req);
break;
}
}
if(tmPing == -1) {
// server status was never requested
break;
}
// add the server to the serverlist
ns.ns_strSession = strSessionName;
ns.ns_strAddress = inet_ntoa(_sinFrom.sin_addr) + CTString(":") + CTString(0, "%d", htons(_sinFrom.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;
}
break;
default:
CPrintF("Unknown enum packet ID %x!\n", _szBuffer[0]);
break;
}
}
} else {
#ifndef PLATFORM_WIN32
//STUBBED("write me");
/* MSLegacy LinuxPort */
if(_bActivated) {
pthread_t _hThread;
int _iThreadId;
_iThreadId = pthread_create(&_hThread, NULL, _MS_Thread, NULL);
if (_iThreadId == 0) {
pthread_detach(_hThread);
}
_bActivated = FALSE;
}
if(_bActivatedLocal) {
pthread_t _hThread;
int _iThreadId;
_iThreadId = pthread_create(&_hThread, NULL, _LocalNet_Thread, NULL);
if (_iThreadId == 0) {
pthread_detach(_hThread);
}
_bActivatedLocal = FALSE;
}
#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;
}
#endif
}
}
/// Cancel the GameAgent serverlist enumeration.
extern void GameAgent_EnumCancel(void)
{
if (_bInitialized) {
CPrintF("...GameAgent_EnumCancel!\n");
ga_asrRequests.Clear();
_uninitWinsock();
}
}
#ifdef PLATFORM_WIN32
DWORD WINAPI _MS_Thread(LPVOID lpParam) {
#else
void* _MS_Thread(void *arg) {
#endif
SOCKET _sockudp = INVALID_SOCKET;
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 0;
}
_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;
socklen_t _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 0;
} 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; i<ga_asrRequests.Count(); i++) {
CServerRequest &req = ga_asrRequests[i];
if(req.sr_ulAddress == _sinClient.sin_addr.s_addr && req.sr_iPort == _sinClient.sin_port) {
tmPing = _pTimer->GetHighPrecisionTimer().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; i<ga_asrRequests.Count(); i++) {
CServerRequest &req = ga_asrRequests[i];
if(req.sr_ulAddress == _sinClient.sin_addr.s_addr && req.sr_iPort == _sinClient.sin_port) {
ga_asrRequests.Delete(&req);
break;
}
}
}
}
pServerIP++;
_iIPPortBufferLen -= 6;
}
if(_szIPPortBuffer) free (_szIPPortBuffer);
_szIPPortBuffer = NULL;
closesocket(_sockudp);
_uninitWinsock();
_bInitialized = FALSE;
_pNetwork->ga_bEnumerationChange = FALSE;
WSACleanup();
return 0;
}
#ifdef PLATFORM_WIN32
DWORD WINAPI _LocalNet_Thread(LPVOID lpParam) {
#else
void* _LocalNet_Thread(void *arg) {
#endif
SOCKET _sockudp = INVALID_SOCKET;
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 0;
}
_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;
socklen_t _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 0;
} 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; i<ga_asrRequests.Count(); i++) {
CServerRequest &req = ga_asrRequests[i];
if(req.sr_ulAddress == _sinClient.sin_addr.s_addr && req.sr_iPort == _sinClient.sin_port) {
tmPing = _pTimer->GetHighPrecisionTimer().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; i<ga_asrRequests.Count(); i++) {
CServerRequest &req = ga_asrRequests[i];
if(req.sr_ulAddress == _sinClient.sin_addr.s_addr && req.sr_iPort == _sinClient.sin_port) {
ga_asrRequests.Delete(&req);
break;
}
}
}
}
pServerIP++;
_iIPPortBufferLocalLen -= 6;
}
if(_szIPPortBufferLocal != NULL) {
delete[] _szIPPortBufferLocal;
}
_szIPPortBufferLocal = NULL;
closesocket(_sockudp);
_uninitWinsock();
_bInitialized = FALSE;
_pNetwork->ga_bEnumerationChange = FALSE;
_pNetwork->ga_strEnumerationStatus = "";
WSACleanup();
return 0;
}