/* ** 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/WOLGameInfo.cpp $ * * DESCRIPTION * * PROGRAMMER * $Author: Steve_t $ * * VERSION INFO * $Revision: 18 $ * $Modtime: 10/31/02 4:30p $ * ******************************************************************************/ #include "WOLGameInfo.h" #include "GameData.h" #include "CNetwork.h" #include #include #include #include #include "modpackagemgr.h" using namespace WWOnline; /****************************************************************************** * * NAME * WOLGameInfo::WOLGameInfo * * DESCRIPTION * Default constructor * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ WOLGameInfo::WOLGameInfo(void) : mIsDataValid(false), mIsMapValid(true), mPingTime(-1) { } /****************************************************************************** * * NAME * WOLGameInfo::WOLGameInfo * * DESCRIPTION * Import information about the game. * * INPUTS * Game - Game to import information from. * * RESULT * NONE * ******************************************************************************/ WOLGameInfo::WOLGameInfo(const cGameData& theGame) { ImportFromGame(theGame); } /****************************************************************************** * * NAME * WOLGameInfo::WOLGameInfo * * DESCRIPTION * Import information about the game from a channel. * * INPUTS * Channel - Channel to import game information from. * * RESULT * NONE * ******************************************************************************/ WOLGameInfo::WOLGameInfo(const RefPtr& channel) { ImportFromChannel(channel); } /****************************************************************************** * * NAME * WOLGameInfo::~WOLGameInfo * * DESCRIPTION * Destructor * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ WOLGameInfo::~WOLGameInfo(void) { } /****************************************************************************** * * NAME * WOLGameInfo::Reset * * DESCRIPTION * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void WOLGameInfo::Reset(void) { mIsDataValid = false; mIsMapValid = true; mVersion = 0; mGameType = 0; mMapName[0] = 0; mModName[0] = 0; mTitle[0] = 0; mMinPlayers = 0; mMaxPlayers = 0; mNumPlayers = 0; mClanID1 = 0; mClanID2 = 0; mIsMod = false; mIsLaddered = false; mIsPassworded = false; mIsQuickmatch = false; mIsDedicated = false; mIsFriendlyFire = false; mIsFreeWeapons = false; mIsTeamRemix = false; mIsTeamChange = false; mIsClanGame = false; mIsRepairBuildings = false; mIsDriverGunner = false; mSpawnWeapons = false; mPingTime = -1; } /****************************************************************************** * * NAME * WOLGameInfo::ImportFromGame * * DESCRIPTION * Import information about the game. * * INPUTS * Game - Game to import information from. * * RESULT * NONE * ******************************************************************************/ void WOLGameInfo::ImportFromGame(const cGameData& theGame) { Reset(); mIsDataValid = true; mIsMapValid = true; mVersion = theGame.Get_Version_Number(); mGameType = theGame.Get_Game_Type(); strncpy(mMapName, theGame.Get_Map_Name(), MAX_TEXT_LENGTH); mMapName[MAX_TEXT_LENGTH - 1] = 0; strncpy(mModName, theGame.Get_Mod_Name(), MAX_TEXT_LENGTH); mModName[MAX_TEXT_LENGTH - 1] = 0; wcstombs(mTitle, theGame.Get_Game_Title(), MAX_TEXT_LENGTH); mTitle[MAX_TEXT_LENGTH - 1] = 0; mMinPlayers = theGame.Get_Min_Players(); mMaxPlayers = theGame.Get_Max_Players(); mNumPlayers = theGame.Get_Current_Players(); mClanID1 = theGame.Get_Clan(0); mClanID2 = theGame.Get_Clan(1); const WCHAR* password = theGame.Get_Password(); mIsPassworded = ((password != NULL) && (wcslen(password) > 0)); mIsLaddered = theGame.IsLaddered.Get(); mIsQuickmatch = theGame.Is_QuickMatch_Server(); mIsDedicated = theGame.IsDedicated.Get(); mIsFriendlyFire = theGame.IsFriendlyFirePermitted.Get(); mIsFreeWeapons = theGame.IsFreeWeapons.Get(); mIsTeamRemix = theGame.RemixTeams.Get(); mIsTeamChange = theGame.IsTeamChangingAllowed.Get(); mIsClanGame = theGame.IsClanGame.Get(); // C&C mode flags mIsRepairBuildings = theGame.CanRepairBuildings.Get(); mIsDriverGunner = theGame.DriverIsAlwaysGunner.Get(); mSpawnWeapons = theGame.SpawnWeapons.Get(); mIsMod = (theGame.Get_Mod_Name().Is_Empty() == false); mPingTime = -1; } /****************************************************************************** * * NAME * WOLGameInfo::ImportFromChannel * * DESCRIPTION * Import information about the game from a channel. * * INPUTS * Channel - Channel to import game information from. * * RESULT * NONE * ******************************************************************************/ void WOLGameInfo::ImportFromChannel(const RefPtr& channel) { Reset(); if (channel.IsValid() == false) { mIsDataValid = false; return; } //ST - Test code //if (channel->GetName() != WideStringClass(L"ladtest07")) { // mIsDataValid = false; // return; //} const char* exInfo = channel->GetExtraInfo(); if ((exInfo == NULL) || (exInfo && (strlen(exInfo) != 36))) { WWDEBUG_SAY(("WOLERROR: Channel ExInfo NULL or wrong size\n")); mIsDataValid = false; return; } // Assume the data will be valid mIsDataValid = true; // Extract ExInfo settings unsigned long fileCRC = 0; unsigned long version = 0; unsigned long clanID1 = 0; unsigned long clanID2 = 0; unsigned char gameType = 0; unsigned char gameFlags1 = 0; unsigned char gameFlags2 = 0; unsigned char modMapIndex = 0; int count = sscanf(exInfo, "%08lX%08lX%08lX%08lX%c%c%c%c", &version, &fileCRC, &clanID1, &clanID2, &gameType, &gameFlags1, &gameFlags2, &modMapIndex); // There should be 8 parameters in the exinfo if (count != 8) { WWDEBUG_SAY(("WOLERROR: Channel ExInfo malformed\n")); mIsDataValid = false; } if (version != cNetwork::Get_Exe_Key()) { mIsDataValid = false; return; } mVersion = version; mGameType = (cGameData::GameTypeEnum)(gameType - '0'); if (mGameType != cGameData::GAME_TYPE_CNC) { WWDEBUG_SAY(("WOLERROR: GameType '%d' out of range\n", mGameType)); mIsDataValid = false; } // Extract game flags mIsLaddered = (channel->GetTournament() != 0); mIsPassworded = channel->IsPassworded(); mIsDedicated = ((gameFlags1 & 0x40) == 0x40); mIsFriendlyFire = ((gameFlags1 & 0x10) == 0x10); mIsFreeWeapons = ((gameFlags1 & 0x08) == 0x08); mIsTeamRemix = ((gameFlags1 & 0x04) == 0x04); mIsTeamChange = ((gameFlags1 & 0x02) == 0x02); mIsClanGame = ((gameFlags1 & 0x01) == 0x01); mIsMod = ((gameFlags2 & 0x40) == 0x40); mSpawnWeapons = ((gameFlags2 & 0x04) == 0x04); mIsRepairBuildings = ((gameFlags2 & 0x02) == 0x02); mIsDriverGunner = ((gameFlags2 & 0x01) == 0x01); // Find the mod and map names from their CRC StringClass mapName(0, true); mapName = ""; StringClass modName(0, true); bool got_map_name = true; if (mIsMod) { // Get the true index (+32 put the index into the printable range) modMapIndex -= 32; // Find mod based on its crc and get map filename based on the index. if (ModPackageMgrClass::Get_Mod_Map_Name_From_CRC_Index(fileCRC, modMapIndex, &modName, &mapName) == false) { //mIsDataValid = false; got_map_name = false; } } else { // Find mod based on its crc and get map filename based on the index. if (ModPackageMgrClass::Get_Mod_Map_Name_From_CRC (0, fileCRC, &modName, &mapName) == false) { //mIsDataValid = false; got_map_name = false; } } strncpy(mModName, modName, MAX_TEXT_LENGTH); mModName[MAX_TEXT_LENGTH - 1] = 0; strncpy(mMapName, mapName, MAX_TEXT_LENGTH); mMapName[MAX_TEXT_LENGTH - 1] = 0; mMinPlayers = channel->GetMinUsers(); mMaxPlayers = channel->GetMaxUsers(); mNumPlayers = channel->GetCurrentUsers(); // Do not count the host as a player for dedicated servers. if (mIsDedicated) { WWASSERT(mNumPlayers > 0); --mNumPlayers; WWASSERT(mMaxPlayers > 0); --mMaxPlayers; } mClanID1 = clanID1; mClanID2 = clanID2; const char* topic = channel->GetTopic(); if (topic == NULL) { WWDEBUG_SAY(("WOLERROR: Channel Topic is NULL\n")); mIsDataValid = false; } // Quickmatch settings are marked by a pipe '|' character mIsQuickmatch = (strchr(topic, '|') != NULL); // Extract game title from topic unsigned int titleLength = (topic[0] - 0x20); WWASSERT(titleLength <= 32); titleLength = min(titleLength, 32); ++topic; strncpy(mTitle, topic, titleLength); mTitle[titleLength] = 0; WWASSERT(strlen(mTitle) == titleLength); topic += titleLength; unsigned int mapLength = (topic[0] - 0x20); WWASSERT(mapLength <= 16); mapLength = min(mapLength, 16); ++topic; if (!got_map_name) { strncpy(mMapName, topic, mapLength); mMapName[mapLength] = 0; WWASSERT(strlen(mMapName) == mapLength); mIsMapValid = false; } topic += mapLength; // Calculate ping PingProfile pings; DecodePingProfile(topic, pings); const PingProfile& localPings = GetLocalPingProfile(); mPingTime = ComparePingProfile(localPings, pings); } /****************************************************************************** * * NAME * WOLGameInfo::ExportToChannel * * DESCRIPTION * Export game information into a channel. * * INPUTS * Channel - Channel to export game information into. * * RESULT * NONE * ******************************************************************************/ void WOLGameInfo::ExportToChannel(const RefPtr& channel) { if (channel.IsValid() && mIsDataValid) { // If this is a dedicated server then the max players must be incremented // by one to account for the host. unsigned int maxPlayers = (mIsDedicated == true) ? (mMaxPlayers + 1) : mMaxPlayers; // Set the channels min and max players channel->SetMinMaxUsers(mMinPlayers, maxPlayers); // Set the tournament type unsigned int tournamentType = (mIsLaddered ? 1 : 0); channel->SetTournament(tournamentType); //------------------------------------------------------------------------- // Encode ExInfo //------------------------------------------------------------------------- // For flags bit 6 must be set to keep the overall value at least a space character unsigned char gameFlags1 = 0x20; gameFlags1 |= mIsDedicated ? 0x40 : 0x00; gameFlags1 |= mIsFriendlyFire ? 0x10 : 0x00; gameFlags1 |= mIsFreeWeapons ? 0x08 : 0x00; gameFlags1 |= mIsTeamRemix ? 0x04 : 0x00; gameFlags1 |= mIsTeamChange ? 0x02 : 0x00; gameFlags1 |= mIsClanGame ? 0x01 : 0x00; unsigned char gameFlags2 = 0x20; gameFlags2 |= mIsMod ? 0x40 : 0x00; gameFlags2 |= mSpawnWeapons ? 0x04: 0x00; gameFlags2 |= mIsRepairBuildings ? 0x02: 0x00; gameFlags2 |= mIsDriverGunner ? 0x01: 0x00; // The file CRC is either the map name or the mod name depending on if // the mod flag is set. unsigned long fileCRC = 0; unsigned char modMapIndex = 0; if (mIsMod) { ModPackageClass& package = ModPackageMgrClass::Get_Current_Package(); fileCRC = package.Get_CRC(); modMapIndex = package.Get_Map_Index(mMapName); } else { fileCRC = ::CRC_Stringi(mMapName); } // Put the map index into the printable ASCII range modMapIndex += 32; // WARNING: The channels ExInfo field has a maximum size of 40 bytes. char exInfo[41]; sprintf(exInfo, "%08lX%08lX%08lX%08lX%c%c%c%c", mVersion, fileCRC, mClanID1, mClanID2, (0x30 + mGameType), gameFlags1, gameFlags2, modMapIndex); channel->SetExtraInfo(exInfo); //------------------------------------------------------------------------- // Encode topic //------------------------------------------------------------------------- unsigned int titleLength = min(strlen(mTitle), 32); titleLength += 0x20; // WARNING: The channels topic field has a maximum size of 80 bytes. // Ping profile and quickmatch settings are encoded in the topic in // addition to the the game title. The combined maximum of ALL these // entries MUST NEVER exceed 80 bytes. char topic[81]; sprintf(topic, "%c%.32s", titleLength, mTitle); // // Only using 61 max right now. Room for a map name maybe? ST - 10/31/2002 2:55PM // unsigned int mapLength = min(strlen(mMapName), 16); mapLength += 0x20; // WARNING: The channels topic field has a maximum size of 80 bytes. // Ping profile and quickmatch settings are encoded in the topic in // addition to the the game title. The combined maximum of ALL these // entries MUST NEVER exceed 80 bytes. char mapinfo[81]; sprintf(mapinfo, "%c%.16s", mapLength, mMapName); strcat(topic, mapinfo); // Add our ping profile const PingProfile& localPings = GetLocalPingProfile(); EncodePingProfile(localPings, topic); channel->SetTopic(topic); } } /****************************************************************************** * * NAME * WOLGameInfo:: * * DESCRIPTION * * INPUTS * * RESULT * ******************************************************************************/ bool WOLGameInfo::IsValidGameChannel(const RefPtr& channel) { WOLGameInfo gameInfo(channel); return (gameInfo.IsDataValid() && (gameInfo.mVersion == (unsigned long)cNetwork::Get_Exe_Key())); } /****************************************************************************** * * NAME * WOLGameInfo::IsClanCompeting * * DESCRIPTION * Check if the specified clan is competing in the game. * * INPUTS * Clan - ID of clan to check. * * RESULT * True if clan is competing. * ******************************************************************************/ bool WOLGameInfo::IsClanCompeting(unsigned long clanID) const { return (mIsClanGame && (clanID != 0) && ((clanID == mClanID1) || (clanID == mClanID2))); } /****************************************************************************** * * NAME * WOLGameInfo::IsClanGameOpen * * DESCRIPTION * Check if the clan game is open for new clans to join. * * INPUTS * NONE * * RESULT * True if the game is open to clans. * ******************************************************************************/ bool WOLGameInfo::IsClanGameOpen(void) const { return (mIsClanGame && ((0 == mClanID1) || (0 == mClanID2))); } /****************************************************************************** * * NAME * WOLGameInfo::CanUserJoin * * DESCRIPTION * Check if the user is able to join this game. * * INPUTS * User * * RESULT * True if user can join this game. * ******************************************************************************/ bool WOLGameInfo::CanUserJoin(const RefPtr& user) { if (!user.IsValid() || !mIsDataValid || !mIsMapValid) { return false; } if (mVersion != (unsigned long)cNetwork::Get_Exe_Key()) { return false; } if (mIsClanGame) { unsigned long userClanID = user->GetSquadID(); return ((0 != userClanID) && (IsClanGameOpen() || IsClanCompeting(userClanID))); } return true; }