/* ** 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 <http://www.gnu.org/licenses/>. */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : Combat * * * * $Archive:: /Commando/Code/Commando/dlgmpwolgamelist.cpp $* * * * Author:: Patrick Smith * * * * $Modtime:: 11/24/02 4:52p $* * * * $Revision:: 76 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "DlgMPWOLGameList.h" #include "specialbuilds.h" #include "cNetwork.h" #include "BandwidthCheck.h" #include "UserOptions.h" #include "GameInitMgr.h" #include "DlgMPWOLPageBuddy.h" #include "DlgMessageBox.h" #include "DlgPasswordPrompt.h" #include <Combat\PlayerType.h> #include "WOLJoinGame.h" #include "WOLLogonMgr.h" #include <WWOnline\WOLServer.h> #include <WWOnline\WOLChannel.h> #include <WWUI\DialogMgr.h> #include <WWUI\ShortcutBarCtrl.h> #include <WWUI\ButtonCtrl.h> #include <WWUI\CheckBoxCtrl.h> #include <WWUI\ListCtrl.h> #include "String_IDs.h" #include <WWTranslateDB\TranslateDB.h> #include "dlgmplanhostoptions.h" #include "debug.h" #include "init.h" using namespace WWOnline; // Game List columns static enum { COL_ICON = 0, COL_HOST_NAME, COL_GAME_TITLE, COL_GAME_MAP, COL_PLAYERS, COL_PING }; #define CLAN_ENTRY_MARKER 0x0C1A0C1A #define PING_GREEN_THRESHOLD 655 #define PING_YELLOW_THRESHOLD 2621 // Sorting flags for game attributes #define FLAGSORT_DEDICATED 0x01 #define FLAGSORT_LADDERED 0x02 #define FLAGSORT_PASSWORDED 0x04 #define FLAGSORT_CLAN 0x08 static void SetGameTypeFlags(ListCtrlClass* list, int itemIndex, const WOLGameInfo& gameInfo); static void SetPingTimeIcon(ListCtrlClass* list, int itemIndex, long pingTime); static int CALLBACK FlagsSortCallback(ListCtrlClass* list, int item1, int item2, uint32 param); static int CALLBACK NumericSortCallback(ListCtrlClass* list, int item1, int item2, uint32 param); static int CALLBACK AlphaSortCallback(ListCtrlClass* list, int index1, int index2, uint32 param); MPWolGameListMenuClass* MPWolGameListMenuClass::_mInstance = NULL; /****************************************************************************** * * NAME * MPWolGameListMenuClass::DoDialog * * DESCRIPTION * Display the Westwood Online advanced game listings dialog. * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::DoDialog(void) { // Create the dialog if necessary, otherwise simply bring it to the front if (_mInstance == NULL) { MPWolGameListMenuClass* dialog = new MPWolGameListMenuClass; WWASSERT(dialog != NULL && "Failed to create WOL GameList dialog"); if (dialog) { dialog->Start_Dialog(); dialog->Release_Ref(); } } else { if (_mInstance->Is_Active_Menu() == false) { DialogMgrClass::Rollback(_mInstance); } } } /****************************************************************************** * * NAME * MPWolGameListMenuClass::MPWolGameListMenuClass * * DESCRIPTION * Constructor * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ MPWolGameListMenuClass::MPWolGameListMenuClass(void) : MenuDialogClass(IDD_MP_WOL_GAME_LIST), mChannelListPending(false), mSortColumn(COL_HOST_NAME), mIsSortAscending(true), mSortFlags(0) { WWDEBUG_SAY(("MPWolGameListMenuClass Instantiated\n")); WWASSERT(_mInstance == NULL); _mInstance = this; mWOLSession = Session::GetInstance(false); WWASSERT_PRINT(mWOLSession.IsValid(), "WOLSession not instantiated!"); } /****************************************************************************** * * NAME * MPWolGameListMenuClass::~MPWolGameListMenuClass * * DESCRIPTION * Destructor * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ MPWolGameListMenuClass::~MPWolGameListMenuClass(void) { WWDEBUG_SAY(("MPWolGameListMenuClass Destroyed\n")); _mInstance = NULL; } /****************************************************************************** * * NAME * MPWolGameListMenuClass::On_Init_Dialog * * DESCRIPTION * One time dialog initialization * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::On_Init_Dialog(void) { #ifdef FREEDEDICATEDSERVER Enable_Dlg_Item(IDC_JOIN_GAME_BUTTON, false); #endif // FREEDEDICATEDSERVER //--------------------------------------------------------------------------- // Configure the shortcut bar //--------------------------------------------------------------------------- ShortcutBarCtrlClass* bar = (ShortcutBarCtrlClass*)Get_Dlg_Item(IDC_SHORTCUT_BAR); if (bar) { bar->Add_Button(IDC_MP_SHORTCUT_CHAT, TRANSLATE(IDS_MP_SHORTCUT_CHAT)); bar->Add_Button(IDC_MP_SHORTCUT_BUDDIES, TRANSLATE(IDS_MP_SHORTCUT_BUDDIES)); bar->Add_Button(IDC_MP_SHORTCUT_INTERNET_OPTIONS, TRANSLATE(IDS_INTERNET_OPTIONS)); bar->Add_Button(IDC_MP_SHORTCUT_NEWS, TRANSLATE(IDS_MP_SHORTCUT_NEWS)); bar->Add_Button(IDC_MP_SHORTCUT_CLANS, TRANSLATE(IDS_MP_SHORTCUT_CLANS)); bar->Add_Button(IDC_MP_SHORTCUT_RANKINGS, TRANSLATE(IDS_MP_SHORTCUT_RANKINGS)); bar->Add_Button(IDC_MP_SHORTCUT_NET_STATUS, TRANSLATE(IDS_MP_SHORTCUT_NET_STATUS)); } //--------------------------------------------------------------------------- // Show the current login and server //--------------------------------------------------------------------------- WideStringClass serverName(64, true); WOLLogonMgr::GetServerName(serverName); WideStringClass string(128, true); string.Format(TRANSLATE (IDS_MENU_CONNECTED_TO_FORMAT), serverName); Set_Dlg_Item_Text(IDC_SERVERNAME, string); WideStringClass loginName(64, true); WOLLogonMgr::GetLoginName(loginName); string.Format(TRANSLATE (IDS_MENU_LOGIN_NAME_FORMAT), loginName); Set_Dlg_Item_Text(IDC_LOGINNAME, string); WideStringClass conn(BandwidthCheckerClass::Get_Bandwidth_As_String(), true); string.Format(TRANSLATE (IDS_MENU_SPEED_FORMAT), conn); Set_Dlg_Item_Text(IDC_CONNECTIONSPEED, string); //--------------------------------------------------------------------------- // Configure sorting buttons //--------------------------------------------------------------------------- ButtonCtrlClass* button = (ButtonCtrlClass*)Get_Dlg_Item(IDC_SORT_DEDICATED_BUTTON); if (button) { button->Set_Bitmap("mul_spec.tga", "mul_nopts.tga"); button->Set_Tooltip_Text(TRANSLATE (IDS_MENU_TOOLTIP_SORT_01)); } button = (ButtonCtrlClass*)Get_Dlg_Item(IDC_SORT_PASSWORD_BUTTON); if (button) { button->Set_Bitmap("mul_pswrd.tga", "mul_nopts.tga"); button->Set_Tooltip_Text(TRANSLATE (IDS_MENU_TOOLTIP_SORT_02)); } button = (ButtonCtrlClass*)Get_Dlg_Item(IDC_SORT_LADDERED_BUTTON); if (button) { button->Set_Bitmap("mul_pts.tga", "mul_nopts.tga"); button->Set_Tooltip_Text(TRANSLATE (IDS_MENU_TOOLTIP_SORT_03)); } button = (ButtonCtrlClass*)Get_Dlg_Item(IDC_SORT_CLAN_BUTTON); if (button) { button->Set_Bitmap("mul_btlcln.tga", "mul_nopts.tga"); button->Set_Tooltip_Text(TRANSLATE (IDS_MENU_TOOLTIP_SORT_04)); } //--------------------------------------------------------------------------- // Configure game listing control //--------------------------------------------------------------------------- ListCtrlClass* list_ctrl = (ListCtrlClass*)Get_Dlg_Item(IDC_GAME_LIST_CTRL); if (list_ctrl) { Vector3 color(1,1,1); list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_ICON), 0.1F, color); list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_HOST_NAME), 0.175F, color); list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_GAME_NAME), 0.325F, color); list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_GAME_MAP), 0.2F, color); list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_PLAYERS), 0.1F, color); list_ctrl->Add_Column(TRANSLATE(IDS_MP_GAME_LIST_HEADER_PING), 0.1F, color); list_ctrl->Allow_NoSelection(true); Observer<ChannelListEvent>::NotifyMe(*mWOLSession); Observer<ServerError>::NotifyMe(*mWOLSession); Observer<SquadEvent>::NotifyMe(*mWOLSession); RequestGameList(); } //--------------------------------------------------------------------------- // Configure game details list control //--------------------------------------------------------------------------- list_ctrl = (ListCtrlClass*)Get_Dlg_Item(IDC_DETAILS_LIST); if (list_ctrl) { list_ctrl->Allow_Selection(false); list_ctrl->Set_Tabstop(20.0f); } MenuDialogClass::On_Init_Dialog(); } /****************************************************************************** * * NAME * MPWolGameListMenuClass::On_Command * * DESCRIPTION * Respond to command messages from UI controls * * INPUTS * ID - ID of control originating the message * Msg - Message identifier * Param - Message specific parameter * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::On_Command(int id, int msg, DWORD param) { switch (id) { case IDC_PAGE_BUDDY_BUTTON: START_DIALOG(MPWolPageBuddyPopupClass); break; case IDC_REFRESH_GAME_LIST: RequestGameList(); break; case IDC_SORT_DEDICATED_BUTTON: SortGameChannels(COL_ICON, true, FLAGSORT_DEDICATED); break; case IDC_SORT_PASSWORD_BUTTON: SortGameChannels(COL_ICON, true, FLAGSORT_PASSWORDED); break; case IDC_SORT_LADDERED_BUTTON: SortGameChannels(COL_ICON, true, FLAGSORT_LADDERED); break; case IDC_SORT_CLAN_BUTTON: SortGameChannels(COL_ICON, true, FLAGSORT_CLAN); break; case IDC_JOIN_GAME_BUTTON: Join_Game(); break; case IDC_MENU_MP_LAN_HOST_BUTTON: // Return to the game list on exit GameInitMgrClass::Set_WOL_Return_Dialog(RenegadeDialogMgrClass::LOC_INTERNET_GAME_LIST); // Create the new game data if ( PTheGameData != NULL ) { delete PTheGameData; PTheGameData = NULL; } PTheGameData = cGameData::Create_Game_Of_Type(cGameData::GAME_TYPE_CNC); WWASSERT(PTheGameData != NULL); // WOL games default as quickmatch servers The_Game()->Set_QuickMatch_Server(true); // Load server preferences. The_Game()->Load_From_Server_Config(); // Move to the next menu DialogBaseClass* dialog = new MPLanHostOptionsMenuClass; dialog->Start_Dialog(); REF_PTR_RELEASE(dialog); break; } MenuDialogClass::On_Command(id, msg, param); } /****************************************************************************** * * NAME * MPWolGameListMenuClass::On_Key_Down * * DESCRIPTION * * INPUTS * * RESULT * ******************************************************************************/ bool MPWolGameListMenuClass::On_Key_Down(uint32 key_id, uint32 key_data) { if (VK_F5 == key_id) { RequestGameList(); return true; } return MenuDialogClass::On_Key_Down(key_id, key_data); } /****************************************************************************** * * NAME * MPWolGameListMenuClass::Join_Game * * DESCRIPTION * Join the currently selected game * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::Join_Game(void) { ListCtrlClass* list = (ListCtrlClass*)Get_Dlg_Item(IDC_GAME_LIST_CTRL); if (list) { // Get the currently selected index int index = list->Get_Curr_Sel(); if (index >= 0) { RefPtr<ChannelData> channel = (ChannelData*)list->Get_Entry_Data(index, COL_HOST_NAME); // If the channel is invalid then do not proceed if (!channel.IsValid()) { return; } WOLGameInfo selectedGame(channel); // If the channel data is invalid then do not proceed if (!selectedGame.IsDataValid()) { return; } // If the user cannot join this game then do not proceed. if (!selectedGame.CanUserJoin(mWOLSession->GetCurrentUser())) { return; } // Return to the game list on exit GameInitMgrClass::Set_WOL_Return_Dialog(RenegadeDialogMgrClass::LOC_INTERNET_GAME_LIST); // If the game to join is passworded then it is necessary for the user to // supply the correct password. Therefore prompt the user to enter the // password. We will attempt to connect to the game when we receive a // signal from the dialog that the user has entered a password. if (channel->IsPassworded()) { mGameToJoin = channel; DlgPasswordPrompt::DoDialog(this); } else { // No password require WOLJoinGame::JoinTheGame(channel->GetName(), L"", true); } } } } /****************************************************************************** * * NAME * MPWolGameListMenuClass::ReceiveSignal * * DESCRIPTION * Handle receipt of password entered signal from the password prompt dialog. * * INPUTS * Signal - Signal that the user has entered a password. * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::ReceiveSignal(DlgPasswordPrompt& passwordDialog) { if (mGameToJoin.IsValid()) { WOLJoinGame::JoinTheGame(mGameToJoin->GetName(), passwordDialog.GetPassword(), true); mGameToJoin.Release(); } } /****************************************************************************** * * NAME * MPWolGameListMenuClass::RequestGameList * * DESCRIPTION * Submit a request for an updated game list. * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::RequestGameList(void) { if (mChannelListPending == false) { if (mWOLSession->RequestGameChannelList()) { mChannelListPending = true; Enable_Dlg_Item(IDC_REFRESH_GAME_LIST, false); ListCtrlClass* list = (ListCtrlClass*)Get_Dlg_Item(IDC_DETAILS_LIST); if (list) { list->Delete_All_Entries(); list->Add_Column(L"", 1.0F, Vector3(1, 1, 1)); list->Insert_Entry(0, TRANSLATE (IDS_MENU_REQUESTING_NEW_CHANNELS)); } } } } /****************************************************************************** * * NAME * MPWolGameListMenuClass::UpdateChannels * * DESCRIPTION * Update the list of game channels. * * INPUTS * List - List control to view channels. * Channels - List of channels * * RESULT * True if the list changed. * ******************************************************************************/ void MPWolGameListMenuClass::UpdateChannels(ListCtrlClass* list, const ChannelList& chanList) { // Get current selection so we can reselect it. WideStringClass selectedGameName(64, true); int selIndex = list->Get_Curr_Sel(); if (selIndex >= 0) { selectedGameName = list->Get_Entry_Text(selIndex, COL_HOST_NAME); } selIndex = -1; int listIndex = list->Get_Entry_Count(); ChannelList::const_iterator chanIter = chanList.begin(); while (chanIter != chanList.end()) { RefPtr<ChannelData> channel = (*chanIter); if (channel.IsValid()) { WOLGameInfo gameInfo(channel); // Only show the channel if it is a valid game channel if (gameInfo.IsDataValid()) { bool newlyAdded = false; // Check if the channel entry is already in the listbox. int itemIndex = list->Find_Entry(COL_HOST_NAME, channel->GetName()); // If the channel was not found then add it now. if (itemIndex == -1) { itemIndex = list->Insert_Entry(listIndex, L""); newlyAdded = true; } // Update the channel in the listbox if (itemIndex >= 0) { // Associate the channel data inside with the entry if (newlyAdded) { ChannelData* rawChannel = channel.ReferencedObject(); rawChannel->AddReference(); list->Set_Entry_Data(itemIndex, COL_HOST_NAME, (unsigned long)rawChannel); // Show the game channel name list->Set_Entry_Text(itemIndex, COL_HOST_NAME, channel->GetName()); } WideStringClass title(0, true); title.Convert_From(gameInfo.Title()); list->Set_Entry_Text(itemIndex, COL_GAME_TITLE, title); // Show the game type icons SetGameTypeFlags(list, itemIndex, gameInfo); // Show the map name WideStringClass mapName(64, true); // // If this is a mod'd game, then display the mod_name\map_name... // if (gameInfo.ModName() != NULL && gameInfo.ModName()[0] != 0) { // // Strip off the extension for both the map and the mod package // char map_name[_MAX_FNAME] = { 0 }; char mod_name[_MAX_FNAME] = { 0 }; ::_splitpath (gameInfo.MapName(), NULL, NULL, map_name, NULL); ::_splitpath (gameInfo.ModName(), NULL, NULL, mod_name, NULL); // // Create the map name from the aggregate of the mod and map // StringClass ascii_map_name; ascii_map_name.Format ("%s\\%s", mod_name, map_name); mapName.Convert_From (ascii_map_name); } else { mapName = gameInfo.MapName(); } list->Set_Entry_Text(itemIndex, COL_GAME_MAP, mapName); // Show the number of current / max players list->Set_Entry_Data(itemIndex, COL_PLAYERS, MAKELONG(gameInfo.NumPlayers(), gameInfo.MaxPlayers())); WideStringClass playersString(64, true); playersString.Format(L"%u/%u", gameInfo.NumPlayers(), gameInfo.MaxPlayers()); list->Set_Entry_Text(itemIndex, COL_PLAYERS, playersString); // Show the ping time ranking SetPingTimeIcon(list, itemIndex, gameInfo.PingTime()); if ((selIndex == -1) && (selectedGameName.Compare_No_Case(channel->GetName()) == 0)) { selIndex = itemIndex; } if (gameInfo.Version() != (unsigned long)cNetwork::Get_Exe_Key() || !gameInfo.IsMapValid()) { ChannelData* rawChannel = (ChannelData*)list->Get_Entry_Data(itemIndex, COL_HOST_NAME); if (rawChannel) { rawChannel->ReleaseReference(); } list->Set_Entry_Data(itemIndex, COL_HOST_NAME, 0); if (gameInfo.IsMapValid()) { list->Set_Entry_Text(itemIndex, COL_GAME_TITLE, TRANSLATE(IDS_MENU_VERSION_MISMATCH)); } /* WideStringClass diagnostic; diagnostic.Format(L"Version Mismatch (v. %u.%u)", HIWORD(gameInfo.Version()), LOWORD(gameInfo.Version())); list->Set_Entry_Text(itemIndex, COL_GAME_TITLE, diagnostic); */ Vector3 grey(0.5F, 0.5F, 0.5F); list->Set_Entry_Color(itemIndex, COL_ICON, grey); list->Set_Entry_Color(itemIndex, COL_HOST_NAME, grey); list->Set_Entry_Color(itemIndex, COL_GAME_TITLE, grey); list->Set_Entry_Color(itemIndex, COL_GAME_MAP, grey); list->Set_Entry_Color(itemIndex, COL_PLAYERS, grey); list->Set_Entry_Color(itemIndex, COL_PING, grey); list->Set_Entry_Data(itemIndex, 0, 0); } } } ++listIndex; } ++chanIter; } // Select the previously selected entry if ((selIndex != -1) && (list->Get_Entry_Count() > 0)) { list->Set_Curr_Sel(selIndex); } } /****************************************************************************** * * NAME * MPWolGameListMenuClass::HandleNotification(ChannelListEvent) * * DESCRIPTION * Process the receipt of a game channel list. * * INPUTS * Channels - List of channels * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::HandleNotification(ChannelListEvent& event) { // Ignore chat channels if (event.GetChannelType() == 0) { return; } // Update the channel listbox ListCtrlClass* list = (ListCtrlClass*)Get_Dlg_Item(IDC_GAME_LIST_CTRL); if (list) { bool sort = false; switch (event.GetEvent()) { default: case ChannelListEvent::Error: case ChannelListEvent::NewList: { ChannelList& chanList = event.Subject(); bool listEmpty = chanList.empty(); ListCtrlClass* details = (ListCtrlClass*)Get_Dlg_Item(IDC_DETAILS_LIST); if (details) { details->Delete_All_Entries(); if (listEmpty) { details->Insert_Entry(0, TRANSLATE(IDS_MENU_NO_GAME_SERVERS_FOUND)); } } Enable_Dlg_Item(IDC_JOIN_GAME_BUTTON, !listEmpty); Enable_Dlg_Item(IDC_REFRESH_GAME_LIST, true); mChannelListPending = false; } break; case ChannelListEvent::Update: UpdateChannels(list, event.Subject()); sort = true; break; case ChannelListEvent::Remove: { ChannelList& chanList = event.Subject(); ChannelList::iterator chanIter = chanList.begin(); while (chanIter != chanList.end()) { RefPtr<ChannelData> channel = (*chanIter); int index = list->Find_Entry(COL_HOST_NAME, channel->GetName()); if (index >= 0) { list->Delete_Entry(index); sort = true; } ++chanIter; } } break; } if (sort) { SortGameChannels(mSortColumn, mIsSortAscending, mSortFlags); } } } /****************************************************************************** * * NAME * MPWolGameListMenuClass::On_ListCtrl_Delete_Entry * * DESCRIPTION * Handle the deletion of a listbox entry * * INPUTS * List - Pointer to list control originating notification. * ID - Identifier of list control originating notification. * Index - Index of entry being deleted * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::On_ListCtrl_Delete_Entry(ListCtrlClass* list, int id, int index) { if (IDC_GAME_LIST_CTRL == id) { ChannelData* rawChannel = (ChannelData*)list->Get_Entry_Data(index, COL_HOST_NAME); if (rawChannel) { rawChannel->ReleaseReference(); } } } /****************************************************************************** * * NAME * MPWolGameListMenuClass::On_ListCtrl_Sel_Change * * DESCRIPTION * Handle user selecting a game entry. * * INPUTS * List - Pointer to list control originating notification. * ID - Identifier of list control originating notification. * OldSel - Index of previous selection * NewSel - Index of new selection * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::On_ListCtrl_Sel_Change(ListCtrlClass* list, int id, int oldIndex, int newIndex) { if (IDC_GAME_LIST_CTRL == id) { Enable_Dlg_Item(IDC_JOIN_GAME_BUTTON, false); mSelectedGame.Reset(); // Clear any detailed game information. ListCtrlClass* detailsList = (ListCtrlClass*)Get_Dlg_Item(IDC_DETAILS_LIST); detailsList->Delete_All_Entries(); if (newIndex >= 0) { RefPtr<ChannelData> channel = (ChannelData*)list->Get_Entry_Data(newIndex, COL_HOST_NAME); if (channel.IsValid()) { mSelectedGame.ImportFromChannel(channel); if (mSelectedGame.IsDataValid()) { WideStringClass text(255, true); // Show host name and exe version // Denzil 02/14/02 The version encoded in the channel is NOT the printable version. // Just show the EXE version until we can work this out. #if(0) text.Format(TRANSLATE(IDS_MENU_HOST_INFO_FORMAT), (const WCHAR*)channel->GetName(), mSelectedGame.Title(), HIWORD(mSelectedGame.Version()), LOWORD(mSelectedGame.Version())); #else unsigned long verMajor = 0; unsigned long verMinor = 0; Get_Version_Number(&verMajor,&verMinor); text.Format(TRANSLATE(IDS_MENU_HOST_INFO_FORMAT), (const WCHAR*)channel->GetName(), mSelectedGame.Title(), HIWORD(verMajor), LOWORD(verMajor)); #endif detailsList->Insert_Entry(0, text); // If this is a clan game then show the opposing clans if (mSelectedGame.IsClanGame()) { GetClanVSClanString(mSelectedGame, text); int entryIndex = detailsList->Insert_Entry(detailsList->Get_Entry_Count(), text); detailsList->Set_Entry_Data(entryIndex, 0, CLAN_ENTRY_MARKER); } // Show server settings text.Format(TRANSLATE(IDS_MENU_HOST_OPTIONS_INFO_FORMAT), mSelectedGame.IsDedicated() ? TRANSLATE(IDS_MENU_TEXT463) : TRANSLATE(IDS_MENU_TEXT464), mSelectedGame.IsPassworded() ? TRANSLATE(IDS_MENU_TEXT463) : TRANSLATE(IDS_MENU_TEXT464), mSelectedGame.IsLaddered() ? TRANSLATE(IDS_MENU_TEXT463) : TRANSLATE(IDS_MENU_TEXT464), mSelectedGame.IsQuickmatch() ? TRANSLATE(IDS_MENU_TEXT463) : TRANSLATE(IDS_MENU_TEXT464)); detailsList->Insert_Entry(detailsList->Get_Entry_Count(), text); text.Format(TRANSLATE(IDS_MENU_HOST_OPTIONS_INFO2_FORMAT), mSelectedGame.IsClanGame() ? TRANSLATE (IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF), mSelectedGame.IsTeamChange() ? TRANSLATE (IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF), mSelectedGame.IsTeamRemix() ? TRANSLATE (IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF), mSelectedGame.IsFriendlyFire() ? TRANSLATE (IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF)); detailsList->Insert_Entry(detailsList->Get_Entry_Count(), text); text.Format(TRANSLATE(IDS_MENU_HOST_OPTIONS_INFO3_FORMAT), mSelectedGame.IsDriverGunner() ? TRANSLATE(IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF), mSelectedGame.IsSpawnWeapons() ? TRANSLATE(IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF), mSelectedGame.IsRepairBuildings() ? TRANSLATE(IDS_MENU_ON) : TRANSLATE(IDS_MENU_OFF)); detailsList->Insert_Entry(detailsList->Get_Entry_Count(), text); } bool canJoin = mSelectedGame.CanUserJoin(mWOLSession->GetCurrentUser()); Enable_Dlg_Item(IDC_JOIN_GAME_BUTTON, canJoin); } } } } /****************************************************************************** * * NAME * MPWolGameListMenuClass:: * * DESCRIPTION * * INPUTS * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::GetClanVSClanString(WOLGameInfo& gameInfo, WideStringClass& text) { const char* clan1Name = "____"; const char* clan2Name = "____"; RefPtr<SquadData> clan = SquadData::FindByID(gameInfo.ClanID1()); if (clan.IsValid()) { clan1Name = clan->GetName(); } else if (gameInfo.ClanID1() != 0) { clan1Name = "????"; mWOLSession->RequestSquadInfoByID(gameInfo.ClanID1()); } clan = SquadData::FindByID(gameInfo.ClanID2()); if (clan.IsValid()) { clan2Name = clan->GetName(); } else if (gameInfo.ClanID2() != 0) { clan2Name = "????"; mWOLSession->RequestSquadInfoByID(gameInfo.ClanID2()); } text.Format(L"%S -VS- %S", clan1Name, clan2Name); } /****************************************************************************** * * NAME * MPWolGameListMenuClass::On_ListCtrl_Column_Click * * DESCRIPTION * Respond to user clicking on a column header of a list control. * * INPUTS * List - Pointer to list control originating notification. * ID - Identifier of list control originating notification. * Column - Number of column clicked. * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::On_ListCtrl_Column_Click(ListCtrlClass* list, int id, int column) { if (IDC_GAME_LIST_CTRL == id) { // If clicking the same column then toggle ascending / descending if (mSortColumn == column) { SortGameChannels(column, !mIsSortAscending, mSortFlags); } else { SortGameChannels(column, true, mSortFlags); } } } /****************************************************************************** * * NAME * MPWolGameListMenuClass::On_ListCtrl_DblClk * * DESCRIPTION * Automatically attempt to join a game if the user double-clicks on the * game entry. * * INPUTS * List - List control * ID - Identifier of list control * Index - Entry index double-clicked * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::On_ListCtrl_DblClk(ListCtrlClass* list, int id, int index) { if (IDC_GAME_LIST_CTRL == id) { Join_Game(); } } /****************************************************************************** * * NAME * On_Last_Menu_Ending * * DESCRIPTION * Callback from menu class signifying the close of the last menu * * INPUTS * * RESULT * ******************************************************************************/ void MPWolGameListMenuClass::On_Last_Menu_Ending (void) { RenegadeDialogMgrClass::Goto_Location(RenegadeDialogMgrClass::LOC_INTERNET_MAIN); } /****************************************************************************** * * NAME * MPWolGameListMenuClass::HandleNotification(ServerError) * * DESCRIPTION * Handle server error notifications from WWOnline session. * * INPUTS * Error - Server error * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::HandleNotification(ServerError& error) { if (CHAT_E_BANNED == error.GetErrorCode()) { // DlgMsgBox::DoDialog(TRANSLATE(IDS_WOL_SERVERMESSAGE), TRANSLATE(IDS_WOL_BANNED)); DlgMsgBox::DoDialog(TRANSLATE (IDS_MENU_SERVER_MESSAGE_TITLE), TRANSLATE (IDS_MENU_WOL_BAN_USER_MESSAGE)); End_Dialog(); } } /****************************************************************************** * * NAME * MPWolGameListMenuClass::HandleNotification(SquadEvent) * * DESCRIPTION * * INPUTS * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::HandleNotification(WWOnline::SquadEvent& event) { if (!mSelectedGame.IsDataValid() && mSelectedGame.IsClanGame()) { return; } RefPtr<SquadData> squad = event.GetSquadData(); if (squad.IsValid()) { unsigned long squadID = squad->GetID(); if (mSelectedGame.ClanID1() == squadID || mSelectedGame.ClanID2() == squadID) { ListCtrlClass* detailsList = (ListCtrlClass*)Get_Dlg_Item(IDC_DETAILS_LIST); if (detailsList) { // Find entry with marker indicating clan vs clan const int count = detailsList->Get_Entry_Count(); for (int index = 0; index < count; ++index) { int marker = detailsList->Get_Entry_Data(index, 0); if (CLAN_ENTRY_MARKER == marker) { WideStringClass text(0, true); GetClanVSClanString(mSelectedGame, text); detailsList->Set_Entry_Text(index, 0, text); break; } } } } } } /****************************************************************************** * * NAME * SetGameTypeFlags * * DESCRIPTION * Setup the game type flags in the icon column of the game listings. * * INPUTS * List - Pointer to list control to work with. * Index - Entry index to set type flags for. * GameInfo - Game information * * RESULT * ******************************************************************************/ void SetGameTypeFlags(ListCtrlClass* list, int itemIndex, const WOLGameInfo& gameInfo) { WWASSERT(list != NULL); list->Reset_Icons(itemIndex, COL_ICON); uint32 flags = 0; if (gameInfo.IsLaddered()) { list->Add_Icon(itemIndex, COL_ICON, "MUL_pts.tga"); flags |= FLAGSORT_LADDERED; } else { list->Add_Icon(itemIndex, COL_ICON, "MUL_NoPts.tga"); } if (gameInfo.IsDedicated()) { list->Add_Icon(itemIndex, COL_ICON, "MUL_Spec.tga"); flags |= FLAGSORT_DEDICATED; } if (gameInfo.IsPassworded()) { list->Add_Icon(itemIndex, COL_ICON, "MUL_Pswrd.tga"); flags |= FLAGSORT_PASSWORDED; } if (gameInfo.IsClanGame()) { list->Add_Icon(itemIndex, COL_ICON, "MUL_Btlcln.tga"); flags |= FLAGSORT_CLAN; } list->Set_Entry_Data(itemIndex, COL_ICON, flags); } /****************************************************************************** * * NAME * SetPingTimeIcon * * DESCRIPTION * Set an icon reflecting the latency for this game. * * INPUTS * List - List control * Index - Entry index * Time - Latency * * RESULT * NONE * ******************************************************************************/ void SetPingTimeIcon(ListCtrlClass* list, int itemIndex, long pingTime) { const char* pingIcon = NULL; if (pingTime <= PING_GREEN_THRESHOLD) { pingIcon = "mul_statg.tga"; } else if (pingTime <= PING_YELLOW_THRESHOLD) { pingIcon = "mul_staty.tga"; } else { pingIcon = "mul_statr.tga"; } list->Reset_Icons(itemIndex, COL_PING); list->Add_Icon(itemIndex, COL_PING, pingIcon); unsigned long displayPing = (unsigned long)((1000.0 / 256.0) * (sqrt(double(pingTime)))); list->Set_Entry_Data(itemIndex, COL_PING, displayPing); WideStringClass text(32, true); text.Format(L" (%lu)", displayPing); list->Set_Entry_Text(itemIndex, COL_PING, text); } /****************************************************************************** * * NAME * MPWolGameListMenuClass::SortGameChannels * * DESCRIPTION * Sort the game channels * * INPUTS * Column - Number of column to sort by. * Ascending - True to sort ascending; false to sort descending * Flags - Game flags to sort by if column is COL_ICON * * RESULT * NONE * ******************************************************************************/ void MPWolGameListMenuClass::SortGameChannels(int column, bool isAscending, unsigned long param) { mSortColumn = column; mIsSortAscending = isAscending; mSortFlags = param; ListCtrlClass* list = (ListCtrlClass*)Get_Dlg_Item(IDC_GAME_LIST_CTRL); if (list) { ListCtrlClass::SORT_TYPE sortDirection = ListCtrlClass::SORT_DESCENDING; if (isAscending) { sortDirection = ListCtrlClass::SORT_ASCENDING; } if (COL_ICON == column) { list->Sort(FlagsSortCallback, mSortFlags); } else if (COL_PLAYERS == column || COL_PING == column) { list->Sort(NumericSortCallback, MAKELONG(column, sortDirection)); } else { list->Sort(AlphaSortCallback, MAKELONG(column, sortDirection)); } list->Set_Sort_Designator(column, sortDirection); } } /****************************************************************************** * * NAME * FlagSortCallback * * DESCRIPTION * Sort list entries by game type flags. * * INPUTS * List - List control performing sort. * Index1 - First entry to compare * Index2 - Second entry to compare * * RESULT * 0 - Items are equal * -1 - Item1 is less than item2 * 1 - Item1 is greater than item2 * ******************************************************************************/ int CALLBACK FlagsSortCallback(ListCtrlClass* list, int index1, int index2, uint32 mask) { uint32 flags1 = list->Get_Entry_Data(index1, COL_ICON); flags1 &= mask; uint32 flags2 = list->Get_Entry_Data(index2, COL_ICON); flags2 &= mask; if (flags1 && !flags2) { return -1; } else if (!flags1 && flags2) { return 1; } else if (flags1 && flags2) { // Secondary sort by ping time int ping1 = list->Get_Entry_Data(index1, COL_PING); int ping2 = list->Get_Entry_Data(index2, COL_PING); int delta = (ping1 - ping2); if (delta < 0) { return -1; } else if (delta > 0) { return 1; } const WCHAR* name1 = list->Get_Entry_Text(index1, COL_HOST_NAME); const WCHAR* name2 = list->Get_Entry_Text(index2, COL_HOST_NAME); return wcsicmp(name1, name2); } return 0; } /****************************************************************************** * * NAME * NumericSortCallback * * DESCRIPTION * Sort list entries numerically from the numbers contained in the entries * data field. * * INPUTS * List - List control performing sort. * Index1 - First entry to compare * Index2 - Second entry to compare * * RESULT * -1 - Item1 is less than item2 * 0 - Items are equal * 1 - Item1 is greater than item2 * ******************************************************************************/ int CALLBACK NumericSortCallback(ListCtrlClass* list, int index1, int index2, uint32 param) { // Sort by numeric value stored in entry data field int column = LOWORD(param); uint32 data1 = list->Get_Entry_Data(index1, column); uint32 data2 = list->Get_Entry_Data(index2, column); int retval = (data1 - data2); if (retval < 0) { retval = -1; } else if (retval > 0) { retval = 1; } else { if (column != COL_PING) { // Secondary sort by ping time data1 = list->Get_Entry_Data(index1, COL_PING); data2 = list->Get_Entry_Data(index2, COL_PING); retval = (data1 - data2); if (retval < 0) { retval = -1; } else if (retval > 0) { retval = 1; } } if (retval == 0) { const WCHAR* name1 = list->Get_Entry_Text(index1, COL_HOST_NAME); const WCHAR* name2 = list->Get_Entry_Text(index2, COL_HOST_NAME); retval = wcsicmp(name1, name2); } } // Invert the return value if we are sorting descendingly ListCtrlClass::SORT_TYPE sortType = (ListCtrlClass::SORT_TYPE)HIWORD(param); if (ListCtrlClass::SORT_DESCENDING == sortType) { return -retval; } return retval; } /****************************************************************************** * * NAME * NumericSortCallback * * DESCRIPTION * Sort list entries alphbetically then by ping time. * * INPUTS * List - List control performing sort. * Index1 - First entry to compare * Index2 - Second entry to compare * * RESULT * -1 - Item1 is less than item2 * 0 - Items are equal * 1 - Item1 is greater than item2 * ******************************************************************************/ int CALLBACK AlphaSortCallback(ListCtrlClass* list, int index1, int index2, uint32 param) { // Sort by numeric value stored in entry data field int column = LOWORD(param); const WCHAR* text1 = list->Get_Entry_Text(index1, column); const WCHAR* text2 = list->Get_Entry_Text(index2, column); int retval = wcsicmp(text1, text2); // If the strings match then secondary sort by ping time. if (retval == 0) { uint32 data1 = list->Get_Entry_Data(index1, COL_PING); uint32 data2 = list->Get_Entry_Data(index2, COL_PING); retval = (data1 - data2); if (retval < 0) { retval = -1; } else if (retval > 0) { retval = 1; } } // Invert the return value if we are sorting descendingly ListCtrlClass::SORT_TYPE sortType = (ListCtrlClass::SORT_TYPE)HIWORD(param); if (ListCtrlClass::SORT_DESCENDING == sortType) { return -retval; } return retval; }