This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Commando/WOLGameInfo.cpp

654 lines
No EOL
16 KiB
C++

/*
** 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/>.
*/
/******************************************************************************
*
* 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 <WWOnline\WOLUser.h>
#include <WWOnline\WOLChannel.h>
#include <WWOnline\PingProfile.h>
#include <WWLib\RealCRC.h>
#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<ChannelData>& 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<ChannelData>& 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 = "<Unknown>";
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<unsigned int>(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<unsigned int>(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<ChannelData>& 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<unsigned int>(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<unsigned int>(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<ChannelData>& 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<UserData>& 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;
}