/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** 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 3 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, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: PeerThread.cpp //////////////////////////////////////////////////////
// GameSpy Peer (chat) thread
// This thread communicates with GameSpy's chat server
// and talks through a message queue with the rest of
// the game.
// Author: Matthew D. Campbell, June 2002
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Registry.h"
#include "Common/StackDump.h"
#include "Common/UserPreferences.h"
#include "Common/Version.h"
#include "GameNetwork/IPEnumeration.h"
#include "GameNetwork/GameSpy/BuddyThread.h"
#include "GameNetwork/GameSpy/PeerDefs.h"
#include "GameNetwork/GameSpy/PeerThread.h"
#include "GameNetwork/GameSpy/PersistentStorageThread.h"
#include "GameNetwork/GameSpy/ThreadUtils.h"
#include "strtok_r.h"
#include "mutex.h"
#include "thread.h"
#include "Common/MiniLog.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// enable this for trying to track down why SBServers are losing their keyvals -MDC 2/20/2003
#undef SERVER_DEBUGGING
#ifdef SERVER_DEBUGGING
void CheckServers(PEER peer);
#endif // SERVER_DEBUGGING
#ifdef DEBUG_LOGGING
//#define PING_TEST
static LogClass s_pingLog("Ping.txt");
#define PING_LOG(x) s_pingLog.log x
#else // DEBUG_LOGGING
#define PING_LOG(x) {}
#endif // DEBUG_LOGGING
#ifdef DEBUG_LOGGING
static LogClass s_stateChangedLog("StateChanged.txt");
#define STATECHANGED_LOG(x) s_stateChangedLog.log x
#else // DEBUG_LOGGING
#define STATECHANGED_LOG(x) {}
#endif // DEBUG_LOGGING
// we should always be using broadcast keys from now on. Remove the old code sometime when
// we're not in a rush, ok?
// -MDC 2/14/2003
#define USE_BROADCAST_KEYS
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
int isThreadHosting = 0;
static UnsignedInt s_lastStateChangedHeartbeat = 0;
static Bool s_wantStateChangedHeartbeat = FALSE;
static UnsignedInt s_heartbeatInterval = 10000;
static SOCKET qr2Sock = INVALID_SOCKET;
enum
{
EXECRC_KEY = NUM_RESERVED_KEYS + 1,
INICRC_KEY,
PW_KEY,
OBS_KEY,
USE_STATS_KEY,
LADIP_KEY,
LADPORT_KEY,
PINGSTR_KEY,
NUMPLAYER_KEY,
MAXPLAYER_KEY,
NUMOBS_KEY,
NAME__KEY,
FACTION__KEY,
COLOR__KEY,
WINS__KEY,
LOSSES__KEY
};
#define EXECRC_STR "exeCRC"
#define INICRC_STR "iniCRC"
#define PW_STR "pw"
#define OBS_STR "obs"
#define USE_STATS_STR "stat"
#define LADIP_STR "ladIP"
#define LADPORT_STR "ladPort"
#define PINGSTR_STR "pings"
#define NUMPLAYER_STR "numRealPlayers"
#define MAXPLAYER_STR "maxRealPlayers"
#define NUMOBS_STR "numObservers"
#define NAME__STR "name"
#define FACTION__STR "faction"
#define COLOR__STR "color"
#define WINS__STR "wins"
#define LOSSES__STR "losses"
//-------------------------------------------------------------------------
typedef std::queue RequestQueue;
typedef std::queue ResponseQueue;
class PeerThreadClass;
class GameSpyPeerMessageQueue : public GameSpyPeerMessageQueueInterface
{
public:
virtual ~GameSpyPeerMessageQueue();
GameSpyPeerMessageQueue();
virtual void startThread( void );
virtual void endThread( void );
virtual Bool isThreadRunning( void );
virtual Bool isConnected( void );
virtual Bool isConnecting( void );
virtual void addRequest( const PeerRequest& req );
virtual Bool getRequest( PeerRequest& req );
virtual void addResponse( const PeerResponse& resp );
virtual Bool getResponse( PeerResponse& resp );
virtual SerialAuthResult getSerialAuthResult( void ) { return m_serialAuth; }
void setSerialAuthResult( SerialAuthResult result ) { m_serialAuth = result; }
PeerThreadClass* getThread( void );
private:
MutexClass m_requestMutex;
MutexClass m_responseMutex;
RequestQueue m_requests;
ResponseQueue m_responses;
PeerThreadClass *m_thread;
SerialAuthResult m_serialAuth;
};
GameSpyPeerMessageQueueInterface* GameSpyPeerMessageQueueInterface::createNewMessageQueue( void )
{
return NEW GameSpyPeerMessageQueue;
}
GameSpyPeerMessageQueueInterface *TheGameSpyPeerMessageQueue;
#define MESSAGE_QUEUE ((GameSpyPeerMessageQueue *)TheGameSpyPeerMessageQueue)
//-------------------------------------------------------------------------
class PeerThreadClass : public ThreadClass
{
public:
PeerThreadClass() : ThreadClass()
{
//Added By Sadullah Nader
//Initializations inserted
m_roomJoined = m_allowObservers = m_hasPassword = FALSE;
m_useStats = TRUE;
m_exeCRC = m_iniCRC = 0;
m_gameVersion = 0;
m_ladderPort = 0;
m_localRoomID = 0;
m_maxPlayers = 0;
m_numObservers = 0;
m_maxPlayers = 0;
m_qmGroupRoom = 0;
m_sawEndOfEnumPlayers = m_sawMatchbot = FALSE;
m_sawCompleteGameList = FALSE;
//
m_isConnecting = m_isConnected = false;
m_groupRoomID = m_profileID = 0;
m_nextStagingServer = 1; m_stagingServers.clear();
m_pingStr = ""; m_mapName = ""; m_ladderIP = ""; m_isHosting = false;
for (Int i=0; i PlayerStatMap;
PlayerStatMap m_groupRoomStats;
PlayerStatMap m_stagingRoomStats;
std::string packStatKey(const char *nick, const char *key);
#endif // USE_BROADCAST_KEYS
// game-hosting info for GOA callbacks
Bool m_isHosting;
Bool m_hasPassword;
std::string m_mapName;
std::string m_playerNames[MAX_SLOTS];
UnsignedInt m_exeCRC;
UnsignedInt m_iniCRC;
UnsignedInt m_gameVersion;
Bool m_allowObservers;
Bool m_useStats;
std::string m_pingStr;
std::string m_ladderIP;
UnsignedShort m_ladderPort;
Int m_playerWins[MAX_SLOTS];
Int m_playerLosses[MAX_SLOTS];
Int m_playerProfileID[MAX_SLOTS];
Int m_playerColors[MAX_SLOTS];
Int m_playerFactions[MAX_SLOTS];
Int m_numPlayers;
Int m_maxPlayers;
Int m_numObservers;
Int m_nextStagingServer;
std::map m_stagingServers;
std::wstring m_localStagingServerName;
Int m_localRoomID;
void doQuickMatch( PEER peer );
QMStatus m_qmStatus;
PeerRequest m_qmInfo;
Bool m_roomJoined;
Int m_qmGroupRoom;
Bool m_sawEndOfEnumPlayers;
Bool m_sawMatchbot;
std::string m_matchbotName;
};
#ifdef USE_BROADCAST_KEYS
const char* PeerThreadClass::s_keys[6] = { "b_locale", "b_wins", "b_losses", "b_points", "b_side", "b_pre" };
char PeerThreadClass::s_valueBuffers[6][20] = { "", "", "", "", "", "" };
const char* PeerThreadClass::s_values[6] = { s_valueBuffers[0], s_valueBuffers[1], s_valueBuffers[2],
s_valueBuffers[3], s_valueBuffers[4], s_valueBuffers[5]};
void PeerThreadClass::trackStatsForPlayer(RoomType roomType, const char *nick, const char *key, const char *val)
{
switch (roomType)
{
case GroupRoom:
m_groupRoomStats[packStatKey(nick, key)] = atoi(val);
break;
case StagingRoom:
m_stagingRoomStats[packStatKey(nick, key)] = atoi(val);
break;
}
}
std::string PeerThreadClass::packStatKey(const char *nick, const char *key)
{
std::string s = nick;
s.append(key);
return s;
}
int PeerThreadClass::lookupStatForPlayer(RoomType roomType, const char *nick, const char *key)
{
std::string fullKey = packStatKey(nick, key);
PlayerStatMap::const_iterator it;
switch (roomType)
{
case GroupRoom:
it = m_groupRoomStats.find(fullKey);
if (it != m_groupRoomStats.end())
return it->second;
break;
case StagingRoom:
it = m_stagingRoomStats.find(fullKey);
if (it != m_stagingRoomStats.end())
return it->second;
break;
}
return 0;
}
void PeerThreadClass::clearPlayerStats(RoomType roomType)
{
switch (roomType)
{
case GroupRoom:
m_groupRoomStats.clear();
break;
case StagingRoom:
m_stagingRoomStats.clear();
break;
}
}
void PeerThreadClass::pushStatsToRoom(PEER peer)
{
DEBUG_LOG(("PeerThreadClass::pushStatsToRoom(): stats are %s=%s,%s=%s,%s=%s,%s=%s,%s=%s,%s=%s\n",
s_keys[0], s_values[0],
s_keys[1], s_values[1],
s_keys[2], s_values[2],
s_keys[3], s_values[3],
s_keys[4], s_values[4],
s_keys[5], s_values[5]));
peerSetRoomKeys(peer, GroupRoom, m_loginName.c_str(), 6, s_keys, s_values);
peerSetRoomKeys(peer, StagingRoom, m_loginName.c_str(), 6, s_keys, s_values);
}
void getRoomKeysCallback(PEER peer, PEERBool success, RoomType roomType, const char *nick, int num, char **keys, char **values, void *param);
void PeerThreadClass::getStatsFromRoom(PEER peer, RoomType roomType)
{
peerGetRoomKeys(peer, GroupRoom, "*", NumKeys, s_keys, getRoomKeysCallback, this, PEERFalse);
}
#endif // USE_BROADCAST_KEYS
Int PeerThreadClass::addServerToMap( SBServer server )
{
Int val = m_nextStagingServer++;
m_stagingServers[val] = server;
return val;
}
Int PeerThreadClass::removeServerFromMap( SBServer server )
{
for (std::map::iterator it = m_stagingServers.begin(); it != m_stagingServers.end(); ++it)
{
if (it->second == server)
{
Int val = it->first;
m_stagingServers.erase(it);
return val;
}
}
return 0;
}
void PeerThreadClass::clearServers( void )
{
m_stagingServers.clear();
}
SBServer PeerThreadClass::findServerByID( Int id )
{
std::map::iterator it = m_stagingServers.find(id);
if (it != m_stagingServers.end())
{
SBServer server = it->second;
if (server && !server->keyvals)
{
DEBUG_CRASH(("Referencing a missing server!"));
return 0;
}
return it->second;
}
return 0;
}
Int PeerThreadClass::findServer( SBServer server )
{
char tmp[10] = "";
const char *newName = SBServerGetStringValue(server, "gamename", tmp);
UnsignedInt newPrivateIP = SBServerGetPrivateInetAddress(server);
UnsignedShort newPrivatePort = SBServerGetPrivateQueryPort(server);
UnsignedInt newPublicIP = SBServerGetPublicInetAddress(server);
SBServer serverToRemove = NULL;
for (std::map::iterator it = m_stagingServers.begin(); it != m_stagingServers.end(); ++it)
{
if (it->second == server)
{
return it->first;
}
else
{
const char *oldName = SBServerGetStringValue(it->second, "gamename", tmp);
UnsignedInt oldPrivateIP = SBServerGetPrivateInetAddress(it->second);
UnsignedShort oldPrivatePort = SBServerGetPrivateQueryPort(it->second);
UnsignedInt oldPublicIP = SBServerGetPublicInetAddress(it->second);
if (!strcmp(oldName, newName) &&
oldPrivateIP == newPrivateIP &&
oldPublicIP == newPublicIP &&
oldPrivatePort == newPrivatePort)
{
serverToRemove = it->second;
}
}
}
if (serverToRemove)
{
// this is the same as another game - it has just migrated to another port. Remove the old and replace it.
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_STAGINGROOM;
resp.stagingRoom.id = removeServerFromMap( serverToRemove );
resp.stagingRoom.action = PEER_REMOVE;
resp.stagingRoom.isStaging = TRUE;
resp.stagingRoom.percentComplete = -1;
TheGameSpyPeerMessageQueue->addResponse(resp);
}
return addServerToMap(server);
}
static enum CallbackType
{
CALLBACK_CONNECT,
CALLBACK_ERROR,
CALLBACK_RECVMESSAGE,
CALLBACK_RECVREQUEST,
CALLBACK_RECVSTATUS,
CALLBACK_MAX
};
void connectCallbackWrapper( PEER peer, PEERBool success, void *param )
{
#ifdef SERVER_DEBUGGING
DEBUG_LOG(("In connectCallbackWrapper()\n"));
CheckServers(peer);
#endif // SERVER_DEBUGGING
if (param != NULL)
{
((PeerThreadClass *)param)->connectCallback( peer, success );
}
}
void nickErrorCallbackWrapper( PEER peer, Int type, const char *nick, void *param )
{
if (param != NULL)
{
((PeerThreadClass *)param)->nickErrorCallback( peer, type, nick );
}
}
static void joinRoomCallback(PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, void *param);
//-------------------------------------------------------------------------
GameSpyPeerMessageQueue::GameSpyPeerMessageQueue()
{
m_thread = NULL;
m_serialAuth = SERIAL_OK;
}
GameSpyPeerMessageQueue::~GameSpyPeerMessageQueue()
{
endThread();
}
void GameSpyPeerMessageQueue::startThread( void )
{
if (!m_thread)
{
m_thread = NEW PeerThreadClass;
m_thread->Execute();
}
else
{
if (!m_thread->Is_Running())
{
m_thread->Execute();
}
}
}
void GameSpyPeerMessageQueue::endThread( void )
{
if (m_thread)
delete m_thread;
m_thread = NULL;
}
Bool GameSpyPeerMessageQueue::isThreadRunning( void )
{
return (m_thread) ? m_thread->Is_Running() : false;
}
Bool GameSpyPeerMessageQueue::isConnected( void )
{
return (m_thread) ? m_thread->isConnected() : false;
}
Bool GameSpyPeerMessageQueue::isConnecting( void )
{
return (m_thread) ? m_thread->isConnecting() : false;
}
void GameSpyPeerMessageQueue::addRequest( const PeerRequest& req )
{
MutexClass::LockClass m(m_requestMutex);
if (m.Failed())
return;
m_requests.push(req);
}
//PeerRequest GameSpyPeerMessageQueue::getRequest( void )
Bool GameSpyPeerMessageQueue::getRequest( PeerRequest& req )
{
MutexClass::LockClass m(m_requestMutex, 0);
if (m.Failed())
return false;
if (m_requests.empty())
return false;
req = m_requests.front();
m_requests.pop();
return true;
}
void GameSpyPeerMessageQueue::addResponse( const PeerResponse& resp )
{
if (resp.nick == "(END)")
return;
MutexClass::LockClass m(m_responseMutex);
if (m.Failed())
return;
m_responses.push(resp);
}
//PeerResponse GameSpyPeerMessageQueue::getResponse( void )
Bool GameSpyPeerMessageQueue::getResponse( PeerResponse& resp )
{
MutexClass::LockClass m(m_responseMutex, 0);
if (m.Failed())
return false;
if (m_responses.empty())
return false;
resp = m_responses.front();
m_responses.pop();
return true;
}
PeerThreadClass* GameSpyPeerMessageQueue::getThread( void )
{
return m_thread;
}
//-------------------------------------------------------------------------
static void disconnectedCallback(PEER peer, const char * reason, void * param);
static void roomMessageCallback(PEER peer, RoomType roomType, const char * nick, const char * message, MessageType messageType, void * param);
static void playerMessageCallback(PEER peer, const char * nick, const char * message, MessageType messageType, void * param);
static void playerJoinedCallback(PEER peer, RoomType roomType, const char * nick, void * param);
static void playerLeftCallback(PEER peer, RoomType roomType, const char * nick, const char * reason, void * param);
static void playerChangedNickCallback(PEER peer, RoomType roomType, const char * oldNick, const char * newNick, void * param);
static void playerInfoCallback(PEER peer, RoomType roomType, const char * nick, unsigned int IP, int profileID, void * param);
static void playerFlagsChangedCallback(PEER peer, RoomType roomType, const char * nick, int oldFlags, int newFlags, void * param);
static void listingGamesCallback(PEER peer, PEERBool success, const char * name, SBServer server, PEERBool staging, int msg, Int percentListed, void * param);
static void roomUTMCallback(PEER peer, RoomType roomType, const char * nick, const char * command, const char * parameters, PEERBool authenticated, void * param);
static void playerUTMCallback(PEER peer, const char * nick, const char * command, const char * parameters, PEERBool authenticated, void * param);
static void gameStartedCallback(PEER peer, UnsignedInt IP, const char *message, void *param);
static void globalKeyChangedCallback(PEER peer, const char *nick, const char *key, const char *val, void *param);
static void roomKeyChangedCallback(PEER peer, RoomType roomType, const char *nick, const char *key, const char *val, void *param);
// convenience function to set buddy status
static void updateBuddyStatus( GameSpyBuddyStatus status, Int groupRoom = 0, std::string gameName = "" )
{
if (!TheGameSpyBuddyMessageQueue)
return;
BuddyRequest req;
req.buddyRequestType = BuddyRequest::BUDDYREQUEST_SETSTATUS;
switch(status)
{
case BUDDY_OFFLINE:
req.arg.status.status = GP_OFFLINE;
strcpy(req.arg.status.statusString, "Offline");
strcpy(req.arg.status.locationString, "");
break;
case BUDDY_ONLINE:
req.arg.status.status = GP_ONLINE;
strcpy(req.arg.status.statusString, "Online");
strcpy(req.arg.status.locationString, "");
break;
case BUDDY_LOBBY:
req.arg.status.status = GP_CHATTING;
strcpy(req.arg.status.statusString, "Chatting");
sprintf(req.arg.status.locationString, "%d", groupRoom);
break;
case BUDDY_STAGING:
req.arg.status.status = GP_STAGING;
strcpy(req.arg.status.statusString, "Staging");
sprintf(req.arg.status.locationString, "%s", gameName.c_str());
break;
case BUDDY_LOADING:
req.arg.status.status = GP_PLAYING;
strcpy(req.arg.status.statusString, "Loading");
sprintf(req.arg.status.locationString, "%s", gameName.c_str());
break;
case BUDDY_PLAYING:
req.arg.status.status = GP_PLAYING;
strcpy(req.arg.status.statusString, "Playing");
sprintf(req.arg.status.locationString, "%s", gameName.c_str());
break;
case BUDDY_MATCHING:
req.arg.status.status = GP_ONLINE;
strcpy(req.arg.status.statusString, "Matching");
strcpy(req.arg.status.locationString, "");
break;
}
DEBUG_LOG(("updateBuddyStatus %d:%s\n", req.arg.status.status, req.arg.status.statusString));
TheGameSpyBuddyMessageQueue->addRequest(req);
}
static void createRoomCallback(PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, void *param)
{
Int *s = (Int *)param;
if (s)
*s = result;
}
static const char * KeyTypeToString(qr2_key_type type)
{
switch(type)
{
case key_server:
return "server";
case key_player:
return "player";
case key_team:
return "team";
}
return "Unkown key type";
}
static const char * ErrorTypeToString(qr2_error_t error)
{
switch(error)
{
case e_qrnoerror:
return "noerror";
case e_qrwsockerror:
return "wsockerror";
case e_qrbinderror:
return "rbinderror";
case e_qrdnserror:
return "dnserror";
case e_qrconnerror:
return "connerror";
}
return "Unknown error type";
}
static void QRServerKeyCallback
(
PEER peer,
int key,
qr2_buffer_t buffer,
void * param
)
{
//DEBUG_LOG(("QR_SERVER_KEY | %d (%s)\n", key, qr2_registered_key_list[key]));
PeerThreadClass *t = (PeerThreadClass *)param;
if (!t)
{
DEBUG_LOG(("QRServerKeyCallback: bailing because of no thread info\n"));
return;
}
if (!t->isHosting())
t->stopHostingAlready(peer);
#ifdef DEBUG_LOGGING
AsciiString val = "";
#define ADD(x) { qr2_buffer_add(buffer, x); val = x; }
#define ADDINT(x) { qr2_buffer_add_int(buffer, x); val.format("%d",x); }
#else
#define ADD(x) { qr2_buffer_add(buffer, x); }
#define ADDINT(x) { qr2_buffer_add_int(buffer, x); }
#endif
switch(key)
{
case HOSTNAME_KEY:
ADD(t->getPlayerName(0).c_str());
break;
case GAMEVER_KEY:
ADDINT(t->gameVersion());
break;
case EXECRC_KEY:
ADDINT(t->exeCRC());
break;
case INICRC_KEY:
ADDINT(t->iniCRC());
break;
case GAMENAME_KEY:
{
std::string tmp = t->getPlayerName(0);
tmp.append(" ");
tmp.append(WideCharStringToMultiByte(t->getLocalStagingServerName().c_str()));
ADD(tmp.c_str());
}
break;
case MAPNAME_KEY:
ADD(t->getMapName().c_str());
break;
case PW_KEY:
ADDINT(t->hasPassword());
break;
case OBS_KEY:
ADDINT(t->allowObservers());
break;
case USE_STATS_KEY:
ADDINT(t->useStats());
break;
case LADIP_KEY:
ADD(t->ladderIP().c_str());
break;
case LADPORT_KEY:
ADDINT(t->ladderPort());
break;
case PINGSTR_KEY:
ADD(t->pingStr().c_str());
break;
case NUMPLAYER_KEY:
ADDINT(t->getNumPlayers());
break;
case MAXPLAYER_KEY:
ADDINT(t->getMaxPlayers());
break;
case NUMOBS_KEY:
ADDINT(t->getNumObservers());
break;
default:
ADD("");
//DEBUG_LOG(("QR_SERVER_KEY | %d (%s)\n", key, qr2_registered_key_list[key]));
break;
}
DEBUG_LOG(("QR_SERVER_KEY | %d (%s) = [%s]\n", key, qr2_registered_key_list[key], val.str()));
}
static void QRPlayerKeyCallback
(
PEER peer,
int key,
int index,
qr2_buffer_t buffer,
void * param
)
{
//DEBUG_LOG(("QR_PLAYER_KEY | %d | %d (%s)\n", key, index, qr2_registered_key_list[key]));
PeerThreadClass *t = (PeerThreadClass *)param;
if (!t)
{
DEBUG_LOG(("QRPlayerKeyCallback: bailing because of no thread info\n"));
return;
}
if (!t->isHosting())
t->stopHostingAlready(peer);
#undef ADD
#undef ADDINT
#ifdef DEBUG_LOGGING
AsciiString val = "";
#define ADD(x) { qr2_buffer_add(buffer, x); val = x; }
#define ADDINT(x) { qr2_buffer_add_int(buffer, x); val.format("%d",x); }
#else
#define ADD(x) { qr2_buffer_add(buffer, x); }
#define ADDINT(x) { qr2_buffer_add_int(buffer, x); }
#endif
switch(key)
{
case NAME__KEY:
ADD(t->getPlayerName(index).c_str());
break;
case WINS__KEY:
ADDINT(t->getPlayerWins(index));
break;
case LOSSES__KEY:
ADDINT(t->getPlayerLosses(index));
break;
case PID__KEY:
ADDINT(t->getPlayerProfileID(index));
break;
case FACTION__KEY:
ADDINT(t->getPlayerLosses(index));
break;
case COLOR__KEY:
ADDINT(t->getPlayerLosses(index));
break;
default:
ADD("");
//DEBUG_LOG(("QR_PLAYER_KEY | %d | %d (%s)\n", key, index, qr2_registered_key_list[key]));
break;
}
DEBUG_LOG(("QR_PLAYER_KEY | %d | %d (%s) = [%s]\n", key, index, qr2_registered_key_list[key], val.str()));
}
static void QRTeamKeyCallback
(
PEER peer,
int key,
int index,
qr2_buffer_t buffer,
void * param
)
{
//DEBUG_LOG(("QR_TEAM_KEY | %d | %d\n", key, index));
PeerThreadClass *t = (PeerThreadClass *)param;
if (!t)
{
DEBUG_LOG(("QRTeamKeyCallback: bailing because of no thread info\n"));
return;
}
if (!t->isHosting())
t->stopHostingAlready(peer);
// we don't report teams, so this shouldn't get called
qr2_buffer_add(buffer, "");
}
static void QRKeyListCallback
(
PEER peer,
qr2_key_type type,
qr2_keybuffer_t keyBuffer,
void * param
)
{
DEBUG_LOG(("QR_KEY_LIST | %s\n", KeyTypeToString(type)));
/*
PeerThreadClass *t = (PeerThreadClass *)param;
if (!t)
{
DEBUG_LOG(("QRKeyListCallback: bailing because of no thread info\n"));
return;
}
if (!t->isHosting())
t->stopHostingAlready(peer);
*/
// register the keys we use
switch(type)
{
case key_server:
qr2_keybuffer_add(keyBuffer, HOSTNAME_KEY);
qr2_keybuffer_add(keyBuffer, GAMEVER_KEY);
//qr2_keybuffer_add(keyBuffer, GAMENAME_KEY);
qr2_keybuffer_add(keyBuffer, MAPNAME_KEY);
qr2_keybuffer_add(keyBuffer, EXECRC_KEY);
qr2_keybuffer_add(keyBuffer, INICRC_KEY);
qr2_keybuffer_add(keyBuffer, PW_KEY);
qr2_keybuffer_add(keyBuffer, OBS_KEY);
qr2_keybuffer_add(keyBuffer, USE_STATS_KEY);
qr2_keybuffer_add(keyBuffer, LADIP_KEY);
qr2_keybuffer_add(keyBuffer, LADPORT_KEY);
qr2_keybuffer_add(keyBuffer, PINGSTR_KEY);
qr2_keybuffer_add(keyBuffer, NUMPLAYER_KEY);
qr2_keybuffer_add(keyBuffer, MAXPLAYER_KEY);
qr2_keybuffer_add(keyBuffer, NUMOBS_KEY);
break;
case key_player:
qr2_keybuffer_add(keyBuffer, NAME__KEY);
qr2_keybuffer_add(keyBuffer, WINS__KEY);
qr2_keybuffer_add(keyBuffer, LOSSES__KEY);
qr2_keybuffer_add(keyBuffer, PID__KEY);
qr2_keybuffer_add(keyBuffer, FACTION__KEY);
qr2_keybuffer_add(keyBuffer, COLOR__KEY);
break;
case key_team:
// no custom team keys
break;
}
}
static int QRCountCallback
(
PEER peer,
qr2_key_type type,
void * param
)
{
PeerThreadClass *t = (PeerThreadClass *)param;
if (!t)
{
DEBUG_LOG(("QRCountCallback: bailing because of no thread info\n"));
return 0;
}
if (!t->isHosting())
t->stopHostingAlready(peer);
if(type == key_player)
{
DEBUG_LOG(("QR_COUNT | %s = %d\n", KeyTypeToString(type), t->getNumPlayers() + t->getNumObservers()));
return t->getNumPlayers() + t->getNumObservers();
}
else if(type == key_team)
{
DEBUG_LOG(("QR_COUNT | %s = %d\n", KeyTypeToString(type), 0));
return 0;
}
DEBUG_LOG(("QR_COUNT | %s = %d\n", KeyTypeToString(type), 0));
return 0;
}
void PeerThreadClass::stopHostingAlready(PEER peer)
{
isThreadHosting = 0; // debugging
s_lastStateChangedHeartbeat = 0;
s_wantStateChangedHeartbeat = FALSE;
peerStopGame(peer);
if (qr2Sock != INVALID_SOCKET)
{
closesocket(qr2Sock);
qr2Sock = INVALID_SOCKET;
}
}
static void QRAddErrorCallback
(
PEER peer,
qr2_error_t error,
char * errorString,
void * param
)
{
DEBUG_LOG(("QR_ADD_ERROR | %s | %s\n", ErrorTypeToString(error), errorString));
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_FAILEDTOHOST;
TheGameSpyPeerMessageQueue->addResponse(resp);
}
static void QRNatNegotiateCallback
(
PEER peer,
int cookie,
void * param
)
{
DEBUG_LOG(("QR_NAT_NEGOTIATE | 0x%08X\n", cookie));
}
static void KickedCallback
(
PEER peer,
RoomType roomType,
const char * nick,
const char * reason,
void * param
)
{
DEBUG_LOG(("Kicked from %d by %s: \"%s\"\n", roomType, nick, reason));
}
static void NewPlayerListCallback
(
PEER peer,
RoomType roomType,
void * param
)
{
DEBUG_LOG(("NewPlayerListCallback\n"));
}
static void AuthenticateCDKeyCallback
(
PEER peer,
int result,
const char * message,
void * param
)
{
DEBUG_LOG(("CD Key Result: %s (%d) %X\n", message, result, param));
#ifdef SERVER_DEBUGGING
CheckServers(peer);
#endif // SERVER_DEBUGGING
SerialAuthResult *val = (SerialAuthResult *)param;
if (val)
{
if (result >= 1)
{
*val = SERIAL_OK;
}
else
{
*val = SERIAL_AUTHFAILED;
}
}
#ifdef SERVER_DEBUGGING
CheckServers(peer);
#endif // SERVER_DEBUGGING
}
static SerialAuthResult doCDKeyAuthentication( PEER peer )
{
SerialAuthResult retval = SERIAL_NONEXISTENT;
if (!peer)
return retval;
AsciiString s = "";
if (GetStringFromRegistry("\\ergc", "", s) && s.isNotEmpty())
{
#ifdef SERVER_DEBUGGING
DEBUG_LOG(("Before peerAuthenticateCDKey()\n"));
CheckServers(peer);
#endif // SERVER_DEBUGGING
peerAuthenticateCDKey(peer, s.str(), AuthenticateCDKeyCallback, &retval, PEERTrue);
#ifdef SERVER_DEBUGGING
DEBUG_LOG(("After peerAuthenticateCDKey()\n"));
CheckServers(peer);
#endif // SERVER_DEBUGGING
}
if (retval == SERIAL_OK)
{
PSRequest req;
req.requestType = PSRequest::PSREQUEST_READCDKEYSTATS;
req.cdkey = s.str();
TheGameSpyPSMessageQueue->addRequest(req);
}
return retval;
}
#define INBUF_LEN 256
void checkQR2Queries( PEER peer, SOCKET sock )
{
static char indata[INBUF_LEN];
struct sockaddr_in saddr;
int saddrlen = sizeof(struct sockaddr_in);
fd_set set;
struct timeval timeout = {0,0};
int error;
FD_ZERO ( &set );
FD_SET ( sock, &set );
while (1)
{
error = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
if (SOCKET_ERROR == error || 0 == error)
return;
//else we have data
error = recvfrom(sock, indata, INBUF_LEN - 1, 0, (struct sockaddr *)&saddr, &saddrlen);
if (error != SOCKET_ERROR)
{
indata[error] = '\0';
peerParseQuery( peer, indata, error, (sockaddr *)&saddr );
}
}
}
static UnsignedInt localIP = 0;
void PeerThreadClass::Thread_Function()
{
try {
_set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace.
PEER peer;
// Setup the callbacks.
///////////////////////
PEERCallbacks callbacks;
memset(&callbacks, 0, sizeof(PEERCallbacks));
callbacks.disconnected = disconnectedCallback;
//callbacks.readyChanged = readyChangedCallback;
callbacks.roomMessage = roomMessageCallback;
callbacks.playerMessage = playerMessageCallback;
callbacks.gameStarted = gameStartedCallback;
callbacks.playerJoined = playerJoinedCallback;
callbacks.playerLeft = playerLeftCallback;
callbacks.playerChangedNick = playerChangedNickCallback;
callbacks.playerFlagsChanged = playerFlagsChangedCallback;
callbacks.playerInfo = playerInfoCallback;
callbacks.roomUTM = roomUTMCallback;
callbacks.playerUTM = playerUTMCallback;
callbacks.globalKeyChanged = globalKeyChangedCallback;
callbacks.roomKeyChanged = roomKeyChangedCallback;
callbacks.qrServerKey = QRServerKeyCallback;
callbacks.qrPlayerKey = QRPlayerKeyCallback;
callbacks.qrTeamKey = QRTeamKeyCallback;
callbacks.qrKeyList = QRKeyListCallback;
callbacks.qrCount = QRCountCallback;
callbacks.qrAddError = QRAddErrorCallback;
callbacks.qrNatNegotiateCallback = QRNatNegotiateCallback;
callbacks.kicked = KickedCallback;
callbacks.newPlayerList = NewPlayerListCallback;
callbacks.param = this;
m_qmGroupRoom = 0;
peer = peerInitialize( &callbacks );
DEBUG_ASSERTCRASH( peer != NULL, ("NULL peer!") );
m_isConnected = m_isConnecting = false;
qr2_register_key(EXECRC_KEY, EXECRC_STR);
qr2_register_key(INICRC_KEY, INICRC_STR);
qr2_register_key(PW_KEY, PW_STR);
qr2_register_key(OBS_KEY, OBS_STR);
qr2_register_key(USE_STATS_KEY, USE_STATS_STR);
qr2_register_key(LADIP_KEY, LADIP_STR);
qr2_register_key(LADPORT_KEY, LADPORT_STR);
qr2_register_key(PINGSTR_KEY, PINGSTR_STR);
qr2_register_key(NUMOBS_KEY, NUMOBS_STR);
qr2_register_key(NUMPLAYER_KEY, NUMPLAYER_STR);
qr2_register_key(MAXPLAYER_KEY, MAXPLAYER_STR);
qr2_register_key(NAME__KEY, NAME__STR "_");
qr2_register_key(WINS__KEY, WINS__STR "_");
qr2_register_key(LOSSES__KEY, LOSSES__STR "_");
qr2_register_key(FACTION__KEY, FACTION__STR "_");
qr2_register_key(COLOR__KEY, COLOR__STR "_");
const Int NumKeys = 15;
unsigned char allKeysArray[NumKeys] = {
/*
PID__KEY,
NAME__KEY,
WINS__KEY,
LOSSES__KEY,
FACTION__KEY,
COLOR__KEY,
*/
MAPNAME_KEY,
GAMEVER_KEY,
GAMENAME_KEY,
EXECRC_KEY,
INICRC_KEY,
PW_KEY,
OBS_KEY,
USE_STATS_KEY,
LADIP_KEY,
LADPORT_KEY,
PINGSTR_KEY,
NUMOBS_KEY,
NUMPLAYER_KEY,
MAXPLAYER_KEY,
HOSTNAME_KEY
};
/*
const char *allKeys = "\\pid_\\mapname\\gamever\\gamename" \
"\\" EXECRC_STR "\\" INICRC_STR \
"\\" PW_STR "\\" OBS_STR "\\" USE_STATS_STR "\\" LADIP_STR "\\" LADPORT_STR \
"\\" PINGSTR_STR "\\" NUMOBS_STR \
"\\" NUMPLAYER_STR "\\" MAXPLAYER_STR \
"\\" NAME__STR "_" "\\" WINS__STR "_" "\\" LOSSES__STR "_" "\\" FACTION__STR "_" "\\" COLOR__STR "_";
*/
const char * key = "username";
peerSetRoomWatchKeys(peer, StagingRoom, 1, &key, PEERTrue);
peerSetRoomWatchKeys(peer, GroupRoom, 1, &key, PEERTrue);
m_localRoomID = 0;
m_localStagingServerName = L"";
m_qmStatus = QM_IDLE;
// Setup which rooms to do pings and cross-pings in.
////////////////////////////////////////////////////
PEERBool pingRooms[NumRooms];
PEERBool crossPingRooms[NumRooms];
pingRooms[TitleRoom] = PEERFalse;
pingRooms[GroupRoom] = PEERFalse;
pingRooms[StagingRoom] = PEERFalse;
crossPingRooms[TitleRoom] = PEERFalse;
crossPingRooms[GroupRoom] = PEERFalse;
crossPingRooms[StagingRoom] = PEERFalse;
/*********
First step, set our game authentication info
We could do:
strcpy(gcd_gamename,"ccgenzh");
strcpy(gcd_secret_key,"D6s9k3");
or
strcpy(gcd_gamename,"ccgeneralsb");
strcpy(gcd_secret_key,"whatever the key is");
...but this is more secure:
**********/
char gameName[12] = {0};
char secretKey[7] = {0};
/**
gameName[0]='c';gameName[1]='c';gameName[2]='g';gameName[3]='e';
gameName[4]='n';gameName[5]='e';gameName[6]='r';gameName[7]='a';
gameName[8]='l';gameName[9]='s';gameName[10]='b';gameName[11]='\0';
secretKey[0]='g';secretKey[1]='3';secretKey[2]='T';secretKey[3]='9';
secretKey[4]='s';secretKey[5]='2';secretKey[6]='\0';
/**/
gameName[0]='c';gameName[1]='c';gameName[2]='g';gameName[3]='e';
gameName[4]='n';gameName[5]='z';gameName[6]='h';gameName[7]='\0';
secretKey[0]='D';secretKey[1]='6';secretKey[2]='s';secretKey[3]='9';
secretKey[4]='k';secretKey[5]='3';secretKey[6]='\0';
/**/
// Set the title.
/////////////////
if(!peerSetTitle( peer , gameName, secretKey, gameName, secretKey, GetRegistryVersion(), 30, PEERTrue, pingRooms, crossPingRooms))
{
DEBUG_CRASH(("Error setting title"));
peerShutdown( peer );
peer = NULL;
return;
}
OptionPreferences pref;
UnsignedInt preferredIP = INADDR_ANY;
UnsignedInt selectedIP = pref.getOnlineIPAddress();
DEBUG_LOG(("Looking for IP %X\n", selectedIP));
IPEnumeration IPs;
EnumeratedIP *IPlist = IPs.getAddresses();
while (IPlist)
{
DEBUG_LOG(("Looking at IP %s\n", IPlist->getIPstring().str()));
if (selectedIP == IPlist->getIP())
{
preferredIP = IPlist->getIP();
DEBUG_LOG(("Connecting to GameSpy chat server via IP address %8.8X\n", preferredIP));
break;
}
IPlist = IPlist->getNext();
}
chatSetLocalIP(preferredIP);
UnsignedInt preferredQRPort = 0;
AsciiString selectedQRPort = pref["GameSpyQRPort"];
if (selectedQRPort.isNotEmpty())
{
preferredQRPort = atoi(selectedQRPort.str());
}
PeerRequest incomingRequest;
while ( running )
{
// deal with requests
if (TheGameSpyPeerMessageQueue->getRequest(incomingRequest))
{
DEBUG_LOG(("TheGameSpyPeerMessageQueue->getRequest() got request of type %d\n", incomingRequest.peerRequestType));
switch (incomingRequest.peerRequestType)
{
case PeerRequest::PEERREQUEST_LOGIN:
{
m_isConnecting = true;
m_originalName = incomingRequest.nick;
m_loginName = incomingRequest.nick;
m_profileID = incomingRequest.login.profileID;
m_password = incomingRequest.password;
m_email = incomingRequest.email;
peerConnect( peer, incomingRequest.nick.c_str(), incomingRequest.login.profileID, nickErrorCallbackWrapper, connectCallbackWrapper, this, PEERTrue );
#ifdef SERVER_DEBUGGING
DEBUG_LOG(("After peerConnect()\n"));
CheckServers(peer);
#endif // SERVER_DEBUGGING
if (m_isConnected)
{
SerialAuthResult ret = doCDKeyAuthentication( peer );
if (ret != SERIAL_OK)
{
m_isConnecting = m_isConnected = false;
MESSAGE_QUEUE->setSerialAuthResult( ret );
peerDisconnect( peer );
}
}
m_isConnecting = false;
// check our connection
//if (m_isConnected)
//{
// GetLocalChatConnectionAddress("peerchat.gamespy.com", 6667, localIP);
//}
}
break;
case PeerRequest::PEERREQUEST_LOGOUT:
m_isConnecting = m_isConnected = false;
peerDisconnect( peer );
break;
case PeerRequest::PEERREQUEST_JOINGROUPROOM:
m_groupRoomID = incomingRequest.groupRoom.id;
isThreadHosting = 0; // debugging
s_lastStateChangedHeartbeat = 0;
s_wantStateChangedHeartbeat = FALSE;
peerStopGame( peer );
peerLeaveRoom( peer, GroupRoom, NULL );
peerLeaveRoom( peer, StagingRoom, NULL );
if (qr2Sock != INVALID_SOCKET)
{
closesocket(qr2Sock);
qr2Sock = INVALID_SOCKET;
}
m_isHosting = false;
m_localRoomID = m_groupRoomID;
DEBUG_LOG(("Requesting to join room %d in thread %X\n", m_localRoomID, this));
peerJoinGroupRoom( peer, incomingRequest.groupRoom.id, joinRoomCallback, (void *)this, PEERTrue );
break;
case PeerRequest::PEERREQUEST_LEAVEGROUPROOM:
m_groupRoomID = 0;
updateBuddyStatus( BUDDY_ONLINE );
peerLeaveRoom( peer, GroupRoom, NULL );
peerLeaveRoom( peer, StagingRoom, NULL ); m_isHosting = false;
break;
case PeerRequest::PEERREQUEST_JOINSTAGINGROOM:
{
m_groupRoomID = 0;
updateBuddyStatus( BUDDY_ONLINE );
peerLeaveRoom( peer, GroupRoom, NULL );
peerLeaveRoom( peer, StagingRoom, NULL ); m_isHosting = false;
SBServer server = findServerByID(incomingRequest.stagingRoom.id);
m_localStagingServerName = incomingRequest.text;
DEBUG_LOG(("Setting m_localStagingServerName to [%ls]\n", m_localStagingServerName.c_str()));
m_localRoomID = incomingRequest.stagingRoom.id;
DEBUG_LOG(("Requesting to join room %d\n", m_localRoomID));
if (server)
{
peerJoinStagingRoom( peer, server, incomingRequest.password.c_str(), joinRoomCallback, (void *)this, PEERTrue );
}
else
{
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_JOINSTAGINGROOM;
resp.joinStagingRoom.id = incomingRequest.stagingRoom.id;
resp.joinStagingRoom.ok = FALSE;
resp.joinStagingRoom.result = PEERJoinFailed;
TheGameSpyPeerMessageQueue->addResponse(resp);
}
}
break;
case PeerRequest::PEERREQUEST_LEAVESTAGINGROOM:
m_groupRoomID = 0;
updateBuddyStatus( BUDDY_ONLINE );
peerLeaveRoom( peer, GroupRoom, NULL );
peerLeaveRoom( peer, StagingRoom, NULL );
isThreadHosting = 0; // debugging
s_lastStateChangedHeartbeat = 0;
s_wantStateChangedHeartbeat = FALSE;
if (m_isHosting)
{
peerStopGame( peer );
if (qr2Sock != INVALID_SOCKET)
{
closesocket(qr2Sock);
qr2Sock = INVALID_SOCKET;
}
m_isHosting = false;
}
break;
case PeerRequest::PEERREQUEST_MESSAGEPLAYER:
{
std::string s = WideCharStringToMultiByte(incomingRequest.text.c_str());
peerMessagePlayer( peer, incomingRequest.nick.c_str(), s.c_str(), (incomingRequest.message.isAction)?ActionMessage:NormalMessage );
}
break;
case PeerRequest::PEERREQUEST_MESSAGEROOM:
{
std::string s = WideCharStringToMultiByte(incomingRequest.text.c_str());
peerMessageRoom( peer, (m_groupRoomID)?GroupRoom:StagingRoom, s.c_str(), (incomingRequest.message.isAction)?ActionMessage:NormalMessage );
}
break;
case PeerRequest::PEERREQUEST_PUSHSTATS:
{
DEBUG_LOG(("PEERREQUEST_PUSHSTATS: stats are %d,%d,%d,%d,%d,%d\n",
incomingRequest.statsToPush.locale, incomingRequest.statsToPush.wins, incomingRequest.statsToPush.losses, incomingRequest.statsToPush.rankPoints, incomingRequest.statsToPush.side, incomingRequest.statsToPush.preorder));
// Testing alternate way to push stats
#ifdef USE_BROADCAST_KEYS
_snprintf(s_valueBuffers[0], 20, "%d", incomingRequest.statsToPush.locale);
_snprintf(s_valueBuffers[1], 20, "%d", incomingRequest.statsToPush.wins);
_snprintf(s_valueBuffers[2], 20, "%d", incomingRequest.statsToPush.losses);
_snprintf(s_valueBuffers[3], 20, "%d", incomingRequest.statsToPush.rankPoints);
_snprintf(s_valueBuffers[4], 20, "%d", incomingRequest.statsToPush.side);
_snprintf(s_valueBuffers[5], 20, "%d", incomingRequest.statsToPush.preorder);
pushStatsToRoom(peer);
#else
const char *keys[6] = { "locale", "wins", "losses", "points", "side", "pre" };
char valueStrings[6][20];
char *values[6] = { valueStrings[0], valueStrings[1], valueStrings[2],
valueStrings[3], valueStrings[4], valueStrings[5]};
_snprintf(values[0], 20, "%d", incomingRequest.statsToPush.locale);
_snprintf(values[1], 20, "%d", incomingRequest.statsToPush.wins);
_snprintf(values[2], 20, "%d", incomingRequest.statsToPush.losses);
_snprintf(values[3], 20, "%d", incomingRequest.statsToPush.rankPoints);
_snprintf(values[4], 20, "%d", incomingRequest.statsToPush.side);
_snprintf(values[5], 20, "%d", incomingRequest.statsToPush.preorder);
peerSetGlobalKeys(peer, 6, (const char **)keys, (const char **)values);
peerSetGlobalWatchKeys(peer, GroupRoom, 0, NULL, PEERFalse);
peerSetGlobalWatchKeys(peer, StagingRoom, 0, NULL, PEERFalse);
peerSetGlobalWatchKeys(peer, GroupRoom, 6, keys, PEERTrue);
peerSetGlobalWatchKeys(peer, StagingRoom, 6, keys, PEERTrue);
#endif
}
break;
case PeerRequest::PEERREQUEST_SETGAMEOPTIONS:
{
m_mapName = incomingRequest.gameOptsMapName;
m_numPlayers = incomingRequest.gameOptions.numPlayers;
m_numObservers = incomingRequest.gameOptions.numObservers;
m_maxPlayers = incomingRequest.gameOptions.maxPlayers;
DEBUG_LOG(("peerStateChanged(): Marking game options state as changed - %d players, %d observers\n", m_numPlayers, m_numObservers));
for (Int i=0; iaddResponse(resp);
if (res != PEERJoinSuccess && res != PEERAlreadyInRoom)
{
m_localRoomID = oldGroupID;
DEBUG_LOG(("Requesting to join room %d\n", m_localRoomID));
if (incomingRequest.stagingRoomCreation.restrictGameList)
{
peerLeaveRoom( peer, StagingRoom, NULL );
}
else
{
peerJoinGroupRoom( peer, oldGroupID, joinRoomCallback, (void *)this, PEERTrue );
}
m_isHosting = FALSE;
m_localStagingServerName = L"";
m_playerNames[0] = "";
}
else
{
if (incomingRequest.stagingRoomCreation.restrictGameList)
{
peerLeaveRoom( peer, GroupRoom, NULL );
}
isThreadHosting = 1; // debugging
s_lastStateChangedHeartbeat = timeGetTime(); // wait the full interval before updating state
s_wantStateChangedHeartbeat = FALSE;
m_isHosting = TRUE;
m_allowObservers = incomingRequest.stagingRoomCreation.allowObservers;
m_useStats = incomingRequest.stagingRoomCreation.useStats;
m_mapName = "";
for (Int i=0; i 0)
m_hasPassword = true;
else
m_hasPassword = false;
m_playerNames[0] = m_loginName;
m_exeCRC = incomingRequest.stagingRoomCreation.exeCRC;
m_iniCRC = incomingRequest.stagingRoomCreation.iniCRC;
m_gameVersion = incomingRequest.stagingRoomCreation.gameVersion;
m_localStagingServerName = incomingRequest.text;
m_ladderIP = incomingRequest.ladderIP;
m_pingStr = incomingRequest.hostPingStr;
m_ladderPort = incomingRequest.stagingRoomCreation.ladPort;
#ifdef USE_BROADCAST_KEYS
pushStatsToRoom(peer);
#endif // USE_BROADCAST_KEYS
DEBUG_LOG(("Setting m_localStagingServerName to [%ls]\n", m_localStagingServerName.c_str()));
updateBuddyStatus( BUDDY_STAGING, 0, WideCharStringToMultiByte(m_localStagingServerName.c_str()) );
}
}
break;
case PeerRequest::PEERREQUEST_STARTGAMELIST:
{
m_sawCompleteGameList = FALSE;
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_STAGINGROOM;
resp.stagingRoom.action = PEER_CLEAR;
resp.stagingRoom.isStaging = TRUE;
resp.stagingRoom.percentComplete = 0;
clearServers();
TheGameSpyPeerMessageQueue->addResponse(resp);
peerStartListingGames( peer, allKeysArray, NumKeys, (incomingRequest.gameList.restrictGameList?"~":NULL), listingGamesCallback, this );
}
break;
case PeerRequest::PEERREQUEST_STOPGAMELIST:
{
peerStopListingGames( peer );
}
break;
case PeerRequest::PEERREQUEST_STARTGAME:
{
peerStartGame( peer, NULL, PEER_STOP_REPORTING);
}
break;
case PeerRequest::PEERREQUEST_UTMPLAYER:
{
if (incomingRequest.nick.length() > 0)
{
peerUTMPlayer( peer, incomingRequest.nick.c_str(), incomingRequest.id.c_str(), incomingRequest.options.c_str(), PEERFalse );
}
}
break;
case PeerRequest::PEERREQUEST_UTMROOM:
{
peerUTMRoom( peer, (incomingRequest.UTM.isStagingRoom)?StagingRoom:GroupRoom, incomingRequest.id.c_str(), incomingRequest.options.c_str(), PEERFalse );
}
break;
case PeerRequest::PEERREQUEST_STARTQUICKMATCH:
{
m_qmInfo = incomingRequest;
doQuickMatch( peer );
}
break;
}
}
if (isThreadHosting && s_wantStateChangedHeartbeat)
{
UnsignedInt now = timeGetTime();
if (now > s_lastStateChangedHeartbeat + s_heartbeatInterval)
{
s_lastStateChangedHeartbeat = now;
s_wantStateChangedHeartbeat = FALSE;
peerStateChanged( peer );
#ifdef DEBUG_LOGGING
static UnsignedInt prev = 0;
UnsignedInt now = timeGetTime();
UnsignedInt diff = now - prev;
prev = now;
#endif
STATECHANGED_LOG(("peerStateChanged() at time %d (difference of %d ms)\n", now, diff));
}
}
// update the network
PEERBool isConnected = PEERTrue;
isConnected = peerIsConnected( peer );
if ( isConnected == PEERTrue )
{
if (qr2Sock != INVALID_SOCKET)
{
// check hosting activity
checkQR2Queries( peer, qr2Sock );
}
peerThink( peer );
}
// end our timeslice
Switch_Thread();
}
DEBUG_LOG(("voluntarily ending peer thread %d\n", running));
peerShutdown( peer );
} catch ( ... ) {
DEBUG_CRASH(("Exception in peer thread!"));
try {
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_DISCONNECT;
resp.discon.reason = DISCONNECT_LOSTCON;
TheGameSpyPeerMessageQueue->addResponse(resp);
}
catch (...)
{
}
}
}
static void qmProfileIDCallback( PEER peer, PEERBool success, const char *nick, int profileID, void *param )
{
Int *i = (Int *)param;
if (!i || !success || !nick)
return;
*i = profileID;
}
static Int matchbotProfileID = 0;
void quickmatchEnumPlayersCallback( PEER peer, PEERBool success, RoomType roomType, int index, const char * nick, int flags, void * param )
{
PeerThreadClass *t = (PeerThreadClass *)param;
if (!t || !success || nick == NULL || nick[0] == '\0')
{
t->sawEndOfEnumPlayers();
return;
}
Int id = 0;
peerGetPlayerProfileID(peer, nick, qmProfileIDCallback, &id, PEERTrue);
DEBUG_LOG(("Saw player %s with id %d (looking for %d)\n", nick, id, matchbotProfileID));
if (id == matchbotProfileID)
{
t->sawMatchbot(nick);
}
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_PLAYERJOIN;
resp.nick = nick;
resp.player.roomType = roomType;
resp.player.IP = 0;
TheGameSpyPeerMessageQueue->addResponse(resp);
}
void PeerThreadClass::handleQMMatch(PEER peer, Int mapIndex, Int seed,
char *playerName[MAX_SLOTS],
char *playerIP[MAX_SLOTS],
char *playerSide[MAX_SLOTS],
char *playerColor[MAX_SLOTS],
char *playerNAT[MAX_SLOTS])
{
if (m_qmStatus == QM_WORKING)
{
m_qmStatus = QM_MATCHED;
peerLeaveRoom(peer, GroupRoom, "");
for (Int i=0; iaddResponse(resp);
}
}
void PeerThreadClass::doQuickMatch( PEER peer )
{
m_qmStatus = QM_JOININGQMCHANNEL;
Bool done = false;
matchbotProfileID = m_qmInfo.QM.botID;
setQMGroupRoom( m_qmInfo.QM.roomID );
m_sawMatchbot = false;
updateBuddyStatus( BUDDY_MATCHING );
while (!done && running)
{
if (!peerIsConnected( peer ))
{
done = true;
}
else
{
// update the network
peerThink( peer );
// end our timeslice
Switch_Thread();
PeerRequest incomingRequest;
if (TheGameSpyPeerMessageQueue->getRequest(incomingRequest))
{
switch (incomingRequest.peerRequestType)
{
case PeerRequest::PEERREQUEST_WIDENQUICKMATCHSEARCH:
{
if (m_qmStatus != QM_IDLE && m_qmStatus != QM_STOPPED && m_sawMatchbot)
{
peerMessagePlayer( peer, m_matchbotName.c_str(), "\\WIDEN", NormalMessage );
}
}
break;
case PeerRequest::PEERREQUEST_STOPQUICKMATCH:
{
m_qmStatus = QM_STOPPED;
peerLeaveRoom(peer, GroupRoom, "");
done = true;
}
break;
case PeerRequest::PEERREQUEST_LOGOUT:
{
m_qmStatus = QM_STOPPED;
peerLeaveRoom(peer, GroupRoom, "");
done = true;
}
break;
case PeerRequest::PEERREQUEST_LEAVEGROUPROOM:
{
m_qmStatus = QM_STOPPED;
peerLeaveRoom(peer, GroupRoom, "");
done = true;
}
break;
case PeerRequest::PEERREQUEST_UTMPLAYER:
{
peerUTMPlayer( peer, incomingRequest.nick.c_str(), incomingRequest.id.c_str(), incomingRequest.options.c_str(), PEERFalse );
}
break;
default:
{
DEBUG_CRASH(("Unanticipated request %d to peer thread!", incomingRequest.peerRequestType));
}
break;
}
}
if (!done)
{
// do the next bit of QM
switch (m_qmStatus)
{
case QM_JOININGQMCHANNEL:
{
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS;
resp.qmStatus.status = QM_JOININGQMCHANNEL;
TheGameSpyPeerMessageQueue->addResponse(resp);
m_groupRoomID = m_qmGroupRoom;
peerLeaveRoom( peer, GroupRoom, NULL );
peerLeaveRoom( peer, StagingRoom, NULL ); m_isHosting = false;
m_localRoomID = m_groupRoomID;
m_roomJoined = false;
DEBUG_LOG(("Requesting to join room %d in thread %X\n", m_localRoomID, this));
peerJoinGroupRoom( peer, m_localRoomID, joinRoomCallback, (void *)this, PEERTrue );
if (m_roomJoined)
{
resp.peerResponseType = PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS;
resp.qmStatus.status = QM_LOOKINGFORBOT;
TheGameSpyPeerMessageQueue->addResponse(resp);
m_qmStatus = QM_LOOKINGFORBOT;
m_sawMatchbot = false;
m_sawEndOfEnumPlayers = false;
peerEnumPlayers( peer, GroupRoom, quickmatchEnumPlayersCallback, this );
}
else
{
resp.peerResponseType = PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS;
resp.qmStatus.status = QM_COULDNOTFINDBOT;
TheGameSpyPeerMessageQueue->addResponse(resp);
done = true;
m_qmStatus = QM_STOPPED;
}
}
break;
case QM_LOOKINGFORBOT:
{
if (m_sawEndOfEnumPlayers)
{
if (m_sawMatchbot)
{
char buf[64];
buf[63] = '\0';
std::string msg = "\\CINFO";
_snprintf(buf, 63, "\\Widen\\%d", m_qmInfo.QM.widenTime);
msg.append(buf);
_snprintf(buf, 63, "\\LadID\\%d", m_qmInfo.QM.ladderID);
msg.append(buf);
_snprintf(buf, 63, "\\LadPass\\%d", m_qmInfo.QM.ladderPassCRC);
msg.append(buf);
_snprintf(buf, 63, "\\PointsMin\\%d", m_qmInfo.QM.minPointPercentage);
msg.append(buf);
_snprintf(buf, 63, "\\PointsMax\\%d", m_qmInfo.QM.maxPointPercentage);
msg.append(buf);
_snprintf(buf, 63, "\\Points\\%d", m_qmInfo.QM.points);
msg.append(buf);
_snprintf(buf, 63, "\\Discons\\%d", m_qmInfo.QM.discons);
msg.append(buf);
_snprintf(buf, 63, "\\DisconMax\\%d", m_qmInfo.QM.maxDiscons);
msg.append(buf);
_snprintf(buf, 63, "\\NumPlayers\\%d", m_qmInfo.QM.numPlayers);
msg.append(buf);
_snprintf(buf, 63, "\\Pings\\%s", m_qmInfo.QM.pings);
msg.append(buf);
_snprintf(buf, 63, "\\IP\\%d", ntohl(peerGetLocalIP(peer)));// not ntohl(localIP), as we need EXTERNAL address for proper NAT negotiation!
msg.append(buf);
_snprintf(buf, 63, "\\Side\\%d", m_qmInfo.QM.side);
msg.append(buf);
_snprintf(buf, 63, "\\Color\\%d", m_qmInfo.QM.color);
msg.append(buf);
_snprintf(buf, 63, "\\NAT\\%d", m_qmInfo.QM.NAT);
msg.append(buf);
_snprintf(buf, 63, "\\EXE\\%d", m_qmInfo.QM.exeCRC);
msg.append(buf);
_snprintf(buf, 63, "\\INI\\%d", m_qmInfo.QM.iniCRC);
msg.append(buf);
buf[0] = 0;
msg.append("\\Maps\\");
for (Int i=0; iaddResponse(resp);
}
else
{
// no QM bot. Bail.
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS;
resp.qmStatus.status = QM_COULDNOTFINDBOT;
TheGameSpyPeerMessageQueue->addResponse(resp);
m_qmStatus = QM_STOPPED;
peerLeaveRoom(peer, GroupRoom, "");
done = true;
}
}
}
break;
case QM_WORKING:
{
}
break;
case QM_MATCHED:
{
// leave QM channel, and clean up. Our work here is done.
peerLeaveRoom( peer, GroupRoom, NULL );
peerLeaveRoom( peer, StagingRoom, NULL ); m_isHosting = false;
m_qmStatus = QM_STOPPED;
peerLeaveRoom(peer, GroupRoom, "");
done = true;
}
break;
case QM_INCHANNEL:
{
}
break;
case QM_NEGOTIATINGFIREWALLS:
{
}
break;
case QM_STARTINGGAME:
{
}
break;
case QM_COULDNOTFINDCHANNEL:
{
}
break;
case QM_COULDNOTNEGOTIATEFIREWALLS:
{
}
break;
}
}
}
}
updateBuddyStatus( BUDDY_ONLINE );
}
static void getPlayerProfileIDCallback(PEER peer, PEERBool success, const char * nick, int profileID, void * param)
{
if (success && param != NULL)
{
*((Int *)param) = profileID;
}
}
static void stagingRoomPlayerEnum( PEER peer, PEERBool success, RoomType roomType, int index, const char * nick, int flags, void * param )
{
DEBUG_LOG(("Enum: success=%d, index=%d, nick=%s, flags=%d\n", success, index, nick, flags));
if (!nick || !success)
return;
Int id = 0;
peerGetPlayerProfileID(peer, nick, getPlayerProfileIDCallback, &id, PEERTrue);
DEBUG_ASSERTCRASH(id != 0, ("Failed to fetch player ID!"));
PeerResponse *resp = (PeerResponse *)param;
if (flags & PEER_FLAG_OP)
{
resp->joinStagingRoom.isHostPresent = TRUE;
}
if (index < MAX_SLOTS)
{
resp->stagingRoomPlayerNames[index] = nick;
}
if (id)
{
PSRequest req;
req.requestType = PSRequest::PSREQUEST_READPLAYERSTATS;
req.player.id = id;
TheGameSpyPSMessageQueue->addRequest(req);
}
}
static void joinRoomCallback(PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, void *param)
{
DEBUG_LOG(("JoinRoomCallback: success==%d, result==%d\n", success, result));
PeerThreadClass *t = (PeerThreadClass *)param;
if (!t)
return;
DEBUG_LOG(("Room id was %d from thread %X\n", t->getLocalRoomID(), t));
DEBUG_LOG(("Current staging server name is [%ls]\n", t->getLocalStagingServerName().c_str()));
DEBUG_LOG(("Room type is %d (GroupRoom=%d, StagingRoom=%d, TitleRoom=%d)\n", roomType, GroupRoom, StagingRoom, TitleRoom));
#ifdef USE_BROADCAST_KEYS
if (success)
{
t->pushStatsToRoom(peer);
t->getStatsFromRoom(peer, roomType);
}
#endif // USE_BROADCAST_KEYS
switch (roomType)
{
case GroupRoom:
{
#ifdef USE_BROADCAST_KEYS
t->clearPlayerStats(GroupRoom);
#endif // USE_BROADCAST_KEYS
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_JOINGROUPROOM;
resp.joinGroupRoom.id = t->getLocalRoomID();
resp.joinGroupRoom.ok = success;
TheGameSpyPeerMessageQueue->addResponse(resp);
t->roomJoined(success == PEERTrue);
DEBUG_LOG(("Entered group room %d, qm is %d\n", t->getLocalRoomID(), t->getQMGroupRoom()));
if ((!t->getQMGroupRoom()) || (t->getQMGroupRoom() != t->getLocalRoomID()))
{
DEBUG_LOG(("Updating buddy status\n"));
updateBuddyStatus( BUDDY_LOBBY, t->getLocalRoomID() );
}
}
break;
case StagingRoom:
{
#ifdef USE_BROADCAST_KEYS
t->clearPlayerStats(StagingRoom);
#endif // USE_BROADCAST_KEYS
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_JOINSTAGINGROOM;
resp.joinStagingRoom.id = t->getLocalRoomID();
resp.joinStagingRoom.ok = success;
resp.joinStagingRoom.result = result;
if (success)
{
DEBUG_LOG(("joinRoomCallback() - game name is now '%ls'\n", t->getLocalStagingServerName().c_str()));
updateBuddyStatus( BUDDY_STAGING, 0, WideCharStringToMultiByte(t->getLocalStagingServerName().c_str()) );
}
resp.joinStagingRoom.isHostPresent = FALSE;
DEBUG_LOG(("Enum of staging room players\n"));
peerEnumPlayers(peer, StagingRoom, stagingRoomPlayerEnum, &resp);
DEBUG_LOG(("Host %s present\n", (resp.joinStagingRoom.isHostPresent)?"is":"is not"));
TheGameSpyPeerMessageQueue->addResponse(resp);
}
break;
}
}
// Gets called once for each group room when listing group rooms.
// After this has been called for each group room, it will be
// called one more time with groupID==0 and name==NULL.
/////////////////////////////////////////////////////////////////
static void listGroupRoomsCallback(PEER peer, PEERBool success,
int groupID, SBServer server,
const char * name, int numWaiting,
int maxWaiting, int numGames,
int numPlaying, void * param)
{
DEBUG_LOG(("listGroupRoomsCallback, success=%d, server=%X, groupID=%d\n", success, server, groupID));
#ifdef SERVER_DEBUGGING
CheckServers(peer);
#endif // SERVER_DEBUGGING
PeerThreadClass *t = (PeerThreadClass *)param;
if (!t)
{
DEBUG_LOG(("No thread! Bailing!\n"));
return;
}
if (success)
{
DEBUG_LOG(("Saw group room of %d (%s) at address %X %X\n", groupID, name, server, (server)?server->keyvals:0));
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_GROUPROOM;
resp.groupRoom.id = groupID;
resp.groupRoom.numWaiting = numWaiting;
resp.groupRoom.maxWaiting = maxWaiting;
resp.groupRoom.numGames = numGames;
resp.groupRoom.numPlaying = numPlaying;
if (name)
{
resp.groupRoomName = name;
//t->setQMGroupRoom(groupID);
}
else
{
resp.groupRoomName.empty();
}
TheGameSpyPeerMessageQueue->addResponse(resp);
#ifdef SERVER_DEBUGGING
CheckServers(peer);
DEBUG_LOG(("\n"));
#endif // SERVER_DEBUGGING
}
else
{
DEBUG_LOG(("Failure!\n"));
}
}
void PeerThreadClass::connectCallback( PEER peer, PEERBool success )
{
PeerResponse resp;
if(!success)
{
//updateBuddyStatus( BUDDY_OFFLINE );
resp.peerResponseType = PeerResponse::PEERRESPONSE_DISCONNECT;
resp.discon.reason = DISCONNECT_COULDNOTCONNECT;
TheGameSpyPeerMessageQueue->addResponse(resp);
return;
}
updateBuddyStatus( BUDDY_ONLINE );
m_isConnected = true;
DEBUG_LOG(("Connected as profile %d (%s)\n", m_profileID, m_loginName.c_str()));
resp.peerResponseType = PeerResponse::PEERRESPONSE_LOGIN;
resp.player.profileID = m_profileID;
resp.nick = m_loginName;
GetLocalChatConnectionAddress("peerchat.gamespy.com", 6667, localIP);
chatSetLocalIP(localIP);
resp.player.internalIP = ntohl(localIP);
resp.player.externalIP = ntohl(peerGetLocalIP(peer));
TheGameSpyPeerMessageQueue->addResponse(resp);
PSRequest psReq;
psReq.requestType = PSRequest::PSREQUEST_READPLAYERSTATS;
psReq.player.id = m_profileID;
psReq.nick = m_originalName;
psReq.email = m_email;
psReq.password = m_password;
TheGameSpyPSMessageQueue->addRequest(psReq);
#ifdef SERVER_DEBUGGING
DEBUG_LOG(("Before peerListGroupRooms()\n"));
CheckServers(peer);
#endif // SERVER_DEBUGGING
peerListGroupRooms( peer, NULL, listGroupRoomsCallback, this, PEERTrue );
#ifdef SERVER_DEBUGGING
DEBUG_LOG(("After peerListGroupRooms()\n"));
CheckServers(peer);
#endif // SERVER_DEBUGGING
}
void PeerThreadClass::nickErrorCallback( PEER peer, Int type, const char *nick )
{
if(type == PEER_IN_USE)
{
Int len = strlen(nick);
std::string nickStr = nick;
Int newVal = 0;
if (nick[len-1] == '}' && nick[len-3] == '{' && isdigit(nick[len-2]))
{
newVal = nick[len-2] - '0' + 1;
nickStr.erase(len-3, 3);
}
DEBUG_LOG(("Nickname taken: was %s, new val = %d, new nick = %s\n", nick, newVal, nickStr.c_str()));
if (newVal < 10)
{
nickStr.append("{");
char tmp[2];
tmp[0] = '0'+newVal;
tmp[1] = '\0';
nickStr.append(tmp);
nickStr.append("}");
// Retry the connect with a similar nick.
m_loginName = nickStr;
peerRetryWithNick(peer, nickStr.c_str());
}
else
{
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_DISCONNECT;
resp.discon.reason = DISCONNECT_NICKTAKEN;
TheGameSpyPeerMessageQueue->addResponse(resp);
// Cancel the connect.
peerRetryWithNick(peer, NULL);
}
}
else
{
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_DISCONNECT;
resp.discon.reason = DISCONNECT_BADNICK;
TheGameSpyPeerMessageQueue->addResponse(resp);
// Cancel the connect.
peerRetryWithNick(peer, NULL);
}
}
void disconnectedCallback(PEER peer, const char * reason, void * param)
{
DEBUG_LOG(("disconnectedCallback(): reason was '%s'\n", reason));
PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (t)
t->markAsDisconnected();
//updateBuddyStatus( BUDDY_OFFLINE );
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_DISCONNECT;
resp.discon.reason = DISCONNECT_LOSTCON;
SerialAuthResult res = TheGameSpyPeerMessageQueue->getSerialAuthResult();
switch (res)
{
case SERIAL_NONEXISTENT:
resp.discon.reason = DISCONNECT_SERIAL_NOT_PRESENT;
break;
case SERIAL_AUTHFAILED:
resp.discon.reason = DISCONNECT_SERIAL_INVALID;
break;
case SERIAL_BANNED:
resp.discon.reason = DISCONNECT_SERIAL_BANNED;
break;
}
TheGameSpyPeerMessageQueue->addResponse(resp);
}
void roomMessageCallback(PEER peer, RoomType roomType, const char * nick, const char * message, MessageType messageType, void * param)
{
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_MESSAGE;
resp.nick = nick;
resp.text = MultiByteToWideCharSingleLine(message);
resp.message.isPrivate = FALSE;
resp.message.isAction = (messageType == ActionMessage);
TheGameSpyPeerMessageQueue->addResponse(resp);
DEBUG_LOG(("Saw text [%hs] (%ls) %d chars Orig was %s (%d chars)\n", nick, resp.text.c_str(), resp.text.length(), message, strlen(message)));
UnsignedInt IP;
peerGetPlayerInfoNoWait(peer, nick, &IP, &resp.message.profileID);
PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (t && (t->getQMStatus() != QM_IDLE && t->getQMStatus() != QM_STOPPED))
{
if (resp.message.profileID == matchbotProfileID)
{
char *lastStr = NULL;
char *cmd = strtok_r((char *)message, " ", &lastStr);
if ( cmd && strcmp(cmd, "MBOT:POOLSIZE") == 0 )
{
Int poolSize = 0;
while (1)
{
char *poolStr = strtok_r(NULL, " ", &lastStr);
char *sizeStr = strtok_r(NULL, " ", &lastStr);
if (poolStr && sizeStr)
{
Int pool = atoi(poolStr);
Int size = atoi(sizeStr);
if (pool == t->getQMLadder())
{
poolSize = size;
break;
}
}
else
break;
}
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS;
resp.qmStatus.status = QM_POOLSIZE;
resp.qmStatus.poolSize = poolSize;
TheGameSpyPeerMessageQueue->addResponse(resp);
}
}
}
}
void gameStartedCallback( PEER peer, UnsignedInt IP, const char *message, void *param )
{
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_GAMESTART;
TheGameSpyPeerMessageQueue->addResponse(resp);
}
void playerMessageCallback(PEER peer, const char * nick, const char * message, MessageType messageType, void * param)
{
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_MESSAGE;
resp.nick = nick;
resp.text = MultiByteToWideCharSingleLine(message);
resp.message.isPrivate = TRUE;
resp.message.isAction = (messageType == ActionMessage);
UnsignedInt IP;
peerGetPlayerInfoNoWait(peer, nick, &IP, &resp.message.profileID);
TheGameSpyPeerMessageQueue->addResponse(resp);
PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (t && (t->getQMStatus() != QM_IDLE && t->getQMStatus() != QM_STOPPED))
{
if (resp.message.isPrivate && resp.message.profileID == matchbotProfileID)
{
char *lastStr = NULL;
char *cmd = strtok_r((char *)message, " ", &lastStr);
if ( cmd && strcmp(cmd, "MBOT:MATCHED") == 0 )
{
char *mapNumStr = strtok_r(NULL, " ", &lastStr);
char *seedStr = strtok_r(NULL, " ", &lastStr);
char *playerStr[MAX_SLOTS];
char *playerIPStr[MAX_SLOTS];
char *playerSideStr[MAX_SLOTS];
char *playerColorStr[MAX_SLOTS];
char *playerNATStr[MAX_SLOTS];
Int numPlayers = 0;
for (Int i=0; i 1)
{
// woohoo! got everything needed for a match!
DEBUG_LOG(("Saw %d-player QM match: map index = %s, seed = %s\n", numPlayers, mapNumStr, seedStr));
t->handleQMMatch(peer, atoi(mapNumStr), atoi(seedStr), playerStr, playerIPStr, playerSideStr, playerColorStr, playerNATStr);
}
}
else if ( cmd && strcmp(cmd, "MBOT:WORKING") == 0 )
{
Int poolSize = 0;
char *poolStr = strtok_r(NULL, " ", &lastStr);
if (poolStr)
poolSize = atoi(poolStr);
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS;
resp.qmStatus.status = QM_WORKING;
resp.qmStatus.poolSize = poolSize;
TheGameSpyPeerMessageQueue->addResponse(resp);
}
else if ( cmd && strcmp(cmd, "MBOT:WIDENINGSEARCH") == 0 )
{
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS;
resp.qmStatus.status = QM_WIDENINGSEARCH;
TheGameSpyPeerMessageQueue->addResponse(resp);
}
}
}
}
void roomUTMCallback(PEER peer, RoomType roomType, const char * nick, const char * command, const char * parameters, PEERBool authenticated, void * param)
{
DEBUG_LOG(("roomUTMCallback: %s says %s = [%s]\n", nick, command, parameters));
if (roomType != StagingRoom)
return;
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_ROOMUTM;
resp.nick = nick;
resp.command = command;
resp.commandOptions = parameters;
TheGameSpyPeerMessageQueue->addResponse(resp);
}
void playerUTMCallback(PEER peer, const char * nick, const char * command, const char * parameters, PEERBool authenticated, void * param)
{
DEBUG_LOG(("playerUTMCallback: %s says %s = [%s]\n", nick, command, parameters));
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_PLAYERUTM;
resp.nick = nick;
resp.command = command;
resp.commandOptions = parameters;
TheGameSpyPeerMessageQueue->addResponse(resp);
}
static void getPlayerInfo(PeerThreadClass *t, PEER peer, const char *nick, Int& id, UnsignedInt& IP,
std::string& locale, Int& wins, Int& losses, Int& rankPoints, Int& side, Int& preorder,
RoomType roomType, Int& flags)
{
if (!t || !nick)
return;
peerGetPlayerInfoNoWait(peer, nick, &IP, &id);
#ifdef USE_BROADCAST_KEYS
//locale.printf
Int localeIndex = t->lookupStatForPlayer(roomType, nick, "b_locale");
AsciiString tmp;
tmp.format("%d", localeIndex);
locale = tmp.str();
wins = t->lookupStatForPlayer(roomType, nick, "b_wins");
losses = t->lookupStatForPlayer(roomType, nick, "b_losses");
rankPoints = t->lookupStatForPlayer(roomType, nick, "b_points");
side = t->lookupStatForPlayer(roomType, nick, "b_side");
preorder = t->lookupStatForPlayer(roomType, nick, "b_pre");
#else // USE_BROADCAST_KEYS
const char *s;
s = peerGetGlobalWatchKey(peer, nick, "locale");
locale = (s)?s:"";
s = peerGetGlobalWatchKey(peer, nick, "wins");
wins = atoi((s)?s:"");
s = peerGetGlobalWatchKey(peer, nick, "losses");
losses = atoi((s)?s:"");
s = peerGetGlobalWatchKey(peer, nick, "points");
rankPoints = atoi((s)?s:"");
s = peerGetGlobalWatchKey(peer, nick, "side");
side = atoi((s)?s:"");
s = peerGetGlobalWatchKey(peer, nick, "pre");
preorder = atoi((s)?s:"");
#endif // USE_BROADCAST_KEYS
flags = 0;
peerGetPlayerFlags(peer, nick, roomType, &flags);
DEBUG_LOG(("getPlayerInfo(%d) - %s has locale %s, wins:%d, losses:%d, rankPoints:%d, side:%d, preorder:%d\n",
id, nick, locale.c_str(), wins, losses, rankPoints, side, preorder));
}
static void roomKeyChangedCallback(PEER peer, RoomType roomType, const char *nick, const char *key, const char *val, void *param)
{
#ifdef USE_BROADCAST_KEYS
PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (!t || !nick || !key || !val)
{
DEBUG_ASSERTCRASH(nick && strcmp(nick,"(END)")==0, ("roomKeyChangedCallback bad values = nick:%X:%s, key:%X:%s, val:%X:%s\n", nick, nick, key, key, val, val));
return;
}
#ifdef DEBUG_LOGGING
if (strcmp(key, "username") && strcmp(key, "b_flags"))
{
DEBUG_LOG(("roomKeyChangedCallback() - %s set %s=%s\n", nick, key, val));
}
#endif
t->trackStatsForPlayer(roomType, nick, key, val);
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_PLAYERINFO;
resp.nick = nick;
resp.player.roomType = roomType;
getPlayerInfo(t, peer, nick, resp.player.profileID, resp.player.IP,
resp.locale, resp.player.wins, resp.player.losses,
resp.player.rankPoints, resp.player.side, resp.player.preorder,
resp.player.roomType, resp.player.flags);
TheGameSpyPeerMessageQueue->addResponse(resp);
#endif // USE_BROADCAST_KEYS
}
#ifdef USE_BROADCAST_KEYS
void getRoomKeysCallback(PEER peer, PEERBool success, RoomType roomType, const char *nick, int num, char **keys, char **values, void *param)
{
PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (!t || !nick || !num || !success || !keys || !values)
{
DEBUG_ASSERTCRASH(!nick || strcmp(nick,"(END)")==0, ("getRoomKeysCallback bad key/value %X/%X, nick=%s", keys, values, nick));
return;
}
for (Int i=0; itrackStatsForPlayer(roomType, nick, keys[i], values[i]);
}
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_PLAYERINFO;
resp.nick = nick;
resp.player.roomType = roomType;
getPlayerInfo(t, peer, nick, resp.player.profileID, resp.player.IP,
resp.locale, resp.player.wins, resp.player.losses,
resp.player.rankPoints, resp.player.side, resp.player.preorder,
resp.player.roomType, resp.player.flags);
TheGameSpyPeerMessageQueue->addResponse(resp);
}
#endif // USE_BROADCAST_KEYS
static void globalKeyChangedCallback(PEER peer, const char *nick, const char *key, const char *val, void *param)
{
if (!nick)
return;
PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (!t)
return;
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_PLAYERINFO;
resp.nick = nick;
resp.player.roomType = t->getCurrentGroupRoom()?GroupRoom:StagingRoom;
getPlayerInfo(t, peer, nick, resp.player.profileID, resp.player.IP,
resp.locale, resp.player.wins, resp.player.losses,
resp.player.rankPoints, resp.player.side, resp.player.preorder,
resp.player.roomType, resp.player.flags);
TheGameSpyPeerMessageQueue->addResponse(resp);
}
void playerJoinedCallback(PEER peer, RoomType roomType, const char * nick, void * param)
{
if (!nick)
return;
PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (!t)
return;
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_PLAYERJOIN;
resp.nick = nick;
resp.player.roomType = roomType;
getPlayerInfo(t, peer, nick, resp.player.profileID, resp.player.IP,
resp.locale, resp.player.wins, resp.player.losses,
resp.player.rankPoints, resp.player.side, resp.player.preorder,
roomType, resp.player.flags);
TheGameSpyPeerMessageQueue->addResponse(resp);
}
void playerLeftCallback(PEER peer, RoomType roomType, const char * nick, const char * reason, void * param)
{
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_PLAYERLEFT;
resp.nick = nick;
resp.player.roomType = roomType;
resp.player.profileID = 0;
PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (!t)
return;
getPlayerInfo(t, peer, nick, resp.player.profileID, resp.player.IP,
resp.locale, resp.player.wins, resp.player.losses,
resp.player.rankPoints, resp.player.side, resp.player.preorder,
roomType, resp.player.flags);
TheGameSpyPeerMessageQueue->addResponse(resp);
// PeerThreadClass *t = (PeerThreadClass *)param;
// DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (t->getQMStatus() != QM_IDLE && t->getQMStatus() != QM_STOPPED)
{
if (!stricmp(t->getQMBotName().c_str(), nick))
{
// matchbot left - bail
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_QUICKMATCHSTATUS;
resp.qmStatus.status = QM_COULDNOTFINDBOT;
TheGameSpyPeerMessageQueue->addResponse(resp);
PeerRequest req;
req.peerRequestType = PeerRequest::PEERREQUEST_STOPQUICKMATCH;
TheGameSpyPeerMessageQueue->addRequest(req);
}
}
}
void playerChangedNickCallback(PEER peer, RoomType roomType, const char * oldNick, const char * newNick, void * param)
{
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_PLAYERCHANGEDNICK;
resp.nick = newNick;
resp.oldNick = oldNick;
resp.player.roomType = roomType;
PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (!t)
return;
getPlayerInfo(t, peer, newNick, resp.player.profileID, resp.player.IP,
resp.locale, resp.player.wins, resp.player.losses,
resp.player.rankPoints, resp.player.side, resp.player.preorder,
roomType, resp.player.flags);
TheGameSpyPeerMessageQueue->addResponse(resp);
}
static void playerInfoCallback(PEER peer, RoomType roomType, const char * nick, unsigned int IP, int profileID, void * param)
{
if (!nick)
return;
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_PLAYERINFO;
resp.nick = nick;
resp.player.roomType = roomType;
PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (!t)
return;
getPlayerInfo(t, peer, nick, resp.player.profileID, resp.player.IP,
resp.locale, resp.player.wins, resp.player.losses,
resp.player.rankPoints, resp.player.side, resp.player.preorder,
roomType, resp.player.flags);
DEBUG_LOG(("**GS playerInfoCallback name=%s, local=%s\n", nick, resp.locale.c_str() ));
TheGameSpyPeerMessageQueue->addResponse(resp);
}
static void playerFlagsChangedCallback(PEER peer, RoomType roomType, const char * nick, int oldFlags, int newFlags, void * param)
{
if (!nick)
return;
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_PLAYERCHANGEDFLAGS;
resp.nick = nick;
resp.player.roomType = roomType;
PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(t, ("No Peer thread!"));
if (!t)
return;
getPlayerInfo(t, peer, nick, resp.player.profileID, resp.player.IP,
resp.locale, resp.player.wins, resp.player.losses,
resp.player.rankPoints, resp.player.side, resp.player.preorder,
roomType, resp.player.flags);
TheGameSpyPeerMessageQueue->addResponse(resp);
}
#ifdef DEBUG_LOGGING
/*
static void enumFunc(char *key, char *val, void *param)
{
DEBUG_LOG((" [%s] = [%s]\n", key, val));
}
*/
#endif
static void listingGamesCallback(PEER peer, PEERBool success, const char * name, SBServer server, PEERBool staging, int msg, Int percentListed, void * param)
{
PeerThreadClass *t = (PeerThreadClass *)param;
if (!t || !success)
return;
#ifdef DEBUG_LOGGING
AsciiString cmdStr = "";
switch(msg)
{
case PEER_ADD:
cmdStr = "PEER_ADD";
break;
case PEER_UPDATE:
cmdStr = "PEER_UPDATE";
break;
case PEER_REMOVE:
cmdStr = "PEER_REMOVE";
break;
case PEER_CLEAR:
cmdStr = "PEER_CLEAR";
break;
case PEER_COMPLETE:
cmdStr = "PEER_COMPLETE";
break;
}
DEBUG_LOG(("listingGamesCallback() - doing command %s on server %X\n", cmdStr.str(), server));
#endif // DEBUG_LOGGING
// PeerThreadClass *t = (PeerThreadClass *)param;
DEBUG_ASSERTCRASH(name || msg==PEER_CLEAR || msg==PEER_COMPLETE, ("Game has no name!\n"));
if (!t || !success || (!name && (msg == PEER_ADD || msg == PEER_UPDATE)))
{
DEBUG_LOG(("Bailing from listingGamesCallback() - success=%d, name=%X, server=%X, msg=%X\n", success, name, server, msg));
return;
}
if (!name)
name = "bogus";
if (server && (msg == PEER_ADD || msg == PEER_UPDATE))
{
DEBUG_ASSERTCRASH(server->keyvals, ("Looking at an already-freed server for msg type %d!", msg));
if (!server->keyvals)
{
msg = PEER_REMOVE;
}
}
if (server && success && (msg == PEER_ADD || msg == PEER_UPDATE))
{
DEBUG_LOG(("Game name is '%s'\n", name));
const char *newname = SBServerGetStringValue(server, "gamename", (char *)name);
if (strcmp(newname, "ccgenzh"))
name = newname;
DEBUG_LOG(("Game name is now '%s'\n", name));
}
DEBUG_LOG(("listingGamesCallback - got percent complete %d\n", percentListed));
if (percentListed == 100)
{
if (!t->getSawCompleteGameList())
{
t->setSawCompleteGameList(TRUE);
PeerResponse completeResp;
completeResp.peerResponseType = PeerResponse::PEERRESPONSE_STAGINGROOMLISTCOMPLETE;
TheGameSpyPeerMessageQueue->addResponse(completeResp);
}
}
AsciiString gameName = name;
AsciiString tmp = gameName;
AsciiString hostName;
tmp.nextToken(&hostName, " ");
const char *firstSpace = gameName.find(' ');
if(firstSpace)
{
gameName.set(firstSpace + 1);
//gameName.trim();
DEBUG_LOG(("Hostname/Gamename split leaves '%s' hosting '%s'\n", hostName.str(), gameName.str()));
}
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_STAGINGROOM;
resp.stagingRoom.action = msg;
resp.stagingRoom.isStaging = staging;
resp.stagingRoom.percentComplete = percentListed;
if (server && (msg == PEER_ADD || msg == PEER_UPDATE))
{
Bool hasPassword = (Bool)SBServerGetIntValue(server, PW_STR, FALSE);
Bool allowObservers = (Bool)SBServerGetIntValue(server, OBS_STR, FALSE);
Bool usesStats = (Bool)SBServerGetIntValue(server, USE_STATS_STR, TRUE);
const char *verStr = SBServerGetStringValue(server, "gamever", "000000");
const char *exeStr = SBServerGetStringValue(server, EXECRC_STR, "000000");
const char *iniStr = SBServerGetStringValue(server, INICRC_STR, "000000");
const char *ladIPStr = SBServerGetStringValue(server, LADIP_STR, "000000");
const char *pingStr = SBServerGetStringValue(server, PINGSTR_STR, "FFFFFFFFFFFFFFFF");
UnsignedShort ladPort = (UnsignedShort)SBServerGetIntValue(server, LADPORT_STR, 0);
UnsignedInt verVal = strtoul(verStr, NULL, 10);
UnsignedInt exeVal = strtoul(exeStr, NULL, 10);
UnsignedInt iniVal = strtoul(iniStr, NULL, 10);
resp.stagingRoom.requiresPassword = hasPassword;
resp.stagingRoom.allowObservers = allowObservers;
resp.stagingRoom.useStats = usesStats;
resp.stagingRoom.version = verVal;
resp.stagingRoom.exeCRC = exeVal;
resp.stagingRoom.iniCRC = iniVal;
resp.stagingServerLadderIP = ladIPStr;
resp.stagingServerPingString = pingStr;
resp.stagingRoom.ladderPort = ladPort;
resp.stagingRoom.numPlayers = SBServerGetIntValue(server, NUMPLAYER_STR, 0);
resp.stagingRoom.numObservers = SBServerGetIntValue(server, NUMOBS_STR, 0);
resp.stagingRoom.maxPlayers = SBServerGetIntValue(server, MAXPLAYER_STR, 8);
resp.stagingRoomMapName = SBServerGetStringValue(server, "mapname", "");
for (Int i=0; ifindServer( server );
DEBUG_LOG(("Add/update a 0/0 server %X (%d, %s) - requesting full update to see if that helps.\n",
server, resp.stagingRoom.id, gameName.str()));
TheGameSpyPeerMessageQueue->addRequest(req);
}
return; // don't actually try to list it.
}
}
switch (msg)
{
case PEER_CLEAR:
t->clearServers();
break;
case PEER_ADD:
case PEER_UPDATE:
resp.stagingRoom.id = t->findServer( server );
DEBUG_LOG(("Add/update on server %X (%d, %s)\n", server, resp.stagingRoom.id, gameName.str()));
resp.stagingServerName = MultiByteToWideCharSingleLine( gameName.str() );
DEBUG_LOG(("Server had basic=%d, full=%d\n", SBServerHasBasicKeys(server), SBServerHasFullKeys(server)));
#ifdef DEBUG_LOGGING
//SBServerEnumKeys(server, enumFunc, NULL);
#endif
break;
case PEER_REMOVE:
DEBUG_LOG(("Removing server %X (%d)\n", server, resp.stagingRoom.id));
resp.stagingRoom.id = t->removeServerFromMap( server );
break;
}
TheGameSpyPeerMessageQueue->addResponse(resp);
}
//-------------------------------------------------------------------------