/* ** 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 . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando * * * * $Archive:: /Commando/Code/Commando/wolgmode.cpp $* * * * $Author:: Steve_t $* * * * $Modtime:: 10/19/02 12:51p $* * * * $Revision:: 124 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "WOLGMode.h" #include "GameData.h" #include "GameChanList.h" #include "GameChannel.h" #include "GameInitMgr.h" #include "WOLChatMgr.h" #include "WOLBuddyMgr.h" #include "WOLQuickMatch.h" #include "WOLGameInfo.h" #include "DlgWOLWait.h" #include "DlgMessageBox.h" #include "_globals.h" #include "colors.h" #include "cNetwork.h" #include "GameResSend.h" #include "PlayerManager.h" #include "MessageWindow.h" #include "natter.h" #include "WWProfile.h" #include "BandwidthCheck.h" #include "WOLLoginProfile.h" #include "DlgDownload.h" #include "AutoStart.h" #include "cpudetect.h" #include "dx8wrapper.h" #include "systeminfolog.h" #include "registry.h" #include "init.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "String_IDs.h" #include #include #include #include "specialbuilds.h" #include "slavemaster.h" #include "sctextobj.h" #include "mainloop.h" using namespace WWOnline; // Check for patches every five minutes #define PATCH_CHECK_FREQUENCY (1000 * 5) const int RENEGADE_GAMECODE = 12; const wchar_t* RENEGADE_LOBBY_PASSWORD = L"not_a_valid_password"; // Password removed per Security review requirements. LFeenanEA - 27th January 2025 const wchar_t* _cdecl Translate_WOLString(const char* token) { if (token) { StringClass desc(80, true); desc.Format("IDS_%s", token); const WCHAR* text = TRANSLATE_BY_DESC(desc); #ifdef WWDEBUG if (STRING_NOT_FOUND == text) { WWDEBUG_SAY(("ERROR: WOL String '%s' not found!\n", token)); return L"WOL_NO_STRING"; } #endif return text; } WWDEBUG_SAY(("WARNING: WOL_xxxx token is NULL\n")); return L"WOL_BADSTRING"; } /****************************************************************************** * * NAME * WolGameModeClass::WolGameModeClass * * DESCRIPTION * Constructor * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ WolGameModeClass::WolGameModeClass() : mWOLChatMgr(NULL), mWOLBuddyMgr(NULL), mGameInProgress(false), mQuietMode(false), mConnected(false), mLastPatchCheckTime(0), mStartQuitProcessTime(0), mPatchAvailable(false), mMonitorConnection(false) { WWDEBUG_SAY(("WOLGameModeClass Instantiated\n")); WOLString::SetLookupFunc(Translate_WOLString); } /****************************************************************************** * * NAME * WolGameModeClass::~WolGameModeClass * * DESCRIPTION * Destructor * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ WolGameModeClass::~WolGameModeClass() { WWDEBUG_SAY(("WOLGameModeClass Destroyed\n")); } /****************************************************************************** * * NAME * WolGameModeClass::Init * * DESCRIPTION * Activate Westwood Online game mode * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Init(void) { WWDEBUG_SAY(("WolGameModeClass Init\n")); // Check if product is already initialized RefPtrConst product = Product::Current(); if (!product.IsValid()) { Product::Initializer(APPLICATION_SUB_KEY_NAME, RENEGADE_GAMECODE, RENEGADE_LOBBY_PASSWORD, RENEGADE_BASE_SKU); } mWOLSession = Session::GetInstance(true); if (mWOLSession.IsValid()) { mWOLSession->EnableProgressiveChannelList(true); mWOLSession->SetAutoRequestFlags(REQUEST_NONE); Observer::NotifyMe(*mWOLSession); mWOLBuddyMgr = WOLBuddyMgr::GetInstance(true); WWASSERT_PRINT(mWOLBuddyMgr, "WOLBuddyManager failed to instantiate."); mWOLChatMgr = WOLChatMgr::GetInstance(true); WWASSERT_PRINT(mWOLChatMgr, "WOLChatMgr failed to instantiate."); WOLNATInterface.Init(); LoginProfile::EnableSaving(mWOLSession->IsStoreLoginAllowed()); } mQuickMatch = NULL; mTheGame = NULL; } /****************************************************************************** * * NAME * WolGameModeClass::Shutdown * * DESCRIPTION * Deactivate Westwood Online game mode. * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Shutdown(void) { WWDEBUG_SAY(("WolGameModeClass Shutdown\n")); if (mWOLSession.IsValid()) { WOLNATInterface.Shutdown(); RefPtr logoutWait = mWOLSession->Logout(); WWASSERT(logoutWait.IsValid()); DlgWOLWait::DoDialog(IDS_WOL_LOGOFF, logoutWait); } if (mWOLBuddyMgr) { mWOLBuddyMgr->Release_Ref(); mWOLBuddyMgr = NULL; } if (mWOLChatMgr) { mWOLChatMgr->Release_Ref(); mWOLChatMgr = NULL; } // Release current profile. LoginProfile::SetCurrent(NULL); LoginInfo::ClearList(); mMonitorConnection = false; mWOLSession.Release(); } /****************************************************************************** * * NAME * WolGameModeClass::Think * * DESCRIPTION * Periodic processing; called once each game loop. * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Think(void) { WWPROFILE("WOL Think"); static unsigned long _last_auto_kick = 0; static unsigned long _kick_history_persist_time = 1000 * 60 * 60; unsigned long time = TIMEGETTIME(); //--------------------------------------------------------------------------- // Yield time to WWOnline //--------------------------------------------------------------------------- if (mWOLSession.IsValid()) { mWOLSession->Process(); } //--------------------------------------------------------------------------- // Periodically update quickmatch bot with recent server information //--------------------------------------------------------------------------- if (mGameInProgress) {// && mQuickMatch) { unsigned long theTime = TIMEGETTIME(); if (theTime >= mSendServerInfoTime) { //mSendServerInfoTime = (theTime + (120 * 1000)); // // Try to help with hacks by updating the topic more frequently. Grrrr. // mSendServerInfoTime = (theTime + (20 * 1000)); RefPtr channel = mWOLSession->GetCurrentChannel(); Update_Channel_Settings(mTheGame, channel); mWOLSession->SendChannelTopic(); if (mQuickMatch) { const char* exInfo = channel->GetExtraInfo(); const char* topic = channel->GetTopic(); mQuickMatch->SendServerInfo(exInfo, topic); } } } //--------------------------------------------------------------------------- // Autokick //--------------------------------------------------------------------------- if (mGameInProgress && (time - _last_auto_kick > 2000 || time < _last_auto_kick)) { _last_auto_kick = time; Auto_Kick(); // // Check for deadbeat channel lurkers. // if (cNetwork::I_Am_Server() && mGameInProgress) { unsigned long time = TIMEGETTIME(); // // Clear out old kicklist entries. // for (int i = IdleKickNameList.Count() - 1 ; i >= 0 ; i--) { if (time - IdleKickTimeList[i] > _kick_history_persist_time || time < IdleKickTimeList[i]) { IdleKickTimeList.Delete(i); IdleKickNameList.Delete(i); } } const UserList& userList = mWOLSession->GetUserList(); const unsigned int count = userList.size(); for (unsigned int index = 0; index < count; index++) { const RefPtr& user = userList[index]; WWASSERT(user.IsValid()); if (user.IsValid()) { // // Make sure it's not me. // const RefPtr& login = mWOLSession->GetCurrentLogin(); WideStringClass myname; if (login.IsValid()) { myname = login->GetNickname(); } if (myname.Compare_No_Case(user->GetName())) { // // Handle timer wrap. // if (time < user->mKickTimer) { user->mKickTimer = time; } if (!cPlayerManager::Find_Player(user->GetName())) { if (time - user->mKickTimer > 120 * 1000) { StringClass tempstr; user->GetName().Convert_To(tempstr); WWDEBUG_SAY(("Kicking '%s' for sitting in the channel while not in the game\n", tempstr.Peek_Buffer())); mWOLSession->KickUser(user->GetName()); user->mKickTimer += 10*1000; // // If he's already been kicked for this recently then ban him. // int chances = 3; for (int i=0 ; i < IdleKickNameList.Count() ; i++) { if (IdleKickNameList[i].Compare_No_Case(tempstr) == 0) { chances--; if (chances == 0) { mWOLSession->BanUser(user->GetName(), true); WWDEBUG_SAY(("Banning '%s' for being kicked too often.\n", tempstr.Peek_Buffer())); break; } } } if (chances > 0) { WWDEBUG_SAY(("'%s' not banned - %d chances left.\n", tempstr.Peek_Buffer(), chances)); StringClass stringy(user->GetName()); IdleKickNameList.Add(stringy); IdleKickTimeList.Add(TIMEGETTIME()); } } } else { user->mKickTimer = time; } } } } } } //--------------------------------------------------------------------------- // Check for restart conditions. //--------------------------------------------------------------------------- if (mMonitorConnection) { // Check for a pending restart for a new patch. if ((mPatchAvailable || !mConnected) && mStartQuitProcessTime) { if (time - mStartQuitProcessTime > 1000 * 4) { if (SlaveMaster.Am_I_Slave()) { Set_Exit_On_Exception(true); cGameData::Set_Manual_Exit(true); } else { Quit_And_Restart(); } return; } } // Check for patches. // This doesn't work because you can't get a server list without also doing a complete reset of wolapi. #if (0) if (time - mLastPatchCheckTime > PATCH_CHECK_FREQUENCY) { mLastPatchCheckTime = time; if (mWOLSession.IsValid()) { #ifdef WWDEBUG bool success = #endif //WWDEBUG mWOLSession->RequestServerList(true); WWASSERT(success); } } #endif //(0) // If we have lost connection and the number of players drops to zero then we can quit. if (mMonitorConnection && !mConnected) { if (The_Game() && The_Game()->Get_Time_Limit_Minutes()) { if (cPlayerManager::Count() == 0) { Quit_And_Restart(); } } } } // Firewall code service. WOLNATInterface.Service(); } /****************************************************************************** * * NAME * WolGameModeClass::System_Timer_Reset * * DESCRIPTION * Reset any internal variables as needed when the system timer resets. * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::System_Timer_Reset(void) { mSendServerInfoTime = (TIMEGETTIME() + (120 * 1000)); } /****************************************************************************** * * NAME * WolGameModeClass::Create_Game * * DESCRIPTION * Perform necessary game creation tasks for WWOnline. * NOTE: This should only be called by game hosts. * * INPUTS * Game - Instance of game to create for Westwood Online. * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Create_Game(cGameData* theGame) { WWDEBUG_SAY(("WolGameModeClass::Create_Game\n")); if (theGame) { mTheGame = theGame; mGameID = 0; mGameInProgress = false; //------------------------------------------------------------------------- // Create and initialize channel for the new game. //------------------------------------------------------------------------- const WideStringClass& name = theGame->Get_Owner(); const wchar_t* password = theGame->Get_Password(); WWDEBUG_SAY(("Creating game channel '%S' Password: '%S'\n", (const WCHAR*)name, password)); RefPtrConst product = Product::Current(); WWASSERT(product.IsValid()); int gameCode = product->GetGameCode(); RefPtr channel = ChannelData::Create(name, password, gameCode); WWASSERT(channel.IsValid()); // Export channel settings for the game. Update_Channel_Settings(theGame, channel); //------------------------------------------------------------------------- // Request WWOnline channel creation //------------------------------------------------------------------------- mChannelCreateSuccessFlag = false; RefPtr createWait = mWOLSession->CreateChannel(channel, password); WWASSERT(createWait.IsValid()); DlgWOLWait::DoDialog(IDS_GAME_CREATECHANNEL, createWait, this, 0, mQuietMode ? 0xffffffff : 0); } } /****************************************************************************** * * NAME * WolGameModeClass::Leave_Game * * DESCRIPTION * Leave WWOnline game * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Leave_Game(void) { WWDEBUG_SAY(("WolGameModeClass::Leave_Game\n")); // Stop listening WOLSession notifications. Observer::StopObserving(); Observer::StopObserving(); Observer::StopObserving(); Observer::StopObserving(); Observer::StopObserving(); // Shutdown quickmatch if (mQuickMatch) { mQuickMatch->Release_Ref(); mQuickMatch = NULL; } // Leave the game channel. if (mTheGame) { WWASSERT(mWOLSession.IsValid()); RefPtr wait = mWOLSession->LeaveChannel(); WWASSERT(wait.IsValid()); DlgWOLWait::DoDialog(IDS_GAME_LEAVECHANNEL, wait); } mTheGame = NULL; mGameID = 0; mGameInProgress = false; } /****************************************************************************** * * NAME * WolGameModeClass::Start_Game * * DESCRIPTION * Start of game processing. This is performed by both the host and clients * at the begining of each game. * * INPUTS * Game - Instance of game starting * * RESULT * NONE * ******************************************************************************/ void Get_Compact_Detail_String(StringClass& tmp); void WolGameModeClass::Start_Game(cGameData* theGame) { WWDEBUG_SAY(("WolGameModeClass::Start_Game\n")); // The game host (server) is responsible for obtaining the game ID if (cNetwork::I_Am_Server()) { WWASSERT(theGame == mTheGame && "Start_Game cGameData* mismatch"); // Re-evaluate the clans in this game. Evaluate_Clans(theGame); //------------------------------------------------------------------------- // Update the channel settings for the new game. //------------------------------------------------------------------------- RefPtr channel = mWOLSession->GetCurrentChannel(); WWASSERT(channel.IsValid()); Update_Channel_Settings(theGame, channel); // Send the chat server the new channel settings. mWOLSession->SendChannelExtraInfo(); mWOLSession->SendChannelTopic(); if (mQuickMatch) { const char* exInfo = channel->GetExtraInfo(); const char* topic = channel->GetTopic(); mQuickMatch->SendServerInfo(exInfo, topic); } // Reset timer to send server information for quickmatch mSendServerInfoTime = (TIMEGETTIME() + (120 * 1000)); //------------------------------------------------------------------------- // Get WWOnline game id for this game. //------------------------------------------------------------------------- const WideStringClass& name = theGame->Get_Owner(); UserList players; RefPtr user = mWOLSession->FindUser(name); players.push_back(user); RefPtr wait = GameStartWait::Create(players, &Game_Start_Timeout_Callback); WWASSERT(wait.IsValid()); Observer::NotifyMe(*mWOLSession); DlgWOLWait::DoDialog(TRANSLATE (IDS_MENU_STARTING_WOL_GAME), wait, NULL, 0, mQuietMode ? 0xffffffff : 0); if (The_Game() && The_Game()->IsDedicated.Is_True()) { mConnected = true; mLastPatchCheckTime = TIMEGETTIME(); mStartQuitProcessTime = 0; mMonitorConnection = true; Observer::NotifyMe(*mWOLSession); Observer::NotifyMe(*mWOLSession); } } else { mTheGame = theGame; } // Prevent the page dialog from showing while we are playing the game. // We will handle the displaying of pages and invitations within the game // messaging system. mWOLBuddyMgr->HidePagedDialog(); Observer::NotifyMe(*mWOLBuddyMgr); // Listen to events from WOLSession Observer::NotifyMe(*mWOLSession); Observer::NotifyMe(*mWOLSession); mGameInProgress = true; } /****************************************************************************** * * NAME * WolGameModeClass::End_Game * * DESCRIPTION * Do end of game processing. * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::End_Game(void) { if (mGameInProgress) { WWDEBUG_SAY(("WolGameModeClass::End_Game\n")); // The server sends the end of game results if (mTheGame) { if (cNetwork::I_Am_Server()) { SendGameResults(mGameID, mTheGame, cPlayerManager::Get_Player_Object_List()); mWOLSession->GetChatObject()->RequestGameEnd(); // // Re-read the ban list. // KickNameList.Delete_All(); KickIPList.Delete_All(); Read_Kick_List(); } else { #ifdef BETACLIENT // Jani do stuff here (send client system information string) StringClass info(255, true); info = "SYSINFO:"; info += CPUDetectClass::Get_Compact_Log(); info += DX8Wrapper::Get_Current_Caps()->Get_Compact_Log(); StringClass tmp(255,true); Get_Compact_Detail_String(tmp); info += tmp; PlayerInfoLog::Get_Compact_Log(tmp); info += tmp; SystemInfoLog::Get_Compact_Log(tmp); info += tmp; BandwidthCheckerClass::Get_Compact_Log(tmp); info += tmp; WOLNATInterface.Get_Compact_Log(tmp); info += tmp; WWDEBUG_SAY(("Sending compact log: %s\n", info.Peek_Buffer())); mWOLSession->SendPrivateGameOptions(mTheGame->Get_Owner(), info); #endif // BETACLIENT } } // Allow the page dialog to show while we are no longer playing the game. Observer::StopObserving(); mWOLBuddyMgr->ShowPagedDialog(); mGameID = 0; mGameInProgress = false; } } /*********************************************************************************************** * WolGameModeClass::Post_Game_Check -- Call when game is about to cycle. * * * * * * * * INPUT: Nothing * * * * OUTPUT: True if cycle should be aborted * * * * WARNINGS: None * * * * HISTORY: * * 11/7/2001 6:07PM ST : Created * *=============================================================================================*/ bool WolGameModeClass::Post_Game_Check(void) { bool retval = false; if (mMonitorConnection && !mConnected) { Quit_And_Restart(); retval = true; } mMonitorConnection = false; return(retval); } /****************************************************************************** * * NAME * WolGameModeClass::Accept_Actions * * DESCRIPTION * * INPUTS * Game - Instance of game joining * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Accept_Actions(void) { WWDEBUG_SAY(("WolGameModeClass::Accept_Actions\n")); } /****************************************************************************** * * NAME * WolGameModeClass::Refusal_Actions * * DESCRIPTION * * INPUTS * Game - Instance of game joining * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Refusal_Actions(void) { WWDEBUG_SAY(("WolGameModeClass::Refusal_Actions\n")); // If the client's request to join the game was refused then we need to make // sure we leave the host's channel. RefPtr wait = mWOLSession->LeaveChannel(); DlgWOLWait::DoDialog(IDS_GAME_LEAVECHANNEL, wait); } /****************************************************************************** * * NAME * WolGameModeClass::Evaluate_Clans * * DESCRIPTION * * INPUTS * Game - Instance of game. * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Evaluate_Clans(cGameData* theGame) { if (theGame && cNetwork::I_Am_Server()) { theGame->Clear_Clans(); if (theGame->IsClanGame.Is_True()) { WWDEBUG_SAY(("CLANS: Evaluating clan assignments\n")); // If the host is a member of a clan then his clan takes a slot RefPtr host = mWOLSession->GetCurrentUser(); if (host.IsValid()) { unsigned long hostClanID = host->GetSquadID(); theGame->Set_Clan(0, hostClanID); WWDEBUG_SAY(("CLANS: Assigning slot 0 to '%S' (host) clan #%lu\n", (const WCHAR*)host->GetName(), hostClanID)); } // Determine which clans are in the game. const UserList& userList = mWOLSession->GetUserList(); const unsigned int count = userList.size(); for (unsigned int index = 0; index < count; ++index) { const RefPtr& user = userList[index]; WWASSERT(user.IsValid()); unsigned long userClanID = user->GetSquadID(); if (userClanID != 0) { // If the clan is not already assigned then assign this clan to the next slot. if (!theGame->Is_Clan_Competing(userClanID)) { int slot = theGame->Find_Free_Clan_Slot(); WWASSERT(slot != -1 && "More clans in game than slots available"); if (slot != -1) { WWDEBUG_SAY(("CLANS: Assigning slot %d to clan #%lu\n", slot, userClanID)); theGame->Set_Clan(slot, userClanID); } } } } // Force server information to be resent. mSendServerInfoTime = 0; } } } /****************************************************************************** * * NAME * WolGameModeClass::Update_Channel_Settings * * DESCRIPTION * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Update_Channel_Settings(cGameData* theGame, const RefPtr& channel) { WWASSERT(theGame != NULL); WWASSERT(channel.IsValid()); if (theGame && channel.IsValid()) { WOLGameInfo gameInfo(*theGame); gameInfo.ExportToChannel(channel); // Quickmatch settings are appended to the channel topic if (theGame->Is_QuickMatch_Server()) { //--------------------------------------------------------------------------- // Get average FPS of game (Capped at 255 fps) //--------------------------------------------------------------------------- unsigned long fps = TimeManager::Get_Average_Frame_Rate(); fps = min(fps, 255); int numPlayers = theGame->Get_Current_Players(); int avgPing = cPlayerManager::Get_Average_Ping(); avgPing = min(avgPing, UCHAR_MAX); unsigned short avgPoints = cPlayerManager::Get_Average_WOL_Points(); int avgPlayed = cPlayerManager::Get_Average_Games_Played(); avgPlayed = min(avgPlayed, USHRT_MAX); // Quickmatch settings format is: |fps,mode,avg_ping,avg_rank,num_players const char* topic = channel->GetTopic(); char newTopic[81]; sprintf(newTopic, "%s|%02lx%02x%04x%04x%c", topic, fps, avgPing, avgPoints, avgPlayed, (32 + numPlayers)); channel->SetTopic(newTopic); } } } /****************************************************************************** * * NAME * WolGameModeClass::Init_WOL_Player * * DESCRIPTION * * INPUTS * Player - Set players WOL settings. * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Init_WOL_Player(cPlayer* player) { if (player) { RefPtr user = mWOLSession->FindUser(player->Get_Name()); if (user.IsValid()) { RefPtr ladder = user->GetTeamLadder(); if (ladder.IsValid()) { player->Set_WOL_Points(ladder->GetPoints()); player->Set_Wol_Rank(ladder->GetRung()); player->Set_Num_Wol_Games(ladder->GetReserved2()); return; } } // Ladder points start at 10000 and vary up or down depending on the players // performance. player->Set_WOL_Points(10000); // Ranking of -1 indicates that the player doesn't have a rank. player->Set_Wol_Rank(-1); player->Set_Num_Wol_Games(0); } } /****************************************************************************** * * NAME * WolGameModeClass::Get_WOL_User_Data * * DESCRIPTION * * INPUTS * Player - Name of player to find. * * RESULT * User - WWOnline user data * ******************************************************************************/ RefPtr WolGameModeClass::Get_WOL_User_Data(const wchar_t* name) { return mWOLSession->FindUser(name); } /****************************************************************************** * * NAME * WolGameModeClass::Page_WOL_User * * DESCRIPTION * * INPUTS * User - Name of user to page. * Message - Message to send to user. * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Page_WOL_User(const wchar_t* name, const wchar_t* msg) { if (name && (wcslen(name) > 0) && msg && (wcslen(msg) > 0)) { mWOLSession->PageUser(name, msg); WideStringClass message(0, true); message.Format(TRANSLATE(IDS_GAME_PAGING), name, msg); CombatManager::Get_Message_Window()->Add_Message(message, COLOR_CONSOLE_TEXT); } } /****************************************************************************** * * NAME * WolGameModeClass::Reply_Last_Page * * DESCRIPTION * Send a reply message to the last user who paged. * * INPUTS * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Reply_Last_Page(const wchar_t* msg) { if (msg && (wcslen(msg) > 0)) { const WCHAR* pager = mWOLBuddyMgr->GetLastPagersName(); if (pager && (wcslen(pager) > 0)) { mWOLBuddyMgr->PageUser(pager, msg); WideStringClass message(0, true); message.Format(TRANSLATE(IDS_GAME_PAGE_REPLYING), pager, msg); CombatManager::Get_Message_Window()->Add_Message(message, COLOR_CONSOLE_TEXT); } else { CombatManager::Get_Message_Window()->Add_Message(TRANSLATE(IDS_GAME_PAGE_REPLY_CANT), COLOR_CONSOLE_TEXT); } } } /****************************************************************************** * * NAME * WolGameModeClass::Locate_WOL_User * * DESCRIPTION * * INPUTS * User - Name of user to locate. * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Locate_WOL_User(const wchar_t* name) { if (name && wcslen(name) > 0) { mWOLSession->RequestLocateUser(name); WideStringClass message(0, true); message.Format(TRANSLATE(IDS_GAME_LOCATING_USER), name); CombatManager::Get_Message_Window()->Add_Message(message, COLOR_CONSOLE_TEXT); } } /****************************************************************************** * * NAME * WolGameModeClass::Invite_WOL_User * * DESCRIPTION * * INPUTS * User - Name of user to locate. * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Invite_WOL_User(const wchar_t* name, const wchar_t* msg) { if (name && wcslen(name)) { cPlayer* player = cPlayerManager::Find_Player(name); if (player) { WideStringClass message(0, true); message.Format(TRANSLATE (IDS_MENU_ALREADY_IN_GAME), name); CombatManager::Get_Message_Window()->Add_Message(message, COLOR_INVITE_TEXT); WWAudioClass::Get_Instance()->Create_Instant_Sound("Private_Message", Matrix3D(1)); } else { mWOLBuddyMgr->InviteUser(name, msg); WideStringClass message(0, true); message.Format(TRANSLATE(IDS_GAME_INVITING_USER), name); CombatManager::Get_Message_Window()->Add_Message(message, COLOR_CONSOLE_TEXT); } } } /****************************************************************************** * * NAME * WolGameModeClass::Join_WOL_User * * DESCRIPTION * * INPUTS * User - Name of user to join. * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::Join_WOL_User(const wchar_t* name) { // If the name is valid and it is not me if (name && wcslen(name) && !mWOLSession->IsCurrentUser(name)) { cPlayer* player = cPlayerManager::Find_Player(name); if (player) { WideStringClass message(0, true); message.Format(TRANSLATE(IDS_MENU_YOURE_ALREADY_IN_GAME), name); CombatManager::Get_Message_Window()->Add_Message(message, COLOR_INVITE_TEXT); WWAudioClass::Get_Instance()->Create_Instant_Sound("Private_Message", Matrix3D(1)); } else { RefPtr user = UserData::Create(name); mWOLBuddyMgr->JoinUser(user); WideStringClass message(0, true); message.Format(TRANSLATE(IDS_GAME_JOINING_USER), name); CombatManager::Get_Message_Window()->Add_Message(message, COLOR_CONSOLE_TEXT); } } } /****************************************************************************** * * NAME * WolGameModeClass::Kick_Player * * DESCRIPTION * Kick a player from the game. Players kicked from a game are also banned * from rejoining that game. Note: This is only allowed by game server hosts. * * INPUTS * Player - Name of player to kick from the game. * * RESULT * NONE * ******************************************************************************/ bool WolGameModeClass::Kick_Player(const wchar_t* name) { // If the name is not NULL. we are the server and the player to kick is // not ourself then proceed with the kick. if (name && (wcslen(name) > 0)) { if (cNetwork::I_Am_Server() && !mWOLSession->IsCurrentUser(name)) { mWOLSession->KickUser(name); mWOLSession->BanUser(name, true); // Disconnect the player from the server WideStringClass playername(0, true); playername = name; cPlayer* player = cPlayerManager::Find_Player(playername); if (player && player->Is_Human()) { WideStringClass message(0, true); message.Format(TRANSLATE(IDS_GAME_KICKING_USER), name); CombatManager::Get_Message_Window()->Add_Message(message, COLOR_CONSOLE_TEXT); int playerID = player->Get_Id(); cNetwork::Server_Kill_Connection(playerID); cNetwork::Cleanup_After_Client(playerID); return(true); } } WideStringClass message(0, true); message.Format(TRANSLATE(IDS_GAME_KICKING_NOT_ALLOWED), name); CombatManager::Get_Message_Window()->Add_Message(message, COLOR_CONSOLE_TEXT); } return(false); } /*********************************************************************************************** * WolGameModeClass::Ban_Player -- Add player to ban list * * * * * * * * INPUT: Player name * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/8/2002 4:33PM ST : Created * *=============================================================================================*/ void WolGameModeClass::Ban_Player(const wchar_t* name, unsigned long ip) { // If the name is not NULL. we are the server and the player to kick is // not ourself then proceed with the kick. if (name && cNetwork::I_Am_Server() && !mWOLSession->IsCurrentUser(name)) { Kick_Player(name); WideStringClass playername(0, true); playername = name; StringClass pn; playername.Convert_To(pn); if (!Is_Banned(pn, ip)) { KickNameList.Add(pn); KickIPList.Add(ip); char ipstr[128]; pn += ":"; unsigned char *ip_ptr = (unsigned char*)&ip; sprintf(ipstr, "%u.%u.%u.%u\n", (unsigned int)ip_ptr[0], (unsigned int)ip_ptr[1], (unsigned int)ip_ptr[2], (unsigned int)ip_ptr[3]); pn += ipstr; FILE *kick_list = fopen("wolbanlist.txt", "at"); if (kick_list != NULL) { fwrite(pn.Peek_Buffer(), 1, pn.Get_Length(), kick_list); fclose(kick_list); } DynamicVectorClass KickIPList; } } } /*********************************************************************************************** * WolGameModeClass::Auto_Kick -- Kick any banned users from the game * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/8/2002 9:24PM ST : Created * *=============================================================================================*/ void WolGameModeClass::Auto_Kick(void) { if (cNetwork::I_Am_Server()) { cPlayer *player = NULL; for (SLNode *player_node = cPlayerManager::Get_Player_Object_List()->Head() ; player_node != NULL; player_node = player_node->Next()) { player = player_node->Data(); if (player->Is_Active()) { if (!mWOLSession->IsCurrentUser(player->Get_Name())) { StringClass player_name(player->Get_Name()); if (Is_Banned(player_name, player->Get_Ip_Address())) { Kick_Player(player->Get_Name()); } } } } } } /*********************************************************************************************** * WolGameModeClass::Is_Banned -- Is this player banned from our WOL server? * * * * * * * * INPUT: Player name * * IP Address * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/8/2002 9:14PM ST : Created * *=============================================================================================*/ bool WolGameModeClass::Is_Banned(const char *player_name, unsigned long ip) { int i; if (KickNameList.Count() == 0) { Read_Kick_List(); } for (i=0 ; i 10) { temp[i + 1] = 0; char *colon_ptr = strchr(temp, ':'); if (colon_ptr) { *colon_ptr = 0; KickNameList.Add(temp); unsigned long ip = inet_addr(colon_ptr + 1); KickIPList.Add(ip); } } } fclose(kick_list); } } /****************************************************************************** * * NAME * WolGameModeClass::HandleNotification(ChannelEvent) * * DESCRIPTION * * INPUTS * Event - * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::HandleNotification(ChannelEvent& event) { // This user has been kicked from the game. if (ChannelKicked == event.GetStatus()) { WWDEBUG_SAY(("WolGameModeClass Player Kicked!\n")); mGameInProgress = false; GameInitMgrClass::End_Game(); GameInitMgrClass::Display_End_Game_Menu(); DlgMsgBox::DoDialog(TRANSLATE(IDS_MENU_SERVER_MESSAGE_TITLE), TRANSLATE (IDS_MENU_SERVER_KICKED_MESSAGE)); } } /****************************************************************************** * * NAME * WolGameModeClass::HandleNotification(UserEvent) * * DESCRIPTION * * INPUTS * Event - * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::HandleNotification(UserEvent& event) { switch (event.GetEvent()) { // A user has been kicked from the game. case UserEvent::Kicked: { WideStringClass message(0, true); message.Format(TRANSLATE(IDS_MENU_PLAYER_KICKED_MESSAGE), event.Subject()->GetName()); CombatManager::Get_Message_Window()->Add_Message(message, COLOR_PUBLIC_TEXT); WWAudioClass::Get_Instance()->Create_Instant_Sound("Public_Message", Matrix3D(1)); } break; case UserEvent::Located: { const RefPtr& user = event.Subject(); // Get the description of the user's location WideStringClass location(64, true); WOLBuddyMgr::GetLocationDescription(user, location); // Build a string containing the user's name WideStringClass message(0, true); message.Format(TRANSLATE(IDS_CHAT_LOCATEDUSER), user->GetName()); message += L" - "; message += location; CombatManager::Get_Message_Window()->Add_Message(message, COLOR_PRIVATE_TEXT); WWAudioClass::Get_Instance()->Create_Instant_Sound("Private_Message", Matrix3D(1)); } break; case UserEvent::LadderInfo: if (mTheGame) { const RefPtr& user = event.Subject(); cPlayer* player = cPlayerManager::Find_Player(user->GetName()); Init_WOL_Player(player); } break; case UserEvent::Join: if (mTheGame && cNetwork::I_Am_Server()) { const RefPtr& user = event.Subject(); bool requestDetails = true; // If the user is joining a clan game. if (mTheGame->IsClanGame.Is_True()) { WWDEBUG_SAY(("CLANS: User join clan assignment\n")); unsigned long userClanID = user->GetSquadID(); WWASSERT(userClanID != 0 && "User not in a clan"); // If the game is open to a new clan then assign the user to a // participating clan or a new slot. if (mTheGame->Is_Clan_Game_Open()) { // If the user is not a member of a participating clan then assign // the next free slot to this users clan. if (!mTheGame->Is_Clan_Competing(userClanID)) { int slot = mTheGame->Find_Free_Clan_Slot(); if (slot != -1) { WWDEBUG_SAY(("CLANS: Slot %d filled by '%S' Clan #%lu\n", slot, (const WCHAR*)user->GetName(), userClanID)); mTheGame->Set_Clan(slot, userClanID); } // Send the chat server the new channel settings. RefPtr channel = mWOLSession->GetCurrentChannel(); Update_Channel_Settings(mTheGame, channel); mWOLSession->SendChannelExtraInfo(); } } else { // If the game is closed and the user is not a member of one of the // competing clans then kick him. if (!mTheGame->Is_Clan_Competing(userClanID)) { WWDEBUG_SAY(("CLANS: Game closed. Kicking user '%S'\n", (const WCHAR*)user->GetName())); mWOLSession->KickUser(user->GetName()); requestDetails = false; } } } if (requestDetails) { mWOLSession->RequestUserDetails(user, REQUEST_LADDERINFO|REQUEST_SQUADINFO); } } break; case UserEvent::Leave: if (mTheGame && cNetwork::I_Am_Server()) { if (mTheGame->IsClanGame.Is_True()) { WWDEBUG_SAY(("CLANS: User leave re-evaluate clan assignment\n")); Evaluate_Clans(mTheGame); } } break; default: break; } } /****************************************************************************** * * NAME * WolGameModeClass::HandleNotification(DlgWOLWaitEvent) * * DESCRIPTION * Response to completion of game channel creation. * * INPUTS * Event - * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::HandleNotification(DlgWOLWaitEvent& wolEvent) { if (wolEvent.Result() == WaitCondition::ConditionMet) { WWASSERT(mTheGame != NULL); mChannelCreateSuccessFlag = true; // Startup quickmatch if we are serving quickmatch games. if (mTheGame->Is_QuickMatch_Server()) { mQuickMatch = WOLQuickMatch::Create(); WWASSERT(mQuickMatch); } mSendServerInfoTime = 0; // Start listening to Game Options messages from clients Observer::NotifyMe(*mWOLSession); // Listen to events from WOLSession Observer::NotifyMe(*mWOLSession); Observer::NotifyMe(*mWOLSession); } else { if (!mQuietMode) { DlgMsgBox::DoDialog(TRANSLATE (IDS_MENU_FAILED_TO_CREATE_GAME), wolEvent.Subject()->GetResultText()); } } Signaler::SendSignal(*this); } /****************************************************************************** * * NAME * WolGameModeClass::HandleNotification(GameStartEvent) * * DESCRIPTION * Handle game start acknowledgement. * * INPUTS * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::HandleNotification(GameStartEvent& start) { WWDEBUG_SAY(("GameStartEvent received\n")); mGameID = 0; if (start.IsSuccess()) { mGameID = start.GetGameID(); WWDEBUG_SAY(("GameID = %ld\n", mGameID)); } } /****************************************************************************** * * NAME * WolGameModeClass::HandleNotification(GameOptionsMessage) * * DESCRIPTION * Handle game options requests coming from other users. * * INPUTS * OptionsMessage - * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::HandleNotification(GameOptionsMessage& message) { if (cNetwork::I_Am_Server() && mTheGame) { const char* request = message.GetOptions(); // Request for game information? if (strcmp("RGINFO", request) == 0) { WideStringClass requestor(0, true); requestor = message.GetSendersName(); //----------------------------------------------------------------------- // Send game information //----------------------------------------------------------------------- unsigned long mapCRC = CRC_Stringi(mTheGame->Get_Map_Name()); float seconds = mTheGame->Get_Time_Remaining_Seconds(); // Game info sent as: MapCRC Seconds remaining StringClass info(0, true); info.Format("GINFO:%08lx %.4f", mapCRC, seconds); WWDEBUG_SAY(("%S\n", info)); mWOLSession->SendPrivateGameOptions(requestor, info); //----------------------------------------------------------------------- // Send team information //----------------------------------------------------------------------- SList* teamList = cTeamManager::Get_Team_Object_List(); WWASSERT(teamList != NULL); SLNode* teamNode = teamList->Head(); while (teamNode) { cTeam* team = teamNode->Data(); WWASSERT(team != NULL); int teamID = team->Get_Id(); int teamScore = team->Get_Score(); info.Format("TINFO:%d %d", teamID, teamScore); WWDEBUG_SAY(("%S\n", info)); mWOLSession->SendPrivateGameOptions(requestor, info); teamNode = teamNode->Next(); } //----------------------------------------------------------------------- // Send player information //----------------------------------------------------------------------- SList* playerList = cPlayerManager::Get_Player_Object_List(); SLNode* playerNode = playerList->Head(); while (playerNode) { cPlayer* player = playerNode->Data(); if (player && player->Is_Active() && player->Is_Human()) { const WideStringClass& name = player->Get_Name(); int type = player->Get_Player_Type(); int rung = player->Get_Rung(); int kills = player->Get_Kills(); int deaths = player->Get_Deaths(); int score = player->Get_Score(); // PINFO string format: Name fps,type,rank,kills,deaths info.Format("PINFO:%S %d %d %d %d %d", (const WCHAR*)name, type, rung, kills, deaths, score); mWOLSession->SendPrivateGameOptions(requestor, info); } playerNode = playerNode->Next(); } } else { // Write client system info to disk const char* cmd = strstr(request, "SYSINFO:"); if (cmd == request) { static int sysinfo_log_disabled=-1; if (sysinfo_log_disabled==-1) { // Read from registry only once per run RegistryClass registry( APPLICATION_SUB_KEY_NAME_DEBUG ); if ( registry.Is_Valid() ) { sysinfo_log_disabled=registry.Get_Int( VALUE_NAME_DISABLE_SERVER_SYSINFO_COLLECTING, -1 ); // Write to registry to create the key if it didn't exist if (sysinfo_log_disabled==-1) { sysinfo_log_disabled=0; registry.Set_Int( VALUE_NAME_DISABLE_SERVER_SYSINFO_COLLECTING, sysinfo_log_disabled); } } } // Only copy the sysinfo if it isn't disabled in registry if (sysinfo_log_disabled==0) { const char* data = (request + strlen("SYSINFO:")); int datalen=strlen(data); if (!datalen) return; StringClass tmp(0,true); StringClass requestor(0, true); requestor = message.GetSendersName(); StringClass datastring; datastring=requestor; datastring+="\t"; unsigned long versionminor,versionmajor; Get_Version_Number(&versionmajor,&versionminor); SYSTEMTIME time; GetSystemTime(&time); tmp.Format("%d/%d/%d %d:%d\t%d.%d\t", time.wMonth, time.wDay, time.wYear, time.wHour, time.wMinute, versionmajor, versionminor); datastring+=tmp; datastring+=data; datastring+="\r\n"; // Verify the sysinfo folder StringClass dirname(0,true); dirname.Format("sysinfo_%d",DebugManager::Get_Version_Number()); if (GetFileAttributes(dirname)==0xffffffff) { if (!CreateDirectory(dirname,NULL)) { return; } } StringClass filename(0,true); filename=dirname; filename+="\\"; tmp=requestor; filename+=tmp; filename+=".txt"; DWORD written; HANDLE file; file = CreateFile(filename, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE != file) { SetFilePointer(file, 0, NULL, FILE_END); WriteFile(file, datastring, strlen(datastring), &written, NULL); CloseHandle(file); } } } } } } /****************************************************************************** * * NAME * WolGameModeClass::HandleNotification(LadderInfoEvent) * * DESCRIPTION * * INPUTS * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::HandleNotification(LadderInfoEvent& ladderEvent) { if (ladderEvent.IsRanked()) { LadderType type = ladderEvent.GetLadderType(); // If this is for the locale user and the ladder information is cached // then update there profile with the latest ladder information. if (LadderType_Team == type) { WideStringClass name(64, true); name = ladderEvent.GetReceivedName(); if (mWOLSession->IsCurrentUser(name)) { LoginProfile* profile = LoginProfile::Get(name, false); if (profile) { WWDEBUG_SAY(("Updating team ladder info for %S\n", profile->GetName())); const WOL::Ladder& wolLadder = ladderEvent.GetWOLLadder(); // Save the number of games played. profile->SetGamesPlayed(wolLadder.reserved2); // Save the ranking information LoginProfile::Ranking rank; rank.Wins = wolLadder.wins; rank.Losses = wolLadder.losses; rank.Deaths = wolLadder.reserved1; rank.Kills = wolLadder.kills; rank.Points = wolLadder.points; rank.Rank = wolLadder.rung; profile->SetRanking(LadderType_Team, rank); profile->SaveSettings(); profile->Release_Ref(); } } } else if (LadderType_Clan == type) { RefPtr user = mWOLSession->GetCurrentUser(); if (user.IsValid()) { RefPtr squad = user->GetSquad(); if (squad.IsValid() && (stricmp(squad->GetAbbr(), ladderEvent.GetReceivedName()) == 0)) { LoginProfile* profile = LoginProfile::Get(user->GetName(), false); if (profile) { WWDEBUG_SAY(("Updating clan ladder info for %S\n", profile->GetName())); const WOL::Ladder& wolLadder = ladderEvent.GetWOLLadder(); // Save the ranking information LoginProfile::Ranking rank; rank.Wins = wolLadder.wins; rank.Losses = wolLadder.losses; rank.Deaths = wolLadder.reserved1; rank.Kills = wolLadder.kills; rank.Points = wolLadder.points; rank.Rank = wolLadder.rung; profile->SetRanking(LadderType_Clan, rank); profile->SaveSettings(); profile->Release_Ref(); } } } } } } /*********************************************************************************************** * WolGameModeClass::HandleNotification -- Handle connection status notifications * * * * * * * * INPUT: Connection status * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/8/2001 10:20PM ST : Created * *=============================================================================================*/ void WolGameModeClass::HandleNotification(ConnectionStatus &status) { if (mMonitorConnection && status == ConnectionDisconnected) { Handle_Disconnect(); } } /*********************************************************************************************** * WolGameModeClass::Handle_Disconnect -- Handle disconnect from chat server * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/8/2001 10:21PM ST : Created * *=============================================================================================*/ void WolGameModeClass::Handle_Disconnect(void) { if (mMonitorConnection && mConnected) { mConnected = false; if (cNetwork::I_Am_Server()) { if (The_Game() && The_Game()->IsDedicated.Is_True() && AutoRestart.Get_Restart_Flag()) { /* ** If the game has a time limit and players then we will wait until the next map change otherwise we will stop now. */ WWASSERT(PTheGameData != NULL); //if (!The_Game()->Get_Time_Limit_Minutes() || SlaveMaster.Am_I_Slave()) { if (cPlayerManager::Count() == 0) { if (SlaveMaster.Am_I_Slave()) { AutoRestart.Set_Restart_Flag(false); Set_Exit_On_Exception(true); cGameData::Set_Manual_Exit(true); } else { Quit_And_Restart(); } } else { /* ** Tell the players we have to quit */ mStartQuitProcessTime = TIMEGETTIME(); if (cNetwork::I_Am_Server()) { WideStringClass widestring(TRANSLATE (IDS_MENU_QUITTING_DUE_TO_CONN_LOSS_MSG), true); cScTextObj * message = new cScTextObj; message->Init(widestring, TEXT_MESSAGE_PUBLIC, true, HOST_TEXT_SENDER, -1); } } //} else { //Quit_And_Restart(); //} } } } } /*********************************************************************************************** * WolGameModeClass::HandleNotification -- Handle server error notifications * * * * * * * * INPUT: Server error tyoe * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/8/2001 10:21PM ST : Created * *=============================================================================================*/ void WolGameModeClass::HandleNotification(ServerError& server_error) { /* ** This doesn't work because you can't get a server list without also doing a complete reset of wolapi. */ #if (0) HRESULT code = server_error.GetErrorCode(); if (code == CHAT_E_MUSTPATCH) { if (mMonitorConnection && mConnected) { mPatchAvailable = true; /* ** If the game has a time limit and players then we will wait until the next map change otherwise we will stop now. */ WWASSERT(PTheGameData != NULL); if (!The_Game()->Get_Time_Limit_Minutes() || cPlayerManager::Count() == 0) { mStartQuitProcessTime = TIMEGETTIME(); } else { Quit_And_Restart(); } } } #endif //(0) } /*********************************************************************************************** * WolGameModeClass::Quit_And_Restart -- Setup a restart then quit the game * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/8/2001 10:21PM ST : Created * *=============================================================================================*/ void WolGameModeClass::Quit_And_Restart(void) { static bool reenter_ye_not = false; if (!reenter_ye_not) { reenter_ye_not = true; /* ** This will cause the server to leave the channel if needed. */ GameInitMgrClass::End_Game(); /* ** If there is a patch available then download it. */ if (mConnected) { WOLLogonMgr::Logoff(); if (mPatchAvailable) { RefPtr wolSession = WWOnline::Session::GetInstance(false); DlgDownload::DoDialog(TRANSLATE(IDS_WOL_DOWNLOAD), wolSession->GetPatchDownloadList(), true); } } else { /* ** If we lost connection then drop out of the game and try to re-establish connection. */ AutoRestart.Set_Restart_Flag(true); Set_Exit_On_Exception(true); cGameData::Set_Manual_Exit(true); Stop_Main_Loop(EXIT_SUCCESS); } reenter_ye_not = false; } } /****************************************************************************** * * NAME * WolGameModeClass::HandleNotification(WOLPagedEvent) * * DESCRIPTION * Handle the display of pages and invitations from users outside the game. * * INPUTS * * RESULT * NONE * ******************************************************************************/ void WolGameModeClass::HandleNotification(WOLPagedEvent& event) { PageMessage* page = event.Subject(); WWASSERT(page && "NULL page in WOLPagedEvent"); switch (event.GetAction()) { case PAGE_RECEIVED: { WideStringClass message(255, true); message.Format(L"%s: %s", page->GetPagersName(), page->GetPageMessage()); CombatManager::Get_Message_Window()->Add_Message(message, COLOR_PAGED_TEXT); WWAudioClass::Get_Instance()->Create_Instant_Sound("Private_Message", Matrix3D(1)); break; } case INVITATION_RECEIVED: case INVITATION_DECLINED: CombatManager::Get_Message_Window()->Add_Message(page->GetPageMessage(), COLOR_INVITE_TEXT); WWAudioClass::Get_Instance()->Create_Instant_Sound("Private_Message", Matrix3D(1)); break; case PAGE_ERROR: case PAGE_NOT_THERE: case PAGE_TURNED_OFF: { WideStringClass message(255, true); message.Format(L"%s %s", TRANSLATE(IDS_WOL_PAGEUSERERROR), page->GetPageMessage()); CombatManager::Get_Message_Window()->Add_Message(page->GetPageMessage(), COLOR_CONSOLE_TEXT); break; } } } void WolGameModeClass::Game_Start_Timeout_Callback(void) { GameModeClass* game = GameModeManager::Find("WOL"); if (game && game->Is_Active()) { WolGameModeClass* wolgame = reinterpret_cast(game); wolgame->Game_Start_Timed_Out(); } } void WolGameModeClass::Game_Start_Timed_Out(void) { /* ** If we lost connection without getting a report from WW Online then the only clue we have is that we will fail to ** get the game ID by timing out. Since this is pretty much fatal anyway, let's quit and restart and see if things fix ** themselves up. ** ** ST - 8/21/2002 11:27AM */ if (cNetwork::I_Am_Server()) { if (The_Game() && The_Game()->IsDedicated.Is_True() && AutoRestart.Get_Restart_Flag()) { mConnected = false; if (SlaveMaster.Am_I_Slave()) { AutoRestart.Set_Restart_Flag(false); Set_Exit_On_Exception(true); cGameData::Set_Manual_Exit(true); } else { Quit_And_Restart(); } } } }