mirror of
https://github.com/electronicarts/CnC_Generals_Zero_Hour.git
synced 2025-12-16 23:51:41 -05:00
Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.
This commit is contained in:
424
Generals/Code/GameEngine/Source/GameNetwork/Connection.cpp
Normal file
424
Generals/Code/GameEngine/Source/GameNetwork/Connection.cpp
Normal file
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/Connection.h"
|
||||
#include "GameNetwork/NetworkUtil.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
|
||||
enum { MaxQuitFlushTime = 30000 }; // wait this many milliseconds at most to retry things before quitting
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*/
|
||||
Connection::Connection() {
|
||||
m_transport = NULL;
|
||||
m_user = NULL;
|
||||
m_netCommandList = NULL;
|
||||
m_retryTime = 2000; // set retry time to 2 seconds.
|
||||
m_lastTimeSent = 0;
|
||||
m_frameGrouping = 1;
|
||||
m_isQuitting = false;
|
||||
m_quitTime = 0;
|
||||
// Added By Sadullah Nader
|
||||
// clearing out the latency tracker
|
||||
m_averageLatency = 0.0f;
|
||||
Int i;
|
||||
for(i = 0; i < CONNECTION_LATENCY_HISTORY_LENGTH; i++)
|
||||
{
|
||||
m_latencies[i] = 0.0f;
|
||||
}
|
||||
// End Add
|
||||
}
|
||||
|
||||
/**
|
||||
* The destructor.
|
||||
*/
|
||||
Connection::~Connection() {
|
||||
if (m_user != NULL) {
|
||||
m_user->deleteInstance();
|
||||
m_user = NULL;
|
||||
}
|
||||
|
||||
if (m_netCommandList != NULL) {
|
||||
m_netCommandList->deleteInstance();
|
||||
m_netCommandList = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the connection and any subsystems.
|
||||
*/
|
||||
void Connection::init() {
|
||||
m_transport = NULL;
|
||||
|
||||
if (m_user != NULL) {
|
||||
m_user->deleteInstance();
|
||||
m_user = NULL;
|
||||
}
|
||||
|
||||
if (m_netCommandList == NULL) {
|
||||
m_netCommandList = newInstance(NetCommandList);
|
||||
m_netCommandList->init();
|
||||
}
|
||||
m_netCommandList->reset();
|
||||
|
||||
m_lastTimeSent = 0;
|
||||
m_frameGrouping = 1;
|
||||
m_numRetries = 0;
|
||||
m_retryMetricsTime = 0;
|
||||
|
||||
for (Int i = 0; i < CONNECTION_LATENCY_HISTORY_LENGTH; ++i) {
|
||||
m_latencies[i] = 0;
|
||||
}
|
||||
m_averageLatency = 0;
|
||||
m_isQuitting = FALSE;
|
||||
m_quitTime = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the connection back to the initial state.
|
||||
*/
|
||||
void Connection::reset() {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Doesn't really do anything.
|
||||
*/
|
||||
void Connection::update() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the transport object that this connection should use.
|
||||
*/
|
||||
void Connection::attachTransport(Transport *transport) {
|
||||
m_transport = transport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign this connection a user. This is the user to whome we send all our packetized goodies.
|
||||
*/
|
||||
void Connection::setUser(User *user) {
|
||||
if (m_user != NULL) {
|
||||
m_user->deleteInstance();
|
||||
}
|
||||
|
||||
m_user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user object.
|
||||
*/
|
||||
User * Connection::getUser() {
|
||||
return m_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this network command to the send queue for this connection.
|
||||
* The relay is the mask specifying the people the person we are sending to should send to.
|
||||
* The relay mostly has to do with the packet router.
|
||||
*/
|
||||
void Connection::sendNetCommandMsg(NetCommandMsg *msg, UnsignedByte relay) {
|
||||
static NetPacket *packet = NULL;
|
||||
|
||||
// this is done so we don't have to allocate and delete a packet every time we send a message.
|
||||
if (packet == NULL) {
|
||||
packet = newInstance(NetPacket);
|
||||
}
|
||||
|
||||
|
||||
if (m_isQuitting)
|
||||
return;
|
||||
|
||||
if (m_netCommandList != NULL) {
|
||||
// check to see if this command will fit in a packet. If not, we need to split it up.
|
||||
// we are splitting up the command here so that the retry logic will not try to
|
||||
// resend the ENTIRE command (i.e. multiple packets work of data) and only do the retry
|
||||
// one wrapper command at a time.
|
||||
packet->reset();
|
||||
|
||||
NetCommandRef *tempref = NEW_NETCOMMANDREF(msg);
|
||||
|
||||
Bool msgFits = packet->addCommand(tempref);
|
||||
tempref->deleteInstance(); // delete the temporary reference.
|
||||
tempref = NULL;
|
||||
|
||||
if (!msgFits) {
|
||||
NetCommandRef *origref = NEW_NETCOMMANDREF(msg);
|
||||
origref->setRelay(relay);
|
||||
// the message doesn't fit in a single packet, need to split it up.
|
||||
NetPacketList packetList = NetPacket::ConstructBigCommandPacketList(origref);
|
||||
NetPacketListIter tempPacketPtr = packetList.begin();
|
||||
|
||||
while (tempPacketPtr != packetList.end()) {
|
||||
NetPacket *tempPacket = (*tempPacketPtr);
|
||||
|
||||
NetCommandList *list = tempPacket->getCommandList();
|
||||
NetCommandRef *ref1 = list->getFirstMessage();
|
||||
while (ref1 != NULL) {
|
||||
NetCommandRef *ref2 = m_netCommandList->addMessage(ref1->getCommand());
|
||||
ref2->setRelay(relay);
|
||||
|
||||
ref1 = ref1->getNext();
|
||||
}
|
||||
|
||||
tempPacket->deleteInstance();
|
||||
tempPacket = NULL;
|
||||
++tempPacketPtr;
|
||||
|
||||
list->deleteInstance();
|
||||
list = NULL;
|
||||
}
|
||||
|
||||
origref->deleteInstance();
|
||||
origref = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// the message fits in a packet, add to the command list normally.
|
||||
NetCommandRef *ref = m_netCommandList->addMessage(msg);
|
||||
|
||||
if (ref != NULL) {
|
||||
|
||||
/*
|
||||
#if ((defined(_DEBUG)) || (defined(_INTERNAL)))
|
||||
if (msg->getNetCommandType() == NETCOMMANDTYPE_GAMECOMMAND) {
|
||||
DEBUG_LOG(("Connection::sendNetCommandMsg - added game command %d to net command list for frame %d.\n",
|
||||
msg->getID(), msg->getExecutionFrame()));
|
||||
} else if (msg->getNetCommandType() == NETCOMMANDTYPE_FRAMEINFO) {
|
||||
DEBUG_LOG(("Connection::sendNetCommandMsg - added frame info for frame %d\n", msg->getExecutionFrame()));
|
||||
}
|
||||
#endif // _DEBUG || _INTERNAL
|
||||
*/
|
||||
|
||||
ref->setRelay(relay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::clearCommandsExceptFrom( Int playerIndex )
|
||||
{
|
||||
NetCommandRef *tmp = m_netCommandList->getFirstMessage();
|
||||
while (tmp)
|
||||
{
|
||||
NetCommandMsg *msg = tmp->getCommand();
|
||||
if (msg->getPlayerID() != playerIndex)
|
||||
{
|
||||
DEBUG_LOG(("Connection::clearCommandsExceptFrom(%d) - clearing a command from %d for frame %d\n",
|
||||
playerIndex, tmp->getCommand()->getPlayerID(), tmp->getCommand()->getExecutionFrame()));
|
||||
m_netCommandList->removeMessage(tmp);
|
||||
NetCommandRef *toDelete = tmp;
|
||||
tmp = tmp->getNext();
|
||||
toDelete->deleteInstance();
|
||||
} else {
|
||||
tmp = tmp->getNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bool Connection::isQueueEmpty() {
|
||||
if (m_netCommandList->getFirstMessage() == NULL) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void Connection::setQuitting( void )
|
||||
{
|
||||
m_isQuitting = TRUE;
|
||||
m_quitTime = timeGetTime();
|
||||
DEBUG_LOG(("Connection::setQuitting() at time %d\n", m_quitTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the good part. We take all the network commands queued up for this connection,
|
||||
* packetize them and put them on the transport's send queue for actual sending.
|
||||
*/
|
||||
UnsignedInt Connection::doSend() {
|
||||
Int numpackets = 0;
|
||||
time_t curtime = timeGetTime();
|
||||
Bool couldQueue = TRUE;
|
||||
|
||||
// Do this check first, since it's an important fail-safe
|
||||
if (m_isQuitting && curtime > m_quitTime + MaxQuitFlushTime)
|
||||
{
|
||||
DEBUG_LOG(("Timed out a quitting connection. Deleting all %d messages\n", m_netCommandList->length()));
|
||||
m_netCommandList->reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((curtime - m_lastTimeSent) < m_frameGrouping) {
|
||||
// DEBUG_LOG(("not sending packet, time = %d, m_lastFrameSent = %d, m_frameGrouping = %d\n", curtime, m_lastTimeSent, m_frameGrouping));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// iterate through all the messages and put them into a packet(s).
|
||||
NetCommandRef *msg = m_netCommandList->getFirstMessage();
|
||||
|
||||
while ((msg != NULL) && couldQueue) {
|
||||
NetPacket *packet = newInstance(NetPacket);
|
||||
packet->init();
|
||||
packet->setAddress(m_user->GetIPAddr(), m_user->GetPort());
|
||||
|
||||
Bool notDone = TRUE;
|
||||
|
||||
// add the command messages until either we run out of messages or the packet is full.
|
||||
while ((msg != NULL) && notDone) {
|
||||
NetCommandRef *next = msg->getNext(); // Need this since msg could be deleted
|
||||
|
||||
time_t timeLastSent = msg->getTimeLastSent();
|
||||
|
||||
if (((curtime - timeLastSent) > m_retryTime) || (timeLastSent == -1)) {
|
||||
notDone = packet->addCommand(msg);
|
||||
if (notDone) {
|
||||
// the msg command was added to the packet.
|
||||
if (CommandRequiresAck(msg->getCommand())) {
|
||||
if (timeLastSent != -1) {
|
||||
++m_numRetries;
|
||||
}
|
||||
doRetryMetrics();
|
||||
msg->setTimeLastSent(curtime);
|
||||
} else {
|
||||
m_netCommandList->removeMessage(msg);
|
||||
msg->deleteInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
msg = next;
|
||||
}
|
||||
|
||||
if (msg != NULL) {
|
||||
DEBUG_LOG(("didn't finish sending all commands in connection\n"));
|
||||
}
|
||||
|
||||
++numpackets;
|
||||
|
||||
/// @todo Make the act of giving the transport object a packet to send more efficient. Make the transport take a NetPacket object rather than the raw data, thus avoiding an extra memcpy.
|
||||
if (packet->getNumCommands() > 0) {
|
||||
// If the packet actually has any information to give, give it to the transport object
|
||||
// for transmission.
|
||||
couldQueue = m_transport->queueSend(packet->getAddr(), packet->getPort(), packet->getData(), packet->getLength());
|
||||
m_lastTimeSent = curtime;
|
||||
}
|
||||
if (packet != NULL) {
|
||||
packet->deleteInstance(); // delete the packet now that we're done with it.
|
||||
}
|
||||
}
|
||||
|
||||
return numpackets;
|
||||
}
|
||||
|
||||
NetCommandRef * Connection::processAck(NetAckStage1CommandMsg *msg) {
|
||||
return processAck(msg->getCommandID(), msg->getOriginalPlayerID());
|
||||
}
|
||||
|
||||
NetCommandRef * Connection::processAck(NetAckBothCommandMsg *msg) {
|
||||
return processAck(msg->getCommandID(), msg->getOriginalPlayerID());
|
||||
}
|
||||
|
||||
NetCommandRef * Connection::processAck(NetCommandMsg *msg) {
|
||||
if (msg->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE1) {
|
||||
NetAckStage1CommandMsg *ackmsg = (NetAckStage1CommandMsg *)msg;
|
||||
return processAck(ackmsg);
|
||||
}
|
||||
|
||||
if (msg->getNetCommandType() == NETCOMMANDTYPE_ACKBOTH) {
|
||||
NetAckBothCommandMsg *ackmsg = (NetAckBothCommandMsg *)msg;
|
||||
return processAck(ackmsg);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The person we are sending to has ack'd one of the messages we sent him.
|
||||
* Take that message off the list of commands to send.
|
||||
*/
|
||||
NetCommandRef * Connection::processAck(UnsignedShort commandID, UnsignedByte originalPlayerID) {
|
||||
NetCommandRef *temp = m_netCommandList->getFirstMessage();
|
||||
while ((temp != NULL) && ((temp->getCommand()->getID() != commandID) || (temp->getCommand()->getPlayerID() != originalPlayerID))) {
|
||||
|
||||
// cycle through the commands till we find the one we need to remove.
|
||||
// Need to check for both the command ID and the player ID.
|
||||
temp = temp->getNext();
|
||||
}
|
||||
if (temp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
Bool doDebug = FALSE;
|
||||
if (temp->getCommand()->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTFRAME) {
|
||||
doDebug = TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
Int index = temp->getCommand()->getID() % CONNECTION_LATENCY_HISTORY_LENGTH;
|
||||
m_averageLatency -= ((Real)(m_latencies[index])) / CONNECTION_LATENCY_HISTORY_LENGTH;
|
||||
Real lat = timeGetTime() - temp->getTimeLastSent();
|
||||
m_averageLatency += lat / CONNECTION_LATENCY_HISTORY_LENGTH;
|
||||
m_latencies[index] = lat;
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if (doDebug == TRUE) {
|
||||
DEBUG_LOG(("Connection::processAck - disconnect frame command %d found, removing from command list.\n", commandID));
|
||||
}
|
||||
#endif
|
||||
m_netCommandList->removeMessage(temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
void Connection::setFrameGrouping(time_t frameGrouping) {
|
||||
m_frameGrouping = frameGrouping;
|
||||
// m_retryTime = frameGrouping * 4;
|
||||
}
|
||||
|
||||
void Connection::doRetryMetrics() {
|
||||
static Int numSeconds = 0;
|
||||
time_t curTime = timeGetTime();
|
||||
|
||||
if ((curTime - m_retryMetricsTime) > 10000) {
|
||||
m_retryMetricsTime = curTime;
|
||||
++numSeconds;
|
||||
// DEBUG_LOG(("Retries in the last 10 seconds = %d, average latency = %fms\n", m_numRetries, m_averageLatency));
|
||||
m_numRetries = 0;
|
||||
// m_retryTime = m_averageLatency * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) || (_INTERNAL)
|
||||
void Connection::debugPrintCommands() {
|
||||
NetCommandRef *ref = m_netCommandList->getFirstMessage();
|
||||
while (ref != NULL) {
|
||||
DEBUG_LOG(("Connection::debugPrintCommands - ID: %d\tType: %s\tRelay: 0x%X for frame %d\n",
|
||||
ref->getCommand()->getID(), GetAsciiNetCommandType(ref->getCommand()->getNetCommandType()).str(),
|
||||
ref->getRelay(), ref->getCommand()->getExecutionFrame()));
|
||||
ref = ref->getNext();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
2455
Generals/Code/GameEngine/Source/GameNetwork/ConnectionManager.cpp
Normal file
2455
Generals/Code/GameEngine/Source/GameNetwork/ConnectionManager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,809 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Recorder.h"
|
||||
#include "GameClient/DisconnectMenu.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameNetwork/DisconnectManager.h"
|
||||
#include "GameNetwork/NetworkInterface.h"
|
||||
#include "GameNetwork/NetworkUtil.h"
|
||||
#include "GameNetwork/GameSpy/PingThread.h"
|
||||
#include "GameNetwork/GameSpy/GSConfig.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
DisconnectManager::DisconnectManager()
|
||||
{
|
||||
// Added By Sadullah Nader
|
||||
// Initializations missing and needed
|
||||
Int i;
|
||||
m_currentPacketRouterIndex = 0;
|
||||
m_lastFrame = 0;
|
||||
m_lastFrameTime = 0;
|
||||
m_lastKeepAliveSendTime = 0;
|
||||
m_haveNotifiedOtherPlayersOfCurrentFrame = FALSE;
|
||||
m_timeOfDisconnectScreenOn = 0;
|
||||
|
||||
for( i = 0; i < MAX_SLOTS; ++i) {
|
||||
m_packetRouterFallback[i] = 0;
|
||||
}
|
||||
|
||||
m_packetRouterTimeout = 0;
|
||||
for( i = 0; i < MAX_SLOTS -1; ++i) {
|
||||
m_playerTimeouts[i] = 0;
|
||||
}
|
||||
|
||||
for( i = 0; i < MAX_SLOTS; ++i) {
|
||||
for (Int j = 0; j < MAX_SLOTS; ++j) {
|
||||
m_playerVotes[i][j].vote = FALSE;
|
||||
m_playerVotes[i][j].frame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisconnectManager::~DisconnectManager() {
|
||||
}
|
||||
|
||||
void DisconnectManager::init() {
|
||||
TheDisconnectMenu->hideScreen(); // make sure the screen starts out hidden.
|
||||
m_lastFrame = 0;
|
||||
m_lastFrameTime = -1;
|
||||
m_lastKeepAliveSendTime = -1;
|
||||
m_disconnectState = DISCONNECTSTATETYPE_SCREENOFF;
|
||||
m_currentPacketRouterIndex = 0;
|
||||
m_timeOfDisconnectScreenOn = 0;
|
||||
|
||||
for (Int i = 0; i < MAX_SLOTS; ++i) {
|
||||
for (Int j = 0; j < MAX_SLOTS; ++j) {
|
||||
m_playerVotes[i][j].vote = FALSE;
|
||||
m_playerVotes[i][j].frame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_SLOTS; ++i) {
|
||||
m_disconnectFrames[i] = 0;
|
||||
m_disconnectFramesReceived[i] = FALSE;
|
||||
}
|
||||
|
||||
m_pingFrame = 0;
|
||||
m_pingsSent = 0;
|
||||
m_pingsRecieved = 0;
|
||||
}
|
||||
|
||||
void DisconnectManager::update(ConnectionManager *conMgr) {
|
||||
if (m_lastFrameTime == -1) {
|
||||
m_lastFrameTime = timeGetTime();
|
||||
}
|
||||
|
||||
// The game logic stalls on the frame we are currently waiting for commands on,
|
||||
// so we have to check for the current logic frame being one higher than
|
||||
// the last one we had the commands ready for.
|
||||
if (TheGameLogic->getFrame() == m_lastFrame) {
|
||||
time_t curTime = timeGetTime();
|
||||
if ((curTime - m_lastFrameTime) > TheGlobalData->m_networkDisconnectTime) {
|
||||
if (m_disconnectState == DISCONNECTSTATETYPE_SCREENOFF) {
|
||||
turnOnScreen(conMgr);
|
||||
}
|
||||
sendKeepAlive(conMgr);
|
||||
}
|
||||
} else {
|
||||
nextFrame(TheGameLogic->getFrame(), conMgr);
|
||||
}
|
||||
|
||||
if (m_disconnectState != DISCONNECTSTATETYPE_SCREENOFF) {
|
||||
updateDisconnectStatus(conMgr);
|
||||
|
||||
// check to see if we need to send pings
|
||||
if (m_pingFrame < TheGameLogic->getFrame())
|
||||
{
|
||||
time_t curTime = timeGetTime();
|
||||
if ((curTime - m_lastFrameTime) > 10000) /// @todo: plug in some better measure here
|
||||
{
|
||||
m_pingFrame = TheGameLogic->getFrame();
|
||||
m_pingsSent = 0;
|
||||
m_pingsRecieved = 0;
|
||||
|
||||
// Send the pings
|
||||
if (ThePinger)
|
||||
{
|
||||
//use next ping server
|
||||
static int serverIndex = 0;
|
||||
serverIndex++;
|
||||
if( serverIndex >= TheGameSpyConfig->getPingServers().size() )
|
||||
serverIndex = 0; //wrap back to first ping server
|
||||
|
||||
std::list<AsciiString>::iterator it = TheGameSpyConfig->getPingServers().begin();
|
||||
for( int i = 0; i < serverIndex; i++ )
|
||||
it++;
|
||||
|
||||
PingRequest req;
|
||||
req.hostname = it->str();
|
||||
req.repetitions = 5;
|
||||
req.timeout = 2000;
|
||||
m_pingsSent = req.repetitions;
|
||||
ThePinger->addRequest(req);
|
||||
DEBUG_LOG(("DisconnectManager::update() - requesting %d pings of %d from %s\n",
|
||||
req.repetitions, req.timeout, req.hostname.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the ping thread, tracking pings if we are on the same frame
|
||||
if (ThePinger)
|
||||
{
|
||||
PingResponse resp;
|
||||
while (ThePinger->getResponse(resp))
|
||||
{
|
||||
if (m_pingFrame != TheGameLogic->getFrame())
|
||||
{
|
||||
// wrong frame - we're not pinging yet
|
||||
DEBUG_LOG(("DisconnectManager::update() - discarding ping of %d from %s (%d reps)\n",
|
||||
resp.avgPing, resp.hostname.c_str(), resp.repetitions));
|
||||
}
|
||||
else
|
||||
{
|
||||
// right frame
|
||||
DEBUG_LOG(("DisconnectManager::update() - keeping ping of %d from %s (%d reps)\n",
|
||||
resp.avgPing, resp.hostname.c_str(), resp.repetitions));
|
||||
if (resp.avgPing < 2000)
|
||||
{
|
||||
m_pingsRecieved += resp.repetitions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnsignedInt DisconnectManager::getPingFrame()
|
||||
{
|
||||
return m_pingFrame;
|
||||
}
|
||||
|
||||
Int DisconnectManager::getPingsSent()
|
||||
{
|
||||
return m_pingsSent;
|
||||
}
|
||||
|
||||
Int DisconnectManager::getPingsRecieved()
|
||||
{
|
||||
return m_pingsRecieved;
|
||||
}
|
||||
|
||||
|
||||
void DisconnectManager::updateDisconnectStatus(ConnectionManager *conMgr) {
|
||||
for (Int i = 0; i < MAX_SLOTS; ++i) {
|
||||
if (conMgr->isPlayerConnected(i)) {
|
||||
Int slot = translatedSlotPosition(i, conMgr->getLocalPlayerID());
|
||||
if (slot != -1) {
|
||||
time_t curTime = timeGetTime();
|
||||
time_t newTime = TheGlobalData->m_networkPlayerTimeoutTime - (curTime - m_playerTimeouts[slot]);
|
||||
|
||||
// if someone is more than 2/3 timed out, lets get our frame numbers sync'd up. Also if someone is voted out
|
||||
// lets do the same thing.
|
||||
|
||||
if (m_haveNotifiedOtherPlayersOfCurrentFrame == FALSE) {
|
||||
if ((newTime < TheGlobalData->m_networkPlayerTimeoutTime / 3) || (isPlayerVotedOut(slot, conMgr) == TRUE)) {
|
||||
TheNetwork->notifyOthersOfCurrentFrame();
|
||||
m_haveNotifiedOtherPlayersOfCurrentFrame = TRUE;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - curTime = %d, m_timeOfDisconnectScreenOn = %d, curTime - m_timeOfDisconnectScreenOn = %d\n", curTime, m_timeOfDisconnectScreenOn, curTime - m_timeOfDisconnectScreenOn));
|
||||
|
||||
if (m_timeOfDisconnectScreenOn != 0) {
|
||||
if ((curTime - m_timeOfDisconnectScreenOn) > TheGlobalData->m_networkDisconnectScreenNotifyTime) {
|
||||
TheNetwork->notifyOthersOfCurrentFrame();
|
||||
m_haveNotifiedOtherPlayersOfCurrentFrame = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((newTime < 0) || (isPlayerVotedOut(slot, conMgr) == TRUE)) {
|
||||
newTime = 0;
|
||||
DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - player %d(translated slot %d) has been voted out or timed out\n", i, slot));
|
||||
if (allOnSameFrame(conMgr) == TRUE) {
|
||||
DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - all on same frame\n"));
|
||||
if (isLocalPlayerNextPacketRouter(conMgr) == TRUE) {
|
||||
DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - local player is next packet router\n"));
|
||||
DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - about to do the disconnect procedure for player %d\n", i));
|
||||
sendDisconnectCommand(i, conMgr);
|
||||
disconnectPlayer(i, conMgr);
|
||||
sendPlayerDestruct(i, conMgr);
|
||||
} else {
|
||||
DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - local player is not the next packet router\n"));
|
||||
}
|
||||
} else {
|
||||
DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - not all on same frame\n"));
|
||||
}
|
||||
}
|
||||
TheDisconnectMenu->setPlayerTimeoutTime(slot, newTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::updateWaitForPacketRouter(ConnectionManager *conMgr) {
|
||||
/*
|
||||
time_t curTime = timeGetTime();
|
||||
time_t newTime = TheGlobalData->m_networkPlayerTimeoutTime - (curTime - m_packetRouterTimeout);
|
||||
if (newTime < 0) {
|
||||
newTime = 0;
|
||||
|
||||
// The guy that we were hoping would be the new packet router isn't. We're screwed, get out of the game.
|
||||
|
||||
DEBUG_LOG(("DisconnectManager::updateWaitForPacketRouter - timed out waiting for new packet router, quitting game\n"));
|
||||
TheNetwork->quitGame();
|
||||
}
|
||||
TheDisconnectMenu->setPacketRouterTimeoutTime(newTime);
|
||||
*/
|
||||
}
|
||||
|
||||
void DisconnectManager::processDisconnectCommand(NetCommandRef *ref, ConnectionManager *conMgr) {
|
||||
NetCommandMsg *msg = ref->getCommand();
|
||||
if (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTKEEPALIVE) {
|
||||
processDisconnectKeepAlive(msg, conMgr);
|
||||
} else if (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTPLAYER) {
|
||||
processDisconnectPlayer(msg, conMgr);
|
||||
} else if (msg->getNetCommandType() == NETCOMMANDTYPE_PACKETROUTERQUERY) {
|
||||
processPacketRouterQuery(msg, conMgr);
|
||||
} else if (msg->getNetCommandType() == NETCOMMANDTYPE_PACKETROUTERACK) {
|
||||
processPacketRouterAck(msg, conMgr);
|
||||
} else if (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTVOTE) {
|
||||
processDisconnectVote(msg, conMgr);
|
||||
} else if (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTFRAME) {
|
||||
processDisconnectFrame(msg, conMgr);
|
||||
} else if (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTSCREENOFF) {
|
||||
processDisconnectScreenOff(msg, conMgr);
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::processDisconnectKeepAlive(NetCommandMsg *msg, ConnectionManager *conMgr) {
|
||||
NetDisconnectKeepAliveCommandMsg *cmdMsg = (NetDisconnectKeepAliveCommandMsg *)msg;
|
||||
Int slot = translatedSlotPosition(cmdMsg->getPlayerID(), conMgr->getLocalPlayerID());
|
||||
if (slot != -1) {
|
||||
resetPlayerTimeout(slot);
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::processDisconnectPlayer(NetCommandMsg *msg, ConnectionManager *conMgr) {
|
||||
NetDisconnectPlayerCommandMsg *cmdMsg = (NetDisconnectPlayerCommandMsg *)msg;
|
||||
DEBUG_LOG(("DisconnectManager::processDisconnectPlayer - Got disconnect player command from player %d. Disconnecting player %d on frame %d\n", msg->getPlayerID(), cmdMsg->getDisconnectSlot(), cmdMsg->getDisconnectFrame()));
|
||||
DEBUG_ASSERTCRASH(TheGameLogic->getFrame() == cmdMsg->getDisconnectFrame(), ("disconnecting player on the wrong frame!!!"));
|
||||
disconnectPlayer(cmdMsg->getDisconnectSlot(), conMgr);
|
||||
}
|
||||
|
||||
void DisconnectManager::processPacketRouterQuery(NetCommandMsg *msg, ConnectionManager *conMgr) {
|
||||
NetPacketRouterQueryCommandMsg *cmdMsg = (NetPacketRouterQueryCommandMsg *)msg;
|
||||
DEBUG_LOG(("DisconnectManager::processPacketRouterQuery - got a packet router query command from player %d\n", msg->getPlayerID()));
|
||||
|
||||
if (conMgr->getPacketRouterSlot() == conMgr->getLocalPlayerID()) {
|
||||
NetPacketRouterAckCommandMsg *ackmsg = newInstance(NetPacketRouterAckCommandMsg);
|
||||
ackmsg->setPlayerID(conMgr->getLocalPlayerID());
|
||||
if (DoesCommandRequireACommandID(ackmsg->getNetCommandType()) == TRUE) {
|
||||
ackmsg->setID(GenerateNextCommandID());
|
||||
}
|
||||
DEBUG_LOG(("DisconnectManager::processPacketRouterQuery - We are the new packet router, responding with an packet router ack. Local player is %d\n", ackmsg->getPlayerID()));
|
||||
conMgr->sendLocalCommandDirect(ackmsg, 1 << cmdMsg->getPlayerID());
|
||||
ackmsg->detach();
|
||||
} else {
|
||||
DEBUG_LOG(("DisconnectManager::processPacketRouterQuery - We are NOT the new packet router, these are not the droids you're looking for.\n"));
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::processPacketRouterAck(NetCommandMsg *msg, ConnectionManager *conMgr) {
|
||||
NetPacketRouterAckCommandMsg *cmdMsg = (NetPacketRouterAckCommandMsg *)msg;
|
||||
DEBUG_LOG(("DisconnectManager::processPacketRouterAck - got packet router ack command from player %d\n", msg->getPlayerID()));
|
||||
|
||||
if (conMgr->getPacketRouterSlot() == cmdMsg->getPlayerID()) {
|
||||
DEBUG_LOG(("DisconnectManager::processPacketRouterAck - packet router command is from who it should be.\n"));
|
||||
resetPacketRouterTimeout();
|
||||
Int currentPacketRouterSlot = conMgr->getPacketRouterSlot();
|
||||
Int currentPacketRouterIndex = 0;
|
||||
while ((currentPacketRouterSlot != conMgr->getPacketRouterFallbackSlot(currentPacketRouterIndex)) && (currentPacketRouterIndex < MAX_SLOTS)) {
|
||||
++currentPacketRouterIndex;
|
||||
}
|
||||
DEBUG_ASSERTCRASH((currentPacketRouterIndex < MAX_SLOTS), ("Invalid packet router index"));
|
||||
|
||||
DEBUG_LOG(("DisconnectManager::processPacketRouterAck - New packet router confirmed, resending pending commands\n"));
|
||||
conMgr->resendPendingCommands();
|
||||
m_currentPacketRouterIndex = currentPacketRouterIndex;
|
||||
DEBUG_LOG(("DisconnectManager::processPacketRouterAck - Setting disconnect state to screen on.\n"));
|
||||
m_disconnectState = DISCONNECTSTATETYPE_SCREENON; ///< set it to screen on so that the next call to AllCommandsReady can set up everything for the next frame properly.
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::processDisconnectVote(NetCommandMsg *msg, ConnectionManager *conMgr) {
|
||||
NetDisconnectVoteCommandMsg *cmdMsg = (NetDisconnectVoteCommandMsg *)msg;
|
||||
DEBUG_LOG(("DisconnectManager::processDisconnectVote - Got a disconnect vote for player %d command from player %d\n", cmdMsg->getSlot(), cmdMsg->getPlayerID()));
|
||||
Int transSlot = translatedSlotPosition(msg->getPlayerID(), conMgr->getLocalPlayerID());
|
||||
|
||||
if (isPlayerInGame(transSlot, conMgr) == FALSE) {
|
||||
// if they've been timed out, voted out, disconnected, don't count their vote.
|
||||
return;
|
||||
}
|
||||
|
||||
applyDisconnectVote(cmdMsg->getSlot(), cmdMsg->getVoteFrame(), cmdMsg->getPlayerID(), conMgr);
|
||||
}
|
||||
|
||||
void DisconnectManager::processDisconnectFrame(NetCommandMsg *msg, ConnectionManager *conMgr) {
|
||||
NetDisconnectFrameCommandMsg *cmdMsg = (NetDisconnectFrameCommandMsg *)msg;
|
||||
UnsignedInt playerID = cmdMsg->getPlayerID();
|
||||
if (m_disconnectFrames[playerID] >= cmdMsg->getDisconnectFrame()) {
|
||||
// this message isn't valid, we have a disconnect frame that is later than this already.
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_disconnectFramesReceived[playerID] == TRUE) {
|
||||
DEBUG_LOG(("DisconnectManager::processDisconnectFrame - Got two disconnect frames without an intervening disconnect screen off command from player %d. Frames are %d and %d\n", playerID, m_disconnectFrames[playerID], cmdMsg->getDisconnectFrame()));
|
||||
}
|
||||
|
||||
DEBUG_LOG(("DisconnectManager::processDisconnectFrame - about to call resetPlayersVotes for player %d\n", playerID));
|
||||
resetPlayersVotes(playerID, cmdMsg->getDisconnectFrame()-1, conMgr);
|
||||
|
||||
m_disconnectFrames[playerID] = cmdMsg->getDisconnectFrame();
|
||||
m_disconnectFramesReceived[playerID] = TRUE;
|
||||
DEBUG_LOG(("DisconnectManager::processDisconnectFrame - Got a disconnect frame for player %d, frame = %d, local player is %d, local disconnect frame = %d, command id = %d\n", cmdMsg->getPlayerID(), cmdMsg->getDisconnectFrame(), conMgr->getLocalPlayerID(), m_disconnectFrames[conMgr->getLocalPlayerID()], cmdMsg->getID()));
|
||||
|
||||
if (playerID == conMgr->getLocalPlayerID()) {
|
||||
DEBUG_LOG(("DisconnectManager::processDisconnectFrame - player %d is the local player\n", playerID));
|
||||
// we just got the message from the local player, check to see if we need to send
|
||||
// commands to anyone we already have heard from.
|
||||
for (Int i = 0; i < MAX_SLOTS; ++i) {
|
||||
if (i != playerID) {
|
||||
Int transSlot = translatedSlotPosition(i, conMgr->getLocalPlayerID());
|
||||
if (isPlayerInGame(transSlot, conMgr) == TRUE) {
|
||||
if ((m_disconnectFrames[i] < m_disconnectFrames[playerID]) && (m_disconnectFramesReceived[i] == TRUE)) {
|
||||
DEBUG_LOG(("DisconnectManager::processDisconnectFrame - I have more frames than player %d, my frame = %d, their frame = %d\n", i, m_disconnectFrames[conMgr->getLocalPlayerID()], m_disconnectFrames[i]));
|
||||
conMgr->sendFrameDataToPlayer(i, m_disconnectFrames[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((m_disconnectFrames[playerID] < m_disconnectFrames[conMgr->getLocalPlayerID()]) && (m_disconnectFramesReceived[playerID] == TRUE)) {
|
||||
DEBUG_LOG(("DisconnectManager::processDisconnectFrame - I have more frames than player %d, my frame = %d, their frame = %d\n", playerID, m_disconnectFrames[conMgr->getLocalPlayerID()], m_disconnectFrames[playerID]));
|
||||
conMgr->sendFrameDataToPlayer(playerID, m_disconnectFrames[playerID]);
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::processDisconnectScreenOff(NetCommandMsg *msg, ConnectionManager *conMgr) {
|
||||
NetDisconnectScreenOffCommandMsg *cmdMsg = (NetDisconnectScreenOffCommandMsg *)msg;
|
||||
UnsignedInt playerID = cmdMsg->getPlayerID();
|
||||
|
||||
DEBUG_LOG(("DisconnectManager::processDisconnectScreenOff - got a screen off command from player %d for frame %d\n", cmdMsg->getPlayerID(), cmdMsg->getNewFrame()));
|
||||
|
||||
if ((playerID < 0) || (playerID >= MAX_SLOTS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UnsignedInt newFrame = cmdMsg->getNewFrame();
|
||||
if (newFrame >= m_disconnectFrames[playerID]) {
|
||||
DEBUG_LOG(("DisconnectManager::processDisconnectScreenOff - resetting the disconnect screen status for player %d\n", playerID));
|
||||
m_disconnectFramesReceived[playerID] = FALSE;
|
||||
m_disconnectFrames[playerID] = newFrame; // just in case we get packets out of order and the disconnect screen off message gets here before the disconnect frame message.
|
||||
|
||||
DEBUG_LOG(("DisconnectManager::processDisconnectScreenOff - about to call resetPlayersVotes for player %d\n", playerID));
|
||||
resetPlayersVotes(playerID, cmdMsg->getNewFrame(), conMgr);
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::applyDisconnectVote(Int slot, UnsignedInt frame, Int fromSlot, ConnectionManager *conMgr) {
|
||||
m_playerVotes[slot][fromSlot].vote = TRUE;
|
||||
m_playerVotes[slot][fromSlot].frame = frame;
|
||||
Int numVotes = countVotesForPlayer(slot);
|
||||
DEBUG_LOG(("DisconnectManager::applyDisconnectVote - added a vote to disconnect slot %d, from slot %d, for frame %d, current votes are %d\n", slot, fromSlot, frame, numVotes));
|
||||
Int transSlot = translatedSlotPosition(slot, conMgr->getLocalPlayerID());
|
||||
if (transSlot != -1) {
|
||||
TheDisconnectMenu->updateVotes(transSlot, numVotes);
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::nextFrame(UnsignedInt frame, ConnectionManager *conMgr) {
|
||||
m_lastFrame = frame;
|
||||
m_lastFrameTime = timeGetTime();
|
||||
resetPlayerTimeouts(conMgr);
|
||||
}
|
||||
|
||||
void DisconnectManager::allCommandsReady(UnsignedInt frame, ConnectionManager *conMgr, Bool waitForPacketRouter) {
|
||||
if (m_disconnectState != DISCONNECTSTATETYPE_SCREENOFF) {
|
||||
DEBUG_LOG(("DisconnectManager::allCommandsReady - setting screen state to off.\n"));
|
||||
|
||||
TheDisconnectMenu->hideScreen();
|
||||
m_disconnectState = DISCONNECTSTATETYPE_SCREENOFF;
|
||||
TheNetwork->notifyOthersOfNewFrame(frame);
|
||||
|
||||
// reset the votes since we're moving to a new frame.
|
||||
for (Int i = 0; i < MAX_SLOTS; ++i) {
|
||||
m_playerVotes[i][conMgr->getLocalPlayerID()].vote = FALSE;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("DisconnectManager::allCommandsReady - resetting m_timeOfDisconnectScreenOn\n"));
|
||||
m_timeOfDisconnectScreenOn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Bool DisconnectManager::allowedToContinue() {
|
||||
if (m_disconnectState != DISCONNECTSTATETYPE_SCREENOFF) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void DisconnectManager::sendKeepAlive(ConnectionManager *conMgr) {
|
||||
time_t curTime = timeGetTime();
|
||||
|
||||
if (((curTime - m_lastKeepAliveSendTime) > 500) || (m_lastKeepAliveSendTime == -1)) {
|
||||
NetDisconnectKeepAliveCommandMsg *msg = newInstance(NetDisconnectKeepAliveCommandMsg);
|
||||
msg->setPlayerID(conMgr->getLocalPlayerID());
|
||||
if (DoesCommandRequireACommandID(msg->getNetCommandType()) == TRUE) {
|
||||
msg->setID(GenerateNextCommandID());
|
||||
}
|
||||
conMgr->sendLocalCommandDirect(msg, 0xff ^ (1 << msg->getPlayerID()));
|
||||
msg->detach();
|
||||
|
||||
m_lastKeepAliveSendTime = curTime;
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::populateDisconnectScreen(ConnectionManager *conMgr) {
|
||||
for (Int i = 0; i < MAX_SLOTS; ++i) {
|
||||
UnicodeString name = conMgr->getPlayerName(i);
|
||||
Int slot = translatedSlotPosition(i, conMgr->getLocalPlayerID());
|
||||
if (slot != -1) {
|
||||
TheDisconnectMenu->setPlayerName(slot, name);
|
||||
|
||||
Int numVotes = countVotesForPlayer(i);
|
||||
TheDisconnectMenu->updateVotes(slot, numVotes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Int DisconnectManager::translatedSlotPosition(Int slot, Int localSlot) {
|
||||
if (slot < localSlot) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
if (slot == localSlot) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (slot - 1);
|
||||
}
|
||||
|
||||
Int DisconnectManager::untranslatedSlotPosition(Int slot, Int localSlot) {
|
||||
if (slot == -1) {
|
||||
return localSlot;
|
||||
}
|
||||
|
||||
if (slot < localSlot) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
return (slot + 1);
|
||||
}
|
||||
|
||||
void DisconnectManager::resetPlayerTimeouts(ConnectionManager *conMgr) {
|
||||
// reset the player timeouts.
|
||||
for (Int i = 0; i < MAX_SLOTS; ++i) {
|
||||
Int slot = translatedSlotPosition(i, conMgr->getLocalPlayerID());
|
||||
if (slot != -1) {
|
||||
resetPlayerTimeout(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::resetPlayerTimeout(Int slot) {
|
||||
m_playerTimeouts[slot] = timeGetTime();
|
||||
}
|
||||
|
||||
void DisconnectManager::resetPacketRouterTimeout() {
|
||||
m_packetRouterTimeout = timeGetTime();
|
||||
}
|
||||
|
||||
void DisconnectManager::turnOnScreen(ConnectionManager *conMgr) {
|
||||
TheDisconnectMenu->showScreen();
|
||||
DEBUG_LOG(("DisconnectManager::turnOnScreen - turning on screen on frame %d\n", TheGameLogic->getFrame()));
|
||||
m_disconnectState = DISCONNECTSTATETYPE_SCREENON;
|
||||
m_lastKeepAliveSendTime = -1;
|
||||
populateDisconnectScreen(conMgr);
|
||||
resetPlayerTimeouts(conMgr);
|
||||
TheDisconnectMenu->hidePacketRouterTimeout();
|
||||
|
||||
m_haveNotifiedOtherPlayersOfCurrentFrame = FALSE;
|
||||
|
||||
m_timeOfDisconnectScreenOn = timeGetTime();
|
||||
DEBUG_LOG(("DisconnectManager::turnOnScreen - turned on screen at time %d\n", m_timeOfDisconnectScreenOn));
|
||||
}
|
||||
|
||||
void DisconnectManager::disconnectPlayer(Int slot, ConnectionManager *conMgr) {
|
||||
DEBUG_LOG(("DisconnectManager::disconnectPlayer - Disconnecting slot number %d on frame %d\n", slot, TheGameLogic->getFrame()));
|
||||
DEBUG_ASSERTCRASH((slot >= 0) && (slot < MAX_SLOTS), ("Attempting to disconnect an invalid slot number"));
|
||||
if ((slot < 0) || (slot >= (MAX_SLOTS))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (TheGameInfo)
|
||||
{
|
||||
GameSlot *gSlot = TheGameInfo->getSlot( slot );
|
||||
if (gSlot)
|
||||
{
|
||||
gSlot->markAsDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
Int transSlot = translatedSlotPosition(slot, conMgr->getLocalPlayerID());
|
||||
|
||||
if (transSlot != -1) {
|
||||
// Ignore any disconnect commands that tell us to disconnect ourselves.
|
||||
|
||||
// Get the disconnecting player off the disconnect window.
|
||||
UnicodeString uname = conMgr->getPlayerName(slot);
|
||||
TheRecorder->logPlayerDisconnect(uname, slot);
|
||||
TheDisconnectMenu->removePlayer(transSlot, uname);
|
||||
|
||||
PlayerLeaveCode retcode = conMgr->disconnectPlayer(slot);
|
||||
DEBUG_ASSERTCRASH((retcode != PLAYERLEAVECODE_UNKNOWN), ("Invalid player leave code"));
|
||||
|
||||
if (retcode == PLAYERLEAVECODE_PACKETROUTER) {
|
||||
DEBUG_LOG(("DisconnectManager::disconnectPlayer - disconnecting player was packet router.\n"));
|
||||
|
||||
conMgr->resendPendingCommands();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::sendDisconnectCommand(Int slot, ConnectionManager *conMgr) {
|
||||
DEBUG_LOG(("DisconnectManager::sendDisconnectCommand - Sending disconnect command for slot number %d\n", slot));
|
||||
DEBUG_ASSERTCRASH((slot >= 0) && (slot < MAX_SLOTS), ("Attempting to send a disconnect command for an invalid slot number"));
|
||||
if ((slot < 0) || (slot >= (MAX_SLOTS))) {
|
||||
return;
|
||||
}
|
||||
|
||||
UnsignedInt disconnectFrame = getMaxDisconnectFrame();
|
||||
|
||||
// Need to do the NetDisconnectPlayerCommandMsg creation and sending here.
|
||||
NetDisconnectPlayerCommandMsg *msg = newInstance(NetDisconnectPlayerCommandMsg);
|
||||
msg->setDisconnectSlot(slot);
|
||||
msg->setDisconnectFrame(disconnectFrame);
|
||||
msg->setPlayerID(conMgr->getLocalPlayerID());
|
||||
if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
|
||||
msg->setID(GenerateNextCommandID());
|
||||
}
|
||||
|
||||
conMgr->sendLocalCommand(msg);
|
||||
|
||||
DEBUG_LOG(("DisconnectManager::sendDisconnectCommand - Sending disconnect command for slot number %d for frame %d\n", slot, disconnectFrame));
|
||||
|
||||
msg->detach();
|
||||
}
|
||||
|
||||
void DisconnectManager::sendVoteCommand(Int slot, ConnectionManager *conMgr) {
|
||||
NetDisconnectVoteCommandMsg *msg = newInstance(NetDisconnectVoteCommandMsg);
|
||||
|
||||
msg->setPlayerID(conMgr->getLocalPlayerID());
|
||||
msg->setSlot(slot);
|
||||
msg->setVoteFrame(TheGameLogic->getFrame());
|
||||
if (DoesCommandRequireACommandID(msg->getNetCommandType()) == TRUE) {
|
||||
msg->setID(GenerateNextCommandID());
|
||||
}
|
||||
|
||||
conMgr->sendLocalCommandDirect(msg, 0xff & ~(1 << conMgr->getLocalPlayerID()));
|
||||
|
||||
msg->detach();
|
||||
}
|
||||
|
||||
void DisconnectManager::voteForPlayerDisconnect(Int slot, ConnectionManager *conMgr) {
|
||||
Int transSlot = untranslatedSlotPosition(slot, conMgr->getLocalPlayerID());
|
||||
|
||||
if (m_playerVotes[transSlot][conMgr->getLocalPlayerID()].vote == FALSE) {
|
||||
m_playerVotes[transSlot][conMgr->getLocalPlayerID()].vote = TRUE;
|
||||
|
||||
sendVoteCommand(transSlot, conMgr);
|
||||
|
||||
// we use the game logic frame cause we might not have sent out our own disconnect frame yet.
|
||||
applyDisconnectVote(transSlot, TheGameLogic->getFrame(), conMgr->getLocalPlayerID(), conMgr);
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectManager::recalculatePacketRouterIndex(ConnectionManager *conMgr) {
|
||||
Int currentPacketRouterSlot = conMgr->getPacketRouterSlot();
|
||||
m_currentPacketRouterIndex = 0;
|
||||
while ((currentPacketRouterSlot != conMgr->getPacketRouterFallbackSlot(m_currentPacketRouterIndex)) && (m_currentPacketRouterIndex < MAX_SLOTS)) {
|
||||
++m_currentPacketRouterIndex;
|
||||
}
|
||||
DEBUG_ASSERTCRASH((m_currentPacketRouterIndex < MAX_SLOTS), ("Invalid packet router index"));
|
||||
}
|
||||
|
||||
Bool DisconnectManager::allOnSameFrame(ConnectionManager *conMgr) {
|
||||
Bool retval = TRUE;
|
||||
for (Int i = 0; (i < MAX_SLOTS) && (retval == TRUE); ++i) {
|
||||
Int transSlot = translatedSlotPosition(i, conMgr->getLocalPlayerID());
|
||||
if (transSlot == -1) {
|
||||
continue;
|
||||
}
|
||||
if ((conMgr->isPlayerConnected(i) == TRUE) && (isPlayerInGame(transSlot, conMgr) == TRUE)) {
|
||||
// ok, i is someone who is in the game and hasn't timed out yet or been voted out.
|
||||
if (m_disconnectFramesReceived[i] == FALSE) {
|
||||
// we don't know what frame they are on yet.
|
||||
retval = FALSE;
|
||||
}
|
||||
if ((m_disconnectFramesReceived[i] == TRUE) && (m_disconnectFrames[conMgr->getLocalPlayerID()] != m_disconnectFrames[i])) {
|
||||
// We know their frame, but they aren't on the same frame as us.
|
||||
retval = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
Bool DisconnectManager::isLocalPlayerNextPacketRouter(ConnectionManager *conMgr) {
|
||||
UnsignedInt localSlot = conMgr->getLocalPlayerID();
|
||||
UnsignedInt packetRouterSlot = conMgr->getPacketRouterSlot();
|
||||
Int transSlot = translatedSlotPosition(packetRouterSlot, localSlot);
|
||||
|
||||
// stop when we have found a packet router that is connected
|
||||
while ((transSlot != -1) && (isPlayerInGame(transSlot, conMgr) == FALSE)) {
|
||||
packetRouterSlot = conMgr->getNextPacketRouterSlot(packetRouterSlot);
|
||||
if ((packetRouterSlot >= MAX_SLOTS) || (packetRouterSlot < 0)) {
|
||||
// don't know who the next packet router is going to be,
|
||||
// so this game is not going to go anywhere anymore.
|
||||
DEBUG_CRASH(("no more players left to be the packet router, this shouldn't happen."));
|
||||
return FALSE;
|
||||
}
|
||||
transSlot = translatedSlotPosition(packetRouterSlot, localSlot);
|
||||
}
|
||||
|
||||
if (packetRouterSlot == localSlot) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Bool DisconnectManager::hasPlayerTimedOut(Int slot) {
|
||||
if (slot == -1) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
time_t newTime = TheGlobalData->m_networkPlayerTimeoutTime - (timeGetTime() - m_playerTimeouts[slot]);
|
||||
if (newTime <= 0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// this function assumes that we are the packet router. (or at least that
|
||||
// we will be after everyone is getting disconnected)
|
||||
void DisconnectManager::sendPlayerDestruct(Int slot, ConnectionManager *conMgr) {
|
||||
UnsignedShort currentID = 0;
|
||||
if (DoesCommandRequireACommandID(NETCOMMANDTYPE_DESTROYPLAYER))
|
||||
{
|
||||
currentID = GenerateNextCommandID();
|
||||
}
|
||||
|
||||
DEBUG_LOG(("Queueing DestroyPlayer %d for frame %d on frame %d as command %d\n",
|
||||
slot, TheNetwork->getExecutionFrame()+1, TheGameLogic->getFrame(), currentID));
|
||||
|
||||
NetDestroyPlayerCommandMsg *netmsg = newInstance(NetDestroyPlayerCommandMsg);
|
||||
netmsg->setExecutionFrame(TheNetwork->getExecutionFrame()+1);
|
||||
netmsg->setPlayerID(conMgr->getLocalPlayerID());
|
||||
netmsg->setID(currentID);
|
||||
netmsg->setPlayerIndex(slot);
|
||||
conMgr->sendLocalCommand(netmsg);
|
||||
netmsg->detach();
|
||||
}
|
||||
|
||||
// the 'slot' variable is supposed to be a translated slot position. (translated slot meaning
|
||||
// that it is the player's position in the disconnect menu)
|
||||
Bool DisconnectManager::isPlayerVotedOut(Int slot, ConnectionManager *conMgr) {
|
||||
if (slot == -1) {
|
||||
// we can't vote out ourselves.
|
||||
return FALSE;
|
||||
}
|
||||
Int transSlot = untranslatedSlotPosition(slot, conMgr->getLocalPlayerID());
|
||||
Int numVotes = countVotesForPlayer(transSlot);
|
||||
if (numVotes >= (conMgr->getNumPlayers() - 1)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
UnsignedInt DisconnectManager::getMaxDisconnectFrame() {
|
||||
UnsignedInt retval = 0;
|
||||
for (Int i = 0; i < MAX_SLOTS; ++i) {
|
||||
if (m_disconnectFrames[i] > retval) {
|
||||
retval = m_disconnectFrames[i];
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
Bool DisconnectManager::isPlayerInGame(Int slot, ConnectionManager *conMgr) {
|
||||
Int transSlot = untranslatedSlotPosition(slot, conMgr->getLocalPlayerID());
|
||||
DEBUG_ASSERTCRASH((transSlot >= 0) && (transSlot < MAX_SLOTS), ("invalid slot number"));
|
||||
if (((transSlot < 0) || (transSlot >= MAX_SLOTS)) || conMgr->isPlayerConnected(transSlot) == FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (isPlayerVotedOut(slot, conMgr) == TRUE) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (hasPlayerTimedOut(slot) == TRUE) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void DisconnectManager::playerHasAdvancedAFrame(Int slot, UnsignedInt frame) {
|
||||
// if they have advanced beyond the frame they had been previously disconnecting on.
|
||||
if (frame >= m_disconnectFrames[slot]) {
|
||||
m_disconnectFrames[slot] = frame; // just in case we get a disconnect frame command after this is called.
|
||||
m_disconnectFramesReceived[slot] = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
Int DisconnectManager::countVotesForPlayer(Int slot) {
|
||||
if ((slot < 0) || (slot >= MAX_SLOTS)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Int retval = 0;
|
||||
for (Int i = 0; i < MAX_SLOTS; ++i) {
|
||||
// using TheGameLogic->getFrame() cause we might not have sent our disconnect frame yet.
|
||||
if ((m_playerVotes[slot][i].vote == TRUE) && (m_playerVotes[slot][i].frame == TheGameLogic->getFrame())) {
|
||||
++retval;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void DisconnectManager::resetPlayersVotes(Int playerID, UnsignedInt frame, ConnectionManager *conMgr) {
|
||||
DEBUG_LOG(("DisconnectManager::resetPlayersVotes - resetting player %d's votes on frame %d\n", playerID, frame));
|
||||
|
||||
// we need to reset this player's votes that happened before or on the given frame.
|
||||
for(Int i = 0; i < MAX_SLOTS; ++i) {
|
||||
if (m_playerVotes[i][playerID].frame <= frame) {
|
||||
DEBUG_LOG(("DisconnectManager::resetPlayersVotes - resetting player %d's vote for player %d from frame %d on frame %d\n", playerID, i, m_playerVotes[i][playerID].frame, frame));
|
||||
m_playerVotes[i][playerID].vote = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
Int numVotes = countVotesForPlayer(playerID);
|
||||
DEBUG_LOG(("DisconnectManager::resetPlayersVotes - after adjusting votes, player %d has %d votes\n", playerID, numVotes));
|
||||
Int transSlot = translatedSlotPosition(playerID, conMgr->getLocalPlayerID());
|
||||
if (transSlot != -1) {
|
||||
TheDisconnectMenu->updateVotes(transSlot, numVotes);
|
||||
}
|
||||
}
|
||||
224
Generals/Code/GameEngine/Source/GameNetwork/DownloadManager.cpp
Normal file
224
Generals/Code/GameEngine/Source/GameNetwork/DownloadManager.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: DownloadManager.cpp //////////////////////////////////////////////////////
|
||||
// Generals download manager code
|
||||
// Author: Matthew D. Campbell, July 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameNetwork/DownloadManager.h"
|
||||
|
||||
DownloadManager *TheDownloadManager;
|
||||
|
||||
DownloadManager::DownloadManager()
|
||||
{
|
||||
m_download = NEW CDownload(this);
|
||||
m_wasError = m_sawEnd = false;
|
||||
|
||||
//Added By Sadullah Nader
|
||||
//Initializations missing and needed
|
||||
|
||||
m_queuedDownloads.clear();
|
||||
|
||||
//
|
||||
|
||||
m_statusString = TheGameText->fetch("FTP:StatusIdle");
|
||||
|
||||
// ----- Initialize Winsock -----
|
||||
m_winsockInit = true;
|
||||
WORD verReq = MAKEWORD(2, 2);
|
||||
WSADATA wsadata;
|
||||
|
||||
int err = WSAStartup(verReq, &wsadata);
|
||||
if (err != 0)
|
||||
{
|
||||
m_winsockInit = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((LOBYTE(wsadata.wVersion) != 2) || (HIBYTE(wsadata.wVersion) !=2))
|
||||
{
|
||||
WSACleanup();
|
||||
m_winsockInit = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DownloadManager::~DownloadManager()
|
||||
{
|
||||
delete m_download;
|
||||
if (m_winsockInit)
|
||||
{
|
||||
WSACleanup();
|
||||
m_winsockInit = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DownloadManager::init( void )
|
||||
{
|
||||
}
|
||||
|
||||
void DownloadManager::reset( void )
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT DownloadManager::update( void )
|
||||
{
|
||||
return m_download->PumpMessages();
|
||||
}
|
||||
|
||||
HRESULT DownloadManager::downloadFile( AsciiString server, AsciiString username, AsciiString password, AsciiString file, AsciiString localfile, AsciiString regkey, Bool tryResume )
|
||||
{
|
||||
return m_download->DownloadFile( server.str(), username.str(), password.str(), file.str(), localfile.str(), regkey.str(), tryResume );
|
||||
}
|
||||
|
||||
void DownloadManager::queueFileForDownload( AsciiString server, AsciiString username, AsciiString password, AsciiString file, AsciiString localfile, AsciiString regkey, Bool tryResume )
|
||||
{
|
||||
QueuedDownload q;
|
||||
q.file = file;
|
||||
q.localFile = localfile;
|
||||
q.password = password;
|
||||
q.regKey = regkey;
|
||||
q.server = server;
|
||||
q.tryResume = tryResume;
|
||||
q.userName = username;
|
||||
|
||||
m_queuedDownloads.push_back(q);
|
||||
}
|
||||
|
||||
HRESULT DownloadManager::downloadNextQueuedFile( void )
|
||||
{
|
||||
QueuedDownload q;
|
||||
std::list<QueuedDownload>::iterator it = m_queuedDownloads.begin();
|
||||
if (it != m_queuedDownloads.end())
|
||||
{
|
||||
q = *it;
|
||||
m_queuedDownloads.pop_front();
|
||||
m_wasError = m_sawEnd = false;
|
||||
return downloadFile( q.server, q.userName, q.password, q.file, q.localFile, q.regKey, q.tryResume );
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_CRASH(("Starting non-existent download!"));
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
AsciiString DownloadManager::getLastLocalFile( void )
|
||||
{
|
||||
char buf[256] = "";
|
||||
m_download->GetLastLocalFile(buf, 256);
|
||||
return buf;
|
||||
}
|
||||
|
||||
HRESULT DownloadManager::OnError( Int error )
|
||||
{
|
||||
m_wasError = true;
|
||||
AsciiString s = "FTP:UnknownError";
|
||||
switch (error)
|
||||
{
|
||||
case DOWNLOADEVENT_NOSUCHSERVER:
|
||||
s = "FTP:NoSuchServer";
|
||||
break;
|
||||
case DOWNLOADEVENT_COULDNOTCONNECT:
|
||||
s = "FTP:CouldNotConnect";
|
||||
break;
|
||||
case DOWNLOADEVENT_LOGINFAILED:
|
||||
s = "FTP:LoginFailed";
|
||||
break;
|
||||
case DOWNLOADEVENT_NOSUCHFILE:
|
||||
s = "FTP:NoSuchFile";
|
||||
break;
|
||||
case DOWNLOADEVENT_LOCALFILEOPENFAILED:
|
||||
s = "FTP:LocalFileOpenFailed";
|
||||
break;
|
||||
case DOWNLOADEVENT_TCPERROR:
|
||||
s = "FTP:TCPError";
|
||||
break;
|
||||
case DOWNLOADEVENT_DISCONNECTERROR:
|
||||
s = "FTP:DisconnectError";
|
||||
break;
|
||||
}
|
||||
m_errorString = TheGameText->fetch(s);
|
||||
DEBUG_LOG(("DownloadManager::OnError(): %s(%d)\n", s.str(), error));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT DownloadManager::OnEnd()
|
||||
{
|
||||
m_sawEnd = true;
|
||||
DEBUG_LOG(("DownloadManager::OnEnd()\n"));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT DownloadManager::OnQueryResume()
|
||||
{
|
||||
DEBUG_LOG(("DownloadManager::OnQueryResume()\n"));
|
||||
//return DOWNLOADEVENT_DONOTRESUME;
|
||||
return DOWNLOADEVENT_RESUME;
|
||||
}
|
||||
|
||||
HRESULT DownloadManager::OnProgressUpdate( Int bytesread, Int totalsize, Int timetaken, Int timeleft )
|
||||
{
|
||||
DEBUG_LOG(("DownloadManager::OnProgressUpdate(): %d/%d %d/%d\n", bytesread, totalsize, timetaken, timeleft));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT DownloadManager::OnStatusUpdate( Int status )
|
||||
{
|
||||
AsciiString s = "FTP:StatusNone";
|
||||
switch (status)
|
||||
{
|
||||
case DOWNLOADSTATUS_CONNECTING:
|
||||
s = "FTP:StatusConnecting";
|
||||
break;
|
||||
case DOWNLOADSTATUS_LOGGINGIN:
|
||||
s = "FTP:StatusLoggingIn";
|
||||
break;
|
||||
case DOWNLOADSTATUS_FINDINGFILE:
|
||||
s = "FTP:StatusFindingFile";
|
||||
break;
|
||||
case DOWNLOADSTATUS_QUERYINGRESUME:
|
||||
s = "FTP:StatusQueryingResume";
|
||||
break;
|
||||
case DOWNLOADSTATUS_DOWNLOADING:
|
||||
s = "FTP:StatusDownloading";
|
||||
break;
|
||||
case DOWNLOADSTATUS_DISCONNECTING:
|
||||
s = "FTP:StatusDisconnecting";
|
||||
break;
|
||||
case DOWNLOADSTATUS_FINISHING:
|
||||
s = "FTP:StatusFinishing";
|
||||
break;
|
||||
case DOWNLOADSTATUS_DONE:
|
||||
s = "FTP:StatusDone";
|
||||
break;
|
||||
}
|
||||
m_statusString = TheGameText->fetch(s);
|
||||
DEBUG_LOG(("DownloadManager::OnStatusUpdate(): %s(%d)\n", s.str(), status));
|
||||
return S_OK;
|
||||
}
|
||||
283
Generals/Code/GameEngine/Source/GameNetwork/FileTransfer.cpp
Normal file
283
Generals/Code/GameEngine/Source/GameNetwork/FileTransfer.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// FILE: FileTransfer.cpp
|
||||
// Author: Matthew D. Campbell, December 2002
|
||||
// Description: File Transfer wrapper using TheNetwork
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameClient/LoadScreen.h"
|
||||
#include "GameClient/Shell.h"
|
||||
#include "GameNetwork/FileTransfer.h"
|
||||
#include "GameNetwork/NetworkUtil.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
static Bool doFileTransfer( AsciiString filename, MapTransferLoadScreen *ls, Int mask )
|
||||
{
|
||||
Bool fileTransferDone = FALSE;
|
||||
Int fileTransferPercent = 0;
|
||||
Int i;
|
||||
|
||||
if (mask)
|
||||
{
|
||||
ls->setCurrentFilename(filename);
|
||||
UnsignedInt startTime = timeGetTime();
|
||||
const Int timeoutPeriod = 2*60*1000;
|
||||
ls->processTimeout(timeoutPeriod/1000);
|
||||
|
||||
ls->update(0);
|
||||
fileTransferDone = FALSE;
|
||||
fileTransferPercent = 0;
|
||||
|
||||
UnsignedShort fileCommandID = 0;
|
||||
Bool sentFile = FALSE;
|
||||
if (TheGameInfo->amIHost())
|
||||
{
|
||||
Sleep(500);
|
||||
fileCommandID = TheNetwork->sendFileAnnounce(filename, mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
sentFile = TRUE;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("Starting file transfer loop\n"));
|
||||
|
||||
while (!fileTransferDone)
|
||||
{
|
||||
if (!sentFile && TheNetwork->areAllQueuesEmpty())
|
||||
{
|
||||
TheNetwork->sendFile(filename, mask, fileCommandID);
|
||||
sentFile = TRUE;
|
||||
}
|
||||
|
||||
// get the progress for each player, and take the min for our overall progress
|
||||
fileTransferDone = TRUE;
|
||||
fileTransferPercent = 100;
|
||||
for (i=1; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
if (TheGameInfo->getConstSlot(i)->isHuman() && !TheGameInfo->getConstSlot(i)->hasMap())
|
||||
{
|
||||
Int slotTransferPercent = TheNetwork->getFileTransferProgress(i, filename);
|
||||
fileTransferPercent = min(fileTransferPercent, slotTransferPercent);
|
||||
|
||||
if (slotTransferPercent == 0)
|
||||
ls->processProgress(i, slotTransferPercent, "MapTransfer:Preparing");
|
||||
else if (slotTransferPercent < 100)
|
||||
ls->processProgress(i, slotTransferPercent, "MapTransfer:Recieving");
|
||||
else
|
||||
ls->processProgress(i, slotTransferPercent, "MapTransfer:Done");
|
||||
}
|
||||
}
|
||||
if (fileTransferPercent < 100)
|
||||
{
|
||||
fileTransferDone = FALSE;
|
||||
if (fileTransferPercent == 0)
|
||||
ls->processProgress(0, fileTransferPercent, "MapTransfer:Preparing");
|
||||
else
|
||||
ls->processProgress(0, fileTransferPercent, "MapTransfer:Sending");
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("File transfer is 100%%!\n"));
|
||||
ls->processProgress(0, fileTransferPercent, "MapTransfer:Done");
|
||||
}
|
||||
|
||||
Int now = timeGetTime();
|
||||
if (now > startTime + timeoutPeriod) // bail if we don't finish in a reasonable amount of time
|
||||
{
|
||||
DEBUG_LOG(("Timing out file transfer\n"));
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ls->processTimeout((startTime + timeoutPeriod - now)/1000);
|
||||
}
|
||||
|
||||
ls->update(fileTransferPercent);
|
||||
}
|
||||
|
||||
if (!fileTransferDone)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
AsciiString GetBasePathFromPath( AsciiString path )
|
||||
{
|
||||
const char *s = path.reverseFind('\\');
|
||||
if (s)
|
||||
{
|
||||
Int len = s - path.str();
|
||||
|
||||
AsciiString base;
|
||||
char *buf = base.getBufferForRead(len + 1);
|
||||
memcpy(buf, path.str(), len);
|
||||
buf[len] = 0;
|
||||
return buf;
|
||||
}
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
AsciiString GetFileFromPath( AsciiString path )
|
||||
{
|
||||
const char *s = path.reverseFind('\\');
|
||||
if (s)
|
||||
return s+1;
|
||||
return path;
|
||||
}
|
||||
|
||||
AsciiString GetExtensionFromFile( AsciiString fname )
|
||||
{
|
||||
const char *s = fname.reverseFind('.');
|
||||
if (s)
|
||||
return s+1;
|
||||
return fname;
|
||||
}
|
||||
|
||||
AsciiString GetBaseFileFromFile( AsciiString fname )
|
||||
{
|
||||
const char *s = fname.reverseFind('.');
|
||||
if (s)
|
||||
{
|
||||
Int len = s - fname.str();
|
||||
|
||||
AsciiString base;
|
||||
char *buf = base.getBufferForRead(len + 1);
|
||||
memcpy(buf, fname.str(), len);
|
||||
buf[len] = 0;
|
||||
return buf;
|
||||
}
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
AsciiString GetPreviewFromMap( AsciiString path )
|
||||
{
|
||||
AsciiString fname = GetBaseFileFromFile(GetFileFromPath(path));
|
||||
AsciiString base = GetBasePathFromPath(path);
|
||||
|
||||
AsciiString out;
|
||||
out.format("%s\\%s.tga", base.str(), fname.str());
|
||||
return out;
|
||||
}
|
||||
|
||||
AsciiString GetINIFromMap( AsciiString path )
|
||||
{
|
||||
AsciiString base = GetBasePathFromPath(path);
|
||||
|
||||
AsciiString out;
|
||||
out.format("%s\\map.ini", base.str());
|
||||
return out;
|
||||
}
|
||||
|
||||
AsciiString GetStrFileFromMap( AsciiString path )
|
||||
{
|
||||
AsciiString base = GetBasePathFromPath(path);
|
||||
|
||||
AsciiString out;
|
||||
out.format("%s\\map.str", base.str());
|
||||
return out;
|
||||
}
|
||||
|
||||
AsciiString GetSoloINIFromMap( AsciiString path )
|
||||
{
|
||||
AsciiString base = GetBasePathFromPath(path);
|
||||
|
||||
AsciiString out;
|
||||
out.format("%s\\solo.ini", base.str());
|
||||
return out;
|
||||
}
|
||||
|
||||
AsciiString GetAssetUsageFromMap( AsciiString path )
|
||||
{
|
||||
AsciiString base = GetBasePathFromPath(path);
|
||||
|
||||
AsciiString out;
|
||||
out.format("%s\\assetusage.txt", base.str());
|
||||
return out;
|
||||
}
|
||||
|
||||
AsciiString GetReadmeFromMap( AsciiString path )
|
||||
{
|
||||
AsciiString base = GetBasePathFromPath(path);
|
||||
|
||||
AsciiString out;
|
||||
out.format("%s\\readme.txt", base.str());
|
||||
return out;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
Bool DoAnyMapTransfers(GameInfo *game)
|
||||
{
|
||||
TheGameInfo = game;
|
||||
Int mask = 0;
|
||||
Int i=0;
|
||||
for (i=1; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
if (TheGameInfo->getConstSlot(i)->isHuman() && !TheGameInfo->getConstSlot(i)->hasMap())
|
||||
{
|
||||
DEBUG_LOG(("Adding player %d to transfer mask\n", i));
|
||||
mask |= (1<<i);
|
||||
}
|
||||
}
|
||||
if (!mask)
|
||||
return TRUE;
|
||||
|
||||
TheShell->hideShell();
|
||||
MapTransferLoadScreen *ls = NEW MapTransferLoadScreen;
|
||||
ls->init(TheGameInfo);
|
||||
Bool ok = TRUE;
|
||||
if (TheGameInfo->getMapContentsMask() & 2)
|
||||
ok = doFileTransfer(GetPreviewFromMap(game->getMap()), ls, mask);
|
||||
if (ok && TheGameInfo->getMapContentsMask() & 4)
|
||||
ok = doFileTransfer(GetINIFromMap(game->getMap()), ls, mask);
|
||||
if (ok && TheGameInfo->getMapContentsMask() & 8)
|
||||
ok = doFileTransfer(GetStrFileFromMap(game->getMap()), ls, mask);
|
||||
if (ok && TheGameInfo->getMapContentsMask() & 16)
|
||||
ok = doFileTransfer(GetSoloINIFromMap(game->getMap()), ls, mask);
|
||||
if (ok && TheGameInfo->getMapContentsMask() & 32)
|
||||
ok = doFileTransfer(GetAssetUsageFromMap(game->getMap()), ls, mask);
|
||||
if (ok && TheGameInfo->getMapContentsMask() & 64)
|
||||
ok = doFileTransfer(GetReadmeFromMap(game->getMap()), ls, mask);
|
||||
if (ok)
|
||||
ok = doFileTransfer(game->getMap(), ls, mask);
|
||||
delete ls;
|
||||
ls = NULL;
|
||||
if (!ok)
|
||||
TheShell->showShell();
|
||||
return ok;
|
||||
}
|
||||
1586
Generals/Code/GameEngine/Source/GameNetwork/FirewallHelper.cpp
Normal file
1586
Generals/Code/GameEngine/Source/GameNetwork/FirewallHelper.cpp
Normal file
File diff suppressed because it is too large
Load Diff
205
Generals/Code/GameEngine/Source/GameNetwork/FrameData.cpp
Normal file
205
Generals/Code/GameEngine/Source/GameNetwork/FrameData.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/FrameData.h"
|
||||
#include "GameNetwork/NetworkUtil.h"
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
FrameData::FrameData()
|
||||
{
|
||||
m_frame = 0;
|
||||
m_commandList = NULL;
|
||||
m_commandCount = 0;
|
||||
m_frameCommandCount = -1;
|
||||
//Added By Sadullah Nader
|
||||
//Initializations missing and needed
|
||||
m_lastFailedCC = 0;
|
||||
m_lastFailedFrameCC = 0;
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
FrameData::~FrameData()
|
||||
{
|
||||
if (m_commandList != NULL) {
|
||||
m_commandList->deleteInstance();
|
||||
m_commandList = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this thing.
|
||||
*/
|
||||
void FrameData::init()
|
||||
{
|
||||
m_frame = 0;
|
||||
if (m_commandList == NULL) {
|
||||
m_commandList = newInstance(NetCommandList);
|
||||
m_commandList->init();
|
||||
}
|
||||
m_commandList->reset();
|
||||
|
||||
m_frameCommandCount = -1;
|
||||
//DEBUG_LOG(("FrameData::init\n"));
|
||||
m_commandCount = 0;
|
||||
m_lastFailedCC = -2;
|
||||
m_lastFailedFrameCC = -2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset this thing.
|
||||
*/
|
||||
void FrameData::reset() {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* update the thing, doesn't do anything at the moment.
|
||||
*/
|
||||
void FrameData::update() {
|
||||
}
|
||||
|
||||
/**
|
||||
* return the frame number this frame data is associated with.
|
||||
*/
|
||||
UnsignedInt FrameData::getFrame() {
|
||||
return m_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the frame number this frame data is associated with.
|
||||
*/
|
||||
void FrameData::setFrame(UnsignedInt frame) {
|
||||
m_frame = frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all the frame command count is equal to the number of commands that have been received.
|
||||
*/
|
||||
FrameDataReturnType FrameData::allCommandsReady(Bool debugSpewage) {
|
||||
if (m_frameCommandCount == m_commandCount) {
|
||||
m_lastFailedFrameCC = -2;
|
||||
m_lastFailedCC = -2;
|
||||
return FRAMEDATA_READY;
|
||||
}
|
||||
|
||||
if (debugSpewage) {
|
||||
if ((m_lastFailedFrameCC != m_frameCommandCount) || (m_lastFailedCC != m_commandCount)) {
|
||||
DEBUG_LOG(("FrameData::allCommandsReady - failed, frame command count = %d, command count = %d\n", m_frameCommandCount, m_commandCount));
|
||||
m_lastFailedFrameCC = m_frameCommandCount;
|
||||
m_lastFailedCC = m_commandCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_commandCount > m_frameCommandCount) {
|
||||
DEBUG_LOG(("FrameData::allCommandsReady - There are more commands than there should be (%d, should be %d). Commands in command list are...\n", m_commandCount, m_frameCommandCount));
|
||||
NetCommandRef *ref = m_commandList->getFirstMessage();
|
||||
while (ref != NULL) {
|
||||
DEBUG_LOG(("%s, frame = %d, id = %d\n", GetAsciiNetCommandType(ref->getCommand()->getNetCommandType()).str(), ref->getCommand()->getExecutionFrame(), ref->getCommand()->getID()));
|
||||
ref = ref->getNext();
|
||||
}
|
||||
DEBUG_LOG(("FrameData::allCommandsReady - End of command list.\n"));
|
||||
DEBUG_LOG(("FrameData::allCommandsReady - about to clear the command list\n"));
|
||||
reset();
|
||||
DEBUG_LOG(("FrameData::allCommandsReady - command list cleared. command list length = %d, command count = %d, frame command count = %d\n", m_commandList->length(), m_commandCount, m_frameCommandCount));
|
||||
return FRAMEDATA_RESEND;
|
||||
}
|
||||
return FRAMEDATA_NOTREADY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the command count for this frame
|
||||
*/
|
||||
void FrameData::setFrameCommandCount(UnsignedInt frameCommandCount) {
|
||||
//DEBUG_LOG(("setFrameCommandCount to %d for frame %d\n", frameCommandCount, m_frame));
|
||||
m_frameCommandCount = frameCommandCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command count for this frame.
|
||||
*/
|
||||
UnsignedInt FrameData::getFrameCommandCount() {
|
||||
return m_frameCommandCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the number of commands received so far.
|
||||
*/
|
||||
UnsignedInt FrameData::getCommandCount() {
|
||||
return m_commandCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a command to this frame
|
||||
*/
|
||||
void FrameData::addCommand(NetCommandMsg *msg) {
|
||||
// need to add the message in order of command ID
|
||||
if (m_commandList == NULL) {
|
||||
init();
|
||||
}
|
||||
|
||||
// We don't need to worry about setting the relay since its not getting sent anywhere.
|
||||
if (m_commandList->findMessage(msg) != NULL) {
|
||||
// We don't want to add the same command twice.
|
||||
return;
|
||||
}
|
||||
m_commandList->addMessage(msg);
|
||||
|
||||
++m_commandCount;
|
||||
//DEBUG_LOG(("added command %d, type = %d(%s), command count = %d, frame command count = %d\n", msg->getID(), msg->getNetCommandType(), GetAsciiNetCommandType(msg->getNetCommandType()).str(), m_commandCount, m_frameCommandCount));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of commands for this frame
|
||||
*/
|
||||
NetCommandList * FrameData::getCommandList() {
|
||||
return m_commandList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set both the command count and the frame command count to 0.
|
||||
*/
|
||||
void FrameData::zeroFrame() {
|
||||
m_commandCount = 0;
|
||||
m_frameCommandCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy all the commands in this frame.
|
||||
*/
|
||||
void FrameData::destroyGameMessages() {
|
||||
if (m_commandList == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_commandList->reset();
|
||||
m_commandCount = 0;
|
||||
}
|
||||
207
Generals/Code/GameEngine/Source/GameNetwork/FrameDataManager.cpp
Normal file
207
Generals/Code/GameEngine/Source/GameNetwork/FrameDataManager.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/FrameDataManager.h"
|
||||
#include "GameNetwork/NetworkUtil.h"
|
||||
|
||||
/**
|
||||
* Constructor. isLocal tells it whether its the frame data manager for the local player or not.
|
||||
*/
|
||||
FrameDataManager::FrameDataManager(Bool isLocal) {
|
||||
m_isLocal = isLocal;
|
||||
|
||||
m_frameData = NEW FrameData[FRAME_DATA_LENGTH];
|
||||
|
||||
m_isQuitting = FALSE;
|
||||
m_quitFrame = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* destructor.
|
||||
*/
|
||||
FrameDataManager::~FrameDataManager() {
|
||||
for (Int i = 0; i < FRAME_DATA_LENGTH; ++i) {
|
||||
m_frameData[i].reset();
|
||||
}
|
||||
|
||||
if (m_frameData)
|
||||
{
|
||||
delete[] m_frameData;
|
||||
m_frameData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all of the frame datas associated with this manager.
|
||||
*/
|
||||
void FrameDataManager::init() {
|
||||
for (Int i = 0; i < FRAME_DATA_LENGTH; ++i) {
|
||||
m_frameData[i].init();
|
||||
if (m_isLocal) {
|
||||
// If this is the local connection, adjust the frame command count.
|
||||
m_frameData[i].setFrameCommandCount(m_frameData[i].getCommandCount());
|
||||
}
|
||||
}
|
||||
|
||||
m_isQuitting = FALSE;
|
||||
m_quitFrame = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the state of all the frames.
|
||||
*/
|
||||
void FrameDataManager::reset() {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* update function. Does nothing at this time.
|
||||
*/
|
||||
void FrameDataManager::update() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a network command to the appropriate frame.
|
||||
*/
|
||||
void FrameDataManager::addNetCommandMsg(NetCommandMsg *msg) {
|
||||
UnsignedInt frame = msg->getExecutionFrame();
|
||||
UnsignedInt frameindex = frame % FRAME_DATA_LENGTH;
|
||||
DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("FrameDataManager::addNetCommandMsg - about to add a command of type %s for frame %d, frame index %d\n", GetAsciiNetCommandType(msg->getNetCommandType()).str(), frame, frameindex));
|
||||
m_frameData[frameindex].addCommand(msg);
|
||||
|
||||
if (m_isLocal) {
|
||||
// If this is the local connection, adjust the frame command count.
|
||||
m_frameData[frameindex].setFrameCommandCount(m_frameData[frameindex].getCommandCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all the commands for the given frame are ready.
|
||||
*/
|
||||
FrameDataReturnType FrameDataManager::allCommandsReady(UnsignedInt frame, Bool debugSpewage) {
|
||||
UnsignedInt frameindex = frame % FRAME_DATA_LENGTH;
|
||||
//DEBUG_ASSERTCRASH(m_frameData[frameindex].getFrame() == frame || frame == 256, ("Looking at old commands!"));
|
||||
return m_frameData[frameindex].allCommandsReady(debugSpewage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command list for the given frame.
|
||||
*/
|
||||
NetCommandList * FrameDataManager::getFrameCommandList(UnsignedInt frame) {
|
||||
UnsignedInt frameindex = frame % FRAME_DATA_LENGTH;
|
||||
return m_frameData[frameindex].getCommandList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the contents of the given frame.
|
||||
*/
|
||||
void FrameDataManager::resetFrame(UnsignedInt frame, Bool isAdvancing) {
|
||||
UnsignedInt frameindex = frame % FRAME_DATA_LENGTH;
|
||||
|
||||
m_frameData[frameindex].reset();
|
||||
|
||||
if (isAdvancing) {
|
||||
m_frameData[frameindex].setFrame(frame + MAX_FRAMES_AHEAD);
|
||||
}
|
||||
|
||||
if (m_isLocal) {
|
||||
m_frameData[frameindex].setFrameCommandCount(m_frameData[frameindex].getCommandCount());
|
||||
}
|
||||
|
||||
DEBUG_ASSERTCRASH(m_frameData[frameindex].getCommandCount() == 0, ("we just reset the frame data and the command count is not zero, huh?"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command count for the given frame.
|
||||
*/
|
||||
UnsignedInt FrameDataManager::getCommandCount(UnsignedInt frame) {
|
||||
UnsignedInt frameindex = frame % FRAME_DATA_LENGTH;
|
||||
|
||||
return m_frameData[frameindex].getCommandCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frame command count for the given frame.
|
||||
*/
|
||||
void FrameDataManager::setFrameCommandCount(UnsignedInt frame, UnsignedInt commandCount) {
|
||||
UnsignedInt frameindex = frame % FRAME_DATA_LENGTH;
|
||||
|
||||
m_frameData[frameindex].setFrameCommandCount(commandCount);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UnsignedInt FrameDataManager::getFrameCommandCount(UnsignedInt frame) {
|
||||
UnsignedInt frameindex = frame % FRAME_DATA_LENGTH;
|
||||
|
||||
return m_frameData[frameindex].getFrameCommandCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set both the command count and the frame command count to 0 for the given frames.
|
||||
*/
|
||||
void FrameDataManager::zeroFrames(UnsignedInt startingFrame, UnsignedInt numFrames) {
|
||||
UnsignedInt frameIndex = startingFrame % FRAME_DATA_LENGTH;
|
||||
for (UnsignedInt i = 0; i < numFrames; ++i) {
|
||||
//DEBUG_LOG(("Calling zeroFrame for frame index %d\n", frameIndex));
|
||||
m_frameData[frameIndex].zeroFrame();
|
||||
++frameIndex;
|
||||
frameIndex = frameIndex % FRAME_DATA_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy all the commands held by this object.
|
||||
*/
|
||||
void FrameDataManager::destroyGameMessages() {
|
||||
for (Int i = 0; i < FRAME_DATA_LENGTH; ++i) {
|
||||
m_frameData[i].destroyGameMessages();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quit frame, also sets the isQuitting flag.
|
||||
*/
|
||||
void FrameDataManager::setQuitFrame(UnsignedInt frame) {
|
||||
m_isQuitting = TRUE;
|
||||
m_quitFrame = frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the quit frame.
|
||||
*/
|
||||
UnsignedInt FrameDataManager::getQuitFrame() {
|
||||
return m_quitFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if this frame data manager is quitting.
|
||||
*/
|
||||
Bool FrameDataManager::getIsQuitting() {
|
||||
return m_isQuitting;
|
||||
}
|
||||
147
Generals/Code/GameEngine/Source/GameNetwork/FrameMetrics.cpp
Normal file
147
Generals/Code/GameEngine/Source/GameNetwork/FrameMetrics.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** FrameMetrics.cpp */
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/FrameMetrics.h"
|
||||
#include "GameClient/Display.h"
|
||||
#include "GameNetwork/NetworkUtil.h"
|
||||
|
||||
FrameMetrics::FrameMetrics()
|
||||
{
|
||||
//Added By Sadullah Nader
|
||||
//Initializations missing and needed
|
||||
m_averageFps = 0.0f;
|
||||
m_averageLatency = 0.0f;
|
||||
m_cushionIndex = 0;
|
||||
m_fpsListIndex = 0;
|
||||
m_lastFpsTimeThing = 0;
|
||||
m_minimumCushion = 0;
|
||||
|
||||
m_pendingLatencies = NEW time_t[MAX_FRAMES_AHEAD];
|
||||
for(Int i = 0; i < MAX_FRAMES_AHEAD; i++)
|
||||
m_pendingLatencies[i] = 0;
|
||||
//
|
||||
m_fpsList = NEW Real[TheGlobalData->m_networkFPSHistoryLength];
|
||||
m_latencyList = NEW Real[TheGlobalData->m_networkLatencyHistoryLength];
|
||||
}
|
||||
|
||||
FrameMetrics::~FrameMetrics() {
|
||||
if (m_fpsList != NULL) {
|
||||
delete m_fpsList;
|
||||
m_fpsList = NULL;
|
||||
}
|
||||
|
||||
if (m_latencyList != NULL) {
|
||||
delete m_latencyList;
|
||||
m_latencyList = NULL;
|
||||
}
|
||||
|
||||
if (m_pendingLatencies)
|
||||
{
|
||||
delete[] m_pendingLatencies;
|
||||
m_pendingLatencies = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameMetrics::init() {
|
||||
m_averageFps = 30;
|
||||
m_averageLatency = (Real)0.2;
|
||||
m_minimumCushion = -1;
|
||||
|
||||
for (Int i = 0; i < TheGlobalData->m_networkFPSHistoryLength; ++i) {
|
||||
m_fpsList[i] = 30.0;
|
||||
}
|
||||
m_fpsListIndex = 0;
|
||||
for (i = 0; i < TheGlobalData->m_networkLatencyHistoryLength; ++i) {
|
||||
m_latencyList[i] = (Real)0.2;
|
||||
}
|
||||
m_cushionIndex = 0;
|
||||
}
|
||||
|
||||
void FrameMetrics::reset() {
|
||||
init();
|
||||
}
|
||||
|
||||
void FrameMetrics::doPerFrameMetrics(UnsignedInt frame) {
|
||||
// Do the measurement of the fps.
|
||||
time_t curTime = timeGetTime();
|
||||
if ((curTime - m_lastFpsTimeThing) >= 1000) {
|
||||
// if ((m_fpsListIndex % 16) == 0) {
|
||||
// DEBUG_LOG(("FrameMetrics::doPerFrameMetrics - adding %f to fps history. average before: %f ", m_fpsList[m_fpsListIndex], m_averageFps));
|
||||
// }
|
||||
m_averageFps -= ((m_fpsList[m_fpsListIndex])) / TheGlobalData->m_networkFPSHistoryLength; // subtract out the old value from the average.
|
||||
m_fpsList[m_fpsListIndex] = TheDisplay->getAverageFPS();
|
||||
// m_fpsList[m_fpsListIndex] = TheGameClient->getFrame() - m_fpsStartingFrame;
|
||||
m_averageFps += ((Real)(m_fpsList[m_fpsListIndex])) / TheGlobalData->m_networkFPSHistoryLength; // add the new value to the average.
|
||||
// DEBUG_LOG(("average after: %f\n", m_averageFps));
|
||||
++m_fpsListIndex;
|
||||
m_fpsListIndex %= TheGlobalData->m_networkFPSHistoryLength;
|
||||
m_lastFpsTimeThing = curTime;
|
||||
}
|
||||
|
||||
Int pendingLatenciesIndex = frame % MAX_FRAMES_AHEAD;
|
||||
m_pendingLatencies[pendingLatenciesIndex] = curTime;
|
||||
|
||||
}
|
||||
|
||||
void FrameMetrics::processLatencyResponse(UnsignedInt frame) {
|
||||
time_t curTime = timeGetTime();
|
||||
Int pendingIndex = frame % MAX_FRAMES_AHEAD;
|
||||
time_t timeDiff = curTime - m_pendingLatencies[pendingIndex];
|
||||
|
||||
Int latencyListIndex = frame % TheGlobalData->m_networkLatencyHistoryLength;
|
||||
m_averageLatency -= m_latencyList[latencyListIndex] / TheGlobalData->m_networkLatencyHistoryLength;
|
||||
m_latencyList[latencyListIndex] = (Real)timeDiff / (Real)1000; // convert to seconds from milliseconds.
|
||||
m_averageLatency += m_latencyList[latencyListIndex] / TheGlobalData->m_networkLatencyHistoryLength;
|
||||
|
||||
if (frame % 16 == 0) {
|
||||
// DEBUG_LOG(("ConnectionManager::processFrameInfoAck - average latency = %f\n", m_averageLatency));
|
||||
}
|
||||
}
|
||||
|
||||
void FrameMetrics::addCushion(Int cushion) {
|
||||
++m_cushionIndex;
|
||||
m_cushionIndex %= TheGlobalData->m_networkCushionHistoryLength;
|
||||
if (m_cushionIndex == 0) {
|
||||
m_minimumCushion = -1;
|
||||
}
|
||||
if ((cushion < m_minimumCushion) || (m_minimumCushion == -1)) {
|
||||
m_minimumCushion = cushion;
|
||||
}
|
||||
}
|
||||
|
||||
Int FrameMetrics::getAverageFPS() {
|
||||
return (Int)m_averageFps;
|
||||
}
|
||||
|
||||
Real FrameMetrics::getAverageLatency() {
|
||||
return m_averageLatency;
|
||||
}
|
||||
|
||||
Int FrameMetrics::getMinimumCushion() {
|
||||
return m_minimumCushion;
|
||||
}
|
||||
466
Generals/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp
Normal file
466
Generals/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp
Normal file
@@ -0,0 +1,466 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: GUIUtil.cpp //////////////////////////////////////////////////////
|
||||
// Author: Matthew D. Campbell, Sept 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/GUIUtil.h"
|
||||
#include "GameNetwork/NetworkDefs.h"
|
||||
#include "GameClient/GameWindowManager.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
#include "Common/NameKeyGenerator.h"
|
||||
|
||||
#include "Common/MultiplayerSettings.h"
|
||||
#include "GameClient/GadgetListBox.h"
|
||||
#include "GameClient/GadgetComboBox.h"
|
||||
#include "GameClient/GadgetTextEntry.h"
|
||||
#include "GameClient/GadgetStaticText.h"
|
||||
#include "GameClient/GadgetPushButton.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameNetwork/GameInfo.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "GameNetwork/LANAPICallbacks.h" // for acceptTrueColor, etc
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static Bool winInitialized = FALSE;
|
||||
|
||||
void EnableSlotListUpdates( Bool val )
|
||||
{
|
||||
winInitialized = val;
|
||||
}
|
||||
|
||||
Bool AreSlotListUpdatesEnabled( void )
|
||||
{
|
||||
return winInitialized;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void EnableAcceptControls(Bool Enabled, GameInfo *myGame, GameWindow *comboPlayer[],
|
||||
GameWindow *comboColor[], GameWindow *comboPlayerTemplate[],
|
||||
GameWindow *comboTeam[], GameWindow *buttonAccept[], GameWindow *buttonStart,
|
||||
GameWindow *buttonMapStartPosition[], Int slotNum)
|
||||
{
|
||||
if(slotNum == -1 || slotNum >= MAX_SLOTS )
|
||||
slotNum = myGame->getLocalSlotNum();
|
||||
|
||||
Bool isObserver = myGame->getConstSlot(slotNum)->getPlayerTemplate() == PLAYERTEMPLATE_OBSERVER;
|
||||
|
||||
if( !myGame->amIHost() && (buttonStart != NULL) )
|
||||
buttonStart->winEnable(Enabled);
|
||||
if(comboColor[slotNum])
|
||||
{
|
||||
if (isObserver)
|
||||
{
|
||||
GadgetComboBoxHideList(comboColor[slotNum]);
|
||||
}
|
||||
comboColor[slotNum]->winEnable(Enabled && !isObserver);
|
||||
}
|
||||
if(comboPlayerTemplate[slotNum])
|
||||
comboPlayerTemplate[slotNum]->winEnable(Enabled);
|
||||
if(comboTeam[slotNum])
|
||||
{
|
||||
if (isObserver)
|
||||
{
|
||||
GadgetComboBoxHideList(comboTeam[slotNum]);
|
||||
}
|
||||
comboTeam[slotNum]->winEnable(Enabled && !isObserver);
|
||||
}
|
||||
|
||||
Bool canChooseStartSpot = FALSE;
|
||||
if (!isObserver)
|
||||
canChooseStartSpot = TRUE;
|
||||
for (Int i=0; i<MAX_SLOTS && !canChooseStartSpot && myGame->amIHost(); ++i)
|
||||
{
|
||||
if (myGame->getConstSlot(i) && myGame->getConstSlot(i)->isAI())
|
||||
canChooseStartSpot = TRUE;
|
||||
}
|
||||
|
||||
if (slotNum == myGame->getLocalSlotNum())
|
||||
{
|
||||
if (myGame->getConstSlot(myGame->getLocalSlotNum())->hasMap())
|
||||
{
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
if (buttonMapStartPosition[i])
|
||||
{
|
||||
buttonMapStartPosition[i]->winEnable(Enabled && canChooseStartSpot);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
if (buttonMapStartPosition[i])
|
||||
buttonMapStartPosition[i]->winEnable(FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void ShowUnderlyingGUIElements( Bool show, const char *layoutFilename, const char *parentName,
|
||||
const char **gadgetsToHide, const char **perPlayerGadgetsToHide )
|
||||
{
|
||||
AsciiString parentNameStr;
|
||||
parentNameStr.format("%s:%s", layoutFilename, parentName);
|
||||
NameKeyType parentID = NAMEKEY(parentNameStr);
|
||||
GameWindow *parent = TheWindowManager->winGetWindowFromId( NULL, parentID );
|
||||
if (!parent)
|
||||
{
|
||||
DEBUG_CRASH(("Window %s not found\n", parentNameStr.str()));
|
||||
return;
|
||||
}
|
||||
|
||||
// hide some GUI elements of the screen underneath
|
||||
GameWindow *win;
|
||||
|
||||
Int player;
|
||||
const char **text;
|
||||
|
||||
text = gadgetsToHide;
|
||||
while (*text)
|
||||
{
|
||||
AsciiString gadgetName;
|
||||
gadgetName.format("%s:%s", layoutFilename, *text);
|
||||
win = TheWindowManager->winGetWindowFromId( parent, NAMEKEY(gadgetName) );
|
||||
//DEBUG_ASSERTCRASH(win, ("Cannot find %s to show/hide it", gadgetName.str()));
|
||||
if (win)
|
||||
{
|
||||
win->winHide( !show );
|
||||
}
|
||||
++text;
|
||||
}
|
||||
|
||||
text = perPlayerGadgetsToHide;
|
||||
while (*text)
|
||||
{
|
||||
for (player = 0; player < MAX_SLOTS; ++player)
|
||||
{
|
||||
AsciiString gadgetName;
|
||||
gadgetName.format("%s:%s%d", layoutFilename, *text, player);
|
||||
win = TheWindowManager->winGetWindowFromId( parent, NAMEKEY(gadgetName) );
|
||||
//DEBUG_ASSERTCRASH(win, ("Cannot find %s to show/hide it", gadgetName.str()));
|
||||
if (win)
|
||||
{
|
||||
win->winHide( !show );
|
||||
}
|
||||
}
|
||||
++text;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void PopulateColorComboBox(Int comboBox, GameWindow *comboArray[], GameInfo *myGame, Bool isObserver)
|
||||
{
|
||||
Int numColors = TheMultiplayerSettings->getNumColors();
|
||||
UnicodeString colorName;
|
||||
std::vector<bool> availableColors;
|
||||
|
||||
for (Int i = 0; i < numColors; i++)
|
||||
availableColors.push_back(true);
|
||||
|
||||
for (i = 0; i < MAX_SLOTS; i++)
|
||||
{
|
||||
GameSlot *slot = myGame->getSlot(i);
|
||||
if( slot && (i != comboBox) && (slot->getColor() >= 0 )&& (slot->getColor() < numColors))
|
||||
{
|
||||
DEBUG_ASSERTCRASH(slot->getColor() >= 0,("We've tried to access array %d and that ain't good",slot->getColor()));
|
||||
availableColors[slot->getColor()] = false;
|
||||
}
|
||||
}
|
||||
|
||||
Bool wasObserver = (GadgetComboBoxGetLength(comboArray[comboBox]) == 1);
|
||||
GadgetComboBoxReset(comboArray[comboBox]);
|
||||
|
||||
MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(PLAYERTEMPLATE_RANDOM);
|
||||
Int newIndex = GadgetComboBoxAddEntry(comboArray[comboBox],
|
||||
(isObserver)?TheGameText->fetch("GUI:None"):TheGameText->fetch("GUI:???"), def->getColor());
|
||||
GadgetComboBoxSetItemData(comboArray[comboBox], newIndex, (void *)-1);
|
||||
|
||||
if (isObserver)
|
||||
{
|
||||
GadgetComboBoxSetSelectedPos(comboArray[comboBox], 0);
|
||||
return;
|
||||
}
|
||||
|
||||
for (Int c=0; c<numColors; ++c)
|
||||
{
|
||||
def = TheMultiplayerSettings->getColor(c);
|
||||
if (!def || availableColors[c] == false)
|
||||
continue;
|
||||
|
||||
colorName = TheGameText->fetch(def->getTooltipName().str());
|
||||
newIndex = GadgetComboBoxAddEntry(comboArray[comboBox], colorName, def->getColor());
|
||||
GadgetComboBoxSetItemData(comboArray[comboBox], newIndex, (void *)c);
|
||||
}
|
||||
if (wasObserver)
|
||||
GadgetComboBoxSetSelectedPos(comboArray[comboBox], 0);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void PopulatePlayerTemplateComboBox(Int comboBox, GameWindow *comboArray[], GameInfo *myGame, Bool allowObservers)
|
||||
{
|
||||
Int numPlayerTemplates = ThePlayerTemplateStore->getPlayerTemplateCount();
|
||||
UnicodeString playerTemplateName;
|
||||
|
||||
GadgetComboBoxReset(comboArray[comboBox]);
|
||||
|
||||
MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(PLAYERTEMPLATE_RANDOM);
|
||||
Int newIndex = GadgetComboBoxAddEntry(comboArray[comboBox], TheGameText->fetch("GUI:Random"), def->getColor());
|
||||
GadgetComboBoxSetItemData(comboArray[comboBox], newIndex, (void *)PLAYERTEMPLATE_RANDOM);
|
||||
|
||||
std::set<AsciiString> seenSides;
|
||||
|
||||
for (Int c=0; c<numPlayerTemplates; ++c)
|
||||
{
|
||||
const PlayerTemplate *fac = ThePlayerTemplateStore->getNthPlayerTemplate(c);
|
||||
if (!fac)
|
||||
continue;
|
||||
|
||||
if (fac->getStartingBuilding().isEmpty())
|
||||
continue;
|
||||
|
||||
AsciiString side;
|
||||
side.format("SIDE:%s", fac->getSide().str());
|
||||
if (seenSides.find(side) != seenSides.end())
|
||||
continue;
|
||||
|
||||
seenSides.insert(side);
|
||||
|
||||
newIndex = GadgetComboBoxAddEntry(comboArray[comboBox], TheGameText->fetch(side), def->getColor());
|
||||
GadgetComboBoxSetItemData(comboArray[comboBox], newIndex, (void *)c);
|
||||
}
|
||||
seenSides.clear();
|
||||
|
||||
// disabling observers for Multiplayer test
|
||||
#ifndef _PLAYTEST
|
||||
if (allowObservers)
|
||||
{
|
||||
def = TheMultiplayerSettings->getColor(PLAYERTEMPLATE_OBSERVER);
|
||||
newIndex = GadgetComboBoxAddEntry(comboArray[comboBox], TheGameText->fetch("GUI:Observer"), def->getColor());
|
||||
GadgetComboBoxSetItemData(comboArray[comboBox], newIndex, (void *)PLAYERTEMPLATE_OBSERVER);
|
||||
}
|
||||
#endif
|
||||
GadgetComboBoxSetSelectedPos(comboArray[comboBox], 0);
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void PopulateTeamComboBox(Int comboBox, GameWindow *comboArray[], GameInfo *myGame, Bool isObserver)
|
||||
{
|
||||
Int numTeams = MAX_SLOTS/2;
|
||||
UnicodeString teamName;
|
||||
|
||||
GadgetComboBoxReset(comboArray[comboBox]);
|
||||
|
||||
MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(PLAYERTEMPLATE_RANDOM);
|
||||
Int newIndex = GadgetComboBoxAddEntry(comboArray[comboBox], TheGameText->fetch("Team:0"), def->getColor());
|
||||
GadgetComboBoxSetItemData(comboArray[comboBox], newIndex, (void *)-1);
|
||||
|
||||
if (isObserver)
|
||||
{
|
||||
GadgetComboBoxSetSelectedPos(comboArray[comboBox], 0);
|
||||
return;
|
||||
}
|
||||
|
||||
for (Int c=0; c<numTeams; ++c)
|
||||
{
|
||||
AsciiString teamStr;
|
||||
teamStr.format("Team:%d", c + 1);
|
||||
teamName = TheGameText->fetch(teamStr.str());
|
||||
newIndex = GadgetComboBoxAddEntry(comboArray[comboBox], teamName, def->getColor());
|
||||
GadgetComboBoxSetItemData(comboArray[comboBox], newIndex, (void *)c);
|
||||
}
|
||||
GadgetComboBoxSetSelectedPos(comboArray[comboBox], 0);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// The slot list displaying function
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void UpdateSlotList( GameInfo *myGame, GameWindow *comboPlayer[],
|
||||
GameWindow *comboColor[], GameWindow *comboPlayerTemplate[],
|
||||
GameWindow *comboTeam[], GameWindow *buttonAccept[],
|
||||
GameWindow *buttonStart, GameWindow *buttonMapStartPosition[] )
|
||||
{
|
||||
if(!AreSlotListUpdatesEnabled())
|
||||
return;
|
||||
//LANGameInfo *myGame = TheLAN->GetMyGame();
|
||||
|
||||
const MapMetaData *mapData = TheMapCache->findMap( myGame->getMap() );
|
||||
Bool willTransfer = TRUE;
|
||||
if (mapData)
|
||||
{
|
||||
willTransfer = !mapData->m_isOfficial;
|
||||
}
|
||||
else
|
||||
{
|
||||
willTransfer = WouldMapTransfer(myGame->getMap());
|
||||
}
|
||||
|
||||
if (myGame)
|
||||
{
|
||||
for( int i =0; i < MAX_SLOTS; i++ )
|
||||
{
|
||||
GameSlot * slot = myGame->getSlot(i);
|
||||
// if i'm host, enable the controls for AI
|
||||
if(myGame->amIHost() && slot && slot->isAI())
|
||||
{
|
||||
EnableAcceptControls(TRUE, myGame, comboPlayer, comboColor, comboPlayerTemplate,
|
||||
comboTeam, buttonAccept, buttonStart, buttonMapStartPosition, i);
|
||||
}
|
||||
else if (slot && myGame->getLocalSlotNum() == i)
|
||||
{
|
||||
if(slot->isAccepted() && !myGame->amIHost())
|
||||
{
|
||||
EnableAcceptControls(FALSE, myGame, comboPlayer, comboColor, comboPlayerTemplate,
|
||||
comboTeam, buttonAccept, buttonStart, buttonMapStartPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (slot->hasMap()) {
|
||||
EnableAcceptControls(TRUE, myGame, comboPlayer, comboColor, comboPlayerTemplate,
|
||||
comboTeam, buttonAccept, buttonStart, buttonMapStartPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnableAcceptControls(willTransfer, myGame, comboPlayer, comboColor, comboPlayerTemplate,
|
||||
comboTeam, buttonAccept, buttonStart, buttonMapStartPosition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if(myGame->amIHost())
|
||||
{
|
||||
EnableAcceptControls(FALSE, myGame, comboPlayer, comboColor, comboPlayerTemplate,
|
||||
comboTeam, buttonAccept, buttonStart, buttonMapStartPosition, i);
|
||||
}
|
||||
if(slot && slot->isHuman())
|
||||
{
|
||||
UnicodeString newName = slot->getName();
|
||||
UnicodeString oldName = GadgetComboBoxGetText(comboPlayer[i]);
|
||||
if (comboPlayer[i] && newName.compare(oldName))
|
||||
{
|
||||
GadgetComboBoxSetText(comboPlayer[i], newName);
|
||||
}
|
||||
if(i!= 0 && buttonAccept && buttonAccept[i])
|
||||
{
|
||||
buttonAccept[i]->winHide(FALSE);
|
||||
//Color In the little accepted boxes
|
||||
if(slot->isAccepted())
|
||||
{
|
||||
if(BitTest(buttonAccept[i]->winGetStatus(), WIN_STATUS_IMAGE ))
|
||||
buttonAccept[i]->winEnable(TRUE);
|
||||
else
|
||||
GadgetButtonSetEnabledColor(buttonAccept[i], acceptTrueColor );
|
||||
}
|
||||
else
|
||||
{
|
||||
if(BitTest(buttonAccept[i]->winGetStatus(), WIN_STATUS_IMAGE ))
|
||||
buttonAccept[i]->winEnable(FALSE);
|
||||
else
|
||||
GadgetButtonSetEnabledColor(buttonAccept[i], acceptFalseColor );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GadgetComboBoxSetSelectedPos(comboPlayer[i], slot->getState(), TRUE);
|
||||
if( buttonAccept && buttonAccept[i] )
|
||||
buttonAccept[i]->winHide(TRUE);
|
||||
}
|
||||
/*
|
||||
if (myGame->getLocalSlotNum() == i && i!=0)
|
||||
{
|
||||
if (comboPlayer[i])
|
||||
comboPlayer[i]->winEnable( TRUE );
|
||||
}
|
||||
else*/ if (!myGame->amIHost())
|
||||
{
|
||||
if (comboPlayer[i])
|
||||
comboPlayer[i]->winEnable( FALSE );
|
||||
}
|
||||
//if( i == myGame->getLocalSlotNum())
|
||||
if((comboColor[i] != NULL) && BitTest(comboColor[i]->winGetStatus(), WIN_STATUS_ENABLED))
|
||||
PopulateColorComboBox(i, comboColor, myGame, myGame->getConstSlot(i)->getPlayerTemplate() == PLAYERTEMPLATE_OBSERVER);
|
||||
Int max, idx;
|
||||
if (comboColor[i] != NULL) {
|
||||
max = GadgetComboBoxGetLength(comboColor[i]);
|
||||
for (idx=0; idx<max; ++idx)
|
||||
{
|
||||
Int color = (Int)GadgetComboBoxGetItemData(comboColor[i], idx);
|
||||
if (color == slot->getColor())
|
||||
{
|
||||
GadgetComboBoxSetSelectedPos(comboColor[i], idx, TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (comboTeam[i] != NULL) {
|
||||
max = GadgetComboBoxGetLength(comboTeam[i]);
|
||||
for (idx=0; idx<max; ++idx)
|
||||
{
|
||||
Int team = (Int)GadgetComboBoxGetItemData(comboTeam[i], idx);
|
||||
if (team == slot->getTeamNumber())
|
||||
{
|
||||
GadgetComboBoxSetSelectedPos(comboTeam[i], idx, TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (comboPlayerTemplate[i] != NULL) {
|
||||
max = GadgetComboBoxGetLength(comboPlayerTemplate[i]);
|
||||
for (idx=0; idx<max; ++idx)
|
||||
{
|
||||
Int playerTemplate = (Int)GadgetComboBoxGetItemData(comboPlayerTemplate[i], idx);
|
||||
if (playerTemplate == slot->getPlayerTemplate())
|
||||
{
|
||||
GadgetComboBoxSetSelectedPos(comboPlayerTemplate[i], idx, TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
1545
Generals/Code/GameEngine/Source/GameNetwork/GameInfo.cpp
Normal file
1545
Generals/Code/GameEngine/Source/GameNetwork/GameInfo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/GameMessageParser.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
GameMessageParser::GameMessageParser()
|
||||
{
|
||||
m_first = NULL;
|
||||
m_argTypeCount = 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
GameMessageParser::GameMessageParser(GameMessage *msg)
|
||||
{
|
||||
m_first = NULL;
|
||||
m_argTypeCount = 0;
|
||||
|
||||
UnsignedByte argCount = msg->getArgumentCount();
|
||||
GameMessageArgumentDataType lasttype = ARGUMENTDATATYPE_UNKNOWN;
|
||||
Int thisTypeCount = 0;
|
||||
|
||||
for (UnsignedByte i = 0; i < argCount; ++i) {
|
||||
GameMessageArgumentDataType type = msg->getArgumentDataType(i);
|
||||
if (type != lasttype) {
|
||||
if (thisTypeCount > 0) {
|
||||
addArgType(lasttype, thisTypeCount);
|
||||
++m_argTypeCount;
|
||||
}
|
||||
lasttype = type;
|
||||
thisTypeCount = 0;
|
||||
}
|
||||
++thisTypeCount;
|
||||
}
|
||||
if (thisTypeCount > 0) {
|
||||
addArgType(lasttype, thisTypeCount);
|
||||
++m_argTypeCount;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
GameMessageParser::~GameMessageParser()
|
||||
{
|
||||
GameMessageParserArgumentType *temp = NULL;
|
||||
while (m_first != NULL) {
|
||||
temp = m_first->getNext();
|
||||
m_first->deleteInstance();
|
||||
m_first = temp;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void GameMessageParser::addArgType(GameMessageArgumentDataType type, Int argCount)
|
||||
{
|
||||
if (m_first == NULL) {
|
||||
m_first = newInstance(GameMessageParserArgumentType)(type, argCount);
|
||||
m_last = m_first;
|
||||
return;
|
||||
}
|
||||
|
||||
m_last->setNext(newInstance(GameMessageParserArgumentType)(type, argCount));
|
||||
m_last = m_last->getNext();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
GameMessageParserArgumentType::GameMessageParserArgumentType(GameMessageArgumentDataType type, Int argCount)
|
||||
{
|
||||
m_next = NULL;
|
||||
m_type = type;
|
||||
m_argCount = argCount;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
GameMessageParserArgumentType::~GameMessageParserArgumentType()
|
||||
{
|
||||
}
|
||||
|
||||
1417
Generals/Code/GameEngine/Source/GameNetwork/GameSpy.cpp
Normal file
1417
Generals/Code/GameEngine/Source/GameNetwork/GameSpy.cpp
Normal file
File diff suppressed because it is too large
Load Diff
339
Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Chat.cpp
Normal file
339
Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Chat.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: Chat.cpp //////////////////////////////////////////////////////
|
||||
// Generals GameSpy chat-related code
|
||||
// Author: Matthew D. Campbell, July 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/AudioEventRTS.h"
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/GadgetListBox.h"
|
||||
#include "GameClient/LanguageFilter.h"
|
||||
#include "GameClient/GameWindowManager.h"
|
||||
#include "GameNetwork/GameSpy/PeerDefsImplementation.h"
|
||||
#include "GameNetwork/GameSpy/PeerThread.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
#define OFFSET(x) (sizeof(Int) * (x))
|
||||
static const FieldParse GameSpyColorFieldParse[] =
|
||||
{
|
||||
|
||||
{ "Default", INI::parseColorInt, NULL, OFFSET(GSCOLOR_DEFAULT) },
|
||||
{ "CurrentRoom", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CURRENTROOM) },
|
||||
{ "ChatRoom", INI::parseColorInt, NULL, OFFSET(GSCOLOR_ROOM) },
|
||||
{ "Game", INI::parseColorInt, NULL, OFFSET(GSCOLOR_GAME) },
|
||||
{ "GameFull", INI::parseColorInt, NULL, OFFSET(GSCOLOR_GAME_FULL) },
|
||||
{ "GameCRCMismatch", INI::parseColorInt, NULL, OFFSET(GSCOLOR_GAME_CRCMISMATCH) },
|
||||
{ "PlayerNormal", INI::parseColorInt, NULL, OFFSET(GSCOLOR_PLAYER_NORMAL) },
|
||||
{ "PlayerOwner", INI::parseColorInt, NULL, OFFSET(GSCOLOR_PLAYER_OWNER) },
|
||||
{ "PlayerBuddy", INI::parseColorInt, NULL, OFFSET(GSCOLOR_PLAYER_BUDDY) },
|
||||
{ "PlayerSelf", INI::parseColorInt, NULL, OFFSET(GSCOLOR_PLAYER_SELF) },
|
||||
{ "PlayerIgnored", INI::parseColorInt, NULL, OFFSET(GSCOLOR_PLAYER_IGNORED) },
|
||||
{ "ChatNormal", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_NORMAL) },
|
||||
{ "ChatEmote", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_EMOTE) },
|
||||
{ "ChatOwner", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_OWNER) },
|
||||
{ "ChatOwnerEmote", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_OWNER_EMOTE) },
|
||||
{ "ChatPriv", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_PRIVATE) },
|
||||
{ "ChatPrivEmote", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_PRIVATE_EMOTE) },
|
||||
{ "ChatPrivOwner", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_PRIVATE_OWNER) },
|
||||
{ "ChatPrivOwnerEmote", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_PRIVATE_OWNER_EMOTE) },
|
||||
{ "ChatBuddy", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_BUDDY) },
|
||||
{ "ChatSelf", INI::parseColorInt, NULL, OFFSET(GSCOLOR_CHAT_SELF) },
|
||||
{ "AcceptTrue", INI::parseColorInt, NULL, OFFSET(GSCOLOR_ACCEPT_TRUE) },
|
||||
{ "AcceptFalse", INI::parseColorInt, NULL, OFFSET(GSCOLOR_ACCEPT_FALSE) },
|
||||
{ "MapSelected", INI::parseColorInt, NULL, OFFSET(GSCOLOR_MAP_SELECTED) },
|
||||
{ "MapUnselected", INI::parseColorInt, NULL, OFFSET(GSCOLOR_MAP_UNSELECTED) },
|
||||
{ "MOTD", INI::parseColorInt, NULL, OFFSET(GSCOLOR_MOTD) },
|
||||
{ "MOTDHeading", INI::parseColorInt, NULL, OFFSET(GSCOLOR_MOTD_HEADING) },
|
||||
|
||||
{ NULL, NULL, NULL, 0 } // keep this last
|
||||
|
||||
};
|
||||
|
||||
void INI::parseOnlineChatColorDefinition( INI* ini )
|
||||
{
|
||||
// parse the ini definition
|
||||
ini->initFromINI( GameSpyColor, GameSpyColorFieldParse );
|
||||
}
|
||||
|
||||
|
||||
Color GameSpyColor[GSCOLOR_MAX] =
|
||||
{
|
||||
GameMakeColor(255,255,255,255), // GSCOLOR_DEFAULT
|
||||
GameMakeColor(255,255, 0,255), // GSCOLOR_CURRENTROOM
|
||||
GameMakeColor(255,255,255,255), // GSCOLOR_ROOM
|
||||
GameMakeColor(128,128,0,255), // GSCOLOR_GAME
|
||||
GameMakeColor(128,128,128,255), // GSCOLOR_GAME_FULL
|
||||
GameMakeColor(128,128,128,255), // GSCOLOR_GAME_CRCMISMATCH
|
||||
GameMakeColor(255, 0, 0,255), // GSCOLOR_PLAYER_NORMAL
|
||||
GameMakeColor(255, 0,255,255), // GSCOLOR_PLAYER_OWNER
|
||||
GameMakeColor(255, 0,128,255), // GSCOLOR_PLAYER_BUDDY
|
||||
GameMakeColor(255, 0, 0,255), // GSCOLOR_PLAYER_SELF
|
||||
GameMakeColor(128,128,128,255), // GSCOLOR_PLAYER_IGNORED
|
||||
GameMakeColor(255,0,0,255), // GSCOLOR_CHAT_NORMAL
|
||||
GameMakeColor(255,128,0,255), // GSCOLOR_CHAT_EMOTE,
|
||||
GameMakeColor(255,255,0,255), // GSCOLOR_CHAT_OWNER,
|
||||
GameMakeColor(128,255,0,255), // GSCOLOR_CHAT_OWNER_EMOTE,
|
||||
GameMakeColor(0,0,255,255), // GSCOLOR_CHAT_PRIVATE,
|
||||
GameMakeColor(0,255,255,255), // GSCOLOR_CHAT_PRIVATE_EMOTE,
|
||||
GameMakeColor(255,0,255,255), // GSCOLOR_CHAT_PRIVATE_OWNER,
|
||||
GameMakeColor(255,128,255,255), // GSCOLOR_CHAT_PRIVATE_OWNER_EMOTE,
|
||||
GameMakeColor(255, 0,255,255), // GSCOLOR_CHAT_BUDDY,
|
||||
GameMakeColor(255, 0,128,255), // GSCOLOR_CHAT_SELF,
|
||||
GameMakeColor( 0,255, 0,255), // GSCOLOR_ACCEPT_TRUE,
|
||||
GameMakeColor(255, 0, 0,255), // GSCOLOR_ACCEPT_FALSE,
|
||||
GameMakeColor(255,255, 0,255), // GSCOLOR_MAP_SELECTED,
|
||||
GameMakeColor(255,255,255,255), // GSCOLOR_MAP_UNSELECTED,
|
||||
GameMakeColor(255,255,255,255), // GSCOLOR_MOTD,
|
||||
GameMakeColor(255,255, 0,255), // GSCOLOR_MOTD_HEADING,
|
||||
};
|
||||
|
||||
Bool GameSpyInfo::sendChat( UnicodeString message, Bool isAction, GameWindow *playerListbox )
|
||||
{
|
||||
RoomType roomType = StagingRoom;
|
||||
if (getCurrentGroupRoom())
|
||||
roomType = GroupRoom;
|
||||
|
||||
PeerRequest req;
|
||||
req.text = message.str();
|
||||
|
||||
message.trim();
|
||||
// Echo the user's input to the chat window
|
||||
if (!message.isEmpty())
|
||||
{
|
||||
if (!playerListbox)
|
||||
{
|
||||
// Public message
|
||||
req.message.isAction = isAction;
|
||||
req.peerRequestType = PeerRequest::PEERREQUEST_MESSAGEROOM;
|
||||
TheGameSpyPeerMessageQueue->addRequest(req);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the selections (is this a private message?)
|
||||
Int maxSel = GadgetListBoxGetListLength(playerListbox);
|
||||
Int *selections;
|
||||
GadgetListBoxGetSelected(playerListbox, (Int *)&selections);
|
||||
|
||||
if (selections[0] == -1)
|
||||
{
|
||||
// Public message
|
||||
req.message.isAction = isAction;
|
||||
req.peerRequestType = PeerRequest::PEERREQUEST_MESSAGEROOM;
|
||||
TheGameSpyPeerMessageQueue->addRequest(req);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Private message
|
||||
|
||||
// Construct a list
|
||||
AsciiString names = AsciiString::TheEmptyString;
|
||||
AsciiString tmp = AsciiString::TheEmptyString;
|
||||
AsciiString aStr; // AsciiString buf for translating Unicode entries
|
||||
names.format("%s", TheGameSpyInfo->getLocalName().str());
|
||||
for (int i=0; i<maxSel; i++)
|
||||
{
|
||||
if (selections[i] != -1)
|
||||
{
|
||||
aStr.translate(GadgetListBoxGetText(playerListbox, selections[i], GadgetListBoxGetNumColumns(playerListbox)-1));
|
||||
if (aStr.compareNoCase(TheGameSpyInfo->getLocalName()))
|
||||
{
|
||||
tmp.format(",%s", aStr.str());
|
||||
names.concat(tmp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!names.isEmpty())
|
||||
{
|
||||
req.nick = names.str();
|
||||
req.message.isAction = isAction;
|
||||
req.peerRequestType = PeerRequest::PEERREQUEST_MESSAGEPLAYER;
|
||||
TheGameSpyPeerMessageQueue->addRequest(req);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameSpyInfo::addChat( AsciiString nick, Int profileID, UnicodeString msg, Bool isPublic, Bool isAction, GameWindow *win )
|
||||
{
|
||||
PlayerInfoMap::iterator it = getPlayerInfoMap()->find(nick);
|
||||
if (it != getPlayerInfoMap()->end())
|
||||
{
|
||||
addChat( it->second, msg, isPublic, isAction, win );
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyInfo::addChat( PlayerInfo p, UnicodeString msg, Bool isPublic, Bool isAction, GameWindow *win )
|
||||
{
|
||||
Int style;
|
||||
if(isSavedIgnored(p.m_profileID) || isIgnored(p.m_name))
|
||||
return;
|
||||
|
||||
Bool isOwner = p.m_flags & PEER_FLAG_OP;
|
||||
Bool isBuddy = getBuddyMap()->find(p.m_profileID) != getBuddyMap()->end();
|
||||
|
||||
Bool isMe = p.m_name.compare(TheGameSpyInfo->getLocalName()) == 0;
|
||||
|
||||
if(!isMe)
|
||||
{
|
||||
if(m_disallowAsainText)
|
||||
{
|
||||
const WideChar *buff = msg.str();
|
||||
Int length = msg.getLength();
|
||||
for(Int i = 0; i < length; ++i)
|
||||
{
|
||||
if(buff[i] >= 256)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(m_disallowNonAsianText)
|
||||
{
|
||||
const WideChar *buff = msg.str();
|
||||
Int length = msg.getLength();
|
||||
Bool hasUnicode = FALSE;
|
||||
for(Int i = 0; i < length; ++i)
|
||||
{
|
||||
if(buff[i] >= 256)
|
||||
{
|
||||
hasUnicode = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!hasUnicode)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPublic)
|
||||
{
|
||||
AudioEventRTS privMsgAudio("GUIMessageReceived");
|
||||
|
||||
if( TheAudio )
|
||||
{
|
||||
TheAudio->addAudioEvent( &privMsgAudio );
|
||||
} // end if
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (isBuddy)
|
||||
{
|
||||
style = GSCOLOR_CHAT_BUDDY;
|
||||
}
|
||||
else if (isPublic && isAction)
|
||||
{
|
||||
style = (isOwner)?GSCOLOR_CHAT_OWNER_EMOTE:GSCOLOR_CHAT_EMOTE;
|
||||
}
|
||||
else if (isPublic)
|
||||
{
|
||||
style = (isOwner)?GSCOLOR_CHAT_OWNER:GSCOLOR_CHAT_NORMAL;
|
||||
}
|
||||
else if (isAction)
|
||||
{
|
||||
style = (isOwner)?GSCOLOR_CHAT_PRIVATE_OWNER_EMOTE:GSCOLOR_CHAT_PRIVATE_EMOTE;
|
||||
}
|
||||
else
|
||||
{
|
||||
style = (isOwner)?GSCOLOR_CHAT_PRIVATE_OWNER:GSCOLOR_CHAT_PRIVATE;
|
||||
}
|
||||
|
||||
UnicodeString name;
|
||||
name.translate(p.m_name);
|
||||
|
||||
// filters language
|
||||
// if( TheGlobalData->m_languageFilterPref )
|
||||
// {
|
||||
TheLanguageFilter->filterLine(msg);
|
||||
// }
|
||||
|
||||
UnicodeString fullMsg;
|
||||
if (isAction)
|
||||
{
|
||||
fullMsg.format( L"%ls %ls", name.str(), msg.str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
fullMsg.format( L"[%ls] %ls", name.str(), msg.str() );
|
||||
}
|
||||
|
||||
Int index = addText(fullMsg, GameSpyColor[style], win);
|
||||
if (index >= 0)
|
||||
{
|
||||
GadgetListBoxSetItemData(win, (void *)p.m_profileID, index);
|
||||
}
|
||||
}
|
||||
|
||||
Int GameSpyInfo::addText( UnicodeString message, Color c, GameWindow *win )
|
||||
{
|
||||
if (TheGameSpyGame && TheGameSpyGame->isInGame() && TheGameSpyGame->isGameInProgress())
|
||||
{
|
||||
static AudioEventRTS messageFromChatSound("GUIMessageReceived");
|
||||
TheAudio->addAudioEvent(&messageFromChatSound);
|
||||
|
||||
TheInGameUI->message(message);
|
||||
}
|
||||
|
||||
if (!win)
|
||||
{
|
||||
// try to pick up a registered text window
|
||||
if (m_textWindows.empty())
|
||||
return -1;
|
||||
|
||||
win = *(m_textWindows.begin());
|
||||
}
|
||||
Int index = GadgetListBoxAddEntryText(win, message, c, -1, -1);
|
||||
GadgetListBoxSetItemData(win, (void *)-1, index);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void GameSpyInfo::registerTextWindow( GameWindow *win )
|
||||
{
|
||||
m_textWindows.insert(win);
|
||||
}
|
||||
|
||||
void GameSpyInfo::unregisterTextWindow( GameWindow *win )
|
||||
{
|
||||
m_textWindows.erase(win);
|
||||
}
|
||||
|
||||
487
Generals/Code/GameEngine/Source/GameNetwork/GameSpy/GSConfig.cpp
Normal file
487
Generals/Code/GameEngine/Source/GameNetwork/GameSpy/GSConfig.cpp
Normal file
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// FILE: GSConfig.cpp
|
||||
// Author: Matthew D. Campbell, Sept 2002
|
||||
// Description: GameSpy online config
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GameState.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
#include "GameNetwork/GameSpy/GSConfig.h"
|
||||
#include "GameNetwork/RankPointValue.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GameSpyConfigInterface *TheGameSpyConfig = NULL;
|
||||
|
||||
class GameSpyConfig : public GameSpyConfigInterface
|
||||
{
|
||||
public:
|
||||
GameSpyConfig( AsciiString config );
|
||||
~GameSpyConfig() {}
|
||||
|
||||
// Pings
|
||||
std::list<AsciiString> getPingServers(void) { return m_pingServers; }
|
||||
Int getNumPingRepetitions(void) { return m_pingReps; }
|
||||
Int getPingTimeoutInMs(void) { return m_pingTimeout; }
|
||||
virtual Int getPingCutoffGood( void ) { return m_pingCutoffGood; }
|
||||
virtual Int getPingCutoffBad( void ) { return m_pingCutoffBad; }
|
||||
|
||||
// QM
|
||||
std::list<AsciiString> getQMMaps(void) { return m_qmMaps; }
|
||||
Int getQMBotID(void) { return m_qmBotID; }
|
||||
Int getQMChannel(void) { return m_qmChannel; }
|
||||
void setQMChannel(Int channel) { m_qmChannel = channel; }
|
||||
|
||||
// Player Info
|
||||
Int getPointsForRank(Int rank);
|
||||
virtual Bool isPlayerVIP(Int id);
|
||||
|
||||
virtual Bool getManglerLocation(Int index, AsciiString& host, UnsignedShort& port);
|
||||
|
||||
// Ladder / Any other external parsing
|
||||
AsciiString getLeftoverConfig(void) { return m_leftoverConfig; }
|
||||
|
||||
// NAT Timeouts
|
||||
virtual Int getTimeBetweenRetries() { return m_natRetryInterval; }
|
||||
virtual Int getMaxManglerRetries() { return m_natMaxManglerRetries; }
|
||||
virtual time_t getRetryInterval() { return m_natManglerRetryInterval; }
|
||||
virtual time_t getKeepaliveInterval() { return m_natKeepaliveInterval; }
|
||||
virtual time_t getPortTimeout() { return m_natPortTimeout; }
|
||||
virtual time_t getRoundTimeout() { return m_natRoundTimeout; }
|
||||
|
||||
// Custom match
|
||||
virtual Bool restrictGamesToLobby() { return m_restrictGamesToLobby; }
|
||||
|
||||
protected:
|
||||
std::list<AsciiString> m_pingServers;
|
||||
Int m_pingReps;
|
||||
Int m_pingTimeout;
|
||||
Int m_pingCutoffGood;
|
||||
Int m_pingCutoffBad;
|
||||
|
||||
Int m_natRetryInterval;
|
||||
Int m_natMaxManglerRetries;
|
||||
time_t m_natManglerRetryInterval;
|
||||
time_t m_natKeepaliveInterval;
|
||||
time_t m_natPortTimeout;
|
||||
time_t m_natRoundTimeout;
|
||||
|
||||
std::vector<AsciiString> m_manglerHosts;
|
||||
std::vector<UnsignedShort> m_manglerPorts;
|
||||
|
||||
std::list<AsciiString> m_qmMaps;
|
||||
Int m_qmBotID;
|
||||
Int m_qmChannel;
|
||||
|
||||
Bool m_restrictGamesToLobby;
|
||||
|
||||
std::set<Int> m_vip; // VIP people
|
||||
|
||||
Int m_rankPoints[MAX_RANKS];
|
||||
|
||||
AsciiString m_leftoverConfig;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GameSpyConfigInterface* GameSpyConfigInterface::create(AsciiString config)
|
||||
{
|
||||
return NEW GameSpyConfig(config);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class SectionChecker
|
||||
{
|
||||
public:
|
||||
typedef std::list<const Bool *> SectionList;
|
||||
void addVar(const Bool *var) { m_bools.push_back(var); }
|
||||
Bool isInSection();
|
||||
protected:
|
||||
SectionList m_bools;
|
||||
};
|
||||
Bool SectionChecker::isInSection() {
|
||||
Bool ret = FALSE;
|
||||
for (SectionList::const_iterator it = m_bools.begin(); it != m_bools.end(); ++it)
|
||||
{
|
||||
ret = ret || **it;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GameSpyConfig::GameSpyConfig( AsciiString config ) :
|
||||
m_natRetryInterval(1000),
|
||||
m_natMaxManglerRetries(25),
|
||||
m_natManglerRetryInterval(300),
|
||||
m_natKeepaliveInterval(15000),
|
||||
m_natPortTimeout(10000),
|
||||
m_natRoundTimeout(10000),
|
||||
m_pingReps(1),
|
||||
m_pingTimeout(1000),
|
||||
m_pingCutoffGood(300),
|
||||
m_pingCutoffBad(600),
|
||||
m_restrictGamesToLobby(FALSE),
|
||||
m_qmBotID(0),
|
||||
m_qmChannel(0)
|
||||
{
|
||||
m_rankPoints[0] = 0;
|
||||
m_rankPoints[1] = 5;
|
||||
m_rankPoints[2] = 10;
|
||||
m_rankPoints[3] = 20;
|
||||
m_rankPoints[4] = 50;
|
||||
m_rankPoints[5] = 100;
|
||||
m_rankPoints[6] = 200;
|
||||
m_rankPoints[7] = 500;
|
||||
m_rankPoints[8] = 1000;
|
||||
m_rankPoints[9] = 2000;
|
||||
|
||||
AsciiString line;
|
||||
Bool inPingServers = FALSE;
|
||||
Bool inPingDuration = FALSE;
|
||||
Bool inQMMaps = FALSE;
|
||||
Bool inQMBot = FALSE;
|
||||
Bool inManglers = FALSE;
|
||||
Bool inVIP = FALSE;
|
||||
Bool inNAT = FALSE;
|
||||
Bool inCustom = FALSE;
|
||||
|
||||
SectionChecker sections;
|
||||
sections.addVar(&inPingServers);
|
||||
sections.addVar(&inPingDuration);
|
||||
sections.addVar(&inQMMaps);
|
||||
sections.addVar(&inQMBot);
|
||||
sections.addVar(&inManglers);
|
||||
sections.addVar(&inVIP);
|
||||
sections.addVar(&inNAT);
|
||||
sections.addVar(&inCustom);
|
||||
|
||||
while (config.nextToken(&line, "\n"))
|
||||
{
|
||||
if (line.getCharAt(line.getLength()-1) == '\r')
|
||||
line.removeLastChar(); // there is a trailing '\r'
|
||||
|
||||
line.trim();
|
||||
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
|
||||
if (!sections.isInSection() && line.compare("<PingServers>") == 0)
|
||||
{
|
||||
inPingServers = TRUE;
|
||||
}
|
||||
else if (inPingServers && line.compare("</PingServers>") == 0)
|
||||
{
|
||||
inPingServers = FALSE;
|
||||
}
|
||||
else if (!sections.isInSection() && line.compare("<PingDuration>") == 0)
|
||||
{
|
||||
inPingDuration = TRUE;
|
||||
}
|
||||
else if (inPingDuration && line.compare("</PingDuration>") == 0)
|
||||
{
|
||||
inPingDuration = FALSE;
|
||||
}
|
||||
else if (!sections.isInSection() && line.compare("<QMMaps>") == 0)
|
||||
{
|
||||
inQMMaps = TRUE;
|
||||
}
|
||||
else if (inQMMaps && line.compare("</QMMaps>") == 0)
|
||||
{
|
||||
inQMMaps = FALSE;
|
||||
}
|
||||
else if (!sections.isInSection() && line.compare("<Manglers>") == 0)
|
||||
{
|
||||
inManglers = TRUE;
|
||||
}
|
||||
else if (inManglers && line.compare("</Manglers>") == 0)
|
||||
{
|
||||
inManglers = FALSE;
|
||||
}
|
||||
else if (!sections.isInSection() && line.compare("<QMBot>") == 0)
|
||||
{
|
||||
inQMBot = TRUE;
|
||||
}
|
||||
else if (inQMBot && line.compare("</QMBot>") == 0)
|
||||
{
|
||||
inQMBot = FALSE;
|
||||
}
|
||||
else if (!sections.isInSection() && line.compare("<VIP>") == 0)
|
||||
{
|
||||
inVIP = TRUE;
|
||||
}
|
||||
else if (inVIP && line.compare("</VIP>") == 0)
|
||||
{
|
||||
inVIP = FALSE;
|
||||
}
|
||||
else if (!sections.isInSection() && line.compare("<NAT>") == 0)
|
||||
{
|
||||
inNAT = TRUE;
|
||||
}
|
||||
else if (inNAT && line.compare("</NAT>") == 0)
|
||||
{
|
||||
inNAT = FALSE;
|
||||
}
|
||||
else if (!sections.isInSection() && line.compare("<Custom>") == 0)
|
||||
{
|
||||
inCustom = TRUE;
|
||||
}
|
||||
else if (inCustom && line.compare("</Custom>") == 0)
|
||||
{
|
||||
inCustom = FALSE;
|
||||
}
|
||||
else if (inVIP)
|
||||
{
|
||||
line.toLower();
|
||||
if (line.getLength())
|
||||
{
|
||||
Int val = atoi(line.str());
|
||||
if (val > 0)
|
||||
m_vip.insert(val);
|
||||
}
|
||||
}
|
||||
else if (inPingServers)
|
||||
{
|
||||
line.toLower();
|
||||
m_pingServers.push_back(line);
|
||||
}
|
||||
else if (inPingDuration)
|
||||
{
|
||||
line.toLower();
|
||||
AsciiString key, val;
|
||||
if (line.nextToken(&key, " ="))
|
||||
{
|
||||
if (key == "reps")
|
||||
{
|
||||
if (line.nextToken(&val, " ="))
|
||||
{
|
||||
m_pingReps = atoi(val.str());
|
||||
}
|
||||
}
|
||||
else if (key == "timeout")
|
||||
{
|
||||
if (line.nextToken(&val, " ="))
|
||||
{
|
||||
m_pingTimeout = atoi(val.str());
|
||||
}
|
||||
}
|
||||
else if (key == "low")
|
||||
{
|
||||
if (line.nextToken(&val, " ="))
|
||||
{
|
||||
m_pingCutoffGood = atoi(val.str());
|
||||
}
|
||||
}
|
||||
else if (key == "med")
|
||||
{
|
||||
if (line.nextToken(&val, " ="))
|
||||
{
|
||||
m_pingCutoffBad = atoi(val.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (inManglers)
|
||||
{
|
||||
line.trim();
|
||||
line.toLower();
|
||||
AsciiString hostStr;
|
||||
AsciiString portStr;
|
||||
line.nextToken(&hostStr, ":");
|
||||
line.nextToken(&portStr, ":\n\r");
|
||||
if (hostStr.isNotEmpty() && portStr.isNotEmpty())
|
||||
{
|
||||
m_manglerHosts.push_back(hostStr);
|
||||
m_manglerPorts.push_back(atoi(portStr.str()));
|
||||
}
|
||||
}
|
||||
else if (inQMMaps)
|
||||
{
|
||||
line.toLower();
|
||||
AsciiString mapName;
|
||||
mapName.format("%s\\%s\\%s.map", TheMapCache->getMapDir().str(), line.str(), line.str());
|
||||
mapName = TheGameState->portableMapPathToRealMapPath(TheGameState->realMapPathToPortableMapPath(mapName));
|
||||
mapName.toLower();
|
||||
|
||||
// [SKB: Jul 01 2003 @ 6:43pm] :
|
||||
// German2 is missing some maps because of content. But, we need the m_qmMaps
|
||||
// to contain same number of strings as the Retail version so that the
|
||||
// QM Bot thinks that they have the same number of maps.
|
||||
#if 1
|
||||
m_qmMaps.push_back(mapName);
|
||||
#else
|
||||
const MapMetaData *md = TheMapCache->findMap(mapName);
|
||||
if (md)
|
||||
{
|
||||
m_qmMaps.push_back(mapName);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (inQMBot)
|
||||
{
|
||||
line.toLower();
|
||||
AsciiString key, val;
|
||||
if (line.nextToken(&key, " ="))
|
||||
{
|
||||
if (key == "id")
|
||||
{
|
||||
if (line.nextToken(&val, " ="))
|
||||
{
|
||||
m_qmBotID = atoi(val.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (inNAT)
|
||||
{
|
||||
line.toLower();
|
||||
AsciiString key, val;
|
||||
if (line.nextToken(&key, " ="))
|
||||
{
|
||||
if (key == "retryinterval")
|
||||
{
|
||||
if (line.nextToken(&val, " ="))
|
||||
{
|
||||
m_natRetryInterval = atoi(val.str());
|
||||
}
|
||||
}
|
||||
else if (key == "manglerretries")
|
||||
{
|
||||
if (line.nextToken(&val, " ="))
|
||||
{
|
||||
m_natMaxManglerRetries = atoi(val.str());
|
||||
}
|
||||
}
|
||||
else if (key == "manglerinterval")
|
||||
{
|
||||
if (line.nextToken(&val, " ="))
|
||||
{
|
||||
m_natManglerRetryInterval = atoi(val.str());
|
||||
}
|
||||
}
|
||||
else if (key == "keepaliveinterval")
|
||||
{
|
||||
if (line.nextToken(&val, " ="))
|
||||
{
|
||||
m_natKeepaliveInterval = atoi(val.str());
|
||||
}
|
||||
}
|
||||
else if (key == "porttimeout")
|
||||
{
|
||||
if (line.nextToken(&val, " ="))
|
||||
{
|
||||
m_natPortTimeout = atoi(val.str());
|
||||
}
|
||||
}
|
||||
else if (key == "roundtimeout")
|
||||
{
|
||||
if (line.nextToken(&val, " ="))
|
||||
{
|
||||
m_natRoundTimeout = atoi(val.str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("Unknown key '%s' = '%s' in NAT block of GameSpy Config\n", key.str(), val.str()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("Key '%s' missing val in NAT block of GameSpy Config\n", key.str()));
|
||||
}
|
||||
}
|
||||
else if (inCustom)
|
||||
{
|
||||
line.toLower();
|
||||
AsciiString key, val;
|
||||
if (line.nextToken(&key, " =") && line.nextToken(&val, " ="))
|
||||
{
|
||||
if (key == "restricted")
|
||||
{
|
||||
m_restrictGamesToLobby = atoi(val.str());
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("Unknown key '%s' = '%s' in Custom block of GameSpy Config\n", key.str(), val.str()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("Key '%s' missing val in Custom block of GameSpy Config\n", key.str()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_leftoverConfig.concat(line);
|
||||
m_leftoverConfig.concat('\n');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Int GameSpyConfig::getPointsForRank(Int rank)
|
||||
{
|
||||
if (rank >= MAX_RANKS) rank = MAX_RANKS-1;
|
||||
if (rank < 0) rank = 0;
|
||||
return m_rankPoints[rank];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Bool GameSpyConfig::getManglerLocation(Int index, AsciiString& host, UnsignedShort& port)
|
||||
{
|
||||
if (index < 0 || index >= m_manglerHosts.size())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
host = m_manglerHosts[index];
|
||||
port = m_manglerPorts[index];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Bool GameSpyConfig::isPlayerVIP(Int id)
|
||||
{
|
||||
std::set<Int>::const_iterator it = std::find(m_vip.begin(), m_vip.end(), id);
|
||||
return it != m_vip.end();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,527 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: LadderDefs.cpp //////////////////////////////////////////////////////
|
||||
// Generals ladder code
|
||||
// Author: Matthew D. Campbell, August 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/GameSpy/ThreadUtils.h"
|
||||
#include "GameNetwork/GameSpy/LadderDefs.h"
|
||||
#include "GameNetwork/GameSpy/PeerDefs.h"
|
||||
#include "GameNetwork/GameSpy/GSConfig.h"
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/File.h"
|
||||
#include "Common/FileSystem.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
LadderList *TheLadderList = NULL;
|
||||
|
||||
LadderInfo::LadderInfo()
|
||||
{
|
||||
playersPerTeam = 1;
|
||||
minWins = 0;
|
||||
maxWins = 0;
|
||||
randomMaps = TRUE;
|
||||
randomFactions = TRUE;
|
||||
validQM = TRUE;
|
||||
validCustom = FALSE;
|
||||
port = 0;
|
||||
submitReplay = FALSE;
|
||||
index = -1;
|
||||
}
|
||||
|
||||
static LadderInfo *parseLadder(AsciiString raw)
|
||||
{
|
||||
DEBUG_LOG(("Looking at ladder:\n%s\n", raw.str()));
|
||||
LadderInfo *lad = NULL;
|
||||
AsciiString line;
|
||||
while (raw.nextToken(&line, "\n"))
|
||||
{
|
||||
if (line.getCharAt(line.getLength()-1) == '\r')
|
||||
line.removeLastChar(); // there is a trailing '\r'
|
||||
|
||||
line.trim();
|
||||
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
|
||||
// woohoo! got a line!
|
||||
line.trim();
|
||||
if ( !lad && line.startsWith("<Ladder ") )
|
||||
{
|
||||
// start of a ladder def
|
||||
lad = NEW LadderInfo;
|
||||
|
||||
// fill in some info
|
||||
AsciiString tokenName, tokenAddr, tokenPort, tokenHomepage;
|
||||
line.removeLastChar(); // the '>'
|
||||
line = line.str() + 7; // the "<Ladder "
|
||||
line.nextToken(&tokenAddr, "\" ");
|
||||
line.nextToken(&tokenPort, " ");
|
||||
line.nextToken(&tokenHomepage, " ");
|
||||
|
||||
lad->name = MultiByteToWideCharSingleLine(tokenName.str()).c_str();
|
||||
while (lad->name.getLength() > 20)
|
||||
lad->name.removeLastChar(); // Per Harvard's request, ladder names are limited to 20 chars
|
||||
lad->address = tokenAddr;
|
||||
lad->port = atoi(tokenPort.str());
|
||||
lad->homepageURL = tokenHomepage;
|
||||
}
|
||||
else if ( lad && line.startsWith("Name ") )
|
||||
{
|
||||
lad->name = MultiByteToWideCharSingleLine(line.str() + 5).c_str();
|
||||
}
|
||||
else if ( lad && line.startsWith("Desc ") )
|
||||
{
|
||||
lad->description = MultiByteToWideCharSingleLine(line.str() + 5).c_str();
|
||||
}
|
||||
else if ( lad && line.startsWith("Loc ") )
|
||||
{
|
||||
lad->location = MultiByteToWideCharSingleLine(line.str() + 4).c_str();
|
||||
}
|
||||
else if ( lad && line.startsWith("TeamSize ") )
|
||||
{
|
||||
lad->playersPerTeam = atoi(line.str() + 9);
|
||||
}
|
||||
else if ( lad && line.startsWith("RandomMaps ") )
|
||||
{
|
||||
lad->randomMaps = atoi(line.str() + 11);
|
||||
}
|
||||
else if ( lad && line.startsWith("RandomFactions ") )
|
||||
{
|
||||
lad->randomFactions = atoi(line.str() + 15);
|
||||
}
|
||||
else if ( lad && line.startsWith("Faction ") )
|
||||
{
|
||||
AsciiString faction = line.str() + 8;
|
||||
AsciiStringList outStringList;
|
||||
ThePlayerTemplateStore->getAllSideStrings(&outStringList);
|
||||
|
||||
AsciiStringList::iterator aIt = std::find(outStringList.begin(), outStringList.end(), faction);
|
||||
if (aIt != outStringList.end())
|
||||
{
|
||||
// valid faction - now check for dupes
|
||||
aIt = std::find(lad->validFactions.begin(), lad->validFactions.end(), faction);
|
||||
if (aIt == lad->validFactions.end())
|
||||
{
|
||||
lad->validFactions.push_back(faction);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
else if ( lad && line.startsWith("QM ") )
|
||||
{
|
||||
lad->validQM = atoi(line.str() + 3);
|
||||
}
|
||||
else if ( lad && line.startsWith("Custom ") )
|
||||
{
|
||||
lad->validCustom = atoi(line.str() + 7);
|
||||
}
|
||||
*/
|
||||
else if ( lad && line.startsWith("MinWins ") )
|
||||
{
|
||||
lad->minWins = atoi(line.str() + 8);
|
||||
}
|
||||
else if ( lad && line.startsWith("MaxWins ") )
|
||||
{
|
||||
lad->maxWins = atoi(line.str() + 8);
|
||||
}
|
||||
else if ( lad && line.startsWith("CryptedPass ") )
|
||||
{
|
||||
lad->cryptedPassword = line.str() + 12;
|
||||
}
|
||||
else if ( lad && line.compare("</Ladder>") == 0 )
|
||||
{
|
||||
DEBUG_LOG(("Saw a ladder: name=%ls, addr=%s:%d, players=%dv%d, pass=%s, replay=%d, homepage=%s\n",
|
||||
lad->name.str(), lad->address.str(), lad->port, lad->playersPerTeam, lad->playersPerTeam, lad->cryptedPassword.str(),
|
||||
lad->submitReplay, lad->homepageURL.str()));
|
||||
// end of a ladder
|
||||
if (lad->playersPerTeam >= 1 && lad->playersPerTeam <= MAX_SLOTS/2)
|
||||
{
|
||||
if (lad->validFactions.size() == 0)
|
||||
{
|
||||
DEBUG_LOG(("No factions specified. Using all.\n"));
|
||||
lad->validFactions.push_back("America");
|
||||
lad->validFactions.push_back("China");
|
||||
lad->validFactions.push_back("GLA");
|
||||
}
|
||||
else
|
||||
{
|
||||
AsciiStringList validFactions = lad->validFactions;
|
||||
for (AsciiStringListIterator it = validFactions.begin(); it != validFactions.end(); ++it)
|
||||
{
|
||||
AsciiString faction = *it;
|
||||
AsciiString marker;
|
||||
marker.format("INI:Faction%s", faction.str());
|
||||
DEBUG_LOG(("Faction %s has marker %s corresponding to str %ls\n", faction.str(), marker.str(), TheGameText->fetch(marker).str()));
|
||||
}
|
||||
}
|
||||
|
||||
if (lad->validMaps.size() == 0)
|
||||
{
|
||||
DEBUG_LOG(("No maps specified. Using all.\n"));
|
||||
std::list<AsciiString> qmMaps = TheGameSpyConfig->getQMMaps();
|
||||
for (std::list<AsciiString>::const_iterator it = qmMaps.begin(); it != qmMaps.end(); ++it)
|
||||
{
|
||||
AsciiString mapName = *it;
|
||||
|
||||
// check sizes on the maps before allowing them
|
||||
const MapMetaData *md = TheMapCache->findMap(mapName);
|
||||
if (md && md->m_numPlayers >= lad->playersPerTeam*2)
|
||||
{
|
||||
lad->validMaps.push_back(mapName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return lad;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no maps? don't play on it!
|
||||
delete lad;
|
||||
lad = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if ( lad && line.startsWith("Map ") )
|
||||
{
|
||||
// valid map
|
||||
AsciiString mapName = line.str() + 4;
|
||||
mapName.trim();
|
||||
if (mapName.isNotEmpty())
|
||||
{
|
||||
mapName.format("%s\\%s\\%s.map", TheMapCache->getMapDir().str(), mapName.str(), mapName.str());
|
||||
mapName = TheGameState->portableMapPathToRealMapPath(TheGameState->realMapPathToPortableMapPath(mapName));
|
||||
mapName.toLower();
|
||||
std::list<AsciiString> qmMaps = TheGameSpyConfig->getQMMaps();
|
||||
if (std::find(qmMaps.begin(), qmMaps.end(), mapName) != qmMaps.end())
|
||||
{
|
||||
// check sizes on the maps before allowing them
|
||||
const MapMetaData *md = TheMapCache->findMap(mapName);
|
||||
if (md && md->m_numPlayers >= lad->playersPerTeam*2)
|
||||
lad->validMaps.push_back(mapName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// bad ladder - kill it
|
||||
delete lad;
|
||||
lad = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (lad)
|
||||
{
|
||||
delete lad;
|
||||
lad = NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LadderList::LadderList()
|
||||
{
|
||||
//Int profile = TheGameSpyInfo->getLocalProfileID();
|
||||
|
||||
AsciiString rawMotd = TheGameSpyConfig->getLeftoverConfig();
|
||||
AsciiString line;
|
||||
Bool inLadders = FALSE;
|
||||
Bool inSpecialLadders = FALSE;
|
||||
Bool inLadder = FALSE;
|
||||
LadderInfo *lad = NULL;
|
||||
Int index = 1;
|
||||
AsciiString rawLadder;
|
||||
|
||||
while (rawMotd.nextToken(&line, "\n"))
|
||||
{
|
||||
if (line.getCharAt(line.getLength()-1) == '\r')
|
||||
line.removeLastChar(); // there is a trailing '\r'
|
||||
|
||||
line.trim();
|
||||
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
|
||||
if (!inLadders && line.compare("<Ladders>") == 0)
|
||||
{
|
||||
inLadders = TRUE;
|
||||
rawLadder.clear();
|
||||
}
|
||||
else if (inLadders && line.compare("</Ladders>") == 0)
|
||||
{
|
||||
inLadders = FALSE;
|
||||
}
|
||||
else if (!inSpecialLadders && line.compare("<SpecialLadders>") == 0)
|
||||
{
|
||||
inSpecialLadders = TRUE;
|
||||
rawLadder.clear();
|
||||
}
|
||||
else if (inSpecialLadders && line.compare("</SpecialLadders>") == 0)
|
||||
{
|
||||
inSpecialLadders = FALSE;
|
||||
}
|
||||
else if (inLadders || inSpecialLadders)
|
||||
{
|
||||
if (line.startsWith("<Ladder ") && !inLadder)
|
||||
{
|
||||
inLadder = TRUE;
|
||||
rawLadder.clear();
|
||||
rawLadder.concat(line);
|
||||
rawLadder.concat('\n');
|
||||
}
|
||||
else if (line.compare("</Ladder>") == 0 && inLadder)
|
||||
{
|
||||
inLadder = FALSE;
|
||||
rawLadder.concat(line);
|
||||
rawLadder.concat('\n');
|
||||
if ((lad = parseLadder(rawLadder)) != NULL)
|
||||
{
|
||||
lad->index = index++;
|
||||
if (inLadders)
|
||||
{
|
||||
DEBUG_LOG(("Adding to standard ladders\n"));
|
||||
m_standardLadders.push_back(lad);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("Adding to special ladders\n"));
|
||||
m_specialLadders.push_back(lad);
|
||||
}
|
||||
}
|
||||
rawLadder.clear();
|
||||
}
|
||||
else if (inLadder)
|
||||
{
|
||||
rawLadder.concat(line);
|
||||
rawLadder.concat('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// look for local ladders
|
||||
loadLocalLadders();
|
||||
|
||||
DEBUG_LOG(("After looking for ladders, we have %d local, %d special && %d normal\n", m_localLadders.size(), m_specialLadders.size(), m_standardLadders.size()));
|
||||
}
|
||||
|
||||
LadderList::~LadderList()
|
||||
{
|
||||
LadderInfoList::iterator it;
|
||||
for (it = m_specialLadders.begin(); it != m_specialLadders.end(); it = m_specialLadders.begin())
|
||||
{
|
||||
delete *it;
|
||||
m_specialLadders.pop_front();
|
||||
}
|
||||
for (it = m_standardLadders.begin(); it != m_standardLadders.end(); it = m_standardLadders.begin())
|
||||
{
|
||||
delete *it;
|
||||
m_standardLadders.pop_front();
|
||||
}
|
||||
for (it = m_localLadders.begin(); it != m_localLadders.end(); it = m_localLadders.begin())
|
||||
{
|
||||
delete *it;
|
||||
m_localLadders.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
const LadderInfo* LadderList::findLadder( const AsciiString& addr, UnsignedShort port )
|
||||
{
|
||||
LadderInfoList::const_iterator cit;
|
||||
|
||||
for (cit = m_specialLadders.begin(); cit != m_specialLadders.end(); ++cit)
|
||||
{
|
||||
const LadderInfo *li = *cit;
|
||||
if (li->address == addr && li->port == port)
|
||||
{
|
||||
return li;
|
||||
}
|
||||
}
|
||||
|
||||
for (cit = m_standardLadders.begin(); cit != m_standardLadders.end(); ++cit)
|
||||
{
|
||||
const LadderInfo *li = *cit;
|
||||
if (li->address == addr && li->port == port)
|
||||
{
|
||||
return li;
|
||||
}
|
||||
}
|
||||
|
||||
for (cit = m_localLadders.begin(); cit != m_localLadders.end(); ++cit)
|
||||
{
|
||||
const LadderInfo *li = *cit;
|
||||
if (li->address == addr && li->port == port)
|
||||
{
|
||||
return li;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const LadderInfo* LadderList::findLadderByIndex( Int index )
|
||||
{
|
||||
if (index == 0)
|
||||
return NULL;
|
||||
|
||||
LadderInfoList::const_iterator cit;
|
||||
|
||||
for (cit = m_specialLadders.begin(); cit != m_specialLadders.end(); ++cit)
|
||||
{
|
||||
const LadderInfo *li = *cit;
|
||||
if (li->index == index)
|
||||
{
|
||||
return li;
|
||||
}
|
||||
}
|
||||
|
||||
for (cit = m_standardLadders.begin(); cit != m_standardLadders.end(); ++cit)
|
||||
{
|
||||
const LadderInfo *li = *cit;
|
||||
if (li->index == index)
|
||||
{
|
||||
return li;
|
||||
}
|
||||
}
|
||||
|
||||
for (cit = m_localLadders.begin(); cit != m_localLadders.end(); ++cit)
|
||||
{
|
||||
const LadderInfo *li = *cit;
|
||||
if (li->index == index)
|
||||
{
|
||||
return li;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const LadderInfoList* LadderList::getSpecialLadders( void )
|
||||
{
|
||||
return &m_specialLadders;
|
||||
}
|
||||
|
||||
const LadderInfoList* LadderList::getStandardLadders( void )
|
||||
{
|
||||
return &m_standardLadders;
|
||||
}
|
||||
|
||||
const LadderInfoList* LadderList::getLocalLadders( void )
|
||||
{
|
||||
return &m_localLadders;
|
||||
}
|
||||
|
||||
void LadderList::loadLocalLadders( void )
|
||||
{
|
||||
AsciiString dirname;
|
||||
dirname.format("%sGeneralsOnline\\Ladders\\", TheGlobalData->getPath_UserData().str());
|
||||
FilenameList filenameList;
|
||||
TheFileSystem->getFileListInDirectory(dirname, AsciiString("*.ini"), filenameList, TRUE);
|
||||
|
||||
Int index = -1;
|
||||
|
||||
FilenameList::iterator it = filenameList.begin();
|
||||
while (it != filenameList.end())
|
||||
{
|
||||
AsciiString filename = *it;
|
||||
DEBUG_LOG(("Looking at possible ladder info file '%s'\n", filename.str()));
|
||||
filename.toLower();
|
||||
checkLadder( filename, index-- );
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void LadderList::checkLadder( AsciiString fname, Int index )
|
||||
{
|
||||
File *fp = TheFileSystem->openFile(fname.str(), File::READ | File::TEXT);
|
||||
char buf[1024];
|
||||
AsciiString rawData;
|
||||
if (fp)
|
||||
{
|
||||
Int len;
|
||||
while (!fp->eof())
|
||||
{
|
||||
len = fp->read(buf, 1023);
|
||||
buf[len] = 0;
|
||||
buf[1023] = 0;
|
||||
rawData.concat(buf);
|
||||
}
|
||||
fp->close();
|
||||
fp = NULL;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("Read %d bytes from '%s'\n", rawData.getLength(), fname.str()));
|
||||
if (rawData.isEmpty())
|
||||
return;
|
||||
|
||||
LadderInfo *li = parseLadder(rawData);
|
||||
if (!li)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// sanity check
|
||||
if (li->address.isEmpty())
|
||||
{
|
||||
DEBUG_LOG(("Bailing because of li->address.isEmpty()\n"));
|
||||
delete li;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!li->port)
|
||||
{
|
||||
DEBUG_LOG(("Bailing because of !li->port\n"));
|
||||
delete li;
|
||||
return;
|
||||
}
|
||||
|
||||
if (li->validMaps.size() == 0)
|
||||
{
|
||||
DEBUG_LOG(("Bailing because of li->validMaps.size() == 0\n"));
|
||||
delete li;
|
||||
return;
|
||||
}
|
||||
|
||||
li->index = index;
|
||||
|
||||
// ladders are QM-only at this point, which kinda invalidates the whole concept of local ladders. Oh well.
|
||||
li->validQM = FALSE; // no local ladders in QM
|
||||
li->validCustom = FALSE;
|
||||
|
||||
//for (Int i=0; i<4; ++i)
|
||||
// fname.removeLastChar(); // remove .lad
|
||||
//li->name = UnicodeString(MultiByteToWideCharSingleLine(fname.reverseFind('\\')+1).c_str());
|
||||
|
||||
DEBUG_LOG(("Adding local ladder %ls\n", li->name.str()));
|
||||
m_localLadders.push_back(li);
|
||||
}
|
||||
@@ -0,0 +1,859 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// FILE: LobbyUtils.cpp
|
||||
// Author: Matthew D. Campbell, Sept 2002
|
||||
// Description: GameSpy lobby utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GameEngine.h"
|
||||
#include "Common/MultiplayerSettings.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/Version.h"
|
||||
#include "GameClient/AnimateWindowManager.h"
|
||||
#include "GameClient/WindowLayout.h"
|
||||
#include "GameClient/Gadget.h"
|
||||
#include "GameClient/Image.h"
|
||||
#include "GameClient/Shell.h"
|
||||
#include "GameClient/KeyDefs.h"
|
||||
#include "GameClient/GameWindowManager.h"
|
||||
#include "GameClient/GadgetComboBox.h"
|
||||
#include "GameClient/GadgetListBox.h"
|
||||
#include "GameClient/GadgetTextEntry.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
#include "GameClient/MessageBox.h"
|
||||
#include "GameClient/Mouse.h"
|
||||
#include "GameNetwork/GameSpyOverlay.h"
|
||||
|
||||
#include "GameClient/LanguageFilter.h"
|
||||
#include "GameNetwork/GameSpy/BuddyDefs.h"
|
||||
#include "GameNetwork/GameSpy/LadderDefs.h"
|
||||
#include "GameNetwork/GameSpy/LobbyUtils.h"
|
||||
#include "GameNetwork/GameSpy/PeerDefs.h"
|
||||
#include "GameNetwork/GameSpy/PeerThread.h"
|
||||
#include "GameNetwork/GameSpy/PersistentStorageDefs.h"
|
||||
#include "GameNetwork/GameSpy/GSConfig.h"
|
||||
|
||||
#include "Common/STLTypedefs.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
|
||||
static enum {
|
||||
COLUMN_NAME = 0,
|
||||
COLUMN_MAP,
|
||||
COLUMN_LADDER,
|
||||
COLUMN_NUMPLAYERS,
|
||||
COLUMN_PASSWORD,
|
||||
COLUMN_OBSERVER,
|
||||
COLUMN_PING,
|
||||
};
|
||||
|
||||
static NameKeyType buttonSortAlphaID = NAMEKEY_INVALID;
|
||||
static NameKeyType buttonSortPingID = NAMEKEY_INVALID;
|
||||
static NameKeyType buttonSortBuddiesID = NAMEKEY_INVALID;
|
||||
static NameKeyType windowSortAlphaID = NAMEKEY_INVALID;
|
||||
static NameKeyType windowSortPingID = NAMEKEY_INVALID;
|
||||
static NameKeyType windowSortBuddiesID = NAMEKEY_INVALID;
|
||||
|
||||
static GameWindow *buttonSortAlpha = NULL;
|
||||
static GameWindow *buttonSortPing = NULL;
|
||||
static GameWindow *buttonSortBuddies = NULL;
|
||||
static GameWindow *windowSortAlpha = NULL;
|
||||
static GameWindow *windowSortPing = NULL;
|
||||
static GameWindow *windowSortBuddies = NULL;
|
||||
|
||||
static GameSortType theGameSortType = GAMESORT_ALPHA_ASCENDING;
|
||||
static Bool sortBuddies = TRUE;
|
||||
static void showSortIcons(void)
|
||||
{
|
||||
if (windowSortAlpha && windowSortPing)
|
||||
{
|
||||
switch(theGameSortType)
|
||||
{
|
||||
case GAMESORT_ALPHA_ASCENDING:
|
||||
windowSortAlpha->winHide(FALSE);
|
||||
windowSortAlpha->winEnable(TRUE);
|
||||
windowSortPing->winHide(TRUE);
|
||||
break;
|
||||
case GAMESORT_ALPHA_DESCENDING:
|
||||
windowSortAlpha->winHide(FALSE);
|
||||
windowSortAlpha->winEnable(FALSE);
|
||||
windowSortPing->winHide(TRUE);
|
||||
break;
|
||||
case GAMESORT_PING_ASCENDING:
|
||||
windowSortPing->winHide(FALSE);
|
||||
windowSortPing->winEnable(TRUE);
|
||||
windowSortAlpha->winHide(TRUE);
|
||||
break;
|
||||
case GAMESORT_PING_DESCENDING:
|
||||
windowSortPing->winHide(FALSE);
|
||||
windowSortPing->winEnable(FALSE);
|
||||
windowSortAlpha->winHide(TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sortBuddies)
|
||||
{
|
||||
if (windowSortBuddies)
|
||||
{
|
||||
windowSortBuddies->winHide(FALSE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (windowSortBuddies)
|
||||
{
|
||||
windowSortBuddies->winHide(TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
void setSortMode( GameSortType sortType ) { theGameSortType = sortType; showSortIcons(); RefreshGameListBoxes(); }
|
||||
void sortByBuddies( Bool doSort ) { sortBuddies = doSort; showSortIcons(); RefreshGameListBoxes(); }
|
||||
|
||||
Bool HandleSortButton( NameKeyType sortButton )
|
||||
{
|
||||
if (sortButton == buttonSortBuddiesID)
|
||||
{
|
||||
sortByBuddies( !sortBuddies );
|
||||
return TRUE;
|
||||
}
|
||||
else if (sortButton == buttonSortAlphaID)
|
||||
{
|
||||
if (theGameSortType == GAMESORT_ALPHA_ASCENDING)
|
||||
{
|
||||
setSortMode(GAMESORT_ALPHA_DESCENDING);
|
||||
}
|
||||
else
|
||||
{
|
||||
setSortMode(GAMESORT_ALPHA_ASCENDING);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
else if (sortButton == buttonSortPingID)
|
||||
{
|
||||
if (theGameSortType == GAMESORT_PING_ASCENDING)
|
||||
{
|
||||
setSortMode(GAMESORT_PING_DESCENDING);
|
||||
}
|
||||
else
|
||||
{
|
||||
setSortMode(GAMESORT_PING_ASCENDING);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// window ids ------------------------------------------------------------------------------
|
||||
static NameKeyType parentID = NAMEKEY_INVALID;
|
||||
//static NameKeyType parentGameListSmallID = NAMEKEY_INVALID;
|
||||
static NameKeyType parentGameListLargeID = NAMEKEY_INVALID;
|
||||
static NameKeyType listboxLobbyGamesSmallID = NAMEKEY_INVALID;
|
||||
static NameKeyType listboxLobbyGamesLargeID = NAMEKEY_INVALID;
|
||||
//static NameKeyType listboxLobbyGameInfoID = NAMEKEY_INVALID;
|
||||
|
||||
// Window Pointers ------------------------------------------------------------------------
|
||||
static GameWindow *parent = NULL;
|
||||
//static GameWindow *parentGameListSmall = NULL;
|
||||
static GameWindow *parentGameListLarge = NULL;
|
||||
//GameWindow *listboxLobbyGamesSmall = NULL;
|
||||
GameWindow *listboxLobbyGamesLarge = NULL;
|
||||
//GameWindow *listboxLobbyGameInfo = NULL;
|
||||
|
||||
static const Image *pingImages[3] = { NULL, NULL, NULL };
|
||||
|
||||
static void gameTooltip(GameWindow *window,
|
||||
WinInstanceData *instData,
|
||||
UnsignedInt mouse)
|
||||
{
|
||||
Int x, y, row, col;
|
||||
x = LOLONGTOSHORT(mouse);
|
||||
y = HILONGTOSHORT(mouse);
|
||||
|
||||
GadgetListBoxGetEntryBasedOnXY(window, x, y, row, col);
|
||||
|
||||
if (row == -1 || col == -1)
|
||||
{
|
||||
TheMouse->setCursorTooltip( UnicodeString::TheEmptyString);//TheGameText->fetch("TOOLTIP:GamesBeingFormed") );
|
||||
return;
|
||||
}
|
||||
|
||||
Int gameID = (Int)GadgetListBoxGetItemData(window, row, 0);
|
||||
GameSpyStagingRoom *room = TheGameSpyInfo->findStagingRoomByID(gameID);
|
||||
if (!room)
|
||||
{
|
||||
TheMouse->setCursorTooltip( TheGameText->fetch("TOOLTIP:UnknownGame") );
|
||||
return;
|
||||
}
|
||||
|
||||
if (col == COLUMN_PING)
|
||||
{
|
||||
#ifdef DEBUG_LOGGING
|
||||
UnicodeString s;
|
||||
s.format(L"Ping is %d ms (cutoffs are %d ms and %d ms\n%hs local pings\n%hs remote pings",
|
||||
room->getPingAsInt(), TheGameSpyConfig->getPingCutoffGood(), TheGameSpyConfig->getPingCutoffBad(),
|
||||
TheGameSpyInfo->getPingString().str(), room->getPingString().str()
|
||||
);
|
||||
TheMouse->setCursorTooltip( s, 10, NULL, 2.0f ); // the text and width are the only params used. the others are the default values.
|
||||
#else
|
||||
TheMouse->setCursorTooltip( TheGameText->fetch("TOOLTIP:PingInfo"), 10, NULL, 2.0f ); // the text and width are the only params used. the others are the default values.
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (col == COLUMN_NUMPLAYERS)
|
||||
{
|
||||
TheMouse->setCursorTooltip( TheGameText->fetch("TOOLTIP:NumberOfPlayers"), 10, NULL, 2.0f ); // the text and width are the only params used. the others are the default values.
|
||||
return;
|
||||
}
|
||||
if (col == COLUMN_PASSWORD)
|
||||
{
|
||||
if (room->getHasPassword())
|
||||
{
|
||||
UnicodeString checkTooltip =TheGameText->fetch("TOOTIP:Password");
|
||||
if(!checkTooltip.compare(L"Password required to joing game"))
|
||||
checkTooltip.set(L"Password required to join game");
|
||||
TheMouse->setCursorTooltip( checkTooltip, 10, NULL, 2.0f ); // the text and width are the only params used. the others are the default values.
|
||||
}
|
||||
else
|
||||
TheMouse->setCursorTooltip( UnicodeString::TheEmptyString );
|
||||
return;
|
||||
}
|
||||
|
||||
UnicodeString tooltip;
|
||||
|
||||
UnicodeString mapName;
|
||||
const MapMetaData *md = TheMapCache->findMap(room->getMap());
|
||||
if (md)
|
||||
{
|
||||
mapName = md->m_displayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *start = room->getMap().reverseFind('\\');
|
||||
if (start)
|
||||
{
|
||||
++start;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = room->getMap().str();
|
||||
}
|
||||
mapName.translate( start );
|
||||
}
|
||||
UnicodeString tmp;
|
||||
tooltip.format(TheGameText->fetch("TOOLTIP:GameInfoGameName"), room->getGameName().str());
|
||||
if (room->getLadderPort() != 0)
|
||||
{
|
||||
const LadderInfo *linfo = TheLadderList->findLadder(room->getLadderIP(), room->getLadderPort());
|
||||
if (linfo)
|
||||
{
|
||||
tmp.format(TheGameText->fetch("TOOLTIP:GameInfoLadderName"), linfo->name.str());
|
||||
tooltip.concat(tmp);
|
||||
}
|
||||
}
|
||||
if (room->getExeCRC() != TheGlobalData->m_exeCRC || room->getIniCRC() != TheGlobalData->m_iniCRC)
|
||||
{
|
||||
tmp.format(TheGameText->fetch("TOOLTIP:InvalidGameVersion"), mapName.str());
|
||||
tooltip.concat(tmp);
|
||||
}
|
||||
tmp.format(TheGameText->fetch("TOOLTIP:GameInfoMap"), mapName.str());
|
||||
tooltip.concat(tmp);
|
||||
|
||||
AsciiString aPlayer;
|
||||
UnicodeString player;
|
||||
Int numPlayers = 0;
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
GameSpyGameSlot *slot = room->getGameSpySlot(i);
|
||||
if (i == 0 && (!slot || !slot->isHuman()))
|
||||
{
|
||||
DEBUG_CRASH(("About to tooltip a non-hosted game!\n"));
|
||||
}
|
||||
if (slot && slot->isHuman())
|
||||
{
|
||||
tmp.format(TheGameText->fetch("TOOLTIP:GameInfoPlayer"), slot->getName().str(), slot->getWins(), slot->getLosses());
|
||||
tooltip.concat(tmp);
|
||||
++numPlayers;
|
||||
}
|
||||
else if (slot && slot->isAI())
|
||||
{
|
||||
++numPlayers;
|
||||
switch(slot->getState())
|
||||
{
|
||||
case SLOT_EASY_AI:
|
||||
tooltip.concat(L'\n');
|
||||
tooltip.concat(TheGameText->fetch("GUI:EasyAI"));
|
||||
break;
|
||||
case SLOT_MED_AI:
|
||||
tooltip.concat(L'\n');
|
||||
tooltip.concat(TheGameText->fetch("GUI:MediumAI"));
|
||||
break;
|
||||
case SLOT_BRUTAL_AI:
|
||||
tooltip.concat(L'\n');
|
||||
tooltip.concat(TheGameText->fetch("GUI:HardAI"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
DEBUG_ASSERTCRASH(numPlayers, ("Tooltipping a 0-player game!\n"));
|
||||
|
||||
TheMouse->setCursorTooltip( tooltip, 10, NULL, 2.0f ); // the text and width are the only params used. the others are the default values.
|
||||
}
|
||||
|
||||
static Bool isSmall = TRUE;
|
||||
|
||||
GameWindow *GetGameListBox( void )
|
||||
{
|
||||
return listboxLobbyGamesLarge;
|
||||
}
|
||||
|
||||
GameWindow *GetGameInfoListBox( void )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NameKeyType GetGameListBoxID( void )
|
||||
{
|
||||
return listboxLobbyGamesLargeID;
|
||||
}
|
||||
|
||||
NameKeyType GetGameInfoListBoxID( void )
|
||||
{
|
||||
return NAMEKEY_INVALID;
|
||||
}
|
||||
|
||||
void GrabWindowInfo( void )
|
||||
{
|
||||
isSmall = TRUE;
|
||||
parentID = NAMEKEY( "WOLCustomLobby.wnd:WOLLobbyMenuParent" );
|
||||
parent = TheWindowManager->winGetWindowFromId(NULL, parentID);
|
||||
|
||||
pingImages[0] = TheMappedImageCollection->findImageByName("Ping03");
|
||||
pingImages[1] = TheMappedImageCollection->findImageByName("Ping02");
|
||||
pingImages[2] = TheMappedImageCollection->findImageByName("Ping01");
|
||||
DEBUG_ASSERTCRASH(pingImages[0], ("Can't find ping image!"));
|
||||
DEBUG_ASSERTCRASH(pingImages[1], ("Can't find ping image!"));
|
||||
DEBUG_ASSERTCRASH(pingImages[2], ("Can't find ping image!"));
|
||||
|
||||
// parentGameListSmallID = NAMEKEY( "WOLCustomLobby.wnd:ParentGameListSmall" );
|
||||
// parentGameListSmall = TheWindowManager->winGetWindowFromId(NULL, parentGameListSmallID);
|
||||
|
||||
parentGameListLargeID = NAMEKEY( "WOLCustomLobby.wnd:ParentGameListLarge" );
|
||||
parentGameListLarge = TheWindowManager->winGetWindowFromId(NULL, parentGameListLargeID);
|
||||
|
||||
listboxLobbyGamesSmallID = NAMEKEY( "WOLCustomLobby.wnd:ListboxGames" );
|
||||
// listboxLobbyGamesSmall = TheWindowManager->winGetWindowFromId(NULL, listboxLobbyGamesSmallID);
|
||||
// listboxLobbyGamesSmall->winSetTooltipFunc(gameTooltip);
|
||||
|
||||
listboxLobbyGamesLargeID = NAMEKEY( "WOLCustomLobby.wnd:ListboxGamesLarge" );
|
||||
listboxLobbyGamesLarge = TheWindowManager->winGetWindowFromId(NULL, listboxLobbyGamesLargeID);
|
||||
listboxLobbyGamesLarge->winSetTooltipFunc(gameTooltip);
|
||||
//
|
||||
// listboxLobbyGameInfoID = NAMEKEY( "WOLCustomLobby.wnd:ListboxGameInfo" );
|
||||
// listboxLobbyGameInfo = TheWindowManager->winGetWindowFromId(NULL, listboxLobbyGameInfoID);
|
||||
|
||||
buttonSortAlphaID = NAMEKEY("WOLCustomLobby.wnd:ButtonSortAlpha");
|
||||
buttonSortPingID = NAMEKEY("WOLCustomLobby.wnd:ButtonSortPing");
|
||||
buttonSortBuddiesID = NAMEKEY("WOLCustomLobby.wnd:ButtonSortBuddies");
|
||||
windowSortAlphaID = NAMEKEY("WOLCustomLobby.wnd:WindowSortAlpha");
|
||||
windowSortPingID = NAMEKEY("WOLCustomLobby.wnd:WindowSortPing");
|
||||
windowSortBuddiesID = NAMEKEY("WOLCustomLobby.wnd:WindowSortBuddies");
|
||||
|
||||
buttonSortAlpha = TheWindowManager->winGetWindowFromId(parent, buttonSortAlphaID);
|
||||
buttonSortPing = TheWindowManager->winGetWindowFromId(parent, buttonSortPingID);
|
||||
buttonSortBuddies = TheWindowManager->winGetWindowFromId(parent, buttonSortBuddiesID);
|
||||
windowSortAlpha = TheWindowManager->winGetWindowFromId(parent, windowSortAlphaID);
|
||||
windowSortPing = TheWindowManager->winGetWindowFromId(parent, windowSortPingID);
|
||||
windowSortBuddies = TheWindowManager->winGetWindowFromId(parent, windowSortBuddiesID);
|
||||
|
||||
showSortIcons();
|
||||
}
|
||||
|
||||
void ReleaseWindowInfo( void )
|
||||
{
|
||||
isSmall = TRUE;
|
||||
parent = NULL;
|
||||
// parentGameListSmall = NULL;
|
||||
parentGameListLarge = NULL;
|
||||
// listboxLobbyGamesSmall = NULL;
|
||||
listboxLobbyGamesLarge = NULL;
|
||||
// listboxLobbyGameInfo = NULL;
|
||||
|
||||
buttonSortAlpha = NULL;
|
||||
buttonSortPing = NULL;
|
||||
buttonSortBuddies = NULL;
|
||||
windowSortAlpha = NULL;
|
||||
windowSortPing = NULL;
|
||||
windowSortBuddies = NULL;
|
||||
}
|
||||
|
||||
typedef std::set<GameSpyStagingRoom *> BuddyGameSet;
|
||||
static BuddyGameSet *theBuddyGames = NULL;
|
||||
static void populateBuddyGames(void)
|
||||
{
|
||||
BuddyInfoMap *m = TheGameSpyInfo->getBuddyMap();
|
||||
theBuddyGames = NEW BuddyGameSet;
|
||||
if (!m)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (BuddyInfoMap::const_iterator bit = m->begin(); bit != m->end(); ++bit)
|
||||
{
|
||||
BuddyInfo info = bit->second;
|
||||
if (info.m_status == GP_STAGING)
|
||||
{
|
||||
StagingRoomMap *srm = TheGameSpyInfo->getStagingRoomList();
|
||||
for (StagingRoomMap::iterator srmIt = srm->begin(); srmIt != srm->end(); ++srmIt)
|
||||
{
|
||||
GameSpyStagingRoom *game = srmIt->second;
|
||||
game->cleanUpSlotPointers();
|
||||
const GameSpyGameSlot *slot = game->getGameSpySlot(0);
|
||||
if (slot && slot->getName() == info.m_locationString)
|
||||
{
|
||||
theBuddyGames->insert(game);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void clearBuddyGames(void)
|
||||
{
|
||||
if (theBuddyGames)
|
||||
delete theBuddyGames;
|
||||
theBuddyGames = NULL;
|
||||
}
|
||||
|
||||
struct GameSortStruct
|
||||
{
|
||||
bool operator()(GameSpyStagingRoom *g1, GameSpyStagingRoom *g2)
|
||||
{
|
||||
// sort CRC mismatches to the bottom
|
||||
Bool g1Good = (g1->getExeCRC() != TheGlobalData->m_exeCRC || g1->getIniCRC() != TheGlobalData->m_iniCRC);
|
||||
Bool g2Good = (g1->getExeCRC() != TheGlobalData->m_exeCRC || g1->getIniCRC() != TheGlobalData->m_iniCRC);
|
||||
if ( g1Good ^ g2Good )
|
||||
{
|
||||
return g1Good;
|
||||
}
|
||||
|
||||
// sort games with private ladders to the bottom
|
||||
Bool g1UnknownLadder = (g1->getLadderPort() && TheLadderList->findLadder(g1->getLadderIP(), g1->getLadderPort()) == NULL);
|
||||
Bool g2UnknownLadder = (g2->getLadderPort() && TheLadderList->findLadder(g2->getLadderIP(), g2->getLadderPort()) == NULL);
|
||||
if ( g1UnknownLadder ^ g2UnknownLadder )
|
||||
{
|
||||
return g2UnknownLadder;
|
||||
}
|
||||
|
||||
// sort full games to the bottom
|
||||
Bool g1Full = (g1->getNumNonObserverPlayers() == g1->getMaxPlayers() || g1->getNumPlayers() == MAX_SLOTS);
|
||||
Bool g2Full = (g2->getNumNonObserverPlayers() == g2->getMaxPlayers() || g2->getNumPlayers() == MAX_SLOTS);
|
||||
if ( g1Full ^ g2Full )
|
||||
{
|
||||
return g2Full;
|
||||
}
|
||||
|
||||
if (sortBuddies)
|
||||
{
|
||||
Bool g1HasBuddies = (theBuddyGames->find(g1) != theBuddyGames->end());
|
||||
Bool g2HasBuddies = (theBuddyGames->find(g2) != theBuddyGames->end());
|
||||
if ( g1HasBuddies ^ g2HasBuddies )
|
||||
{
|
||||
return g1HasBuddies;
|
||||
}
|
||||
}
|
||||
|
||||
switch(theGameSortType)
|
||||
{
|
||||
case GAMESORT_ALPHA_ASCENDING:
|
||||
return wcsicmp(g1->getGameName().str(), g2->getGameName().str()) < 0;
|
||||
break;
|
||||
case GAMESORT_ALPHA_DESCENDING:
|
||||
return wcsicmp(g1->getGameName().str(),g2->getGameName().str()) > 0;
|
||||
break;
|
||||
case GAMESORT_PING_ASCENDING:
|
||||
return g1->getPingAsInt() < g2->getPingAsInt();
|
||||
break;
|
||||
case GAMESORT_PING_DESCENDING:
|
||||
return g1->getPingAsInt() > g2->getPingAsInt();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static Int insertGame( GameWindow *win, GameSpyStagingRoom *game, Bool showMap )
|
||||
{
|
||||
game->cleanUpSlotPointers();
|
||||
Color gameColor = GameSpyColor[GSCOLOR_GAME];
|
||||
if (game->getNumNonObserverPlayers() == game->getMaxPlayers() || game->getNumPlayers() == MAX_SLOTS)
|
||||
{
|
||||
gameColor = GameSpyColor[GSCOLOR_GAME_FULL];
|
||||
}
|
||||
if (game->getExeCRC() != TheGlobalData->m_exeCRC || game->getIniCRC() != TheGlobalData->m_iniCRC)
|
||||
{
|
||||
gameColor = GameSpyColor[GSCOLOR_GAME_CRCMISMATCH];
|
||||
}
|
||||
UnicodeString gameName = game->getGameName();
|
||||
|
||||
if(TheGameSpyInfo->getDisallowAsianText())
|
||||
{
|
||||
const WideChar *buff = gameName.str();
|
||||
Int length = gameName.getLength();
|
||||
for(Int i = 0; i < length; ++i)
|
||||
{
|
||||
if(buff[i] >= 256)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if(TheGameSpyInfo->getDisallowNonAsianText())
|
||||
{
|
||||
const WideChar *buff = gameName.str();
|
||||
Int length = gameName.getLength();
|
||||
Bool hasUnicode = FALSE;
|
||||
for(Int i = 0; i < length; ++i)
|
||||
{
|
||||
if(buff[i] >= 256)
|
||||
{
|
||||
hasUnicode = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!hasUnicode)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Int index = GadgetListBoxAddEntryText(win, game->getGameName(), gameColor, -1, COLUMN_NAME);
|
||||
GadgetListBoxSetItemData(win, (void *)game->getID(), index);
|
||||
|
||||
UnicodeString s;
|
||||
|
||||
if (showMap)
|
||||
{
|
||||
UnicodeString mapName;
|
||||
const MapMetaData *md = TheMapCache->findMap(game->getMap());
|
||||
if (md)
|
||||
{
|
||||
mapName = md->m_displayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *start = game->getMap().reverseFind('\\');
|
||||
if (start)
|
||||
{
|
||||
++start;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = game->getMap().str();
|
||||
}
|
||||
mapName.translate( start );
|
||||
}
|
||||
GadgetListBoxAddEntryText(win, mapName, gameColor, index, COLUMN_MAP);
|
||||
|
||||
const LadderInfo * li = TheLadderList->findLadder(game->getLadderIP(), game->getLadderPort());
|
||||
if (li)
|
||||
{
|
||||
GadgetListBoxAddEntryText(win, li->name, gameColor, index, COLUMN_LADDER);
|
||||
}
|
||||
else if (game->getLadderPort())
|
||||
{
|
||||
GadgetListBoxAddEntryText(win, TheGameText->fetch("GUI:UnknownLadder"), gameColor, index, COLUMN_LADDER);
|
||||
}
|
||||
else
|
||||
{
|
||||
GadgetListBoxAddEntryText(win, TheGameText->fetch("GUI:NoLadder"), gameColor, index, COLUMN_LADDER);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GadgetListBoxAddEntryText(win, UnicodeString(L" "), gameColor, index, COLUMN_MAP);
|
||||
GadgetListBoxAddEntryText(win, UnicodeString(L" "), gameColor, index, COLUMN_LADDER);
|
||||
}
|
||||
|
||||
s.format(L"%d/%d", game->getReportedNumPlayers(), game->getReportedMaxPlayers());
|
||||
GadgetListBoxAddEntryText(win, s, gameColor, index, COLUMN_NUMPLAYERS);
|
||||
|
||||
if (game->getHasPassword())
|
||||
{
|
||||
const Image *img = TheMappedImageCollection->findImageByName("Password");
|
||||
Int width = 10, height = 10;
|
||||
if (img)
|
||||
{
|
||||
width = img->getImageWidth();
|
||||
height = img->getImageHeight();
|
||||
}
|
||||
GadgetListBoxAddEntryImage(win, img, index, COLUMN_PASSWORD, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
GadgetListBoxAddEntryText(win, UnicodeString(L" "), gameColor, index, COLUMN_PASSWORD);
|
||||
}
|
||||
|
||||
if (game->getAllowObservers())
|
||||
{
|
||||
const Image *img = TheMappedImageCollection->findImageByName("Observer");
|
||||
GadgetListBoxAddEntryImage(win, img, index, COLUMN_OBSERVER);
|
||||
}
|
||||
else
|
||||
{
|
||||
GadgetListBoxAddEntryText(win, UnicodeString(L" "), gameColor, index, COLUMN_OBSERVER);
|
||||
}
|
||||
|
||||
s.format(L"%d", game->getPingAsInt());
|
||||
GadgetListBoxAddEntryText(win, s, gameColor, index, COLUMN_PING);
|
||||
Int ping = game->getPingAsInt();
|
||||
Int width = 10, height = 10;
|
||||
if (pingImages[0])
|
||||
{
|
||||
width = pingImages[0]->getImageWidth();
|
||||
height = pingImages[0]->getImageHeight();
|
||||
}
|
||||
// CLH picking an arbitrary number for our ping display
|
||||
if (ping < TheGameSpyConfig->getPingCutoffGood())
|
||||
{
|
||||
GadgetListBoxAddEntryImage(win, pingImages[0], index, COLUMN_PING, width, height);
|
||||
}
|
||||
else if (ping < TheGameSpyConfig->getPingCutoffBad())
|
||||
{
|
||||
GadgetListBoxAddEntryImage(win, pingImages[1], index, COLUMN_PING, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
GadgetListBoxAddEntryImage(win, pingImages[2], index, COLUMN_PING, width, height);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void RefreshGameListBox( GameWindow *win, Bool showMap )
|
||||
{
|
||||
if (!win)
|
||||
return;
|
||||
|
||||
// save off selection
|
||||
Int selectedIndex = -1;
|
||||
Int indexToSelect = -1;
|
||||
Int selectedID = 0;
|
||||
GadgetListBoxGetSelected(win, &selectedIndex);
|
||||
if (selectedIndex != -1 )
|
||||
{
|
||||
selectedID = (Int)GadgetListBoxGetItemData(win, selectedIndex);
|
||||
}
|
||||
int prevPos = GadgetListBoxGetTopVisibleEntry( win );
|
||||
|
||||
// empty listbox
|
||||
GadgetListBoxReset(win);
|
||||
|
||||
// sort our games
|
||||
typedef std::multiset<GameSpyStagingRoom *, GameSortStruct> SortedGameList;
|
||||
SortedGameList sgl;
|
||||
StagingRoomMap *srm = TheGameSpyInfo->getStagingRoomList();
|
||||
populateBuddyGames();
|
||||
for (StagingRoomMap::iterator srmIt = srm->begin(); srmIt != srm->end(); ++srmIt)
|
||||
{
|
||||
sgl.insert(srmIt->second);
|
||||
}
|
||||
|
||||
// populate listbox
|
||||
for (SortedGameList::iterator sglIt = sgl.begin(); sglIt != sgl.end(); ++sglIt)
|
||||
{
|
||||
GameSpyStagingRoom *game = *sglIt;
|
||||
if (game)
|
||||
{
|
||||
Int index = insertGame(win, game, showMap);
|
||||
if (game->getID() == selectedID)
|
||||
{
|
||||
indexToSelect = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearBuddyGames();
|
||||
|
||||
// restore selection
|
||||
GadgetListBoxSetSelected(win, indexToSelect); // even for -1, so we can disable the 'Join Game' button
|
||||
// if(prevPos > 10)
|
||||
GadgetListBoxSetTopVisibleEntry( win, prevPos );//+ 1
|
||||
|
||||
if (indexToSelect < 0 && selectedID)
|
||||
{
|
||||
TheWindowManager->winSetLoneWindow(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshGameInfoListBox( GameWindow *mainWin, GameWindow *win )
|
||||
{
|
||||
// if (!mainWin || !win)
|
||||
// return;
|
||||
//
|
||||
// GadgetListBoxReset(win);
|
||||
//
|
||||
// Int selected = -1;
|
||||
// GadgetListBoxGetSelected(mainWin, &selected);
|
||||
// if (selected < 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// Int selectedID = (Int)GadgetListBoxGetItemData(mainWin, selected);
|
||||
// if (selectedID < 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// StagingRoomMap *srm = TheGameSpyInfo->getStagingRoomList();
|
||||
// StagingRoomMap::iterator srmIt = srm->find(selectedID);
|
||||
// if (srmIt != srm->end())
|
||||
// {
|
||||
// GameSpyStagingRoom *theRoom = srmIt->second;
|
||||
// theRoom->cleanUpSlotPointers();
|
||||
//
|
||||
// // game name
|
||||
//// GadgetListBoxAddEntryText(listboxLobbyGameInfo, theRoom->getGameName(), GameSpyColor[GSCOLOR_DEFAULT], -1);
|
||||
//
|
||||
// const LadderInfo * li = TheLadderList->findLadder(theRoom->getLadderIP(), theRoom->getLadderPort());
|
||||
// if (li)
|
||||
// {
|
||||
// UnicodeString tmp;
|
||||
// tmp.format(TheGameText->fetch("TOOLTIP:LadderName"), li->name.str());
|
||||
// GadgetListBoxAddEntryText(listboxLobbyGameInfo, tmp, GameSpyColor[GSCOLOR_DEFAULT], -1);
|
||||
// }
|
||||
// else if (theRoom->getLadderPort())
|
||||
// {
|
||||
// GadgetListBoxAddEntryText(listboxLobbyGameInfo, TheGameText->fetch("TOOLTIP:UnknownLadder"), GameSpyColor[GSCOLOR_DEFAULT], -1);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// GadgetListBoxAddEntryText(listboxLobbyGameInfo, TheGameText->fetch("TOOLTIP:NoLadder"), GameSpyColor[GSCOLOR_DEFAULT], -1);
|
||||
// }
|
||||
//
|
||||
// if (theRoom->getExeCRC() != TheGlobalData->m_exeCRC || theRoom->getIniCRC() != TheGlobalData->m_iniCRC)
|
||||
// {
|
||||
// GadgetListBoxAddEntryText(listboxLobbyGameInfo, TheGameText->fetch("TOOLTIP:InvalidGameVersionSingleLine"), GameSpyColor[GSCOLOR_DEFAULT], -1);
|
||||
// }
|
||||
//
|
||||
// // map name
|
||||
// UnicodeString mapName;
|
||||
// const MapMetaData *md = TheMapCache->findMap(theRoom->getMap());
|
||||
// if (md)
|
||||
// {
|
||||
// mapName = md->m_displayName;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const char *start = theRoom->getMap().reverseFind('\\');
|
||||
// if (start)
|
||||
// {
|
||||
// ++start;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// start = theRoom->getMap().str();
|
||||
// }
|
||||
// mapName.translate( start );
|
||||
// }
|
||||
//
|
||||
// GadgetListBoxAddEntryText(listboxLobbyGameInfo, mapName, GameSpyColor[GSCOLOR_DEFAULT], -1);
|
||||
//
|
||||
// // player list (rank, win/loss, side)
|
||||
// for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
// {
|
||||
// const GameSpyGameSlot *slot = theRoom->getGameSpySlot(i);
|
||||
// if (slot && slot->isHuman())
|
||||
// {
|
||||
// UnicodeString theName, theRating, thePlayerTemplate;
|
||||
// Int colorIdx = slot->getColor();
|
||||
// theName = slot->getName();
|
||||
// theRating.format(L" (%d-%d)", slot->getWins(), slot->getLosses());
|
||||
// const PlayerTemplate * pt = ThePlayerTemplateStore->getNthPlayerTemplate(slot->getPlayerTemplate());
|
||||
// if (pt)
|
||||
// {
|
||||
// thePlayerTemplate = pt->getDisplayName();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// thePlayerTemplate = TheGameText->fetch("GUI:Random");
|
||||
// }
|
||||
//
|
||||
// UnicodeString theText;
|
||||
// theText.format(L"%ls - %ls - %ls", theName.str(), thePlayerTemplate.str(), theRating.str());
|
||||
//
|
||||
// Int theColor = GameSpyColor[GSCOLOR_DEFAULT];
|
||||
// const MultiplayerColorDefinition *mcd = TheMultiplayerSettings->getColor(colorIdx);
|
||||
// if (mcd)
|
||||
// {
|
||||
// theColor = mcd->getColor();
|
||||
// }
|
||||
//
|
||||
// GadgetListBoxAddEntryText(listboxLobbyGameInfo, theText, theColor, -1);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
void RefreshGameListBoxes( void )
|
||||
{
|
||||
GameWindow *main = GetGameListBox();
|
||||
GameWindow *info = GetGameInfoListBox();
|
||||
|
||||
RefreshGameListBox( main, (info == NULL) );
|
||||
|
||||
if (info)
|
||||
{
|
||||
RefreshGameInfoListBox( main, info );
|
||||
}
|
||||
}
|
||||
|
||||
void ToggleGameListType( void )
|
||||
{
|
||||
isSmall = !isSmall;
|
||||
if(isSmall)
|
||||
{
|
||||
parentGameListLarge->winHide(TRUE);
|
||||
// parentGameListSmall->winHide(FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
parentGameListLarge->winHide(FALSE);
|
||||
// parentGameListSmall->winHide(TRUE);
|
||||
}
|
||||
|
||||
RefreshGameListBoxes();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,895 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// FILE: MainMenuUtils.cpp
|
||||
// Author: Matthew D. Campbell, Sept 2002
|
||||
// Description: GameSpy version check, patch download, etc utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
//#include "Common/Registry.h"
|
||||
#include "Common/UserPreferences.h"
|
||||
#include "Common/Version.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/MessageBox.h"
|
||||
#include "GameClient/Shell.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
|
||||
#include "GameClient/ShellHooks.h"
|
||||
|
||||
#include "GameSpy/ghttp/ghttp.h"
|
||||
|
||||
#include "GameNetwork/DownloadManager.h"
|
||||
#include "GameNetwork/GameSpy/BuddyThread.h"
|
||||
#include "GameNetwork/GameSpy/MainMenuUtils.h"
|
||||
#include "GameNetwork/GameSpy/PeerDefs.h"
|
||||
#include "GameNetwork/GameSpy/PeerThread.h"
|
||||
|
||||
#include "WWDownload/Registry.h"
|
||||
#include "WWDownload/URLBuilder.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Bool checkingForPatchBeforeGameSpy = FALSE;
|
||||
static Int checksLeftBeforeOnline = 0;
|
||||
static Int timeThroughOnline = 0; // used to avoid having old callbacks cause problems
|
||||
static Bool mustDownloadPatch = FALSE;
|
||||
static Bool cantConnectBeforeOnline = FALSE;
|
||||
static std::list<QueuedDownload> queuedDownloads;
|
||||
|
||||
static char *MOTDBuffer = NULL;
|
||||
static char *configBuffer = NULL;
|
||||
GameWindow *onlineCancelWindow = NULL;
|
||||
|
||||
static Bool s_asyncDNSThreadDone = TRUE;
|
||||
static Bool s_asyncDNSThreadSucceeded = FALSE;
|
||||
static Bool s_asyncDNSLookupInProgress = FALSE;
|
||||
static HANDLE s_asyncDNSThreadHandle = NULL;
|
||||
enum {
|
||||
LOOKUP_INPROGRESS,
|
||||
LOOKUP_FAILED,
|
||||
LOOKUP_SUCCEEDED,
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void startOnline( void );
|
||||
static void reallyStartPatchCheck( void );
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// someone has hit a button allowing downloads to start
|
||||
void StartDownloadingPatches( void )
|
||||
{
|
||||
if (queuedDownloads.empty())
|
||||
{
|
||||
HandleCanceledDownload();
|
||||
return;
|
||||
}
|
||||
|
||||
WindowLayout *layout;
|
||||
layout = TheWindowManager->winCreateLayout( AsciiString( "Menus/DownloadMenu.wnd" ) );
|
||||
layout->runInit();
|
||||
layout->hide( FALSE );
|
||||
layout->bringForward();
|
||||
HandleCanceledDownload(FALSE);
|
||||
DEBUG_ASSERTCRASH(TheDownloadManager, ("No download manager!"));
|
||||
if (TheDownloadManager)
|
||||
{
|
||||
std::list<QueuedDownload>::iterator it = queuedDownloads.begin();
|
||||
while (it != queuedDownloads.end())
|
||||
{
|
||||
QueuedDownload q = *it;
|
||||
TheDownloadManager->queueFileForDownload(q.server, q.userName, q.password,
|
||||
q.file, q.localFile, q.regKey, q.tryResume);
|
||||
queuedDownloads.pop_front();
|
||||
it = queuedDownloads.begin();
|
||||
}
|
||||
TheDownloadManager->downloadNextQueuedFile();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// user agrees to patch before going online
|
||||
static void patchBeforeOnlineCallback( void )
|
||||
{
|
||||
StartDownloadingPatches();
|
||||
}
|
||||
|
||||
// user doesn't want to patch before going online
|
||||
static void noPatchBeforeOnlineCallback( void )
|
||||
{
|
||||
queuedDownloads.clear();
|
||||
if (mustDownloadPatch || cantConnectBeforeOnline)
|
||||
{
|
||||
// go back to normal
|
||||
HandleCanceledDownload();
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear out unneeded downloads and go on
|
||||
startOnline();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Bool hasWriteAccess()
|
||||
{
|
||||
const char* filename = "PatchAccessTest.txt";
|
||||
|
||||
remove(filename);
|
||||
|
||||
int handle = _open( filename, _O_CREAT | _O_RDWR, _S_IREAD | _S_IWRITE);
|
||||
if (handle == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_close(handle);
|
||||
remove(filename);
|
||||
|
||||
unsigned int val;
|
||||
if (!GetUnsignedIntFromRegistry("", "Version", val))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetUnsignedIntInRegistry("", "Version", val))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void startOnline( void )
|
||||
{
|
||||
checkingForPatchBeforeGameSpy = FALSE;
|
||||
|
||||
DEBUG_ASSERTCRASH(checksLeftBeforeOnline==0, ("starting online with pending callbacks"));
|
||||
if (onlineCancelWindow)
|
||||
{
|
||||
TheWindowManager->winDestroy(onlineCancelWindow);
|
||||
onlineCancelWindow = NULL;
|
||||
}
|
||||
|
||||
if (cantConnectBeforeOnline)
|
||||
{
|
||||
MessageBoxOk(TheGameText->fetch("GUI:CannotConnectToServservTitle"),
|
||||
TheGameText->fetch("GUI:CannotConnectToServserv"),
|
||||
noPatchBeforeOnlineCallback);
|
||||
return;
|
||||
}
|
||||
if (queuedDownloads.size())
|
||||
{
|
||||
if (!hasWriteAccess())
|
||||
{
|
||||
MessageBoxOk(TheGameText->fetch("GUI:Error"),
|
||||
TheGameText->fetch("GUI:MustHaveAdminRights"),
|
||||
noPatchBeforeOnlineCallback);
|
||||
}
|
||||
else if (mustDownloadPatch)
|
||||
{
|
||||
MessageBoxOkCancel(TheGameText->fetch("GUI:PatchAvailable"),
|
||||
TheGameText->fetch("GUI:MustPatchForOnline"),
|
||||
patchBeforeOnlineCallback, noPatchBeforeOnlineCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxYesNo(TheGameText->fetch("GUI:PatchAvailable"),
|
||||
TheGameText->fetch("GUI:CanPatchForOnline"),
|
||||
patchBeforeOnlineCallback, noPatchBeforeOnlineCallback);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
TheScriptEngine->signalUIInteract(TheShellHookNames[SHELL_SCRIPT_HOOK_MAIN_MENU_ONLINE_SELECTED]);
|
||||
|
||||
DEBUG_ASSERTCRASH( !TheGameSpyBuddyMessageQueue, ("TheGameSpyBuddyMessageQueue exists!") );
|
||||
DEBUG_ASSERTCRASH( !TheGameSpyPeerMessageQueue, ("TheGameSpyPeerMessageQueue exists!") );
|
||||
DEBUG_ASSERTCRASH( !TheGameSpyInfo, ("TheGameSpyInfo exists!") );
|
||||
SetUpGameSpy(MOTDBuffer, configBuffer);
|
||||
if (MOTDBuffer)
|
||||
{
|
||||
delete[] MOTDBuffer;
|
||||
MOTDBuffer = NULL;
|
||||
}
|
||||
if (configBuffer)
|
||||
{
|
||||
delete[] configBuffer;
|
||||
configBuffer = NULL;
|
||||
}
|
||||
|
||||
#ifdef ALLOW_NON_PROFILED_LOGIN
|
||||
UserPreferences pref;
|
||||
pref.load("GameSpyLogin.ini");
|
||||
UserPreferences::const_iterator it = pref.find("useProfiles");
|
||||
if (it != pref.end() && it->second.compareNoCase("yes") == 0)
|
||||
#endif ALLOW_NON_PROFILED_LOGIN
|
||||
TheShell->push( AsciiString("Menus/GameSpyLoginProfile.wnd") );
|
||||
#ifdef ALLOW_NON_PROFILED_LOGIN
|
||||
else
|
||||
TheShell->push( AsciiString("Menus/GameSpyLoginQuick.wnd") );
|
||||
#endif ALLOW_NON_PROFILED_LOGIN
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void queuePatch(Bool mandatory, AsciiString downloadURL)
|
||||
{
|
||||
QueuedDownload q;
|
||||
Bool success = TRUE;
|
||||
|
||||
AsciiString connectionType;
|
||||
success &= downloadURL.nextToken(&connectionType, ":");
|
||||
|
||||
AsciiString server;
|
||||
success &= downloadURL.nextToken(&server, ":/");
|
||||
|
||||
AsciiString user;
|
||||
success &= downloadURL.nextToken(&user, ":@");
|
||||
|
||||
AsciiString pass;
|
||||
success &= downloadURL.nextToken(&pass, "@/");
|
||||
|
||||
AsciiString filePath;
|
||||
success &= downloadURL.nextToken(&filePath, "");
|
||||
|
||||
if (!success && user.isNotEmpty())
|
||||
{
|
||||
// no user/pass combo - move the file into it's proper place
|
||||
filePath = user;
|
||||
user = ""; // LFeenanEA - Credentials removed as per Security requirements
|
||||
pass = "";
|
||||
success = TRUE;
|
||||
}
|
||||
|
||||
AsciiString fileStr = filePath;
|
||||
const char *s = filePath.reverseFind('/');
|
||||
if (s)
|
||||
fileStr = s+1;
|
||||
AsciiString fileName = "patches\\";
|
||||
fileName.concat(fileStr);
|
||||
|
||||
DEBUG_LOG(("download URL split: %d [%s] [%s] [%s] [%s] [%s] [%s]\n",
|
||||
success, connectionType.str(), server.str(), user.str(), pass.str(),
|
||||
filePath.str(), fileName.str()));
|
||||
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
q.file = filePath;
|
||||
q.localFile = fileName;
|
||||
q.password = pass;
|
||||
q.regKey = "";
|
||||
q.server = server;
|
||||
q.tryResume = TRUE;
|
||||
q.userName = user;
|
||||
|
||||
std::list<QueuedDownload>::iterator it = queuedDownloads.begin();
|
||||
while (it != queuedDownloads.end())
|
||||
{
|
||||
if (it->localFile == q.localFile)
|
||||
return; // don't add it if it exists already (because we can check multiple times)
|
||||
++it;
|
||||
}
|
||||
|
||||
queuedDownloads.push_back(q);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static GHTTPBool motdCallback( GHTTPRequest request, GHTTPResult result,
|
||||
char * buffer, int bufferLen, void * param )
|
||||
{
|
||||
Int run = (Int)param;
|
||||
if (run != timeThroughOnline)
|
||||
{
|
||||
DEBUG_CRASH(("Old callback being called!"));
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
if (MOTDBuffer)
|
||||
{
|
||||
delete[] MOTDBuffer;
|
||||
MOTDBuffer = NULL;
|
||||
}
|
||||
|
||||
MOTDBuffer = NEW char[bufferLen];
|
||||
memcpy(MOTDBuffer, buffer, bufferLen);
|
||||
MOTDBuffer[bufferLen-1] = 0;
|
||||
|
||||
--checksLeftBeforeOnline;
|
||||
DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
|
||||
if (onlineCancelWindow && !checksLeftBeforeOnline)
|
||||
{
|
||||
TheWindowManager->winDestroy(onlineCancelWindow);
|
||||
onlineCancelWindow = NULL;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("------- Got MOTD before going online -------\n"));
|
||||
DEBUG_LOG(("%s\n", (MOTDBuffer)?MOTDBuffer:""));
|
||||
DEBUG_LOG(("--------------------------------------------\n"));
|
||||
|
||||
if (!checksLeftBeforeOnline)
|
||||
startOnline();
|
||||
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static GHTTPBool configCallback( GHTTPRequest request, GHTTPResult result,
|
||||
char * buffer, int bufferLen, void * param )
|
||||
{
|
||||
Int run = (Int)param;
|
||||
if (run != timeThroughOnline)
|
||||
{
|
||||
DEBUG_CRASH(("Old callback being called!"));
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
if (configBuffer)
|
||||
{
|
||||
delete[] configBuffer;
|
||||
configBuffer = NULL;
|
||||
}
|
||||
|
||||
if (result != GHTTPSuccess || bufferLen < 100)
|
||||
{
|
||||
if (!checkingForPatchBeforeGameSpy)
|
||||
return GHTTPTrue;
|
||||
--checksLeftBeforeOnline;
|
||||
if (onlineCancelWindow && !checksLeftBeforeOnline)
|
||||
{
|
||||
TheWindowManager->winDestroy(onlineCancelWindow);
|
||||
onlineCancelWindow = NULL;
|
||||
}
|
||||
cantConnectBeforeOnline = TRUE;
|
||||
if (!checksLeftBeforeOnline)
|
||||
{
|
||||
startOnline();
|
||||
}
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
configBuffer = NEW char[bufferLen];
|
||||
memcpy(configBuffer, buffer, bufferLen);
|
||||
configBuffer[bufferLen-1] = 0;
|
||||
|
||||
AsciiString fname;
|
||||
fname.format("%sGeneralsOnline\\Config.txt", TheGlobalData->getPath_UserData().str());
|
||||
FILE *fp = fopen(fname.str(), "wb");
|
||||
if (fp)
|
||||
{
|
||||
fwrite(configBuffer, bufferLen, 1, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
--checksLeftBeforeOnline;
|
||||
DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
|
||||
if (onlineCancelWindow && !checksLeftBeforeOnline)
|
||||
{
|
||||
TheWindowManager->winDestroy(onlineCancelWindow);
|
||||
onlineCancelWindow = NULL;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("Got Config before going online\n"));
|
||||
|
||||
if (!checksLeftBeforeOnline)
|
||||
startOnline();
|
||||
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static GHTTPBool configHeadCallback( GHTTPRequest request, GHTTPResult result,
|
||||
char * buffer, int bufferLen, void * param )
|
||||
{
|
||||
Int run = (Int)param;
|
||||
if (run != timeThroughOnline)
|
||||
{
|
||||
DEBUG_CRASH(("Old callback being called!"));
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("HTTP head resp: res=%d, len=%d, buf=[%s]\n", result, bufferLen, buffer));
|
||||
|
||||
if (result == GHTTPSuccess)
|
||||
{
|
||||
DEBUG_LOG(("Headers are [%s]\n", ghttpGetHeaders( request )));
|
||||
|
||||
AsciiString headers(ghttpGetHeaders( request ));
|
||||
AsciiString line;
|
||||
while (headers.nextToken(&line, "\n\r"))
|
||||
{
|
||||
AsciiString key, val;
|
||||
line.nextToken(&key, ": ");
|
||||
line.nextToken(&val, ": \r\n");
|
||||
|
||||
if (key.compare("Content-Length") == 0 && val.isNotEmpty())
|
||||
{
|
||||
Int serverLen = atoi(val.str());
|
||||
Int fileLen = 0;
|
||||
AsciiString fname;
|
||||
fname.format("%sGeneralsOnline\\Config.txt", TheGlobalData->getPath_UserData().str());
|
||||
FILE *fp = fopen(fname.str(), "rb");
|
||||
if (fp)
|
||||
{
|
||||
fseek(fp, 0, SEEK_END);
|
||||
fileLen = ftell(fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (serverLen == fileLen)
|
||||
{
|
||||
// we don't need to download the MOTD again
|
||||
--checksLeftBeforeOnline;
|
||||
DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
|
||||
if (onlineCancelWindow && !checksLeftBeforeOnline)
|
||||
{
|
||||
TheWindowManager->winDestroy(onlineCancelWindow);
|
||||
onlineCancelWindow = NULL;
|
||||
}
|
||||
|
||||
if (configBuffer)
|
||||
{
|
||||
delete[] configBuffer;
|
||||
configBuffer = NULL;
|
||||
}
|
||||
|
||||
AsciiString fname;
|
||||
fname.format("%sGeneralsOnline\\Config.txt", TheGlobalData->getPath_UserData().str());
|
||||
FILE *fp = fopen(fname.str(), "rb");
|
||||
if (fp)
|
||||
{
|
||||
configBuffer = NEW char[fileLen];
|
||||
fread(configBuffer, fileLen, 1, fp);
|
||||
configBuffer[fileLen-1] = 0;
|
||||
fclose(fp);
|
||||
|
||||
DEBUG_LOG(("Got Config before going online\n"));
|
||||
|
||||
if (!checksLeftBeforeOnline)
|
||||
startOnline();
|
||||
|
||||
return GHTTPTrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we need to download the MOTD again
|
||||
std::string gameURL, mapURL;
|
||||
std::string configURL, motdURL;
|
||||
FormatURLFromRegistry(gameURL, mapURL, configURL, motdURL);
|
||||
ghttpGet( configURL.c_str(), GHTTPFalse, configCallback, param );
|
||||
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static GHTTPBool gamePatchCheckCallback( GHTTPRequest request, GHTTPResult result, char * buffer, int bufferLen, void * param )
|
||||
{
|
||||
Int run = (Int)param;
|
||||
if (run != timeThroughOnline)
|
||||
{
|
||||
DEBUG_CRASH(("Old callback being called!"));
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
--checksLeftBeforeOnline;
|
||||
DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
|
||||
|
||||
DEBUG_LOG(("Result=%d, buffer=[%s], len=%d\n", result, buffer, bufferLen));
|
||||
if (result != GHTTPSuccess)
|
||||
{
|
||||
if (!checkingForPatchBeforeGameSpy)
|
||||
return GHTTPTrue;
|
||||
cantConnectBeforeOnline = TRUE;
|
||||
if (!checksLeftBeforeOnline)
|
||||
{
|
||||
startOnline();
|
||||
}
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
AsciiString message = buffer;
|
||||
AsciiString line;
|
||||
while (message.nextToken(&line, "\r\n"))
|
||||
{
|
||||
AsciiString type, req, url;
|
||||
Bool ok = TRUE;
|
||||
ok &= line.nextToken(&type, " ");
|
||||
ok &= line.nextToken(&req, " ");
|
||||
ok &= line.nextToken(&url, " ");
|
||||
if (ok && type == "patch")
|
||||
{
|
||||
DEBUG_LOG(("Saw a patch: %d/[%s]\n", atoi(req.str()), url.str()));
|
||||
queuePatch( atoi(req.str()), url );
|
||||
if (atoi(req.str()))
|
||||
{
|
||||
mustDownloadPatch = TRUE;
|
||||
}
|
||||
}
|
||||
else if (ok && type == "server")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (!checksLeftBeforeOnline)
|
||||
{
|
||||
startOnline();
|
||||
}
|
||||
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CancelPatchCheckCallbackAndReopenDropdown( void )
|
||||
{
|
||||
HandleCanceledDownload();
|
||||
CancelPatchCheckCallback();
|
||||
}
|
||||
|
||||
void CancelPatchCheckCallback( void )
|
||||
{
|
||||
s_asyncDNSLookupInProgress = FALSE;
|
||||
HandleCanceledDownload(FALSE); // don't dropdown
|
||||
checkingForPatchBeforeGameSpy = FALSE;
|
||||
checksLeftBeforeOnline = 0;
|
||||
if (onlineCancelWindow)
|
||||
{
|
||||
TheWindowManager->winDestroy(onlineCancelWindow);
|
||||
onlineCancelWindow = NULL;
|
||||
}
|
||||
queuedDownloads.clear();
|
||||
if (MOTDBuffer)
|
||||
{
|
||||
delete[] MOTDBuffer;
|
||||
MOTDBuffer = NULL;
|
||||
}
|
||||
if (configBuffer)
|
||||
{
|
||||
delete[] configBuffer;
|
||||
configBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static GHTTPBool overallStatsCallback( GHTTPRequest request, GHTTPResult result, char * buffer, int bufferLen, void * param )
|
||||
{
|
||||
DEBUG_LOG(("overallStatsCallback() - Result=%d, len=%d\n", result, bufferLen));
|
||||
if (result != GHTTPSuccess)
|
||||
{
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
OverallStats USA, China, GLA;
|
||||
AsciiString message = buffer;
|
||||
|
||||
Int state = STATS_MAX; // STATS_MAX == none
|
||||
AsciiString line;
|
||||
OverallStats *stats = NULL;
|
||||
while (message.nextToken(&line, "\n"))
|
||||
{
|
||||
line.trim();
|
||||
line.toLower();
|
||||
if (strstr(line.str(), "today"))
|
||||
{
|
||||
state = STATS_TODAY;
|
||||
}
|
||||
else if (strstr(line.str(), "yesterday"))
|
||||
{
|
||||
state = STATS_YESTERDAY;
|
||||
}
|
||||
else if (strstr(line.str(), "all time"))
|
||||
{
|
||||
state = STATS_ALLTIME;
|
||||
}
|
||||
else if (strstr(line.str(), "last week"))
|
||||
{
|
||||
state = STATS_LASTWEEK;
|
||||
}
|
||||
else if (state != STATS_MAX && strstr(line.str(), "usa"))
|
||||
{
|
||||
stats = &USA;
|
||||
}
|
||||
else if (state != STATS_MAX && strstr(line.str(), "china"))
|
||||
{
|
||||
stats = &China;
|
||||
}
|
||||
else if (state != STATS_MAX && strstr(line.str(), "gla"))
|
||||
{
|
||||
stats = &GLA;
|
||||
}
|
||||
|
||||
if (stats)
|
||||
{
|
||||
AsciiString totalLine, winsLine, lossesLine;
|
||||
message.nextToken(&totalLine, "\n");
|
||||
message.nextToken(&winsLine, "\n");
|
||||
message.nextToken(&lossesLine, "\n");
|
||||
while (totalLine.isNotEmpty() && !isdigit(totalLine.getCharAt(0)))
|
||||
{
|
||||
totalLine = totalLine.str()+1;
|
||||
}
|
||||
while (winsLine.isNotEmpty() && !isdigit(winsLine.getCharAt(0)))
|
||||
{
|
||||
winsLine = winsLine.str()+1;
|
||||
}
|
||||
while (lossesLine.isNotEmpty() && !isdigit(lossesLine.getCharAt(0)))
|
||||
{
|
||||
lossesLine = lossesLine.str()+1;
|
||||
}
|
||||
if (totalLine.isNotEmpty() && winsLine.isNotEmpty() && lossesLine.isNotEmpty())
|
||||
{
|
||||
stats->wins[state] = atoi(winsLine.str());
|
||||
stats->losses[state] = atoi(lossesLine.str());
|
||||
}
|
||||
|
||||
stats = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HandleOverallStats(USA, China, GLA);
|
||||
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static GHTTPBool numPlayersOnlineCallback( GHTTPRequest request, GHTTPResult result, char * buffer, int bufferLen, void * param )
|
||||
{
|
||||
DEBUG_LOG(("numPlayersOnlineCallback() - Result=%d, buffer=[%s], len=%d\n", result, buffer, bufferLen));
|
||||
if (result != GHTTPSuccess)
|
||||
{
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
AsciiString message = buffer;
|
||||
message.trim();
|
||||
const char *s = message.reverseFind('\\');
|
||||
if (!s)
|
||||
{
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
if (*s == '\\')
|
||||
++s;
|
||||
|
||||
DEBUG_LOG(("Message was '%s', trimmed to '%s'=%d\n", buffer, s, atoi(s)));
|
||||
HandleNumPlayersOnline(atoi(s));
|
||||
|
||||
return GHTTPTrue;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CheckOverallStats( void )
|
||||
{
|
||||
ghttpGet("http://gamestats.gamespy.com/ccgenerals/display.html",
|
||||
GHTTPFalse, overallStatsCallback, NULL);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CheckNumPlayersOnline( void )
|
||||
{
|
||||
ghttpGet("http://launch.gamespyarcade.com/software/launch/arcadecount2.dll?svcname=ccgenerals",
|
||||
GHTTPFalse, numPlayersOnlineCallback, NULL);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DWORD WINAPI asyncGethostbynameThreadFunc( void * szName )
|
||||
{
|
||||
HOSTENT *he = gethostbyname( (const char *)szName );
|
||||
|
||||
if (he)
|
||||
{
|
||||
s_asyncDNSThreadSucceeded = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_asyncDNSThreadSucceeded = FALSE;
|
||||
}
|
||||
|
||||
s_asyncDNSThreadDone = TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int asyncGethostbyname(char * szName)
|
||||
{
|
||||
static int stat = 0;
|
||||
static unsigned long threadid;
|
||||
|
||||
if( stat == 0 )
|
||||
{
|
||||
/* Kick off gethostname thread */
|
||||
s_asyncDNSThreadDone = FALSE;
|
||||
s_asyncDNSThreadHandle = CreateThread( NULL, 0, asyncGethostbynameThreadFunc, szName, 0, &threadid );
|
||||
|
||||
if( s_asyncDNSThreadHandle == NULL )
|
||||
{
|
||||
return( LOOKUP_FAILED );
|
||||
}
|
||||
stat = 1;
|
||||
}
|
||||
if( stat == 1 )
|
||||
{
|
||||
if( s_asyncDNSThreadDone )
|
||||
{
|
||||
/* Thread finished */
|
||||
stat = 0;
|
||||
s_asyncDNSLookupInProgress = FALSE;
|
||||
s_asyncDNSThreadHandle = NULL;
|
||||
return( (s_asyncDNSThreadSucceeded)?LOOKUP_SUCCEEDED:LOOKUP_FAILED );
|
||||
}
|
||||
}
|
||||
|
||||
return( LOOKUP_INPROGRESS );
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GameSpy's HTTP SDK has had at least 1 crash bug, so we're going to just bail and
|
||||
// never try again if they crash us. We won't be able to get back online again (we'll
|
||||
// time out) but at least we'll live.
|
||||
static Bool isHttpOk = TRUE;
|
||||
|
||||
void HTTPThinkWrapper( void )
|
||||
{
|
||||
if (s_asyncDNSLookupInProgress)
|
||||
{
|
||||
Int ret = asyncGethostbyname("servserv.generals.ea.com");
|
||||
switch(ret)
|
||||
{
|
||||
case LOOKUP_FAILED:
|
||||
cantConnectBeforeOnline = TRUE;
|
||||
startOnline();
|
||||
break;
|
||||
case LOOKUP_SUCCEEDED:
|
||||
reallyStartPatchCheck();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isHttpOk)
|
||||
{
|
||||
try
|
||||
{
|
||||
ghttpThink();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
isHttpOk = FALSE; // we can't abort the login, since we might be done with the
|
||||
// required checks and are fetching extras. If it is a required
|
||||
// check, we'll time out normally.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void StopAsyncDNSCheck( void )
|
||||
{
|
||||
if (s_asyncDNSThreadHandle)
|
||||
{
|
||||
#ifdef DEBUG_CRASHING
|
||||
Int res =
|
||||
#endif
|
||||
TerminateThread(s_asyncDNSThreadHandle,0);
|
||||
DEBUG_ASSERTCRASH(res, ("Could not terminate the Async DNS Lookup thread!")); // Thread still not killed!
|
||||
}
|
||||
s_asyncDNSThreadHandle = NULL;
|
||||
s_asyncDNSLookupInProgress = FALSE;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void StartPatchCheck( void )
|
||||
{
|
||||
checkingForPatchBeforeGameSpy = TRUE;
|
||||
cantConnectBeforeOnline = FALSE;
|
||||
timeThroughOnline++;
|
||||
checksLeftBeforeOnline = 0;
|
||||
|
||||
onlineCancelWindow = MessageBoxCancel(TheGameText->fetch("GUI:CheckingForPatches"),
|
||||
TheGameText->fetch("GUI:CheckingForPatches"), CancelPatchCheckCallbackAndReopenDropdown);
|
||||
|
||||
s_asyncDNSLookupInProgress = TRUE;
|
||||
Int ret = asyncGethostbyname("servserv.generals.ea.com");
|
||||
switch(ret)
|
||||
{
|
||||
case LOOKUP_FAILED:
|
||||
cantConnectBeforeOnline = TRUE;
|
||||
startOnline();
|
||||
break;
|
||||
case LOOKUP_SUCCEEDED:
|
||||
reallyStartPatchCheck();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void reallyStartPatchCheck( void )
|
||||
{
|
||||
checksLeftBeforeOnline = 4;
|
||||
|
||||
std::string gameURL, mapURL;
|
||||
std::string configURL, motdURL;
|
||||
|
||||
FormatURLFromRegistry(gameURL, mapURL, configURL, motdURL);
|
||||
|
||||
std::string proxy;
|
||||
if (GetStringFromRegistry("", "Proxy", proxy))
|
||||
{
|
||||
if (!proxy.empty())
|
||||
{
|
||||
ghttpSetProxy(proxy.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// check for a patch first
|
||||
DEBUG_LOG(("Game patch check: [%s]\n", gameURL.c_str()));
|
||||
DEBUG_LOG(("Map patch check: [%s]\n", mapURL.c_str()));
|
||||
DEBUG_LOG(("Config: [%s]\n", configURL.c_str()));
|
||||
DEBUG_LOG(("MOTD: [%s]\n", motdURL.c_str()));
|
||||
ghttpGet(gameURL.c_str(), GHTTPFalse, gamePatchCheckCallback, (void *)timeThroughOnline);
|
||||
ghttpGet(mapURL.c_str(), GHTTPFalse, gamePatchCheckCallback, (void *)timeThroughOnline);
|
||||
ghttpHead(configURL.c_str(), GHTTPFalse, configHeadCallback, (void *)timeThroughOnline);
|
||||
ghttpGet(motdURL.c_str(), GHTTPFalse, motdCallback, (void *)timeThroughOnline);
|
||||
|
||||
// check total game stats
|
||||
CheckOverallStats();
|
||||
|
||||
// check the users online
|
||||
CheckNumPlayersOnline();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
938
Generals/Code/GameEngine/Source/GameNetwork/GameSpy/PeerDefs.cpp
Normal file
938
Generals/Code/GameEngine/Source/GameNetwork/GameSpy/PeerDefs.cpp
Normal file
@@ -0,0 +1,938 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FILE: PeerDefs.cpp //////////////////////////////////////////////////////
|
||||
// Generals GameSpy Peer (chat) definitions
|
||||
// Author: Matthew D. Campbell, June 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include <set>
|
||||
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/IgnorePreferences.h"
|
||||
#include "Common/CustomMatchPreferences.h"
|
||||
#include "Common/GameSpyMiscPreferences.h"
|
||||
#include "Common/Recorder.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
#include "GameClient/ShellHooks.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameNetwork/GameSpy/LadderDefs.h"
|
||||
#include "GameNetwork/GameSpy/PeerDefsImplementation.h"
|
||||
#include "GameNetwork/GameSpy/BuddyThread.h"
|
||||
#include "GameNetwork/GameSpy/PeerThread.h"
|
||||
#include "GameNetwork/GameSpy/PingThread.h"
|
||||
#include "GameNetwork/GameSpy/PersistentStorageThread.h"
|
||||
#include "GameNetwork/GameSpy/GSConfig.h"
|
||||
#include "GameNetwork/GameSpyOverlay.h"
|
||||
#include "GameNetwork/RankPointValue.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
GameSpyInfoInterface *TheGameSpyInfo = NULL;
|
||||
GameSpyStagingRoom *TheGameSpyGame = NULL;
|
||||
void deleteNotificationBox( void );
|
||||
|
||||
bool AsciiComparator::operator()(AsciiString s1, AsciiString s2) const
|
||||
{
|
||||
return stricmp(s1.str(), s2.str()) < 0;
|
||||
}
|
||||
|
||||
GameSpyInfo::GameSpyInfo()
|
||||
{
|
||||
reset();
|
||||
TheGameSpyGame = &m_localStagingRoom;
|
||||
m_isDisconAfterGameStart = FALSE;
|
||||
}
|
||||
|
||||
GameSpyInfo::~GameSpyInfo()
|
||||
{
|
||||
TheGameSpyGame = NULL;
|
||||
reset();
|
||||
}
|
||||
|
||||
void GameSpyInfo::reset( void )
|
||||
{
|
||||
m_sawFullGameList = FALSE;
|
||||
m_isDisconAfterGameStart = FALSE;
|
||||
m_currentGroupRoomID = 0;
|
||||
clearGroupRoomList();
|
||||
clearStagingRoomList();
|
||||
m_localStagingRoomID = 0;
|
||||
m_buddyRequestMap.clear();
|
||||
m_buddyMap.clear();
|
||||
m_buddyMessages.clear();
|
||||
m_joinedStagingRoom = 0;
|
||||
m_isHosting = false;
|
||||
m_localStagingRoomID = 0;
|
||||
m_localStagingRoom.reset();
|
||||
m_gotGroupRoomList = false;
|
||||
m_localName = "";
|
||||
m_localProfileID = 0;
|
||||
m_maxMessagesPerUpdate = 100;
|
||||
|
||||
// Added By Sadullah Nader
|
||||
// Initialization missing and needed
|
||||
m_disallowAsainText = FALSE;
|
||||
m_disallowNonAsianText = FALSE;
|
||||
m_disconReason = 0;
|
||||
m_localBaseName.clear();
|
||||
m_localEmail.clear();
|
||||
m_localPasswd.clear();
|
||||
m_pingString.clear();
|
||||
m_rawConfig.clear();
|
||||
m_rawMotd.clear();
|
||||
//
|
||||
|
||||
m_internalIP = m_externalIP = 0;
|
||||
|
||||
m_savedIgnoreMap.clear();
|
||||
m_preorderPlayers.clear();
|
||||
|
||||
m_cachedLocalPlayerStats.reset();
|
||||
|
||||
m_additionalDisconnects = -1;
|
||||
}
|
||||
|
||||
Bool GameSpyInfo::didPlayerPreorder( Int profileID ) const
|
||||
{
|
||||
std::set<Int>::const_iterator it = m_preorderPlayers.find(profileID);
|
||||
return (it != m_preorderPlayers.end());
|
||||
}
|
||||
|
||||
void GameSpyInfo::markPlayerAsPreorder( Int profileID )
|
||||
{
|
||||
m_preorderPlayers.insert(profileID);
|
||||
}
|
||||
|
||||
void GameSpyInfo::setLocalIPs(UnsignedInt internalIP, UnsignedInt externalIP)
|
||||
{
|
||||
m_internalIP = internalIP;
|
||||
m_externalIP = externalIP;
|
||||
}
|
||||
|
||||
void GameSpyInfo::readAdditionalDisconnects( void )
|
||||
{
|
||||
m_additionalDisconnects = GetAdditionalDisconnectsFromUserFile(m_localProfileID);
|
||||
DEBUG_LOG(("GameSpyInfo::readAdditionalDisconnects() found %d disconnects.\n", m_additionalDisconnects));
|
||||
}
|
||||
|
||||
Int GameSpyInfo::getAdditionalDisconnects( void )
|
||||
{
|
||||
DEBUG_LOG(("GameSpyInfo::getAdditionalDisconnects() would have returned %d. Returning 0 instead.\n", m_additionalDisconnects));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GameSpyInfo::clearAdditionalDisconnects( void )
|
||||
{
|
||||
m_additionalDisconnects = 0;
|
||||
}
|
||||
|
||||
GameSpyInfoInterface* GameSpyInfoInterface::createNewGameSpyInfoInterface( void )
|
||||
{
|
||||
return NEW GameSpyInfo;
|
||||
}
|
||||
|
||||
Bool GameSpyInfo::amIHost( void )
|
||||
{
|
||||
return m_isHosting;
|
||||
}
|
||||
|
||||
GameSpyStagingRoom* GameSpyInfo::getCurrentStagingRoom( void )
|
||||
{
|
||||
if (m_isHosting || m_joinedStagingRoom)
|
||||
return &m_localStagingRoom;
|
||||
|
||||
StagingRoomMap::iterator it = m_stagingRooms.find(m_joinedStagingRoom);
|
||||
if (it != m_stagingRooms.end())
|
||||
return it->second;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void GameSpyInfo::setGameOptions( void )
|
||||
{
|
||||
if (!m_isHosting)
|
||||
return;
|
||||
|
||||
// set options for game lists, and UTM players in-game
|
||||
PeerRequest req;
|
||||
req.peerRequestType = PeerRequest::PEERREQUEST_SETGAMEOPTIONS;
|
||||
req.options = GameInfoToAsciiString(&m_localStagingRoom).str();
|
||||
|
||||
Int i;
|
||||
AsciiString mapName = TheGameState->realMapPathToPortableMapPath(m_localStagingRoom.getMap());
|
||||
AsciiString newMapName;
|
||||
for (i=0; i<mapName.getLength(); ++i)
|
||||
{
|
||||
char c = mapName.getCharAt(i);
|
||||
if (c != '\\')
|
||||
newMapName.concat(c);
|
||||
else
|
||||
newMapName.concat('/');
|
||||
}
|
||||
req.gameOptsMapName = newMapName.str();
|
||||
|
||||
//const MapMetaData *md = TheMapCache->findMap(mapName);
|
||||
//if (!md)
|
||||
//return; // there really isn't any need to send info like this...
|
||||
|
||||
req.gameOptions.numPlayers = 0;
|
||||
req.gameOptions.numObservers = 0;
|
||||
Int numOpenSlots = 0;
|
||||
AsciiString playerInfo = "";
|
||||
for (i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
Int wins = 0, losses = 0, profileID = 0;
|
||||
GameSpyGameSlot *slot = TheGameSpyGame->getGameSpySlot(i);
|
||||
req.gameOptsPlayerNames[i] = "";
|
||||
if (!slot->isOccupied())
|
||||
{
|
||||
if (slot->isOpen())
|
||||
++numOpenSlots;
|
||||
}
|
||||
else
|
||||
{
|
||||
AsciiString playerName;
|
||||
if (slot->isHuman())
|
||||
{
|
||||
playerName.translate(slot->getName());
|
||||
req.gameOptsPlayerNames[i] = playerName.str();
|
||||
PlayerInfoMap::iterator it = m_playerInfoMap.find(playerName);
|
||||
if (it != m_playerInfoMap.end())
|
||||
{
|
||||
wins = it->second.m_wins;
|
||||
losses = it->second.m_losses;
|
||||
profileID = it->second.m_profileID;
|
||||
}
|
||||
req.gameOptions.wins[req.gameOptions.numObservers+req.gameOptions.numPlayers] = wins;
|
||||
req.gameOptions.losses[req.gameOptions.numObservers+req.gameOptions.numPlayers] = losses;
|
||||
req.gameOptions.profileID[req.gameOptions.numObservers+req.gameOptions.numPlayers] = profileID;
|
||||
req.gameOptions.faction[req.gameOptions.numObservers+req.gameOptions.numPlayers] = slot->getPlayerTemplate();
|
||||
req.gameOptions.color[req.gameOptions.numObservers+req.gameOptions.numPlayers] = slot->getColor();
|
||||
if (slot->getPlayerTemplate() == PLAYERTEMPLATE_OBSERVER)
|
||||
{
|
||||
++req.gameOptions.numObservers;
|
||||
}
|
||||
else
|
||||
{
|
||||
++req.gameOptions.numPlayers;
|
||||
}
|
||||
}
|
||||
else if (slot->isAI())
|
||||
{
|
||||
// add in AI players
|
||||
switch (slot->getState())
|
||||
{
|
||||
case SLOT_EASY_AI:
|
||||
playerName = "CE";
|
||||
break;
|
||||
case SLOT_MED_AI:
|
||||
playerName = "CM";
|
||||
break;
|
||||
case SLOT_BRUTAL_AI:
|
||||
playerName = "CH";
|
||||
break;
|
||||
}
|
||||
req.gameOptsPlayerNames[i] = playerName.str(); // name is unused - we go off of the profileID
|
||||
req.gameOptions.wins[req.gameOptions.numObservers+req.gameOptions.numPlayers] = 0;
|
||||
req.gameOptions.losses[req.gameOptions.numObservers+req.gameOptions.numPlayers] = 0;
|
||||
req.gameOptions.profileID[req.gameOptions.numObservers+req.gameOptions.numPlayers] = slot->getState();
|
||||
req.gameOptions.faction[req.gameOptions.numObservers+req.gameOptions.numPlayers] = slot->getPlayerTemplate();
|
||||
req.gameOptions.color[req.gameOptions.numObservers+req.gameOptions.numPlayers] = slot->getColor();
|
||||
++req.gameOptions.numPlayers;
|
||||
}
|
||||
}
|
||||
}
|
||||
req.gameOptions.maxPlayers = numOpenSlots + req.gameOptions.numPlayers + req.gameOptions.numObservers;
|
||||
TheGameSpyPeerMessageQueue->addRequest(req);
|
||||
|
||||
req.peerRequestType = PeerRequest::PEERREQUEST_UTMROOM;
|
||||
req.UTM.isStagingRoom = TRUE;
|
||||
req.id = "Pings/";
|
||||
AsciiString pings;
|
||||
for (i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
if (i!=0)
|
||||
pings.concat(",");
|
||||
|
||||
GameSpyGameSlot *slot = TheGameSpyGame->getGameSpySlot(i);
|
||||
if (slot && slot->isHuman())
|
||||
{
|
||||
pings.concat(slot->getPingString());
|
||||
}
|
||||
else
|
||||
{
|
||||
pings.concat("0");
|
||||
}
|
||||
}
|
||||
req.options = pings.str();
|
||||
TheGameSpyPeerMessageQueue->addRequest(req);
|
||||
}
|
||||
|
||||
Bool GameSpyInfo::isBuddy( Int id )
|
||||
{
|
||||
return m_buddyMap.find(id) != m_buddyMap.end();
|
||||
}
|
||||
|
||||
void GameSpyInfo::addGroupRoom( GameSpyGroupRoom room )
|
||||
{
|
||||
if (room.m_groupID == 0)
|
||||
{
|
||||
m_gotGroupRoomList = TRUE;
|
||||
|
||||
GroupRoomMap::iterator iter;
|
||||
|
||||
// figure out how many good strings we've got
|
||||
std::vector<UnicodeString> names;
|
||||
Int numRooms = 0;
|
||||
for (iter = getGroupRoomList()->begin(); iter != getGroupRoomList()->end(); ++iter)
|
||||
{
|
||||
GameSpyGroupRoom room = iter->second;
|
||||
if (room.m_groupID != TheGameSpyConfig->getQMChannel())
|
||||
{
|
||||
++numRooms;
|
||||
|
||||
AsciiString groupLabel;
|
||||
groupLabel.format("GUI:%s", room.m_name.str());
|
||||
|
||||
Bool exists = FALSE;
|
||||
UnicodeString groupName = TheGameText->fetch(groupLabel, &exists);
|
||||
if (exists)
|
||||
{
|
||||
names.push_back(groupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!names.empty() && names.size() != numRooms)
|
||||
{
|
||||
// didn't get all names. fix up
|
||||
Int nameIndex = 0;
|
||||
Int timesThrough = 1; // start with USA Lobby 1
|
||||
for (iter = TheGameSpyInfo->getGroupRoomList()->begin(); iter != TheGameSpyInfo->getGroupRoomList()->end(); ++iter)
|
||||
{
|
||||
GameSpyGroupRoom room = iter->second;
|
||||
if (room.m_groupID != TheGameSpyConfig->getQMChannel())
|
||||
{
|
||||
room.m_translatedName.format(L"%ls %d", names[nameIndex].str(), timesThrough);
|
||||
nameIndex = (nameIndex+1)%names.size();
|
||||
m_groupRooms[room.m_groupID] = room;
|
||||
if (!nameIndex)
|
||||
{
|
||||
// we've looped through the name list already. increment the timesThrough counter
|
||||
++timesThrough;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("Adding group room %d (%s)\n", room.m_groupID, room.m_name.str()));
|
||||
AsciiString groupLabel;
|
||||
groupLabel.format("GUI:%s", room.m_name.str());
|
||||
room.m_translatedName = TheGameText->fetch(groupLabel);
|
||||
m_groupRooms[room.m_groupID] = room;
|
||||
if ( !stricmp("quickmatch", room.m_name.str()) )
|
||||
{
|
||||
DEBUG_LOG(("Group room %d (%s) is the QuickMatch room\n", room.m_groupID, room.m_name.str()));
|
||||
TheGameSpyConfig->setQMChannel(room.m_groupID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyInfo::joinGroupRoom( Int groupID )
|
||||
{
|
||||
if (groupID > 0)
|
||||
{
|
||||
PeerRequest req;
|
||||
req.peerRequestType = PeerRequest::PEERREQUEST_JOINGROUPROOM;
|
||||
req.groupRoom.id = groupID;
|
||||
TheGameSpyPeerMessageQueue->addRequest(req);
|
||||
m_playerInfoMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyInfo::leaveGroupRoom( void )
|
||||
{
|
||||
PeerRequest req;
|
||||
req.peerRequestType = PeerRequest::PEERREQUEST_LEAVEGROUPROOM;
|
||||
TheGameSpyPeerMessageQueue->addRequest(req);
|
||||
setCurrentGroupRoom(0);
|
||||
m_playerInfoMap.clear();
|
||||
}
|
||||
|
||||
void GameSpyInfo::joinBestGroupRoom( void )
|
||||
{
|
||||
if (m_currentGroupRoomID)
|
||||
{
|
||||
DEBUG_LOG(("Bailing from GameSpyInfo::joinBestGroupRoom() - we were already in a room\n"));
|
||||
m_currentGroupRoomID = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_groupRooms.size())
|
||||
{
|
||||
int minID = -1;
|
||||
int minPlayers = 1000;
|
||||
GroupRoomMap::iterator iter = m_groupRooms.begin();
|
||||
while (iter != m_groupRooms.end())
|
||||
{
|
||||
GameSpyGroupRoom room = iter->second;
|
||||
DEBUG_LOG(("Group room %d: %s (%d, %d, %d, %d)\n", room.m_groupID, room.m_name.str(), room.m_numWaiting, room.m_maxWaiting,
|
||||
room.m_numGames, room.m_numPlaying));
|
||||
|
||||
if (TheGameSpyConfig->getQMChannel() != room.m_groupID && minPlayers > 25 && room.m_numWaiting < minPlayers)
|
||||
{
|
||||
minID = room.m_groupID;
|
||||
minPlayers = room.m_numWaiting;
|
||||
}
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (minID > 0)
|
||||
{
|
||||
PeerRequest req;
|
||||
req.peerRequestType = PeerRequest::PEERREQUEST_JOINGROUPROOM;
|
||||
req.groupRoom.id = minID;
|
||||
TheGameSpyPeerMessageQueue->addRequest(req);
|
||||
m_playerInfoMap.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
GSMessageBoxOk(TheGameText->fetch("GUI:Error"), TheGameText->fetch("GUI:GSGroupRoomJoinFail"), NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GSMessageBoxOk(TheGameText->fetch("GUI:Error"), TheGameText->fetch("GUI:GSGroupRoomJoinFail"), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyInfo::updatePlayerInfo( PlayerInfo pi, AsciiString oldNick )
|
||||
{
|
||||
if (!oldNick.isEmpty())
|
||||
playerLeftGroupRoom(oldNick);
|
||||
|
||||
m_playerInfoMap[pi.m_name] = pi;
|
||||
|
||||
if (pi.m_preorder != 0)
|
||||
markPlayerAsPreorder(pi.m_profileID);
|
||||
}
|
||||
|
||||
void GameSpyInfo::playerLeftGroupRoom( AsciiString nick )
|
||||
{
|
||||
PlayerInfoMap::iterator it = m_playerInfoMap.find(nick);
|
||||
if (it != m_playerInfoMap.end())
|
||||
{
|
||||
m_playerInfoMap.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyInfo::clearStagingRoomList( void )
|
||||
{
|
||||
Int numRoomsRemoved = 0;
|
||||
m_sawFullGameList = FALSE;
|
||||
m_stagingRoomsDirty = FALSE;
|
||||
|
||||
StagingRoomMap::iterator it = m_stagingRooms.begin();
|
||||
while (it != m_stagingRooms.end())
|
||||
{
|
||||
++numRoomsRemoved;
|
||||
|
||||
delete it->second;
|
||||
m_stagingRooms.erase(it);
|
||||
it = m_stagingRooms.begin();
|
||||
}
|
||||
if (numRoomsRemoved > 0)
|
||||
{
|
||||
//m_stagingRoomsDirty = true; // only consider ourselves dirty if we actually removed some games.
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyInfo::addStagingRoom( GameSpyStagingRoom room )
|
||||
{
|
||||
removeStagingRoom(room);
|
||||
GameSpyStagingRoom *newRoom = NEW GameSpyStagingRoom;
|
||||
*newRoom = room;
|
||||
newRoom->cleanUpSlotPointers();
|
||||
m_stagingRooms[room.getID()] = newRoom;
|
||||
m_stagingRoomsDirty = m_sawFullGameList;
|
||||
}
|
||||
|
||||
void GameSpyInfo::updateStagingRoom( GameSpyStagingRoom room )
|
||||
{
|
||||
addStagingRoom(room);
|
||||
}
|
||||
|
||||
void GameSpyInfo::removeStagingRoom( GameSpyStagingRoom room )
|
||||
{
|
||||
StagingRoomMap::iterator it = m_stagingRooms.find(room.getID());
|
||||
if (it != m_stagingRooms.end())
|
||||
{
|
||||
delete it->second;
|
||||
m_stagingRooms.erase(it);
|
||||
|
||||
m_stagingRoomsDirty = m_sawFullGameList;
|
||||
}
|
||||
}
|
||||
|
||||
Bool GameSpyInfo::hasStagingRoomListChanged( void )
|
||||
{
|
||||
Bool val = m_stagingRoomsDirty;
|
||||
m_stagingRoomsDirty = false;
|
||||
return val;
|
||||
}
|
||||
|
||||
GameSpyStagingRoom* GameSpyInfo::findStagingRoomByID( Int id )
|
||||
{
|
||||
StagingRoomMap::iterator it = m_stagingRooms.find(id);
|
||||
if (it != m_stagingRooms.end())
|
||||
return it->second;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void GameSpyInfo::leaveStagingRoom( void )
|
||||
{
|
||||
m_localStagingRoomID = 0;
|
||||
PeerRequest req;
|
||||
req.peerRequestType = PeerRequest::PEERREQUEST_LEAVESTAGINGROOM;
|
||||
TheGameSpyPeerMessageQueue->addRequest(req);
|
||||
m_playerInfoMap.clear();
|
||||
m_joinedStagingRoom = FALSE;
|
||||
m_isHosting = FALSE;
|
||||
}
|
||||
|
||||
void GameSpyInfo::markAsStagingRoomHost( void )
|
||||
{
|
||||
m_localStagingRoomID = 0;
|
||||
m_joinedStagingRoom = FALSE; m_isHosting = TRUE;
|
||||
m_localStagingRoom.reset();
|
||||
m_localStagingRoom.enterGame();
|
||||
m_localStagingRoom.setSeed(GetTickCount());
|
||||
|
||||
GameSlot newSlot;
|
||||
UnicodeString uName;
|
||||
uName.translate(m_localName);
|
||||
newSlot.setState(SLOT_PLAYER, uName);
|
||||
|
||||
m_localStagingRoom.setLocalIP(m_externalIP);
|
||||
newSlot.setIP(m_externalIP);
|
||||
|
||||
m_localStagingRoom.setSlot(0,newSlot);
|
||||
m_localStagingRoom.setLocalName(m_localName);
|
||||
|
||||
TheMapCache->updateCache();
|
||||
m_localStagingRoom.setMap(getDefaultMap(TRUE));
|
||||
m_localStagingRoom.adjustSlotsForMap(); // close slots that the map can't hold. BGC
|
||||
}
|
||||
|
||||
void GameSpyInfo::markAsStagingRoomJoiner( Int game )
|
||||
{
|
||||
m_localStagingRoomID = game;
|
||||
m_joinedStagingRoom = TRUE; m_isHosting = FALSE;
|
||||
m_localStagingRoom.reset();
|
||||
m_localStagingRoom.enterGame();
|
||||
StagingRoomMap::iterator it = m_stagingRooms.find(game);
|
||||
if (it != m_stagingRooms.end())
|
||||
{
|
||||
GameSpyStagingRoom *info = it->second;
|
||||
info->cleanUpSlotPointers();
|
||||
AsciiString options = GameInfoToAsciiString(info);
|
||||
#ifdef DEBUG_CRASHING
|
||||
Bool res =
|
||||
#endif
|
||||
ParseAsciiStringToGameInfo(&m_localStagingRoom, options);
|
||||
DEBUG_ASSERTCRASH(res, ("Could not parse game info \"%s\"", options.str()));
|
||||
m_localStagingRoom.setInGame();
|
||||
m_localStagingRoom.setLocalName(m_localName);
|
||||
m_localStagingRoom.setExeCRC(info->getExeCRC());
|
||||
m_localStagingRoom.setIniCRC(info->getIniCRC());
|
||||
m_localStagingRoom.setAllowObservers(info->getAllowObservers());
|
||||
m_localStagingRoom.setHasPassword(info->getHasPassword());
|
||||
m_localStagingRoom.setGameName(info->getGameName());
|
||||
DEBUG_LOG(("Joining game: host is %ls\n", m_localStagingRoom.getConstSlot(0)->getName().str()));
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyInfo::setMOTD( const AsciiString& motd )
|
||||
{
|
||||
m_rawMotd = motd;
|
||||
}
|
||||
|
||||
const AsciiString& GameSpyInfo::getMOTD( void )
|
||||
{
|
||||
return m_rawMotd;
|
||||
}
|
||||
|
||||
void GameSpyInfo::setConfig( const AsciiString& config )
|
||||
{
|
||||
m_rawConfig = config;
|
||||
}
|
||||
|
||||
const AsciiString& GameSpyInfo::getConfig( void )
|
||||
{
|
||||
return m_rawConfig;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
void SetUpGameSpy( const char *motdBuffer, const char *configBuffer )
|
||||
{
|
||||
if (!motdBuffer)
|
||||
motdBuffer = "";
|
||||
if (!configBuffer)
|
||||
configBuffer = "";
|
||||
TearDownGameSpy();
|
||||
|
||||
AsciiString dir = TheGlobalData->getPath_UserData();
|
||||
CreateDirectory(dir.str(), NULL);
|
||||
dir.format("%sGeneralsOnline", TheGlobalData->getPath_UserData().str());
|
||||
CreateDirectory(dir.str(), NULL);
|
||||
dir.format("%sGeneralsOnline\\Ladders", TheGlobalData->getPath_UserData().str());
|
||||
CreateDirectory(dir.str(), NULL);
|
||||
|
||||
TheGameSpyBuddyMessageQueue = GameSpyBuddyMessageQueueInterface::createNewMessageQueue();
|
||||
TheGameSpyBuddyMessageQueue->startThread();
|
||||
|
||||
TheGameSpyPeerMessageQueue = GameSpyPeerMessageQueueInterface::createNewMessageQueue();
|
||||
TheGameSpyPeerMessageQueue->startThread();
|
||||
|
||||
TheGameSpyPSMessageQueue = GameSpyPSMessageQueueInterface::createNewMessageQueue();
|
||||
TheGameSpyPSMessageQueue->startThread();
|
||||
|
||||
/*
|
||||
TheGameSpyGame = NEW GameSpyStagingRoom;
|
||||
*/
|
||||
|
||||
TheGameSpyInfo = GameSpyInfoInterface::createNewGameSpyInfoInterface();
|
||||
TheGameSpyInfo->setMOTD(motdBuffer);
|
||||
TheGameSpyInfo->setConfig(configBuffer);
|
||||
|
||||
CustomMatchPreferences pref;
|
||||
TheGameSpyInfo->setDisallowAsianText(pref.getDisallowAsianText());
|
||||
TheGameSpyInfo->setDisallowNonAsianText( pref.getDisallowNonAsianText());
|
||||
|
||||
|
||||
TheGameSpyConfig = GameSpyConfigInterface::create(configBuffer);
|
||||
|
||||
TheLadderList = NEW LadderList;
|
||||
|
||||
ThePinger = PingerInterface::createNewPingerInterface();
|
||||
ThePinger->startThreads();
|
||||
|
||||
TheRankPointValues = NEW RankPoints;
|
||||
}
|
||||
|
||||
void TearDownGameSpy( void )
|
||||
{
|
||||
// save off cached stats
|
||||
if (TheGameSpyInfo && TheGameSpyInfo->getLocalProfileID())
|
||||
{
|
||||
// /* This was done on the score screen, so there is no need to do it now.
|
||||
// *
|
||||
PSPlayerStats localPSStats = TheGameSpyPSMessageQueue->findPlayerStatsByID(TheGameSpyInfo->getLocalProfileID());
|
||||
if (localPSStats.id != 0)
|
||||
{
|
||||
GameSpyMiscPreferences mPref;
|
||||
mPref.setCachedStats(GameSpyPSMessageQueueInterface::formatPlayerKVPairs(localPSStats).c_str());
|
||||
mPref.write();
|
||||
}
|
||||
// */
|
||||
}
|
||||
|
||||
// End our threads before we kill off the singletons they reference. No crashy-crash for you!
|
||||
if (TheGameSpyPSMessageQueue)
|
||||
TheGameSpyPSMessageQueue->endThread();
|
||||
if (TheGameSpyBuddyMessageQueue)
|
||||
TheGameSpyBuddyMessageQueue->endThread();
|
||||
if (TheGameSpyPeerMessageQueue)
|
||||
TheGameSpyPeerMessageQueue->endThread();
|
||||
if (ThePinger)
|
||||
ThePinger->endThreads();
|
||||
|
||||
if(TheRankPointValues)
|
||||
{
|
||||
delete TheRankPointValues;
|
||||
TheRankPointValues = NULL;
|
||||
}
|
||||
if (TheGameSpyPSMessageQueue)
|
||||
{
|
||||
delete TheGameSpyPSMessageQueue;
|
||||
TheGameSpyPSMessageQueue = NULL;
|
||||
}
|
||||
|
||||
if (TheGameSpyBuddyMessageQueue)
|
||||
{
|
||||
delete TheGameSpyBuddyMessageQueue;
|
||||
TheGameSpyBuddyMessageQueue = NULL;
|
||||
}
|
||||
|
||||
if (TheGameSpyPeerMessageQueue)
|
||||
{
|
||||
delete TheGameSpyPeerMessageQueue;
|
||||
TheGameSpyPeerMessageQueue = NULL;
|
||||
}
|
||||
|
||||
if (TheGameSpyInfo)
|
||||
{
|
||||
if (TheGameSpyInfo->getInternalIP())
|
||||
{
|
||||
// we've logged in before. mark us as logging out.
|
||||
SignalUIInteraction(SHELL_SCRIPT_HOOK_GENERALS_ONLINE_LOGOUT);
|
||||
}
|
||||
delete TheGameSpyInfo;
|
||||
TheGameSpyInfo = NULL;
|
||||
}
|
||||
|
||||
if (ThePinger)
|
||||
{
|
||||
delete ThePinger;
|
||||
ThePinger = NULL;
|
||||
}
|
||||
|
||||
if (TheLadderList)
|
||||
{
|
||||
delete TheLadderList;
|
||||
TheLadderList = NULL;
|
||||
}
|
||||
|
||||
if (TheGameSpyConfig)
|
||||
{
|
||||
delete TheGameSpyConfig;
|
||||
TheGameSpyConfig = NULL;
|
||||
}
|
||||
|
||||
// make sure the notification box doesn't exist
|
||||
deleteNotificationBox();
|
||||
}
|
||||
|
||||
|
||||
void GameSpyInfo::addToIgnoreList( AsciiString nick )
|
||||
{
|
||||
m_ignoreList.insert(nick);
|
||||
}
|
||||
|
||||
void GameSpyInfo::removeFromIgnoreList( AsciiString nick )
|
||||
{
|
||||
m_ignoreList.erase(nick);
|
||||
}
|
||||
|
||||
Bool GameSpyInfo::isIgnored( AsciiString nick )
|
||||
{
|
||||
return m_ignoreList.find(nick) != m_ignoreList.end();
|
||||
}
|
||||
|
||||
IgnoreList GameSpyInfo::returnIgnoreList( void )
|
||||
{
|
||||
return m_ignoreList;
|
||||
}
|
||||
|
||||
void GameSpyInfo::addToSavedIgnoreList( Int profileID, AsciiString nick)
|
||||
{
|
||||
m_savedIgnoreMap[profileID] = nick;
|
||||
IgnorePreferences pref;
|
||||
pref.setIgnore(nick, profileID, true);
|
||||
pref.write();
|
||||
}
|
||||
|
||||
void GameSpyInfo::removeFromSavedIgnoreList( Int profileID )
|
||||
{
|
||||
m_savedIgnoreMap.erase(profileID);
|
||||
IgnorePreferences pref;
|
||||
pref.setIgnore(AsciiString::TheEmptyString, profileID, false);
|
||||
pref.write();
|
||||
}
|
||||
|
||||
Bool GameSpyInfo::isSavedIgnored( Int profileID )
|
||||
{
|
||||
return m_savedIgnoreMap.find(profileID) != m_savedIgnoreMap.end();
|
||||
}
|
||||
|
||||
SavedIgnoreMap GameSpyInfo::returnSavedIgnoreList( void )
|
||||
{
|
||||
return m_savedIgnoreMap;
|
||||
}
|
||||
|
||||
static Int grabHexInt(const char *s)
|
||||
{
|
||||
char tmp[5] = "0xff";
|
||||
tmp[2] = s[0];
|
||||
tmp[3] = s[1];
|
||||
Int b = strtol(tmp, NULL, 16);
|
||||
return b;
|
||||
}
|
||||
|
||||
Int GameSpyInfo::getPingValue( const AsciiString& otherPing )
|
||||
{
|
||||
if (m_pingString.getLength() != otherPing.getLength())
|
||||
{
|
||||
return TheGameSpyConfig->getPingTimeoutInMs();
|
||||
}
|
||||
|
||||
if (m_pingString.getLength() % 2 != 0)
|
||||
{
|
||||
return TheGameSpyConfig->getPingTimeoutInMs();
|
||||
}
|
||||
|
||||
Int best = 255+255;
|
||||
const char *myStr = m_pingString.str();
|
||||
const char *otherStr = otherPing.str();
|
||||
|
||||
while (*myStr)
|
||||
{
|
||||
Int myVal = grabHexInt(myStr);
|
||||
Int otherVal = grabHexInt(otherStr);
|
||||
Int val = myVal + otherVal;
|
||||
best = (val < best) ? val : best;
|
||||
myStr += 2;
|
||||
otherStr += 2;
|
||||
}
|
||||
|
||||
return best * TheGameSpyConfig->getPingTimeoutInMs() / (255+255);
|
||||
}
|
||||
|
||||
Bool PlayerInfo::isIgnored( void )
|
||||
{
|
||||
return (m_profileID)?TheGameSpyInfo->isSavedIgnored(m_profileID):TheGameSpyInfo->isIgnored(m_name);
|
||||
}
|
||||
|
||||
void GameSpyInfo::loadSavedIgnoreList( void )
|
||||
{
|
||||
m_savedIgnoreMap.clear();
|
||||
IgnorePreferences prefs;
|
||||
m_savedIgnoreMap = prefs.getIgnores();
|
||||
}
|
||||
|
||||
void GameSpyInfo::setDisallowAsianText( Bool val )
|
||||
{
|
||||
m_disallowAsainText = val;
|
||||
}
|
||||
|
||||
void GameSpyInfo::setDisallowNonAsianText( Bool val )
|
||||
{
|
||||
m_disallowNonAsianText = val;
|
||||
}
|
||||
|
||||
Bool GameSpyInfo::getDisallowAsianText( void )
|
||||
{
|
||||
return m_disallowAsainText;
|
||||
}
|
||||
Bool GameSpyInfo::getDisallowNonAsianText(void )
|
||||
{
|
||||
return m_disallowNonAsianText;
|
||||
}
|
||||
|
||||
void GameSpyInfo::setMaxMessagesPerUpdate( Int num )
|
||||
{
|
||||
m_maxMessagesPerUpdate = num;
|
||||
}
|
||||
|
||||
Int GameSpyInfo::getMaxMessagesPerUpdate( void )
|
||||
{
|
||||
return m_maxMessagesPerUpdate;
|
||||
}
|
||||
|
||||
/**This function is used to force an update of player's gamespy stats with an additional
|
||||
disconnection. This is used upon starting a new game so that if user disconnects prior
|
||||
to finishing game, the disconnection stays on the server. If he completes the game, we
|
||||
remove this extra disconnection inside of populatePlayerInfo() on the ScoreScreen. This
|
||||
seems like the only secure way to handle this issue since users can abort the process
|
||||
before we can detect/log disconnections.*/
|
||||
void GameSpyInfo::updateAdditionalGameSpyDisconnections(Int count)
|
||||
{
|
||||
if (TheRecorder->isMultiplayer() && TheGameLogic->isInInternetGame())
|
||||
{
|
||||
Int localID = TheGameSpyInfo->getLocalProfileID();
|
||||
PSPlayerStats stats = TheGameSpyPSMessageQueue->findPlayerStatsByID(localID);
|
||||
|
||||
Player *player=ThePlayerList->getLocalPlayer();
|
||||
|
||||
Int ptIdx;
|
||||
const PlayerTemplate *myTemplate = player->getPlayerTemplate();
|
||||
DEBUG_LOG(("myTemplate = %X(%s)\n", myTemplate, myTemplate->getName().str()));
|
||||
for (ptIdx = 0; ptIdx < ThePlayerTemplateStore->getPlayerTemplateCount(); ++ptIdx)
|
||||
{
|
||||
const PlayerTemplate *nthTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(ptIdx);
|
||||
DEBUG_LOG(("nthTemplate = %X(%s)\n", nthTemplate, nthTemplate->getName().str()));
|
||||
if (nthTemplate == myTemplate)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Bool anyAI = FALSE;
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
const GameSlot *slot = TheGameInfo->getConstSlot(i);
|
||||
|
||||
if (slot->isAI())
|
||||
{
|
||||
anyAI = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
//Check for cases where we're not tracking stats.
|
||||
if (anyAI || stats.id == 0 || myTemplate->isObserver() || player->getPlayerType() != PLAYER_HUMAN || player->isPlayerObserver())
|
||||
return;
|
||||
|
||||
Int disCons=stats.discons[ptIdx];
|
||||
disCons += count;
|
||||
if (disCons < 0)
|
||||
{ DEBUG_LOG(("updateAdditionalGameSpyDisconnections() - disconnection count below zero\n"));
|
||||
return; //something is wrong here
|
||||
}
|
||||
stats.discons[ptIdx] = disCons; //add an additional disconnection to their stats.
|
||||
|
||||
//Add an additional disconnection to player stats.
|
||||
PSRequest req;
|
||||
|
||||
req.requestType = PSRequest::PSREQUEST_UPDATEPLAYERSTATS;
|
||||
req.email = TheGameSpyInfo->getLocalEmail().str();
|
||||
req.nick = TheGameSpyInfo->getLocalBaseName().str();
|
||||
req.password = TheGameSpyInfo->getLocalPassword().str();
|
||||
req.player = stats;
|
||||
req.addDesync = FALSE;
|
||||
req.addDiscon = FALSE;
|
||||
req.lastHouse = ptIdx;
|
||||
|
||||
TheGameSpyPSMessageQueue->addRequest(req);
|
||||
TheGameSpyPSMessageQueue->trackPlayerStats(stats);
|
||||
|
||||
// force an update of our shtuff
|
||||
PSResponse newResp;
|
||||
newResp.responseType = PSResponse::PSRESPONSE_PLAYERSTATS;
|
||||
newResp.player = stats;
|
||||
TheGameSpyPSMessageQueue->addResponse(newResp);
|
||||
|
||||
// cache our stuff for easy reading next time
|
||||
GameSpyMiscPreferences mPref;
|
||||
mPref.setCachedStats(GameSpyPSMessageQueueInterface::formatPlayerKVPairs(stats).c_str());
|
||||
mPref.write();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,896 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: StagingRoomGameInfo.cpp //////////////////////////////////////////////////////
|
||||
// Generals GameSpy GameInfo-related code
|
||||
// Author: Matthew D. Campbell, July 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/ScoreKeeper.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
#include "GameClient/Shell.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/VictoryConditions.h"
|
||||
#include "GameNetwork/FileTransfer.h"
|
||||
#include "GameNetwork/GameSpy/BuddyThread.h"
|
||||
#include "GameNetwork/GameSpy/PeerDefs.h"
|
||||
#include "GameNetwork/GameSpy/PersistentStorageThread.h"
|
||||
#include "GameNetwork/GameSpy/ThreadUtils.h"
|
||||
#include "GameNetwork/GameSpyOverlay.h"
|
||||
#include "GameNetwork/NAT.h"
|
||||
#include "GameNetwork/NetworkInterface.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
// GameSpyGameSlot -------------------------------------------
|
||||
|
||||
GameSpyGameSlot::GameSpyGameSlot()
|
||||
{
|
||||
GameSlot();
|
||||
m_gameSpyLogin.clear();
|
||||
m_gameSpyLocale.clear();
|
||||
m_profileID = 0;
|
||||
m_wins = 0;
|
||||
m_losses = 0;
|
||||
m_rankPoints = 0;
|
||||
m_favoriteSide = 0;
|
||||
m_pingInt = 0;
|
||||
// Added By Sadullah Nader
|
||||
// Initializations missing and needed
|
||||
m_profileID = 0;
|
||||
m_pingStr.clear();
|
||||
}
|
||||
|
||||
// Helper Functions ----------------------------------------
|
||||
/*
|
||||
** Function definitions for the MIB-II entry points.
|
||||
*/
|
||||
|
||||
BOOL (__stdcall *SnmpExtensionInitPtr)(IN DWORD dwUpTimeReference, OUT HANDLE *phSubagentTrapEvent, OUT AsnObjectIdentifier *pFirstSupportedRegion);
|
||||
BOOL (__stdcall *SnmpExtensionQueryPtr)(IN BYTE bPduType, IN OUT RFC1157VarBindList *pVarBindList, OUT AsnInteger32 *pErrorStatus, OUT AsnInteger32 *pErrorIndex);
|
||||
LPVOID (__stdcall *SnmpUtilMemAllocPtr)(IN DWORD bytes);
|
||||
VOID (__stdcall *SnmpUtilMemFreePtr)(IN LPVOID pMem);
|
||||
|
||||
typedef struct tConnInfoStruct {
|
||||
unsigned int State;
|
||||
unsigned long LocalIP;
|
||||
unsigned short LocalPort;
|
||||
unsigned long RemoteIP;
|
||||
unsigned short RemotePort;
|
||||
} ConnInfoStruct;
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
|
||||
#endif
|
||||
|
||||
/***********************************************************************************************
|
||||
* Get_Local_Chat_Connection_Address -- Which address are we using to talk to the chat server? *
|
||||
* *
|
||||
* *
|
||||
* *
|
||||
* INPUT: Ptr to address to return local address * *
|
||||
* *
|
||||
* OUTPUT: True if success *
|
||||
* *
|
||||
* WARNINGS: None *
|
||||
* *
|
||||
* HISTORY: *
|
||||
* 10/27/00 3:24PM ST : Created *
|
||||
*=============================================================================================*/
|
||||
Bool GetLocalChatConnectionAddress(AsciiString serverName, UnsignedShort serverPort, UnsignedInt& localIP)
|
||||
{
|
||||
//return false;
|
||||
/*
|
||||
** Local defines.
|
||||
*/
|
||||
enum {
|
||||
CLOSED = 1,
|
||||
LISTENING,
|
||||
SYN_SENT,
|
||||
SEN_RECEIVED,
|
||||
ESTABLISHED,
|
||||
FIN_WAIT,
|
||||
FIN_WAIT2,
|
||||
CLOSE_WAIT,
|
||||
LAST_ACK,
|
||||
CLOSING,
|
||||
TIME_WAIT,
|
||||
DELETE_TCB
|
||||
};
|
||||
|
||||
enum {
|
||||
tcpConnState = 1,
|
||||
tcpConnLocalAddress,
|
||||
tcpConnLocalPort,
|
||||
tcpConnRemAddress,
|
||||
tcpConnRemPort
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Locals.
|
||||
*/
|
||||
unsigned char serverAddress[4];
|
||||
unsigned char remoteAddress[4];
|
||||
HANDLE trap_handle;
|
||||
AsnObjectIdentifier first_supported_region;
|
||||
std::vector<ConnInfoStruct> connectionVector;
|
||||
int last_field;
|
||||
int index;
|
||||
AsnInteger error_status;
|
||||
AsnInteger error_index;
|
||||
int conn_entry_type_index;
|
||||
int conn_entry_type;
|
||||
Bool found;
|
||||
|
||||
/*
|
||||
** Statics.
|
||||
*/
|
||||
static char _conn_state[][32] = {
|
||||
"?",
|
||||
"CLOSED",
|
||||
"LISTENING",
|
||||
"SYN_SENT",
|
||||
"SEN_RECEIVED",
|
||||
"ESTABLISHED",
|
||||
"FIN_WAIT",
|
||||
"FIN_WAIT2",
|
||||
"CLOSE_WAIT",
|
||||
"LAST_ACK",
|
||||
"CLOSING",
|
||||
"TIME_WAIT",
|
||||
"DELETE_TCB"
|
||||
};
|
||||
|
||||
DEBUG_LOG(("Finding local address used to talk to the chat server\n"));
|
||||
DEBUG_LOG(("Current chat server name is %s\n", serverName.str()));
|
||||
DEBUG_LOG(("Chat server port is %d\n", serverPort));
|
||||
|
||||
/*
|
||||
** Get the address of the chat server.
|
||||
*/
|
||||
DEBUG_LOG( ("About to call gethostbyname\n"));
|
||||
struct hostent *host_info = gethostbyname(serverName.str());
|
||||
|
||||
if (!host_info) {
|
||||
DEBUG_LOG( ("gethostbyname failed! Error code %d\n", WSAGetLastError()));
|
||||
return(false);
|
||||
}
|
||||
|
||||
memcpy(serverAddress, &host_info->h_addr_list[0][0], 4);
|
||||
unsigned long temp = *((unsigned long*)(&serverAddress[0]));
|
||||
temp = ntohl(temp);
|
||||
*((unsigned long*)(&serverAddress[0])) = temp;
|
||||
|
||||
DEBUG_LOG(("Host address is %d.%d.%d.%d\n", serverAddress[3], serverAddress[2], serverAddress[1], serverAddress[0]));
|
||||
|
||||
/*
|
||||
** Load the MIB-II SNMP DLL.
|
||||
*/
|
||||
DEBUG_LOG(("About to load INETMIB1.DLL\n"));
|
||||
|
||||
HINSTANCE mib_ii_dll = LoadLibrary("inetmib1.dll");
|
||||
if (mib_ii_dll == NULL) {
|
||||
DEBUG_LOG(("Failed to load INETMIB1.DLL\n"));
|
||||
return(false);
|
||||
}
|
||||
|
||||
DEBUG_LOG(("About to load SNMPAPI.DLL\n"));
|
||||
|
||||
HINSTANCE snmpapi_dll = LoadLibrary("snmpapi.dll");
|
||||
if (snmpapi_dll == NULL) {
|
||||
DEBUG_LOG(("Failed to load SNMPAPI.DLL\n"));
|
||||
FreeLibrary(mib_ii_dll);
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
** Get the function pointers into the .dll
|
||||
*/
|
||||
SnmpExtensionInitPtr = (int (__stdcall *)(unsigned long,void ** ,AsnObjectIdentifier *)) GetProcAddress(mib_ii_dll, "SnmpExtensionInit");
|
||||
SnmpExtensionQueryPtr = (int (__stdcall *)(unsigned char,SnmpVarBindList *,long *,long *)) GetProcAddress(mib_ii_dll, "SnmpExtensionQuery");
|
||||
SnmpUtilMemAllocPtr = (void *(__stdcall *)(unsigned long)) GetProcAddress(snmpapi_dll, "SnmpUtilMemAlloc");
|
||||
SnmpUtilMemFreePtr = (void (__stdcall *)(void *)) GetProcAddress(snmpapi_dll, "SnmpUtilMemFree");
|
||||
if (SnmpExtensionInitPtr == NULL || SnmpExtensionQueryPtr == NULL || SnmpUtilMemAllocPtr == NULL || SnmpUtilMemFreePtr == NULL) {
|
||||
DEBUG_LOG(("Failed to get proc addresses for linked functions\n"));
|
||||
FreeLibrary(snmpapi_dll);
|
||||
FreeLibrary(mib_ii_dll);
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
RFC1157VarBindList *bind_list_ptr = (RFC1157VarBindList *) SnmpUtilMemAllocPtr(sizeof(RFC1157VarBindList));
|
||||
RFC1157VarBind *bind_ptr = (RFC1157VarBind *) SnmpUtilMemAllocPtr(sizeof(RFC1157VarBind));
|
||||
|
||||
/*
|
||||
** OK, here we go. Try to initialise the .dll
|
||||
*/
|
||||
DEBUG_LOG(("About to init INETMIB1.DLL\n"));
|
||||
int ok = SnmpExtensionInitPtr(GetCurrentTime(), &trap_handle, &first_supported_region);
|
||||
|
||||
if (!ok) {
|
||||
/*
|
||||
** Aw crap.
|
||||
*/
|
||||
DEBUG_LOG(("Failed to init the .dll\n"));
|
||||
SnmpUtilMemFreePtr(bind_list_ptr);
|
||||
SnmpUtilMemFreePtr(bind_ptr);
|
||||
FreeLibrary(snmpapi_dll);
|
||||
FreeLibrary(mib_ii_dll);
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
** Name of mib_ii object we want to query. See RFC 1213.
|
||||
**
|
||||
** iso.org.dod.internet.mgmt.mib-2.tcp.tcpConnTable.TcpConnEntry.tcpConnState
|
||||
** 1 3 6 1 2 1 6 13 1 1
|
||||
*/
|
||||
unsigned int mib_ii_name[] = {1,3,6,1,2,1,6,13,1,1};
|
||||
unsigned int *mib_ii_name_ptr = (unsigned int *) SnmpUtilMemAllocPtr(sizeof(mib_ii_name));
|
||||
memcpy(mib_ii_name_ptr, mib_ii_name, sizeof(mib_ii_name));
|
||||
|
||||
/*
|
||||
** Get the index of the conn entry data.
|
||||
*/
|
||||
conn_entry_type_index = ARRAY_SIZE(mib_ii_name) - 1;
|
||||
|
||||
/*
|
||||
** Set up the bind list.
|
||||
*/
|
||||
bind_ptr->name.idLength = ARRAY_SIZE(mib_ii_name);
|
||||
bind_ptr->name.ids = mib_ii_name;
|
||||
bind_list_ptr->list = bind_ptr;
|
||||
bind_list_ptr->len = 1;
|
||||
|
||||
|
||||
/*
|
||||
** We start with the tcpConnLocalAddress field.
|
||||
*/
|
||||
last_field = 1;
|
||||
|
||||
/*
|
||||
** First connection.
|
||||
*/
|
||||
index = 0;
|
||||
|
||||
/*
|
||||
** Suck out that tcp connection info....
|
||||
*/
|
||||
while (true) {
|
||||
|
||||
if (!SnmpExtensionQueryPtr(SNMP_PDU_GETNEXT, bind_list_ptr, &error_status, &error_index)) {
|
||||
//if (!SnmpExtensionQueryPtr(ASN_RFC1157_GETNEXTREQUEST, bind_list_ptr, &error_status, &error_index)) {
|
||||
DEBUG_LOG(("SnmpExtensionQuery returned false\n"));
|
||||
SnmpUtilMemFreePtr(bind_list_ptr);
|
||||
SnmpUtilMemFreePtr(bind_ptr);
|
||||
FreeLibrary(snmpapi_dll);
|
||||
FreeLibrary(mib_ii_dll);
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
** If this is something new we aren't looking for then we are done.
|
||||
*/
|
||||
if (bind_ptr->name.idLength < ARRAY_SIZE(mib_ii_name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** Get the type of info we are looking at. See RFC1213.
|
||||
**
|
||||
** 1 = tcpConnState
|
||||
** 2 = tcpConnLocalAddress
|
||||
** 3 = tcpConnLocalPort
|
||||
** 4 = tcpConnRemAddress
|
||||
** 5 = tcpConnRemPort
|
||||
**
|
||||
** tcpConnState is one of the following...
|
||||
**
|
||||
** 1 closed
|
||||
** 2 listen
|
||||
** 3 synSent
|
||||
** 4 synReceived
|
||||
** 5 established
|
||||
** 6 finWait1
|
||||
** 7 finWait2
|
||||
** 8 closeWait
|
||||
** 9 lastAck
|
||||
** 10 closing
|
||||
** 11 timeWait
|
||||
** 12 deleteTCB
|
||||
*/
|
||||
conn_entry_type = bind_ptr->name.ids[conn_entry_type_index];
|
||||
|
||||
if (last_field != conn_entry_type) {
|
||||
index = 0;
|
||||
last_field = conn_entry_type;
|
||||
}
|
||||
|
||||
switch (conn_entry_type) {
|
||||
|
||||
/*
|
||||
** 1. First field in the entry. Need to create a new connection info struct
|
||||
** here to store this connection in.
|
||||
*/
|
||||
case tcpConnState:
|
||||
{
|
||||
ConnInfoStruct new_conn;
|
||||
new_conn.State = bind_ptr->value.asnValue.number;
|
||||
connectionVector.push_back(new_conn);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** 2. Local address field.
|
||||
*/
|
||||
case tcpConnLocalAddress:
|
||||
DEBUG_ASSERTCRASH(index < connectionVector.size(), ("Bad connection index"));
|
||||
connectionVector[index].LocalIP = *((unsigned long*)bind_ptr->value.asnValue.address.stream);
|
||||
index++;
|
||||
break;
|
||||
|
||||
/*
|
||||
** 3. Local port field.
|
||||
*/
|
||||
case tcpConnLocalPort:
|
||||
DEBUG_ASSERTCRASH(index < connectionVector.size(), ("Bad connection index"));
|
||||
connectionVector[index].LocalPort = bind_ptr->value.asnValue.number;
|
||||
//connectionVector[index]->LocalPort = ntohs(connectionVector[index]->LocalPort);
|
||||
index++;
|
||||
break;
|
||||
|
||||
/*
|
||||
** 4. Remote address field.
|
||||
*/
|
||||
case tcpConnRemAddress:
|
||||
DEBUG_ASSERTCRASH(index < connectionVector.size(), ("Bad connection index"));
|
||||
connectionVector[index].RemoteIP = *((unsigned long*)bind_ptr->value.asnValue.address.stream);
|
||||
index++;
|
||||
break;
|
||||
|
||||
/*
|
||||
** 5. Remote port field.
|
||||
*/
|
||||
case tcpConnRemPort:
|
||||
DEBUG_ASSERTCRASH(index < connectionVector.size(), ("Bad connection index"));
|
||||
connectionVector[index].RemotePort = bind_ptr->value.asnValue.number;
|
||||
//connectionVector[index]->RemotePort = ntohs(connectionVector[index]->RemotePort);
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SnmpUtilMemFreePtr(bind_list_ptr);
|
||||
SnmpUtilMemFreePtr(bind_ptr);
|
||||
SnmpUtilMemFreePtr(mib_ii_name_ptr);
|
||||
|
||||
DEBUG_LOG(("Got %d connections in list, parsing...\n", connectionVector.size()));
|
||||
|
||||
/*
|
||||
** Right, we got the lot. Lets see if any of them have the same address as the chat
|
||||
** server we think we are talking to.
|
||||
*/
|
||||
found = false;
|
||||
for (Int i=0; i<connectionVector.size(); ++i) {
|
||||
ConnInfoStruct connection = connectionVector[i];
|
||||
|
||||
temp = ntohl(connection.RemoteIP);
|
||||
memcpy(remoteAddress, (unsigned char*)&temp, 4);
|
||||
|
||||
/*
|
||||
** See if this connection has the same address as our server.
|
||||
*/
|
||||
if (!found && memcmp(remoteAddress, serverAddress, 4) == 0) {
|
||||
DEBUG_LOG(("Found connection with same remote address as server\n"));
|
||||
|
||||
if (serverPort == 0 || serverPort == (unsigned int)connection.RemotePort) {
|
||||
|
||||
DEBUG_LOG(("Connection has same port\n"));
|
||||
/*
|
||||
** Make sure the connection is current.
|
||||
*/
|
||||
if (connection.State == ESTABLISHED) {
|
||||
DEBUG_LOG(("Connection is ESTABLISHED\n"));
|
||||
localIP = connection.LocalIP;
|
||||
found = true;
|
||||
} else {
|
||||
DEBUG_LOG(("Connection is not ESTABLISHED - skipping\n"));
|
||||
}
|
||||
} else {
|
||||
DEBUG_LOG(("Connection has different port. Port is %d, looking for %d\n", connection.RemotePort, serverPort));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
DEBUG_LOG(("Using address 0x%8.8X to talk to chat server\n", localIP));
|
||||
}
|
||||
|
||||
FreeLibrary(snmpapi_dll);
|
||||
FreeLibrary(mib_ii_dll);
|
||||
return(found);
|
||||
}
|
||||
|
||||
// GameSpyGameSlot ----------------------------------------
|
||||
|
||||
void GameSpyGameSlot::setPingString( AsciiString pingStr )
|
||||
{
|
||||
m_pingStr = pingStr;
|
||||
m_pingInt = TheGameSpyInfo->getPingValue(pingStr);
|
||||
}
|
||||
|
||||
// GameSpyStagingRoom ----------------------------------------
|
||||
|
||||
GameSpyStagingRoom::GameSpyStagingRoom()
|
||||
{
|
||||
cleanUpSlotPointers();
|
||||
|
||||
setLocalIP(0);
|
||||
m_transport = NULL;
|
||||
|
||||
m_localName = "localhost";
|
||||
|
||||
m_ladderIP.clear();
|
||||
m_ladderPort = 0;
|
||||
}
|
||||
|
||||
void GameSpyStagingRoom::cleanUpSlotPointers( void )
|
||||
{
|
||||
for (Int i = 0; i< MAX_SLOTS; ++i)
|
||||
setSlotPointer(i, &m_GameSpySlot[i]);
|
||||
}
|
||||
|
||||
GameSpyGameSlot * GameSpyStagingRoom::getGameSpySlot( Int index )
|
||||
{
|
||||
GameSlot *slot = getSlot(index);
|
||||
DEBUG_ASSERTCRASH(slot && (slot == &(m_GameSpySlot[index])), ("Bad game slot pointer\n"));
|
||||
return (GameSpyGameSlot *)slot;
|
||||
}
|
||||
|
||||
void GameSpyStagingRoom::init( void )
|
||||
{
|
||||
GameInfo::init();
|
||||
}
|
||||
|
||||
void GameSpyStagingRoom::setPingString( AsciiString pingStr )
|
||||
{
|
||||
m_pingStr = pingStr;
|
||||
m_pingInt = TheGameSpyInfo->getPingValue(pingStr);
|
||||
}
|
||||
|
||||
Bool GameSpyStagingRoom::amIHost( void ) const
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_inGame, ("Looking for game slot while not in game"));
|
||||
if (!m_inGame)
|
||||
return false;
|
||||
|
||||
return getConstSlot(0)->isPlayer(m_localName);
|
||||
}
|
||||
|
||||
void GameSpyStagingRoom::resetAccepted( void )
|
||||
{
|
||||
GameInfo::resetAccepted();
|
||||
|
||||
if (amIHost())
|
||||
{
|
||||
/*
|
||||
peerStateChanged(TheGameSpyChat->getPeer());
|
||||
m_hasBeenQueried = false;
|
||||
DEBUG_LOG(("resetAccepted() called peerStateChange()\n"));
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
Int GameSpyStagingRoom::getLocalSlotNum( void ) const
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_inGame, ("Looking for local game slot while not in game"));
|
||||
if (!m_inGame)
|
||||
return -1;
|
||||
|
||||
AsciiString localName = TheGameSpyInfo->getLocalName();
|
||||
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
const GameSlot *slot = getConstSlot(i);
|
||||
if (slot == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (slot->isPlayer(localName))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void GameSpyStagingRoom::startGame(Int gameID)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_inGame, ("Starting a game while not in game"));
|
||||
DEBUG_LOG(("GameSpyStagingRoom::startGame - game id = %d\n", gameID));
|
||||
DEBUG_ASSERTCRASH(m_transport == NULL, ("m_transport is not NULL when it should be"));
|
||||
DEBUG_ASSERTCRASH(TheNAT == NULL, ("TheNAT is not NULL when it should be"));
|
||||
|
||||
UnsignedInt localIP = TheGameSpyInfo->getInternalIP();
|
||||
setLocalIP(localIP);
|
||||
|
||||
if (TheNAT != NULL) {
|
||||
delete TheNAT;
|
||||
TheNAT = NULL;
|
||||
}
|
||||
|
||||
// fill in GS-specific info
|
||||
Int numHumans = 0;
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
if (m_GameSpySlot[i].isHuman())
|
||||
{
|
||||
++numHumans;
|
||||
AsciiString gsName;
|
||||
gsName.translate( m_GameSpySlot[i].getName() );
|
||||
m_GameSpySlot[i].setLoginName( gsName );
|
||||
|
||||
if (m_isQM)
|
||||
{
|
||||
if (getLocalSlotNum() == i)
|
||||
m_GameSpySlot[i].setProfileID(TheGameSpyInfo->getLocalProfileID()); // hehe - we know our own. the rest, they'll tell us.
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayerInfoMap *pInfoMap = TheGameSpyInfo->getPlayerInfoMap();
|
||||
PlayerInfoMap::iterator it = pInfoMap->find(gsName);
|
||||
if (it != pInfoMap->end())
|
||||
{
|
||||
m_GameSpySlot[i].setProfileID(it->second.m_profileID);
|
||||
m_GameSpySlot[i].setLocale(it->second.m_locale);
|
||||
m_GameSpySlot[i].setSlotRankPoints(it->second.m_rankPoints);
|
||||
m_GameSpySlot[i].setFavoriteSide(it->second.m_side);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_CRASH(("No player info for %s", gsName.str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if (numHumans < 2)
|
||||
{
|
||||
launchGame();
|
||||
if (TheGameSpyInfo)
|
||||
TheGameSpyInfo->leaveStagingRoom();
|
||||
}
|
||||
else
|
||||
//#endif defined(_DEBUG) || defined(_INTERNAL)
|
||||
{
|
||||
TheNAT = NEW NAT();
|
||||
TheNAT->attachSlotList(m_slot, getLocalSlotNum(), m_localIP);
|
||||
TheNAT->establishConnectionPaths();
|
||||
}
|
||||
}
|
||||
|
||||
AsciiString GameSpyStagingRoom::generateGameSpyGameResultsPacket( void )
|
||||
{
|
||||
Int i;
|
||||
Int endFrame = TheVictoryConditions->getEndFrame();
|
||||
Int localSlotNum = getLocalSlotNum();
|
||||
Int winningTeam = -1;
|
||||
Int numHumans = 0;
|
||||
Int numPlayers = 0;
|
||||
Int numAIs = 0;
|
||||
Int numTeamsAtGameEnd = 0;
|
||||
Int lastTeamAtGameEnd = -1;
|
||||
for (i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
AsciiString playerName;
|
||||
playerName.format("player%d", i);
|
||||
Player *p = ThePlayerList->findPlayerWithNameKey(NAMEKEY(playerName));
|
||||
if (p)
|
||||
{
|
||||
++numHumans;
|
||||
if (TheVictoryConditions->hasAchievedVictory(p))
|
||||
{
|
||||
winningTeam = getSlot(i)->getTeamNumber();
|
||||
}
|
||||
|
||||
// check if he lasted
|
||||
GameSlot *slot = getSlot(i);
|
||||
if (!slot->disconnected())
|
||||
{
|
||||
if (slot->getTeamNumber() != lastTeamAtGameEnd || numTeamsAtGameEnd == 0)
|
||||
{
|
||||
lastTeamAtGameEnd = slot->getTeamNumber();
|
||||
++numTeamsAtGameEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_GameSpySlot[i].isAI())
|
||||
++numAIs;
|
||||
}
|
||||
}
|
||||
numPlayers = numHumans + numAIs;
|
||||
|
||||
AsciiString mapName;
|
||||
for (i=0; i<getMap().getLength(); ++i)
|
||||
{
|
||||
char c = getMap().getCharAt(i);
|
||||
if (c == '\\')
|
||||
c = '/';
|
||||
mapName.concat(c);
|
||||
}
|
||||
|
||||
AsciiString results;
|
||||
results.format("\\seed\\%d\\hostname\\%s\\mapname\\%s\\numplayers\\%d\\duration\\%d\\gamemode\\exiting\\localplayer\\%d",
|
||||
getSeed(), m_GameSpySlot[0].getLoginName().str(), mapName.str(), numPlayers, endFrame, localSlotNum);
|
||||
|
||||
Int playerID = 0;
|
||||
for (i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
AsciiString playerName;
|
||||
playerName.format("player%d", i);
|
||||
Player *p = ThePlayerList->findPlayerWithNameKey(NAMEKEY(playerName));
|
||||
if (p)
|
||||
{
|
||||
GameSpyGameSlot *slot = &(m_GameSpySlot[i]);
|
||||
AsciiString playerName = (slot->isHuman())?slot->getLoginName():"AIPlayer";
|
||||
Int gsPlayerID = slot->getProfileID();
|
||||
Bool disconnected = slot->disconnected();
|
||||
|
||||
AsciiString result = "loss", side = "USA";
|
||||
if (disconnected)
|
||||
result = "discon";
|
||||
else if (TheNetwork->sawCRCMismatch())
|
||||
result = "desync";
|
||||
else if (TheVictoryConditions->hasAchievedVictory(p))
|
||||
result = "win";
|
||||
|
||||
side = p->getPlayerTemplate()->getSide();
|
||||
if (side == "America")
|
||||
side = "USA";
|
||||
|
||||
AsciiString playerStr;
|
||||
playerStr.format("\\player_%d\\%s\\pid_%d\\%d\\team_%d\\%d\\result_%d\\%s\\side_%d\\%s",
|
||||
playerID, playerName.str(), playerID, gsPlayerID, playerID, slot->getTeamNumber(),
|
||||
playerID, result.str(), playerID, side.str());
|
||||
results.concat(playerStr);
|
||||
|
||||
++playerID;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
AsciiString GameSpyStagingRoom::generateLadderGameResultsPacket( void )
|
||||
{
|
||||
Int i;
|
||||
Int endFrame = TheVictoryConditions->getEndFrame();
|
||||
Int localSlotNum = getLocalSlotNum();
|
||||
Bool sawGameEnd = (endFrame > 0);
|
||||
Int winningTeam = -1;
|
||||
Int numPlayers = 0;
|
||||
Int numTeamsAtGameEnd = 0;
|
||||
Int lastTeamAtGameEnd = -1;
|
||||
Player* p[MAX_SLOTS];
|
||||
for (i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
AsciiString playerName;
|
||||
playerName.format("player%d", i);
|
||||
p[i] = ThePlayerList->findPlayerWithNameKey(NAMEKEY(playerName));
|
||||
if (p[i])
|
||||
{
|
||||
++numPlayers;
|
||||
if (TheVictoryConditions->hasAchievedVictory(p[i]))
|
||||
{
|
||||
winningTeam = getSlot(i)->getTeamNumber();
|
||||
}
|
||||
|
||||
// check if he lasted
|
||||
GameSlot *slot = getSlot(i);
|
||||
if (!slot->disconnected())
|
||||
{
|
||||
if (slot->getTeamNumber() != lastTeamAtGameEnd || numTeamsAtGameEnd == 0)
|
||||
{
|
||||
lastTeamAtGameEnd = slot->getTeamNumber();
|
||||
++numTeamsAtGameEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AsciiString results;
|
||||
results.format("seed=%d,slotNum=%d,sawDesync=%d,sawGameEnd=%d,winningTeam=%d,disconEnd=%d,duration=%d,numPlayers=%d,isQM=%d,map=%s",
|
||||
getSeed(), localSlotNum, TheNetwork->sawCRCMismatch(), sawGameEnd, winningTeam, (numTeamsAtGameEnd < 2),
|
||||
endFrame, numPlayers, m_isQM, TheGameState->realMapPathToPortableMapPath(getMap()).str());
|
||||
|
||||
AsciiString tempStr;
|
||||
tempStr.format(",ladderIP=%s,ladderPort=%d", getLadderIP().str(), getLadderPort());
|
||||
results.concat(tempStr);
|
||||
|
||||
Int playerID = 0;
|
||||
for (i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
AsciiString playerName;
|
||||
playerName.format("player%d", i);
|
||||
if (p[i])
|
||||
{
|
||||
GameSpyGameSlot *slot = &(m_GameSpySlot[i]);
|
||||
ScoreKeeper *keeper = p[i]->getScoreKeeper();
|
||||
AsciiString playerName = slot->getLoginName();
|
||||
Int gsPlayerID = slot->getProfileID();
|
||||
PSPlayerStats stats = TheGameSpyPSMessageQueue->findPlayerStatsByID(gsPlayerID);
|
||||
Int fps = TheNetwork->getAverageFPS();
|
||||
Int unitsKilled = keeper->getTotalUnitsDestroyed();
|
||||
Int unitsLost = keeper->getTotalUnitsLost();
|
||||
Int unitsBuilt = keeper->getTotalUnitsBuilt();
|
||||
Int buildingsKilled = keeper->getTotalBuildingsDestroyed();
|
||||
Int buildingsLost = keeper->getTotalBuildingsLost();
|
||||
Int buildingsBuilt = keeper->getTotalBuildingsBuilt();
|
||||
Int earnings = keeper->getTotalMoneyEarned();
|
||||
Int techCaptured = keeper->getTotalTechBuildingsCaptured();
|
||||
Bool disconnected = slot->disconnected();
|
||||
|
||||
AsciiString playerStr;
|
||||
playerStr.format(",player%d=%s,playerID%d=%d,locale%d=%d",
|
||||
playerID, playerName.str(), playerID, gsPlayerID, playerID, stats.locale);
|
||||
results.concat(playerStr);
|
||||
playerStr.format(",unitsKilled%d=%d,unitsLost%d=%d,unitsBuilt%d=%d",
|
||||
playerID, unitsKilled, playerID, unitsLost, playerID, unitsBuilt);
|
||||
results.concat(playerStr);
|
||||
playerStr.format(",buildingsKilled%d=%d,buildingsLost%d=%d,buildingsBuilt%d=%d",
|
||||
playerID, buildingsKilled, playerID, buildingsLost, playerID, buildingsBuilt);
|
||||
results.concat(playerStr);
|
||||
playerStr.format(",fps%d=%d,cash%d=%d,capturedTech%d=%d,discon%d=%d,side%d=%s,team%d=%d",
|
||||
playerID, fps, playerID, earnings, playerID, techCaptured, playerID, disconnected, playerID, p[i]->getPlayerTemplate()->getSide().str(), playerID, slot->getTeamNumber());
|
||||
results.concat(playerStr);
|
||||
|
||||
++playerID;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a trailing size value (so the server can ensure it got the entire packet)
|
||||
results.concat(",size=");
|
||||
int resultsLen = results.getLength()+10;
|
||||
AsciiString tail;
|
||||
tail.format("%10.10d", resultsLen);
|
||||
results.concat(tail);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
void GameSpyStagingRoom::launchGame( void )
|
||||
{
|
||||
setGameInProgress(TRUE);
|
||||
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
const GameSpyGameSlot *slot = getGameSpySlot(i);
|
||||
if (slot->isHuman())
|
||||
{
|
||||
if (TheGameSpyInfo->didPlayerPreorder(slot->getProfileID()))
|
||||
markPlayerAsPreorder(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Set up the game network
|
||||
AsciiString user;
|
||||
AsciiString userList;
|
||||
DEBUG_ASSERTCRASH(TheNetwork == NULL, ("For some reason TheNetwork isn't NULL at the start of this game. Better look into that."));
|
||||
|
||||
if (TheNetwork != NULL) {
|
||||
delete TheNetwork;
|
||||
TheNetwork = NULL;
|
||||
}
|
||||
|
||||
// Time to initialize TheNetwork for this game.
|
||||
TheNetwork = NetworkInterface::createNetwork();
|
||||
TheNetwork->init();
|
||||
|
||||
TheNetwork->setLocalAddress(getLocalIP(), (TheNAT)?TheNAT->getSlotPort(getLocalSlotNum()):8888);
|
||||
if (TheNAT)
|
||||
TheNetwork->attachTransport(TheNAT->getTransport());
|
||||
else
|
||||
TheNetwork->initTransport();
|
||||
|
||||
TheNetwork->parseUserList(this);
|
||||
|
||||
|
||||
if (TheGameLogic->isInGame()) {
|
||||
TheGameLogic->clearGameData();
|
||||
}
|
||||
|
||||
Bool filesOk = DoAnyMapTransfers(this);
|
||||
|
||||
// see if we really have the map. if not, back out.
|
||||
TheMapCache->updateCache();
|
||||
if (!filesOk || TheMapCache->findMap(getMap()) == NULL)
|
||||
{
|
||||
DEBUG_LOG(("After transfer, we didn't really have the map. Bailing...\n"));
|
||||
if (TheNetwork != NULL) {
|
||||
delete TheNetwork;
|
||||
TheNetwork = NULL;
|
||||
}
|
||||
GSMessageBoxOk(TheGameText->fetch("GUI:Error"), TheGameText->fetch("GUI:CouldNotTransferMap"));
|
||||
|
||||
void PopBackToLobby( void );
|
||||
PopBackToLobby();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// shutdown the top, but do not pop it off the stack
|
||||
// TheShell->hideShell();
|
||||
// setup the Global Data with the Map and Seed
|
||||
TheWritableGlobalData->m_pendingFile = TheGameSpyGame->getMap();
|
||||
|
||||
// send a message to the logic for a new game
|
||||
GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_NEW_GAME );
|
||||
msg->appendIntegerArgument(GAME_INTERNET);
|
||||
|
||||
TheWritableGlobalData->m_useFpsLimit = false;
|
||||
|
||||
// Set the random seed
|
||||
InitGameLogicRandom( getSeed() );
|
||||
DEBUG_LOG(("InitGameLogicRandom( %d )\n", getSeed()));
|
||||
|
||||
// mark us as "Loading" in the buddy list
|
||||
BuddyRequest req;
|
||||
req.buddyRequestType = BuddyRequest::BUDDYREQUEST_SETSTATUS;
|
||||
req.arg.status.status = GP_PLAYING;
|
||||
strcpy(req.arg.status.statusString, "Loading");
|
||||
sprintf(req.arg.status.locationString, "%s", WideCharStringToMultiByte(TheGameSpyGame->getGameName().str()).c_str());
|
||||
TheGameSpyBuddyMessageQueue->addRequest(req);
|
||||
|
||||
if (TheNAT != NULL) {
|
||||
delete TheNAT;
|
||||
TheNAT = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyStagingRoom::reset(void)
|
||||
{
|
||||
#ifdef DEBUG_LOGGING
|
||||
if (this == TheGameSpyGame)
|
||||
{
|
||||
WindowLayout *theLayout = TheShell->findScreenByFilename("Menus/GameSpyGameOptionsMenu.wnd");
|
||||
if (theLayout)
|
||||
{
|
||||
DEBUG_LOG(("Resetting TheGameSpyGame on the game options menu!\n"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
GameInfo::reset();
|
||||
}
|
||||
@@ -0,0 +1,679 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: BuddyThread.cpp //////////////////////////////////////////////////////
|
||||
// GameSpy Presence & Messaging (Buddy) thread
|
||||
// This thread communicates with GameSpy's buddy 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 "GameNetwork/GameSpy/BuddyThread.h"
|
||||
#include "GameNetwork/GameSpy/PeerThread.h"
|
||||
#include "GameNetwork/GameSpy/PersistentStorageThread.h"
|
||||
#include "GameNetwork/GameSpy/ThreadUtils.h"
|
||||
|
||||
#include "Common/StackDump.h"
|
||||
|
||||
#include "mutex.h"
|
||||
#include "thread.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
typedef std::queue<BuddyRequest> RequestQueue;
|
||||
typedef std::queue<BuddyResponse> ResponseQueue;
|
||||
class BuddyThreadClass;
|
||||
|
||||
class GameSpyBuddyMessageQueue : public GameSpyBuddyMessageQueueInterface
|
||||
{
|
||||
public:
|
||||
virtual ~GameSpyBuddyMessageQueue();
|
||||
GameSpyBuddyMessageQueue();
|
||||
virtual void startThread( void );
|
||||
virtual void endThread( void );
|
||||
virtual Bool isThreadRunning( void );
|
||||
virtual Bool isConnected( void );
|
||||
virtual Bool isConnecting( void );
|
||||
|
||||
virtual void addRequest( const BuddyRequest& req );
|
||||
virtual Bool getRequest( BuddyRequest& req );
|
||||
|
||||
virtual void addResponse( const BuddyResponse& resp );
|
||||
virtual Bool getResponse( BuddyResponse& resp );
|
||||
|
||||
virtual GPProfile getLocalProfileID( void );
|
||||
|
||||
BuddyThreadClass* getThread( void );
|
||||
|
||||
private:
|
||||
MutexClass m_requestMutex;
|
||||
MutexClass m_responseMutex;
|
||||
RequestQueue m_requests;
|
||||
ResponseQueue m_responses;
|
||||
BuddyThreadClass *m_thread;
|
||||
};
|
||||
|
||||
GameSpyBuddyMessageQueueInterface* GameSpyBuddyMessageQueueInterface::createNewMessageQueue( void )
|
||||
{
|
||||
return NEW GameSpyBuddyMessageQueue;
|
||||
}
|
||||
|
||||
GameSpyBuddyMessageQueueInterface *TheGameSpyBuddyMessageQueue;
|
||||
#define MESSAGE_QUEUE ((GameSpyBuddyMessageQueue *)TheGameSpyBuddyMessageQueue)
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
class BuddyThreadClass : public ThreadClass
|
||||
{
|
||||
|
||||
public:
|
||||
BuddyThreadClass() : ThreadClass() { m_isNewAccount = m_isdeleting = m_isConnecting = m_isConnected = false; m_profileID = 0; m_lastErrorCode = 0; }
|
||||
|
||||
void Thread_Function();
|
||||
|
||||
void errorCallback( GPConnection *con, GPErrorArg *arg );
|
||||
void messageCallback( GPConnection *con, GPRecvBuddyMessageArg *arg );
|
||||
void connectCallback( GPConnection *con, GPConnectResponseArg *arg );
|
||||
void requestCallback( GPConnection *con, GPRecvBuddyRequestArg *arg );
|
||||
void statusCallback( GPConnection *con, GPRecvBuddyStatusArg *arg );
|
||||
|
||||
Bool isConnecting( void ) { return m_isConnecting; }
|
||||
Bool isConnected( void ) { return m_isConnected; }
|
||||
|
||||
GPProfile getLocalProfileID( void ) { return m_profileID; }
|
||||
|
||||
private:
|
||||
Bool m_isNewAccount;
|
||||
Bool m_isConnecting;
|
||||
Bool m_isConnected;
|
||||
GPProfile m_profileID;
|
||||
Int m_lastErrorCode;
|
||||
Bool m_isdeleting;
|
||||
std::string m_nick, m_email, m_pass;
|
||||
};
|
||||
|
||||
static enum CallbackType
|
||||
{
|
||||
CALLBACK_CONNECT,
|
||||
CALLBACK_ERROR,
|
||||
CALLBACK_RECVMESSAGE,
|
||||
CALLBACK_RECVREQUEST,
|
||||
CALLBACK_RECVSTATUS,
|
||||
CALLBACK_MAX
|
||||
};
|
||||
|
||||
void callbackWrapper( GPConnection *con, void *arg, void *param )
|
||||
{
|
||||
CallbackType info = (CallbackType)(Int)param;
|
||||
BuddyThreadClass *thread = MESSAGE_QUEUE->getThread() ? MESSAGE_QUEUE->getThread() : NULL /*(TheGameSpyBuddyMessageQueue)?TheGameSpyBuddyMessageQueue->getThread():NULL*/;
|
||||
if (!thread)
|
||||
return;
|
||||
|
||||
switch (info)
|
||||
{
|
||||
case CALLBACK_CONNECT:
|
||||
thread->connectCallback( con, (GPConnectResponseArg *)arg );
|
||||
break;
|
||||
case CALLBACK_ERROR:
|
||||
thread->errorCallback( con, (GPErrorArg *)arg );
|
||||
break;
|
||||
case CALLBACK_RECVMESSAGE:
|
||||
thread->messageCallback( con, (GPRecvBuddyMessageArg *)arg );
|
||||
break;
|
||||
case CALLBACK_RECVREQUEST:
|
||||
thread->requestCallback( con, (GPRecvBuddyRequestArg *)arg );
|
||||
break;
|
||||
case CALLBACK_RECVSTATUS:
|
||||
thread->statusCallback( con, (GPRecvBuddyStatusArg *)arg );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
GameSpyBuddyMessageQueue::GameSpyBuddyMessageQueue()
|
||||
{
|
||||
m_thread = NULL;
|
||||
}
|
||||
|
||||
GameSpyBuddyMessageQueue::~GameSpyBuddyMessageQueue()
|
||||
{
|
||||
endThread();
|
||||
}
|
||||
|
||||
void GameSpyBuddyMessageQueue::startThread( void )
|
||||
{
|
||||
if (!m_thread)
|
||||
{
|
||||
m_thread = NEW BuddyThreadClass;
|
||||
m_thread->Execute();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_thread->Is_Running())
|
||||
{
|
||||
m_thread->Execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyBuddyMessageQueue::endThread( void )
|
||||
{
|
||||
if (m_thread)
|
||||
delete m_thread;
|
||||
m_thread = NULL;
|
||||
}
|
||||
|
||||
Bool GameSpyBuddyMessageQueue::isThreadRunning( void )
|
||||
{
|
||||
return (m_thread) ? m_thread->Is_Running() : false;
|
||||
}
|
||||
|
||||
Bool GameSpyBuddyMessageQueue::isConnected( void )
|
||||
{
|
||||
return (m_thread) ? m_thread->isConnected() : false;
|
||||
}
|
||||
|
||||
Bool GameSpyBuddyMessageQueue::isConnecting( void )
|
||||
{
|
||||
return (m_thread) ? m_thread->isConnecting() : false;
|
||||
}
|
||||
|
||||
void GameSpyBuddyMessageQueue::addRequest( const BuddyRequest& req )
|
||||
{
|
||||
MutexClass::LockClass m(m_requestMutex);
|
||||
if (m.Failed())
|
||||
return;
|
||||
|
||||
m_requests.push(req);
|
||||
}
|
||||
|
||||
Bool GameSpyBuddyMessageQueue::getRequest( BuddyRequest& 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 GameSpyBuddyMessageQueue::addResponse( const BuddyResponse& resp )
|
||||
{
|
||||
MutexClass::LockClass m(m_responseMutex);
|
||||
if (m.Failed())
|
||||
return;
|
||||
|
||||
m_responses.push(resp);
|
||||
}
|
||||
|
||||
Bool GameSpyBuddyMessageQueue::getResponse( BuddyResponse& 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;
|
||||
}
|
||||
|
||||
BuddyThreadClass* GameSpyBuddyMessageQueue::getThread( void )
|
||||
{
|
||||
return m_thread;
|
||||
}
|
||||
|
||||
GPProfile GameSpyBuddyMessageQueue::getLocalProfileID( void )
|
||||
{
|
||||
return (m_thread) ? m_thread->getLocalProfileID() : 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void BuddyThreadClass::Thread_Function()
|
||||
{
|
||||
try {
|
||||
_set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace.
|
||||
GPConnection gpCon;
|
||||
GPConnection *con = &gpCon;
|
||||
gpInitialize( con, 0 );
|
||||
m_isConnected = m_isConnecting = false;
|
||||
|
||||
gpSetCallback( con, GP_ERROR, callbackWrapper, (void *)CALLBACK_ERROR );
|
||||
gpSetCallback( con, GP_RECV_BUDDY_MESSAGE, callbackWrapper, (void *)CALLBACK_RECVMESSAGE );
|
||||
gpSetCallback( con, GP_RECV_BUDDY_REQUEST, callbackWrapper, (void *)CALLBACK_RECVREQUEST );
|
||||
gpSetCallback( con, GP_RECV_BUDDY_STATUS, callbackWrapper, (void *)CALLBACK_RECVSTATUS );
|
||||
|
||||
GPEnum lastStatus = GP_OFFLINE;
|
||||
std::string lastStatusString;
|
||||
|
||||
BuddyRequest incomingRequest;
|
||||
while ( running )
|
||||
{
|
||||
// deal with requests
|
||||
if (TheGameSpyBuddyMessageQueue->getRequest(incomingRequest))
|
||||
{
|
||||
switch (incomingRequest.buddyRequestType)
|
||||
{
|
||||
case BuddyRequest::BUDDYREQUEST_LOGIN:
|
||||
m_isConnecting = true;
|
||||
m_nick = incomingRequest.arg.login.nick;
|
||||
m_email = incomingRequest.arg.login.email;
|
||||
m_pass = incomingRequest.arg.login.password;
|
||||
m_isConnected = (gpConnect( con, incomingRequest.arg.login.nick, incomingRequest.arg.login.email,
|
||||
incomingRequest.arg.login.password, (incomingRequest.arg.login.hasFirewall)?GP_FIREWALL:GP_NO_FIREWALL,
|
||||
GP_BLOCKING, callbackWrapper, (void *)CALLBACK_CONNECT ) == GP_NO_ERROR);
|
||||
m_isConnecting = false;
|
||||
break;
|
||||
|
||||
case BuddyRequest::BUDDYREQUEST_RELOGIN:
|
||||
m_isConnecting = true;
|
||||
m_isConnected = (gpConnect( con, m_nick.c_str(), m_email.c_str(), m_pass.c_str(), GP_FIREWALL,
|
||||
GP_BLOCKING, callbackWrapper, (void *)CALLBACK_CONNECT ) == GP_NO_ERROR);
|
||||
m_isConnecting = false;
|
||||
break;
|
||||
case BuddyRequest::BUDDYREQUEST_DELETEACCT:
|
||||
m_isdeleting = true;
|
||||
gpDeleteProfile( con );
|
||||
break;
|
||||
case BuddyRequest::BUDDYREQUEST_LOGOUT:
|
||||
m_isConnecting = m_isConnected = false;
|
||||
gpDisconnect( con );
|
||||
break;
|
||||
case BuddyRequest::BUDDYREQUEST_MESSAGE:
|
||||
{
|
||||
std::string s = WideCharStringToMultiByte( incomingRequest.arg.message.text );
|
||||
DEBUG_LOG(("Sending a buddy message to %d [%s]\n", incomingRequest.arg.message.recipient, s.c_str()));
|
||||
gpSendBuddyMessage( con, incomingRequest.arg.message.recipient, s.c_str() );
|
||||
}
|
||||
break;
|
||||
case BuddyRequest::BUDDYREQUEST_LOGINNEW:
|
||||
{
|
||||
m_isConnecting = true;
|
||||
m_nick = incomingRequest.arg.login.nick;
|
||||
m_email = incomingRequest.arg.login.email;
|
||||
m_pass = incomingRequest.arg.login.password;
|
||||
m_isNewAccount = TRUE;
|
||||
m_isConnected = (gpConnectNewUser( con, incomingRequest.arg.login.nick, incomingRequest.arg.login.email,
|
||||
incomingRequest.arg.login.password, (incomingRequest.arg.login.hasFirewall)?GP_FIREWALL:GP_NO_FIREWALL,
|
||||
GP_BLOCKING, callbackWrapper, (void *)CALLBACK_CONNECT ) == GP_NO_ERROR);
|
||||
if (m_isNewAccount) // if we didn't re-login
|
||||
{
|
||||
gpSetInfoMask( con, GP_MASK_NONE ); // don't share info
|
||||
}
|
||||
m_isConnecting = false;
|
||||
}
|
||||
break;
|
||||
case BuddyRequest::BUDDYREQUEST_ADDBUDDY:
|
||||
{
|
||||
std::string s = WideCharStringToMultiByte( incomingRequest.arg.addbuddy.text );
|
||||
gpSendBuddyRequest( con, incomingRequest.arg.addbuddy.id, s.c_str() );
|
||||
}
|
||||
break;
|
||||
case BuddyRequest::BUDDYREQUEST_DELBUDDY:
|
||||
{
|
||||
gpDeleteBuddy( con, incomingRequest.arg.profile.id );
|
||||
}
|
||||
break;
|
||||
case BuddyRequest::BUDDYREQUEST_OKADD:
|
||||
{
|
||||
gpAuthBuddyRequest( con, incomingRequest.arg.profile.id );
|
||||
}
|
||||
break;
|
||||
case BuddyRequest::BUDDYREQUEST_DENYADD:
|
||||
{
|
||||
gpDenyBuddyRequest( con, incomingRequest.arg.profile.id );
|
||||
}
|
||||
case BuddyRequest::BUDDYREQUEST_SETSTATUS:
|
||||
{
|
||||
//don't blast our 'Loading' status with 'Online'.
|
||||
if (lastStatus == GP_PLAYING && lastStatusString == "Loading" && incomingRequest.arg.status.status == GP_ONLINE)
|
||||
break;
|
||||
|
||||
DEBUG_LOG(("BUDDYREQUEST_SETSTATUS: status is now %d:%s/%s\n",
|
||||
incomingRequest.arg.status.status, incomingRequest.arg.status.statusString, incomingRequest.arg.status.locationString));
|
||||
gpSetStatus( con, incomingRequest.arg.status.status, incomingRequest.arg.status.statusString,
|
||||
incomingRequest.arg.status.locationString );
|
||||
lastStatus = incomingRequest.arg.status.status;
|
||||
lastStatusString = incomingRequest.arg.status.statusString;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// update the network
|
||||
GPEnum isConnected = GP_CONNECTED;
|
||||
GPResult res = GP_NO_ERROR;
|
||||
res = gpIsConnected( con, &isConnected );
|
||||
if ( isConnected == GP_CONNECTED && res == GP_NO_ERROR )
|
||||
gpProcess( con );
|
||||
|
||||
// end our timeslice
|
||||
Switch_Thread();
|
||||
}
|
||||
|
||||
gpDestroy( con );
|
||||
} catch ( ... ) {
|
||||
DEBUG_CRASH(("Exception in buddy thread!"));
|
||||
}
|
||||
}
|
||||
|
||||
void BuddyThreadClass::errorCallback( GPConnection *con, GPErrorArg *arg )
|
||||
{
|
||||
// log the error
|
||||
DEBUG_LOG(("GPErrorCallback\n"));
|
||||
m_lastErrorCode = arg->errorCode;
|
||||
|
||||
char errorCodeString[256];
|
||||
char resultString[256];
|
||||
|
||||
#define RESULT(x) case x: strcpy(resultString, #x); break;
|
||||
switch(arg->result)
|
||||
{
|
||||
RESULT(GP_NO_ERROR)
|
||||
RESULT(GP_MEMORY_ERROR)
|
||||
RESULT(GP_PARAMETER_ERROR)
|
||||
RESULT(GP_NETWORK_ERROR)
|
||||
RESULT(GP_SERVER_ERROR)
|
||||
default:
|
||||
strcpy(resultString, "Unknown result!");
|
||||
}
|
||||
#undef RESULT
|
||||
|
||||
#define ERRORCODE(x) case x: strcpy(errorCodeString, #x); break;
|
||||
switch(arg->errorCode)
|
||||
{
|
||||
ERRORCODE(GP_GENERAL)
|
||||
ERRORCODE(GP_PARSE)
|
||||
ERRORCODE(GP_NOT_LOGGED_IN)
|
||||
ERRORCODE(GP_BAD_SESSKEY)
|
||||
ERRORCODE(GP_DATABASE)
|
||||
ERRORCODE(GP_NETWORK)
|
||||
ERRORCODE(GP_FORCED_DISCONNECT)
|
||||
ERRORCODE(GP_CONNECTION_CLOSED)
|
||||
ERRORCODE(GP_LOGIN)
|
||||
ERRORCODE(GP_LOGIN_TIMEOUT)
|
||||
ERRORCODE(GP_LOGIN_BAD_NICK)
|
||||
ERRORCODE(GP_LOGIN_BAD_EMAIL)
|
||||
ERRORCODE(GP_LOGIN_BAD_PASSWORD)
|
||||
ERRORCODE(GP_LOGIN_BAD_PROFILE)
|
||||
ERRORCODE(GP_LOGIN_PROFILE_DELETED)
|
||||
ERRORCODE(GP_LOGIN_CONNECTION_FAILED)
|
||||
ERRORCODE(GP_LOGIN_SERVER_AUTH_FAILED)
|
||||
ERRORCODE(GP_NEWUSER)
|
||||
ERRORCODE(GP_NEWUSER_BAD_NICK)
|
||||
ERRORCODE(GP_NEWUSER_BAD_PASSWORD)
|
||||
ERRORCODE(GP_UPDATEUI)
|
||||
ERRORCODE(GP_UPDATEUI_BAD_EMAIL)
|
||||
ERRORCODE(GP_NEWPROFILE)
|
||||
ERRORCODE(GP_NEWPROFILE_BAD_NICK)
|
||||
ERRORCODE(GP_NEWPROFILE_BAD_OLD_NICK)
|
||||
ERRORCODE(GP_UPDATEPRO)
|
||||
ERRORCODE(GP_UPDATEPRO_BAD_NICK)
|
||||
ERRORCODE(GP_ADDBUDDY)
|
||||
ERRORCODE(GP_ADDBUDDY_BAD_FROM)
|
||||
ERRORCODE(GP_ADDBUDDY_BAD_NEW)
|
||||
ERRORCODE(GP_ADDBUDDY_ALREADY_BUDDY)
|
||||
ERRORCODE(GP_AUTHADD)
|
||||
ERRORCODE(GP_AUTHADD_BAD_FROM)
|
||||
ERRORCODE(GP_AUTHADD_BAD_SIG)
|
||||
ERRORCODE(GP_STATUS)
|
||||
ERRORCODE(GP_BM)
|
||||
ERRORCODE(GP_BM_NOT_BUDDY)
|
||||
ERRORCODE(GP_GETPROFILE)
|
||||
ERRORCODE(GP_GETPROFILE_BAD_PROFILE)
|
||||
ERRORCODE(GP_DELBUDDY)
|
||||
ERRORCODE(GP_DELBUDDY_NOT_BUDDY)
|
||||
ERRORCODE(GP_DELPROFILE)
|
||||
ERRORCODE(GP_DELPROFILE_LAST_PROFILE)
|
||||
ERRORCODE(GP_SEARCH)
|
||||
ERRORCODE(GP_SEARCH_CONNECTION_FAILED)
|
||||
default:
|
||||
strcpy(errorCodeString, "Unknown error code!");
|
||||
}
|
||||
#undef ERRORCODE
|
||||
|
||||
if(arg->fatal)
|
||||
{
|
||||
DEBUG_LOG(( "-----------\n"));
|
||||
DEBUG_LOG(( "GP FATAL ERROR\n"));
|
||||
DEBUG_LOG(( "-----------\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(( "-----\n"));
|
||||
DEBUG_LOG(( "GP ERROR\n"));
|
||||
DEBUG_LOG(( "-----\n"));
|
||||
}
|
||||
DEBUG_LOG(( "RESULT: %s (%d)\n", resultString, arg->result));
|
||||
DEBUG_LOG(( "ERROR CODE: %s (0x%X)\n", errorCodeString, arg->errorCode));
|
||||
DEBUG_LOG(( "ERROR STRING: %s\n", arg->errorString));
|
||||
|
||||
if (arg->fatal == GP_FATAL)
|
||||
{
|
||||
BuddyResponse errorResponse;
|
||||
errorResponse.buddyResponseType = BuddyResponse::BUDDYRESPONSE_DISCONNECT;
|
||||
errorResponse.result = arg->result;
|
||||
errorResponse.arg.error.errorCode = arg->errorCode;
|
||||
errorResponse.arg.error.fatal = arg->fatal;
|
||||
strncpy(errorResponse.arg.error.errorString, arg->errorString, MAX_BUDDY_CHAT_LEN);
|
||||
errorResponse.arg.error.errorString[MAX_BUDDY_CHAT_LEN-1] = 0;
|
||||
m_isConnecting = m_isConnected = false;
|
||||
TheGameSpyBuddyMessageQueue->addResponse( errorResponse );
|
||||
if (m_isdeleting)
|
||||
{
|
||||
PeerRequest req;
|
||||
req.peerRequestType = PeerRequest::PEERREQUEST_LOGOUT;
|
||||
TheGameSpyPeerMessageQueue->addRequest( req );
|
||||
m_isdeleting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void getNickForMessage( GPConnection *con, GPGetInfoResponseArg *arg, void *param )
|
||||
{
|
||||
BuddyResponse *resp = (BuddyResponse *)param;
|
||||
strcpy(resp->arg.message.nick, arg->nick);
|
||||
}
|
||||
|
||||
void BuddyThreadClass::messageCallback( GPConnection *con, GPRecvBuddyMessageArg *arg )
|
||||
{
|
||||
BuddyResponse messageResponse;
|
||||
messageResponse.buddyResponseType = BuddyResponse::BUDDYRESPONSE_MESSAGE;
|
||||
messageResponse.profile = arg->profile;
|
||||
|
||||
// get info about the person asking to be our buddy
|
||||
gpGetInfo( con, arg->profile, GP_CHECK_CACHE, GP_BLOCKING, (GPCallback)getNickForMessage, &messageResponse);
|
||||
|
||||
std::wstring s = MultiByteToWideCharSingleLine( arg->message );
|
||||
wcsncpy(messageResponse.arg.message.text, s.c_str(), MAX_BUDDY_CHAT_LEN);
|
||||
messageResponse.arg.message.text[MAX_BUDDY_CHAT_LEN-1] = 0;
|
||||
messageResponse.arg.message.date = arg->date;
|
||||
DEBUG_LOG(("Got a buddy message from %d [%ls]\n", arg->profile, s.c_str()));
|
||||
TheGameSpyBuddyMessageQueue->addResponse( messageResponse );
|
||||
}
|
||||
|
||||
void BuddyThreadClass::connectCallback( GPConnection *con, GPConnectResponseArg *arg )
|
||||
{
|
||||
BuddyResponse loginResponse;
|
||||
if (arg->result == GP_NO_ERROR)
|
||||
{
|
||||
loginResponse.buddyResponseType = BuddyResponse::BUDDYRESPONSE_LOGIN;
|
||||
loginResponse.result = arg->result;
|
||||
loginResponse.profile = arg->profile;
|
||||
TheGameSpyBuddyMessageQueue->addResponse( loginResponse );
|
||||
m_profileID = arg->profile;
|
||||
|
||||
if (!TheGameSpyPeerMessageQueue->isConnected() && !TheGameSpyPeerMessageQueue->isConnecting())
|
||||
{
|
||||
DEBUG_LOG(("Buddy connect: trying chat connect\n"));
|
||||
PeerRequest req;
|
||||
req.peerRequestType = PeerRequest::PEERREQUEST_LOGIN;
|
||||
req.nick = m_nick;
|
||||
req.password = m_pass;
|
||||
req.email = m_email;
|
||||
req.login.profileID = arg->profile;
|
||||
TheGameSpyPeerMessageQueue->addRequest(req);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TheGameSpyPeerMessageQueue->isConnected() && !TheGameSpyPeerMessageQueue->isConnecting())
|
||||
{
|
||||
if (m_lastErrorCode == GP_NEWUSER_BAD_NICK)
|
||||
{
|
||||
m_isNewAccount = FALSE;
|
||||
// they just hit 'create account' instead of 'log in'. Fix them.
|
||||
DEBUG_LOG(("User Error: Create Account instead of Login. Fixing them...\n"));
|
||||
BuddyRequest req;
|
||||
req.buddyRequestType = BuddyRequest::BUDDYREQUEST_LOGIN;
|
||||
strcpy(req.arg.login.nick, m_nick.c_str());
|
||||
strcpy(req.arg.login.email, m_email.c_str());
|
||||
strcpy(req.arg.login.password, m_pass.c_str());
|
||||
req.arg.login.hasFirewall = true;
|
||||
TheGameSpyBuddyMessageQueue->addRequest( req );
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG(("Buddy connect failed (%d/%d): posting a failed chat connect\n", arg->result, m_lastErrorCode));
|
||||
PeerResponse resp;
|
||||
resp.peerResponseType = PeerResponse::PEERRESPONSE_DISCONNECT;
|
||||
resp.discon.reason = DISCONNECT_COULDNOTCONNECT;
|
||||
switch (m_lastErrorCode)
|
||||
{
|
||||
case GP_LOGIN_TIMEOUT:
|
||||
resp.discon.reason = DISCONNECT_GP_LOGIN_TIMEOUT;
|
||||
break;
|
||||
case GP_LOGIN_BAD_NICK:
|
||||
resp.discon.reason = DISCONNECT_GP_LOGIN_BAD_NICK;
|
||||
break;
|
||||
case GP_LOGIN_BAD_EMAIL:
|
||||
resp.discon.reason = DISCONNECT_GP_LOGIN_BAD_EMAIL;
|
||||
break;
|
||||
case GP_LOGIN_BAD_PASSWORD:
|
||||
resp.discon.reason = DISCONNECT_GP_LOGIN_BAD_PASSWORD;
|
||||
break;
|
||||
case GP_LOGIN_BAD_PROFILE:
|
||||
resp.discon.reason = DISCONNECT_GP_LOGIN_BAD_PROFILE;
|
||||
break;
|
||||
case GP_LOGIN_PROFILE_DELETED:
|
||||
resp.discon.reason = DISCONNECT_GP_LOGIN_PROFILE_DELETED;
|
||||
break;
|
||||
case GP_LOGIN_CONNECTION_FAILED:
|
||||
resp.discon.reason = DISCONNECT_GP_LOGIN_CONNECTION_FAILED;
|
||||
break;
|
||||
case GP_LOGIN_SERVER_AUTH_FAILED:
|
||||
resp.discon.reason = DISCONNECT_GP_LOGIN_SERVER_AUTH_FAILED;
|
||||
break;
|
||||
case GP_NEWUSER_BAD_NICK:
|
||||
resp.discon.reason = DISCONNECT_GP_NEWUSER_BAD_NICK;
|
||||
break;
|
||||
case GP_NEWUSER_BAD_PASSWORD:
|
||||
resp.discon.reason = DISCONNECT_GP_NEWUSER_BAD_PASSWORD;
|
||||
break;
|
||||
case GP_NEWPROFILE_BAD_NICK:
|
||||
resp.discon.reason = DISCONNECT_GP_NEWPROFILE_BAD_NICK;
|
||||
break;
|
||||
case GP_NEWPROFILE_BAD_OLD_NICK:
|
||||
resp.discon.reason = DISCONNECT_GP_NEWPROFILE_BAD_OLD_NICK;
|
||||
break;
|
||||
}
|
||||
TheGameSpyPeerMessageQueue->addResponse(resp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
|
||||
static void getInfoResponseForRequest( GPConnection *con, GPGetInfoResponseArg *arg, void *param )
|
||||
{
|
||||
BuddyResponse *resp = (BuddyResponse *)param;
|
||||
resp->profile = arg->profile;
|
||||
strcpy(resp->arg.request.nick, arg->nick);
|
||||
strcpy(resp->arg.request.email, arg->email);
|
||||
strcpy(resp->arg.request.countrycode, arg->countrycode);
|
||||
}
|
||||
|
||||
void BuddyThreadClass::requestCallback( GPConnection *con, GPRecvBuddyRequestArg *arg )
|
||||
{
|
||||
BuddyResponse response;
|
||||
response.buddyResponseType = BuddyResponse::BUDDYRESPONSE_REQUEST;
|
||||
response.profile = arg->profile;
|
||||
|
||||
// get info about the person asking to be our buddy
|
||||
gpGetInfo( con, arg->profile, GP_CHECK_CACHE, GP_BLOCKING, (GPCallback)getInfoResponseForRequest, &response);
|
||||
|
||||
std::wstring s = MultiByteToWideCharSingleLine( arg->reason );
|
||||
wcsncpy(response.arg.request.text, s.c_str(), GP_REASON_LEN);
|
||||
response.arg.request.text[GP_REASON_LEN-1] = 0;
|
||||
|
||||
TheGameSpyBuddyMessageQueue->addResponse( response );
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
|
||||
static void getInfoResponseForStatus(GPConnection * connection, GPGetInfoResponseArg * arg, void * param)
|
||||
{
|
||||
BuddyResponse *resp = (BuddyResponse *)param;
|
||||
resp->profile = arg->profile;
|
||||
strcpy(resp->arg.status.nick, arg->nick);
|
||||
strcpy(resp->arg.status.email, arg->email);
|
||||
strcpy(resp->arg.status.countrycode, arg->countrycode);
|
||||
}
|
||||
|
||||
void BuddyThreadClass::statusCallback( GPConnection *con, GPRecvBuddyStatusArg *arg )
|
||||
{
|
||||
BuddyResponse response;
|
||||
|
||||
// get user's name
|
||||
response.buddyResponseType = BuddyResponse::BUDDYRESPONSE_STATUS;
|
||||
gpGetInfo( con, arg->profile, GP_CHECK_CACHE, GP_BLOCKING, (GPCallback)getInfoResponseForStatus, &response);
|
||||
|
||||
// get user's status
|
||||
GPBuddyStatus status;
|
||||
gpGetBuddyStatus( con, arg->index, &status );
|
||||
strcpy(response.arg.status.location, status.locationString);
|
||||
strcpy(response.arg.status.statusString, status.statusString);
|
||||
response.arg.status.status = status.status;
|
||||
DEBUG_LOG(("Got buddy status for %d(%s) - status %d\n", status.profile, response.arg.status.nick, status.status));
|
||||
|
||||
// relay to UI
|
||||
TheGameSpyBuddyMessageQueue->addResponse( response );
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: PingThread.cpp //////////////////////////////////////////////////////
|
||||
// Ping thread
|
||||
// Author: Matthew D. Campbell, August 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include <winsock.h> // This one has to be here. Prevents collisions with winsock2.h
|
||||
|
||||
#include "GameNetwork/GameSpy/GameResultsThread.h"
|
||||
#include "mutex.h"
|
||||
#include "thread.h"
|
||||
|
||||
#include "Common/StackDump.h"
|
||||
#include "Common/SubsystemInterface.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
static const Int NumWorkerThreads = 1;
|
||||
|
||||
typedef std::queue<GameResultsRequest> RequestQueue;
|
||||
typedef std::queue<GameResultsResponse> ResponseQueue;
|
||||
class GameResultsThreadClass;
|
||||
|
||||
class GameResultsQueue : public GameResultsInterface
|
||||
{
|
||||
public:
|
||||
virtual ~GameResultsQueue();
|
||||
GameResultsQueue();
|
||||
|
||||
virtual void init() {}
|
||||
virtual void reset() {}
|
||||
virtual void update() {}
|
||||
|
||||
virtual void startThreads( void );
|
||||
virtual void endThreads( void );
|
||||
virtual Bool areThreadsRunning( void );
|
||||
|
||||
virtual void addRequest( const GameResultsRequest& req );
|
||||
virtual Bool getRequest( GameResultsRequest& resp );
|
||||
|
||||
virtual void addResponse( const GameResultsResponse& resp );
|
||||
virtual Bool getResponse( GameResultsResponse& resp );
|
||||
|
||||
virtual Bool areGameResultsBeingSent( void );
|
||||
|
||||
private:
|
||||
MutexClass m_requestMutex;
|
||||
MutexClass m_responseMutex;
|
||||
RequestQueue m_requests;
|
||||
ResponseQueue m_responses;
|
||||
Int m_requestCount;
|
||||
Int m_responseCount;
|
||||
|
||||
GameResultsThreadClass *m_workerThreads[NumWorkerThreads];
|
||||
};
|
||||
|
||||
GameResultsInterface* GameResultsInterface::createNewGameResultsInterface( void )
|
||||
{
|
||||
return NEW GameResultsQueue;
|
||||
}
|
||||
|
||||
GameResultsInterface *TheGameResultsQueue;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
class GameResultsThreadClass : public ThreadClass
|
||||
{
|
||||
|
||||
public:
|
||||
GameResultsThreadClass() : ThreadClass() {}
|
||||
|
||||
void Thread_Function();
|
||||
|
||||
private:
|
||||
Int sendGameResults( UnsignedInt IP, UnsignedShort port, const std::string& results );
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
GameResultsQueue::GameResultsQueue() : m_requestCount(0), m_responseCount(0)
|
||||
{
|
||||
for (Int i=0; i<NumWorkerThreads; ++i)
|
||||
{
|
||||
m_workerThreads[i] = NULL;
|
||||
}
|
||||
|
||||
startThreads();
|
||||
}
|
||||
|
||||
GameResultsQueue::~GameResultsQueue()
|
||||
{
|
||||
endThreads();
|
||||
}
|
||||
|
||||
void GameResultsQueue::startThreads( void )
|
||||
{
|
||||
endThreads();
|
||||
for (Int i=0; i<NumWorkerThreads; ++i)
|
||||
{
|
||||
m_workerThreads[i] = NEW GameResultsThreadClass;
|
||||
m_workerThreads[i]->Execute();
|
||||
}
|
||||
}
|
||||
|
||||
void GameResultsQueue::endThreads( void )
|
||||
{
|
||||
for (Int i=0; i<NumWorkerThreads; ++i)
|
||||
{
|
||||
if (m_workerThreads[i])
|
||||
{
|
||||
delete m_workerThreads[i];
|
||||
m_workerThreads[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bool GameResultsQueue::areThreadsRunning( void )
|
||||
{
|
||||
for (Int i=0; i<NumWorkerThreads; ++i)
|
||||
{
|
||||
if (m_workerThreads[i])
|
||||
{
|
||||
if (m_workerThreads[i]->Is_Running())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameResultsQueue::addRequest( const GameResultsRequest& req )
|
||||
{
|
||||
MutexClass::LockClass m(m_requestMutex);
|
||||
|
||||
++m_requestCount;
|
||||
m_requests.push(req);
|
||||
}
|
||||
|
||||
Bool GameResultsQueue::getRequest( GameResultsRequest& 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 GameResultsQueue::addResponse( const GameResultsResponse& resp )
|
||||
{
|
||||
{
|
||||
MutexClass::LockClass m(m_responseMutex);
|
||||
|
||||
++m_responseCount;
|
||||
m_responses.push(resp);
|
||||
}
|
||||
}
|
||||
|
||||
Bool GameResultsQueue::getResponse( GameResultsResponse& 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;
|
||||
}
|
||||
|
||||
Bool GameResultsQueue::areGameResultsBeingSent( void )
|
||||
{
|
||||
MutexClass::LockClass m(m_requestMutex, 0);
|
||||
if (m.Failed())
|
||||
return true;
|
||||
|
||||
return m_requestCount > 0;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Wrap ladder results in HTTP POST
|
||||
static WrapHTTP( const std::string& hostname, std::string& results )
|
||||
{
|
||||
const char HEADER[] =
|
||||
"PUT / HTTP/1.1\r\n"
|
||||
"Connection: Close\r\n"
|
||||
"Host: %s\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"\r\n";
|
||||
|
||||
char szHdr[256] = {0};
|
||||
_snprintf( szHdr, 255, HEADER, hostname.c_str(), results.length() );
|
||||
results = szHdr + results;
|
||||
} //WrapHTTP
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void GameResultsThreadClass::Thread_Function()
|
||||
{
|
||||
try {
|
||||
_set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace.
|
||||
GameResultsRequest req;
|
||||
|
||||
WSADATA wsaData;
|
||||
|
||||
// Fire up winsock (prob already done, but doesn't matter)
|
||||
WORD wVersionRequested = MAKEWORD(1, 1);
|
||||
WSAStartup( wVersionRequested, &wsaData );
|
||||
|
||||
while ( running )
|
||||
{
|
||||
// deal with requests
|
||||
if (TheGameResultsQueue && TheGameResultsQueue->getRequest(req))
|
||||
{
|
||||
// resolve the hostname
|
||||
const char *hostnameBuffer = req.hostname.c_str();
|
||||
UnsignedInt IP = 0xFFFFFFFF;
|
||||
if (isdigit(hostnameBuffer[0]))
|
||||
{
|
||||
IP = inet_addr(hostnameBuffer);
|
||||
in_addr hostNode;
|
||||
hostNode.s_addr = IP;
|
||||
DEBUG_LOG(("sending game results to %s - IP = %s\n", hostnameBuffer, inet_ntoa(hostNode) ));
|
||||
}
|
||||
else
|
||||
{
|
||||
HOSTENT *hostStruct;
|
||||
in_addr *hostNode;
|
||||
hostStruct = gethostbyname(hostnameBuffer);
|
||||
if (hostStruct == NULL)
|
||||
{
|
||||
DEBUG_LOG(("sending game results to %s - host lookup failed\n", hostnameBuffer));
|
||||
|
||||
// Even though this failed to resolve IP, still need to send a
|
||||
// callback.
|
||||
IP = 0xFFFFFFFF; // flag for IP resolve failed
|
||||
}
|
||||
hostNode = (in_addr *) hostStruct->h_addr;
|
||||
IP = hostNode->s_addr;
|
||||
DEBUG_LOG(("sending game results to %s IP = %s\n", hostnameBuffer, inet_ntoa(*hostNode) ));
|
||||
}
|
||||
|
||||
int result = sendGameResults( IP, req.port, req.results );
|
||||
GameResultsResponse resp;
|
||||
resp.hostname = req.hostname;
|
||||
resp.port = req.port;
|
||||
resp.sentOk = (result == req.results.length());
|
||||
|
||||
}
|
||||
|
||||
// end our timeslice
|
||||
Switch_Thread();
|
||||
}
|
||||
|
||||
WSACleanup();
|
||||
} catch ( ... ) {
|
||||
DEBUG_CRASH(("Exception in results thread!"));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
#define CASE(x) case (x): return #x;
|
||||
|
||||
static const char *getWSAErrorString( Int error )
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
CASE(WSABASEERR)
|
||||
CASE(WSAEINTR)
|
||||
CASE(WSAEBADF)
|
||||
CASE(WSAEACCES)
|
||||
CASE(WSAEFAULT)
|
||||
CASE(WSAEINVAL)
|
||||
CASE(WSAEMFILE)
|
||||
CASE(WSAEWOULDBLOCK)
|
||||
CASE(WSAEINPROGRESS)
|
||||
CASE(WSAEALREADY)
|
||||
CASE(WSAENOTSOCK)
|
||||
CASE(WSAEDESTADDRREQ)
|
||||
CASE(WSAEMSGSIZE)
|
||||
CASE(WSAEPROTOTYPE)
|
||||
CASE(WSAENOPROTOOPT)
|
||||
CASE(WSAEPROTONOSUPPORT)
|
||||
CASE(WSAESOCKTNOSUPPORT)
|
||||
CASE(WSAEOPNOTSUPP)
|
||||
CASE(WSAEPFNOSUPPORT)
|
||||
CASE(WSAEAFNOSUPPORT)
|
||||
CASE(WSAEADDRINUSE)
|
||||
CASE(WSAEADDRNOTAVAIL)
|
||||
CASE(WSAENETDOWN)
|
||||
CASE(WSAENETUNREACH)
|
||||
CASE(WSAENETRESET)
|
||||
CASE(WSAECONNABORTED)
|
||||
CASE(WSAECONNRESET)
|
||||
CASE(WSAENOBUFS)
|
||||
CASE(WSAEISCONN)
|
||||
CASE(WSAENOTCONN)
|
||||
CASE(WSAESHUTDOWN)
|
||||
CASE(WSAETOOMANYREFS)
|
||||
CASE(WSAETIMEDOUT)
|
||||
CASE(WSAECONNREFUSED)
|
||||
CASE(WSAELOOP)
|
||||
CASE(WSAENAMETOOLONG)
|
||||
CASE(WSAEHOSTDOWN)
|
||||
CASE(WSAEHOSTUNREACH)
|
||||
CASE(WSAENOTEMPTY)
|
||||
CASE(WSAEPROCLIM)
|
||||
CASE(WSAEUSERS)
|
||||
CASE(WSAEDQUOT)
|
||||
CASE(WSAESTALE)
|
||||
CASE(WSAEREMOTE)
|
||||
CASE(WSAEDISCON)
|
||||
CASE(WSASYSNOTREADY)
|
||||
CASE(WSAVERNOTSUPPORTED)
|
||||
CASE(WSANOTINITIALISED)
|
||||
CASE(WSAHOST_NOT_FOUND)
|
||||
CASE(WSATRY_AGAIN)
|
||||
CASE(WSANO_RECOVERY)
|
||||
CASE(WSANO_DATA)
|
||||
default:
|
||||
return "Not a Winsock error";
|
||||
}
|
||||
}
|
||||
|
||||
#undef CASE
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
Int GameResultsThreadClass::sendGameResults( UnsignedInt IP, UnsignedShort port, const std::string& results )
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
// create the socket
|
||||
Int sock = socket( AF_INET, SOCK_STREAM, 0 );
|
||||
if (sock < 0)
|
||||
{
|
||||
DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - socket() returned %d(%s)\n", sock, getWSAErrorString(sock)));
|
||||
return sock;
|
||||
}
|
||||
|
||||
// fill in address info
|
||||
struct sockaddr_in sockAddr;
|
||||
memset( &sockAddr, 0, sizeof( sockAddr ) );
|
||||
sockAddr.sin_family = AF_INET;
|
||||
sockAddr.sin_addr.s_addr = IP;
|
||||
sockAddr.sin_port = htons(port);
|
||||
|
||||
// Start the connection process....
|
||||
if( connect( sock, (struct sockaddr *)&sockAddr, sizeof( sockAddr ) ) == -1 )
|
||||
{
|
||||
error = WSAGetLastError();
|
||||
DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - connect() returned %d(%s)\n", error, getWSAErrorString(error)));
|
||||
if( ( error == WSAEWOULDBLOCK ) || ( error == WSAEINVAL ) || ( error == WSAEALREADY ) )
|
||||
{
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( error != WSAEISCONN )
|
||||
{
|
||||
closesocket( sock );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
if (send( sock, results.c_str(), results.length(), 0 ) == SOCKET_ERROR)
|
||||
{
|
||||
error = WSAGetLastError();
|
||||
DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - send() returned %d(%s)\n", error, getWSAErrorString(error)));
|
||||
closesocket(sock);
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
closesocket(sock);
|
||||
|
||||
return results.length();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: PingThread.cpp //////////////////////////////////////////////////////
|
||||
// Ping thread
|
||||
// Author: Matthew D. Campbell, August 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include <winsock.h> // This one has to be here. Prevents collisions with windsock2.h
|
||||
|
||||
#include "GameNetwork/GameSpy/PingThread.h"
|
||||
#include "mutex.h"
|
||||
#include "thread.h"
|
||||
|
||||
#include "Common/StackDump.h"
|
||||
#include "Common/SubsystemInterface.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
static const Int NumWorkerThreads = 10;
|
||||
|
||||
typedef std::queue<PingRequest> RequestQueue;
|
||||
typedef std::queue<PingResponse> ResponseQueue;
|
||||
class PingThreadClass;
|
||||
|
||||
class Pinger : public PingerInterface
|
||||
{
|
||||
public:
|
||||
virtual ~Pinger();
|
||||
Pinger();
|
||||
virtual void startThreads( void );
|
||||
virtual void endThreads( void );
|
||||
virtual Bool areThreadsRunning( void );
|
||||
|
||||
virtual void addRequest( const PingRequest& req );
|
||||
virtual Bool getRequest( PingRequest& resp );
|
||||
|
||||
virtual void addResponse( const PingResponse& resp );
|
||||
virtual Bool getResponse( PingResponse& resp );
|
||||
|
||||
virtual Bool arePingsInProgress( void );
|
||||
virtual Int getPing( AsciiString hostname );
|
||||
|
||||
virtual void clearPingMap( void );
|
||||
virtual AsciiString getPingString( Int timeout );
|
||||
|
||||
private:
|
||||
MutexClass m_requestMutex;
|
||||
MutexClass m_responseMutex;
|
||||
MutexClass m_pingMapMutex;
|
||||
RequestQueue m_requests;
|
||||
ResponseQueue m_responses;
|
||||
Int m_requestCount;
|
||||
Int m_responseCount;
|
||||
|
||||
std::map<std::string, Int> m_pingMap;
|
||||
|
||||
PingThreadClass *m_workerThreads[NumWorkerThreads];
|
||||
};
|
||||
|
||||
PingerInterface* PingerInterface::createNewPingerInterface( void )
|
||||
{
|
||||
return NEW Pinger;
|
||||
}
|
||||
|
||||
PingerInterface *ThePinger;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
class PingThreadClass : public ThreadClass
|
||||
{
|
||||
|
||||
public:
|
||||
PingThreadClass() : ThreadClass() {}
|
||||
|
||||
void Thread_Function();
|
||||
|
||||
private:
|
||||
Int doPing( UnsignedInt IP, Int timeout );
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
Pinger::Pinger() : m_requestCount(0), m_responseCount(0)
|
||||
{
|
||||
for (Int i=0; i<NumWorkerThreads; ++i)
|
||||
{
|
||||
m_workerThreads[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Pinger::~Pinger()
|
||||
{
|
||||
endThreads();
|
||||
}
|
||||
|
||||
void Pinger::startThreads( void )
|
||||
{
|
||||
endThreads();
|
||||
for (Int i=0; i<NumWorkerThreads; ++i)
|
||||
{
|
||||
m_workerThreads[i] = NEW PingThreadClass;
|
||||
m_workerThreads[i]->Execute();
|
||||
}
|
||||
}
|
||||
|
||||
void Pinger::endThreads( void )
|
||||
{
|
||||
for (Int i=0; i<NumWorkerThreads; ++i)
|
||||
{
|
||||
if (m_workerThreads[i])
|
||||
{
|
||||
delete m_workerThreads[i];
|
||||
m_workerThreads[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bool Pinger::areThreadsRunning( void )
|
||||
{
|
||||
for (Int i=0; i<NumWorkerThreads; ++i)
|
||||
{
|
||||
if (m_workerThreads[i])
|
||||
{
|
||||
if (m_workerThreads[i]->Is_Running())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Pinger::addRequest( const PingRequest& req )
|
||||
{
|
||||
MutexClass::LockClass m(m_requestMutex);
|
||||
|
||||
++m_requestCount;
|
||||
m_requests.push(req);
|
||||
}
|
||||
|
||||
Bool Pinger::getRequest( PingRequest& 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 Pinger::addResponse( const PingResponse& resp )
|
||||
{
|
||||
{
|
||||
MutexClass::LockClass m(m_pingMapMutex);
|
||||
|
||||
m_pingMap[resp.hostname] = resp.avgPing;
|
||||
}
|
||||
{
|
||||
MutexClass::LockClass m(m_responseMutex);
|
||||
|
||||
++m_responseCount;
|
||||
m_responses.push(resp);
|
||||
}
|
||||
}
|
||||
|
||||
Bool Pinger::getResponse( PingResponse& 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;
|
||||
}
|
||||
|
||||
Bool Pinger::arePingsInProgress( void )
|
||||
{
|
||||
return (m_requestCount != m_responseCount);
|
||||
}
|
||||
|
||||
Int Pinger::getPing( AsciiString hostname )
|
||||
{
|
||||
MutexClass::LockClass m(m_pingMapMutex, 0);
|
||||
if (m.Failed())
|
||||
return false;
|
||||
|
||||
std::map<std::string, Int>::const_iterator it = m_pingMap.find(hostname.str());
|
||||
if (it != m_pingMap.end())
|
||||
return it->second;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Pinger::clearPingMap( void )
|
||||
{
|
||||
MutexClass::LockClass m(m_pingMapMutex);
|
||||
m_pingMap.clear();
|
||||
}
|
||||
|
||||
AsciiString Pinger::getPingString( Int timeout )
|
||||
{
|
||||
MutexClass::LockClass m(m_pingMapMutex);
|
||||
|
||||
AsciiString pingString;
|
||||
AsciiString tmp;
|
||||
for (std::map<std::string, Int>::const_iterator it = m_pingMap.begin(); it != m_pingMap.end(); ++it)
|
||||
{
|
||||
Int ping = it->second;
|
||||
if (ping < 0 || ping > timeout)
|
||||
ping = timeout;
|
||||
ping = ping * 255 / timeout;
|
||||
tmp.format("%2.2X", ping);
|
||||
pingString.concat(tmp);
|
||||
}
|
||||
return pingString;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void PingThreadClass::Thread_Function()
|
||||
{
|
||||
try {
|
||||
_set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace.
|
||||
PingRequest req;
|
||||
|
||||
WSADATA wsaData;
|
||||
|
||||
// Fire up winsock (prob already done, but doesn't matter)
|
||||
WORD wVersionRequested = MAKEWORD(1, 1);
|
||||
WSAStartup( wVersionRequested, &wsaData );
|
||||
|
||||
while ( running )
|
||||
{
|
||||
// deal with requests
|
||||
if (ThePinger->getRequest(req))
|
||||
{
|
||||
// resolve the hostname
|
||||
const char *hostnameBuffer = req.hostname.c_str();
|
||||
UnsignedInt IP = 0xFFFFFFFF;
|
||||
if (isdigit(hostnameBuffer[0]))
|
||||
{
|
||||
IP = inet_addr(hostnameBuffer);
|
||||
in_addr hostNode;
|
||||
hostNode.s_addr = IP;
|
||||
DEBUG_LOG(("pinging %s - IP = %s\n", hostnameBuffer, inet_ntoa(hostNode) ));
|
||||
}
|
||||
else
|
||||
{
|
||||
HOSTENT *hostStruct;
|
||||
in_addr *hostNode;
|
||||
hostStruct = gethostbyname(hostnameBuffer);
|
||||
if (hostStruct == NULL)
|
||||
{
|
||||
DEBUG_LOG(("pinging %s - host lookup failed\n", hostnameBuffer));
|
||||
|
||||
// Even though this failed to resolve IP, still need to send a
|
||||
// callback.
|
||||
IP = 0xFFFFFFFF; // flag for IP resolve failed
|
||||
}
|
||||
hostNode = (in_addr *) hostStruct->h_addr;
|
||||
IP = hostNode->s_addr;
|
||||
DEBUG_LOG(("pinging %s IP = %s\n", hostnameBuffer, inet_ntoa(*hostNode) ));
|
||||
}
|
||||
|
||||
// do ping
|
||||
Int totalPing = 0;
|
||||
Int goodReps = 0;
|
||||
Int reps = req.repetitions;
|
||||
while (reps-- && running && IP != 0xFFFFFFFF)
|
||||
{
|
||||
Int ping = doPing(IP, req.timeout);
|
||||
if (ping >= 0)
|
||||
{
|
||||
totalPing += ping;
|
||||
++goodReps;
|
||||
}
|
||||
|
||||
// end our timeslice
|
||||
Switch_Thread();
|
||||
}
|
||||
if (!goodReps)
|
||||
totalPing = -1;
|
||||
else
|
||||
totalPing = totalPing / goodReps;
|
||||
|
||||
PingResponse resp;
|
||||
resp.hostname = req.hostname;
|
||||
resp.avgPing = totalPing;
|
||||
resp.repetitions = goodReps;
|
||||
ThePinger->addResponse(resp);
|
||||
}
|
||||
|
||||
// end our timeslice
|
||||
Switch_Thread();
|
||||
}
|
||||
|
||||
WSACleanup();
|
||||
} catch ( ... ) {
|
||||
DEBUG_CRASH(("Exception in ping thread!"));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
HANDLE WINAPI IcmpCreateFile(VOID); /* INVALID_HANDLE_VALUE on error */
|
||||
BOOL WINAPI IcmpCloseHandle(HANDLE IcmpHandle); /* FALSE on error */
|
||||
|
||||
/* Note 2: For the most part, you can refer to RFC 791 for detials
|
||||
* on how to fill in values for the IP option information structure.
|
||||
*/
|
||||
typedef struct ip_option_information
|
||||
{
|
||||
UnsignedByte Ttl; /* Time To Live (used for traceroute) */
|
||||
UnsignedByte Tos; /* Type Of Service (usually 0) */
|
||||
UnsignedByte Flags; /* IP header flags (usually 0) */
|
||||
UnsignedByte OptionsSize; /* Size of options data (usually 0, max 40) */
|
||||
UnsignedByte FAR *OptionsData; /* Options data buffer */
|
||||
}
|
||||
IPINFO, *PIPINFO, FAR *LPIPINFO;
|
||||
|
||||
|
||||
/* Note 1: The Reply Buffer will have an array of ICMP_ECHO_REPLY
|
||||
* structures, followed by options and the data in ICMP echo reply
|
||||
* datagram received. You must have room for at least one ICMP
|
||||
* echo reply structure, plus 8 bytes for an ICMP header.
|
||||
*/
|
||||
typedef struct icmp_echo_reply
|
||||
{
|
||||
UnsignedInt Address; /* source address */
|
||||
////////UnsignedInt Status; /* IP status value (see below) */
|
||||
UnsignedInt RTTime; /* Round Trip Time in milliseconds */
|
||||
UnsignedShort DataSize; /* reply data size */
|
||||
UnsignedShort Reserved; /* */
|
||||
void FAR *Data; /* reply data buffer */
|
||||
struct ip_option_information Options; /* reply options */
|
||||
}
|
||||
ICMPECHO, *PICMPECHO, FAR *LPICMPECHO;
|
||||
|
||||
|
||||
DWORD WINAPI IcmpSendEcho(
|
||||
HANDLE IcmpHandle, /* handle returned from IcmpCreateFile() */
|
||||
UnsignedInt DestAddress, /* destination IP address (in network order) */
|
||||
LPVOID RequestData, /* pointer to buffer to send */
|
||||
WORD RequestSize, /* length of data in buffer */
|
||||
LPIPINFO RequestOptns, /* see Note 2 */
|
||||
LPVOID ReplyBuffer, /* see Note 1 */
|
||||
DWORD ReplySize, /* length of reply (must allow at least 1 reply) */
|
||||
DWORD Timeout /* time in milliseconds to wait for reply */
|
||||
);
|
||||
|
||||
|
||||
#define IP_STATUS_BASE 11000
|
||||
#define IP_SUCCESS 0
|
||||
#define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1)
|
||||
#define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2)
|
||||
#define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3)
|
||||
#define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4)
|
||||
#define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5)
|
||||
#define IP_NO_RESOURCES (IP_STATUS_BASE + 6)
|
||||
#define IP_BAD_OPTION (IP_STATUS_BASE + 7)
|
||||
#define IP_HW_ERROR (IP_STATUS_BASE + 8)
|
||||
#define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9)
|
||||
#define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10)
|
||||
#define IP_BAD_REQ (IP_STATUS_BASE + 11)
|
||||
#define IP_BAD_ROUTE (IP_STATUS_BASE + 12)
|
||||
#define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13)
|
||||
#define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14)
|
||||
#define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15)
|
||||
#define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16)
|
||||
#define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17)
|
||||
#define IP_BAD_DESTINATION (IP_STATUS_BASE + 18)
|
||||
#define IP_ADDR_DELETED (IP_STATUS_BASE + 19)
|
||||
#define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20)
|
||||
#define IP_MTU_CHANGE (IP_STATUS_BASE + 21)
|
||||
#define IP_UNLOAD (IP_STATUS_BASE + 22)
|
||||
#define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50)
|
||||
#define MAX_IP_STATUS IP_GENERAL_FAILURE
|
||||
#define IP_PENDING (IP_STATUS_BASE + 255)
|
||||
|
||||
|
||||
#define BUFSIZE 8192
|
||||
#define DEFAULT_LEN 32
|
||||
#define LOOPLIMIT 4
|
||||
#define DEFAULT_TTL 64
|
||||
|
||||
Int PingThreadClass::doPing(UnsignedInt IP, Int timeout)
|
||||
{
|
||||
/*
|
||||
* Initialize default settings
|
||||
*/
|
||||
|
||||
IPINFO stIPInfo, *lpstIPInfo;
|
||||
HANDLE hICMP, hICMP_DLL;
|
||||
int i, j, nDataLen, nLoopLimit, nTimeOut, nTTL, nTOS;
|
||||
DWORD dwReplyCount;
|
||||
///////IN_ADDR stDestAddr;
|
||||
BOOL fRet, fDontStop;
|
||||
///BOOL fTraceRoute;
|
||||
|
||||
nDataLen = DEFAULT_LEN;
|
||||
nLoopLimit = LOOPLIMIT;
|
||||
nTimeOut = timeout;
|
||||
fDontStop = FALSE;
|
||||
lpstIPInfo = NULL;
|
||||
nTTL = DEFAULT_TTL;
|
||||
nTOS = 0;
|
||||
|
||||
Int pingTime = -1; // in case of error
|
||||
|
||||
char achReqData[BUFSIZE];
|
||||
char achRepData[sizeof(ICMPECHO) + BUFSIZE];
|
||||
|
||||
|
||||
HANDLE ( WINAPI *lpfnIcmpCreateFile )( VOID ) = NULL;
|
||||
BOOL ( WINAPI *lpfnIcmpCloseHandle )( HANDLE ) = NULL;
|
||||
DWORD (WINAPI *lpfnIcmpSendEcho)(HANDLE, DWORD, LPVOID, WORD, LPVOID,
|
||||
LPVOID, DWORD, DWORD) = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Load the ICMP.DLL
|
||||
*/
|
||||
hICMP_DLL = LoadLibrary("ICMP.DLL");
|
||||
if (hICMP_DLL == 0)
|
||||
{
|
||||
DEBUG_LOG(("LoadLibrary() failed: Unable to locate ICMP.DLL!\n"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get pointers to ICMP.DLL functions
|
||||
*/
|
||||
lpfnIcmpCreateFile = (void * (__stdcall *)(void))GetProcAddress( (HINSTANCE)hICMP_DLL, "IcmpCreateFile");
|
||||
lpfnIcmpCloseHandle = (int (__stdcall *)(void *))GetProcAddress( (HINSTANCE)hICMP_DLL, "IcmpCloseHandle");
|
||||
lpfnIcmpSendEcho = (unsigned long (__stdcall *)(void *, unsigned long, void *, unsigned short,
|
||||
void *, void *, unsigned long, unsigned long))GetProcAddress( (HINSTANCE)hICMP_DLL, "IcmpSendEcho" );
|
||||
|
||||
if ((!lpfnIcmpCreateFile) ||
|
||||
(!lpfnIcmpCloseHandle) ||
|
||||
(!lpfnIcmpSendEcho))
|
||||
{
|
||||
DEBUG_LOG(("GetProcAddr() failed for at least one function.\n"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IcmpCreateFile() - Open the ping service
|
||||
*/
|
||||
hICMP = (HANDLE) lpfnIcmpCreateFile();
|
||||
if (hICMP == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DEBUG_LOG(("IcmpCreateFile() failed"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init data buffer printable ASCII
|
||||
* 32 (space) through 126 (tilde)
|
||||
*/
|
||||
for (j = 0, i = 32; j < nDataLen; j++, i++)
|
||||
{
|
||||
if (i >= 126)
|
||||
i = 32;
|
||||
achReqData[j] = i;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init IPInfo structure
|
||||
*/
|
||||
lpstIPInfo = &stIPInfo;
|
||||
stIPInfo.Ttl = nTTL;
|
||||
stIPInfo.Tos = nTOS;
|
||||
stIPInfo.Flags = 0;
|
||||
stIPInfo.OptionsSize = 0;
|
||||
stIPInfo.OptionsData = NULL;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* IcmpSendEcho() - Send the ICMP Echo Request
|
||||
* and read the Reply
|
||||
*/
|
||||
dwReplyCount = lpfnIcmpSendEcho(
|
||||
hICMP,
|
||||
IP,
|
||||
achReqData,
|
||||
nDataLen,
|
||||
lpstIPInfo,
|
||||
achRepData,
|
||||
sizeof(achRepData),
|
||||
nTimeOut);
|
||||
if (dwReplyCount != 0)
|
||||
{
|
||||
//////////IN_ADDR stDestAddr;
|
||||
DWORD dwStatus;
|
||||
|
||||
pingTime = (*(UnsignedInt *) & (achRepData[8]));
|
||||
|
||||
// I've seen the ping time bigger than the timeout by a little
|
||||
// bit. How lame.
|
||||
if (pingTime > timeout)
|
||||
pingTime = timeout;
|
||||
|
||||
dwStatus = *(DWORD *) & (achRepData[4]);
|
||||
if (dwStatus != IP_SUCCESS)
|
||||
{
|
||||
DEBUG_LOG(("ICMPERR: %d\n", dwStatus));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("IcmpSendEcho() failed: %d\n", dwReplyCount));
|
||||
// Ok we didn't get a packet, just say everything's OK
|
||||
// and the time was -1
|
||||
pingTime = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IcmpCloseHandle - Close the ICMP handle
|
||||
*/
|
||||
fRet = lpfnIcmpCloseHandle(hICMP);
|
||||
if (fRet == FALSE)
|
||||
{
|
||||
DEBUG_LOG(("Error closing ICMP handle\n"));
|
||||
}
|
||||
|
||||
// Say what you will about goto's but it's handy for stuff like this
|
||||
cleanup:
|
||||
|
||||
// Shut down...
|
||||
if (hICMP_DLL)
|
||||
FreeLibrary((HINSTANCE)hICMP_DLL);
|
||||
|
||||
return pingTime;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ThreadUtils.cpp //////////////////////////////////////////////////////
|
||||
// GameSpy thread utils
|
||||
// Author: Matthew D. Campbell, July 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
std::wstring MultiByteToWideCharSingleLine( const char *orig )
|
||||
{
|
||||
Int len = strlen(orig);
|
||||
WideChar *dest = NEW WideChar[len+1];
|
||||
|
||||
MultiByteToWideChar(CP_UTF8, 0, orig, -1, dest, len);
|
||||
WideChar *c = NULL;
|
||||
do
|
||||
{
|
||||
c = wcschr(dest, L'\n');
|
||||
if (c)
|
||||
{
|
||||
*c = L' ';
|
||||
}
|
||||
}
|
||||
while ( c != NULL );
|
||||
do
|
||||
{
|
||||
c = wcschr(dest, L'\r');
|
||||
if (c)
|
||||
{
|
||||
*c = L' ';
|
||||
}
|
||||
}
|
||||
while ( c != NULL );
|
||||
|
||||
dest[len] = 0;
|
||||
std::wstring ret = dest;
|
||||
delete dest;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string WideCharStringToMultiByte( const WideChar *orig )
|
||||
{
|
||||
std::string ret;
|
||||
Int len = WideCharToMultiByte( CP_UTF8, 0, orig, wcslen(orig), NULL, 0, NULL, NULL ) + 1;
|
||||
if (len > 0)
|
||||
{
|
||||
char *dest = NEW char[len];
|
||||
WideCharToMultiByte( CP_UTF8, 0, orig, -1, dest, len, NULL, NULL );
|
||||
dest[len-1] = 0;
|
||||
ret = dest;
|
||||
delete dest;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
455
Generals/Code/GameEngine/Source/GameNetwork/GameSpyChat.cpp
Normal file
455
Generals/Code/GameEngine/Source/GameNetwork/GameSpyChat.cpp
Normal file
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: GameSpyChat.cpp //////////////////////////////////////////////////////
|
||||
// GameSpy chat handlers
|
||||
// Author: Matthew D. Campbell, February 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/GadgetListBox.h"
|
||||
#include "GameClient/LanguageFilter.h"
|
||||
#include "GameNetwork/GameSpy.h"
|
||||
#include "GameNetwork/GameSpyChat.h"
|
||||
#include "Common/QuotedPrintable.h"
|
||||
|
||||
typedef set<AsciiString>::const_iterator AsciiSetIter;
|
||||
|
||||
/**
|
||||
* handleSlashCommands looks for slash ccommands and handles them,
|
||||
* returning true if it found one, false otherwise.
|
||||
* /i,/ignore list ignored players
|
||||
* /i,/ignore +name1 -name2 ignore name1, stop ignoring name2
|
||||
* /m,/me: shorthand for an action
|
||||
* /o,/on: find command to look up a user's location
|
||||
* /f,/find: find command to look up a user's location
|
||||
* /p,/page: page user(s)
|
||||
* /r,/reply: reply to last page
|
||||
* /raw: raw IRC command (only in debug & internal)
|
||||
* /oper: become an IRC op (only in debug & internal)
|
||||
* /quit: send the IRC quit command to exit WOL
|
||||
*/
|
||||
static Bool handleSlashCommands( UnicodeString message, Bool isAction, GameWindow *playerListbox )
|
||||
{
|
||||
/*
|
||||
if (message.getCharAt(0) == L'/')
|
||||
{
|
||||
UnicodeString remainder = UnicodeString(message.str() + 1);
|
||||
UnicodeString token;
|
||||
|
||||
switch (message.getCharAt(1))
|
||||
{
|
||||
case L'i':
|
||||
case L'I':
|
||||
remainder.nextToken(&token);
|
||||
if (token.compareNoCase(L"i") == 0 || token.compareNoCase(L"ignore") == 0)
|
||||
{
|
||||
if (remainder.isEmpty())
|
||||
{
|
||||
// List the people we're ignoring
|
||||
TheWOL->addText(TheGameText->fetch("WOL:BeginIgnoreList"));
|
||||
set<AsciiString> *ignoreList = getIgnoreList();
|
||||
if (ignoreList)
|
||||
{
|
||||
UnicodeString msg;
|
||||
UnicodeString uName;
|
||||
AsciiSetIter iter = ignoreList->begin();
|
||||
while (iter != ignoreList->end())
|
||||
{
|
||||
uName.translate(*iter);
|
||||
msg.format(TheGameText->fetch("WOL:IgnoredUser"), uName.str());
|
||||
TheWOL->addText(msg);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
TheWOL->addText(TheGameText->fetch("WOL:EndIgnoreList"));
|
||||
}
|
||||
|
||||
while ( remainder.nextToken(&token) )
|
||||
{
|
||||
AsciiString name;
|
||||
int doIgnore = 0;
|
||||
if (token.getCharAt(0) == L'+')
|
||||
{
|
||||
// Ignore somebody
|
||||
token = UnicodeString(token.str() + 1);
|
||||
name.translate(token);
|
||||
doIgnore = 1;
|
||||
}
|
||||
else if (token.getCharAt(0) == L'-')
|
||||
{
|
||||
// Listen to someone again
|
||||
token = UnicodeString(token.str() + 1);
|
||||
name.translate(token);
|
||||
doIgnore = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore somebody
|
||||
token = UnicodeString(token.str());
|
||||
name.translate(token);
|
||||
doIgnore = 1;
|
||||
}
|
||||
IChat *ichat = TheWOL->getIChat();
|
||||
User user;
|
||||
strncpy((char *)user.name, name.str(), 9);
|
||||
user.name[9] = 0;
|
||||
ichat->SetSquelch(&user, doIgnore);
|
||||
|
||||
if (doIgnore)
|
||||
addIgnore(name);
|
||||
else
|
||||
removeIgnore(name);
|
||||
|
||||
UnicodeString msg;
|
||||
UnicodeString uName;
|
||||
uName.translate(name);
|
||||
msg.format(TheGameText->fetch("WOL:IgnoredUser"), uName.str());
|
||||
TheWOL->addText(msg);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case L'r':
|
||||
case L'R':
|
||||
remainder.nextToken(&token);
|
||||
#if defined _DEBUG || defined _INTERNAL
|
||||
if (token.compareNoCase(L"raw") == 0)
|
||||
{
|
||||
// Send raw IRC commands (Ascii only)
|
||||
AsciiString str;
|
||||
str.translate(remainder);
|
||||
str.concat('\n');
|
||||
IChat *ichat = TheWOL->getIChat();
|
||||
ichat->RequestRawMessage(str.str());
|
||||
TheWOL->addText(remainder);
|
||||
return true; // show it anyway
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
#if defined _DEBUG || defined _INTERNAL
|
||||
case L'k':
|
||||
case L'K':
|
||||
remainder.nextToken(&token);
|
||||
if (token.compareNoCase(L"kick") == 0)
|
||||
{
|
||||
|
||||
while ( remainder.nextToken(&token) )
|
||||
{
|
||||
AsciiString name;
|
||||
name.translate(token);
|
||||
IChat *ichat = TheWOL->getIChat();
|
||||
User user;
|
||||
strncpy((char *)user.name, name.str(), 9);
|
||||
user.name[9] = 0;
|
||||
ichat->RequestUserKick(&user);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case L'o':
|
||||
case L'O':
|
||||
remainder.nextToken(&token);
|
||||
if (token.compareNoCase(L"on") == 0 || token.compareNoCase(L"o") == 0)
|
||||
{
|
||||
remainder.nextToken(&token);
|
||||
AsciiString userName;
|
||||
userName.translate(token);
|
||||
User user;
|
||||
strncpy((char *)user.name, userName.str(), 10);
|
||||
user.name[9] = 0;
|
||||
if (user.name[0] == 0)
|
||||
{
|
||||
// didn't enter a name
|
||||
TheWOL->addText(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send find command
|
||||
IChat *ichat = TheWOL->getIChat();
|
||||
ichat->RequestGlobalFind(&user);
|
||||
}
|
||||
return true; // show it anyway
|
||||
}
|
||||
#if defined _DEBUG || defined _INTERNAL
|
||||
else if (token.compareNoCase(L"oper") == 0)
|
||||
{
|
||||
// Send raw IRC oper command
|
||||
AsciiString str;
|
||||
str.translate(message);
|
||||
str.concat('\n');
|
||||
IChat *ichat = TheWOL->getIChat();
|
||||
ichat->RequestRawMessage(str.str());
|
||||
TheWOL->addText(message);
|
||||
return true; // show it anyway
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case L'p':
|
||||
case L'P':
|
||||
remainder.nextToken(&token);
|
||||
if (token.compareNoCase(L"page") == 0 || token.compareNoCase(L"p") == 0)
|
||||
{
|
||||
remainder.nextToken(&token);
|
||||
AsciiString userName;
|
||||
userName.translate(token);
|
||||
User user;
|
||||
strncpy((char *)user.name, userName.str(), 10);
|
||||
user.name[9] = 0;
|
||||
remainder.trim();
|
||||
if (user.name[0] == 0 || remainder.isEmpty())
|
||||
{
|
||||
// didn't enter a name or message
|
||||
TheWOL->addText(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send page command
|
||||
IChat *ichat = TheWOL->getIChat();
|
||||
ichat->RequestGlobalUnicodePage(&user, remainder.str());
|
||||
}
|
||||
return true; // show it anyway
|
||||
}
|
||||
break;
|
||||
case L'q':
|
||||
case L'Q':
|
||||
remainder.nextToken(&token);
|
||||
if (token.compareNoCase(L"quit") == 0)
|
||||
{
|
||||
TheWOL->setState(WOLAPI_LOGIN);
|
||||
TheWOL->addCommand(WOLCOMMAND_LOGOUT);
|
||||
//TheWOL->setScreen(WOLAPI_MENU_WELCOME);
|
||||
return true; // show it anyway
|
||||
}
|
||||
break;
|
||||
#if defined _DEBUG || defined _INTERNAL
|
||||
case L'c':
|
||||
case L'C':
|
||||
remainder.nextToken(&token);
|
||||
if (token.compareNoCase(L"colortest") == 0)
|
||||
{
|
||||
addColorText(token, 0xDD, 0xE2, 0x0D, 0xff);
|
||||
addColorText(token, 0xFF, 0x19, 0x19, 0xff);
|
||||
addColorText(token, 0x2A, 0x74, 0xE2, 0xff);
|
||||
addColorText(token, 0x3E, 0xD1, 0x2E, 0xff);
|
||||
addColorText(token, 0xFF, 0xA0, 0x19, 0xff);
|
||||
addColorText(token, 0x32, 0xD7, 0xE6, 0xff);
|
||||
addColorText(token, 0x95, 0x28, 0xBD, 0xff);
|
||||
addColorText(token, 0xFF, 0x9A, 0xEB, 0xff);
|
||||
return true; // show it anyway
|
||||
}
|
||||
break;
|
||||
#endif // _DEBUG || defined _INTERNAL
|
||||
}
|
||||
}
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
static handleUnicodeMessage( const char *nick, UnicodeString msg, Bool isPublic, Bool isAction );
|
||||
|
||||
Bool GameSpySendChat( UnicodeString message, Bool isAction, GameWindow *playerListbox )
|
||||
{
|
||||
RoomType roomType = StagingRoom;
|
||||
if (TheGameSpyChat->getCurrentGroupRoomID())
|
||||
roomType = GroupRoom;
|
||||
|
||||
message.trim();
|
||||
// Echo the user's input to the chat window
|
||||
if (!message.isEmpty())
|
||||
{
|
||||
// Check for slash commands
|
||||
if (handleSlashCommands(message, isAction, playerListbox))
|
||||
{
|
||||
return false; // already handled
|
||||
}
|
||||
|
||||
if (!playerListbox)
|
||||
{
|
||||
// Public message
|
||||
if (isAction)
|
||||
{
|
||||
peerMessageRoom(TheGameSpyChat->getPeer(), roomType, UnicodeStringToQuotedPrintable(message).str(), ActionMessage);
|
||||
//if (roomType == StagingRoom)
|
||||
//handleUnicodeMessage(TheGameSpyChat->getloginName().str(), message, true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
peerMessageRoom(TheGameSpyChat->getPeer(), roomType, UnicodeStringToQuotedPrintable(message).str(), NormalMessage);
|
||||
//if (roomType == StagingRoom)
|
||||
//handleUnicodeMessage(TheGameSpyChat->getloginName().str(), message, true, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the selections (is this a private message?)
|
||||
Int maxSel = GadgetListBoxGetListLength(playerListbox);
|
||||
Int *selections;
|
||||
GadgetListBoxGetSelected(playerListbox, (Int *)&selections);
|
||||
|
||||
if (selections[0] == -1)
|
||||
{
|
||||
// Public message
|
||||
if (isAction)
|
||||
{
|
||||
peerMessageRoom(TheGameSpyChat->getPeer(), roomType, UnicodeStringToQuotedPrintable(message).str(), ActionMessage);
|
||||
//if (roomType == StagingRoom)
|
||||
//handleUnicodeMessage(TheGameSpyChat->getloginName().str(), message, true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
peerMessageRoom(TheGameSpyChat->getPeer(), roomType, UnicodeStringToQuotedPrintable(message).str(), NormalMessage);
|
||||
//if (roomType == StagingRoom)
|
||||
//handleUnicodeMessage(TheGameSpyChat->getloginName().str(), message, true, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Private message
|
||||
|
||||
// Construct a list
|
||||
AsciiString names = AsciiString::TheEmptyString;
|
||||
AsciiString tmp = AsciiString::TheEmptyString;
|
||||
AsciiString aStr; // AsciiString buf for translating Unicode entries
|
||||
names.format("%s", TheGameSpyChat->getLoginName().str());
|
||||
for (int i=0; i<maxSel; i++)
|
||||
{
|
||||
if (selections[i] != -1)
|
||||
{
|
||||
aStr.translate(GadgetListBoxGetText(playerListbox, selections[i], 0));
|
||||
if (aStr.compareNoCase(TheGameSpyChat->getLoginName()))
|
||||
{
|
||||
tmp.format(",%s", aStr.str());
|
||||
names.concat(tmp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!names.isEmpty())
|
||||
{
|
||||
if (isAction)
|
||||
{
|
||||
peerMessagePlayer(TheGameSpyChat->getPeer(), names.str(), UnicodeStringToQuotedPrintable(message).str(), ActionMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
peerMessagePlayer(TheGameSpyChat->getPeer(), names.str(), UnicodeStringToQuotedPrintable(message).str(), NormalMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RoomMessageCallback(PEER peer, RoomType roomType,
|
||||
const char * nick, const char * message,
|
||||
MessageType messageType, void * param)
|
||||
{
|
||||
DEBUG_LOG(("RoomMessageCallback\n"));
|
||||
handleUnicodeMessage(nick, QuotedPrintableToUnicodeString(message), true, (messageType == ActionMessage));
|
||||
}
|
||||
|
||||
void PlayerMessageCallback(PEER peer,
|
||||
const char * nick, const char * message,
|
||||
MessageType messageType, void * param)
|
||||
{
|
||||
DEBUG_LOG(("PlayerMessageCallback\n"));
|
||||
handleUnicodeMessage(nick, QuotedPrintableToUnicodeString(message), false, (messageType == ActionMessage));
|
||||
}
|
||||
|
||||
static handleUnicodeMessage( const char *nick, UnicodeString msg, Bool isPublic, Bool isAction )
|
||||
{
|
||||
GameSpyColors style;
|
||||
|
||||
Bool isOwner = false;
|
||||
Int flags = 0;
|
||||
if (TheGameSpyChat->getCurrentGroupRoomID())
|
||||
peerGetPlayerFlags(TheGameSpyChat->getPeer(), nick, GroupRoom, &flags);
|
||||
else
|
||||
peerGetPlayerFlags(TheGameSpyChat->getPeer(), nick, StagingRoom, &flags);
|
||||
isOwner = flags & PEER_FLAG_OP;
|
||||
|
||||
if (isPublic && isAction)
|
||||
{
|
||||
style = (isOwner)?GSCOLOR_CHAT_OWNER_EMOTE:GSCOLOR_CHAT_EMOTE;
|
||||
}
|
||||
else if (isPublic)
|
||||
{
|
||||
style = (isOwner)?GSCOLOR_CHAT_OWNER:GSCOLOR_CHAT_NORMAL;
|
||||
}
|
||||
else if (isAction)
|
||||
{
|
||||
style = (isOwner)?GSCOLOR_CHAT_PRIVATE_OWNER_EMOTE:GSCOLOR_CHAT_PRIVATE_EMOTE;
|
||||
}
|
||||
else
|
||||
{
|
||||
style = (isOwner)?GSCOLOR_CHAT_PRIVATE_OWNER:GSCOLOR_CHAT_PRIVATE;
|
||||
}
|
||||
|
||||
UnicodeString name;
|
||||
name.translate(nick);
|
||||
|
||||
// filters language
|
||||
// if( TheGlobalData->m_languageFilterPref )
|
||||
// {
|
||||
TheLanguageFilter->filterLine(msg);
|
||||
// }
|
||||
|
||||
UnicodeString fullMsg;
|
||||
if (isAction)
|
||||
{
|
||||
fullMsg.format( L"%ls %ls", name.str(), msg.str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
fullMsg.format( L"[%ls] %ls", name.str(), msg.str() );
|
||||
}
|
||||
GameSpyAddText(fullMsg, style);
|
||||
}
|
||||
|
||||
void GameSpyAddText( UnicodeString message, GameSpyColors color )
|
||||
{
|
||||
GameWindow *textWindow = NULL;
|
||||
|
||||
if (!textWindow)
|
||||
textWindow = listboxLobbyChat;
|
||||
if (!textWindow)
|
||||
textWindow = listboxGameSetupChat;
|
||||
|
||||
if (!textWindow)
|
||||
return;
|
||||
|
||||
GadgetListBoxAddEntryText(textWindow, message, GameSpyColor[color], -1, -1);
|
||||
|
||||
}
|
||||
|
||||
162
Generals/Code/GameEngine/Source/GameNetwork/GameSpyGP.cpp
Normal file
162
Generals/Code/GameEngine/Source/GameNetwork/GameSpyGP.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: GameSpyGP.cpp //////////////////////////////////////////////////////
|
||||
// GameSpy GP callbacks, utils, etc
|
||||
// Author: Matthew D. Campbell, February 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameNetwork/GameSpy.h"
|
||||
#include "GameNetwork/GameSpyGP.h"
|
||||
#include "GameNetwork/GameSpyOverlay.h"
|
||||
|
||||
GPConnection TheGPConnectionObj;
|
||||
GPConnection *TheGPConnection = &TheGPConnectionObj;
|
||||
GPProfile GameSpyLocalProfile = 0;
|
||||
char GameSpyProfilePassword[64];
|
||||
|
||||
void GPRecvBuddyMessageCallback(GPConnection * pconnection, GPRecvBuddyMessageArg * arg, void * param)
|
||||
{
|
||||
DEBUG_LOG(("GPRecvBuddyMessageCallback: message from %d is %s\n", arg->profile, arg->message));
|
||||
|
||||
//gpGetInfo(pconn, arg->profile, GP_DONT_CHECK_CACHE, GP_BLOCKING, (GPCallback)Whois, NULL);
|
||||
//printf("MESSAGE (%d): %s: %s\n", msgCount,whois, arg->message);
|
||||
}
|
||||
|
||||
static void buddyTryReconnect( void )
|
||||
{
|
||||
TheGameSpyChat->reconnectProfile();
|
||||
}
|
||||
|
||||
void GPErrorCallback(GPConnection * pconnection, GPErrorArg * arg, void * param)
|
||||
{
|
||||
DEBUG_LOG(("GPErrorCallback\n"));
|
||||
|
||||
AsciiString errorCodeString;
|
||||
AsciiString resultString;
|
||||
|
||||
#define RESULT(x) case x: resultString = #x; break;
|
||||
switch(arg->result)
|
||||
{
|
||||
RESULT(GP_NO_ERROR)
|
||||
RESULT(GP_MEMORY_ERROR)
|
||||
RESULT(GP_PARAMETER_ERROR)
|
||||
RESULT(GP_NETWORK_ERROR)
|
||||
RESULT(GP_SERVER_ERROR)
|
||||
default:
|
||||
resultString = "Unknown result!";
|
||||
}
|
||||
#undef RESULT
|
||||
|
||||
#define ERRORCODE(x) case x: errorCodeString = #x; break;
|
||||
switch(arg->errorCode)
|
||||
{
|
||||
ERRORCODE(GP_GENERAL)
|
||||
ERRORCODE(GP_PARSE)
|
||||
ERRORCODE(GP_NOT_LOGGED_IN)
|
||||
ERRORCODE(GP_BAD_SESSKEY)
|
||||
ERRORCODE(GP_DATABASE)
|
||||
ERRORCODE(GP_NETWORK)
|
||||
ERRORCODE(GP_FORCED_DISCONNECT)
|
||||
ERRORCODE(GP_CONNECTION_CLOSED)
|
||||
ERRORCODE(GP_LOGIN)
|
||||
ERRORCODE(GP_LOGIN_TIMEOUT)
|
||||
ERRORCODE(GP_LOGIN_BAD_NICK)
|
||||
ERRORCODE(GP_LOGIN_BAD_EMAIL)
|
||||
ERRORCODE(GP_LOGIN_BAD_PASSWORD)
|
||||
ERRORCODE(GP_LOGIN_BAD_PROFILE)
|
||||
ERRORCODE(GP_LOGIN_PROFILE_DELETED)
|
||||
ERRORCODE(GP_LOGIN_CONNECTION_FAILED)
|
||||
ERRORCODE(GP_LOGIN_SERVER_AUTH_FAILED)
|
||||
ERRORCODE(GP_NEWUSER)
|
||||
ERRORCODE(GP_NEWUSER_BAD_NICK)
|
||||
ERRORCODE(GP_NEWUSER_BAD_PASSWORD)
|
||||
ERRORCODE(GP_UPDATEUI)
|
||||
ERRORCODE(GP_UPDATEUI_BAD_EMAIL)
|
||||
ERRORCODE(GP_NEWPROFILE)
|
||||
ERRORCODE(GP_NEWPROFILE_BAD_NICK)
|
||||
ERRORCODE(GP_NEWPROFILE_BAD_OLD_NICK)
|
||||
ERRORCODE(GP_UPDATEPRO)
|
||||
ERRORCODE(GP_UPDATEPRO_BAD_NICK)
|
||||
ERRORCODE(GP_ADDBUDDY)
|
||||
ERRORCODE(GP_ADDBUDDY_BAD_FROM)
|
||||
ERRORCODE(GP_ADDBUDDY_BAD_NEW)
|
||||
ERRORCODE(GP_ADDBUDDY_ALREADY_BUDDY)
|
||||
ERRORCODE(GP_AUTHADD)
|
||||
ERRORCODE(GP_AUTHADD_BAD_FROM)
|
||||
ERRORCODE(GP_AUTHADD_BAD_SIG)
|
||||
ERRORCODE(GP_STATUS)
|
||||
ERRORCODE(GP_BM)
|
||||
ERRORCODE(GP_BM_NOT_BUDDY)
|
||||
ERRORCODE(GP_GETPROFILE)
|
||||
ERRORCODE(GP_GETPROFILE_BAD_PROFILE)
|
||||
ERRORCODE(GP_DELBUDDY)
|
||||
ERRORCODE(GP_DELBUDDY_NOT_BUDDY)
|
||||
ERRORCODE(GP_DELPROFILE)
|
||||
ERRORCODE(GP_DELPROFILE_LAST_PROFILE)
|
||||
ERRORCODE(GP_SEARCH)
|
||||
ERRORCODE(GP_SEARCH_CONNECTION_FAILED)
|
||||
default:
|
||||
errorCodeString = "Unknown error code!";
|
||||
}
|
||||
#undef ERRORCODE
|
||||
|
||||
if(arg->fatal)
|
||||
{
|
||||
DEBUG_LOG(( "-----------\n"));
|
||||
DEBUG_LOG(( "GP FATAL ERROR\n"));
|
||||
DEBUG_LOG(( "-----------\n"));
|
||||
|
||||
// if we're still connected to the chat server, tell the user. He can always hit the buddy
|
||||
// button to try reconnecting. Oh yes, also hide the buddy popup.
|
||||
GameSpyCloseOverlay(GSOVERLAY_BUDDY);
|
||||
if (TheGameSpyChat->isConnected())
|
||||
{
|
||||
GSMessageBoxYesNo(TheGameText->fetch("GUI:GPErrorTitle"), TheGameText->fetch("GUI:GPDisconnected"), buddyTryReconnect, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(( "-----\n"));
|
||||
DEBUG_LOG(( "GP ERROR\n"));
|
||||
DEBUG_LOG(( "-----\n"));
|
||||
}
|
||||
DEBUG_LOG(( "RESULT: %s (%d)\n", resultString.str(), arg->result));
|
||||
DEBUG_LOG(( "ERROR CODE: %s (0x%X)\n", errorCodeString.str(), arg->errorCode));
|
||||
DEBUG_LOG(( "ERROR STRING: %s\n", arg->errorString));
|
||||
}
|
||||
|
||||
void GPRecvBuddyStatusCallback(GPConnection * connection, GPRecvBuddyStatusArg * arg, void * param)
|
||||
{
|
||||
DEBUG_LOG(("GPRecvBuddyStatusCallback: info on %d is in %d\n", arg->profile, arg->index));
|
||||
|
||||
//GameSpyUpdateBuddyOverlay();
|
||||
}
|
||||
|
||||
void GPRecvBuddyRequestCallback(GPConnection * connection, GPRecvBuddyRequestArg * arg, void * param)
|
||||
{
|
||||
DEBUG_LOG(("GPRecvBuddyRequestCallback: %d wants to be our buddy because '%s'\n", arg->profile, arg->reason));
|
||||
}
|
||||
754
Generals/Code/GameEngine/Source/GameNetwork/GameSpyGameInfo.cpp
Normal file
754
Generals/Code/GameEngine/Source/GameNetwork/GameSpyGameInfo.cpp
Normal file
@@ -0,0 +1,754 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: GameSpyGameInfo.cpp //////////////////////////////////////////////////////
|
||||
// GameSpy game setup state info
|
||||
// Author: Matthew D. Campbell, December 2001
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GameEngine.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/Scorekeeper.h"
|
||||
#include "GameClient/Shell.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameNetwork/GameSpy/PeerDefs.h"
|
||||
#include "GameNetwork/GameSpyGameInfo.h"
|
||||
#include "GameNetwork/NetworkInterface.h"
|
||||
#include "GameNetwork/NetworkUtil.h"
|
||||
#include "GameNetwork/NetworkDefs.h"
|
||||
#include "GameNetwork/NAT.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/VictoryConditions.h"
|
||||
|
||||
// Singleton ------------------------------------------
|
||||
|
||||
GameSpyGameInfo *TheGameSpyGame = NULL;
|
||||
|
||||
// Helper Functions ----------------------------------------
|
||||
|
||||
GameSpyGameSlot::GameSpyGameSlot()
|
||||
{
|
||||
GameSlot();
|
||||
m_gameSpyLogin.clear();
|
||||
m_gameSpyLocale.clear();
|
||||
m_profileID = 0;
|
||||
}
|
||||
|
||||
// Helper Functions ----------------------------------------
|
||||
/*
|
||||
** Function definitions for the MIB-II entry points.
|
||||
*/
|
||||
|
||||
BOOL (__stdcall *SnmpExtensionInitPtr)(IN DWORD dwUpTimeReference, OUT HANDLE *phSubagentTrapEvent, OUT AsnObjectIdentifier *pFirstSupportedRegion);
|
||||
BOOL (__stdcall *SnmpExtensionQueryPtr)(IN BYTE bPduType, IN OUT RFC1157VarBindList *pVarBindList, OUT AsnInteger32 *pErrorStatus, OUT AsnInteger32 *pErrorIndex);
|
||||
LPVOID (__stdcall *SnmpUtilMemAllocPtr)(IN DWORD bytes);
|
||||
VOID (__stdcall *SnmpUtilMemFreePtr)(IN LPVOID pMem);
|
||||
|
||||
typedef struct tConnInfoStruct {
|
||||
unsigned int State;
|
||||
unsigned long LocalIP;
|
||||
unsigned short LocalPort;
|
||||
unsigned long RemoteIP;
|
||||
unsigned short RemotePort;
|
||||
} ConnInfoStruct;
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
|
||||
#endif
|
||||
|
||||
/***********************************************************************************************
|
||||
* Get_Local_Chat_Connection_Address -- Which address are we using to talk to the chat server? *
|
||||
* *
|
||||
* *
|
||||
* *
|
||||
* INPUT: Ptr to address to return local address * *
|
||||
* *
|
||||
* OUTPUT: True if success *
|
||||
* *
|
||||
* WARNINGS: None *
|
||||
* *
|
||||
* HISTORY: *
|
||||
* 10/27/00 3:24PM ST : Created *
|
||||
*=============================================================================================*/
|
||||
Bool GetLocalChatConnectionAddress(AsciiString serverName, UnsignedShort serverPort, UnsignedInt& localIP)
|
||||
{
|
||||
//return false;
|
||||
/*
|
||||
** Local defines.
|
||||
*/
|
||||
enum {
|
||||
CLOSED = 1,
|
||||
LISTENING,
|
||||
SYN_SENT,
|
||||
SEN_RECEIVED,
|
||||
ESTABLISHED,
|
||||
FIN_WAIT,
|
||||
FIN_WAIT2,
|
||||
CLOSE_WAIT,
|
||||
LAST_ACK,
|
||||
CLOSING,
|
||||
TIME_WAIT,
|
||||
DELETE_TCB
|
||||
};
|
||||
|
||||
enum {
|
||||
tcpConnState = 1,
|
||||
tcpConnLocalAddress,
|
||||
tcpConnLocalPort,
|
||||
tcpConnRemAddress,
|
||||
tcpConnRemPort
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Locals.
|
||||
*/
|
||||
unsigned char serverAddress[4];
|
||||
unsigned char remoteAddress[4];
|
||||
HANDLE trap_handle;
|
||||
AsnObjectIdentifier first_supported_region;
|
||||
std::vector<ConnInfoStruct> connectionVector;
|
||||
int last_field;
|
||||
int index;
|
||||
AsnInteger error_status;
|
||||
AsnInteger error_index;
|
||||
int conn_entry_type_index;
|
||||
int conn_entry_type;
|
||||
Bool found;
|
||||
|
||||
/*
|
||||
** Statics.
|
||||
*/
|
||||
static char _conn_state[][32] = {
|
||||
"?",
|
||||
"CLOSED",
|
||||
"LISTENING",
|
||||
"SYN_SENT",
|
||||
"SEN_RECEIVED",
|
||||
"ESTABLISHED",
|
||||
"FIN_WAIT",
|
||||
"FIN_WAIT2",
|
||||
"CLOSE_WAIT",
|
||||
"LAST_ACK",
|
||||
"CLOSING",
|
||||
"TIME_WAIT",
|
||||
"DELETE_TCB"
|
||||
};
|
||||
|
||||
DEBUG_LOG(("Finding local address used to talk to the chat server\n"));
|
||||
DEBUG_LOG(("Current chat server name is %s\n", serverName.str()));
|
||||
DEBUG_LOG(("Chat server port is %d\n", serverPort));
|
||||
|
||||
/*
|
||||
** Get the address of the chat server.
|
||||
*/
|
||||
DEBUG_LOG( ("About to call gethostbyname\n"));
|
||||
struct hostent *host_info = gethostbyname(serverName.str());
|
||||
|
||||
if (!host_info) {
|
||||
DEBUG_LOG( ("gethostbyname failed! Error code %d\n", WSAGetLastError()));
|
||||
return(false);
|
||||
}
|
||||
|
||||
memcpy(serverAddress, &host_info->h_addr_list[0][0], 4);
|
||||
unsigned long temp = *((unsigned long*)(&serverAddress[0]));
|
||||
temp = ntohl(temp);
|
||||
*((unsigned long*)(&serverAddress[0])) = temp;
|
||||
|
||||
DEBUG_LOG(("Host address is %d.%d.%d.%d\n", serverAddress[3], serverAddress[2], serverAddress[1], serverAddress[0]));
|
||||
|
||||
/*
|
||||
** Load the MIB-II SNMP DLL.
|
||||
*/
|
||||
DEBUG_LOG(("About to load INETMIB1.DLL\n"));
|
||||
|
||||
HINSTANCE mib_ii_dll = LoadLibrary("inetmib1.dll");
|
||||
if (mib_ii_dll == NULL) {
|
||||
DEBUG_LOG(("Failed to load INETMIB1.DLL\n"));
|
||||
return(false);
|
||||
}
|
||||
|
||||
DEBUG_LOG(("About to load SNMPAPI.DLL\n"));
|
||||
|
||||
HINSTANCE snmpapi_dll = LoadLibrary("snmpapi.dll");
|
||||
if (snmpapi_dll == NULL) {
|
||||
DEBUG_LOG(("Failed to load SNMPAPI.DLL\n"));
|
||||
FreeLibrary(mib_ii_dll);
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
** Get the function pointers into the .dll
|
||||
*/
|
||||
SnmpExtensionInitPtr = (int (__stdcall *)(unsigned long,void ** ,AsnObjectIdentifier *)) GetProcAddress(mib_ii_dll, "SnmpExtensionInit");
|
||||
SnmpExtensionQueryPtr = (int (__stdcall *)(unsigned char,SnmpVarBindList *,long *,long *)) GetProcAddress(mib_ii_dll, "SnmpExtensionQuery");
|
||||
SnmpUtilMemAllocPtr = (void *(__stdcall *)(unsigned long)) GetProcAddress(snmpapi_dll, "SnmpUtilMemAlloc");
|
||||
SnmpUtilMemFreePtr = (void (__stdcall *)(void *)) GetProcAddress(snmpapi_dll, "SnmpUtilMemFree");
|
||||
if (SnmpExtensionInitPtr == NULL || SnmpExtensionQueryPtr == NULL || SnmpUtilMemAllocPtr == NULL || SnmpUtilMemFreePtr == NULL) {
|
||||
DEBUG_LOG(("Failed to get proc addresses for linked functions\n"));
|
||||
FreeLibrary(snmpapi_dll);
|
||||
FreeLibrary(mib_ii_dll);
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
RFC1157VarBindList *bind_list_ptr = (RFC1157VarBindList *) SnmpUtilMemAllocPtr(sizeof(RFC1157VarBindList));
|
||||
RFC1157VarBind *bind_ptr = (RFC1157VarBind *) SnmpUtilMemAllocPtr(sizeof(RFC1157VarBind));
|
||||
|
||||
/*
|
||||
** OK, here we go. Try to initialise the .dll
|
||||
*/
|
||||
DEBUG_LOG(("About to init INETMIB1.DLL\n"));
|
||||
int ok = SnmpExtensionInitPtr(GetCurrentTime(), &trap_handle, &first_supported_region);
|
||||
|
||||
if (!ok) {
|
||||
/*
|
||||
** Aw crap.
|
||||
*/
|
||||
DEBUG_LOG(("Failed to init the .dll\n"));
|
||||
SnmpUtilMemFreePtr(bind_list_ptr);
|
||||
SnmpUtilMemFreePtr(bind_ptr);
|
||||
FreeLibrary(snmpapi_dll);
|
||||
FreeLibrary(mib_ii_dll);
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
** Name of mib_ii object we want to query. See RFC 1213.
|
||||
**
|
||||
** iso.org.dod.internet.mgmt.mib-2.tcp.tcpConnTable.TcpConnEntry.tcpConnState
|
||||
** 1 3 6 1 2 1 6 13 1 1
|
||||
*/
|
||||
unsigned int mib_ii_name[] = {1,3,6,1,2,1,6,13,1,1};
|
||||
unsigned int *mib_ii_name_ptr = (unsigned int *) SnmpUtilMemAllocPtr(sizeof(mib_ii_name));
|
||||
memcpy(mib_ii_name_ptr, mib_ii_name, sizeof(mib_ii_name));
|
||||
|
||||
/*
|
||||
** Get the index of the conn entry data.
|
||||
*/
|
||||
conn_entry_type_index = ARRAY_SIZE(mib_ii_name) - 1;
|
||||
|
||||
/*
|
||||
** Set up the bind list.
|
||||
*/
|
||||
bind_ptr->name.idLength = ARRAY_SIZE(mib_ii_name);
|
||||
bind_ptr->name.ids = mib_ii_name;
|
||||
bind_list_ptr->list = bind_ptr;
|
||||
bind_list_ptr->len = 1;
|
||||
|
||||
|
||||
/*
|
||||
** We start with the tcpConnLocalAddress field.
|
||||
*/
|
||||
last_field = 1;
|
||||
|
||||
/*
|
||||
** First connection.
|
||||
*/
|
||||
index = 0;
|
||||
|
||||
/*
|
||||
** Suck out that tcp connection info....
|
||||
*/
|
||||
while (true) {
|
||||
|
||||
if (!SnmpExtensionQueryPtr(SNMP_PDU_GETNEXT, bind_list_ptr, &error_status, &error_index)) {
|
||||
//if (!SnmpExtensionQueryPtr(ASN_RFC1157_GETNEXTREQUEST, bind_list_ptr, &error_status, &error_index)) {
|
||||
DEBUG_LOG(("SnmpExtensionQuery returned false\n"));
|
||||
SnmpUtilMemFreePtr(bind_list_ptr);
|
||||
SnmpUtilMemFreePtr(bind_ptr);
|
||||
FreeLibrary(snmpapi_dll);
|
||||
FreeLibrary(mib_ii_dll);
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
** If this is something new we aren't looking for then we are done.
|
||||
*/
|
||||
if (bind_ptr->name.idLength < ARRAY_SIZE(mib_ii_name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** Get the type of info we are looking at. See RFC1213.
|
||||
**
|
||||
** 1 = tcpConnState
|
||||
** 2 = tcpConnLocalAddress
|
||||
** 3 = tcpConnLocalPort
|
||||
** 4 = tcpConnRemAddress
|
||||
** 5 = tcpConnRemPort
|
||||
**
|
||||
** tcpConnState is one of the following...
|
||||
**
|
||||
** 1 closed
|
||||
** 2 listen
|
||||
** 3 synSent
|
||||
** 4 synReceived
|
||||
** 5 established
|
||||
** 6 finWait1
|
||||
** 7 finWait2
|
||||
** 8 closeWait
|
||||
** 9 lastAck
|
||||
** 10 closing
|
||||
** 11 timeWait
|
||||
** 12 deleteTCB
|
||||
*/
|
||||
conn_entry_type = bind_ptr->name.ids[conn_entry_type_index];
|
||||
|
||||
if (last_field != conn_entry_type) {
|
||||
index = 0;
|
||||
last_field = conn_entry_type;
|
||||
}
|
||||
|
||||
switch (conn_entry_type) {
|
||||
|
||||
/*
|
||||
** 1. First field in the entry. Need to create a new connection info struct
|
||||
** here to store this connection in.
|
||||
*/
|
||||
case tcpConnState:
|
||||
{
|
||||
ConnInfoStruct new_conn;
|
||||
new_conn.State = bind_ptr->value.asnValue.number;
|
||||
connectionVector.push_back(new_conn);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** 2. Local address field.
|
||||
*/
|
||||
case tcpConnLocalAddress:
|
||||
DEBUG_ASSERTCRASH(index < connectionVector.size(), ("Bad connection index"));
|
||||
connectionVector[index].LocalIP = *((unsigned long*)bind_ptr->value.asnValue.address.stream);
|
||||
index++;
|
||||
break;
|
||||
|
||||
/*
|
||||
** 3. Local port field.
|
||||
*/
|
||||
case tcpConnLocalPort:
|
||||
DEBUG_ASSERTCRASH(index < connectionVector.size(), ("Bad connection index"));
|
||||
connectionVector[index].LocalPort = bind_ptr->value.asnValue.number;
|
||||
//connectionVector[index]->LocalPort = ntohs(connectionVector[index]->LocalPort);
|
||||
index++;
|
||||
break;
|
||||
|
||||
/*
|
||||
** 4. Remote address field.
|
||||
*/
|
||||
case tcpConnRemAddress:
|
||||
DEBUG_ASSERTCRASH(index < connectionVector.size(), ("Bad connection index"));
|
||||
connectionVector[index].RemoteIP = *((unsigned long*)bind_ptr->value.asnValue.address.stream);
|
||||
index++;
|
||||
break;
|
||||
|
||||
/*
|
||||
** 5. Remote port field.
|
||||
*/
|
||||
case tcpConnRemPort:
|
||||
DEBUG_ASSERTCRASH(index < connectionVector.size(), ("Bad connection index"));
|
||||
connectionVector[index].RemotePort = bind_ptr->value.asnValue.number;
|
||||
//connectionVector[index]->RemotePort = ntohs(connectionVector[index]->RemotePort);
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SnmpUtilMemFreePtr(bind_list_ptr);
|
||||
SnmpUtilMemFreePtr(bind_ptr);
|
||||
SnmpUtilMemFreePtr(mib_ii_name_ptr);
|
||||
|
||||
DEBUG_LOG(("Got %d connections in list, parsing...\n", connectionVector.size()));
|
||||
|
||||
/*
|
||||
** Right, we got the lot. Lets see if any of them have the same address as the chat
|
||||
** server we think we are talking to.
|
||||
*/
|
||||
found = false;
|
||||
for (Int i=0; i<connectionVector.size(); ++i) {
|
||||
ConnInfoStruct connection = connectionVector[i];
|
||||
|
||||
temp = ntohl(connection.RemoteIP);
|
||||
memcpy(remoteAddress, (unsigned char*)&temp, 4);
|
||||
|
||||
/*
|
||||
** See if this connection has the same address as our server.
|
||||
*/
|
||||
if (!found && memcmp(remoteAddress, serverAddress, 4) == 0) {
|
||||
DEBUG_LOG(("Found connection with same remote address as server\n"));
|
||||
|
||||
if (serverPort == 0 || serverPort == (unsigned int)connection.RemotePort) {
|
||||
|
||||
DEBUG_LOG(("Connection has same port\n"));
|
||||
/*
|
||||
** Make sure the connection is current.
|
||||
*/
|
||||
if (connection.State == ESTABLISHED) {
|
||||
DEBUG_LOG(("Connection is ESTABLISHED\n"));
|
||||
localIP = connection.LocalIP;
|
||||
found = true;
|
||||
} else {
|
||||
DEBUG_LOG(("Connection is not ESTABLISHED - skipping\n"));
|
||||
}
|
||||
} else {
|
||||
DEBUG_LOG(("Connection has different port. Port is %d, looking for %d\n", connection.RemotePort, serverPort));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
DEBUG_LOG(("Using address 0x%8.8X to talk to chat server\n", localIP));
|
||||
}
|
||||
|
||||
FreeLibrary(snmpapi_dll);
|
||||
FreeLibrary(mib_ii_dll);
|
||||
return(found);
|
||||
}
|
||||
|
||||
|
||||
// GameSpyGameInfo ----------------------------------------
|
||||
|
||||
GameSpyGameInfo::GameSpyGameInfo()
|
||||
{
|
||||
m_isQM = FALSE;
|
||||
m_hasBeenQueried = FALSE;
|
||||
for (Int i = 0; i< MAX_SLOTS; ++i)
|
||||
setSlotPointer(i, &m_GameSpySlot[i]);
|
||||
|
||||
UnsignedInt localIP;
|
||||
if (GetLocalChatConnectionAddress("peerchat.gamespy.com", 6667, localIP))
|
||||
{
|
||||
localIP = ntohl(localIP); // The IP returned from GetLocalChatConnectionAddress is in network byte order.
|
||||
setLocalIP(localIP);
|
||||
}
|
||||
else
|
||||
{
|
||||
setLocalIP(0);
|
||||
}
|
||||
m_server = NULL;
|
||||
m_transport = NULL;
|
||||
}
|
||||
|
||||
// Misc game-related functionality --------------------
|
||||
|
||||
void GameSpyStartGame( void )
|
||||
{
|
||||
if (TheGameSpyGame)
|
||||
{
|
||||
int i;
|
||||
|
||||
int numUsers = 0;
|
||||
for (i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
GameSlot *slot = TheGameSpyGame->getSlot(i);
|
||||
if (slot && slot->isOccupied())
|
||||
numUsers++;
|
||||
}
|
||||
|
||||
if (numUsers < 2)
|
||||
{
|
||||
if (TheGameSpyGame->amIHost())
|
||||
{
|
||||
UnicodeString text;
|
||||
text.format(TheGameText->fetch("LAN:NeedMorePlayers"),numUsers);
|
||||
TheGameSpyInfo->addText(text, GSCOLOR_DEFAULT, NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
TheGameSpyGame->startGame(0);
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyLaunchGame( void )
|
||||
{
|
||||
if (TheGameSpyGame)
|
||||
{
|
||||
|
||||
// Set up the game network
|
||||
AsciiString user;
|
||||
AsciiString userList;
|
||||
DEBUG_ASSERTCRASH(TheNetwork == NULL, ("For some reason TheNetwork isn't NULL at the start of this game. Better look into that."));
|
||||
|
||||
if (TheNetwork != NULL) {
|
||||
delete TheNetwork;
|
||||
TheNetwork = NULL;
|
||||
}
|
||||
|
||||
// Time to initialize TheNetwork for this game.
|
||||
TheNetwork = NetworkInterface::createNetwork();
|
||||
TheNetwork->init();
|
||||
/*
|
||||
if (!TheGameSpyGame->amIHost())
|
||||
TheNetwork->setLocalAddress((207<<24) | (138<<16) | (47<<8) | 15, 8088);
|
||||
else
|
||||
*/
|
||||
TheNetwork->setLocalAddress(TheGameSpyGame->getLocalIP(), TheNAT->getSlotPort(TheGameSpyGame->getLocalSlotNum()));
|
||||
TheNetwork->attachTransport(TheNAT->getTransport());
|
||||
|
||||
user = TheGameSpyInfo->getLocalName();
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
GameSlot *slot = TheGameSpyGame->getSlot(i);
|
||||
if (!slot)
|
||||
{
|
||||
DEBUG_CRASH(("No GameSlot[%d]!", i));
|
||||
delete TheNetwork;
|
||||
TheNetwork = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// UnsignedInt ip = htonl(slot->getIP());
|
||||
UnsignedInt ip = slot->getIP();
|
||||
AsciiString tmpUserName;
|
||||
tmpUserName.translate(slot->getName());
|
||||
if (ip)
|
||||
{
|
||||
/*
|
||||
if (i == 1)
|
||||
{
|
||||
user.format(",%s@207.138.47.15:8088", tmpUserName.str());
|
||||
}
|
||||
else
|
||||
*/
|
||||
{
|
||||
user.format(",%s@%d.%d.%d.%d:%d", tmpUserName.str(),
|
||||
((ip & 0xff000000) >> 24),
|
||||
((ip & 0xff0000) >> 16),
|
||||
((ip & 0xff00) >> 8),
|
||||
((ip & 0xff)),
|
||||
TheNAT->getSlotPort(i)
|
||||
);
|
||||
}
|
||||
userList.concat(user);
|
||||
}
|
||||
}
|
||||
userList.trim();
|
||||
|
||||
TheNetwork->parseUserList(TheGameSpyGame);
|
||||
|
||||
// shutdown the top, but do not pop it off the stack
|
||||
// TheShell->hideShell();
|
||||
// setup the Global Data with the Map and Seed
|
||||
TheGlobalData->m_pendingFile = TheGameSpyGame->getMap();
|
||||
|
||||
if (TheGameLogic->isInGame()) {
|
||||
TheGameLogic->clearGameData();
|
||||
}
|
||||
// send a message to the logic for a new game
|
||||
GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_NEW_GAME );
|
||||
msg->appendIntegerArgument(GAME_INTERNET);
|
||||
|
||||
TheGlobalData->m_useFpsLimit = false;
|
||||
|
||||
// Set the random seed
|
||||
InitGameLogicRandom( TheGameSpyGame->getSeed() );
|
||||
DEBUG_LOG(("InitGameLogicRandom( %d )\n", TheGameSpyGame->getSeed()));
|
||||
|
||||
if (TheNAT != NULL) {
|
||||
delete TheNAT;
|
||||
TheNAT = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyGameInfo::init( void )
|
||||
{
|
||||
GameInfo::init();
|
||||
|
||||
m_hasBeenQueried = false;
|
||||
}
|
||||
|
||||
void GameSpyGameInfo::resetAccepted( void )
|
||||
{
|
||||
GameInfo::resetAccepted();
|
||||
|
||||
if (m_hasBeenQueried && amIHost())
|
||||
{
|
||||
// ANCIENTMUNKEE peerStateChanged(TheGameSpyChat->getPeer());
|
||||
m_hasBeenQueried = false;
|
||||
DEBUG_LOG(("resetAccepted() called peerStateChange()\n"));
|
||||
}
|
||||
}
|
||||
|
||||
Int GameSpyGameInfo::getLocalSlotNum( void ) const
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_inGame, ("Looking for local game slot while not in game"));
|
||||
if (!m_inGame)
|
||||
return -1;
|
||||
|
||||
AsciiString localName = TheGameSpyInfo->getLocalName();
|
||||
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
const GameSlot *slot = getConstSlot(i);
|
||||
if (slot == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (slot->isPlayer(localName))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void GameSpyGameInfo::gotGOACall( void )
|
||||
{
|
||||
DEBUG_LOG(("gotGOACall()\n"));
|
||||
m_hasBeenQueried = true;
|
||||
}
|
||||
|
||||
void GameSpyGameInfo::startGame(Int gameID)
|
||||
{
|
||||
DEBUG_LOG(("GameSpyGameInfo::startGame - game id = %d\n", gameID));
|
||||
DEBUG_ASSERTCRASH(m_transport == NULL, ("m_transport is not NULL when it should be"));
|
||||
DEBUG_ASSERTCRASH(TheNAT == NULL, ("TheNAT is not NULL when it should be"));
|
||||
|
||||
// fill in GS-specific info
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
if (m_GameSpySlot[i].isHuman())
|
||||
{
|
||||
AsciiString gsName;
|
||||
gsName.translate( m_GameSpySlot[i].getName() );
|
||||
m_GameSpySlot[i].setLoginName( gsName );
|
||||
|
||||
PlayerInfoMap *pInfoMap = TheGameSpyInfo->getPlayerInfoMap();
|
||||
PlayerInfoMap::iterator it = pInfoMap->find(gsName);
|
||||
if (it != pInfoMap->end())
|
||||
{
|
||||
m_GameSpySlot[i].setProfileID(it->second.m_profileID);
|
||||
m_GameSpySlot[i].setLocale(it->second.m_locale);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_CRASH(("No player info for %s", gsName.str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TheNAT != NULL) {
|
||||
delete TheNAT;
|
||||
TheNAT = NULL;
|
||||
}
|
||||
TheNAT = NEW NAT();
|
||||
TheNAT->attachSlotList(m_slot, getLocalSlotNum(), m_localIP);
|
||||
TheNAT->establishConnectionPaths();
|
||||
}
|
||||
|
||||
AsciiString GameSpyGameInfo::generateGameResultsPacket( void )
|
||||
{
|
||||
Int i;
|
||||
Int endFrame = TheVictoryConditions->getEndFrame();
|
||||
Int localSlotNum = getLocalSlotNum();
|
||||
//GameSlot *localSlot = getSlot(localSlotNum);
|
||||
Bool sawGameEnd = (endFrame > 0);// && localSlot->lastFrameInGame() <= endFrame);
|
||||
Int winningTeam = -1;
|
||||
Int numPlayers = 0;
|
||||
Int numTeamsAtGameEnd = 0;
|
||||
Int lastTeamAtGameEnd = -1;
|
||||
for (i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
AsciiString playerName;
|
||||
playerName.format("player%d", i);
|
||||
Player *p = ThePlayerList->findPlayerWithNameKey(NAMEKEY(playerName));
|
||||
if (p)
|
||||
{
|
||||
++numPlayers;
|
||||
if (TheVictoryConditions->hasAchievedVictory(p))
|
||||
{
|
||||
winningTeam = getSlot(i)->getTeamNumber();
|
||||
}
|
||||
|
||||
// check if he lasted
|
||||
GameSlot *slot = getSlot(i);
|
||||
if (!slot->disconnected())
|
||||
{
|
||||
if (slot->getTeamNumber() != lastTeamAtGameEnd || numTeamsAtGameEnd == 0)
|
||||
{
|
||||
lastTeamAtGameEnd = slot->getTeamNumber();
|
||||
++numTeamsAtGameEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AsciiString results;
|
||||
results.format("seed=%d,slotNum=%d,sawDesync=%d,sawGameEnd=%d,winningTeam=%d,disconEnd=%d,duration=%d,numPlayers=%d,isQM=%d",
|
||||
getSeed(), localSlotNum, TheNetwork->sawCRCMismatch(), sawGameEnd, winningTeam, (numTeamsAtGameEnd != 0),
|
||||
endFrame, numPlayers, m_isQM);
|
||||
|
||||
Int playerID = 0;
|
||||
for (i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
AsciiString playerName;
|
||||
playerName.format("player%d", i);
|
||||
Player *p = ThePlayerList->findPlayerWithNameKey(NAMEKEY(playerName));
|
||||
if (p)
|
||||
{
|
||||
GameSpyGameSlot *slot = &(m_GameSpySlot[i]);
|
||||
ScoreKeeper *keeper = p->getScoreKeeper();
|
||||
AsciiString playerName = slot->getLoginName();
|
||||
Int gsPlayerID = slot->getProfileID();
|
||||
AsciiString locale = slot->getLocale();
|
||||
Int fps = TheNetwork->getAverageFPS();
|
||||
Int unitsKilled = keeper->getTotalUnitsDestroyed();
|
||||
Int unitsLost = keeper->getTotalUnitsLost();
|
||||
Int unitsBuilt = keeper->getTotalUnitsBuilt();
|
||||
Int buildingsKilled = keeper->getTotalBuildingsDestroyed();
|
||||
Int buildingsLost = keeper->getTotalBuildingsLost();
|
||||
Int buildingsBuilt = keeper->getTotalBuildingsBuilt();
|
||||
Int earnings = keeper->getTotalMoneyEarned();
|
||||
Int techCaptured = keeper->getTotalTechBuildingsCaptured();
|
||||
Bool disconnected = slot->disconnected();
|
||||
|
||||
AsciiString playerStr;
|
||||
playerStr.format(",player%d=%s,playerID%d=%d,locale%d=%s",
|
||||
playerID, playerName.str(), playerID, gsPlayerID, playerID, locale.str());
|
||||
results.concat(playerStr);
|
||||
playerStr.format(",unitsKilled%d=%d,unitsLost%d=%d,unitsBuilt%d=%d",
|
||||
playerID, unitsKilled, playerID, unitsLost, playerID, unitsBuilt);
|
||||
results.concat(playerStr);
|
||||
playerStr.format(",buildingsKilled%d=%d,buildingsLost%d=%d,buildingsBuilt%d=%d",
|
||||
playerID, buildingsKilled, playerID, buildingsLost, playerID, buildingsBuilt);
|
||||
results.concat(playerStr);
|
||||
playerStr.format(",fps%d=%d,cash%d=%d,capturedTech%d=%d,discon%d=%d",
|
||||
playerID, fps, playerID, earnings, playerID, techCaptured, playerID, disconnected);
|
||||
results.concat(playerStr);
|
||||
|
||||
++playerID;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a trailing size value (so the server can ensure it got the entire packet)
|
||||
int resultsLen = results.getLength()+10;
|
||||
AsciiString tail;
|
||||
tail.format("%10.10d", resultsLen);
|
||||
results.concat(tail);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
333
Generals/Code/GameEngine/Source/GameNetwork/GameSpyOverlay.cpp
Normal file
333
Generals/Code/GameEngine/Source/GameNetwork/GameSpyOverlay.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: wolscreens.cpp //////////////////////////////////////////////////////
|
||||
// Westwood Online screen setup/teardown
|
||||
// Author: Matthew D. Campbell, November 2001
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/AudioEventRTS.h"
|
||||
|
||||
#include "GameClient/GadgetListBox.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/MessageBox.h"
|
||||
#include "GameClient/ShellHooks.h"
|
||||
//#include "GameNetwork/GameSpy.h"
|
||||
//#include "GameNetwork/GameSpyGP.h"
|
||||
|
||||
#include "GameNetwork/GameSpyOverlay.h"
|
||||
//#include "GameNetwork/GameSpy/PeerDefs.h"
|
||||
#include "GameNetwork/GameSpy/BuddyThread.h"
|
||||
|
||||
void deleteNotificationBox( void );
|
||||
static void raiseOverlays( void );
|
||||
|
||||
// Message boxes -------------------------------------
|
||||
static GameWindow *messageBoxWindow = NULL;
|
||||
static GameWinMsgBoxFunc okFunc = NULL;
|
||||
static GameWinMsgBoxFunc cancelFunc = NULL;
|
||||
static Bool reOpenPlayerInfoFlag = FALSE;
|
||||
/**
|
||||
* messageBoxOK is called when a message box is destroyed
|
||||
* by way of an OK button, so we can clear our pointers to it.
|
||||
*/
|
||||
static void messageBoxOK( void )
|
||||
{
|
||||
DEBUG_ASSERTCRASH(messageBoxWindow, ("Message box window went away without being there in the first place!"));
|
||||
messageBoxWindow = NULL;
|
||||
if (okFunc)
|
||||
{
|
||||
okFunc();
|
||||
okFunc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* messageBoxCancel is called when a message box is destroyed
|
||||
* by way of a Cancel button, so we can clear our pointers to it.
|
||||
*/
|
||||
static void messageBoxCancel( void )
|
||||
{
|
||||
DEBUG_ASSERTCRASH(messageBoxWindow, ("Message box window went away without being there in the first place!"));
|
||||
messageBoxWindow = NULL;
|
||||
if (cancelFunc)
|
||||
{
|
||||
cancelFunc();
|
||||
cancelFunc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clearGSMessageBoxes removes the current message box if
|
||||
* one is present. This is usually done when putting up a
|
||||
* second messageBox.
|
||||
*/
|
||||
void ClearGSMessageBoxes( void )
|
||||
{
|
||||
if (messageBoxWindow)
|
||||
{
|
||||
TheWindowManager->winDestroy(messageBoxWindow);
|
||||
messageBoxWindow = NULL;
|
||||
}
|
||||
|
||||
if (okFunc)
|
||||
{
|
||||
okFunc = NULL;
|
||||
}
|
||||
|
||||
if (cancelFunc)
|
||||
{
|
||||
cancelFunc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GSMessageBoxOk puts up an OK dialog box and saves the
|
||||
* pointers to it and its callbacks.
|
||||
*/
|
||||
void GSMessageBoxOk(UnicodeString title, UnicodeString message, GameWinMsgBoxFunc newOkFunc)
|
||||
{
|
||||
ClearGSMessageBoxes();
|
||||
messageBoxWindow = MessageBoxOk(title, message, messageBoxOK);
|
||||
okFunc = newOkFunc;
|
||||
}
|
||||
|
||||
/**
|
||||
* GSMessageBoxOkCancel puts up an OK/Cancel dialog box and saves the
|
||||
* pointers to it and its callbacks.
|
||||
*/
|
||||
void GSMessageBoxOkCancel(UnicodeString title, UnicodeString message, GameWinMsgBoxFunc newOkFunc, GameWinMsgBoxFunc newCancelFunc)
|
||||
{
|
||||
ClearGSMessageBoxes();
|
||||
messageBoxWindow = MessageBoxOkCancel(title, message, messageBoxOK, messageBoxCancel);
|
||||
okFunc = newOkFunc;
|
||||
cancelFunc = newCancelFunc;
|
||||
}
|
||||
|
||||
/**
|
||||
* GSMessageBoxYesNo puts up a Yes/No dialog box and saves the
|
||||
* pointers to it and its callbacks.
|
||||
*/
|
||||
void GSMessageBoxYesNo(UnicodeString title, UnicodeString message, GameWinMsgBoxFunc newYesFunc, GameWinMsgBoxFunc newNoFunc)
|
||||
{
|
||||
ClearGSMessageBoxes();
|
||||
messageBoxWindow = MessageBoxYesNo(title, message, messageBoxOK, messageBoxCancel);
|
||||
okFunc = newYesFunc;
|
||||
cancelFunc = newNoFunc;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the screen transitions underneath the dialog box, we
|
||||
* need to raise it to keep it visible.
|
||||
*/
|
||||
void RaiseGSMessageBox( void )
|
||||
{
|
||||
raiseOverlays();
|
||||
|
||||
if (!messageBoxWindow)
|
||||
return;
|
||||
|
||||
messageBoxWindow->winBringToTop();
|
||||
}
|
||||
|
||||
// Overlay screens -------------------------------------
|
||||
|
||||
/**
|
||||
* gsOverlays holds a list of the .wnd files used in GS overlays.
|
||||
* The entries *MUST* be in the same order as the GSOverlayType enum.
|
||||
*/
|
||||
static const char * gsOverlays[GSOVERLAY_MAX] =
|
||||
{
|
||||
"Menus/PopupPlayerInfo.wnd", // Player info (right-click)
|
||||
"Menus/WOLMapSelectMenu.wnd", // Map select
|
||||
"Menus/WOLBuddyOverlay.wnd", // Buddy list
|
||||
"Menus/WOLPageOverlay.wnd", // Find/page
|
||||
"Menus/PopupHostGame.wnd", // Hosting options (game name, password, etc)
|
||||
"Menus/PopupJoinGame.wnd", // Joining options (password, etc)
|
||||
"Menus/PopupLadderSelect.wnd",// LadderSelect
|
||||
"Menus/PopupLocaleSelect.wnd",// Prompt for user's locale
|
||||
"Menus/OptionsMenu.wnd", // popup options
|
||||
};
|
||||
|
||||
static WindowLayout *overlayLayouts[GSOVERLAY_MAX] =
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void buddyTryReconnect( void )
|
||||
{
|
||||
BuddyRequest req;
|
||||
req.buddyRequestType = BuddyRequest::BUDDYREQUEST_RELOGIN;
|
||||
TheGameSpyBuddyMessageQueue->addRequest( req );
|
||||
}
|
||||
|
||||
void GameSpyOpenOverlay( GSOverlayType overlay )
|
||||
{
|
||||
if (overlay == GSOVERLAY_BUDDY)
|
||||
{
|
||||
if (!TheGameSpyBuddyMessageQueue->isConnected())
|
||||
{
|
||||
// not connected - is it because we were disconnected?
|
||||
if (TheGameSpyBuddyMessageQueue->getLocalProfileID())
|
||||
{
|
||||
// used to be connected
|
||||
GSMessageBoxYesNo(TheGameText->fetch("GUI:GPErrorTitle"), TheGameText->fetch("GUI:GPDisconnected"), buddyTryReconnect, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no profile
|
||||
GSMessageBoxOk(TheGameText->fetch("GUI:GPErrorTitle"), TheGameText->fetch("GUI:GPNoProfile"), NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
AudioEventRTS buttonClick("GUICommunicatorOpen");
|
||||
|
||||
if( TheAudio )
|
||||
{
|
||||
TheAudio->addAudioEvent( &buttonClick );
|
||||
} // end if
|
||||
}
|
||||
if (overlayLayouts[overlay])
|
||||
{
|
||||
overlayLayouts[overlay]->hide( FALSE );
|
||||
overlayLayouts[overlay]->bringForward();
|
||||
}
|
||||
else
|
||||
{
|
||||
overlayLayouts[overlay] = TheWindowManager->winCreateLayout( AsciiString( gsOverlays[overlay] ) );
|
||||
overlayLayouts[overlay]->runInit();
|
||||
overlayLayouts[overlay]->hide( FALSE );
|
||||
overlayLayouts[overlay]->bringForward();
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyCloseOverlay( GSOverlayType overlay )
|
||||
{
|
||||
switch(overlay)
|
||||
{
|
||||
case GSOVERLAY_PLAYERINFO:
|
||||
DEBUG_LOG(("Closing overlay GSOVERLAY_PLAYERINFO\n"));
|
||||
break;
|
||||
case GSOVERLAY_MAPSELECT:
|
||||
DEBUG_LOG(("Closing overlay GSOVERLAY_MAPSELECT\n"));
|
||||
break;
|
||||
case GSOVERLAY_BUDDY:
|
||||
DEBUG_LOG(("Closing overlay GSOVERLAY_BUDDY\n"));
|
||||
break;
|
||||
case GSOVERLAY_PAGE:
|
||||
DEBUG_LOG(("Closing overlay GSOVERLAY_PAGE\n"));
|
||||
break;
|
||||
case GSOVERLAY_GAMEOPTIONS:
|
||||
DEBUG_LOG(("Closing overlay GSOVERLAY_GAMEOPTIONS\n"));
|
||||
break;
|
||||
case GSOVERLAY_GAMEPASSWORD:
|
||||
DEBUG_LOG(("Closing overlay GSOVERLAY_GAMEPASSWORD\n"));
|
||||
break;
|
||||
case GSOVERLAY_LADDERSELECT:
|
||||
DEBUG_LOG(("Closing overlay GSOVERLAY_LADDERSELECT\n"));
|
||||
break;
|
||||
case GSOVERLAY_OPTIONS:
|
||||
DEBUG_LOG(("Closing overlay GSOVERLAY_OPTIONS\n"));
|
||||
if( overlayLayouts[overlay] )
|
||||
{
|
||||
SignalUIInteraction(SHELL_SCRIPT_HOOK_OPTIONS_CLOSED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if( overlayLayouts[overlay] )
|
||||
{
|
||||
overlayLayouts[overlay]->runShutdown();
|
||||
overlayLayouts[overlay]->destroyWindows();
|
||||
overlayLayouts[overlay]->deleteInstance();
|
||||
overlayLayouts[overlay] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Bool GameSpyIsOverlayOpen( GSOverlayType overlay )
|
||||
{
|
||||
return (overlayLayouts[overlay] != NULL);
|
||||
}
|
||||
|
||||
void GameSpyToggleOverlay( GSOverlayType overlay )
|
||||
{
|
||||
if (GameSpyIsOverlayOpen(overlay))
|
||||
GameSpyCloseOverlay(overlay);
|
||||
else
|
||||
GameSpyOpenOverlay(overlay);
|
||||
}
|
||||
|
||||
void raiseOverlays( void )
|
||||
{
|
||||
for (int i=0; i<GSOVERLAY_MAX; ++i)
|
||||
{
|
||||
if (overlayLayouts[(GSOverlayType)i])
|
||||
{
|
||||
overlayLayouts[(GSOverlayType)i]->bringForward();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyCloseAllOverlays( void )
|
||||
{
|
||||
for (int i=0; i<GSOVERLAY_MAX; ++i)
|
||||
{
|
||||
GameSpyCloseOverlay((GSOverlayType)i);
|
||||
}
|
||||
|
||||
// if we're shutting down the rest, chances are we don't want this popping up.
|
||||
deleteNotificationBox();
|
||||
}
|
||||
|
||||
void GameSpyUpdateOverlays( void )
|
||||
{
|
||||
for (int i=0; i<GSOVERLAY_MAX; ++i)
|
||||
{
|
||||
if (overlayLayouts[(GSOverlayType)i])
|
||||
{
|
||||
overlayLayouts[(GSOverlayType)i]->runUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReOpenPlayerInfo( void )
|
||||
{
|
||||
reOpenPlayerInfoFlag = TRUE;
|
||||
}
|
||||
void CheckReOpenPlayerInfo(void )
|
||||
{
|
||||
if(!reOpenPlayerInfoFlag)
|
||||
return;
|
||||
|
||||
GameSpyOpenOverlay(GSOVERLAY_PLAYERINFO);
|
||||
reOpenPlayerInfoFlag = FALSE;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: GameSpyPersistentStorage.cpp //////////////////////////////////////////////////////
|
||||
// GameSpy Persistent Storage callbacks, utils, etc
|
||||
// Author: Matthew D. Campbell, March 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameSpy/gstats/gpersist.h"
|
||||
|
||||
#include "GameClient/Shell.h"
|
||||
#include "GameClient/MessageBox.h"
|
||||
#include "GameNetwork/GameSpy.h"
|
||||
#include "GameNetwork/GameSpyGP.h"
|
||||
#include "GameNetwork/GameSpyPersistentStorage.h"
|
||||
#include "GameNetwork/GameSpyThread.h"
|
||||
|
||||
static Bool isProfileAuthorized = false;
|
||||
|
||||
static Bool gameSpyInitPersistentStorageConnection( void );
|
||||
static void getPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, char *data, int len, void *instance);
|
||||
static void setPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, void *instance);
|
||||
|
||||
|
||||
class GameSpyPlayerInfo : public GameSpyPlayerInfoInterface
|
||||
{
|
||||
public:
|
||||
GameSpyPlayerInfo() { m_locale.clear(); m_wins = m_losses = m_operationCount = 0; m_shouldDisconnect = false; }
|
||||
virtual ~GameSpyPlayerInfo() { reset(); }
|
||||
|
||||
virtual void init( void ) { m_locale.clear(); m_wins = m_losses = m_operationCount = 0; queueDisconnect(); };
|
||||
virtual void reset( void ) { m_locale.clear(); m_wins = m_losses = m_operationCount = 0; queueDisconnect(); };
|
||||
virtual void update( void );
|
||||
|
||||
virtual AsciiString getLocale( void ) { return m_locale; }
|
||||
virtual Int getWins( void ) { return m_wins; }
|
||||
virtual Int getLosses( void ) { return m_losses; }
|
||||
|
||||
virtual void setLocale( AsciiString locale, Bool setOnServer );
|
||||
virtual void setWins( Int wins, Bool setOnServer );
|
||||
virtual void setLosses( Int losses, Bool setOnServer );
|
||||
|
||||
virtual void readFromServer( void );
|
||||
virtual void threadReadFromServer( void );
|
||||
virtual void threadSetLocale( AsciiString val );
|
||||
virtual void threadSetWins ( AsciiString val );
|
||||
virtual void threadSetLosses( AsciiString val );
|
||||
|
||||
void queueDisconnect( void ) { MutexClass::LockClass m(TheGameSpyMutex); if (IsStatsConnected()) m_shouldDisconnect = true; else m_shouldDisconnect = false; }
|
||||
|
||||
private:
|
||||
void setValue( AsciiString key, AsciiString val, Bool setOnServer );
|
||||
|
||||
AsciiString m_locale;
|
||||
Int m_wins;
|
||||
Int m_losses;
|
||||
Int m_operationCount;
|
||||
Bool m_shouldDisconnect;
|
||||
};
|
||||
|
||||
void GameSpyPlayerInfo::update( void )
|
||||
{
|
||||
if (IsStatsConnected())
|
||||
{
|
||||
if (m_shouldDisconnect)
|
||||
{
|
||||
DEBUG_LOG(("Persistent Storage close\n"));
|
||||
CloseStatsConnection();
|
||||
}
|
||||
else
|
||||
{
|
||||
PersistThink();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyPlayerInfo::readFromServer( void )
|
||||
{
|
||||
TheGameSpyThread->queueReadPersistentStatsFromServer();
|
||||
}
|
||||
|
||||
void GameSpyPlayerInfo::threadReadFromServer( void )
|
||||
{
|
||||
MutexClass::LockClass m(TheGameSpyMutex);
|
||||
if (gameSpyInitPersistentStorageConnection())
|
||||
{
|
||||
// get persistent info
|
||||
m_operationCount++;
|
||||
DEBUG_LOG(("GameSpyPlayerInfo::readFromServer() operation count = %d\n", m_operationCount));
|
||||
GetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, "\\locale\\wins\\losses", getPersistentDataCallback, &m_operationCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
//TheGameSpyThread->setNextShellScreen("Menus/WOLWelcomeMenu.wnd");
|
||||
//TheShell->pop();
|
||||
//TheShell->push("Menus/WOLWelcomeMenu.wnd");
|
||||
}
|
||||
}
|
||||
|
||||
void GameSpyPlayerInfo::setLocale( AsciiString locale, Bool setOnServer )
|
||||
{
|
||||
m_locale = locale;
|
||||
|
||||
if (!TheGameSpyChat->getProfileID() || !setOnServer)
|
||||
return;
|
||||
|
||||
setValue("locale", m_locale, setOnServer);
|
||||
}
|
||||
|
||||
void GameSpyPlayerInfo::setWins( Int wins, Bool setOnServer )
|
||||
{
|
||||
m_wins = wins;
|
||||
|
||||
if (!TheGameSpyChat->getProfileID() || !setOnServer)
|
||||
return;
|
||||
|
||||
AsciiString winStr;
|
||||
winStr.format("%d", wins);
|
||||
|
||||
setValue("wins", winStr, setOnServer);
|
||||
}
|
||||
|
||||
void GameSpyPlayerInfo::setLosses( Int losses, Bool setOnServer )
|
||||
{
|
||||
m_losses = losses;
|
||||
|
||||
if (!TheGameSpyChat->getProfileID() || !setOnServer)
|
||||
return;
|
||||
|
||||
AsciiString lossesStr;
|
||||
lossesStr.format("%d", losses);
|
||||
|
||||
setValue("losses", lossesStr, setOnServer);
|
||||
}
|
||||
|
||||
void GameSpyPlayerInfo::setValue( AsciiString key, AsciiString val, Bool setOnServer )
|
||||
{
|
||||
if (!setOnServer)
|
||||
return;
|
||||
|
||||
if (key == "locale")
|
||||
TheGameSpyThread->queueUpdateLocale(val);
|
||||
else if (key == "wins")
|
||||
TheGameSpyThread->queueUpdateWins(val);
|
||||
else if (key == "losses")
|
||||
TheGameSpyThread->queueUpdateLosses(val);
|
||||
}
|
||||
|
||||
void GameSpyPlayerInfo::threadSetLocale( AsciiString val )
|
||||
{
|
||||
MutexClass::LockClass m(TheGameSpyMutex);
|
||||
if (!gameSpyInitPersistentStorageConnection())
|
||||
return;
|
||||
|
||||
// set locale info
|
||||
AsciiString key = "locale";
|
||||
AsciiString str;
|
||||
str.format("\\%s\\%s", key.str(), val.str());
|
||||
char *writable = strdup(str.str());
|
||||
m_operationCount++;
|
||||
DEBUG_LOG(("GameSpyPlayerInfo::set%s() operation count = %d\n", key.str(), m_operationCount));
|
||||
SetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, writable, setPersistentDataCallback, &m_operationCount);
|
||||
free(writable);
|
||||
}
|
||||
|
||||
void GameSpyPlayerInfo::threadSetWins( AsciiString val )
|
||||
{
|
||||
MutexClass::LockClass m(TheGameSpyMutex);
|
||||
if (!gameSpyInitPersistentStorageConnection())
|
||||
return;
|
||||
|
||||
// set win info
|
||||
AsciiString key = "wins";
|
||||
AsciiString str;
|
||||
str.format("\\%s\\%s", key.str(), val.str());
|
||||
char *writable = strdup(str.str());
|
||||
m_operationCount++;
|
||||
DEBUG_LOG(("GameSpyPlayerInfo::set%s() operation count = %d\n", key.str(), m_operationCount));
|
||||
SetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, writable, setPersistentDataCallback, &m_operationCount);
|
||||
free(writable);
|
||||
}
|
||||
|
||||
void GameSpyPlayerInfo::threadSetLosses( AsciiString val )
|
||||
{
|
||||
MutexClass::LockClass m(TheGameSpyMutex);
|
||||
if (!gameSpyInitPersistentStorageConnection())
|
||||
return;
|
||||
|
||||
// set loss info
|
||||
AsciiString key = "losses";
|
||||
AsciiString str;
|
||||
str.format("\\%s\\%s", key.str(), val.str());
|
||||
char *writable = strdup(str.str());
|
||||
m_operationCount++;
|
||||
DEBUG_LOG(("GameSpyPlayerInfo::set%s() operation count = %d\n", key.str(), m_operationCount));
|
||||
SetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, writable, setPersistentDataCallback, &m_operationCount);
|
||||
free(writable);
|
||||
}
|
||||
|
||||
GameSpyPlayerInfoInterface *TheGameSpyPlayerInfo = NULL;
|
||||
|
||||
GameSpyPlayerInfoInterface *createGameSpyPlayerInfo( void )
|
||||
{
|
||||
return NEW GameSpyPlayerInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void persAuthCallback(int localid, int profileid, int authenticated, char *errmsg, void *instance)
|
||||
{
|
||||
DEBUG_LOG(("Auth callback: localid: %d profileid: %d auth: %d err: %s\n",localid, profileid, authenticated, errmsg));
|
||||
isProfileAuthorized = (authenticated != 0);
|
||||
}
|
||||
|
||||
static void getPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, char *data, int len, void *instance)
|
||||
{
|
||||
DEBUG_LOG(("Data get callback: localid: %d profileid: %d success: %d len: %d data: %s\n",localid, profileid, success, len, data));
|
||||
|
||||
if (!TheGameSpyPlayerInfo)
|
||||
{
|
||||
//TheGameSpyThread->setNextShellScreen("Menus/WOLWelcomeMenu.wnd");
|
||||
//TheShell->pop();
|
||||
//TheShell->push("Menus/WOLWelcomeMenu.wnd");
|
||||
return;
|
||||
}
|
||||
|
||||
AsciiString str = data;
|
||||
AsciiString key, val;
|
||||
while (!str.isEmpty())
|
||||
{
|
||||
str.nextToken(&key, "\\");
|
||||
str.nextToken(&val, "\\");
|
||||
if (!key.isEmpty() && !val.isEmpty())
|
||||
{
|
||||
if (!key.compareNoCase("locale"))
|
||||
{
|
||||
TheGameSpyPlayerInfo->setLocale(val, false);
|
||||
}
|
||||
else if (!key.compareNoCase("wins"))
|
||||
{
|
||||
TheGameSpyPlayerInfo->setWins(atoi(val.str()), false);
|
||||
}
|
||||
else if (!key.compareNoCase("losses"))
|
||||
{
|
||||
TheGameSpyPlayerInfo->setLosses(atoi(val.str()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decrement count of active operations
|
||||
Int *opCount = (Int *)instance;
|
||||
(*opCount) --;
|
||||
DEBUG_LOG(("getPersistentDataCallback() operation count = %d\n", (*opCount)));
|
||||
if (!*opCount)
|
||||
{
|
||||
DEBUG_LOG(("getPersistentDataCallback() queue disconnect\n"));
|
||||
((GameSpyPlayerInfo *)TheGameSpyPlayerInfo)->queueDisconnect();
|
||||
}
|
||||
|
||||
const char *keys[3] = { "locale", "wins", "losses" };
|
||||
char valueStrings[3][20];
|
||||
char *values[3] = { valueStrings[0], valueStrings[1], valueStrings[2] };
|
||||
_snprintf(values[0], 20, "%s", TheGameSpyPlayerInfo->getLocale().str());
|
||||
_snprintf(values[1], 20, "%d", TheGameSpyPlayerInfo->getWins());
|
||||
_snprintf(values[2], 20, "%d", TheGameSpyPlayerInfo->getLosses());
|
||||
peerSetGlobalKeys(TheGameSpyChat->getPeer(), 3, (const char **)keys, (const char **)values);
|
||||
peerSetGlobalWatchKeys(TheGameSpyChat->getPeer(), GroupRoom, 3, keys, PEERTrue);
|
||||
peerSetGlobalWatchKeys(TheGameSpyChat->getPeer(), StagingRoom, 3, keys, PEERTrue);
|
||||
|
||||
// choose next screen
|
||||
if (TheGameSpyPlayerInfo->getLocale().isEmpty())
|
||||
{
|
||||
TheGameSpyThread->setShowLocaleSelect(true);
|
||||
}
|
||||
}
|
||||
|
||||
static void setPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, void *instance)
|
||||
{
|
||||
DEBUG_LOG(("Data save callback: localid: %d profileid: %d success: %d\n", localid, profileid, success));
|
||||
|
||||
Int *opCount = (Int *)instance;
|
||||
(*opCount) --;
|
||||
DEBUG_LOG(("setPersistentDataCallback() operation count = %d\n", (*opCount)));
|
||||
if (!*opCount)
|
||||
{
|
||||
DEBUG_LOG(("setPersistentDataCallback() queue disconnect\n"));
|
||||
((GameSpyPlayerInfo *)TheGameSpyPlayerInfo)->queueDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
static Bool gameSpyInitPersistentStorageConnection( void )
|
||||
{
|
||||
if (IsStatsConnected())
|
||||
return true;
|
||||
|
||||
isProfileAuthorized = false;
|
||||
Int result;
|
||||
|
||||
/*********
|
||||
First step, set our game authentication info
|
||||
We could do:
|
||||
strcpy(gcd_gamename,"gmtest");
|
||||
strcpy(gcd_secret_key,"HA6zkS");
|
||||
...but this is more secure:
|
||||
**********/
|
||||
gcd_gamename[0]='g';gcd_gamename[1]='m';gcd_gamename[2]='t';gcd_gamename[3]='e';
|
||||
gcd_gamename[4]='s';gcd_gamename[5]='t';gcd_gamename[6]='\0';
|
||||
gcd_secret_key[0]='H';gcd_secret_key[1]='A';gcd_secret_key[2]='6';gcd_secret_key[3]='z';
|
||||
gcd_secret_key[4]='k';gcd_secret_key[5]='S';gcd_secret_key[6]='\0';
|
||||
|
||||
/*********
|
||||
Next, open the stats connection. This may block for
|
||||
a 1-2 seconds, so it should be done before the actual game starts.
|
||||
**********/
|
||||
result = InitStatsConnection(0);
|
||||
|
||||
if (result != GE_NOERROR)
|
||||
{
|
||||
DEBUG_LOG(("InitStatsConnection returned %d\n",result));
|
||||
return isProfileAuthorized;
|
||||
}
|
||||
|
||||
if (TheGameSpyChat->getProfileID())
|
||||
{
|
||||
char validate[33];
|
||||
|
||||
/***********
|
||||
We'll go ahead and start the authentication, using a Presence & Messaging SDK
|
||||
profileid / password. To generate the new validation token, we'll need to pass
|
||||
in the password for the profile we are authenticating.
|
||||
Again, if this is done in a client/server setting, with the Persistent Storage
|
||||
access being done on the server, and the P&M SDK is used on the client, the
|
||||
server will need to send the challenge (GetChallenge(NULL)) to the client, the
|
||||
client will create the validation token using GenerateAuth, and send it
|
||||
back to the server for use in PreAuthenticatePlayerPM
|
||||
***********/
|
||||
char *munkeeHack = strdup(TheGameSpyChat->getPassword().str()); // GenerateAuth takes a char*, not a const char* :P
|
||||
GenerateAuth(GetChallenge(NULL), munkeeHack, validate);
|
||||
free (munkeeHack);
|
||||
|
||||
/************
|
||||
After we get the validation token, we pass it and the profileid of the user
|
||||
we are authenticating into PreAuthenticatePlayerPM.
|
||||
We pass the same authentication callback as for the first user, but a different
|
||||
localid this time.
|
||||
************/
|
||||
PreAuthenticatePlayerPM(0, TheGameSpyChat->getProfileID(), validate, persAuthCallback, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
return isProfileAuthorized;
|
||||
}
|
||||
|
||||
UnsignedInt timeoutTime = timeGetTime() + 5000;
|
||||
while (!isProfileAuthorized && timeGetTime() < timeoutTime && IsStatsConnected())
|
||||
{
|
||||
PersistThink();
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
DEBUG_LOG(("Persistent Storage connect: %d\n", isProfileAuthorized));
|
||||
return isProfileAuthorized;
|
||||
}
|
||||
|
||||
185
Generals/Code/GameEngine/Source/GameNetwork/IPEnumeration.cpp
Normal file
185
Generals/Code/GameEngine/Source/GameNetwork/IPEnumeration.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/IPEnumeration.h"
|
||||
|
||||
IPEnumeration::IPEnumeration( void )
|
||||
{
|
||||
m_IPlist = NULL;
|
||||
m_isWinsockInitialized = false;
|
||||
}
|
||||
|
||||
IPEnumeration::~IPEnumeration( void )
|
||||
{
|
||||
if (m_isWinsockInitialized)
|
||||
{
|
||||
WSACleanup();
|
||||
m_isWinsockInitialized = false;
|
||||
}
|
||||
|
||||
EnumeratedIP *ip = m_IPlist;
|
||||
while (ip)
|
||||
{
|
||||
ip = ip->getNext();
|
||||
m_IPlist->deleteInstance();
|
||||
m_IPlist = ip;
|
||||
}
|
||||
}
|
||||
|
||||
EnumeratedIP * IPEnumeration::getAddresses( void )
|
||||
{
|
||||
if (m_IPlist)
|
||||
return m_IPlist;
|
||||
|
||||
if (!m_isWinsockInitialized)
|
||||
{
|
||||
WORD verReq = MAKEWORD(2, 2);
|
||||
WSADATA wsadata;
|
||||
|
||||
int err = WSAStartup(verReq, &wsadata);
|
||||
if (err != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((LOBYTE(wsadata.wVersion) != 2) || (HIBYTE(wsadata.wVersion) !=2)) {
|
||||
WSACleanup();
|
||||
return NULL;
|
||||
}
|
||||
m_isWinsockInitialized = true;
|
||||
}
|
||||
|
||||
// get the local machine's host name
|
||||
char hostname[256];
|
||||
if (gethostname(hostname, sizeof(hostname)))
|
||||
{
|
||||
DEBUG_LOG(("Failed call to gethostname; WSAGetLastError returned %d\n", WSAGetLastError()));
|
||||
return NULL;
|
||||
}
|
||||
DEBUG_LOG(("Hostname is '%s'\n", hostname));
|
||||
|
||||
// get host information from the host name
|
||||
HOSTENT* hostEnt = gethostbyname(hostname);
|
||||
if (hostEnt == NULL)
|
||||
{
|
||||
DEBUG_LOG(("Failed call to gethostnyname; WSAGetLastError returned %d\n", WSAGetLastError()));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// sanity-check the length of the IP adress
|
||||
if (hostEnt->h_length != 4)
|
||||
{
|
||||
DEBUG_LOG(("gethostbyname returns oddly-sized IP addresses!\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// construct a list of addresses
|
||||
int numAddresses = 0;
|
||||
char *entry;
|
||||
while ( (entry = hostEnt->h_addr_list[numAddresses++]) != 0 )
|
||||
{
|
||||
EnumeratedIP *newIP = newInstance(EnumeratedIP);
|
||||
|
||||
AsciiString str;
|
||||
str.format("%d.%d.%d.%d", (unsigned char)entry[0], (unsigned char)entry[1], (unsigned char)entry[2], (unsigned char)entry[3]);
|
||||
|
||||
UnsignedInt testIP = *((UnsignedInt *)entry);
|
||||
UnsignedInt ip = ntohl(testIP);
|
||||
|
||||
/*
|
||||
ip = *entry++;
|
||||
ip <<= 8;
|
||||
ip += *entry++;
|
||||
ip <<= 8;
|
||||
ip += *entry++;
|
||||
ip <<= 8;
|
||||
ip += *entry++;
|
||||
*/
|
||||
|
||||
newIP->setIPstring(str);
|
||||
newIP->setIP(ip);
|
||||
|
||||
DEBUG_LOG(("IP: 0x%8.8X / 0x%8.8X (%s)\n", testIP, ip, str.str()));
|
||||
|
||||
// Add the IP to the list in ascending order
|
||||
if (!m_IPlist)
|
||||
{
|
||||
m_IPlist = newIP;
|
||||
newIP->setNext(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newIP->getIP() < m_IPlist->getIP())
|
||||
{
|
||||
newIP->setNext(m_IPlist);
|
||||
m_IPlist = newIP;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnumeratedIP *p = m_IPlist;
|
||||
while (p->getNext() && p->getNext()->getIP() < newIP->getIP())
|
||||
{
|
||||
p = p->getNext();
|
||||
}
|
||||
newIP->setNext(p->getNext());
|
||||
p->setNext(newIP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_IPlist;
|
||||
}
|
||||
|
||||
AsciiString IPEnumeration::getMachineName( void )
|
||||
{
|
||||
if (!m_isWinsockInitialized)
|
||||
{
|
||||
WORD verReq = MAKEWORD(2, 2);
|
||||
WSADATA wsadata;
|
||||
|
||||
int err = WSAStartup(verReq, &wsadata);
|
||||
if (err != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((LOBYTE(wsadata.wVersion) != 2) || (HIBYTE(wsadata.wVersion) !=2)) {
|
||||
WSACleanup();
|
||||
return NULL;
|
||||
}
|
||||
m_isWinsockInitialized = true;
|
||||
}
|
||||
|
||||
// get the local machine's host name
|
||||
char hostname[256];
|
||||
if (gethostname(hostname, sizeof(hostname)))
|
||||
{
|
||||
DEBUG_LOG(("Failed call to gethostname; WSAGetLastError returned %d\n", WSAGetLastError()));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return AsciiString(hostname);
|
||||
}
|
||||
|
||||
|
||||
1298
Generals/Code/GameEngine/Source/GameNetwork/LANAPI.cpp
Normal file
1298
Generals/Code/GameEngine/Source/GameNetwork/LANAPI.cpp
Normal file
File diff suppressed because it is too large
Load Diff
742
Generals/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp
Normal file
742
Generals/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp
Normal file
@@ -0,0 +1,742 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// FILE: LANAPICallbacks.cpp
|
||||
// Author: Chris Huybregts, October 2001
|
||||
// Description: LAN API Callbacks
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "strtok_r.h"
|
||||
#include "Common/GameEngine.h"
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/MessageStream.h"
|
||||
#include "Common/MultiplayerSettings.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/QuotedPrintable.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/UserPreferences.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/LanguageFilter.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
#include "GameClient/MessageBox.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameNetwork/FileTransfer.h"
|
||||
#include "GameNetwork/LANAPICallbacks.h"
|
||||
#include "GameNetwork/NetworkUtil.h"
|
||||
|
||||
LANAPI *TheLAN = NULL;
|
||||
extern Bool LANbuttonPushed;
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//Colors used for the chat dialogs
|
||||
const Color playerColor = GameMakeColor(255,255,255,255);
|
||||
const Color gameColor = GameMakeColor(255,255,255,255);
|
||||
const Color gameInProgressColor = GameMakeColor(128,128,128,255);
|
||||
const Color chatNormalColor = GameMakeColor(50,215,230,255);
|
||||
const Color chatActionColor = GameMakeColor(255,0,255,255);
|
||||
const Color chatLocalNormalColor = GameMakeColor(255,128,0,255);
|
||||
const Color chatLocalActionColor = GameMakeColor(128,255,255,255);
|
||||
const Color chatSystemColor = GameMakeColor(255,255,255,255);
|
||||
const Color acceptTrueColor = GameMakeColor(0,255,0,255);
|
||||
const Color acceptFalseColor = GameMakeColor(255,0,0,255);
|
||||
|
||||
|
||||
UnicodeString LANAPIInterface::getErrorStringFromReturnType( ReturnType ret )
|
||||
{
|
||||
switch (ret)
|
||||
{
|
||||
case RET_OK:
|
||||
return TheGameText->fetch("LAN:OK");
|
||||
case RET_TIMEOUT:
|
||||
return TheGameText->fetch("LAN:ErrorTimeout");
|
||||
case RET_GAME_FULL:
|
||||
return TheGameText->fetch("LAN:ErrorGameFull");
|
||||
case RET_DUPLICATE_NAME:
|
||||
return TheGameText->fetch("LAN:ErrorDuplicateName");
|
||||
case RET_CRC_MISMATCH:
|
||||
return TheGameText->fetch("LAN:ErrorCRCMismatch");
|
||||
case RET_GAME_STARTED:
|
||||
return TheGameText->fetch("LAN:ErrorGameStarted");
|
||||
case RET_GAME_EXISTS:
|
||||
return TheGameText->fetch("LAN:ErrorGameExists");
|
||||
case RET_GAME_GONE:
|
||||
return TheGameText->fetch("LAN:ErrorGameGone");
|
||||
case RET_BUSY:
|
||||
return TheGameText->fetch("LAN:ErrorBusy");
|
||||
case RET_SERIAL_DUPE:
|
||||
return TheGameText->fetch("WOL:ChatErrorSerialDup");
|
||||
default:
|
||||
return TheGameText->fetch("LAN:ErrorUnknown");
|
||||
}
|
||||
}
|
||||
|
||||
// On functions are (generally) the result of network traffic
|
||||
|
||||
void LANAPI::OnAccept( UnsignedInt playerIP, Bool status )
|
||||
{
|
||||
if( AmIHost() )
|
||||
{
|
||||
|
||||
for (Int i = 0; i < MAX_SLOTS; i++)
|
||||
{
|
||||
if (m_currentGame->getIP(i) == playerIP)
|
||||
{
|
||||
if(status)
|
||||
m_currentGame->getLANSlot(i)->setAccept();
|
||||
else
|
||||
m_currentGame->getLANSlot(i)->unAccept();
|
||||
break;
|
||||
}// if
|
||||
}// for
|
||||
if (i != MAX_SLOTS )
|
||||
{
|
||||
RequestGameOptions( GenerateGameOptionsString(), false );
|
||||
lanUpdateSlotList();
|
||||
}
|
||||
}//if
|
||||
else
|
||||
{
|
||||
//i'm not the host but if the accept came from the host...
|
||||
if( m_currentGame->getIP(0) == playerIP )
|
||||
{
|
||||
UnicodeString text;
|
||||
text = TheGameText->fetch("GUI:HostWantsToStart");
|
||||
OnChat(UnicodeString(L"SYSTEM"), m_localIP, text, LANCHAT_SYSTEM);
|
||||
}
|
||||
}
|
||||
}// void LANAPI::OnAccept( UnicodeString player, Bool status )
|
||||
|
||||
void LANAPI::OnHasMap( UnsignedInt playerIP, Bool status )
|
||||
{
|
||||
if( AmIHost() )
|
||||
{
|
||||
|
||||
for (Int i = 0; i < MAX_SLOTS; i++)
|
||||
{
|
||||
if (m_currentGame->getIP(i) == playerIP)
|
||||
{
|
||||
m_currentGame->getLANSlot(i)->setMapAvailability( status );
|
||||
break;
|
||||
}// if
|
||||
}// for
|
||||
if (i != MAX_SLOTS )
|
||||
{
|
||||
UnicodeString mapDisplayName;
|
||||
const MapMetaData *mapData = TheMapCache->findMap( m_currentGame->getMap() );
|
||||
Bool willTransfer = TRUE;
|
||||
if (mapData)
|
||||
{
|
||||
mapDisplayName.format(L"%ls", mapData->m_displayName.str());
|
||||
if (mapData->m_isOfficial)
|
||||
willTransfer = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mapDisplayName.format(L"%hs", m_currentGame->getMap().str());
|
||||
willTransfer = WouldMapTransfer(m_currentGame->getMap());
|
||||
}
|
||||
if (!status)
|
||||
{
|
||||
UnicodeString text;
|
||||
if (willTransfer)
|
||||
text.format(TheGameText->fetch("GUI:PlayerNoMapWillTransfer"), m_currentGame->getLANSlot(i)->getName().str(), mapDisplayName.str());
|
||||
else
|
||||
text.format(TheGameText->fetch("GUI:PlayerNoMap"), m_currentGame->getLANSlot(i)->getName().str(), mapDisplayName.str());
|
||||
OnChat(UnicodeString(L"SYSTEM"), m_localIP, text, LANCHAT_SYSTEM);
|
||||
}
|
||||
lanUpdateSlotList();
|
||||
}
|
||||
}//if
|
||||
}// void LANAPI::OnHasMap( UnicodeString player, Bool status )
|
||||
|
||||
void LANAPI::OnGameStartTimer( Int seconds )
|
||||
{
|
||||
UnicodeString text;
|
||||
if (seconds == 1)
|
||||
text.format(TheGameText->fetch("LAN:GameStartTimerSingular"), seconds);
|
||||
else
|
||||
text.format(TheGameText->fetch("LAN:GameStartTimerPlural"), seconds);
|
||||
OnChat(UnicodeString(L"SYSTEM"), m_localIP, text, LANCHAT_SYSTEM);
|
||||
}
|
||||
|
||||
void LANAPI::OnGameStart( void )
|
||||
{
|
||||
//DEBUG_LOG(("Map is '%s', preview is '%s'\n", m_currentGame->getMap().str(), GetPreviewFromMap(m_currentGame->getMap()).str()));
|
||||
//DEBUG_LOG(("Map is '%s', INI is '%s'\n", m_currentGame->getMap().str(), GetINIFromMap(m_currentGame->getMap()).str()));
|
||||
|
||||
#if !defined(_PLAYTEST)
|
||||
if (m_currentGame)
|
||||
{
|
||||
LANPreferences pref;
|
||||
AsciiString option;
|
||||
option.format("%d", m_currentGame->getLANSlot( m_currentGame->getLocalSlotNum() )->getPlayerTemplate());
|
||||
pref["PlayerTemplate"] = option;
|
||||
option.format("%d", m_currentGame->getLANSlot( m_currentGame->getLocalSlotNum() )->getColor());
|
||||
pref["Color"] = option;
|
||||
if (m_currentGame->amIHost())
|
||||
pref["Map"] = AsciiStringToQuotedPrintable(m_currentGame->getMap());
|
||||
pref.write();
|
||||
|
||||
m_isInLANMenu = FALSE;
|
||||
|
||||
//m_currentGame->startGame(0);
|
||||
|
||||
// Set up the game network
|
||||
DEBUG_ASSERTCRASH(TheNetwork == NULL, ("For some reason TheNetwork isn't NULL at the start of this game. Better look into that."));
|
||||
|
||||
if (TheNetwork != NULL) {
|
||||
delete TheNetwork;
|
||||
TheNetwork = NULL;
|
||||
}
|
||||
|
||||
// Time to initialize TheNetwork for this game.
|
||||
TheNetwork = NetworkInterface::createNetwork();
|
||||
TheNetwork->init();
|
||||
TheNetwork->setLocalAddress(m_localIP, 8088);
|
||||
TheNetwork->initTransport();
|
||||
|
||||
TheNetwork->parseUserList(m_currentGame);
|
||||
|
||||
if (TheGameLogic->isInGame())
|
||||
TheGameLogic->clearGameData();
|
||||
|
||||
Bool filesOk = DoAnyMapTransfers(m_currentGame);
|
||||
|
||||
// see if we really have the map. if not, back out.
|
||||
TheMapCache->updateCache();
|
||||
if (!filesOk || TheMapCache->findMap(m_currentGame->getMap()) == NULL)
|
||||
{
|
||||
DEBUG_LOG(("After transfer, we didn't really have the map. Bailing...\n"));
|
||||
OnPlayerLeave(m_name);
|
||||
removeGame(m_currentGame);
|
||||
m_currentGame = NULL;
|
||||
m_inLobby = TRUE;
|
||||
if (TheNetwork != NULL) {
|
||||
delete TheNetwork;
|
||||
TheNetwork = NULL;
|
||||
}
|
||||
OnChat(UnicodeString::TheEmptyString, 0, TheGameText->fetch("GUI:CouldNotTransferMap"), LANCHAT_SYSTEM);
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentGame->startGame(0);
|
||||
|
||||
// shutdown the top, but do not pop it off the stack
|
||||
//TheShell->hideShell();
|
||||
// setup the Global Data with the Map and Seed
|
||||
TheWritableGlobalData->m_pendingFile = m_currentGame->getMap();
|
||||
|
||||
// send a message to the logic for a new game
|
||||
GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_NEW_GAME );
|
||||
msg->appendIntegerArgument(GAME_LAN);
|
||||
|
||||
TheWritableGlobalData->m_useFpsLimit = false;
|
||||
|
||||
// Set the random seed
|
||||
InitGameLogicRandom( m_currentGame->getSeed() );
|
||||
DEBUG_LOG(("InitGameLogicRandom( %d )\n", m_currentGame->getSeed()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void LANAPI::OnGameOptions( UnsignedInt playerIP, Int playerSlot, AsciiString options )
|
||||
{
|
||||
if (!m_currentGame)
|
||||
return;
|
||||
|
||||
if (m_currentGame->getIP(playerSlot) != playerIP)
|
||||
return; // He's not in our game?!?
|
||||
|
||||
|
||||
if (m_currentGame->isGameInProgress())
|
||||
return; // we don't want to process any game options while in game.
|
||||
|
||||
if (playerSlot == 0 && !m_currentGame->amIHost())
|
||||
{
|
||||
m_currentGame->setLastHeard(timeGetTime());
|
||||
AsciiString oldOptions = GameInfoToAsciiString(m_currentGame); // save these off for if we get booted
|
||||
if(ParseGameOptionsString(m_currentGame,options))
|
||||
{
|
||||
lanUpdateSlotList();
|
||||
updateGameOptions();
|
||||
}
|
||||
Bool booted = true;
|
||||
for(Int player = 1; player< MAX_SLOTS; player++)
|
||||
{
|
||||
if(m_currentGame->getIP(player) == m_localIP)
|
||||
{
|
||||
booted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(booted)
|
||||
{
|
||||
// restore the options with us in so we can save prefs
|
||||
ParseGameOptionsString(m_currentGame, oldOptions);
|
||||
OnPlayerLeave(m_name);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check for user/host updates
|
||||
{
|
||||
AsciiString key;
|
||||
AsciiString munkee = options;
|
||||
munkee.nextToken(&key, "=");
|
||||
//DEBUG_LOG(("GameOpt request: key=%s, val=%s from player %d\n", key.str(), munkee.str(), playerSlot));
|
||||
|
||||
LANGameSlot *slot = m_currentGame->getLANSlot(playerSlot);
|
||||
if (!slot)
|
||||
return;
|
||||
|
||||
if (key == "User")
|
||||
{
|
||||
slot->setLogin(munkee.str()+1);
|
||||
return;
|
||||
}
|
||||
else if (key == "Host")
|
||||
{
|
||||
slot->setHost(munkee.str()+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse player requests (side, color, etc)
|
||||
if( AmIHost() && m_localIP != playerIP)
|
||||
{
|
||||
if (options.compare("HELLO") == 0)
|
||||
{
|
||||
m_currentGame->setPlayerLastHeard(playerSlot, timeGetTime());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentGame->setPlayerLastHeard(playerSlot, timeGetTime());
|
||||
Bool change = false;
|
||||
Bool shouldUnaccept = false;
|
||||
AsciiString key;
|
||||
options.nextToken(&key, "=");
|
||||
Int val = atoi(options.str()+1);
|
||||
DEBUG_LOG(("GameOpt request: key=%s, val=%s from player %d\n", key.str(), options.str(), playerSlot));
|
||||
|
||||
LANGameSlot *slot = m_currentGame->getLANSlot(playerSlot);
|
||||
if (!slot)
|
||||
return;
|
||||
|
||||
if (key == "Color")
|
||||
{
|
||||
if (val >= -1 && val < TheMultiplayerSettings->getNumColors() && val != slot->getColor() && slot->getPlayerTemplate() != PLAYERTEMPLATE_OBSERVER)
|
||||
{
|
||||
Bool colorAvailable = TRUE;
|
||||
if(val != -1 )
|
||||
{
|
||||
for(Int i=0; i <MAX_SLOTS; i++)
|
||||
{
|
||||
LANGameSlot *checkSlot = m_currentGame->getLANSlot(i);
|
||||
if(val == checkSlot->getColor() && slot != checkSlot)
|
||||
{
|
||||
colorAvailable = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(colorAvailable)
|
||||
slot->setColor(val);
|
||||
change = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("Rejecting invalid color %d\n", val));
|
||||
}
|
||||
}
|
||||
else if (key == "PlayerTemplate")
|
||||
{
|
||||
if (val >= PLAYERTEMPLATE_MIN && val < ThePlayerTemplateStore->getPlayerTemplateCount() && val != slot->getPlayerTemplate())
|
||||
{
|
||||
slot->setPlayerTemplate(val);
|
||||
if (val == PLAYERTEMPLATE_OBSERVER)
|
||||
{
|
||||
slot->setColor(-1);
|
||||
slot->setStartPos(-1);
|
||||
slot->setTeamNumber(-1);
|
||||
}
|
||||
change = true;
|
||||
shouldUnaccept = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("Rejecting invalid PlayerTemplate %d\n", val));
|
||||
}
|
||||
}
|
||||
else if (key == "StartPos" && slot->getPlayerTemplate() != PLAYERTEMPLATE_OBSERVER)
|
||||
{
|
||||
|
||||
if (val >= -1 && val < MAX_SLOTS && val != slot->getStartPos())
|
||||
{
|
||||
Bool startPosAvailable = TRUE;
|
||||
if(val != -1)
|
||||
for(Int i=0; i <MAX_SLOTS; i++)
|
||||
{
|
||||
LANGameSlot *checkSlot = m_currentGame->getLANSlot(i);
|
||||
if(val == checkSlot->getStartPos() && slot != checkSlot)
|
||||
{
|
||||
startPosAvailable = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(startPosAvailable)
|
||||
slot->setStartPos(val);
|
||||
change = true;
|
||||
shouldUnaccept = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("Rejecting invalid startPos %d\n", val));
|
||||
}
|
||||
}
|
||||
else if (key == "Team")
|
||||
{
|
||||
if (val >= -1 && val < MAX_SLOTS/2 && val != slot->getTeamNumber() && slot->getPlayerTemplate() != PLAYERTEMPLATE_OBSERVER)
|
||||
{
|
||||
slot->setTeamNumber(val);
|
||||
change = true;
|
||||
shouldUnaccept = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("Rejecting invalid team %d\n", val));
|
||||
}
|
||||
}
|
||||
else if (key == "NAT")
|
||||
{
|
||||
if ((val >= FirewallHelperClass::FIREWALL_TYPE_SIMPLE) &&
|
||||
(val <= FirewallHelperClass::FIREWALL_TYPE_DESTINATION_PORT_DELTA))
|
||||
{
|
||||
slot->setNATBehavior((FirewallHelperClass::FirewallBehaviorType)val);
|
||||
DEBUG_LOG(("NAT behavior set to %d for player %d\n", val, playerSlot));
|
||||
change = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("Rejecting invalid NAT behavior %d\n", (Int)val));
|
||||
}
|
||||
}
|
||||
|
||||
if (change)
|
||||
{
|
||||
if (shouldUnaccept)
|
||||
m_currentGame->resetAccepted();
|
||||
RequestGameOptions(GenerateGameOptionsString(), true);
|
||||
lanUpdateSlotList();
|
||||
DEBUG_LOG(("Slot value is color=%d, PlayerTemplate=%d, startPos=%d, team=%d\n",
|
||||
slot->getColor(), slot->getPlayerTemplate(), slot->getStartPos(), slot->getTeamNumber()));
|
||||
DEBUG_LOG(("Slot list updated to %s\n", GenerateGameOptionsString().str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void LANAPI::OnSlotList( ReturnType ret, LANGameInfo *theGame )
|
||||
{
|
||||
if (!theGame || theGame != m_currentGame)
|
||||
return;
|
||||
|
||||
Bool foundMe = false;
|
||||
for (int player = 0; player < MAX_SLOTS; ++player)
|
||||
{
|
||||
if (m_currentGame->getIP(player) == m_localIP)
|
||||
{
|
||||
foundMe = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundMe)
|
||||
{
|
||||
// I've been kicked - back to the lobby for me!
|
||||
// We're counting on the fact that OnPlayerLeave winds up calling reset on TheLAN.
|
||||
OnPlayerLeave(m_name);
|
||||
return;
|
||||
}
|
||||
|
||||
lanUpdateSlotList();
|
||||
}
|
||||
*/
|
||||
void LANAPI::OnPlayerJoin( Int slot, UnicodeString playerName )
|
||||
{
|
||||
if (m_currentGame && m_currentGame->getIP(0) == m_localIP)
|
||||
{
|
||||
// Someone New Joined.. lets reset the accepts
|
||||
m_currentGame->resetAccepted();
|
||||
|
||||
// Send out the game options
|
||||
RequestGameOptions(GenerateGameOptionsString(), true);
|
||||
}
|
||||
|
||||
lanUpdateSlotList();
|
||||
}
|
||||
|
||||
void LANAPI::OnGameJoin( ReturnType ret, LANGameInfo *theGame )
|
||||
{
|
||||
if (ret == RET_OK)
|
||||
{
|
||||
LANbuttonPushed = true;
|
||||
TheShell->push( AsciiString("Menus/LanGameOptionsMenu.wnd") );
|
||||
//lanUpdateSlotList();
|
||||
|
||||
LANPreferences pref;
|
||||
AsciiString options;
|
||||
options.format("PlayerTemplate=%d", pref.getPreferredFaction());
|
||||
RequestGameOptions(options, true);
|
||||
options.format("Color=%d", pref.getPreferredColor());
|
||||
RequestGameOptions(options, true);
|
||||
options.format("User=%s", m_userName.str());
|
||||
RequestGameOptions( options, true );
|
||||
options.format("Host=%s", m_hostName.str());
|
||||
RequestGameOptions( options, true );
|
||||
options.format("NAT=%d", FirewallHelperClass::FIREWALL_TYPE_SIMPLE); // BGC: This is a LAN game, so there is no firewall.
|
||||
RequestGameOptions( options, true );
|
||||
}
|
||||
else if (ret != RET_BUSY)
|
||||
{
|
||||
/// @todo: re-enable lobby controls? Error msgs?
|
||||
UnicodeString title, body;
|
||||
title = TheGameText->fetch("LAN:JoinFailed");
|
||||
body = getErrorStringFromReturnType(ret);
|
||||
MessageBoxOk(title, body, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::OnHostLeave( void )
|
||||
{
|
||||
DEBUG_ASSERTCRASH(!m_inLobby && m_currentGame, ("Game info is gone!"));
|
||||
if (m_inLobby || !m_currentGame)
|
||||
return;
|
||||
LANbuttonPushed = true;
|
||||
DEBUG_LOG(("Host left - popping to lobby\n"));
|
||||
TheShell->pop();
|
||||
}
|
||||
|
||||
void LANAPI::OnPlayerLeave( UnicodeString player )
|
||||
{
|
||||
DEBUG_ASSERTCRASH(!m_inLobby && m_currentGame, ("Game info is gone!"));
|
||||
if (m_inLobby || !m_currentGame || m_currentGame->isGameInProgress())
|
||||
return;
|
||||
|
||||
if (m_name.compare(player) == 0)
|
||||
{
|
||||
// We're leaving. Save options and Pop the shell up a screen.
|
||||
//DEBUG_ASSERTCRASH(false, ("Slot is %d\n", m_currentGame->getLocalSlotNum()));
|
||||
if (m_currentGame && m_currentGame->isInGame() && m_currentGame->getLocalSlotNum() >= 0)
|
||||
{
|
||||
LANPreferences pref;
|
||||
AsciiString option;
|
||||
option.format("%d", m_currentGame->getLANSlot( m_currentGame->getLocalSlotNum() )->getPlayerTemplate());
|
||||
pref["PlayerTemplate"] = option;
|
||||
option.format("%d", m_currentGame->getLANSlot( m_currentGame->getLocalSlotNum() )->getColor());
|
||||
pref["Color"] = option;
|
||||
if (m_currentGame->amIHost())
|
||||
pref["Map"] = AsciiStringToQuotedPrintable(m_currentGame->getMap());
|
||||
pref.write();
|
||||
}
|
||||
LANbuttonPushed = true;
|
||||
DEBUG_LOG(("OnPlayerLeave says we're leaving! pop away!\n"));
|
||||
TheShell->pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_currentGame && m_currentGame->getIP(0) == m_localIP)
|
||||
{
|
||||
// Force a new slotlist send
|
||||
m_lastResendTime = 0;
|
||||
|
||||
lanUpdateSlotList();
|
||||
RequestGameOptions( GenerateGameOptionsString(), true );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::OnGameList( LANGameInfo *gameList )
|
||||
{
|
||||
|
||||
if (m_inLobby)
|
||||
{
|
||||
LANDisplayGameList(listboxGames, gameList);
|
||||
}
|
||||
}//void LANAPI::OnGameList( LANGameInfo *gameList )
|
||||
|
||||
void LANAPI::OnGameCreate( ReturnType ret )
|
||||
{
|
||||
if (ret == RET_OK)
|
||||
{
|
||||
|
||||
LANbuttonPushed = true;
|
||||
TheShell->push( AsciiString("Menus/LanGameOptionsMenu.wnd") );
|
||||
|
||||
RequestLobbyLeave( false );
|
||||
//RequestGameAnnounce( ); // can't do this here, since we don't have a map set
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_inLobby)
|
||||
{
|
||||
switch( ret )
|
||||
{
|
||||
case RET_GAME_EXISTS:
|
||||
GadgetListBoxAddEntryText(listboxChatWindow, TheGameText->fetch("LAN:ErrorGameExists"), chatSystemColor, -1, -1);
|
||||
break;
|
||||
case RET_BUSY:
|
||||
GadgetListBoxAddEntryText(listboxChatWindow, TheGameText->fetch("LAN:ErrorBusy"), chatSystemColor, -1, -1);
|
||||
break;
|
||||
default:
|
||||
GadgetListBoxAddEntryText(listboxChatWindow, TheGameText->fetch("LAN:ErrorUnknown"), chatSystemColor, -1, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}//void OnGameCreate( ReturnType ret )
|
||||
|
||||
void LANAPI::OnPlayerList( LANPlayer *playerList )
|
||||
{
|
||||
if (m_inLobby)
|
||||
{
|
||||
|
||||
UnsignedInt selectedIP = 0;
|
||||
Int selectedIndex = -1;
|
||||
Int indexToSelect = -1;
|
||||
GadgetListBoxGetSelected(listboxPlayers, &selectedIndex);
|
||||
|
||||
if (selectedIndex != -1 )
|
||||
selectedIP = (UnsignedInt) GadgetListBoxGetItemData(listboxPlayers, selectedIndex, 0);
|
||||
|
||||
GadgetListBoxReset(listboxPlayers);
|
||||
|
||||
LANPlayer *player = m_lobbyPlayers;
|
||||
while (player)
|
||||
{
|
||||
Int addedIndex = GadgetListBoxAddEntryText(listboxPlayers, player->getName(), playerColor, -1, -1);
|
||||
GadgetListBoxSetItemData(listboxPlayers, (void *)player->getIP(),addedIndex, 0 );
|
||||
|
||||
if (selectedIP == player->getIP())
|
||||
indexToSelect = addedIndex;
|
||||
|
||||
player = player->getNext();
|
||||
}
|
||||
|
||||
if (indexToSelect >= 0)
|
||||
GadgetListBoxSetSelected(listboxPlayers, indexToSelect);
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::OnNameChange( UnsignedInt IP, UnicodeString newName )
|
||||
{
|
||||
OnPlayerList(m_lobbyPlayers);
|
||||
}
|
||||
|
||||
void LANAPI::OnInActive(UnsignedInt IP) {
|
||||
|
||||
}
|
||||
|
||||
void LANAPI::OnChat( UnicodeString player, UnsignedInt ip, UnicodeString message, ChatType format )
|
||||
{
|
||||
GameWindow *chatWindow = NULL;
|
||||
|
||||
if (m_inLobby)
|
||||
{
|
||||
chatWindow = listboxChatWindow;
|
||||
}
|
||||
else if( m_currentGame && m_currentGame->isGameInProgress() && TheShell->isShellActive())
|
||||
{
|
||||
chatWindow = listboxChatWindowScoreScreen;
|
||||
}
|
||||
else if( m_currentGame && !m_currentGame->isGameInProgress())
|
||||
{
|
||||
chatWindow = listboxChatWindowLanGame;
|
||||
}
|
||||
if (chatWindow == NULL)
|
||||
return;
|
||||
Int index = -1;
|
||||
UnicodeString unicodeChat;
|
||||
switch (format)
|
||||
{
|
||||
case LANAPIInterface::LANCHAT_SYSTEM:
|
||||
unicodeChat = L"";
|
||||
unicodeChat.concat(message);
|
||||
unicodeChat.concat(L"");
|
||||
index =GadgetListBoxAddEntryText(chatWindow, unicodeChat, chatSystemColor, -1, -1);
|
||||
break;
|
||||
case LANAPIInterface::LANCHAT_EMOTE:
|
||||
unicodeChat = player;
|
||||
unicodeChat.concat(L' ');
|
||||
unicodeChat.concat(message);
|
||||
if (ip == m_localIP)
|
||||
index =GadgetListBoxAddEntryText(chatWindow, unicodeChat, chatLocalActionColor, -1, -1);
|
||||
else
|
||||
index =GadgetListBoxAddEntryText(chatWindow, unicodeChat, chatActionColor, -1, -1);
|
||||
break;
|
||||
case LANAPIInterface::LANCHAT_NORMAL:
|
||||
default:
|
||||
{
|
||||
// Do the language filtering.
|
||||
TheLanguageFilter->filterLine(message);
|
||||
|
||||
Color chatColor = GameMakeColor(255, 255, 255, 255);
|
||||
if (m_currentGame)
|
||||
{
|
||||
Int slotNum = m_currentGame->getSlotNum(player);
|
||||
// it'll be -1 if its invalid.
|
||||
if (slotNum >= 0) {
|
||||
GameSlot *gs = m_currentGame->getSlot(slotNum);
|
||||
if (gs) {
|
||||
Int colorIndex = gs->getColor();
|
||||
MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(colorIndex);
|
||||
if (def)
|
||||
chatColor = def->getColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unicodeChat = L"[";
|
||||
unicodeChat.concat(player);
|
||||
unicodeChat.concat(L"] ");
|
||||
unicodeChat.concat(message);
|
||||
if (ip == m_localIP)
|
||||
index =GadgetListBoxAddEntryText(chatWindow, unicodeChat, chatColor, -1, -1);
|
||||
else
|
||||
index =GadgetListBoxAddEntryText(chatWindow, unicodeChat, chatColor, -1, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
GadgetListBoxSetItemData(chatWindow, (void *)-1, index);
|
||||
}
|
||||
680
Generals/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp
Normal file
680
Generals/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp
Normal file
@@ -0,0 +1,680 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// FILE: LANAPIHandlers.cpp
|
||||
// Author: Matthew D. Campbell, October 2001
|
||||
// Description: LAN callback handlers
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/CRC.h"
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/Registry.h"
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/QuotedPrintable.h"
|
||||
#include "Common/UserPreferences.h"
|
||||
#include "GameNetwork/LANAPI.h"
|
||||
#include "GameNetwork/LANAPICallbacks.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
|
||||
void LANAPI::handleRequestLocations( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (m_inLobby)
|
||||
{
|
||||
LANMessage reply;
|
||||
fillInLANMessage( &reply );
|
||||
reply.LANMessageType = LANMessage::MSG_LOBBY_ANNOUNCE;
|
||||
|
||||
sendMessage(&reply);
|
||||
m_lastResendTime = timeGetTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
// In game - are we a game host?
|
||||
if (m_currentGame)
|
||||
{
|
||||
if (m_currentGame->getIP(0) == m_localIP)
|
||||
{
|
||||
LANMessage reply;
|
||||
fillInLANMessage( &reply );
|
||||
reply.LANMessageType = LANMessage::MSG_GAME_ANNOUNCE;
|
||||
AsciiString gameOpts = GenerateGameOptionsString();
|
||||
strncpy(reply.GameInfo.options,gameOpts.str(),m_lanMaxOptionsLength);
|
||||
wcsncpy(reply.GameInfo.gameName, m_currentGame->getName().str(), g_lanGameNameLength);
|
||||
reply.GameInfo.gameName[g_lanGameNameLength] = 0;
|
||||
reply.GameInfo.inProgress = m_currentGame->isGameInProgress();
|
||||
|
||||
sendMessage(&reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're a joiner
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add the player to the lobby player list
|
||||
LANPlayer *player = LookupPlayer(senderIP);
|
||||
if (!player)
|
||||
{
|
||||
player = NEW LANPlayer;
|
||||
player->setIP(senderIP);
|
||||
}
|
||||
else
|
||||
{
|
||||
removePlayer(player);
|
||||
}
|
||||
player->setName(UnicodeString(msg->name));
|
||||
player->setHost(msg->hostName);
|
||||
player->setLogin(msg->userName);
|
||||
player->setLastHeard(timeGetTime());
|
||||
|
||||
addPlayer(player);
|
||||
|
||||
OnNameChange(player->getIP(), player->getName());
|
||||
}
|
||||
|
||||
void LANAPI::handleGameAnnounce( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (senderIP == m_localIP)
|
||||
{
|
||||
return; // Don't try to update own info
|
||||
}
|
||||
else if (m_currentGame && m_currentGame->isGameInProgress())
|
||||
{
|
||||
return; // Don't care about games if we're playing
|
||||
}
|
||||
else if (senderIP == m_directConnectRemoteIP)
|
||||
{
|
||||
|
||||
if (m_currentGame == NULL)
|
||||
{
|
||||
LANGameInfo *game = LookupGame(UnicodeString(msg->GameInfo.gameName));
|
||||
if (!game)
|
||||
{
|
||||
game = NEW LANGameInfo;
|
||||
game->setName(UnicodeString(msg->GameInfo.gameName));
|
||||
addGame(game);
|
||||
}
|
||||
Bool success = ParseGameOptionsString(game,AsciiString(msg->GameInfo.options));
|
||||
game->setGameInProgress(msg->GameInfo.inProgress);
|
||||
game->setIsDirectConnect(msg->GameInfo.isDirectConnect);
|
||||
game->setLastHeard(timeGetTime());
|
||||
if (!success)
|
||||
{
|
||||
// remove from list
|
||||
removeGame(game);
|
||||
delete game;
|
||||
game = NULL;
|
||||
return;
|
||||
}
|
||||
RequestGameJoin(game, m_directConnectRemoteIP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LANGameInfo *game = LookupGame(UnicodeString(msg->GameInfo.gameName));
|
||||
if (!game)
|
||||
{
|
||||
game = NEW LANGameInfo;
|
||||
game->setName(UnicodeString(msg->GameInfo.gameName));
|
||||
addGame(game);
|
||||
}
|
||||
Bool success = ParseGameOptionsString(game,AsciiString(msg->GameInfo.options));
|
||||
game->setGameInProgress(msg->GameInfo.inProgress);
|
||||
game->setIsDirectConnect(msg->GameInfo.isDirectConnect);
|
||||
game->setLastHeard(timeGetTime());
|
||||
if (!success)
|
||||
{
|
||||
// remove from list
|
||||
removeGame(game);
|
||||
delete game;
|
||||
game = NULL;
|
||||
}
|
||||
|
||||
OnGameList( m_games );
|
||||
// if (game == m_currentGame && !m_inLobby)
|
||||
// OnSlotList(RET_OK, game);
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleLobbyAnnounce( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
LANPlayer *player = LookupPlayer(senderIP);
|
||||
if (!player)
|
||||
{
|
||||
player = NEW LANPlayer;
|
||||
player->setIP(senderIP);
|
||||
}
|
||||
else
|
||||
{
|
||||
removePlayer(player);
|
||||
}
|
||||
player->setName(UnicodeString(msg->name));
|
||||
player->setHost(msg->hostName);
|
||||
player->setLogin(msg->userName);
|
||||
player->setLastHeard(timeGetTime());
|
||||
|
||||
addPlayer(player);
|
||||
|
||||
OnNameChange(player->getIP(), player->getName());
|
||||
}
|
||||
|
||||
void LANAPI::handleRequestGameInfo( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
// In game - are we a game host?
|
||||
if (m_currentGame)
|
||||
{
|
||||
if (m_currentGame->getIP(0) == m_localIP || (m_currentGame->isGameInProgress() && TheNetwork && TheNetwork->isPacketRouter())) // if we're in game we should reply if we're the packet router
|
||||
{
|
||||
LANMessage reply;
|
||||
fillInLANMessage( &reply );
|
||||
reply.LANMessageType = LANMessage::MSG_GAME_ANNOUNCE;
|
||||
|
||||
AsciiString gameOpts = GameInfoToAsciiString(m_currentGame);
|
||||
strncpy(reply.GameInfo.options,gameOpts.str(),m_lanMaxOptionsLength);
|
||||
wcsncpy(reply.GameInfo.gameName, m_currentGame->getName().str(), g_lanGameNameLength);
|
||||
reply.GameInfo.gameName[g_lanGameNameLength] = 0;
|
||||
reply.GameInfo.inProgress = m_currentGame->isGameInProgress();
|
||||
reply.GameInfo.isDirectConnect = m_currentGame->getIsDirectConnect();
|
||||
|
||||
sendMessage(&reply, senderIP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleRequestJoin( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
UnsignedInt responseIP = senderIP; // need this cause the player may or may not be
|
||||
// in the player list at the sendMessage.
|
||||
|
||||
if (msg->GameToJoin.gameIP != m_localIP)
|
||||
{
|
||||
return; // Not us. Ignore it.
|
||||
}
|
||||
LANMessage reply;
|
||||
fillInLANMessage( &reply );
|
||||
if (!m_inLobby && m_currentGame && m_currentGame->getIP(0) == m_localIP)
|
||||
{
|
||||
if (m_currentGame->isGameInProgress())
|
||||
{
|
||||
reply.LANMessageType = LANMessage::MSG_JOIN_DENY;
|
||||
reply.GameNotJoined.reason = LANAPIInterface::RET_GAME_STARTED;
|
||||
reply.GameNotJoined.gameIP = m_localIP;
|
||||
reply.GameNotJoined.playerIP = senderIP;
|
||||
DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because game already started.\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
int player;
|
||||
Bool canJoin = true;
|
||||
|
||||
// see if the CRCs match
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if (TheGlobalData->m_netMinPlayers > 0) {
|
||||
#endif
|
||||
if (msg->GameToJoin.iniCRC != TheGlobalData->m_iniCRC ||
|
||||
msg->GameToJoin.exeCRC != TheGlobalData->m_exeCRC)
|
||||
{
|
||||
DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because of CRC mismatch. CRCs are them/us INI:%X/%X exe:%X/%X\n",
|
||||
msg->GameToJoin.iniCRC, TheGlobalData->m_iniCRC,
|
||||
msg->GameToJoin.exeCRC, TheGlobalData->m_exeCRC));
|
||||
reply.LANMessageType = LANMessage::MSG_JOIN_DENY;
|
||||
reply.GameNotJoined.reason = LANAPIInterface::RET_CRC_MISMATCH;
|
||||
reply.GameNotJoined.gameIP = m_localIP;
|
||||
reply.GameNotJoined.playerIP = senderIP;
|
||||
canJoin = false;
|
||||
}
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
}
|
||||
#endif
|
||||
|
||||
// check for a duplicate serial
|
||||
AsciiString s;
|
||||
for (player = 0; canJoin && player<MAX_SLOTS; ++player)
|
||||
{
|
||||
LANGameSlot *slot = m_currentGame->getLANSlot(player);
|
||||
s.clear();
|
||||
if (player == 0)
|
||||
{
|
||||
GetStringFromRegistry("\\ergc", "", s);
|
||||
}
|
||||
else if (slot->isHuman())
|
||||
{
|
||||
s = slot->getSerial();
|
||||
if (s.isEmpty())
|
||||
s = "<Munkee>";
|
||||
}
|
||||
|
||||
if (s.isNotEmpty())
|
||||
{
|
||||
DEBUG_LOG(("Checking serial '%s' in slot %d\n", s.str(), player));
|
||||
|
||||
if (!strncmp(s.str(), msg->GameToJoin.serial, g_maxSerialLength))
|
||||
{
|
||||
// serials match! kick the punk!
|
||||
reply.LANMessageType = LANMessage::MSG_JOIN_DENY;
|
||||
reply.GameNotJoined.reason = LANAPIInterface::RET_SERIAL_DUPE;
|
||||
reply.GameNotJoined.gameIP = m_localIP;
|
||||
reply.GameNotJoined.playerIP = senderIP;
|
||||
canJoin = false;
|
||||
|
||||
DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because of duplicate serial # (%s).\n", s.str()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We're the host, so see if he has a duplicate name
|
||||
for (player = 0; canJoin && player<MAX_SLOTS; ++player)
|
||||
{
|
||||
LANGameSlot *slot = m_currentGame->getLANSlot(player);
|
||||
if (slot->isHuman() && slot->getName().compare(msg->name) == 0)
|
||||
{
|
||||
// just deny duplicates
|
||||
reply.LANMessageType = LANMessage::MSG_JOIN_DENY;
|
||||
reply.GameNotJoined.reason = LANAPIInterface::RET_DUPLICATE_NAME;
|
||||
reply.GameNotJoined.gameIP = m_localIP;
|
||||
reply.GameNotJoined.playerIP = senderIP;
|
||||
canJoin = false;
|
||||
|
||||
DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because of duplicate names.\n"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// See if there's room
|
||||
// First get the number of players currently in the room.
|
||||
Int numPlayers = 0;
|
||||
for (player = 0; player < MAX_SLOTS; ++player)
|
||||
{
|
||||
if (m_currentGame->getLANSlot(player)->isOccupied()
|
||||
&& !(m_currentGame->getLANSlot(player)->getPlayerTemplate() == PLAYERTEMPLATE_OBSERVER))
|
||||
{
|
||||
++numPlayers;
|
||||
}
|
||||
}
|
||||
|
||||
// now get the number of starting spots on the map.
|
||||
Int numStartingSpots = MAX_SLOTS;
|
||||
const MapMetaData *md = TheMapCache->findMap(m_currentGame->getMap());
|
||||
if (md != NULL)
|
||||
{
|
||||
numStartingSpots = md->m_numPlayers;
|
||||
}
|
||||
|
||||
if (numPlayers < numStartingSpots) {
|
||||
for (player = 0; canJoin && player<MAX_SLOTS; ++player)
|
||||
{
|
||||
if (m_currentGame->getLANSlot(player)->isOpen())
|
||||
{
|
||||
// OK, add him in.
|
||||
reply.LANMessageType = LANMessage::MSG_JOIN_ACCEPT;
|
||||
wcsncpy(reply.GameJoined.gameName, m_currentGame->getName().str(), g_lanGameNameLength);
|
||||
reply.GameJoined.gameName[g_lanGameNameLength] = 0;
|
||||
reply.GameJoined.slotPosition = player;
|
||||
reply.GameJoined.gameIP = m_localIP;
|
||||
reply.GameJoined.playerIP = senderIP;
|
||||
|
||||
LANGameSlot newSlot;
|
||||
newSlot.setState(SLOT_PLAYER, UnicodeString(msg->name));
|
||||
newSlot.setIP(senderIP);
|
||||
newSlot.setPort(NETWORK_BASE_PORT_NUMBER);
|
||||
newSlot.setLastHeard(timeGetTime());
|
||||
newSlot.setSerial(msg->GameToJoin.serial);
|
||||
m_currentGame->setSlot(player,newSlot);
|
||||
DEBUG_LOG(("LANAPI::handleRequestJoin - added player %ls at ip 0x%08x to the game\n", msg->name, senderIP));
|
||||
|
||||
OnPlayerJoin(player, UnicodeString(msg->name));
|
||||
responseIP = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (canJoin && player == MAX_SLOTS)
|
||||
{
|
||||
reply.LANMessageType = LANMessage::MSG_JOIN_DENY;
|
||||
wcsncpy(reply.GameNotJoined.gameName, m_currentGame->getName().str(), g_lanGameNameLength);
|
||||
reply.GameNotJoined.gameName[g_lanGameNameLength] = 0;
|
||||
reply.GameNotJoined.reason = LANAPIInterface::RET_GAME_FULL;
|
||||
reply.GameNotJoined.gameIP = m_localIP;
|
||||
reply.GameNotJoined.playerIP = senderIP;
|
||||
DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because game is full.\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.LANMessageType = LANMessage::MSG_JOIN_DENY;
|
||||
reply.GameNotJoined.reason = LANAPIInterface::RET_GAME_GONE;
|
||||
reply.GameNotJoined.gameIP = m_localIP;
|
||||
reply.GameNotJoined.playerIP = senderIP;
|
||||
}
|
||||
sendMessage(&reply, responseIP);
|
||||
RequestGameOptions(GenerateGameOptionsString(), true);
|
||||
}
|
||||
|
||||
void LANAPI::handleJoinAccept( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (msg->GameJoined.playerIP == m_localIP) // Is it for us?
|
||||
{
|
||||
if (m_pendingAction == ACT_JOIN) // Are we trying to join?
|
||||
{
|
||||
m_currentGame = LookupGame(UnicodeString(msg->GameJoined.gameName));
|
||||
|
||||
if (!m_currentGame)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(false, ("Could not find game to join!"));
|
||||
OnGameJoin(RET_UNKNOWN, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_inLobby = false;
|
||||
AsciiString options = GameInfoToAsciiString(m_currentGame);
|
||||
m_currentGame->enterGame();
|
||||
ParseAsciiStringToGameInfo(m_currentGame, options);
|
||||
|
||||
Int pos = msg->GameJoined.slotPosition;
|
||||
|
||||
LANGameSlot slot;
|
||||
slot.setState(SLOT_PLAYER, m_name);
|
||||
slot.setIP(m_localIP);
|
||||
slot.setPort(NETWORK_BASE_PORT_NUMBER);
|
||||
slot.setLastHeard(0);
|
||||
slot.setLogin(m_userName);
|
||||
slot.setHost(m_hostName);
|
||||
m_currentGame->setSlot(pos, slot);
|
||||
|
||||
m_currentGame->getLANSlot(0)->setHost(msg->hostName);
|
||||
m_currentGame->getLANSlot(0)->setLogin(msg->userName);
|
||||
|
||||
LANPreferences prefs;
|
||||
AsciiString entry;
|
||||
entry.format("%d.%d.%d.%d:%s", senderIP >> 24, (senderIP & 0xff0000) >> 16, (senderIP & 0xff00) >> 8, senderIP & 0xff, UnicodeStringToQuotedPrintable(m_currentGame->getSlot(0)->getName()).str());
|
||||
prefs["RemoteIP0"] = entry;
|
||||
prefs.write();
|
||||
|
||||
OnGameJoin(RET_OK, m_currentGame);
|
||||
//DEBUG_ASSERTCRASH(false, ("setting host to %ls@%ls\n", m_currentGame->getLANSlot(0)->getUser()->getLogin().str(),
|
||||
// m_currentGame->getLANSlot(0)->getUser()->getHost().str()));
|
||||
}
|
||||
m_pendingAction = ACT_NONE;
|
||||
m_expiration = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleJoinDeny( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (msg->GameJoined.playerIP == m_localIP) // Is it for us?
|
||||
{
|
||||
if (m_pendingAction == ACT_JOIN) // Are we trying to join?
|
||||
{
|
||||
OnGameJoin(msg->GameNotJoined.reason, LookupGame(UnicodeString(msg->GameNotJoined.gameName)));
|
||||
m_pendingAction = ACT_NONE;
|
||||
m_expiration = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleRequestGameLeave( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (!m_inLobby && m_currentGame && !m_currentGame->isGameInProgress())
|
||||
{
|
||||
int player;
|
||||
for (player = 0; player < MAX_SLOTS; ++player)
|
||||
{
|
||||
if (m_currentGame->getIP(player) == senderIP)
|
||||
{
|
||||
if (player == 0)
|
||||
{
|
||||
OnHostLeave();
|
||||
removeGame(m_currentGame);
|
||||
delete m_currentGame;
|
||||
m_currentGame = NULL;
|
||||
|
||||
/// @todo re-add myself to lobby? Or just keep me there all the time? If we send a LOBBY_ANNOUNCE things'll work out...
|
||||
LANPlayer *lanPlayer = LookupPlayer(m_localIP);
|
||||
if (!lanPlayer)
|
||||
{
|
||||
lanPlayer = NEW LANPlayer;
|
||||
lanPlayer->setIP(m_localIP);
|
||||
}
|
||||
else
|
||||
{
|
||||
removePlayer(lanPlayer);
|
||||
}
|
||||
lanPlayer->setName(UnicodeString(m_name));
|
||||
lanPlayer->setHost(m_hostName);
|
||||
lanPlayer->setLogin(m_userName);
|
||||
lanPlayer->setLastHeard(timeGetTime());
|
||||
addPlayer(lanPlayer);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AmIHost())
|
||||
{
|
||||
// remove the deadbeat
|
||||
LANGameSlot slot;
|
||||
slot.setState(SLOT_OPEN);
|
||||
m_currentGame->setSlot( player, slot );
|
||||
}
|
||||
OnPlayerLeave(UnicodeString(msg->name));
|
||||
m_currentGame->getLANSlot(player)->setState(SLOT_OPEN);
|
||||
m_currentGame->resetAccepted();
|
||||
RequestGameOptions(GenerateGameOptionsString(), false, senderIP);
|
||||
//m_currentGame->endGame();
|
||||
}
|
||||
break;
|
||||
}
|
||||
DEBUG_ASSERTCRASH(player < MAX_SLOTS, ("Didn't find player!"));
|
||||
}
|
||||
}
|
||||
else if (m_inLobby)
|
||||
{
|
||||
// Look for dissappearing games
|
||||
LANGameInfo *game = m_games;
|
||||
while (game)
|
||||
{
|
||||
if (game->getName().compare(msg->GameToLeave.gameName) == 0)
|
||||
{
|
||||
removeGame(game);
|
||||
delete game;
|
||||
OnGameList(m_games);
|
||||
break;
|
||||
}
|
||||
game = game->getNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleRequestLobbyLeave( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (m_inLobby)
|
||||
{
|
||||
LANPlayer *player = m_lobbyPlayers;
|
||||
while (player)
|
||||
{
|
||||
if (player->getIP() == senderIP)
|
||||
{
|
||||
removePlayer(player);
|
||||
OnPlayerList(m_lobbyPlayers);
|
||||
break;
|
||||
}
|
||||
player = player->getNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleSetAccept( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (!m_inLobby && m_currentGame && !m_currentGame->isGameInProgress())
|
||||
{
|
||||
int player;
|
||||
for (player = 0; player < MAX_SLOTS; ++player)
|
||||
{
|
||||
if (m_currentGame->getIP(player) == senderIP)
|
||||
{
|
||||
OnAccept(senderIP, msg->Accept.isAccepted);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleHasMap( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (!m_inLobby && m_currentGame)
|
||||
{
|
||||
CRC mapNameCRC;
|
||||
// mapNameCRC.computeCRC(m_currentGame->getMap().str(), m_currentGame->getMap().getLength());
|
||||
AsciiString portableMapName = TheGameState->realMapPathToPortableMapPath(m_currentGame->getMap());
|
||||
mapNameCRC.computeCRC(portableMapName.str(), portableMapName.getLength());
|
||||
if (mapNameCRC.get() != msg->MapStatus.mapCRC)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int player;
|
||||
for (player = 0; player < MAX_SLOTS; ++player)
|
||||
{
|
||||
if (m_currentGame->getIP(player) == senderIP)
|
||||
{
|
||||
OnHasMap(senderIP, msg->MapStatus.hasMap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleChat( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (m_inLobby)
|
||||
{
|
||||
LANPlayer *player;
|
||||
if((player=LookupPlayer(senderIP)) != 0)
|
||||
{
|
||||
OnChat(UnicodeString(player->getName()), player->getIP(), UnicodeString(msg->Chat.message), msg->Chat.chatType);
|
||||
player->setLastHeard(timeGetTime());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LookupGame(UnicodeString(msg->Chat.gameName)) != m_currentGame)
|
||||
{
|
||||
DEBUG_LOG(("Game '%ls' is not my game\n", msg->Chat.gameName));
|
||||
if (m_currentGame)
|
||||
{
|
||||
DEBUG_LOG(("Current game is '%ls'\n", m_currentGame->getName().str()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int player;
|
||||
for (player = 0; player < MAX_SLOTS; ++player)
|
||||
{
|
||||
if (m_currentGame && m_currentGame->getIP(player) == senderIP)
|
||||
{
|
||||
OnChat(UnicodeString(msg->name), m_currentGame->getIP(player), UnicodeString(msg->Chat.message), msg->Chat.chatType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleGameStart( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (!m_inLobby && m_currentGame && m_currentGame->getIP(0) == senderIP && !m_currentGame->isGameInProgress())
|
||||
{
|
||||
OnGameStart();
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleGameStartTimer( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (!m_inLobby && m_currentGame && m_currentGame->getIP(0) == senderIP && !m_currentGame->isGameInProgress())
|
||||
{
|
||||
OnGameStartTimer(msg->StartTimer.seconds);
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleGameOptions( LANMessage *msg, UnsignedInt senderIP )
|
||||
{
|
||||
if (!m_inLobby && m_currentGame && !m_currentGame->isGameInProgress())
|
||||
{
|
||||
int player;
|
||||
for (player = 0; player < MAX_SLOTS; ++player)
|
||||
{
|
||||
if (m_currentGame->getIP(player) == senderIP)
|
||||
{
|
||||
OnGameOptions(senderIP, player, AsciiString(msg->GameOptions.options));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LANAPI::handleInActive(LANMessage *msg, UnsignedInt senderIP) {
|
||||
if (m_inLobby || (m_currentGame == NULL) || (m_currentGame->isGameInProgress())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check to see if we are the host of this game.
|
||||
if (m_currentGame->amIHost() == FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
UnicodeString playerName;
|
||||
playerName = msg->name;
|
||||
|
||||
Int slotNum = m_currentGame->getSlotNum(playerName);
|
||||
if (slotNum < 0)
|
||||
return;
|
||||
GameSlot *slot = m_currentGame->getSlot(slotNum);
|
||||
if (slot == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (senderIP != slot->getIP()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// don't want to unaccept the host, that's silly. They can't hit start alt-tabbed anyways.
|
||||
if (senderIP == TheLAN->GetLocalIP()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only unaccept if the timer hasn't started yet.
|
||||
if (m_gameStartTime != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
slot->unAccept();
|
||||
AsciiString options = GenerateGameOptionsString();
|
||||
RequestGameOptions(options, FALSE);
|
||||
lanUpdateSlotList();
|
||||
}
|
||||
320
Generals/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp
Normal file
320
Generals/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: LANGameInfo.cpp //////////////////////////////////////////////////////
|
||||
// LAN game setup state info
|
||||
// Author: Matthew D. Campbell, December 2001
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameClient/GameInfoWindow.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/GadgetListBox.h"
|
||||
#include "GameNetwork/LANGameInfo.h"
|
||||
#include "GameNetwork/LANAPICallbacks.h"
|
||||
#include "Common/MultiplayerSettings.h"
|
||||
#include "strtok_r.h"
|
||||
/*
|
||||
#include "GameNetwork/LAN.h"
|
||||
#include "GameNetwork/LANGame.h"
|
||||
#include "GameNetwork/LANPing.h"
|
||||
#include "GameNetwork/LANusers.h"
|
||||
#include "GameNetwork/LANmenus.h"
|
||||
*/
|
||||
|
||||
// Singleton ------------------------------------------
|
||||
|
||||
LANGameInfo *TheLANGameInfo = NULL;
|
||||
|
||||
// LANGameSlot ----------------------------------------
|
||||
|
||||
LANGameSlot::LANGameSlot()
|
||||
{
|
||||
m_lastHeard = 0;
|
||||
}
|
||||
|
||||
|
||||
LANPlayer * LANGameSlot::getUser( void )
|
||||
{
|
||||
if (isHuman())
|
||||
{
|
||||
m_user.setIP(getIP());
|
||||
m_user.setLastHeard(getLastHeard());
|
||||
m_user.setName(getName());
|
||||
m_user.setNext(NULL);
|
||||
return &m_user;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Various tests
|
||||
Bool LANGameSlot::isUser( LANPlayer *user )
|
||||
{
|
||||
return (user && m_state == SLOT_PLAYER && user->getIP() == getIP());
|
||||
}
|
||||
|
||||
Bool LANGameSlot::isUser( UnicodeString userName )
|
||||
{
|
||||
return (m_state == SLOT_PLAYER && !userName.compareNoCase(getName()));
|
||||
}
|
||||
|
||||
Bool LANGameSlot::isLocalPlayer( void ) const
|
||||
{
|
||||
return isHuman() && TheLAN && TheLAN->GetLocalIP() == getIP();
|
||||
}
|
||||
|
||||
// LANGameInfo ----------------------------------------
|
||||
|
||||
LANGameInfo::LANGameInfo()
|
||||
{
|
||||
//Added By Sadullah Nader
|
||||
//Initializtions missing and needed
|
||||
m_lastHeard = 0;
|
||||
m_next = NULL;
|
||||
//
|
||||
for (Int i = 0; i< MAX_SLOTS; ++i)
|
||||
setSlotPointer(i, &m_LANSlot[i]);
|
||||
|
||||
setLocalIP(TheLAN->GetLocalIP());
|
||||
}
|
||||
|
||||
void LANGameInfo::setSlot( Int slotNum, LANGameSlot slotInfo )
|
||||
{
|
||||
DEBUG_ASSERTCRASH( slotNum >= 0 && slotNum < MAX_SLOTS, ("LANGameInfo::setSlot - Invalid slot number"));
|
||||
if (slotNum < 0 || slotNum >= MAX_SLOTS)
|
||||
return;
|
||||
|
||||
m_LANSlot[slotNum] = slotInfo;
|
||||
|
||||
if (slotNum == 0)
|
||||
{
|
||||
m_LANSlot[slotNum].setAccept();
|
||||
m_LANSlot[slotNum].setMapAvailability(true);
|
||||
}
|
||||
}
|
||||
|
||||
LANGameSlot* LANGameInfo::getLANSlot( Int slotNum )
|
||||
{
|
||||
DEBUG_ASSERTCRASH( slotNum >= 0 && slotNum < MAX_SLOTS, ("LANGameInfo::getLANSlot - Invalid slot number"));
|
||||
if (slotNum < 0 || slotNum >= MAX_SLOTS)
|
||||
return NULL;
|
||||
|
||||
return &m_LANSlot[slotNum];
|
||||
}
|
||||
|
||||
const LANGameSlot* LANGameInfo::getConstLANSlot( Int slotNum ) const
|
||||
{
|
||||
DEBUG_ASSERTCRASH( slotNum >= 0 && slotNum < MAX_SLOTS, ("LANGameInfo::getConstLANSlot - Invalid slot number"));
|
||||
if (slotNum < 0 || slotNum >= MAX_SLOTS)
|
||||
return NULL;
|
||||
|
||||
return &m_LANSlot[slotNum];
|
||||
}
|
||||
|
||||
Int LANGameInfo::getLocalSlotNum( void ) const
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_inGame, ("Looking for local game slot while not in game"));
|
||||
if (!m_inGame)
|
||||
return -1;
|
||||
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
const LANGameSlot *slot = getConstLANSlot(i);
|
||||
if (slot->isLocalPlayer())
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Int LANGameInfo::getSlotNum( UnicodeString userName )
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_inGame, ("Looking for game slot while not in game"));
|
||||
if (!m_inGame)
|
||||
return -1;
|
||||
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
LANGameSlot *slot = getLANSlot(i);
|
||||
if (slot->isUser( userName ))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Bool LANGameInfo::amIHost( void )
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_inGame, ("Looking for game slot while not in game"));
|
||||
if (!m_inGame)
|
||||
return false;
|
||||
|
||||
return getLANSlot(0)->isLocalPlayer();
|
||||
}
|
||||
|
||||
void LANGameInfo::setMap( AsciiString mapName )
|
||||
{
|
||||
GameInfo::setMap(mapName);
|
||||
}
|
||||
|
||||
void LANGameInfo::setSeed( Int seed )
|
||||
{
|
||||
GameInfo::setSeed(seed);
|
||||
}
|
||||
|
||||
void LANGameInfo::resetAccepted( void )
|
||||
{
|
||||
if (TheLAN)
|
||||
{
|
||||
TheLAN->ResetGameStartTimer();
|
||||
if (TheLAN->GetMyGame() == this && TheLAN->AmIHost())
|
||||
LANEnableStartButton(true);
|
||||
}
|
||||
for(int i = 0; i< MAX_SLOTS; i++)
|
||||
{
|
||||
m_LANSlot[i].unAccept();
|
||||
}
|
||||
}
|
||||
// Misc game-related functionality --------------------
|
||||
|
||||
|
||||
void LANDisplayGameList( GameWindow *gameListbox, LANGameInfo *gameList )
|
||||
{
|
||||
LANGameInfo *selectedPtr = NULL;
|
||||
Int selectedIndex = -1;
|
||||
Int indexToSelect = -1;
|
||||
if (gameListbox)
|
||||
{
|
||||
GadgetListBoxGetSelected(gameListbox, &selectedIndex);
|
||||
|
||||
if (selectedIndex != -1 )
|
||||
{
|
||||
selectedPtr = (LANGameInfo *)GadgetListBoxGetItemData(gameListbox, selectedIndex, 0);
|
||||
}
|
||||
|
||||
GadgetListBoxReset(gameListbox);
|
||||
|
||||
while (gameList)
|
||||
{
|
||||
UnicodeString txtGName;
|
||||
txtGName = L"";
|
||||
if( gameList->isGameInProgress() )
|
||||
{
|
||||
txtGName.concat(L"[");
|
||||
}
|
||||
txtGName.concat(gameList->getPlayerName(0));
|
||||
if( gameList->isGameInProgress() )
|
||||
{
|
||||
txtGName.concat(L"]");
|
||||
}
|
||||
Int addedIndex = GadgetListBoxAddEntryText(gameListbox, txtGName, (gameList->isGameInProgress())?gameInProgressColor:gameColor, -1, -1);
|
||||
GadgetListBoxSetItemData(gameListbox, (void *)gameList, addedIndex, 0 );
|
||||
|
||||
if (selectedPtr == gameList)
|
||||
indexToSelect = addedIndex;
|
||||
|
||||
gameList = gameList->getNext();
|
||||
}
|
||||
|
||||
if (indexToSelect >= 0)
|
||||
GadgetListBoxSetSelected(gameListbox, indexToSelect);
|
||||
else
|
||||
HideGameInfoWindow(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
AsciiString GenerateGameOptionsString( void )
|
||||
{
|
||||
if(!TheLAN->GetMyGame() || !TheLAN->GetMyGame()->amIHost())
|
||||
return AsciiString.TheEmptyString;
|
||||
|
||||
return GameInfoToAsciiString(TheLAN->GetMyGame());
|
||||
}
|
||||
|
||||
Bool ParseGameOptionsString(LANGameInfo *game, AsciiString options)
|
||||
{
|
||||
if (!TheLAN || !game)
|
||||
return false;
|
||||
|
||||
Int oldLocalSlotNum = (game->isInGame()) ? game->getLocalSlotNum() : -1;
|
||||
Bool wasInGame = oldLocalSlotNum >= 0;
|
||||
// Int hadMap = wasInGame && game->getSlot(oldLocalSlotNum)->hasMap();
|
||||
AsciiString oldMap = game->getMap();
|
||||
UnsignedInt oldMapCRC, newMapCRC;
|
||||
oldMapCRC = game->getMapCRC();
|
||||
|
||||
std::map<UnicodeString, UnicodeString> oldLogins, oldMachines;
|
||||
std::map<UnicodeString, UnicodeString>::iterator mapIt;
|
||||
Int i;
|
||||
for (i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
LANGameSlot *slot = game->getLANSlot(i);
|
||||
if (slot && slot->isHuman())
|
||||
{
|
||||
//DEBUG_LOG(("Saving off %ls@%ls for %ls\n", slot->getUser()->getLogin().str(), slot->getUser()->getHost().str(), slot->getName().str()));
|
||||
oldLogins[slot->getName()] = slot->getUser()->getLogin();
|
||||
oldMachines[slot->getName()] = slot->getUser()->getHost();
|
||||
}
|
||||
}
|
||||
|
||||
if (ParseAsciiStringToGameInfo(game, options))
|
||||
{
|
||||
Int newLocalSlotNum = (game->isInGame()) ? game->getLocalSlotNum() : -1;
|
||||
Bool isInGame = newLocalSlotNum >= 0;
|
||||
if (!TheLAN->AmIHost() && isInGame)
|
||||
{
|
||||
// Int hasMap = game->getSlot(newLocalSlotNum)->hasMap();
|
||||
newMapCRC = game->getMapCRC();
|
||||
//DEBUG_LOG(("wasInGame:%d isInGame:%d hadMap:%d hasMap:%d oldMap:%s newMap:%s\n", wasInGame, isInGame, hadMap, hasMap, oldMap.str(), game->getMap().str()));
|
||||
if ( (oldMapCRC ^ newMapCRC)/*(hasMap ^ hadMap)*/ || (!wasInGame && isInGame) )
|
||||
{
|
||||
// it changed. send it
|
||||
TheLAN->RequestHasMap();
|
||||
lanUpdateSlotList();
|
||||
updateGameOptions();
|
||||
}
|
||||
}
|
||||
// clean up LAN users, etc.
|
||||
UnsignedInt now = timeGetTime();
|
||||
for (i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
LANGameSlot *slot = game->getLANSlot(i);
|
||||
|
||||
if (slot->isHuman())
|
||||
{
|
||||
slot->setLastHeard(now);
|
||||
mapIt = oldLogins.find(slot->getName());
|
||||
if (mapIt != oldLogins.end())
|
||||
slot->setLogin(mapIt->second);
|
||||
mapIt = oldMachines.find(slot->getName());
|
||||
if (mapIt != oldMachines.end())
|
||||
slot->setHost(mapIt->second);
|
||||
//DEBUG_LOG(("Restored %ls@%ls for %ls\n", slot->getUser()->getLogin().str(), slot->getUser()->getHost().str(), slot->getName().str()));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
1325
Generals/Code/GameEngine/Source/GameNetwork/NAT.cpp
Normal file
1325
Generals/Code/GameEngine/Source/GameNetwork/NAT.cpp
Normal file
File diff suppressed because it is too large
Load Diff
455
Generals/Code/GameEngine/Source/GameNetwork/NetCommandList.cpp
Normal file
455
Generals/Code/GameEngine/Source/GameNetwork/NetCommandList.cpp
Normal file
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/NetCommandList.h"
|
||||
#include "GameNetwork/NetworkUtil.h"
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
NetCommandList::NetCommandList() {
|
||||
m_first = NULL;
|
||||
m_last = NULL;
|
||||
m_lastMessageInserted = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
NetCommandList::~NetCommandList() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the given list of commands to this list.
|
||||
*/
|
||||
void NetCommandList::appendList(NetCommandList *list) {
|
||||
if (list == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to do it this way because of the reference counting that needs to happen in appendMessage.
|
||||
NetCommandRef *msg = list->getFirstMessage();
|
||||
NetCommandRef *next = NULL;
|
||||
while (msg != NULL) {
|
||||
next = msg->getNext();
|
||||
NetCommandRef *temp = addMessage(msg->getCommand());
|
||||
if (temp != NULL) {
|
||||
temp->setRelay(msg->getRelay());
|
||||
}
|
||||
|
||||
msg = next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first message in this list.
|
||||
*/
|
||||
NetCommandRef *NetCommandList::getFirstMessage() {
|
||||
return m_first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given message from this list.
|
||||
*/
|
||||
void NetCommandList::removeMessage(NetCommandRef *msg) {
|
||||
if (m_lastMessageInserted == msg) {
|
||||
m_lastMessageInserted = msg->getNext();
|
||||
}
|
||||
|
||||
if (msg->getPrev() != NULL) {
|
||||
msg->getPrev()->setNext(msg->getNext());
|
||||
}
|
||||
if (msg->getNext() != NULL) {
|
||||
msg->getNext()->setPrev(msg->getPrev());
|
||||
}
|
||||
|
||||
if (msg == m_first) {
|
||||
m_first = msg->getNext();
|
||||
}
|
||||
if (msg == m_last) {
|
||||
m_last = msg->getPrev();
|
||||
}
|
||||
|
||||
msg->setNext(NULL);
|
||||
msg->setPrev(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the list.
|
||||
*/
|
||||
void NetCommandList::init() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the contents of this list.
|
||||
*/
|
||||
void NetCommandList::reset() {
|
||||
NetCommandRef *temp = m_first;
|
||||
while (m_first != NULL) {
|
||||
temp = m_first->getNext();
|
||||
m_first->setNext(NULL);
|
||||
m_first->setPrev(NULL);
|
||||
m_first->deleteInstance();
|
||||
m_first = temp;
|
||||
}
|
||||
m_last = NULL;
|
||||
m_lastMessageInserted = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert sorts msg. Assumes that all the previous message inserts were done using this function.
|
||||
* The message is sorted in based first on command type, then player id, and then command id.
|
||||
*/
|
||||
NetCommandRef * NetCommandList::addMessage(NetCommandMsg *cmdMsg) {
|
||||
if (cmdMsg == NULL) {
|
||||
DEBUG_ASSERTCRASH(cmdMsg != NULL, ("NetCommandList::addMessage - command message was NULL"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// UnsignedInt id = cmdMsg->getID();
|
||||
|
||||
NetCommandRef *msg = NEW_NETCOMMANDREF(cmdMsg);
|
||||
|
||||
if (m_first == NULL) {
|
||||
// this is the first node, so we don't have to worry about ordering it.
|
||||
m_first = msg;
|
||||
m_last = msg;
|
||||
m_lastMessageInserted = msg;
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (m_lastMessageInserted != NULL) {
|
||||
// Messages that are inserted in order should just be put in one right after the other.
|
||||
// So saving the placement of the last message inserted can give us a huge boost in
|
||||
// efficiency.
|
||||
NetCommandRef *theNext = m_lastMessageInserted->getNext();
|
||||
if ((m_lastMessageInserted->getCommand()->getNetCommandType() == msg->getCommand()->getNetCommandType()) &&
|
||||
(m_lastMessageInserted->getCommand()->getPlayerID() == msg->getCommand()->getPlayerID()) &&
|
||||
(m_lastMessageInserted->getCommand()->getID() < msg->getCommand()->getID()) &&
|
||||
((theNext == NULL) || ((theNext->getCommand()->getNetCommandType() > msg->getCommand()->getNetCommandType()) ||
|
||||
(theNext->getCommand()->getPlayerID() > msg->getCommand()->getPlayerID()) ||
|
||||
(theNext->getCommand()->getID() > msg->getCommand()->getID())))) {
|
||||
|
||||
// Make sure this command isn't already in the list.
|
||||
if (isEqualCommandMsg(m_lastMessageInserted->getCommand(), msg->getCommand())) {
|
||||
|
||||
// This command is already in the list, don't duplicate it.
|
||||
msg->deleteInstance();
|
||||
msg = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (theNext == NULL) {
|
||||
// this means that m_lastMessageInserted == m_last, so m_last should point to the msg that is being inserted.
|
||||
msg->setNext(m_lastMessageInserted->getNext());
|
||||
msg->setPrev(m_lastMessageInserted);
|
||||
m_lastMessageInserted->setNext(msg);
|
||||
m_lastMessageInserted = msg;
|
||||
m_last = msg;
|
||||
} else {
|
||||
msg->setNext(m_lastMessageInserted->getNext());
|
||||
msg->setPrev(m_lastMessageInserted);
|
||||
m_lastMessageInserted->setNext(msg);
|
||||
msg->getNext()->setPrev(msg);
|
||||
m_lastMessageInserted = msg;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->getCommand()->getNetCommandType() > m_last->getCommand()->getNetCommandType()) {
|
||||
// easy optimization for a command that goes at the end of the list
|
||||
// since they are likely to be added in order.
|
||||
|
||||
// Make sure this command isn't already in the list.
|
||||
if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
|
||||
|
||||
// This command is already in the list, don't duplicate it.
|
||||
msg->deleteInstance();
|
||||
msg = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg->setPrev(m_last);
|
||||
msg->setNext(NULL);
|
||||
m_last->setNext(msg);
|
||||
m_last = msg;
|
||||
m_lastMessageInserted = msg;
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (msg->getCommand()->getNetCommandType() < m_first->getCommand()->getNetCommandType()) {
|
||||
// Make sure this command isn't already in the list.
|
||||
if (isEqualCommandMsg(m_first->getCommand(), msg->getCommand())) {
|
||||
|
||||
// This command is already in the list, don't duplicate it.
|
||||
msg->deleteInstance();
|
||||
msg = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The command goes at the head of the list.
|
||||
msg->setNext(m_first);
|
||||
msg->setPrev(NULL);
|
||||
m_first->setPrev(msg);
|
||||
m_first = msg;
|
||||
m_lastMessageInserted = msg;
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
// Find the start of the command type we're looking for.
|
||||
NetCommandRef *tempmsg = m_first;
|
||||
while ((tempmsg != NULL) && (msg->getCommand()->getNetCommandType() > tempmsg->getCommand()->getNetCommandType())) {
|
||||
tempmsg = tempmsg->getNext();
|
||||
}
|
||||
|
||||
if (tempmsg == NULL) {
|
||||
// Make sure this command isn't already in the list.
|
||||
if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
|
||||
|
||||
// This command is already in the list, don't duplicate it.
|
||||
msg->deleteInstance();
|
||||
msg = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// message goes at the end of the list.
|
||||
msg->setPrev(m_last);
|
||||
msg->setNext(NULL);
|
||||
m_last->setNext(msg);
|
||||
m_last = msg;
|
||||
m_lastMessageInserted = msg;
|
||||
return msg;
|
||||
}
|
||||
|
||||
// Now find the player position. munkee.
|
||||
while ((tempmsg != NULL) && (msg->getCommand()->getNetCommandType() == tempmsg->getCommand()->getNetCommandType()) && (msg->getCommand()->getPlayerID() > tempmsg->getCommand()->getPlayerID())) {
|
||||
tempmsg = tempmsg->getNext();
|
||||
}
|
||||
|
||||
if (tempmsg == NULL) {
|
||||
// Make sure this command isn't already in the list.
|
||||
if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
|
||||
|
||||
// This command is already in the list, don't duplicate it.
|
||||
msg->deleteInstance();
|
||||
msg = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// message goes at the end of the list.
|
||||
msg->setPrev(m_last);
|
||||
msg->setNext(NULL);
|
||||
m_last->setNext(msg);
|
||||
m_last = msg;
|
||||
m_lastMessageInserted = msg;
|
||||
return msg;
|
||||
}
|
||||
|
||||
// Find the position within the player's section based on the command ID.
|
||||
// If the command type doesn't require a command ID, sort by whatever it should be sorted by.
|
||||
while ((tempmsg != NULL) && (msg->getCommand()->getNetCommandType() == tempmsg->getCommand()->getNetCommandType()) && (msg->getCommand()->getPlayerID() == tempmsg->getCommand()->getPlayerID()) && (msg->getCommand()->getSortNumber() > tempmsg->getCommand()->getSortNumber())) {
|
||||
tempmsg = tempmsg->getNext();
|
||||
}
|
||||
|
||||
if (tempmsg == NULL) {
|
||||
// Make sure this command isn't already in the list.
|
||||
if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
|
||||
|
||||
// This command is already in the list, don't duplicate it.
|
||||
msg->deleteInstance();
|
||||
msg = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This message goes at the end of the list.
|
||||
msg->setPrev(m_last);
|
||||
msg->setNext(NULL);
|
||||
m_last->setNext(msg);
|
||||
m_last = msg;
|
||||
m_lastMessageInserted = msg;
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (tempmsg == m_first) {
|
||||
// Make sure this command isn't already in the list.
|
||||
if (isEqualCommandMsg(m_first->getCommand(), msg->getCommand())) {
|
||||
|
||||
// This command is already in the list, don't duplicate it.
|
||||
msg->deleteInstance();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This message goes at the head of the list.
|
||||
msg->setNext(m_first);
|
||||
msg->setPrev(NULL);
|
||||
m_first->setPrev(msg);
|
||||
m_first = msg;
|
||||
m_lastMessageInserted = msg;
|
||||
return msg;
|
||||
}
|
||||
|
||||
// Make sure this command isn't already in the list.
|
||||
if (isEqualCommandMsg(tempmsg->getCommand(), msg->getCommand())) {
|
||||
|
||||
// This command is already in the list, don't duplicate it.
|
||||
msg->deleteInstance();
|
||||
msg = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Insert message before tempmsg.
|
||||
msg->setNext(tempmsg);
|
||||
msg->setPrev(tempmsg->getPrev());
|
||||
msg->getPrev()->setNext(msg);
|
||||
tempmsg->setPrev(msg);
|
||||
m_lastMessageInserted = msg;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
Int NetCommandList::length() {
|
||||
Int retval = 0;
|
||||
NetCommandRef *temp = m_first;
|
||||
while (temp != NULL) {
|
||||
++retval;
|
||||
temp = temp->getNext();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is really inefficient, but we can probably get away with it because
|
||||
* there shouldn't be too many messages for any given frame.
|
||||
*/
|
||||
NetCommandRef * NetCommandList::findMessage(NetCommandMsg *msg) {
|
||||
NetCommandRef *retval = m_first;
|
||||
while ((retval != NULL) && (isEqualCommandMsg(retval->getCommand(), msg) == FALSE)) {
|
||||
retval = retval->getNext();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
NetCommandRef * NetCommandList::findMessage(UnsignedShort commandID, UnsignedByte playerID) {
|
||||
NetCommandRef *retval = m_first;
|
||||
while (retval != NULL) {
|
||||
if (DoesCommandRequireACommandID(retval->getCommand()->getNetCommandType())) {
|
||||
if ((retval->getCommand()->getID() == commandID) && (retval->getCommand()->getPlayerID() == playerID)) {
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
retval = retval->getNext();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
Bool NetCommandList::isEqualCommandMsg(NetCommandMsg *msg1, NetCommandMsg *msg2) {
|
||||
if (DoesCommandRequireACommandID(msg1->getNetCommandType()) != DoesCommandRequireACommandID(msg2->getNetCommandType())) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// At this point we know that the commands both do or do not require a command id.
|
||||
// Do or do not, there is no try.
|
||||
if (DoesCommandRequireACommandID(msg1->getNetCommandType())) {
|
||||
// Are the commands from the same player?
|
||||
if (msg1->getPlayerID() != msg2->getPlayerID()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Do they have the same command ID?
|
||||
if (msg1->getID() != msg2->getID()) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// If we've gotten this far, we know that the commands do not require a command id.
|
||||
// So now our equality checking becomes type-specific.
|
||||
|
||||
// Are they the same type?
|
||||
if (msg1->getNetCommandType() != msg2->getNetCommandType()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Are they from the same player?
|
||||
if (msg1->getPlayerID() != msg2->getPlayerID()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// They are the same type and from the same player.
|
||||
// Time for the type specific stuff.
|
||||
if (msg1->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE1) {
|
||||
NetAckStage1CommandMsg *ack1 = (NetAckStage1CommandMsg *)msg1;
|
||||
NetAckStage1CommandMsg *ack2 = (NetAckStage1CommandMsg *)msg2;
|
||||
|
||||
if (ack1->getOriginalPlayerID() != ack2->getOriginalPlayerID()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ack1->getCommandID() != ack2->getCommandID()) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// They are the same type and from the same player.
|
||||
// Time for the type specific stuff.
|
||||
if (msg1->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE2) {
|
||||
NetAckStage2CommandMsg *ack1 = (NetAckStage2CommandMsg *)msg1;
|
||||
NetAckStage2CommandMsg *ack2 = (NetAckStage2CommandMsg *)msg2;
|
||||
|
||||
if (ack1->getOriginalPlayerID() != ack2->getOriginalPlayerID()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ack1->getCommandID() != ack2->getCommandID()) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// They are the same type and from the same player.
|
||||
// Time for the type specific stuff.
|
||||
if (msg1->getNetCommandType() == NETCOMMANDTYPE_ACKBOTH) {
|
||||
NetAckBothCommandMsg *ack1 = (NetAckBothCommandMsg *)msg1;
|
||||
NetAckBothCommandMsg *ack2 = (NetAckBothCommandMsg *)msg2;
|
||||
|
||||
if (ack1->getOriginalPlayerID() != ack2->getOriginalPlayerID()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ack1->getCommandID() != ack2->getCommandID()) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
1069
Generals/Code/GameEngine/Source/GameNetwork/NetCommandMsg.cpp
Normal file
1069
Generals/Code/GameEngine/Source/GameNetwork/NetCommandMsg.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/NetCommandRef.h"
|
||||
|
||||
#ifdef DEBUG_NETCOMMANDREF
|
||||
static UnsignedInt refNum = 0;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Constructor. Attach to the given network command.
|
||||
*/
|
||||
#ifdef DEBUG_NETCOMMANDREF
|
||||
NetCommandRef::NetCommandRef(NetCommandMsg *msg, char *filename, int line)
|
||||
#else
|
||||
NetCommandRef::NetCommandRef(NetCommandMsg *msg)
|
||||
#endif
|
||||
{
|
||||
m_msg = msg;
|
||||
m_next = NULL;
|
||||
m_prev = NULL;
|
||||
m_msg->attach();
|
||||
m_timeLastSent = -1;
|
||||
|
||||
#ifdef DEBUG_NETCOMMANDREF
|
||||
m_id = ++refNum;
|
||||
DEBUG_LOG(("NetCommandRef %d allocated in file %s line %d\n", m_id, filename, line));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor. Detach from the network command.
|
||||
*/
|
||||
NetCommandRef::~NetCommandRef()
|
||||
{
|
||||
if (m_msg != NULL)
|
||||
{
|
||||
m_msg->detach();
|
||||
}
|
||||
DEBUG_ASSERTCRASH(m_next == NULL, ("NetCommandRef::~NetCommandRef - m_next != NULL"));
|
||||
DEBUG_ASSERTCRASH(m_prev == NULL, ("NetCommandRef::~NetCommandRef - m_prev != NULL"));
|
||||
|
||||
#ifdef DEBUG_NETCOMMANDREF
|
||||
DEBUG_LOG(("NetCommandRef %d deleted\n", m_id));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////// NetCommandWrapperList.cpp ////////////////////////////////
|
||||
// Bryan Cleveland
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/NetCommandWrapperList.h"
|
||||
#include "GameNetwork/NetPacket.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////// NetCommandWrapperListNode ///////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NetCommandWrapperListNode::NetCommandWrapperListNode(NetWrapperCommandMsg *msg)
|
||||
{
|
||||
//Added By Sadullah Nader
|
||||
//Initializations inserted
|
||||
m_next = NULL;
|
||||
|
||||
//
|
||||
|
||||
m_numChunks = msg->getNumChunks();
|
||||
m_chunksPresent = NEW Bool[m_numChunks]; // pool[]ify
|
||||
m_numChunksPresent = 0;
|
||||
|
||||
for (Int i = 0; i < m_numChunks; ++i) {
|
||||
m_chunksPresent[i] = FALSE;
|
||||
}
|
||||
|
||||
m_dataLength = msg->getTotalDataLength();
|
||||
m_data = NEW UnsignedByte[m_dataLength]; // pool[]ify
|
||||
|
||||
m_commandID = msg->getWrappedCommandID();
|
||||
}
|
||||
|
||||
NetCommandWrapperListNode::~NetCommandWrapperListNode() {
|
||||
if (m_chunksPresent != NULL) {
|
||||
delete[] m_chunksPresent;
|
||||
m_chunksPresent = NULL;
|
||||
}
|
||||
|
||||
if (m_data != NULL) {
|
||||
delete[] m_data;
|
||||
m_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Bool NetCommandWrapperListNode::isComplete() {
|
||||
return m_numChunksPresent == m_numChunks;
|
||||
}
|
||||
|
||||
Int NetCommandWrapperListNode::getPercentComplete(void) {
|
||||
if (isComplete())
|
||||
return 100;
|
||||
else
|
||||
return min(99, REAL_TO_INT( ((Real)m_numChunksPresent)/((Real)m_numChunks)*100.0f ));
|
||||
}
|
||||
|
||||
UnsignedShort NetCommandWrapperListNode::getCommandID() {
|
||||
return m_commandID;
|
||||
}
|
||||
|
||||
UnsignedInt NetCommandWrapperListNode::getRawDataLength() {
|
||||
return m_dataLength;
|
||||
}
|
||||
|
||||
void NetCommandWrapperListNode::copyChunkData(NetWrapperCommandMsg *msg) {
|
||||
if (msg == NULL) {
|
||||
DEBUG_CRASH(("Trying to copy data from a non-existent wrapper command message"));
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_ASSERTCRASH(msg->getChunkNumber() < m_numChunks, ("MunkeeChunk %d of %d\n",
|
||||
msg->getChunkNumber(), m_numChunks));
|
||||
if (msg->getChunkNumber() >= m_numChunks)
|
||||
return;
|
||||
|
||||
DEBUG_LOG(("NetCommandWrapperListNode::copyChunkData() - copying chunk %d\n",
|
||||
msg->getChunkNumber()));
|
||||
|
||||
if (m_chunksPresent[msg->getChunkNumber()] == TRUE) {
|
||||
// we already received this chunk, no need to recopy it.
|
||||
return;
|
||||
}
|
||||
|
||||
m_chunksPresent[msg->getChunkNumber()] = TRUE;
|
||||
UnsignedInt offset = msg->getDataOffset();
|
||||
memcpy(m_data + offset, msg->getData(), msg->getDataLength());
|
||||
++m_numChunksPresent;
|
||||
}
|
||||
|
||||
UnsignedByte * NetCommandWrapperListNode::getRawData() {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////// NetCommandWrapperList ///////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NetCommandWrapperList::NetCommandWrapperList() {
|
||||
m_list = NULL;
|
||||
}
|
||||
|
||||
NetCommandWrapperList::~NetCommandWrapperList() {
|
||||
NetCommandWrapperListNode *temp;
|
||||
while (m_list != NULL) {
|
||||
temp = m_list->m_next;
|
||||
m_list->deleteInstance();
|
||||
m_list = temp;
|
||||
}
|
||||
}
|
||||
|
||||
void NetCommandWrapperList::init() {
|
||||
m_list = NULL;
|
||||
}
|
||||
|
||||
void NetCommandWrapperList::reset() {
|
||||
NetCommandWrapperListNode *temp;
|
||||
while (m_list != NULL) {
|
||||
temp = m_list->m_next;
|
||||
m_list->deleteInstance();
|
||||
m_list = temp;
|
||||
}
|
||||
}
|
||||
|
||||
Int NetCommandWrapperList::getPercentComplete(UnsignedShort wrappedCommandID)
|
||||
{
|
||||
NetCommandWrapperListNode *temp = m_list;
|
||||
|
||||
while ((temp != NULL) && (temp->getCommandID() != wrappedCommandID)) {
|
||||
temp = temp->m_next;
|
||||
}
|
||||
|
||||
if (!temp)
|
||||
return 0;
|
||||
|
||||
return temp->getPercentComplete();
|
||||
}
|
||||
|
||||
void NetCommandWrapperList::processWrapper(NetCommandRef *ref) {
|
||||
NetCommandWrapperListNode *temp = m_list;
|
||||
NetWrapperCommandMsg *msg = (NetWrapperCommandMsg *)(ref->getCommand());
|
||||
|
||||
while ((temp != NULL) && (temp->getCommandID() != msg->getWrappedCommandID())) {
|
||||
temp = temp->m_next;
|
||||
}
|
||||
|
||||
if (temp == NULL) {
|
||||
temp = newInstance(NetCommandWrapperListNode)(msg);
|
||||
temp->m_next = m_list;
|
||||
m_list = temp;
|
||||
}
|
||||
|
||||
temp->copyChunkData(msg);
|
||||
}
|
||||
|
||||
NetCommandList * NetCommandWrapperList::getReadyCommands()
|
||||
{
|
||||
NetCommandList *retlist = newInstance(NetCommandList);
|
||||
retlist->init();
|
||||
|
||||
NetCommandWrapperListNode *temp = m_list;
|
||||
NetCommandWrapperListNode *next = NULL;
|
||||
|
||||
while (temp != NULL) {
|
||||
next = temp->m_next;
|
||||
if (temp->isComplete()) {
|
||||
NetCommandRef *msg = NetPacket::ConstructNetCommandMsgFromRawData(temp->getRawData(), temp->getRawDataLength());
|
||||
NetCommandRef *ret = retlist->addMessage(msg->getCommand());
|
||||
ret->setRelay(msg->getRelay());
|
||||
|
||||
msg->deleteInstance();
|
||||
msg = NULL;
|
||||
|
||||
removeFromList(temp);
|
||||
temp = NULL;
|
||||
}
|
||||
temp = next;
|
||||
}
|
||||
|
||||
return retlist;
|
||||
}
|
||||
|
||||
void NetCommandWrapperList::removeFromList(NetCommandWrapperListNode *node) {
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
NetCommandWrapperListNode *temp = m_list;
|
||||
NetCommandWrapperListNode *prev = NULL;
|
||||
|
||||
while ((temp != NULL) && (temp->getCommandID() != node->getCommandID())) {
|
||||
prev = temp;
|
||||
temp = temp->m_next;
|
||||
}
|
||||
|
||||
if (temp == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prev == NULL) {
|
||||
m_list = temp->m_next;
|
||||
temp->deleteInstance();
|
||||
temp = NULL;
|
||||
} else {
|
||||
prev->m_next = temp->m_next;
|
||||
temp->deleteInstance();
|
||||
temp = NULL;
|
||||
}
|
||||
}
|
||||
227
Generals/Code/GameEngine/Source/GameNetwork/NetMessageStream.cpp
Normal file
227
Generals/Code/GameEngine/Source/GameNetwork/NetMessageStream.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
|
||||
// NetMessageStream.cpp
|
||||
// Holds misc functions to encapsulate GameMessages into Command Packets to send
|
||||
// over the network.
|
||||
// Author: Matthew D. Campbell, July 2001
|
||||
/*
|
||||
#include "stdlib.h" // VC++ wants this here, or gives compile error...
|
||||
|
||||
#include "Common/GameType.h"
|
||||
#include "Common/MessageStream.h"
|
||||
#include "Common/GameEngine.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameNetwork/NetworkInterface.h"
|
||||
#include "GameNetwork/NetworkDefs.h"
|
||||
|
||||
|
||||
|
||||
// The per-player pointers for the list of commands
|
||||
static CommandMsg *CommandHead[MAX_SLOTS] = { /// @todo: remove static initialization
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
static CommandMsg *CommandTail[MAX_SLOTS] = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* AddToNetCommandList adds a CommandMsg to a list of commands.
|
||||
*
|
||||
static Bool AddToNetCommandList(GameMessage *msg, UnsignedInt timestamp, CommandMsg *& CommandHead, CommandMsg *& CommandTail)
|
||||
{
|
||||
CommandMsg *cmdMsg = NEW CommandMsg(timestamp, msg);
|
||||
if (!cmdMsg)
|
||||
{
|
||||
DEBUG_LOG(("Alloc failed!\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CommandTail == NULL)
|
||||
{
|
||||
CommandHead = cmdMsg;
|
||||
CommandTail = cmdMsg;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmdMsg->SetPrevCommandMsg(CommandTail);
|
||||
CommandTail->SetNextCommandMsg(cmdMsg);
|
||||
CommandTail = cmdMsg;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* AddToRemoteNetCommandList is used by TheNetwork to queue up commands recieved from other players.
|
||||
*
|
||||
Bool AddToNetCommandList(Int playerNum, GameMessage *msg, UnsignedInt timestamp)
|
||||
{
|
||||
if (playerNum < 0 || playerNum >= MAX_SLOTS)
|
||||
return false;
|
||||
|
||||
DEBUG_LOG(("Adding msg to NetCommandList %d\n", playerNum));
|
||||
return AddToNetCommandList(msg, timestamp, CommandHead[playerNum], CommandTail[playerNum]);
|
||||
}
|
||||
|
||||
/**
|
||||
* GetCommandMsg returns a GameMessage (deleting its CommandMsg wrapper) that is valid
|
||||
* for the current frame, or NULL.
|
||||
*
|
||||
static GameMessage * GetCommandMsg(UnsignedInt timestamp, CommandMsg *& CommandHead, CommandMsg *& CommandTail)
|
||||
{
|
||||
if (!CommandHead)
|
||||
return NULL;
|
||||
|
||||
if (CommandHead->GetTimestamp() < timestamp)
|
||||
{
|
||||
DEBUG_LOG(("Time is %d, yet message timestamp is %d!\n", timestamp, CommandHead->GetTimestamp()));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (CommandHead->GetTimestamp() != timestamp)
|
||||
return NULL;
|
||||
|
||||
CommandMsg *theMsg = CommandHead;
|
||||
|
||||
if (CommandHead->GetNextCommandMsg())
|
||||
{
|
||||
CommandHead->GetNextCommandMsg()->SetPrevCommandMsg(NULL);
|
||||
CommandHead = CommandHead->GetNextCommandMsg();
|
||||
}
|
||||
else
|
||||
{
|
||||
CommandHead = CommandTail = NULL;
|
||||
}
|
||||
|
||||
GameMessage *msg = theMsg->GetGameMessage();
|
||||
delete theMsg;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetCommandMsg returns a message from the command list.
|
||||
*
|
||||
GameMessage * GetCommandMsg(UnsignedInt timestamp, Int playerNum)
|
||||
{
|
||||
if (playerNum < 0 || playerNum >= MAX_SLOTS)
|
||||
return NULL;
|
||||
|
||||
//DEBUG_LOG(("Adding msg to NetCommandList %d\n", playerNum));
|
||||
return GetCommandMsg(timestamp, CommandHead[playerNum], CommandTail[playerNum]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//====================================================================================
|
||||
|
||||
// The commandBuf & commandPacket hold the commands we're building up for the frame.
|
||||
static unsigned char commandBuf[sizeof(CommandPacket)+1];
|
||||
static CommandPacket *commandPacket = (CommandPacket *)(commandBuf+1);
|
||||
|
||||
/**
|
||||
* ClearCommandPacket clears the command packet at the start of the frame.
|
||||
*
|
||||
void ClearCommandPacket(UnsignedInt frame)
|
||||
{
|
||||
commandPacket->m_frame = frame;
|
||||
commandPacket->m_numCommands = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* AddCommandToPacket creates a packet containing all move/attack/etc commands
|
||||
* for the current frame.
|
||||
*
|
||||
Bool AddCommandToPacket(const GameMessage *msg)
|
||||
{
|
||||
int messageSize = sizeofMessageHeader + sizeofMessageArg * msg->getArgumentCount();
|
||||
|
||||
// If we have too much, send what we have
|
||||
if (bytesUsed && (bytesUsed + sizeof(CommandPacketHeader) + messageSize >= MAX_MESSAGE_LEN))
|
||||
{
|
||||
commandBuf[0] = MSGTYPE_PARTIALCOMMAND;
|
||||
if (!TheNetwork->queueSend(BROADCAST_CON, commandBuf, bytesUsed + sizeof(CommandPacketHeader) + 1, MSG_NEEDACK | MSG_SEQUENCED))
|
||||
{
|
||||
//DEBUG_ASSERTCRASH(false, ("Too many commands in one frame! Some will be dropped."));
|
||||
DEBUG_LOG(("Too many commands in one frame! Some will be dropped."));
|
||||
return false;
|
||||
}
|
||||
commandBuf[0] = MSGTYPE_COMMANDCOUNT;
|
||||
commandPacket->header.m_numCommands = 0;
|
||||
bytesUsed = 0;
|
||||
}
|
||||
|
||||
if (bytesUsed + sizeof(CommandPacketHeader) + messageSize >= MAX_MESSAGE_LEN)
|
||||
{
|
||||
//DEBUG_ASSERTCRASH(false, ("Too many commands in one frame! Some will be dropped."));
|
||||
DEBUG_LOG(("Too many commands in one frame! Some will be dropped."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// We have room, so add the message
|
||||
commandPacket->header.m_numCommands++;
|
||||
commandPacket->m_commands[bytesUsed++] = (unsigned char)msg->getType();
|
||||
commandPacket->m_commands[bytesUsed++] = msg->getArgumentCount();
|
||||
|
||||
for (int i=0; i<msg->getArgumentCount(); ++i)
|
||||
{
|
||||
memcpy((unsigned char *)(commandPacket->m_commands + bytesUsed), (unsigned char *)msg->getArgument(i), sizeofMessageArg);
|
||||
bytesUsed += sizeofMessageArg;
|
||||
}
|
||||
|
||||
//DEBUG_ASSERTCRASH(bytesUsed + sizeof(CommandPacketHeader) < MAX_MESSAGE_LEN, ("Memory overwrite constructing command packet!"));
|
||||
//DEBUG_LOG(("Memory overwrite constructing command packet!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* TheNetwork calls GetCommandPacket to get commands to send.
|
||||
*
|
||||
CommandPacket *GetCommandPacket(void)
|
||||
{
|
||||
commandBuf[0] = MSGTYPE_COMMANDCOUNT;
|
||||
return commandPacket;
|
||||
}
|
||||
|
||||
//====================================================================================
|
||||
|
||||
*/
|
||||
5857
Generals/Code/GameEngine/Source/GameNetwork/NetPacket.cpp
Normal file
5857
Generals/Code/GameEngine/Source/GameNetwork/NetPacket.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1034
Generals/Code/GameEngine/Source/GameNetwork/Network.cpp
Normal file
1034
Generals/Code/GameEngine/Source/GameNetwork/Network.cpp
Normal file
File diff suppressed because it is too large
Load Diff
272
Generals/Code/GameEngine/Source/GameNetwork/NetworkUtil.cpp
Normal file
272
Generals/Code/GameEngine/Source/GameNetwork/NetworkUtil.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/NetworkUtil.h"
|
||||
|
||||
Int MAX_FRAMES_AHEAD = 128;
|
||||
Int MIN_RUNAHEAD = 10;
|
||||
Int FRAME_DATA_LENGTH = (MAX_FRAMES_AHEAD+1)*2;
|
||||
Int FRAMES_TO_KEEP = (MAX_FRAMES_AHEAD/2) + 1;
|
||||
|
||||
#ifdef DEBUG_LOGGING
|
||||
|
||||
void dumpBufferToLog(const void *vBuf, Int len, const char *fname, Int line)
|
||||
{
|
||||
DEBUG_LOG(("======= dumpBufferToLog() %d bytes =======\n", len));
|
||||
DEBUG_LOG(("Source: %s:%d\n", fname, line));
|
||||
const char *buf = (const char *)vBuf;
|
||||
Int numLines = len / 8;
|
||||
if ((len % 8) != 0)
|
||||
{
|
||||
++numLines;
|
||||
}
|
||||
for (Int dumpindex = 0; dumpindex < numLines; ++dumpindex)
|
||||
{
|
||||
Int offset = dumpindex*8;
|
||||
DEBUG_LOG(("\t%5.5d\t", offset));
|
||||
Int dumpindex2;
|
||||
Int numBytesThisLine = min(8, len - offset);
|
||||
for (dumpindex2 = 0; dumpindex2 < numBytesThisLine; ++dumpindex2)
|
||||
{
|
||||
Int c = (buf[offset + dumpindex2] & 0xff);
|
||||
DEBUG_LOG(("%02X ", c));
|
||||
}
|
||||
for (; dumpindex2 < 8; ++dumpindex2)
|
||||
{
|
||||
DEBUG_LOG((" "));
|
||||
}
|
||||
DEBUG_LOG((" | "));
|
||||
for (dumpindex2 = 0; dumpindex2 < numBytesThisLine; ++dumpindex2)
|
||||
{
|
||||
char c = buf[offset + dumpindex2];
|
||||
DEBUG_LOG(("%c", (isprint(c)?c:'.')));
|
||||
}
|
||||
DEBUG_LOG(("\n"));
|
||||
}
|
||||
DEBUG_LOG(("End of packet dump\n"));
|
||||
}
|
||||
|
||||
#endif // DEBUG_LOGGING
|
||||
|
||||
/**
|
||||
* ResolveIP turns a string ("games2.westwood.com", or "192.168.0.1") into
|
||||
* a 32-bit unsigned integer.
|
||||
*/
|
||||
UnsignedInt ResolveIP(AsciiString host)
|
||||
{
|
||||
struct hostent *hostStruct;
|
||||
struct in_addr *hostNode;
|
||||
|
||||
if (host.getLength() == 0)
|
||||
{
|
||||
DEBUG_LOG(("ResolveIP(): Can't resolve NULL\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// String such as "127.0.0.1"
|
||||
if (isdigit(host.getCharAt(0)))
|
||||
{
|
||||
return ( ntohl(inet_addr(host.str())) );
|
||||
}
|
||||
|
||||
// String such as "localhost"
|
||||
hostStruct = gethostbyname(host.str());
|
||||
if (hostStruct == NULL)
|
||||
{
|
||||
DEBUG_LOG(("ResolveIP(): Can't resolve %s\n", host.str()));
|
||||
return 0;
|
||||
}
|
||||
hostNode = (struct in_addr *) hostStruct->h_addr;
|
||||
return ( ntohl(hostNode->s_addr) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next network command ID.
|
||||
*/
|
||||
UnsignedShort GenerateNextCommandID() {
|
||||
static UnsignedShort commandID = 64000;
|
||||
++commandID;
|
||||
return commandID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this type of command requires a unique command ID.
|
||||
*/
|
||||
Bool DoesCommandRequireACommandID(NetCommandType type) {
|
||||
if ((type == NETCOMMANDTYPE_GAMECOMMAND) ||
|
||||
(type == NETCOMMANDTYPE_FRAMEINFO) ||
|
||||
(type == NETCOMMANDTYPE_PLAYERLEAVE) ||
|
||||
(type == NETCOMMANDTYPE_DESTROYPLAYER) ||
|
||||
(type == NETCOMMANDTYPE_RUNAHEADMETRICS) ||
|
||||
(type == NETCOMMANDTYPE_RUNAHEAD) ||
|
||||
(type == NETCOMMANDTYPE_CHAT) ||
|
||||
(type == NETCOMMANDTYPE_DISCONNECTVOTE) ||
|
||||
(type == NETCOMMANDTYPE_LOADCOMPLETE) ||
|
||||
(type == NETCOMMANDTYPE_TIMEOUTSTART) ||
|
||||
(type == NETCOMMANDTYPE_WRAPPER) ||
|
||||
(type == NETCOMMANDTYPE_FILE) ||
|
||||
(type == NETCOMMANDTYPE_FILEANNOUNCE) ||
|
||||
(type == NETCOMMANDTYPE_FILEPROGRESS) ||
|
||||
(type == NETCOMMANDTYPE_DISCONNECTPLAYER) ||
|
||||
(type == NETCOMMANDTYPE_DISCONNECTFRAME) ||
|
||||
(type == NETCOMMANDTYPE_DISCONNECTSCREENOFF) ||
|
||||
(type == NETCOMMANDTYPE_FRAMERESENDREQUEST))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this type of network command requires an ack.
|
||||
*/
|
||||
Bool CommandRequiresAck(NetCommandMsg *msg) {
|
||||
if ((msg->getNetCommandType() == NETCOMMANDTYPE_GAMECOMMAND) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_FRAMEINFO) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_PLAYERLEAVE) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_DESTROYPLAYER) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_RUNAHEADMETRICS) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_RUNAHEAD) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_CHAT) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTVOTE) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTPLAYER) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_LOADCOMPLETE) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_TIMEOUTSTART) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_WRAPPER) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_FILE) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_FILEANNOUNCE) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_FILEPROGRESS) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTPLAYER) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTFRAME) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTSCREENOFF) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_FRAMERESENDREQUEST))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Bool IsCommandSynchronized(NetCommandType type) {
|
||||
if ((type == NETCOMMANDTYPE_GAMECOMMAND) ||
|
||||
(type == NETCOMMANDTYPE_FRAMEINFO) ||
|
||||
(type == NETCOMMANDTYPE_PLAYERLEAVE) ||
|
||||
(type == NETCOMMANDTYPE_DESTROYPLAYER) ||
|
||||
(type == NETCOMMANDTYPE_RUNAHEAD))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this type of network command requires the ack to be sent directly to the player
|
||||
* rather than going through the packet router. This should really only be used by commands
|
||||
* used on the disconnect screen.
|
||||
*/
|
||||
Bool CommandRequiresDirectSend(NetCommandMsg *msg) {
|
||||
if ((msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTVOTE) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTPLAYER) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_LOADCOMPLETE) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_TIMEOUTSTART) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_FILE) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_FILEANNOUNCE) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_FILEPROGRESS) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTFRAME) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTSCREENOFF) ||
|
||||
(msg->getNetCommandType() == NETCOMMANDTYPE_FRAMERESENDREQUEST)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
AsciiString GetAsciiNetCommandType(NetCommandType type) {
|
||||
AsciiString s;
|
||||
if (type == NETCOMMANDTYPE_FRAMEINFO) {
|
||||
s.set("NETCOMMANDTYPE_FRAMEINFO");
|
||||
} else if (type == NETCOMMANDTYPE_GAMECOMMAND) {
|
||||
s.set("NETCOMMANDTYPE_GAMECOMMAND");
|
||||
} else if (type == NETCOMMANDTYPE_PLAYERLEAVE) {
|
||||
s.set("NETCOMMANDTYPE_PLAYERLEAVE");
|
||||
} else if (type == NETCOMMANDTYPE_RUNAHEADMETRICS) {
|
||||
s.set("NETCOMMANDTYPE_RUNAHEADMETRICS");
|
||||
} else if (type == NETCOMMANDTYPE_RUNAHEAD) {
|
||||
s.set("NETCOMMANDTYPE_RUNAHEAD");
|
||||
} else if (type == NETCOMMANDTYPE_DESTROYPLAYER) {
|
||||
s.set("NETCOMMANDTYPE_DESTROYPLAYER");
|
||||
} else if (type == NETCOMMANDTYPE_ACKBOTH) {
|
||||
s.set("NETCOMMANDTYPE_ACKBOTH");
|
||||
} else if (type == NETCOMMANDTYPE_ACKSTAGE1) {
|
||||
s.set("NETCOMMANDTYPE_ACKSTAGE1");
|
||||
} else if (type == NETCOMMANDTYPE_ACKSTAGE2) {
|
||||
s.set("NETCOMMANDTYPE_ACKSTAGE2");
|
||||
} else if (type == NETCOMMANDTYPE_FRAMEINFO) {
|
||||
s.set("NETCOMMANDTYPE_FRAMEINFO");
|
||||
} else if (type == NETCOMMANDTYPE_KEEPALIVE) {
|
||||
s.set("NETCOMMANDTYPE_KEEPALIVE");
|
||||
} else if (type == NETCOMMANDTYPE_DISCONNECTCHAT) {
|
||||
s.set("NETCOMMANDTYPE_DISCONNECTCHAT");
|
||||
} else if (type == NETCOMMANDTYPE_CHAT) {
|
||||
s.set("NETCOMMANDTYPE_CHAT");
|
||||
} else if (type == NETCOMMANDTYPE_MANGLERQUERY) {
|
||||
s.set("NETCOMMANDTYPE_MANGLERQUERY");
|
||||
} else if (type == NETCOMMANDTYPE_MANGLERRESPONSE) {
|
||||
s.set("NETCOMMANDTYPE_MANGLERRESPONSE");
|
||||
} else if (type == NETCOMMANDTYPE_DISCONNECTKEEPALIVE) {
|
||||
s.set("NETCOMMANDTYPE_DISCONNECTKEEPALIVE");
|
||||
} else if (type == NETCOMMANDTYPE_DISCONNECTPLAYER) {
|
||||
s.set("NETCOMMANDTYPE_DISCONNECTPLAYER");
|
||||
} else if (type == NETCOMMANDTYPE_PACKETROUTERQUERY) {
|
||||
s.set("NETCOMMANDTYPE_PACKETROUTERQUERY");
|
||||
} else if (type == NETCOMMANDTYPE_PACKETROUTERACK) {
|
||||
s.set("NETCOMMANDTYPE_PACKETROUTERACK");
|
||||
} else if (type == NETCOMMANDTYPE_DISCONNECTVOTE) {
|
||||
s.set("NETCOMMANDTYPE_DISCONNECTVOTE");
|
||||
} else if (type == NETCOMMANDTYPE_PROGRESS) {
|
||||
s.set("NETCOMMANDTYPE_PROGRESS");
|
||||
} else if (type == NETCOMMANDTYPE_LOADCOMPLETE) {
|
||||
s.set("NETCOMMANDTYPE_LOADCOMPLETE");
|
||||
} else if (type == NETCOMMANDTYPE_TIMEOUTSTART) {
|
||||
s.set("NETCOMMANDTYPE_TIMEOUTSTART");
|
||||
} else if (type == NETCOMMANDTYPE_WRAPPER) {
|
||||
s.set("NETCOMMANDTYPE_WRAPPER");
|
||||
} else if (type == NETCOMMANDTYPE_FILE) {
|
||||
s.set("NETCOMMANDTYPE_FILE");
|
||||
} else if (type == NETCOMMANDTYPE_FILEANNOUNCE) {
|
||||
s.set("NETCOMMANDTYPE_FILEANNOUNCE");
|
||||
} else if (type == NETCOMMANDTYPE_FILEPROGRESS) {
|
||||
s.set("NETCOMMANDTYPE_FILEPROGRESS");
|
||||
} else if (type == NETCOMMANDTYPE_DISCONNECTFRAME) {
|
||||
s.set("NETCOMMANDTYPE_DISCONNECTFRAME");
|
||||
} else if (type == NETCOMMANDTYPE_DISCONNECTSCREENOFF) {
|
||||
s.set("NETCOMMANDTYPE_DISCONNECTSCREENOFF");
|
||||
} else if (type == NETCOMMANDTYPE_FRAMERESENDREQUEST) {
|
||||
s.set("NETCOMMANDTYPE_FRAMERESENDREQUEST");
|
||||
} else {
|
||||
s.set("UNKNOWN");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
508
Generals/Code/GameEngine/Source/GameNetwork/Transport.cpp
Normal file
508
Generals/Code/GameEngine/Source/GameNetwork/Transport.cpp
Normal file
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/CRC.h"
|
||||
#include "GameNetwork/Transport.h"
|
||||
#include "GameNetwork/NetworkInterface.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Packet-level encryption is an XOR operation, for speed reasons. To get
|
||||
// the max throughput, we only XOR whole 4-byte words, so the last bytes
|
||||
// can be non-XOR'd.
|
||||
|
||||
// This assumes the buf is a multiple of 4 bytes. Extra is not encrypted.
|
||||
static inline void encryptBuf( unsigned char *buf, Int len )
|
||||
{
|
||||
UnsignedInt mask = 0x0000Fade;
|
||||
|
||||
UnsignedInt *uintPtr = (UnsignedInt *) (buf);
|
||||
|
||||
for (int i=0 ; i<len/4 ; i++) {
|
||||
*uintPtr = (*uintPtr) ^ mask;
|
||||
*uintPtr = htonl(*uintPtr);
|
||||
uintPtr++;
|
||||
mask += 0x00000321; // just for fun
|
||||
}
|
||||
}
|
||||
|
||||
// This assumes the buf is a multiple of 4 bytes. Extra is not encrypted.
|
||||
static inline void decryptBuf( unsigned char *buf, Int len )
|
||||
{
|
||||
UnsignedInt mask = 0x0000Fade;
|
||||
|
||||
UnsignedInt *uintPtr = (UnsignedInt *) (buf);
|
||||
|
||||
for (int i=0 ; i<len/4 ; i++) {
|
||||
*uintPtr = htonl(*uintPtr);
|
||||
*uintPtr = (*uintPtr) ^ mask;
|
||||
uintPtr++;
|
||||
mask += 0x00000321; // just for fun
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
Transport::Transport(void)
|
||||
{
|
||||
m_winsockInit = false;
|
||||
m_udpsock = NULL;
|
||||
}
|
||||
|
||||
Transport::~Transport(void)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
Bool Transport::init( AsciiString ip, UnsignedShort port )
|
||||
{
|
||||
return init(ResolveIP(ip), port);
|
||||
}
|
||||
|
||||
Bool Transport::init( UnsignedInt ip, UnsignedShort port )
|
||||
{
|
||||
// ----- Initialize Winsock -----
|
||||
if (!m_winsockInit)
|
||||
{
|
||||
WORD verReq = MAKEWORD(2, 2);
|
||||
WSADATA wsadata;
|
||||
|
||||
int err = WSAStartup(verReq, &wsadata);
|
||||
if (err != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((LOBYTE(wsadata.wVersion) != 2) || (HIBYTE(wsadata.wVersion) !=2)) {
|
||||
WSACleanup();
|
||||
return false;
|
||||
}
|
||||
m_winsockInit = true;
|
||||
}
|
||||
|
||||
// ------- Bind our port --------
|
||||
if (m_udpsock)
|
||||
delete m_udpsock;
|
||||
m_udpsock = NEW UDP();
|
||||
|
||||
if (!m_udpsock)
|
||||
return false;
|
||||
|
||||
int retval = -1;
|
||||
time_t now = timeGetTime();
|
||||
while ((retval != 0) && ((timeGetTime() - now) < 1000)) {
|
||||
retval = m_udpsock->Bind(ip, port);
|
||||
}
|
||||
|
||||
if (retval != 0) {
|
||||
DEBUG_CRASH(("Could not bind to 0x%8.8X:%d\n", ip, port));
|
||||
DEBUG_LOG(("Transport::init - Failure to bind socket with error code %x\n", retval));
|
||||
delete m_udpsock;
|
||||
m_udpsock = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------- Clear buffers --------
|
||||
for (int i=0; i<MAX_MESSAGES; ++i)
|
||||
{
|
||||
m_outBuffer[i].length = 0;
|
||||
m_inBuffer[i].length = 0;
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
m_delayedInBuffer[i].message.length = 0;
|
||||
#endif
|
||||
}
|
||||
for (i=0; i<MAX_TRANSPORT_STATISTICS_SECONDS; ++i)
|
||||
{
|
||||
m_incomingBytes[i] = 0;
|
||||
m_outgoingBytes[i] = 0;
|
||||
m_unknownBytes[i] = 0;
|
||||
m_incomingPackets[i] = 0;
|
||||
m_outgoingPackets[i] = 0;
|
||||
m_unknownPackets[i] = 0;
|
||||
}
|
||||
m_statisticsSlot = 0;
|
||||
m_lastSecond = timeGetTime();
|
||||
|
||||
m_port = port;
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if (TheGlobalData->m_latencyAverage > 0 || TheGlobalData->m_latencyNoise)
|
||||
m_useLatency = true;
|
||||
|
||||
if (TheGlobalData->m_packetLoss)
|
||||
m_usePacketLoss = true;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Transport::reset( void )
|
||||
{
|
||||
if (m_udpsock)
|
||||
{
|
||||
delete m_udpsock;
|
||||
m_udpsock = NULL;
|
||||
}
|
||||
|
||||
if (m_winsockInit)
|
||||
{
|
||||
WSACleanup();
|
||||
m_winsockInit = false;
|
||||
}
|
||||
}
|
||||
|
||||
Bool Transport::update( void )
|
||||
{
|
||||
Bool retval = TRUE;
|
||||
if (doRecv() == FALSE && m_udpsock && m_udpsock->GetStatus() == UDP::ADDRNOTAVAIL)
|
||||
{
|
||||
retval = FALSE;
|
||||
}
|
||||
DEBUG_ASSERTLOG(retval, ("WSA error is %s\n", GetWSAErrorString(WSAGetLastError()).str()));
|
||||
if (doSend() == FALSE && m_udpsock && m_udpsock->GetStatus() == UDP::ADDRNOTAVAIL)
|
||||
{
|
||||
retval = FALSE;
|
||||
}
|
||||
DEBUG_ASSERTLOG(retval, ("WSA error is %s\n", GetWSAErrorString(WSAGetLastError()).str()));
|
||||
return retval;
|
||||
}
|
||||
|
||||
Bool Transport::doSend() {
|
||||
if (!m_udpsock)
|
||||
{
|
||||
DEBUG_LOG(("Transport::doSend() - m_udpSock is NULL!\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Bool retval = TRUE;
|
||||
|
||||
// Statistics gathering
|
||||
UnsignedInt now = timeGetTime();
|
||||
if (m_lastSecond + 1000 < now)
|
||||
{
|
||||
m_lastSecond = now;
|
||||
m_statisticsSlot = (m_statisticsSlot + 1) % MAX_TRANSPORT_STATISTICS_SECONDS;
|
||||
m_outgoingPackets[m_statisticsSlot] = 0;
|
||||
m_outgoingBytes[m_statisticsSlot] = 0;
|
||||
m_incomingPackets[m_statisticsSlot] = 0;
|
||||
m_incomingBytes[m_statisticsSlot] = 0;
|
||||
m_unknownPackets[m_statisticsSlot] = 0;
|
||||
m_unknownBytes[m_statisticsSlot] = 0;
|
||||
}
|
||||
|
||||
// Send all messages
|
||||
int i;
|
||||
for (i=0; i<MAX_MESSAGES; ++i)
|
||||
{
|
||||
if (m_outBuffer[i].length != 0)
|
||||
{
|
||||
int bytesSent = 0;
|
||||
// Send this message
|
||||
if ((bytesSent = m_udpsock->Write((unsigned char *)(&m_outBuffer[i]), m_outBuffer[i].length + sizeof(TransportMessageHeader), m_outBuffer[i].addr, m_outBuffer[i].port)) > 0)
|
||||
{
|
||||
//DEBUG_LOG(("Sending %d bytes to %d:%d\n", m_outBuffer[i].length + sizeof(TransportMessageHeader), m_outBuffer[i].addr, m_outBuffer[i].port));
|
||||
m_outgoingPackets[m_statisticsSlot]++;
|
||||
m_outgoingBytes[m_statisticsSlot] += m_outBuffer[i].length + sizeof(TransportMessageHeader);
|
||||
m_outBuffer[i].length = 0; // Remove from queue
|
||||
// DEBUG_LOG(("Transport::doSend - sent %d butes to %d.%d.%d.%d:%d\n", bytesSent,
|
||||
// (m_outBuffer[i].addr >> 24) & 0xff,
|
||||
// (m_outBuffer[i].addr >> 16) & 0xff,
|
||||
// (m_outBuffer[i].addr >> 8) & 0xff,
|
||||
// m_outBuffer[i].addr & 0xff,
|
||||
// m_outBuffer[i].port));
|
||||
}
|
||||
else
|
||||
{
|
||||
//DEBUG_LOG(("Could not write to socket!!! Not discarding message!\n"));
|
||||
retval = FALSE;
|
||||
//DEBUG_LOG(("Transport::doSend returning FALSE\n"));
|
||||
}
|
||||
}
|
||||
} // for (i=0; i<MAX_MESSAGES; ++i)
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
// Latency simulation - deliver anything we're holding on to that is ready
|
||||
if (m_useLatency)
|
||||
{
|
||||
for (i=0; i<MAX_MESSAGES; ++i)
|
||||
{
|
||||
if (m_delayedInBuffer[i].message.length != 0 && m_delayedInBuffer[i].deliveryTime <= now)
|
||||
{
|
||||
for (int j=0; j<MAX_MESSAGES; ++j)
|
||||
{
|
||||
if (m_inBuffer[j].length == 0)
|
||||
{
|
||||
// Empty slot; use it
|
||||
memcpy(&m_inBuffer[j], &m_delayedInBuffer[i].message, sizeof(TransportMessage));
|
||||
m_delayedInBuffer[i].message.length = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
Bool Transport::doRecv()
|
||||
{
|
||||
if (!m_udpsock)
|
||||
{
|
||||
DEBUG_LOG(("Transport::doRecv() - m_udpSock is NULL!\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Bool retval = TRUE;
|
||||
|
||||
// Read in anything on our socket
|
||||
sockaddr_in from;
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
UnsignedInt now = timeGetTime();
|
||||
#endif
|
||||
|
||||
TransportMessage incomingMessage;
|
||||
unsigned char *buf = (unsigned char *)&incomingMessage;
|
||||
int len = MAX_MESSAGE_LEN;
|
||||
// DEBUG_LOG(("Transport::doRecv - checking\n"));
|
||||
while ( (len=m_udpsock->Read(buf, MAX_MESSAGE_LEN, &from)) > 0 )
|
||||
{
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
// Packet loss simulation
|
||||
if (m_usePacketLoss)
|
||||
{
|
||||
if ( TheGlobalData->m_packetLoss >= GameClientRandomValue(0, 100) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// DEBUG_LOG(("Transport::doRecv - Got something! len = %d\n", len));
|
||||
// Decrypt the packet
|
||||
// DEBUG_LOG(("buffer = "));
|
||||
// for (Int munkee = 0; munkee < len; ++munkee) {
|
||||
// DEBUG_LOG(("%02x", *(buf + munkee)));
|
||||
// }
|
||||
// DEBUG_LOG(("\n"));
|
||||
decryptBuf(buf, len);
|
||||
|
||||
incomingMessage.length = len - sizeof(TransportMessageHeader);
|
||||
|
||||
if (len <= sizeof(TransportMessageHeader) || !isGeneralsPacket( &incomingMessage ))
|
||||
{
|
||||
m_unknownPackets[m_statisticsSlot]++;
|
||||
m_unknownBytes[m_statisticsSlot] += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Something there; stick it somewhere
|
||||
// DEBUG_LOG(("Saw %d bytes from %d:%d\n", len, ntohl(from.sin_addr.S_un.S_addr), ntohs(from.sin_port)));
|
||||
m_incomingPackets[m_statisticsSlot]++;
|
||||
m_incomingBytes[m_statisticsSlot] += len;
|
||||
|
||||
for (int i=0; i<MAX_MESSAGES; ++i)
|
||||
{
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
// Latency simulation
|
||||
if (m_useLatency)
|
||||
{
|
||||
if (m_delayedInBuffer[i].message.length == 0)
|
||||
{
|
||||
// Empty slot; use it
|
||||
m_delayedInBuffer[i].deliveryTime =
|
||||
now + TheGlobalData->m_latencyAverage +
|
||||
(Int)(TheGlobalData->m_latencyAmplitude * sin(now * TheGlobalData->m_latencyPeriod)) +
|
||||
GameClientRandomValue(-TheGlobalData->m_latencyNoise, TheGlobalData->m_latencyNoise);
|
||||
m_delayedInBuffer[i].message.length = incomingMessage.length;
|
||||
m_delayedInBuffer[i].message.addr = ntohl(from.sin_addr.S_un.S_addr);
|
||||
m_delayedInBuffer[i].message.port = ntohs(from.sin_port);
|
||||
memcpy(&m_delayedInBuffer[i].message, buf, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
if (m_inBuffer[i].length == 0)
|
||||
{
|
||||
// Empty slot; use it
|
||||
m_inBuffer[i].length = incomingMessage.length;
|
||||
m_inBuffer[i].addr = ntohl(from.sin_addr.S_un.S_addr);
|
||||
m_inBuffer[i].port = ntohs(from.sin_port);
|
||||
memcpy(&m_inBuffer[i], buf, len);
|
||||
break;
|
||||
}
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
//DEBUG_ASSERTCRASH(i<MAX_MESSAGES, ("Message lost!"));
|
||||
}
|
||||
|
||||
if (len == -1) {
|
||||
// there was a socket error trying to perform a read.
|
||||
//DEBUG_LOG(("Transport::doRecv returning FALSE\n"));
|
||||
retval = FALSE;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
Bool Transport::queueSend(UnsignedInt addr, UnsignedShort port, const UnsignedByte *buf, Int len /*,
|
||||
NetMessageFlags flags, Int id */)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (len < 1 || len > MAX_PACKET_SIZE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i=0; i<MAX_MESSAGES; ++i)
|
||||
{
|
||||
if (m_outBuffer[i].length == 0)
|
||||
{
|
||||
// Insert data here
|
||||
m_outBuffer[i].length = len;
|
||||
memcpy(m_outBuffer[i].data, buf, len);
|
||||
m_outBuffer[i].addr = addr;
|
||||
m_outBuffer[i].port = port;
|
||||
// m_outBuffer[i].header.flags = flags;
|
||||
// m_outBuffer[i].header.id = id;
|
||||
m_outBuffer[i].header.magic = GENERALS_MAGIC_NUMBER;
|
||||
|
||||
CRC crc;
|
||||
crc.computeCRC( (unsigned char *)(&(m_outBuffer[i].header.magic)), m_outBuffer[i].length + sizeof(TransportMessageHeader) - sizeof(UnsignedInt) );
|
||||
// DEBUG_LOG(("About to assign the CRC for the packet\n"));
|
||||
m_outBuffer[i].header.crc = crc.get();
|
||||
|
||||
// Encrypt packet
|
||||
// DEBUG_LOG(("buffer: "));
|
||||
encryptBuf((unsigned char *)&m_outBuffer[i], len + sizeof(TransportMessageHeader));
|
||||
// DEBUG_LOG(("\n"));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Bool Transport::isGeneralsPacket( TransportMessage *msg )
|
||||
{
|
||||
if (!msg)
|
||||
return false;
|
||||
|
||||
if (msg->length < 0 || msg->length > MAX_MESSAGE_LEN)
|
||||
return false;
|
||||
|
||||
CRC crc;
|
||||
// crc.computeCRC( (unsigned char *)msg->data, msg->length );
|
||||
crc.computeCRC( (unsigned char *)(&(msg->header.magic)), msg->length + sizeof(TransportMessageHeader) - sizeof(UnsignedInt) );
|
||||
|
||||
if (crc.get() != msg->header.crc)
|
||||
return false;
|
||||
|
||||
if (msg->header.magic != GENERALS_MAGIC_NUMBER)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Statistics ---------------------------------------------------
|
||||
Real Transport::getIncomingBytesPerSecond( void )
|
||||
{
|
||||
Real val = 0.0;
|
||||
for (int i=0; i<MAX_TRANSPORT_STATISTICS_SECONDS; ++i)
|
||||
{
|
||||
if (i != m_statisticsSlot)
|
||||
val += m_incomingBytes[i];
|
||||
}
|
||||
return val / (MAX_TRANSPORT_STATISTICS_SECONDS-1);
|
||||
}
|
||||
|
||||
Real Transport::getIncomingPacketsPerSecond( void )
|
||||
{
|
||||
Real val = 0.0;
|
||||
for (int i=0; i<MAX_TRANSPORT_STATISTICS_SECONDS; ++i)
|
||||
{
|
||||
if (i != m_statisticsSlot)
|
||||
val += m_incomingPackets[i];
|
||||
}
|
||||
return val / (MAX_TRANSPORT_STATISTICS_SECONDS-1);
|
||||
}
|
||||
|
||||
Real Transport::getOutgoingBytesPerSecond( void )
|
||||
{
|
||||
Real val = 0.0;
|
||||
for (int i=0; i<MAX_TRANSPORT_STATISTICS_SECONDS; ++i)
|
||||
{
|
||||
if (i != m_statisticsSlot)
|
||||
val += m_outgoingBytes[i];
|
||||
}
|
||||
return val / (MAX_TRANSPORT_STATISTICS_SECONDS-1);
|
||||
}
|
||||
|
||||
Real Transport::getOutgoingPacketsPerSecond( void )
|
||||
{
|
||||
Real val = 0.0;
|
||||
for (int i=0; i<MAX_TRANSPORT_STATISTICS_SECONDS; ++i)
|
||||
{
|
||||
if (i != m_statisticsSlot)
|
||||
val += m_outgoingPackets[i];
|
||||
}
|
||||
return val / (MAX_TRANSPORT_STATISTICS_SECONDS-1);
|
||||
}
|
||||
|
||||
Real Transport::getUnknownBytesPerSecond( void )
|
||||
{
|
||||
Real val = 0.0;
|
||||
for (int i=0; i<MAX_TRANSPORT_STATISTICS_SECONDS; ++i)
|
||||
{
|
||||
if (i != m_statisticsSlot)
|
||||
val += m_unknownBytes[i];
|
||||
}
|
||||
return val / (MAX_TRANSPORT_STATISTICS_SECONDS-1);
|
||||
}
|
||||
|
||||
Real Transport::getUnknownPacketsPerSecond( void )
|
||||
{
|
||||
Real val = 0.0;
|
||||
for (int i=0; i<MAX_TRANSPORT_STATISTICS_SECONDS; ++i)
|
||||
{
|
||||
if (i != m_statisticsSlot)
|
||||
val += m_unknownPackets[i];
|
||||
}
|
||||
return val / (MAX_TRANSPORT_STATISTICS_SECONDS-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
74
Generals/Code/GameEngine/Source/GameNetwork/User.cpp
Normal file
74
Generals/Code/GameEngine/Source/GameNetwork/User.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// User class copy and comparisons
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameNetwork/User.h"
|
||||
|
||||
/**
|
||||
* Constructor. Sets up the member variables with the appropriate values.
|
||||
*/
|
||||
User::User(UnicodeString name, UnsignedInt addr, UnsignedInt port) {
|
||||
m_name.set(name);
|
||||
m_ipaddr = addr;
|
||||
m_port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* The assignment operator.
|
||||
*/
|
||||
User & User::operator= (const User *other)
|
||||
{
|
||||
m_name = other->m_name;
|
||||
m_ipaddr = other->m_ipaddr;
|
||||
m_port = other->m_port;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The equality operator.
|
||||
*/
|
||||
Bool User::operator== (const User *other)
|
||||
{
|
||||
return (m_name.compare(other->m_name) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The inequality operator.
|
||||
*/
|
||||
Bool User::operator!= (const User *other)
|
||||
{
|
||||
return (m_name.compare(other->m_name) != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of this user.
|
||||
*/
|
||||
void User::setName(UnicodeString name) {
|
||||
m_name.set(name);
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* NAME
|
||||
* $Archive: $
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* PROGRAMMER
|
||||
* Bryan Cleveland
|
||||
* $Author: $
|
||||
*
|
||||
* VERSION INFO
|
||||
* $Revision: $
|
||||
* $Modtime: $
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
//#include "WinMain.h"
|
||||
#include "GameNetwork/WOLBrowser/WebBrowser.h"
|
||||
#include "GameClient/GameWindow.h"
|
||||
#include "GameClient/Display.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
/**
|
||||
* OLEInitializer class - Init and shutdown OLE & COM as a global
|
||||
* object. Scary, nasty stuff, COM. /me shivers.
|
||||
*/
|
||||
class OLEInitializer
|
||||
{
|
||||
public:
|
||||
OLEInitializer()
|
||||
{
|
||||
// Initialize this instance
|
||||
OleInitialize(NULL);
|
||||
}
|
||||
~OLEInitializer()
|
||||
{
|
||||
OleUninitialize();
|
||||
}
|
||||
};
|
||||
OLEInitializer g_OLEInitializer;
|
||||
CComModule _Module;
|
||||
|
||||
CComObject<WebBrowser> * TheWebBrowser = NULL;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* NAME
|
||||
* WebBrowser::WebBrowser
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Default constructor
|
||||
*
|
||||
* INPUTS
|
||||
* NONE
|
||||
*
|
||||
* RESULT
|
||||
* NONE
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
WebBrowser::WebBrowser() :
|
||||
mRefCount(1)
|
||||
{
|
||||
DEBUG_LOG(("Instantiating embedded WebBrowser\n"));
|
||||
m_urlList = NULL;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* NAME
|
||||
* WebBrowser::~WebBrowser
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Destructor
|
||||
*
|
||||
* INPUTS
|
||||
* NONE
|
||||
*
|
||||
* RESULT
|
||||
* NONE
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
WebBrowser::~WebBrowser()
|
||||
{
|
||||
DEBUG_LOG(("Destructing embedded WebBrowser\n"));
|
||||
if (this == TheWebBrowser) {
|
||||
DEBUG_LOG(("WebBrowser::~WebBrowser - setting TheWebBrowser to NULL\n"));
|
||||
TheWebBrowser = NULL;
|
||||
}
|
||||
WebBrowserURL *url = m_urlList;
|
||||
while (url != NULL) {
|
||||
WebBrowserURL *temp = url;
|
||||
url = url->m_next;
|
||||
temp->deleteInstance();
|
||||
temp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The INI data fields for Webpage URL's */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const FieldParse WebBrowserURL::m_URLFieldParseTable[] =
|
||||
{
|
||||
|
||||
{ "URL", INI::parseAsciiString, NULL, offsetof( WebBrowserURL, m_url ) },
|
||||
{ NULL, NULL, NULL, 0 },
|
||||
|
||||
};
|
||||
|
||||
WebBrowserURL::WebBrowserURL()
|
||||
{
|
||||
m_next = NULL;
|
||||
m_tag.clear();
|
||||
m_url.clear();
|
||||
}
|
||||
|
||||
WebBrowserURL::~WebBrowserURL()
|
||||
{
|
||||
}
|
||||
/******************************************************************************
|
||||
*
|
||||
* NAME
|
||||
* WebBrowser::init
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Perform post creation initialization.
|
||||
*
|
||||
* INPUTS
|
||||
* NONE
|
||||
*
|
||||
* RESULT
|
||||
* NONE
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
void WebBrowser::init()
|
||||
{
|
||||
m_urlList = NULL;
|
||||
INI ini;
|
||||
ini.load( AsciiString( "Data\\INI\\Webpages.ini" ), INI_LOAD_OVERWRITE, NULL );
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* NAME
|
||||
* WebBrowser::reset
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Perform post creation initialization.
|
||||
*
|
||||
* INPUTS
|
||||
* NONE
|
||||
*
|
||||
* RESULT
|
||||
* NONE
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
void WebBrowser::reset()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowser::update( void )
|
||||
{
|
||||
}
|
||||
|
||||
WebBrowserURL * WebBrowser::findURL(AsciiString tag)
|
||||
{
|
||||
WebBrowserURL *retval = m_urlList;
|
||||
|
||||
while ((retval != NULL) && tag.compareNoCase(retval->m_tag.str()))
|
||||
{
|
||||
retval = retval->m_next;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
WebBrowserURL * WebBrowser::makeNewURL(AsciiString tag)
|
||||
{
|
||||
WebBrowserURL *newURL = newInstance(WebBrowserURL);
|
||||
|
||||
newURL->m_tag = tag;
|
||||
|
||||
newURL->m_next = m_urlList;
|
||||
m_urlList = newURL;
|
||||
|
||||
return newURL;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* NAME
|
||||
* IUnknown::QueryInterface
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* INPUTS
|
||||
* IID - Interface ID
|
||||
*
|
||||
* RESULT
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
STDMETHODIMP WebBrowser::QueryInterface(REFIID iid, void** ppv)
|
||||
{
|
||||
*ppv = NULL;
|
||||
|
||||
if ((iid == IID_IUnknown) || (iid == IID_IBrowserDispatch))
|
||||
{
|
||||
*ppv = static_cast<IBrowserDispatch*>(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
static_cast<IUnknown*>(*ppv)->AddRef();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* NAME
|
||||
* IUnknown::AddRef
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* INPUTS
|
||||
* NONE
|
||||
*
|
||||
* RESULT
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
ULONG STDMETHODCALLTYPE WebBrowser::AddRef(void)
|
||||
{
|
||||
return ++mRefCount;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* NAME
|
||||
* IUnknown::Release
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* INPUTS
|
||||
* NONE
|
||||
*
|
||||
* RESULT
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
ULONG STDMETHODCALLTYPE WebBrowser::Release(void)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(mRefCount > 0, ("Negative reference count"));
|
||||
--mRefCount;
|
||||
|
||||
if (mRefCount == 0)
|
||||
{
|
||||
DEBUG_LOG(("WebBrowser::Release - all references released, deleting the object.\n"));
|
||||
if (this == TheWebBrowser) {
|
||||
TheWebBrowser = NULL;
|
||||
}
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mRefCount;
|
||||
}
|
||||
|
||||
STDMETHODIMP WebBrowser::TestMethod(Int num1)
|
||||
{
|
||||
DEBUG_LOG(("WebBrowser::TestMethod - num1 = %d\n", num1));
|
||||
return S_OK;
|
||||
}
|
||||
513
Generals/Code/GameEngine/Source/GameNetwork/udp.cpp
Normal file
513
Generals/Code/GameEngine/Source/GameNetwork/udp.cpp
Normal file
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: Udp.cpp //////////////////////////////////////////////////////////////
|
||||
// Implementation of UDP socket wrapper class (taken from wnet lib)
|
||||
// Author: Matthew D. Campbell, July 2001
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////
|
||||
#include "Common/GameEngine.h"
|
||||
//#include "GameNetwork/NetworkInterface.h"
|
||||
#include "GameNetwork/udp.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
|
||||
#define CASE(x) case (x): return #x;
|
||||
|
||||
AsciiString GetWSAErrorString( Int error )
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
CASE(WSABASEERR)
|
||||
CASE(WSAEINTR)
|
||||
CASE(WSAEBADF)
|
||||
CASE(WSAEACCES)
|
||||
CASE(WSAEFAULT)
|
||||
CASE(WSAEINVAL)
|
||||
CASE(WSAEMFILE)
|
||||
CASE(WSAEWOULDBLOCK)
|
||||
CASE(WSAEINPROGRESS)
|
||||
CASE(WSAEALREADY)
|
||||
CASE(WSAENOTSOCK)
|
||||
CASE(WSAEDESTADDRREQ)
|
||||
CASE(WSAEMSGSIZE)
|
||||
CASE(WSAEPROTOTYPE)
|
||||
CASE(WSAENOPROTOOPT)
|
||||
CASE(WSAEPROTONOSUPPORT)
|
||||
CASE(WSAESOCKTNOSUPPORT)
|
||||
CASE(WSAEOPNOTSUPP)
|
||||
CASE(WSAEPFNOSUPPORT)
|
||||
CASE(WSAEAFNOSUPPORT)
|
||||
CASE(WSAEADDRINUSE)
|
||||
CASE(WSAEADDRNOTAVAIL)
|
||||
CASE(WSAENETDOWN)
|
||||
CASE(WSAENETUNREACH)
|
||||
CASE(WSAENETRESET)
|
||||
CASE(WSAECONNABORTED)
|
||||
CASE(WSAECONNRESET)
|
||||
CASE(WSAENOBUFS)
|
||||
CASE(WSAEISCONN)
|
||||
CASE(WSAENOTCONN)
|
||||
CASE(WSAESHUTDOWN)
|
||||
CASE(WSAETOOMANYREFS)
|
||||
CASE(WSAETIMEDOUT)
|
||||
CASE(WSAECONNREFUSED)
|
||||
CASE(WSAELOOP)
|
||||
CASE(WSAENAMETOOLONG)
|
||||
CASE(WSAEHOSTDOWN)
|
||||
CASE(WSAEHOSTUNREACH)
|
||||
CASE(WSAENOTEMPTY)
|
||||
CASE(WSAEPROCLIM)
|
||||
CASE(WSAEUSERS)
|
||||
CASE(WSAEDQUOT)
|
||||
CASE(WSAESTALE)
|
||||
CASE(WSAEREMOTE)
|
||||
CASE(WSAEDISCON)
|
||||
CASE(WSASYSNOTREADY)
|
||||
CASE(WSAVERNOTSUPPORTED)
|
||||
CASE(WSANOTINITIALISED)
|
||||
CASE(WSAHOST_NOT_FOUND)
|
||||
CASE(WSATRY_AGAIN)
|
||||
CASE(WSANO_RECOVERY)
|
||||
CASE(WSANO_DATA)
|
||||
default:
|
||||
{
|
||||
AsciiString ret;
|
||||
ret.format("Not a Winsock error (%d)", error);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return AsciiString::TheEmptyString; // will not be hit, ever.
|
||||
}
|
||||
|
||||
#undef CASE
|
||||
|
||||
#endif // defined(_DEBUG) || defined(_INTERNAL)
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
UDP::UDP()
|
||||
{
|
||||
fd=0;
|
||||
}
|
||||
|
||||
UDP::~UDP()
|
||||
{
|
||||
if (fd)
|
||||
closesocket(fd);
|
||||
}
|
||||
|
||||
Int UDP::Bind(const char *Host,UnsignedShort port)
|
||||
{
|
||||
char hostName[100];
|
||||
struct hostent *hostStruct;
|
||||
struct in_addr *hostNode;
|
||||
|
||||
if (isdigit(Host[0]))
|
||||
return ( Bind( ntohl(inet_addr(Host)), port) );
|
||||
|
||||
strcpy(hostName, Host);
|
||||
|
||||
hostStruct = gethostbyname(Host);
|
||||
if (hostStruct == NULL)
|
||||
return (0);
|
||||
hostNode = (struct in_addr *) hostStruct->h_addr;
|
||||
return ( Bind(ntohl(hostNode->s_addr),port) );
|
||||
}
|
||||
|
||||
// You must call bind, implicit binding is for sissies
|
||||
// Well... you can get implicit binding if you pass 0 for either arg
|
||||
Int UDP::Bind(UnsignedInt IP,UnsignedShort Port)
|
||||
{
|
||||
int retval;
|
||||
int status;
|
||||
|
||||
IP=htonl(IP);
|
||||
Port=htons(Port);
|
||||
|
||||
addr.sin_family=AF_INET;
|
||||
addr.sin_port=Port;
|
||||
addr.sin_addr.s_addr=IP;
|
||||
fd=socket(AF_INET,SOCK_DGRAM,DEFAULT_PROTOCOL);
|
||||
#ifdef _WINDOWS
|
||||
if (fd==SOCKET_ERROR)
|
||||
fd=-1;
|
||||
#endif
|
||||
if (fd==-1)
|
||||
return(UNKNOWN);
|
||||
|
||||
retval=bind(fd,(struct sockaddr *)&addr,sizeof(addr));
|
||||
|
||||
#ifdef _WINDOWS
|
||||
if (retval==SOCKET_ERROR)
|
||||
{
|
||||
retval=-1;
|
||||
m_lastError = WSAGetLastError();
|
||||
}
|
||||
#endif
|
||||
if (retval==-1)
|
||||
{
|
||||
status=GetStatus();
|
||||
//CERR("Bind failure (" << status << ") IP " << IP << " PORT " << Port )
|
||||
return(status);
|
||||
}
|
||||
|
||||
int namelen=sizeof(addr);
|
||||
getsockname(fd, (struct sockaddr *)&addr, &namelen);
|
||||
|
||||
myIP=ntohl(addr.sin_addr.s_addr);
|
||||
myPort=ntohs(addr.sin_port);
|
||||
|
||||
retval=SetBlocking(FALSE);
|
||||
if (retval==-1)
|
||||
fprintf(stderr,"Couldn't set nonblocking mode!\n");
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
Int UDP::getLocalAddr(UnsignedInt &ip, UnsignedShort &port)
|
||||
{
|
||||
ip=myIP;
|
||||
port=myPort;
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
// private function
|
||||
Int UDP::SetBlocking(Int block)
|
||||
{
|
||||
#ifdef _WINDOWS
|
||||
unsigned long flag=1;
|
||||
if (block)
|
||||
flag=0;
|
||||
int retval;
|
||||
retval=ioctlsocket(fd,FIONBIO,&flag);
|
||||
if (retval==SOCKET_ERROR)
|
||||
return(UNKNOWN);
|
||||
else
|
||||
return(OK);
|
||||
#else // UNIX
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if (block==FALSE) // set nonblocking
|
||||
flags |= O_NONBLOCK;
|
||||
else // set blocking
|
||||
flags &= ~(O_NONBLOCK);
|
||||
|
||||
if (fcntl(fd, F_SETFL, flags) < 0)
|
||||
{
|
||||
return(UNKNOWN);
|
||||
}
|
||||
return(OK);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Int UDP::Write(const unsigned char *msg,UnsignedInt len,UnsignedInt IP,UnsignedShort port)
|
||||
{
|
||||
Int retval;
|
||||
struct sockaddr_in to;
|
||||
|
||||
// This happens frequently
|
||||
if ((IP==0)||(port==0)) return(ADDRNOTAVAIL);
|
||||
|
||||
#ifdef _UNIX
|
||||
errno=0;
|
||||
#endif
|
||||
to.sin_port=htons(port);
|
||||
to.sin_addr.s_addr=htonl(IP);
|
||||
to.sin_family=AF_INET;
|
||||
|
||||
ClearStatus();
|
||||
retval=sendto(fd,(const char *)msg,len,0,(struct sockaddr *)&to,sizeof(to));
|
||||
#ifdef _WINDOWS
|
||||
if (retval==SOCKET_ERROR)
|
||||
{
|
||||
retval=-1;
|
||||
m_lastError = WSAGetLastError();
|
||||
#ifdef DEBUG_LOGGING
|
||||
static Int errCount = 0;
|
||||
#endif
|
||||
DEBUG_ASSERTLOG(errCount++ > 100, ("UDP::Write() - WSA error is %s\n", GetWSAErrorString(WSAGetLastError()).str()));
|
||||
}
|
||||
#endif
|
||||
|
||||
return(retval);
|
||||
}
|
||||
|
||||
Int UDP::Read(unsigned char *msg,UnsignedInt len,sockaddr_in *from)
|
||||
{
|
||||
Int retval;
|
||||
int alen=sizeof(sockaddr_in);
|
||||
|
||||
if (from!=NULL)
|
||||
{
|
||||
retval=recvfrom(fd,(char *)msg,len,0,(struct sockaddr *)from,&alen);
|
||||
#ifdef _WINDOWS
|
||||
if (retval == SOCKET_ERROR)
|
||||
{
|
||||
if (WSAGetLastError() != WSAEWOULDBLOCK)
|
||||
{
|
||||
// failing because of a blocking error isn't really such a bad thing.
|
||||
m_lastError = WSAGetLastError();
|
||||
#ifdef DEBUG_LOGGING
|
||||
static Int errCount = 0;
|
||||
#endif
|
||||
DEBUG_ASSERTLOG(errCount++ > 100, ("UDP::Read() - WSA error is %s\n", GetWSAErrorString(WSAGetLastError()).str()));
|
||||
retval = -1;
|
||||
} else {
|
||||
retval = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
retval=recvfrom(fd,(char *)msg,len,0,NULL,NULL);
|
||||
#ifdef _WINDOWS
|
||||
if (retval==SOCKET_ERROR)
|
||||
{
|
||||
if (WSAGetLastError() != WSAEWOULDBLOCK)
|
||||
{
|
||||
// failing because of a blocking error isn't really such a bad thing.
|
||||
m_lastError = WSAGetLastError();
|
||||
#ifdef DEBUG_LOGGING
|
||||
static Int errCount = 0;
|
||||
#endif
|
||||
DEBUG_ASSERTLOG(errCount++ > 100, ("UDP::Read() - WSA error is %s\n", GetWSAErrorString(WSAGetLastError()).str()));
|
||||
retval = -1;
|
||||
} else {
|
||||
retval = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
|
||||
|
||||
void UDP::ClearStatus(void)
|
||||
{
|
||||
#ifndef _WINDOWS
|
||||
errno=0;
|
||||
#endif
|
||||
|
||||
m_lastError = 0;
|
||||
}
|
||||
|
||||
UDP::sockStat UDP::GetStatus(void)
|
||||
{
|
||||
Int status = m_lastError;
|
||||
#ifdef _WINDOWS
|
||||
//int status=WSAGetLastError();
|
||||
if (status==0) return(OK);
|
||||
else if (status==WSAEINTR) return(INTR);
|
||||
else if (status==WSAEINPROGRESS) return(INPROGRESS);
|
||||
else if (status==WSAECONNREFUSED) return(CONNREFUSED);
|
||||
else if (status==WSAEINVAL) return(INVAL);
|
||||
else if (status==WSAEISCONN) return(ISCONN);
|
||||
else if (status==WSAENOTSOCK) return(NOTSOCK);
|
||||
else if (status==WSAETIMEDOUT) return(TIMEDOUT);
|
||||
else if (status==WSAEALREADY) return(ALREADY);
|
||||
else if (status==WSAEWOULDBLOCK) return(WOULDBLOCK);
|
||||
else if (status==WSAEBADF) return(BADF);
|
||||
else return((UDP::sockStat)status);
|
||||
#else
|
||||
//int status=errno;
|
||||
if (status==0) return(OK);
|
||||
else if (status==EINTR) return(INTR);
|
||||
else if (status==EINPROGRESS) return(INPROGRESS);
|
||||
else if (status==ECONNREFUSED) return(CONNREFUSED);
|
||||
else if (status==EINVAL) return(INVAL);
|
||||
else if (status==EISCONN) return(ISCONN);
|
||||
else if (status==ENOTSOCK) return(NOTSOCK);
|
||||
else if (status==ETIMEDOUT) return(TIMEDOUT);
|
||||
else if (status==EALREADY) return(ALREADY);
|
||||
else if (status==EAGAIN) return(AGAIN);
|
||||
else if (status==EWOULDBLOCK) return(WOULDBLOCK);
|
||||
else if (status==EBADF) return(BADF);
|
||||
else return(UNKNOWN);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
//
|
||||
// Wait for net activity on this socket
|
||||
//
|
||||
int UDP::Wait(Int sec,Int usec,fd_set &returnSet)
|
||||
{
|
||||
fd_set inputSet;
|
||||
|
||||
FD_ZERO(&inputSet);
|
||||
FD_SET(fd,&inputSet);
|
||||
|
||||
return(Wait(sec,usec,inputSet,returnSet));
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
//
|
||||
// Wait for net activity on a list of sockets
|
||||
//
|
||||
int UDP::Wait(Int sec,Int usec,fd_set &givenSet,fd_set &returnSet)
|
||||
{
|
||||
Wtime timeout,timenow,timethen;
|
||||
fd_set backupSet;
|
||||
int retval=0,done,givenMax;
|
||||
Bool noTimeout=FALSE;
|
||||
timeval tv;
|
||||
|
||||
returnSet=givenSet;
|
||||
backupSet=returnSet;
|
||||
|
||||
if ((sec==-1)&&(usec==-1))
|
||||
noTimeout=TRUE;
|
||||
|
||||
timeout.SetSec(sec);
|
||||
timeout.SetUsec(usec);
|
||||
timethen+=timeout;
|
||||
|
||||
givenMax=fd;
|
||||
for (UnsignedInt i=0; i<(sizeof(fd_set)*8); i++) // i=maxFD+1
|
||||
{
|
||||
if (FD_ISSET(i,&givenSet))
|
||||
givenMax=i;
|
||||
}
|
||||
///DBGMSG("WAIT fd="<<fd<<" givenMax="<<givenMax);
|
||||
|
||||
done=0;
|
||||
while( ! done)
|
||||
{
|
||||
if (noTimeout)
|
||||
retval=select(givenMax+1,&returnSet,0,0,NULL);
|
||||
else
|
||||
{
|
||||
timeout.GetTimevalMT(tv);
|
||||
retval=select(givenMax+1,&returnSet,0,0,&tv);
|
||||
}
|
||||
|
||||
if (retval>=0)
|
||||
done=1;
|
||||
|
||||
else if ((retval==-1)&&(errno==EINTR)) // in case of signal
|
||||
{
|
||||
if (noTimeout==FALSE)
|
||||
{
|
||||
timenow.Update();
|
||||
timeout=timethen-timenow;
|
||||
}
|
||||
if ((noTimeout==FALSE)&&(timenow.GetSec()==0)&&(timenow.GetUsec()==0))
|
||||
done=1;
|
||||
else
|
||||
returnSet=backupSet;
|
||||
}
|
||||
else // maybe out of memory?
|
||||
{
|
||||
done=1;
|
||||
}
|
||||
}
|
||||
///DBGMSG("Wait retval: "<<retval);
|
||||
return(retval);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
// Set the kernel buffer sizes for incoming, and outgoing packets
|
||||
//
|
||||
// Linux seems to have a buffer max of 32767 bytes for this,
|
||||
// (which is the default). If you try and set the size to
|
||||
// greater than the default it just sets it to 32767.
|
||||
|
||||
Int UDP::SetInputBuffer(UnsignedInt bytes)
|
||||
{
|
||||
int retval,arg=bytes;
|
||||
|
||||
retval=setsockopt(fd,SOL_SOCKET,SO_RCVBUF,
|
||||
(char *)&arg,sizeof(int));
|
||||
if (retval==0)
|
||||
return(TRUE);
|
||||
else
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
// Same note goes for the output buffer
|
||||
|
||||
Int UDP::SetOutputBuffer(UnsignedInt bytes)
|
||||
{
|
||||
int retval,arg=bytes;
|
||||
|
||||
retval=setsockopt(fd,SOL_SOCKET,SO_SNDBUF,
|
||||
(char *)&arg,sizeof(int));
|
||||
if (retval==0)
|
||||
return(TRUE);
|
||||
else
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
// Get the system buffer sizes
|
||||
|
||||
int UDP::GetInputBuffer(void)
|
||||
{
|
||||
int retval,arg=0,len=sizeof(int);
|
||||
|
||||
retval=getsockopt(fd,SOL_SOCKET,SO_RCVBUF,
|
||||
(char *)&arg,&len);
|
||||
return(arg);
|
||||
}
|
||||
|
||||
|
||||
int UDP::GetOutputBuffer(void)
|
||||
{
|
||||
int retval,arg=0,len=sizeof(int);
|
||||
|
||||
retval=getsockopt(fd,SOL_SOCKET,SO_SNDBUF,
|
||||
(char *)&arg,&len);
|
||||
return(arg);
|
||||
}
|
||||
|
||||
Int UDP::AllowBroadcasts(Bool status)
|
||||
{
|
||||
int retval;
|
||||
BOOL val = status;
|
||||
retval = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *)&val, sizeof(BOOL));
|
||||
if (retval == 0)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
Reference in New Issue
Block a user