/*
** Command & Conquer Renegade(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 .
*/
/******************************************************************************
*
* FILE
* $Archive: /Commando/Code/Commando/WOLQuickMatch.cpp $
*
* DESCRIPTION
*
* PROGRAMMER
* $Author: Denzil_l $
*
* VERSION INFO
* $Revision: 37 $
* $Modtime: 2/20/02 5:30p $
*
******************************************************************************/
#include "WOLQuickMatch.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include "cnetwork.h"
#include "translatedb.h"
#include "string_ids.h"
using namespace WWOnline;
typedef void (*QMDispatchFunc)(WOLQuickMatch*, const wchar_t*);
struct QMResponseDispatch
{
const wchar_t* Token;
QMDispatchFunc Dispatch;
};
#define QUICKMATCH_CHANNELNAME L"lob_39_0"
#define QUICKMATCH_BOTNAME L"matchbot"
/******************************************************************************
*
* NAME
* WOLQuickMatch::Create
*
* DESCRIPTION
* Create an instance to a quickmatch game matcher.
*
* INPUTS
* NONE
*
* RESULT
* Quickmatch - Quickmatch instance.
*
******************************************************************************/
WOLQuickMatch* WOLQuickMatch::Create(void)
{
WOLQuickMatch* match = new WOLQuickMatch;
if (match)
{
if (match->FinalizeCreate())
{
return match;
}
match->Release_Ref();
}
return NULL;
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::WOLQuickMatch
*
* DESCRIPTION
* Default constructor
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
WOLQuickMatch::WOLQuickMatch()
{
WWDEBUG_SAY(("WOLQuickMatch: Instantiating\n"));
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::~WOLQuickMatch
*
* DESCRIPTION
* Destructor
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
WOLQuickMatch::~WOLQuickMatch()
{
WWDEBUG_SAY(("WOLQuickMatch: Destructing\n"));
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::FinalizeCreate
*
* DESCRIPTION
* Creation initializaton / finalization
*
* INPUTS
* NONE
*
* RESULT
* Success - True if successful
*
******************************************************************************/
bool WOLQuickMatch::FinalizeCreate(void)
{
mWOLSession = Session::GetInstance(false);
if (!mWOLSession.IsValid())
{
WWDEBUG_SAY(("WOLQuickMatch: ERROR - WWOnline not instantiated\n"));
return false;
}
return true;
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::ConnectClient
*
* DESCRIPTION
* Connect a game client to the quickmatch bot.
*
* INPUTS
* ChannelName - Name of matching channel
* BotName - Name of matching bot
*
* RESULT
* Wait - Wait condition to process.
*
******************************************************************************/
RefPtr WOLQuickMatch::ConnectClient(void)
{
WWDEBUG_SAY(("WOLQuickMatch: Connecting client to '%S' '%S'\n",
QUICKMATCH_CHANNELNAME, QUICKMATCH_BOTNAME));
// Make sure that we are logged on to WWOnline
if (mWOLSession->GetConnectionStatus() != ConnectionConnected)
{
WWDEBUG_SAY(("WOLQuickMatch: ERROR - Not connected to WWOnline server\n"));
RefPtr wait = SingleWait::Create(TRANSLATE(IDS_WOL_CONNECTING));
wait->EndWait(WaitCondition::Error, TRANSLATE(IDS_WOL_NOTCONNECTED));
return wait;
}
// Generate client connect wait condition
RefPtr connectWait = SerialWait::Create();
if (connectWait.IsValid())
{
Observer::NotifyMe(*mWOLSession);
Observer::NotifyMe(*mWOLSession);
// Request channel list
RefPtr channelListWait = mWOLSession->GetNewChatChannelList();
connectWait->Add(channelListWait);
// Join the matching channel
RefPtr product = Product::Current();
WWASSERT(product.IsValid() && "WOLProduct not initialized.");
const wchar_t* password = product->GetChannelPassword();
RefPtr joinWait = mWOLSession->JoinChannel(QUICKMATCH_CHANNELNAME, password, 0);
connectWait->Add(joinWait);
// Make sure the matching bot is there.
RefPtr findBotWait = GetUserWait::Create(mWOLSession, QUICKMATCH_BOTNAME);
connectWait->Add(findBotWait);
}
return connectWait;
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::Disconnect
*
* DESCRIPTION
* Disconnect from quickmatch
*
* INPUTS
* NONE
*
* RESULT
* Wait - Disconnect wait condition to process.
*
******************************************************************************/
RefPtr WOLQuickMatch::Disconnect(void)
{
WWDEBUG_SAY(("WOLQuickMatch: Disconnecting\n"));
Observer::StopObserving();
Observer::StopObserving();
// If we are in the matching channel then disconnect.
RefPtr channel = mWOLSession->GetCurrentChannel();
if (channel.IsValid())
{
const WideStringClass& name = channel->GetName();
if (name.Compare_No_Case(QUICKMATCH_CHANNELNAME) == 0)
{
return mWOLSession->LeaveChannel();
}
}
return NULL;
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::SendClientInfo
*
* DESCRIPTION
* Send client matching preferences to the quickmatch bot.
*
* INPUTS
* NONE
*
* RESULT
* True if successful.
*
******************************************************************************/
bool WOLQuickMatch::SendClientInfo(void)
{
unsigned long ver = cNetwork::Get_Exe_Key();
// Get CPU speed
int speed = CPUDetectClass::Get_Processor_Speed();
// Get amount of physical memory
MEMORYSTATUS memStatus;
GlobalMemoryStatus(&memStatus);
unsigned long memory = (memStatus.dwTotalPhys / 1048576);
//-------------------------------------------------------------------------
// Gather pings
//-------------------------------------------------------------------------
const PingProfile& pings = GetLocalPingProfile();
char pseudoPings[18] = {0};
EncodePingProfile(pings, pseudoPings);
//-------------------------------------------------------------------------
// Get clients ladder points
//-------------------------------------------------------------------------
RefPtr client = mWOLSession->GetCurrentUser();
WWASSERT(client.IsValid());
if (client.IsValid() == false)
{
return false;
}
int tpoints = 0;
unsigned int played = 0;
RefPtr ladder = client->GetTeamLadder();
if (ladder.IsValid())
{
tpoints = ladder->GetPoints();
played = ladder->GetReserved2();
}
//-------------------------------------------------------------------------
// Generate client information message
//-------------------------------------------------------------------------
WideStringClass clientMsg(256, true);
clientMsg.Format(L"CINFO VER=%lu CPU=%lu MEM=%lu TPOINTS=%ld PLAYED=%lu PINGS=%S",
ver, speed, memory, tpoints, played, pseudoPings);
WWDEBUG_SAY(("WOLQuickMatch: '%S'\n", (const WCHAR*)clientMsg));
return mWOLSession->SendPrivateMessage(QUICKMATCH_BOTNAME, (const WCHAR*)clientMsg);
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::SendServerInfo
*
* DESCRIPTION
* Send information describing the game,
*
* INPUTS
*
* RESULT
* NONE
*
******************************************************************************/
void WOLQuickMatch::SendServerInfo(const char* exInfo, const char* topic)
{
if (exInfo && topic)
{
#pragma message(__FILE__" *** HACK ALERT *** SINFO msg is imitating WOLAPI IRC topic!")
// *** WARNING *** DANGER *** HACK ALERT ****
//
// The SINFO message sent to the matching bot is assembled in such
// a way as to imitate the IRC topic string that WOLAPI produces.
WideStringClass botMsg(0, true);
botMsg.Format(L"SINFO %S%S", exInfo, topic);
WWDEBUG_SAY(("WOLQuickMatch: '%S'\n", (const WCHAR*)botMsg));
mWOLSession->SendPrivateMessage(QUICKMATCH_BOTNAME, botMsg);
}
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::SendStatus
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
* NONE
*
******************************************************************************/
void WOLQuickMatch::SendStatus(const wchar_t* statusMsg)
{
WWDEBUG_SAY(("WOLQuickMatch: Status '%S'\n", statusMsg));
WideStringClass msg(256, true);
msg = statusMsg;
QuickMatchEvent status(QuickMatchEvent::QMMSG, msg);
NotifyObservers(status);
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::ParseResponse
*
* DESCRIPTION
* Handle response messages from the quickmatch bot.
*
* INPUTS
* Message - Response message from quickmatch bot.
*
* RESULT
* NONE
*
******************************************************************************/
void WOLQuickMatch::ParseResponse(const wchar_t* message)
{
if (message)
{
static QMResponseDispatch _dispatch[] =
{
{L"INFO ", WOLQuickMatch::ProcessInfo},
{L"ERROR ", WOLQuickMatch::ProcessError},
{L"START ", WOLQuickMatch::ProcessStart},
{NULL, WOLQuickMatch::ProcessUnknown}
};
int index = 0;
const wchar_t* token = _dispatch[index].Token;
while (token)
{
// Find the first occurance of the token in the message
wchar_t* cmd = wcsstr(message, token);
// If the token was found and it is at the start of the message
// then return the type of message this is.
if (cmd && cmd == message)
{
const wchar_t* data = (message + wcslen(token));
_dispatch[index].Dispatch(this, data);
}
index++;
token = _dispatch[index].Token;
}
}
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::ProcessInfo
*
* DESCRIPTION
* Process information messages.
*
* INPUTS
* Message -
*
* RESULT
* NONE
*
******************************************************************************/
void WOLQuickMatch::ProcessInfo(WOLQuickMatch* quickmatch, const wchar_t* data)
{
WideStringClass msg(255, true);
msg = data;
QuickMatchEvent status(QuickMatchEvent::QMINFO, msg);
quickmatch->NotifyObservers(status);
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::ProcessError
*
* DESCRIPTION
* Process error messages from the quickmatch bot.
*
* INPUTS
* Message - Error message
*
* RESULT
* NONE
*
******************************************************************************/
void WOLQuickMatch::ProcessError(WOLQuickMatch* quickmatch, const wchar_t* data)
{
WideStringClass msg(255, true);
msg = data;
QuickMatchEvent status(QuickMatchEvent::QMERROR, msg);
quickmatch->NotifyObservers(status);
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::ProcessStart
*
* DESCRIPTION
* Process start message from the quickmatch bot.
*
* INPUTS
* Data - Start game parameters
*
* RESULT
* NONE
*
******************************************************************************/
void WOLQuickMatch::ProcessStart(WOLQuickMatch* quickmatch, const wchar_t* data)
{
// Send message indicating successful match
WideStringClass msg(255, true);
msg.Format(TRANSLATE(IDS_MENU_QM_MATCHED_WITH), data);
QuickMatchEvent status(QuickMatchEvent::QMMSG, msg);
quickmatch->NotifyObservers(status);
// Send message that we are matched.
msg = data;
QuickMatchEvent matchedEvent(QuickMatchEvent::QMMATCHED, msg);
quickmatch->NotifyObservers(matchedEvent);
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::ProcessUnknown
*
* DESCRIPTION
* Process unknown messages from the quickmatch bot.
*
* INPUTS
* Message - Unknown message
*
* RESULT
* NONE
*
******************************************************************************/
void WOLQuickMatch::ProcessUnknown(WOLQuickMatch* quickmatch, const wchar_t* data)
{
WideStringClass msg(255, true);
msg = data;
QuickMatchEvent status(QuickMatchEvent::QMUNKNOWN, msg);
quickmatch->NotifyObservers(status);
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::HandleNotification(ServerError)
*
* DESCRIPTION
* Handle server errors.
*
* INPUTS
* Error - Server error
*
* RESULT
* NONE
*
******************************************************************************/
void WOLQuickMatch::HandleNotification(ServerError& error)
{
const wchar_t* errorMsg = error.GetDescription();
WWDEBUG_SAY(("WOLQuickMatch: ERROR - ServerError '%S'\n", errorMsg));
QuickMatchEvent status(QuickMatchEvent::QMERROR, errorMsg);
NotifyObservers(status);
}
/******************************************************************************
*
* NAME
* WOLQuickMatch::HandleNotification(ChatMessageEvent)
*
* DESCRIPTION
* Handle private messages coming from the matchbot.
*
* INPUTS
* Message - Chat message
*
* RESULT
* NONE
*
******************************************************************************/
void WOLQuickMatch::HandleNotification(ChatMessage& message)
{
const WideStringClass& sender = message.GetSendersName();
if (sender.Compare_No_Case(QUICKMATCH_BOTNAME) == 0)
{
WWDEBUG_SAY(("WOLQuickMatch: BotMsg - '%S'\n", message.GetMessage()));
ParseResponse(message.GetMessage());
}
}