/* ** 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()); } }