From b83de8e7584a1d3b4e9131fc5c832818bdf36e66 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:08:36 +0200 Subject: [PATCH 1/2] unify(network): Merge GameNetwork and related code (#1735) --- .../Include/Common/CustomMatchPreferences.h | 11 ++ .../GameEngine/Include/Common/GlobalData.h | 5 +- .../Include/Common/MultiplayerSettings.h | 20 +++- .../Include/Common/SkirmishPreferences.h | 6 ++ .../Include/Common/UserPreferences.h | 6 ++ .../Include/GameNetwork/DisconnectManager.h | 1 - .../GameEngine/Include/GameNetwork/GUIUtil.h | 3 +- .../GameEngine/Include/GameNetwork/GameInfo.h | 22 ++++ .../GameEngine/Source/Common/GlobalData.cpp | 4 +- .../GameEngine/Source/Common/RTS/Player.cpp | 12 ++- .../Source/Common/UserPreferences.cpp | 82 ++++++++++++++ .../GUI/GUICallbacks/Menus/LanLobbyMenu.cpp | 42 ++++++++ .../Menus/SkirmishGameOptionsMenu.cpp | 44 ++++++++ .../Source/GameNetwork/ConnectionManager.cpp | 2 + .../Source/GameNetwork/FirewallHelper.cpp | 1 - .../Source/GameNetwork/GameInfo.cpp | 101 ++++++++++++++++-- .../GameEngine/Source/GameNetwork/LANAPI.cpp | 1 - .../Source/GameNetwork/LANAPICallbacks.cpp | 6 +- .../Source/GameNetwork/LANAPIhandlers.cpp | 3 + .../GameEngine/Source/GameNetwork/Network.cpp | 6 ++ .../GameEngine/Source/Common/RTS/Player.cpp | 2 +- .../Source/GameNetwork/ConnectionManager.cpp | 16 +++ .../Source/GameNetwork/GameInfo.cpp | 9 ++ .../GameEngine/Source/GameNetwork/LANAPI.cpp | 6 +- .../Source/GameNetwork/LANAPIhandlers.cpp | 8 +- .../GameEngine/Source/GameNetwork/Network.cpp | 2 + 26 files changed, 394 insertions(+), 27 deletions(-) diff --git a/Generals/Code/GameEngine/Include/Common/CustomMatchPreferences.h b/Generals/Code/GameEngine/Include/Common/CustomMatchPreferences.h index d5631fb4e4..cc384c60a0 100644 --- a/Generals/Code/GameEngine/Include/Common/CustomMatchPreferences.h +++ b/Generals/Code/GameEngine/Include/Common/CustomMatchPreferences.h @@ -75,4 +75,15 @@ class CustomMatchPreferences : public UserPreferences Bool getDisallowNonAsianText( void ); void setDisallowNonAsianText( Bool val ); + Bool getSuperweaponRestricted(void) const; + void setSuperweaponRestricted( Bool superweaponRestricted); + + Money getStartingCash(void) const; + void setStartingCash( const Money &startingCash ); + + Bool getFactionsLimited(void) const; // Prefers to only use the original 3 sides, not USA Air Force General, GLA Toxin General, et al + void setFactionsLimited( Bool factionsLimited ); + + Bool getUseStats( void ) const; + void setUseStats( Bool useStats ); }; diff --git a/Generals/Code/GameEngine/Include/Common/GlobalData.h b/Generals/Code/GameEngine/Include/Common/GlobalData.h index f2ee30eaf1..7868846753 100644 --- a/Generals/Code/GameEngine/Include/Common/GlobalData.h +++ b/Generals/Code/GameEngine/Include/Common/GlobalData.h @@ -36,6 +36,7 @@ #include "Common/SubsystemInterface.h" #include "GameClient/Color.h" #include "Common/STLTypedefs.h" +#include "Common/Money.h" // FORWARD DECLARATIONS /////////////////////////////////////////////////////////////////////////// struct FieldParse; @@ -438,8 +439,8 @@ class GlobalData : public SubsystemInterface Real m_standardMinefieldDistance; - Bool m_showMetrics; ///< whether or not to show the metrics. - Int m_defaultStartingCash; ///< The amount of cash a player starts with by default. + Bool m_showMetrics; ///< whether or not to show the metrics. + Money m_defaultStartingCash; ///< The amount of cash a player starts with by default. Bool m_debugShowGraphicalFramerate; ///< Whether or not to show the graphical framerate bar. diff --git a/Generals/Code/GameEngine/Include/Common/MultiplayerSettings.h b/Generals/Code/GameEngine/Include/Common/MultiplayerSettings.h index 9cfb403465..af79c5dca7 100644 --- a/Generals/Code/GameEngine/Include/Common/MultiplayerSettings.h +++ b/Generals/Code/GameEngine/Include/Common/MultiplayerSettings.h @@ -30,6 +30,7 @@ #pragma once #include "GameClient/Color.h" +#include "Common/Money.h" // FORWARD DECLARATIONS /////////////////////////////////////////////////////////////////////////// struct FieldParse; @@ -66,6 +67,9 @@ class MultiplayerColorDefinition typedef std::map MultiplayerColorList; typedef std::map::iterator MultiplayerColorIter; +// A list of values to display in the starting money dropdown +typedef std::vector< Money > MultiplayerStartingMoneyList; + //------------------------------------------------------------------------------------------------- /** Multiplayer Settings container class * Defines multiplayer settings */ @@ -88,8 +92,6 @@ class MultiplayerSettings : public SubsystemInterface MultiplayerColorDefinition * findMultiplayerColorDefinitionByName(AsciiString name); MultiplayerColorDefinition * newMultiplayerColorDefinition(AsciiString name); - inline Int getInitialCreditsMin( void ) { return m_initialCreditsMin; } - inline Int getInitialCreditsMax( void ) { return m_initialCreditsMax; } inline Int getStartCountdownTimerSeconds( void ) { return m_startCountdownTimerSeconds; } inline Int getMaxBeaconsPerPlayer( void ) { return m_maxBeaconsPerPlayer; } inline Bool isShroudInMultiplayer( void ) { return m_isShroudInMultiplayer; } @@ -106,6 +108,17 @@ class MultiplayerSettings : public SubsystemInterface } MultiplayerColorDefinition * getColor(Int which); + + const Money & getDefaultStartingMoney() const + { + DEBUG_ASSERTCRASH( m_gotDefaultStartingMoney, ("You must specify a default starting money amount in multiplayer.ini") ); + return m_defaultStartingMoney; + } + + const MultiplayerStartingMoneyList & getStartingMoneyList() const { return m_startingMoneyList; } + + void addStartingMoneyChoice( const Money & money, Bool isDefault ); + private: Int m_initialCreditsMin; Int m_initialCreditsMax; @@ -120,6 +133,9 @@ class MultiplayerSettings : public SubsystemInterface Int m_numColors; MultiplayerColorDefinition m_observerColor; MultiplayerColorDefinition m_randomColor; + MultiplayerStartingMoneyList m_startingMoneyList; + Money m_defaultStartingMoney; + Bool m_gotDefaultStartingMoney; }; // singleton diff --git a/Generals/Code/GameEngine/Include/Common/SkirmishPreferences.h b/Generals/Code/GameEngine/Include/Common/SkirmishPreferences.h index 855fd78744..562b4720af 100644 --- a/Generals/Code/GameEngine/Include/Common/SkirmishPreferences.h +++ b/Generals/Code/GameEngine/Include/Common/SkirmishPreferences.h @@ -54,4 +54,10 @@ class SkirmishPreferences : public UserPreferences Int getPreferredColor(void); // convenience function AsciiString getPreferredMap(void); // convenience function Bool usesSystemMapDir(void); // convenience function + + Bool getSuperweaponRestricted(void) const; + void setSuperweaponRestricted( Bool superweaponRestricted); + + Money getStartingCash(void) const; + void setStartingCash( const Money &startingCash ); }; diff --git a/Generals/Code/GameEngine/Include/Common/UserPreferences.h b/Generals/Code/GameEngine/Include/Common/UserPreferences.h index dd10d91161..3727887199 100644 --- a/Generals/Code/GameEngine/Include/Common/UserPreferences.h +++ b/Generals/Code/GameEngine/Include/Common/UserPreferences.h @@ -35,6 +35,7 @@ //----------------------------------------------------------------------------- #include "Common/STLTypedefs.h" +class Money; typedef UnsignedInt CursorCaptureMode; typedef UnsignedInt ScreenEdgeScrollMode; @@ -164,4 +165,9 @@ class LANPreferences : public UserPreferences Bool usesSystemMapDir(void); // convenience function Int getNumRemoteIPs(void); // convenience function UnicodeString getRemoteIPEntry(Int i); // convenience function + + Bool getSuperweaponRestricted(void) const; + Money getStartingCash(void) const; + void setSuperweaponRestricted( Bool superweaponRestricted); + void setStartingCash( const Money & startingCash ); }; diff --git a/Generals/Code/GameEngine/Include/GameNetwork/DisconnectManager.h b/Generals/Code/GameEngine/Include/GameNetwork/DisconnectManager.h index 9b0b16dccf..0448566d88 100644 --- a/Generals/Code/GameEngine/Include/GameNetwork/DisconnectManager.h +++ b/Generals/Code/GameEngine/Include/GameNetwork/DisconnectManager.h @@ -119,7 +119,6 @@ class DisconnectManager Bool m_haveNotifiedOtherPlayersOfCurrentFrame; time_t m_timeOfDisconnectScreenOn; - Int m_pingsSent; Int m_pingsRecieved; UnsignedInt m_pingFrame; diff --git a/Generals/Code/GameEngine/Include/GameNetwork/GUIUtil.h b/Generals/Code/GameEngine/Include/GameNetwork/GUIUtil.h index 4f7db27a35..fc3b8059a4 100644 --- a/Generals/Code/GameEngine/Include/GameNetwork/GUIUtil.h +++ b/Generals/Code/GameEngine/Include/GameNetwork/GUIUtil.h @@ -34,8 +34,9 @@ void ShowUnderlyingGUIElements( Bool show, const char *layoutFilename, const cha const char **gadgetsToHide, const char **perPlayerGadgetsToHide ); void PopulateColorComboBox(Int comboBox, GameWindow *comboArray[], GameInfo *myGame, Bool isObserver = FALSE); -void PopulatePlayerTemplateComboBox(Int comboBox, GameWindow *comboArray[], GameInfo *myGame, Bool allowObservers); +void PopulatePlayerTemplateComboBox(Int comboBox, GameWindow *comboArray[], GameInfo *myGame, Bool allowObservers ); void PopulateTeamComboBox(Int comboBox, GameWindow *comboArray[], GameInfo *myGame, Bool isObserver = FALSE); +void PopulateStartingCashComboBox(GameWindow *comboBox, GameInfo *myGame); void EnableSlotListUpdates( Bool val ); Bool AreSlotListUpdatesEnabled( void ); diff --git a/Generals/Code/GameEngine/Include/GameNetwork/GameInfo.h b/Generals/Code/GameEngine/Include/GameNetwork/GameInfo.h index 103287154d..21aab5bc52 100644 --- a/Generals/Code/GameEngine/Include/GameNetwork/GameInfo.h +++ b/Generals/Code/GameEngine/Include/GameNetwork/GameInfo.h @@ -29,6 +29,7 @@ #pragma once #include "Common/Snapshot.h" +#include "Common/Money.h" #include "GameNetwork/NetworkDefs.h" #include "GameNetwork/FirewallHelper.h" @@ -190,6 +191,13 @@ class GameInfo inline Int getMapContentsMask( void ) const; ///< Get the map contents mask void setSeed( Int seed ); ///< Set the random seed for the game inline Int getSeed( void ) const; ///< Get the game seed + inline Int getUseStats( void ) const; ///< Does this game count towards gamespy stats? + inline void setUseStats( Int useStats ); + + inline UnsignedShort getSuperweaponRestriction( void ) const; ///< Get any optional limits on superweapons + void setSuperweaponRestriction( UnsignedShort restriction ); ///< Set the optional limits on superweapons + inline const Money & getStartingCash(void) const; + void setStartingCash( const Money & startingCash ); void setSlotPointer( Int index, GameSlot *slot ); ///< Set the slot info pointer @@ -219,6 +227,9 @@ class GameInfo Bool isPlayerPreorder(Int index); void markPlayerAsPreorder(Int index); + inline Bool oldFactionsOnly(void) const; + inline void setOldFactionsOnly( Bool oldFactionsOnly ); + protected: Int m_preorderMask; Int m_crcInterval; @@ -236,6 +247,10 @@ class GameInfo UnsignedInt m_mapSize; Int m_mapMask; Int m_seed; + Int m_useStats; + Money m_startingCash; + UnsignedShort m_superweaponRestriction; + Bool m_oldFactionsOnly; // Only USA, China, GLA -- not USA Air Force General, GLA Toxic General, et al }; extern GameInfo *TheGameInfo; @@ -251,6 +266,12 @@ Bool GameInfo::isInGame( void ) const { return m_inGame; } void GameInfo::setInGame( void ) { m_inGame = true; } Bool GameInfo::isGameInProgress( void ) const { return m_inProgress; } void GameInfo::setGameInProgress( Bool inProgress ) { m_inProgress = inProgress; } +Int GameInfo::getUseStats( void ) const { return m_useStats; } +void GameInfo::setUseStats( Int useStats ) { m_useStats = useStats; } +const Money&GameInfo::getStartingCash( void ) const { return m_startingCash; } +UnsignedShort GameInfo::getSuperweaponRestriction( void ) const { return m_superweaponRestriction; } +Bool GameInfo::oldFactionsOnly(void) const { return m_oldFactionsOnly; } +void GameInfo::setOldFactionsOnly( Bool oldFactionsOnly ) { m_oldFactionsOnly = oldFactionsOnly; } AsciiString GameInfoToAsciiString( const GameInfo *game ); Bool ParseAsciiStringToGameInfo( GameInfo *game, AsciiString options ); @@ -281,3 +302,4 @@ class SkirmishGameInfo : public GameInfo, public Snapshot }; extern SkirmishGameInfo *TheSkirmishGameInfo; +extern SkirmishGameInfo *TheChallengeGameInfo; diff --git a/Generals/Code/GameEngine/Source/Common/GlobalData.cpp b/Generals/Code/GameEngine/Source/Common/GlobalData.cpp index 8ad763222e..aed88c1afa 100644 --- a/Generals/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/Generals/Code/GameEngine/Source/Common/GlobalData.cpp @@ -464,8 +464,8 @@ GlobalData* GlobalData::m_theOriginal = NULL; { "ObjectPlacementShadows", INI::parseBool, NULL, offsetof( GlobalData, m_objectPlacementShadows ) }, { "StandardPublicBone", INI::parseAsciiStringVectorAppend, NULL, offsetof(GlobalData, m_standardPublicBones) }, - { "ShowMetrics", INI::parseBool, NULL, offsetof( GlobalData, m_showMetrics ) }, - { "DefaultStartingCash", INI::parseUnsignedInt, NULL, offsetof( GlobalData, m_defaultStartingCash ) }, + { "ShowMetrics", INI::parseBool, NULL, offsetof( GlobalData, m_showMetrics ) }, + { "DefaultStartingCash", Money::parseMoneyAmount, NULL, offsetof( GlobalData, m_defaultStartingCash ) }, // NOTE: m_doubleClickTimeMS is still in use, but we disallow setting it from the GameData.ini file. It is now set in the constructor according to the windows parameter. // { "DoubleClickTimeMS", INI::parseUnsignedInt, NULL, offsetof( GlobalData, m_doubleClickTimeMS ) }, diff --git a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp index 177c6e5a0e..b43934e9e5 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -98,6 +98,7 @@ #include "GameLogic/Module/BattlePlanUpdate.h" #include "GameLogic/VictoryConditions.h" +#include "GameNetwork/GameInfo.h" //Grey for neutral. @@ -429,7 +430,16 @@ void Player::init(const PlayerTemplate* pt) if( m_money.countMoney() == 0 ) { - m_money.deposit( TheGlobalData->m_defaultStartingCash, FALSE ); + // TheSuperHackers @bugfix Now correctly deposits the money and fixes its audio and academy issues. + // Note that copying the entire Money class instead would also copy the player index inside of it. + if ( TheGameInfo ) + { + m_money.deposit( TheGameInfo->getStartingCash().countMoney(), FALSE ); + } + else + { + m_money.deposit( TheGlobalData->m_defaultStartingCash.countMoney(), FALSE ); + } } m_playerDisplayName.clear(); diff --git a/Generals/Code/GameEngine/Source/Common/UserPreferences.cpp b/Generals/Code/GameEngine/Source/Common/UserPreferences.cpp index e975b44632..7aba8c271e 100644 --- a/Generals/Code/GameEngine/Source/Common/UserPreferences.cpp +++ b/Generals/Code/GameEngine/Source/Common/UserPreferences.cpp @@ -671,6 +671,88 @@ void CustomMatchPreferences::setPreferredMap(AsciiString val) (*this)["Map"] = AsciiStringToQuotedPrintable(val); } + +static const char superweaponRestrictionKey[] = "SuperweaponRestrict"; + +Bool CustomMatchPreferences::getSuperweaponRestricted(void) const +{ + const_iterator it = find(superweaponRestrictionKey); + if (it == end()) + { + return false; + } + + return ( it->second.compareNoCase( "yes" ) == 0 ); +} + +void CustomMatchPreferences::setSuperweaponRestricted( Bool superweaponRestricted ) +{ + (*this)[superweaponRestrictionKey] = superweaponRestricted ? "Yes" : "No"; +} + +static const char startingCashKey[] = "StartingCash"; +Money CustomMatchPreferences::getStartingCash(void) const +{ + const_iterator it = find(startingCashKey); + if (it == end()) + { + return TheMultiplayerSettings->getDefaultStartingMoney(); + } + + Money money; + money.deposit( strtoul( it->second.str(), NULL, 10 ), FALSE ); + + return money; +} + +void CustomMatchPreferences::setStartingCash( const Money & startingCash ) +{ + AsciiString option; + + option.format( "%d", startingCash.countMoney() ); + + (*this)[startingCashKey] = option; +} + + +static const char limitFactionsKey[] = "LimitArmies"; + +// Prefers to only use the original 3 sides, not USA Air Force General, GLA Toxin General, et al +Bool CustomMatchPreferences::getFactionsLimited(void) const +{ + const_iterator it = find(limitFactionsKey); + if (it == end()) + { + return false; // The default + } + + return ( it->second.compareNoCase( "yes" ) == 0 ); +} + +void CustomMatchPreferences::setFactionsLimited( Bool factionsLimited ) +{ + (*this)[limitFactionsKey] = factionsLimited ? "Yes" : "No"; +} + + +static const char useStatsKey[] = "UseStats"; + +Bool CustomMatchPreferences::getUseStats(void) const +{ + const_iterator it = find(useStatsKey); + if (it == end()) + { + return true; // The default + } + + return ( it->second.compareNoCase( "yes" ) == 0 ); +} + +void CustomMatchPreferences::setUseStats( Bool useStats ) +{ + (*this)[useStatsKey] = useStats ? "Yes" : "No"; +} + //----------------------------------------------------------------------------- // GameSpyMiscPreferences base class //----------------------------------------------------------------------------- diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanLobbyMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanLobbyMenu.cpp index 5015309083..8e8a60605a 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanLobbyMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanLobbyMenu.cpp @@ -243,6 +243,48 @@ UnicodeString LANPreferences::getRemoteIPEntry(Int i) return ret; } +static const char superweaponRestrictionKey[] = "SuperweaponRestrict"; + +Bool LANPreferences::getSuperweaponRestricted(void) const +{ + LANPreferences::const_iterator it = find(superweaponRestrictionKey); + if (it == end()) + { + return false; + } + + return ( it->second.compareNoCase( "yes" ) == 0 ); +} + +void LANPreferences::setSuperweaponRestricted( Bool superweaponRestricted ) +{ + (*this)[superweaponRestrictionKey] = superweaponRestricted ? "Yes" : "No"; +} + +static const char startingCashKey[] = "StartingCash"; +Money LANPreferences::getStartingCash(void) const +{ + LANPreferences::const_iterator it = find(startingCashKey); + if (it == end()) + { + return TheMultiplayerSettings->getDefaultStartingMoney(); + } + + Money money; + money.deposit( strtoul( it->second.str(), NULL, 10 ), FALSE ); + + return money; +} + +void LANPreferences::setStartingCash( const Money & startingCash ) +{ + AsciiString option; + + option.format( "%d", startingCash.countMoney() ); + + (*this)[startingCashKey] = option; +} + // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/SkirmishGameOptionsMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/SkirmishGameOptionsMenu.cpp index 954509f710..474d3d0dcf 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/SkirmishGameOptionsMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/SkirmishGameOptionsMenu.cpp @@ -294,6 +294,50 @@ AsciiString SkirmishPreferences::getPreferredMap(void) return ret; } +static const char superweaponRestrictionKey[] = "SuperweaponRestrict"; + +Bool SkirmishPreferences::getSuperweaponRestricted(void) const +{ + const_iterator it = find(superweaponRestrictionKey); + if (it == end()) + { + return false; + } + + return ( it->second.compareNoCase( "yes" ) == 0 ); +} + +void SkirmishPreferences::setSuperweaponRestricted( Bool superweaponRestricted ) +{ + (*this)[superweaponRestrictionKey] = superweaponRestricted ? "Yes" : "No"; +} + +static const char startingCashKey[] = "StartingCash"; +Money SkirmishPreferences::getStartingCash(void) const +{ + const_iterator it = find(startingCashKey); + if (it == end()) + { + return TheMultiplayerSettings->getDefaultStartingMoney(); + } + + Money money; + money.deposit( strtoul( it->second.str(), NULL, 10 ), FALSE ); + + return money; +} + +void SkirmishPreferences::setStartingCash( const Money & startingCash ) +{ + AsciiString option; + + option.format( "%d", startingCash.countMoney() ); + + (*this)[startingCashKey] = option; +} + + + Bool SkirmishPreferences::write(void) { if (!TheSkirmishGameInfo) diff --git a/Generals/Code/GameEngine/Source/GameNetwork/ConnectionManager.cpp b/Generals/Code/GameEngine/Source/GameNetwork/ConnectionManager.cpp index 3361ddf350..be9a3fcf51 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/ConnectionManager.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/ConnectionManager.cpp @@ -1753,6 +1753,7 @@ void ConnectionManager::quitGame() { disconnectMsg->detach(); +#if RTS_GENERALS // if we get here, we hit Quit on the disconnect screen. Mark everyone as having disconnected from us // so the online stats can give us appropriate feedback. if (TheGameInfo) @@ -1766,6 +1767,7 @@ void ConnectionManager::quitGame() { } } } +#endif disconnectLocalPlayer(); } diff --git a/Generals/Code/GameEngine/Source/GameNetwork/FirewallHelper.cpp b/Generals/Code/GameEngine/Source/GameNetwork/FirewallHelper.cpp index fab68b6898..db36c9acd9 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/FirewallHelper.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/FirewallHelper.cpp @@ -448,7 +448,6 @@ UnsignedShort FirewallHelperClass::getManglerResponse(UnsignedShort packetID, In } Int retval = m_spareSockets[i].udp->Read((unsigned char *)message, sizeof(ManglerData), &addr); if (retval > 0) { - CRC crc; crc.computeCRC((unsigned char *)(&(message->data.magic)), sizeof(ManglerData) - sizeof(unsigned int)); if (crc.get() != htonl(message->data.CRC)) { diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameInfo.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameInfo.cpp index 340e2c079a..ff608a434c 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameInfo.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameInfo.cpp @@ -304,13 +304,18 @@ void GameInfo::reset( void ) m_mapName = AsciiString("NOMAP"); m_mapMask = 0; m_seed = GetTickCount(); //GameClientRandomValue(0, INT_MAX - 1); + m_useStats = TRUE; m_surrendered = FALSE; + m_oldFactionsOnly = FALSE; // Added By Sadullah Nader // Initializations missing and needed // m_localIP = 0; // BGC - actually we don't want this to be reset since the m_localIP is // set properly in the constructor of LANGameInfo which uses this as a base class. m_mapCRC = 0; m_mapSize = 0; + m_superweaponRestriction = 0; + m_startingCash = TheGlobalData->m_defaultStartingCash; + // for (Int i=0; igetMapContentsMask(), newMapName.str(), game->getMapCRC(), game->getMapSize(), game->getSeed(), game->getCRCInterval()); +#else + optionsString.format("US=%d;M=%2.2x%s;MC=%X;MS=%d;SD=%d;C=%d;SR=%u;SC=%u;O=%c;", game->getUseStats(), game->getMapContentsMask(), newMapName.str(), + game->getMapCRC(), game->getMapSize(), game->getSeed(), game->getCRCInterval(), game->getSuperweaponRestriction(), + game->getStartingCash().countMoney(), game->oldFactionsOnly() ? 'Y' : 'N' ); +#endif + + //add player info for each slot optionsString.concat(slotListID); optionsString.concat('='); for (Int i=0; igetConstSlot(i); + AsciiString str; if (slot && slot->isHuman()) { - AsciiString name = WideCharStringToMultiByte(slot->getName().str()).c_str(); - - str.format( "H%s,%X,%d,%c%c,%d,%d,%d,%d,%d:", - name.str(), slot->getIP(), slot->getPort(), + AsciiString tmp; //all this data goes after name + tmp.format( ",%X,%d,%c%c,%d,%d,%d,%d,%d:", + slot->getIP(), slot->getPort(), (slot->isAccepted()?'T':'F'), (slot->hasMap()?'T':'F'), slot->getColor(), slot->getPlayerTemplate(), slot->getStartPos(), slot->getTeamNumber(), slot->getNATBehavior() ); + //make sure name doesn't cause overflow of m_lanMaxOptionsLength + int lenCur = tmp.getLength() + optionsString.getLength() + 2; //+2 for H and trailing ; + int lenRem = m_lanMaxOptionsLength - lenCur; //length remaining before overflowing + int lenMax = lenRem / (MAX_SLOTS-i); //share lenRem with all remaining slots + AsciiString name = WideCharStringToMultiByte(slot->getName().str()).c_str(); + while( name.getLength() > lenMax ) + name.removeLastChar(); //what a horrible way to truncate. I hate AsciiString. + + str.format( "H%s%s", name.str(), tmp.str() ); } else if (slot && slot->isAI()) { @@ -986,9 +1018,13 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options) Int seed = 0; Int crc = 100; Bool sawCRC = FALSE; + Bool oldFactionsOnly = FALSE; + Int useStats = TRUE; + Money startingCash = TheGlobalData->m_defaultStartingCash; + UnsignedShort restriction = 0; // Always the default - Bool sawMap, sawMapCRC, sawMapSize, sawSeed, sawSlotlist; - sawMap = sawMapCRC = sawMapSize = sawSeed = sawSlotlist = FALSE; + Bool sawMap, sawMapCRC, sawMapSize, sawSeed, sawSlotlist, sawUseStats, sawSuperweaponRestriction, sawStartingCash, sawOldFactions; + sawMap = sawMapCRC = sawMapSize = sawSeed = sawSlotlist = sawUseStats = sawSuperweaponRestriction = sawStartingCash = sawOldFactions = FALSE; //DEBUG_LOG(("Saw options of %s", options.str())); DEBUG_LOG(("ParseAsciiStringToGameInfo - parsing [%s]", options.str())); @@ -1015,6 +1051,12 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options) break; } + if (key.compare("US") == 0) + { + useStats = atoi(val.str()); + sawUseStats = true; + } + else if (key.compare("M") == 0) { if (val.getLength() < 3) @@ -1075,6 +1117,23 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options) crc = atoi(val.str()); sawCRC = TRUE; } + else if (key.compare("SR") == 0 ) + { + restriction = (UnsignedShort)atoi(val.str()); + sawSuperweaponRestriction = TRUE; + } + else if (key.compare("SC") == 0 ) + { + UnsignedInt startingCashAmount = strtoul( val.str(), NULL, 10 ); + startingCash.init(); + startingCash.deposit( startingCashAmount, FALSE ); + sawStartingCash = TRUE; + } + else if (key.compare("O") == 0 ) + { + oldFactionsOnly = ( val.compareNoCase( "Y" ) == 0 ); + sawOldFactions = TRUE; + } else if (key.getLength() == 1 && *key.str() == slotListID) { sawSlotlist = true; @@ -1106,7 +1165,7 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options) break; } UnicodeString name; - name.set(MultiByteToWideCharSingleLine(slotValue.str() +1).c_str()); + name.set(MultiByteToWideCharSingleLine(slotValue.str() +1).c_str()); //DEBUG_LOG(("ParseAsciiStringToGameInfo - name is %s", slotValue.str()+1)); @@ -1414,7 +1473,7 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options) free(buf); //DEBUG_LOG(("Options were ok == %d", optionsOk)); - if (optionsOk && sawMap && sawMapCRC && sawMapSize && sawSeed && sawSlotlist && sawCRC) + if (optionsOk && sawMap && sawMapCRC && sawMapSize && sawSeed && sawSlotlist && sawCRC && sawUseStats && sawSuperweaponRestriction && sawStartingCash && sawOldFactions ) { // We were setting the Global Data directly here, but Instead, I'm now // first setting the data in game. We'll set the global data when @@ -1433,6 +1492,10 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options) game->setMapContentsMask(mapContentsMask); game->setSeed(seed); game->setCRCInterval(crc); + game->setUseStats(useStats); + game->setSuperweaponRestriction(restriction); + game->setStartingCash( startingCash ); + game->setOldFactionsOnly( oldFactionsOnly ); return true; } @@ -1459,7 +1522,11 @@ void SkirmishGameInfo::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ void SkirmishGameInfo::xfer( Xfer *xfer ) { +#if RTS_GENERALS const XferVersion currentVersion = 2; +#else + const XferVersion currentVersion = 4; +#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -1538,6 +1605,24 @@ void SkirmishGameInfo::xfer( Xfer *xfer ) xfer->xferInt(&m_mapMask); xfer->xferInt(&m_seed); + if ( version >= 3 ) + { + xfer->xferUnsignedShort( &m_superweaponRestriction ); + + if ( version == 3 ) + { + // Version 3 had a bool which is now gone + Bool obsoleteBool; + xfer->xferBool( &obsoleteBool ); + } + + xfer->xferSnapshot( &m_startingCash ); + } + else if ( xfer->getXferMode() == XFER_LOAD ) + { + m_superweaponRestriction = 0; + m_startingCash = TheGlobalData->m_defaultStartingCash; + } } diff --git a/Generals/Code/GameEngine/Source/GameNetwork/LANAPI.cpp b/Generals/Code/GameEngine/Source/GameNetwork/LANAPI.cpp index b17eb107c0..30b0c5d40e 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/LANAPI.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/LANAPI.cpp @@ -840,7 +840,6 @@ void LANAPI::RequestGameOptions( AsciiString gameOptions, Bool isPublic, Unsigne fillInLANMessage( &msg ); msg.LANMessageType = LANMessage::MSG_GAME_OPTIONS; strlcpy(msg.GameOptions.options, gameOptions.str(), ARRAY_SIZE(msg.GameOptions.options)); - msg.GameOptions.options[m_lanMaxOptionsLength] = 0; sendMessage(&msg, ip); m_lastGameopt = gameOptions; diff --git a/Generals/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp b/Generals/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp index a7ce13afaa..9f679217d6 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp @@ -196,7 +196,11 @@ void LANAPI::OnGameStart( void ) option.format("%d", m_currentGame->getLANSlot( m_currentGame->getLocalSlotNum() )->getColor()); pref["Color"] = option; if (m_currentGame->amIHost()) - pref["Map"] = AsciiStringToQuotedPrintable(m_currentGame->getMap()); + { + pref["Map"] = AsciiStringToQuotedPrintable(m_currentGame->getMap()); + pref.setSuperweaponRestricted( m_currentGame->getSuperweaponRestriction() > 0 ); + pref.setStartingCash( m_currentGame->getStartingCash() ); + } pref.write(); m_isInLANMenu = FALSE; diff --git a/Generals/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/Generals/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index 9f8876f096..1e58c37ab5 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -232,6 +232,8 @@ void LANAPI::handleRequestJoin( LANMessage *msg, UnsignedInt senderIP ) #if defined(RTS_DEBUG) if (TheGlobalData->m_netMinPlayers > 0) { #endif +// TheSuperHackers @todo Enable CRC checks! +#if !RTS_ZEROHOUR if (msg->GameToJoin.iniCRC != TheGlobalData->m_iniCRC || msg->GameToJoin.exeCRC != TheGlobalData->m_exeCRC) { @@ -244,6 +246,7 @@ void LANAPI::handleRequestJoin( LANMessage *msg, UnsignedInt senderIP ) reply.GameNotJoined.playerIP = senderIP; canJoin = false; } +#endif #if defined(RTS_DEBUG) } #endif diff --git a/Generals/Code/GameEngine/Source/GameNetwork/Network.cpp b/Generals/Code/GameEngine/Source/GameNetwork/Network.cpp index ced611d7a8..710835eb24 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/Network.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/Network.cpp @@ -946,6 +946,12 @@ void Network::quitGame() { m_conMgr->quitGame(); } +#if !RTS_GENERALS || !RETAIL_COMPATIBLE_CRC + // Blow up / Transfer your units when you quit. Like a normal quit menu quit. + GameMessage *msg = TheMessageStream->appendMessage(GameMessage::MSG_SELF_DESTRUCT); + msg->appendBooleanArgument(TRUE); +#endif + TheGameLogic->exitGame(); m_localStatus = NETLOCALSTATUS_POSTGAME; DEBUG_LOG(("Network::quitGame - quitting game...")); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp index 109e7f71d6..cfbec09d44 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -434,7 +434,7 @@ void Player::init(const PlayerTemplate* pt) if( m_money.countMoney() == 0 ) { - // TheSuperHacker @bugfix Now correctly deposits the money and fixes its audio and academy issues. + // TheSuperHackers @bugfix Now correctly deposits the money and fixes its audio and academy issues. // Note that copying the entire Money class instead would also copy the player index inside of it. if ( TheGameInfo ) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/ConnectionManager.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/ConnectionManager.cpp index 5c02dd79a0..a1f6a2ca8b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/ConnectionManager.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/ConnectionManager.cpp @@ -1753,6 +1753,22 @@ void ConnectionManager::quitGame() { disconnectMsg->detach(); +#if RTS_GENERALS + // if we get here, we hit Quit on the disconnect screen. Mark everyone as having disconnected from us + // so the online stats can give us appropriate feedback. + if (TheGameInfo) + { + for (Int i = 0; i < MAX_SLOTS; ++i) + { + GameSlot *gSlot = TheGameInfo->getSlot( i ); + if (gSlot && !gSlot->lastFrameInGame()) + { + gSlot->markAsDisconnected(); + } + } + } +#endif + disconnectLocalPlayer(); } diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp index e9371c842d..20298e6551 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp @@ -922,9 +922,14 @@ AsciiString GameInfoToAsciiString( const GameInfo *game ) } AsciiString optionsString; +#if RTS_GENERALS + optionsString.format("M=%2.2x%s;MC=%X;MS=%d;SD=%d;C=%d;", game->getMapContentsMask(), newMapName.str(), + game->getMapCRC(), game->getMapSize(), game->getSeed(), game->getCRCInterval()); +#else optionsString.format("US=%d;M=%2.2x%s;MC=%X;MS=%d;SD=%d;C=%d;SR=%u;SC=%u;O=%c;", game->getUseStats(), game->getMapContentsMask(), newMapName.str(), game->getMapCRC(), game->getMapSize(), game->getSeed(), game->getCRCInterval(), game->getSuperweaponRestriction(), game->getStartingCash().countMoney(), game->oldFactionsOnly() ? 'Y' : 'N' ); +#endif //add player info for each slot optionsString.concat(slotListID); @@ -1517,7 +1522,11 @@ void SkirmishGameInfo::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ void SkirmishGameInfo::xfer( Xfer *xfer ) { +#if RTS_GENERALS + const XferVersion currentVersion = 2; +#else const XferVersion currentVersion = 4; +#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp index 69c8731110..9dbbd65a62 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp @@ -681,7 +681,7 @@ void LANAPI::RequestGameLeave( void ) LANMessage msg; msg.LANMessageType = LANMessage::MSG_REQUEST_GAME_LEAVE; fillInLANMessage( &msg ); - wcslcpy(msg.GameToLeave.gameName, (m_currentGame)?m_currentGame->getName().str():L"", ARRAY_SIZE(msg.GameToLeave.gameName)); + wcslcpy(msg.PlayerInfo.playerName, m_name.str(), ARRAY_SIZE(msg.PlayerInfo.playerName)); sendMessage(&msg); m_transport->update(); // Send immediately, before OnPlayerLeave below resets everything. @@ -712,7 +712,7 @@ void LANAPI::RequestGameAnnounce( void ) reply.LANMessageType = LANMessage::MSG_GAME_ANNOUNCE; AsciiString gameOpts = GameInfoToAsciiString(m_currentGame); - strlcpy(reply.GameInfo.options,gameOpts.str(), ARRAY_SIZE(reply.GameOptions.options)); + strlcpy(reply.GameInfo.options,gameOpts.str(), ARRAY_SIZE(reply.GameInfo.options)); wcslcpy(reply.GameInfo.gameName, m_currentGame->getName().str(), ARRAY_SIZE(reply.GameInfo.gameName)); reply.GameInfo.inProgress = m_currentGame->isGameInProgress(); reply.GameInfo.isDirectConnect = m_currentGame->getIsDirectConnect(); @@ -781,7 +781,7 @@ void LANAPI::RequestChat( UnicodeString message, ChatType format ) { LANMessage msg; fillInLANMessage( &msg ); - wcslcpy(msg.Chat.gameName, (m_currentGame)?m_currentGame->getName().str():L"", ARRAY_SIZE(msg.Chat.gameName)); + wcslcpy(msg.Chat.gameName, (m_currentGame) ? m_currentGame->getName().str() : L"", ARRAY_SIZE(msg.Chat.gameName)); msg.LANMessageType = LANMessage::MSG_CHAT; msg.Chat.chatType = format; wcslcpy(msg.Chat.message, message.str(), ARRAY_SIZE(msg.Chat.message)); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index d2244cc77a..979001c725 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -62,7 +62,7 @@ void LANAPI::handleRequestLocations( LANMessage *msg, UnsignedInt senderIP ) fillInLANMessage( &reply ); reply.LANMessageType = LANMessage::MSG_GAME_ANNOUNCE; AsciiString gameOpts = GenerateGameOptionsString(); - strlcpy(reply.GameInfo.options,gameOpts.str(), ARRAY_SIZE(reply.GameInfo.options)); + strlcpy(reply.GameInfo.options, gameOpts.str(), ARRAY_SIZE(reply.GameInfo.options)); wcslcpy(reply.GameInfo.gameName, m_currentGame->getName().str(), ARRAY_SIZE(reply.GameInfo.gameName)); reply.GameInfo.inProgress = m_currentGame->isGameInProgress(); @@ -232,7 +232,9 @@ void LANAPI::handleRequestJoin( LANMessage *msg, UnsignedInt senderIP ) #if defined(RTS_DEBUG) if (TheGlobalData->m_netMinPlayers > 0) { #endif -/* if (msg->GameToJoin.iniCRC != TheGlobalData->m_iniCRC || +// TheSuperHackers @todo Enable CRC checks! +#if !RTS_ZEROHOUR + if (msg->GameToJoin.iniCRC != TheGlobalData->m_iniCRC || msg->GameToJoin.exeCRC != TheGlobalData->m_exeCRC) { DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because of CRC mismatch. CRCs are them/us INI:%X/%X exe:%X/%X", @@ -244,7 +246,7 @@ void LANAPI::handleRequestJoin( LANMessage *msg, UnsignedInt senderIP ) reply.GameNotJoined.playerIP = senderIP; canJoin = false; } -*/ +#endif #if defined(RTS_DEBUG) } #endif diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/Network.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/Network.cpp index 47af9940ee..2570d6431c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/Network.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/Network.cpp @@ -946,9 +946,11 @@ void Network::quitGame() { m_conMgr->quitGame(); } +#if !RTS_GENERALS || !RETAIL_COMPATIBLE_CRC // Blow up / Transfer your units when you quit. Like a normal quit menu quit. GameMessage *msg = TheMessageStream->appendMessage(GameMessage::MSG_SELF_DESTRUCT); msg->appendBooleanArgument(TRUE); +#endif TheGameLogic->exitGame(); m_localStatus = NETLOCALSTATUS_POSTGAME; From 49ca656afaec2f3c3bd653690c55f0ab8fbe8095 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Thu, 23 Oct 2025 22:09:34 +0200 Subject: [PATCH 2/2] unify(network): Merge GameSpy and related code (#1735) --- .../Include/Common/PlayerTemplate.h | 2 + .../Include/GameNetwork/GameSpy/LobbyUtils.h | 3 + .../GameNetwork/GameSpy/MainMenuUtils.h | 6 ++ .../GameSpy/PeerDefsImplementation.h | 2 - .../Include/GameNetwork/GameSpy/PeerThread.h | 2 + .../GameSpy/PersistentStorageThread.h | 2 +- .../GUI/GUICallbacks/Menus/PopupHostGame.cpp | 7 ++ .../GUI/GUICallbacks/Menus/WOLLobbyMenu.cpp | 1 + .../Source/GameNetwork/GameSpy/Chat.cpp | 39 +++++--- .../Source/GameNetwork/GameSpy/GSConfig.cpp | 2 +- .../Source/GameNetwork/GameSpy/LadderDefs.cpp | 14 ++- .../Source/GameNetwork/GameSpy/LobbyUtils.cpp | 88 ++++++++++++++++++- .../GameNetwork/GameSpy/MainMenuUtils.cpp | 20 ++++- .../Source/GameNetwork/GameSpy/PeerDefs.cpp | 21 +++-- .../GameSpy/StagingRoomGameInfo.cpp | 5 ++ .../GameSpy/Thread/BuddyThread.cpp | 7 +- .../GameSpy/Thread/GameResultsThread.cpp | 20 +---- .../GameNetwork/GameSpy/Thread/PeerThread.cpp | 73 ++++++++++++--- .../Thread/PersistentStorageThread.cpp | 58 +++++------- .../Include/GameNetwork/GameSpy/GSConfig.h | 11 ++- .../GameNetwork/GameSpy/MainMenuUtils.h | 22 +++++ .../Source/GameNetwork/GameSpy/Chat.cpp | 8 ++ .../Source/GameNetwork/GameSpy/GSConfig.cpp | 8 ++ .../Source/GameNetwork/GameSpy/LadderDefs.cpp | 2 +- .../Source/GameNetwork/GameSpy/LobbyUtils.cpp | 15 +++- .../GameNetwork/GameSpy/MainMenuUtils.cpp | 88 ++++++++++++++++++- .../GameSpy/StagingRoomGameInfo.cpp | 7 +- .../GameSpy/Thread/BuddyThread.cpp | 7 +- .../GameNetwork/GameSpy/Thread/PeerThread.cpp | 38 +++++++- .../Thread/PersistentStorageThread.cpp | 16 ++++ 30 files changed, 481 insertions(+), 113 deletions(-) diff --git a/Generals/Code/GameEngine/Include/Common/PlayerTemplate.h b/Generals/Code/GameEngine/Include/Common/PlayerTemplate.h index 80f3eba14b..a1bbe03929 100644 --- a/Generals/Code/GameEngine/Include/Common/PlayerTemplate.h +++ b/Generals/Code/GameEngine/Include/Common/PlayerTemplate.h @@ -111,6 +111,7 @@ class PlayerTemplate //const Image *getHiliteImage( void ) const; //const Image *getPushedImage( void ) const; const Image *getSideIconImage( void ) const; + inline const AsciiString getTooltip() const { return m_tooltip; } const ScienceVec& getIntrinsicSciences() const { return m_intrinsicSciences; } Int getIntrinsicSciencePurchasePoints() const { return m_intrinsicSPP; } @@ -158,6 +159,7 @@ class PlayerTemplate AsciiString m_specialPowerShortcutWinName; ///< The name of the window we'll be using for the shortcut bar Int m_specialPowerShortcutButtonCount; ///< The number of buttons located on the shortcut bar AsciiString m_loadScreenMusic; ///< the load screen music we want to play + AsciiString m_tooltip; ///< The tooltip describing this player template Bool m_observer; Bool m_playableSide; diff --git a/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/LobbyUtils.h b/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/LobbyUtils.h index a28bf1cc4a..805b46dede 100644 --- a/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/LobbyUtils.h +++ b/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/LobbyUtils.h @@ -40,6 +40,9 @@ void RefreshGameInfoListBox( GameWindow *mainWin, GameWindow *win ); void RefreshGameListBoxes( void ); void ToggleGameListType( void ); +void playerTemplateComboBoxTooltip(GameWindow *wndComboBox, WinInstanceData *instData, UnsignedInt mouse); +void playerTemplateListBoxTooltip(GameWindow *wndListBox, WinInstanceData *instData, UnsignedInt mouse); + enum GameSortType CPP_11(: Int) { GAMESORT_ALPHA_ASCENDING = 0, diff --git a/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/MainMenuUtils.h b/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/MainMenuUtils.h index c3dec63f6d..84a75eac45 100644 --- a/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/MainMenuUtils.h +++ b/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/MainMenuUtils.h @@ -36,6 +36,7 @@ void CancelPatchCheckCallback( void ); void StartDownloadingPatches( void ); void HandleCanceledDownload( Bool resetDropDown = TRUE ); +#if RTS_GENERALS enum OverallStatsPeriod CPP_11(: Int) { STATS_TODAY = 0, @@ -51,9 +52,14 @@ struct OverallStats Int wins[STATS_MAX]; Int losses[STATS_MAX]; }; +#endif void CheckOverallStats( void ); +#if RTS_GENERALS void HandleOverallStats( const OverallStats& USA, const OverallStats& China, const OverallStats& GLA ); +#else +void HandleOverallStats( const char* szHTTPStats, unsigned len ); +#endif void CheckNumPlayersOnline( void ); void HandleNumPlayersOnline( Int numPlayersOnline ); diff --git a/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PeerDefsImplementation.h b/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PeerDefsImplementation.h index 5cc3b606fc..05a61af37d 100644 --- a/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PeerDefsImplementation.h +++ b/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PeerDefsImplementation.h @@ -61,7 +61,6 @@ class GameSpyInfo : public GameSpyInfoInterface virtual void setLocalPassword( AsciiString passwd ) { m_localPasswd = passwd; } virtual void setLocalBaseName( AsciiString name ) { m_localBaseName = name; } virtual AsciiString getLocalBaseName( void ){ return m_localBaseName; } - virtual void setCachedLocalPlayerStats( PSPlayerStats stats ) {m_cachedLocalPlayerStats = stats; } virtual PSPlayerStats getCachedLocalPlayerStats( void ){ return m_cachedLocalPlayerStats; } @@ -174,6 +173,5 @@ class GameSpyInfo : public GameSpyInfoInterface std::set m_textWindows; std::set m_preorderPlayers; - Int m_additionalDisconnects; }; diff --git a/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PeerThread.h b/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PeerThread.h index 113f6a0af9..d2df592081 100644 --- a/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PeerThread.h +++ b/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PeerThread.h @@ -116,6 +116,7 @@ class PeerRequest UnsignedInt iniCRC; UnsignedInt gameVersion; Bool allowObservers; + Bool useStats; UnsignedShort ladPort; UnsignedInt ladPassCRC; Bool restrictGameList; @@ -328,6 +329,7 @@ class PeerResponse Bool isStaging; Bool requiresPassword; Bool allowObservers; + Bool useStats; UnsignedInt version; UnsignedInt exeCRC; UnsignedInt iniCRC; diff --git a/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PersistentStorageThread.h b/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PersistentStorageThread.h index 348b1b2f35..7832c036f8 100644 --- a/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PersistentStorageThread.h +++ b/Generals/Code/GameEngine/Include/GameNetwork/GameSpy/PersistentStorageThread.h @@ -44,7 +44,7 @@ class PSPlayerStats Int id; PerGeneralMap wins; PerGeneralMap losses; - PerGeneralMap games; + PerGeneralMap games; //first: playerTemplate #, second: #games played (see also gamesAsRandom) PerGeneralMap duration; PerGeneralMap unitsKilled; PerGeneralMap unitsLost; diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupHostGame.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupHostGame.cpp index c62705bad6..036afbcc97 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupHostGame.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupHostGame.cpp @@ -542,10 +542,17 @@ void createGame( void ) req.password = passwd.str(); CustomMatchPreferences customPref; Bool aO = GadgetCheckBoxIsChecked(checkBoxAllowObservers); + Bool limitArmies = FALSE; + Bool useStats = TRUE; customPref.setAllowsObserver(aO); + customPref.setFactionsLimited( limitArmies ); + customPref.setUseStats( useStats ); customPref.write(); req.stagingRoomCreation.allowObservers = aO; + req.stagingRoomCreation.useStats = useStats; TheGameSpyGame->setAllowObservers(aO); + TheGameSpyGame->setOldFactionsOnly( limitArmies ); + TheGameSpyGame->setUseStats( useStats ); req.stagingRoomCreation.exeCRC = TheGlobalData->m_exeCRC; req.stagingRoomCreation.iniCRC = TheGlobalData->m_iniCRC; req.stagingRoomCreation.gameVersion = TheGameSpyInfo->getInternalIP(); diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLLobbyMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLLobbyMenu.cpp index 8eb8db217f..261d397ada 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLLobbyMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLLobbyMenu.cpp @@ -1173,6 +1173,7 @@ void WOLLobbyMenuUpdate( WindowLayout * layout, void *userData) room.setExeCRC(resp.stagingRoom.exeCRC); room.setIniCRC(resp.stagingRoom.iniCRC); room.setAllowObservers(resp.stagingRoom.allowObservers); + room.setUseStats(resp.stagingRoom.useStats); room.setPingString(resp.stagingServerPingString.c_str()); room.setLadderIP(resp.stagingServerLadderIP.c_str()); room.setLadderPort(resp.stagingRoom.ladderPort); diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Chat.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Chat.cpp index 355270f39a..d3528dc090 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Chat.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Chat.cpp @@ -89,12 +89,20 @@ Color GameSpyColor[GSCOLOR_MAX] = GameMakeColor(128,128,0,255), // GSCOLOR_GAME GameMakeColor(128,128,128,255), // GSCOLOR_GAME_FULL GameMakeColor(128,128,128,255), // GSCOLOR_GAME_CRCMISMATCH +#if RTS_GENERALS GameMakeColor(255, 0, 0,255), // GSCOLOR_PLAYER_NORMAL +#else + GameMakeColor(255,255,255,255), // GSCOLOR_PLAYER_NORMAL +#endif GameMakeColor(255, 0,255,255), // GSCOLOR_PLAYER_OWNER GameMakeColor(255, 0,128,255), // GSCOLOR_PLAYER_BUDDY GameMakeColor(255, 0, 0,255), // GSCOLOR_PLAYER_SELF GameMakeColor(128,128,128,255), // GSCOLOR_PLAYER_IGNORED +#if RTS_GENERALS GameMakeColor(255,0,0,255), // GSCOLOR_CHAT_NORMAL +#else + GameMakeColor(255,255,255,255), // GSCOLOR_CHAT_NORMAL +#endif GameMakeColor(255,128,0,255), // GSCOLOR_CHAT_EMOTE, GameMakeColor(255,255,0,255), // GSCOLOR_CHAT_OWNER, GameMakeColor(128,255,0,255), // GSCOLOR_CHAT_OWNER_EMOTE, @@ -114,6 +122,8 @@ Color GameSpyColor[GSCOLOR_MAX] = Bool GameSpyInfo::sendChat( UnicodeString message, Bool isAction, GameWindow *playerListbox ) { + static UnicodeString s_prevMsg = UnicodeString::TheEmptyString; //stop spam before it happens + RoomType roomType = StagingRoom; if (getCurrentGroupRoom()) roomType = GroupRoom; @@ -126,11 +136,14 @@ Bool GameSpyInfo::sendChat( UnicodeString message, Bool isAction, GameWindow *pl if (!message.isEmpty()) { if (!playerListbox) - { - // Public message - req.message.isAction = isAction; - req.peerRequestType = PeerRequest::PEERREQUEST_MESSAGEROOM; - TheGameSpyPeerMessageQueue->addRequest(req); + { // Public message + if( isAction || message.compare(s_prevMsg) != 0 ) //don't send duplicate messages + { + req.message.isAction = isAction; + req.peerRequestType = PeerRequest::PEERREQUEST_MESSAGEROOM; + TheGameSpyPeerMessageQueue->addRequest(req); + s_prevMsg = message; + } return false; } @@ -140,11 +153,14 @@ Bool GameSpyInfo::sendChat( UnicodeString message, Bool isAction, GameWindow *pl GadgetListBoxGetSelected(playerListbox, (Int *)&selections); if (selections[0] == -1) - { - // Public message - req.message.isAction = isAction; - req.peerRequestType = PeerRequest::PEERREQUEST_MESSAGEROOM; - TheGameSpyPeerMessageQueue->addRequest(req); + { // Public message + if( isAction || message.compare(s_prevMsg) != 0 ) //don't send duplicate messages + { + req.message.isAction = isAction; + req.peerRequestType = PeerRequest::PEERREQUEST_MESSAGEROOM; + TheGameSpyPeerMessageQueue->addRequest(req); + s_prevMsg = message; + } return false; } else @@ -180,10 +196,11 @@ Bool GameSpyInfo::sendChat( UnicodeString message, Bool isAction, GameWindow *pl req.peerRequestType = PeerRequest::PEERREQUEST_MESSAGEPLAYER; TheGameSpyPeerMessageQueue->addRequest(req); } - + s_prevMsg = message; return true; } } + s_prevMsg = message; return false; } diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/GSConfig.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/GSConfig.cpp index f9b1a3e601..0b18d9b087 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/GSConfig.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/GSConfig.cpp @@ -336,7 +336,7 @@ m_qmChannel(0) // German2 is missing some maps because of content. But, we need the m_qmMaps // to contain same number of strings as the Retail version so that the // QM Bot thinks that they have the same number of maps. - #if 1 + #if RTS_GENERALS m_qmMaps.push_back(mapName); #else const MapMetaData *md = TheMapCache->findMap(mapName); diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/LadderDefs.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/LadderDefs.cpp index 6f80eb6497..5536ad1097 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/LadderDefs.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/LadderDefs.cpp @@ -166,9 +166,17 @@ static LadderInfo *parseLadder(AsciiString raw) if (lad->validFactions.size() == 0) { DEBUG_LOG(("No factions specified. Using all.")); - lad->validFactions.push_back("America"); - lad->validFactions.push_back("China"); - lad->validFactions.push_back("GLA"); + lad->validFactions.clear(); + Int numTemplates = ThePlayerTemplateStore->getPlayerTemplateCount(); + for ( Int i = 0; i < numTemplates; ++i ) + { + const PlayerTemplate *pt = ThePlayerTemplateStore->getNthPlayerTemplate(i); + if (!pt) + continue; + + if (pt->isPlayableSide() && pt->getSide().compare("Boss") != 0 ) + lad->validFactions.push_back(pt->getSide()); + } } else { diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp index 85fd05bb4f..e3f7d688b0 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp @@ -64,6 +64,7 @@ // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// +// Note: if you add more columns, you must modify the .wnd files and change the listbox properties (yuck!) enum { COLUMN_NAME = 0, COLUMN_MAP, @@ -71,6 +72,9 @@ enum { COLUMN_NUMPLAYERS, COLUMN_PASSWORD, COLUMN_OBSERVER, +#if !RTS_GENERALS + COLUMN_USE_STATS, +#endif COLUMN_PING, }; @@ -215,7 +219,7 @@ static void gameTooltip(GameWindow *window, if (col == COLUMN_PING) { -#ifdef DEBUG_LOGGING +#if 0 //def DEBUG_LOGGING UnicodeString s; s.format(L"Ping is %d ms (cutoffs are %d ms and %d ms\n%hs local pings\n%hs remote pings", room->getPingAsInt(), TheGameSpyConfig->getPingCutoffGood(), TheGameSpyConfig->getPingCutoffBad(), @@ -245,6 +249,20 @@ static void gameTooltip(GameWindow *window, TheMouse->setCursorTooltip( UnicodeString::TheEmptyString ); return; } +#if !RTS_GENERALS + if (col == COLUMN_USE_STATS) + { + if ( room->getUseStats() ) + { + TheMouse->setCursorTooltip( TheGameText->fetch("TOOLTIP:UseStatsOn") ); + } + else + { + TheMouse->setCursorTooltip( TheGameText->fetch("TOOLTIP:UseStatsOff") ); + } + return; + } +#endif UnicodeString tooltip; @@ -628,6 +646,18 @@ static Int insertGame( GameWindow *win, GameSpyStagingRoom *game, Bool showMap ) GadgetListBoxAddEntryText(win, UnicodeString(L" "), gameColor, index, COLUMN_OBSERVER); } +#if !RTS_GENERALS + { + if (game->getUseStats()) + { + if (const Image *img = TheMappedImageCollection->findImageByName("GoodStatsIcon")) + { + GadgetListBoxAddEntryImage(win, img, index, COLUMN_USE_STATS, img->getImageHeight(), img->getImageWidth()); + } + } + } +#endif + s.format(L"%d", game->getPingAsInt()); GadgetListBoxAddEntryText(win, s, gameColor, index, COLUMN_PING); Int ping = game->getPingAsInt(); @@ -851,3 +881,59 @@ void ToggleGameListType( void ) RefreshGameListBoxes(); } +// for use by GameWindow::winSetTooltipFunc +// displays the Army Tooltip for the player templates +void playerTemplateComboBoxTooltip(GameWindow *wndComboBox, WinInstanceData *instData, UnsignedInt mouse) +{ + Int index = 0; + GadgetComboBoxGetSelectedPos(wndComboBox, &index); + Int templateNum = (Int)GadgetComboBoxGetItemData(wndComboBox, index); + UnicodeString ustringTooltip; + if (templateNum == -1) + { + // the "Random" template is always first + ustringTooltip = TheGameText->fetch("TOOLTIP:BioStrategyLong_Random"); + } + else + { + const PlayerTemplate *playerTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(templateNum); + if (playerTemplate) + { + ustringTooltip = TheGameText->fetch(playerTemplate->getTooltip()); + } + } + TheMouse->setCursorTooltip(ustringTooltip); +} + +// ----------------------------------------------------------------------------- + +// for use by GameWindow::winSetTooltipFunc +// displays the Army Tooltip for the player templates +void playerTemplateListBoxTooltip(GameWindow *wndListBox, WinInstanceData *instData, UnsignedInt mouse) +{ + Int x, y, row, col; + x = LOLONGTOSHORT(mouse); + y = HILONGTOSHORT(mouse); + GadgetListBoxGetEntryBasedOnXY(wndListBox, x, y, row, col); + if (row == -1 || col == -1) + return; + + Int templateNum = (Int)GadgetListBoxGetItemData(wndListBox, row, col); + UnicodeString ustringTooltip; + if (templateNum == -1) + { + // the "Random" template is always first + ustringTooltip = TheGameText->fetch("TOOLTIP:BioStrategyLong_Random"); + } + else + { + const PlayerTemplate *playerTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(templateNum); + if (playerTemplate) + { + ustringTooltip = TheGameText->fetch(playerTemplate->getTooltip()); + } + } + + // use no tooltip delay here + TheMouse->setCursorTooltip(ustringTooltip, 0); +} diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/MainMenuUtils.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/MainMenuUtils.cpp index 829a489fc9..7789cc6a41 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/MainMenuUtils.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/MainMenuUtils.cpp @@ -582,6 +582,7 @@ static GHTTPBool overallStatsCallback( GHTTPRequest request, GHTTPResult result, return GHTTPTrue; } +#if RTS_GENERALS OverallStats USA, China, GLA; AsciiString message = buffer; @@ -650,6 +651,9 @@ static GHTTPBool overallStatsCallback( GHTTPRequest request, GHTTPResult result, } HandleOverallStats(USA, China, GLA); +#elif RTS_ZEROHOUR + HandleOverallStats( buffer, bufferLen ); +#endif return GHTTPTrue; } @@ -685,16 +689,24 @@ static GHTTPBool numPlayersOnlineCallback( GHTTPRequest request, GHTTPResult res void CheckOverallStats( void ) { - ghttpGet("http://gamestats.gamespy.com/ccgenerals/display.html", - GHTTPFalse, overallStatsCallback, NULL); +#if RTS_GENERALS + const char *const url = "http://gamestats.gamespy.com/ccgenerals/display.html"; +#elif RTS_ZEROHOUR + const char *const url = "http://gamestats.gamespy.com/ccgenzh/display.html"; +#endif + ghttpGet(url, GHTTPFalse, overallStatsCallback, NULL); } /////////////////////////////////////////////////////////////////////////////////////// void CheckNumPlayersOnline( void ) { - ghttpGet("http://launch.gamespyarcade.com/software/launch/arcadecount2.dll?svcname=ccgenerals", - GHTTPFalse, numPlayersOnlineCallback, NULL); +#if RTS_GENERALS + const char *const url = "http://launch.gamespyarcade.com/software/launch/arcadecount2.dll?svcname=ccgenerals"; +#elif RTS_ZEROHOUR + const char *const url = "http://launch.gamespyarcade.com/software/launch/arcadecount2.dll?svcname=ccgenzh"; +#endif + ghttpGet(url, GHTTPFalse, numPlayersOnlineCallback, NULL); } /////////////////////////////////////////////////////////////////////////////////////// diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/PeerDefs.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/PeerDefs.cpp index fac98dedc3..aa83c40bed 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/PeerDefs.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/PeerDefs.cpp @@ -48,7 +48,7 @@ GameSpyInfoInterface *TheGameSpyInfo = NULL; -GameSpyStagingRoom *TheGameSpyGame = NULL; +extern GameSpyStagingRoom *TheGameSpyGame = NULL; void deleteNotificationBox( void ); bool AsciiComparator::operator()(AsciiString s1, AsciiString s2) const @@ -191,10 +191,6 @@ void GameSpyInfo::setGameOptions( void ) } req.gameOptsMapName = newMapName.str(); - //const MapMetaData *md = TheMapCache->findMap(mapName); - //if (!md) - //return; // there really isn't any need to send info like this... - req.gameOptions.numPlayers = 0; req.gameOptions.numObservers = 0; Int numOpenSlots = 0; @@ -528,10 +524,21 @@ void GameSpyInfo::markAsStagingRoomHost( void ) { m_localStagingRoomID = 0; m_joinedStagingRoom = FALSE; m_isHosting = TRUE; - m_localStagingRoom.reset(); + + // There are a few options we don't want to reset when we are hosting (they carry over + // from the the create game dialog). + // Interesting fact: oldFactionsOnly will be carried over correctly if I remove these + // lines. UseStats won't be. I have no idea why. + Int useStats = m_localStagingRoom.getUseStats(); + Bool oldFactionsOnly = m_localStagingRoom.oldFactionsOnly(); + + m_localStagingRoom.reset(); m_localStagingRoom.enterGame(); m_localStagingRoom.setSeed(GetTickCount()); + m_localStagingRoom.setUseStats( useStats ); + m_localStagingRoom.setOldFactionsOnly( oldFactionsOnly ); + GameSlot newSlot; UnicodeString uName; uName.translate(m_localName); @@ -841,7 +848,7 @@ seems like the only secure way to handle this issue since users can abort the pr before we can detect/log disconnections.*/ void GameSpyInfo::updateAdditionalGameSpyDisconnections(Int count) { - if (TheRecorder->isMultiplayer() && TheGameLogic->isInInternetGame()) + if (TheRecorder->isMultiplayer() && TheGameLogic->isInInternetGame() && TheGameSpyGame && TheGameSpyGame->getUseStats()) { Int localID = TheGameSpyInfo->getLocalProfileID(); PSPlayerStats stats = TheGameSpyPSMessageQueue->findPlayerStatsByID(localID); diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/StagingRoomGameInfo.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/StagingRoomGameInfo.cpp index be97354724..bef79f1671 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/StagingRoomGameInfo.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/StagingRoomGameInfo.cpp @@ -762,8 +762,13 @@ AsciiString GameSpyStagingRoom::generateLadderGameResultsPacket( void ) playerStr.format(",buildingsKilled%d=%d,buildingsLost%d=%d,buildingsBuilt%d=%d", playerID, buildingsKilled, playerID, buildingsLost, playerID, buildingsBuilt); results.concat(playerStr); +#if RTS_GENERALS playerStr.format(",fps%d=%d,cash%d=%d,capturedTech%d=%d,discon%d=%d,side%d=%s,team%d=%d", playerID, fps, playerID, earnings, playerID, techCaptured, playerID, disconnected, playerID, p[i]->getPlayerTemplate()->getSide().str(), playerID, slot->getTeamNumber()); +#elif RTS_ZEROHOUR + playerStr.format(",fps%d=%d,cash%d=%d,capturedTech%d=%d,discon%d=%d,side%d=%s", + playerID, fps, playerID, earnings, playerID, techCaptured, playerID, disconnected, playerID, p[i]->getPlayerTemplate()->getSide().str()); +#endif results.concat(playerStr); ++playerID; diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/BuddyThread.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/BuddyThread.cpp index 39f8f3469b..71227d8646 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/BuddyThread.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/BuddyThread.cpp @@ -260,7 +260,12 @@ void BuddyThreadClass::Thread_Function() try { GPConnection gpCon; GPConnection *con = &gpCon; - gpInitialize( con, 675, 0, GP_PARTNERID_GAMESPY ); +#if RTS_GENERALS + const int productID = 675; +#elif RTS_ZEROHOUR + const int productID = 823; +#endif + gpInitialize( con, productID, 0, GP_PARTNERID_GAMESPY ); m_isConnected = m_isConnecting = false; gpSetCallback( con, GP_ERROR, callbackWrapper, (void *)CALLBACK_ERROR ); diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/GameResultsThread.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/GameResultsThread.cpp index f7ab74904b..6afc484a0c 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/GameResultsThread.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/GameResultsThread.cpp @@ -201,24 +201,6 @@ Bool GameResultsQueue::areGameResultsBeingSent( void ) return m_requestCount > 0; } - -//------------------------------------------------------------------------- -// Wrap ladder results in HTTP POST -static void WrapHTTP( const std::string& hostname, std::string& results ) -{ - const char HEADER[] = - "PUT / HTTP/1.1\r\n" - "Connection: Close\r\n" - "Host: %s\r\n" - "Content-Length: %d\r\n" - "\r\n"; - - char szHdr[256]; - snprintf( szHdr, 256, HEADER, hostname.c_str(), results.length() ); - results = szHdr + results; -} - - //------------------------------------------------------------------------- void GameResultsThreadClass::Thread_Function() @@ -288,6 +270,7 @@ void GameResultsThreadClass::Thread_Function() //------------------------------------------------------------------------- +#ifdef DEBUG_LOGGING #define CASE(x) case (x): return #x; static const char *getWSAErrorString( Int error ) @@ -353,6 +336,7 @@ static const char *getWSAErrorString( Int error ) #undef CASE +#endif //------------------------------------------------------------------------- Int GameResultsThreadClass::sendGameResults( UnsignedInt IP, UnsignedShort port, const std::string& results ) diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PeerThread.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PeerThread.cpp index 58e32d178e..01cf131cdc 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PeerThread.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PeerThread.cpp @@ -92,6 +92,9 @@ enum INICRC_KEY, PW_KEY, OBS_KEY, +#if !RTS_GENERALS + USE_STATS_KEY, +#endif LADIP_KEY, LADPORT_KEY, PINGSTR_KEY, @@ -109,6 +112,7 @@ enum #define INICRC_STR "iniCRC" #define PW_STR "pw" #define OBS_STR "obs" +#define USE_STATS_STR "stat" #define LADIP_STR "ladIP" #define LADPORT_STR "ladPort" #define PINGSTR_STR "pings" @@ -178,6 +182,7 @@ class PeerThreadClass : public ThreadClass //Added By Sadullah Nader //Initializations inserted m_roomJoined = m_allowObservers = m_hasPassword = FALSE; + m_useStats = TRUE; m_exeCRC = m_iniCRC = 0; m_gameVersion = 0; m_ladderPort = 0; @@ -230,6 +235,7 @@ class PeerThreadClass : public ThreadClass void stopHostingAlready(PEER peer); Bool hasPassword( void ) { return m_hasPassword; } Bool allowObservers( void ) { return m_allowObservers; } + Bool useStats(void) const { return m_useStats; } std::string getMapName( void ) { return m_mapName; } UnsignedInt exeCRC( void ) { return m_exeCRC; } UnsignedInt iniCRC( void ) { return m_iniCRC; } @@ -301,6 +307,7 @@ class PeerThreadClass : public ThreadClass UnsignedInt m_iniCRC; UnsignedInt m_gameVersion; Bool m_allowObservers; + Bool m_useStats; std::string m_pingStr; std::string m_ladderIP; UnsignedShort m_ladderPort; @@ -798,6 +805,11 @@ static void QRServerKeyCallback case OBS_KEY: ADDINT(t->allowObservers()); break; +#if !RTS_GENERALS + case USE_STATS_KEY: + ADDINT(t->useStats()); + break; +#endif case LADIP_KEY: ADD(t->ladderIP().c_str()); break; @@ -942,6 +954,9 @@ static void QRKeyListCallback qr2_keybuffer_add(keyBuffer, INICRC_KEY); qr2_keybuffer_add(keyBuffer, PW_KEY); qr2_keybuffer_add(keyBuffer, OBS_KEY); +#if !RTS_GENERALS + qr2_keybuffer_add(keyBuffer, USE_STATS_KEY); +#endif qr2_keybuffer_add(keyBuffer, LADIP_KEY); qr2_keybuffer_add(keyBuffer, LADPORT_KEY); qr2_keybuffer_add(keyBuffer, PINGSTR_KEY); @@ -1191,6 +1206,9 @@ void PeerThreadClass::Thread_Function() qr2_register_key(INICRC_KEY, INICRC_STR); qr2_register_key(PW_KEY, PW_STR); qr2_register_key(OBS_KEY, OBS_STR); +#if !RTS_GENERALS + qr2_register_key(USE_STATS_KEY, USE_STATS_STR); +#endif qr2_register_key(LADIP_KEY, LADIP_STR); qr2_register_key(LADPORT_KEY, LADPORT_STR); qr2_register_key(PINGSTR_KEY, PINGSTR_STR); @@ -1203,7 +1221,11 @@ void PeerThreadClass::Thread_Function() qr2_register_key(FACTION__KEY, FACTION__STR "_"); qr2_register_key(COLOR__KEY, COLOR__STR "_"); +#if RTS_GENERALS const Int NumKeys = 14; +#else + const Int NumKeys = 15; +#endif unsigned char allKeysArray[NumKeys] = { /* PID__KEY, @@ -1220,6 +1242,9 @@ void PeerThreadClass::Thread_Function() INICRC_KEY, PW_KEY, OBS_KEY, +#if !RTS_GENERALS + USE_STATS_KEY, +#endif LADIP_KEY, LADPORT_KEY, PINGSTR_KEY, @@ -1232,7 +1257,7 @@ void PeerThreadClass::Thread_Function() /* const char *allKeys = "\\pid_\\mapname\\gamever\\gamename" \ "\\" EXECRC_STR "\\" INICRC_STR \ - "\\" PW_STR "\\" OBS_STR "\\" LADIP_STR "\\" LADPORT_STR \ + "\\" PW_STR "\\" OBS_STR "\\" USE_STATS_STR "\\" LADIP_STR "\\" LADPORT_STR \ "\\" PINGSTR_STR "\\" NUMOBS_STR \ "\\" NUMPLAYER_STR "\\" MAXPLAYER_STR \ "\\" NAME__STR "_" "\\" WINS__STR "_" "\\" LOSSES__STR "_" "\\" FACTION__STR "_" "\\" COLOR__STR "_"; @@ -1261,15 +1286,23 @@ void PeerThreadClass::Thread_Function() /********* First step, set our game authentication info We could do: + Generals: strcpy(gcd_gamename,"ccgenerals"); strcpy(gcd_secret_key,"h5T2f6"); + ZeroHour: + strcpy(gcd_gamename,"ccgenzh"); + strcpy(gcd_secret_key,"D6s9k3"); or + Generals: strcpy(gcd_gamename,"ccgeneralsb"); strcpy(gcd_secret_key,"g3T9s2"); + ZeroHour: + strcpy(gcd_gamename,"ccgeneralsb"); + strcpy(gcd_secret_key,"whatever the key is"); ...but this is more secure: **********/ - char gameName[12]; - char secretKey[7]; + char gameName[12] = {0}; + char secretKey[7] = {0}; /** gameName[0]='c';gameName[1]='c';gameName[2]='g';gameName[3]='e'; gameName[4]='n';gameName[5]='e';gameName[6]='r';gameName[7]='a'; @@ -1277,11 +1310,18 @@ void PeerThreadClass::Thread_Function() secretKey[0]='g';secretKey[1]='3';secretKey[2]='T';secretKey[3]='9'; secretKey[4]='s';secretKey[5]='2';secretKey[6]='\0'; /**/ +#if RTS_GENERALS gameName[0]='c';gameName[1]='c';gameName[2]='g';gameName[3]='e'; gameName[4]='n';gameName[5]='e';gameName[6]='r';gameName[7]='a'; gameName[8]='l';gameName[9]='s';gameName[10]='\0'; secretKey[0]='h';secretKey[1]='5';secretKey[2]='T';secretKey[3]='2'; secretKey[4]='f';secretKey[5]='6';secretKey[6]='\0'; +#elif RTS_ZEROHOUR + gameName[0]='c';gameName[1]='c';gameName[2]='g';gameName[3]='e'; + gameName[4]='n';gameName[5]='z';gameName[6]='h';gameName[7]='\0'; + secretKey[0]='D';secretKey[1]='6';secretKey[2]='s';secretKey[3]='9'; + secretKey[4]='k';secretKey[5]='3';secretKey[6]='\0'; +#endif /**/ // Set the title. @@ -1620,6 +1660,7 @@ void PeerThreadClass::Thread_Function() s_wantStateChangedHeartbeat = FALSE; m_isHosting = TRUE; m_allowObservers = incomingRequest.stagingRoomCreation.allowObservers; + m_useStats = incomingRequest.stagingRoomCreation.useStats; m_mapName = ""; for (Int i=0; iaddResponse(resp); } @@ -2761,6 +2802,10 @@ static void enumFunc(char *key, char *val, void *param) static void listingGamesCallback(PEER peer, PEERBool success, const char * name, SBServer server, PEERBool staging, int msg, Int percentListed, void * param) { + PeerThreadClass *t = (PeerThreadClass *)param; + if (!t || !success) + return; + #ifdef DEBUG_LOGGING AsciiString cmdStr = ""; switch(msg) @@ -2784,8 +2829,8 @@ static void listingGamesCallback(PEER peer, PEERBool success, const char * name, DEBUG_LOG(("listingGamesCallback() - doing command %s on server %X", cmdStr.str(), server)); #endif // DEBUG_LOGGING - PeerThreadClass *t = (PeerThreadClass *)param; - DEBUG_ASSERTCRASH(name, ("Game has no name!")); +// PeerThreadClass *t = (PeerThreadClass *)param; + DEBUG_ASSERTCRASH(name || msg==PEER_CLEAR || msg==PEER_COMPLETE, ("Game has no name!")); if (!t || !success || (!name && (msg == PEER_ADD || msg == PEER_UPDATE))) { DEBUG_LOG(("Bailing from listingGamesCallback() - success=%d, name=%X, server=%X, msg=%X", success, name, server, msg)); @@ -2807,7 +2852,11 @@ static void listingGamesCallback(PEER peer, PEERBool success, const char * name, { DEBUG_LOG(("Game name is '%s'", name)); const char *newname = SBServerGetStringValue(server, "gamename", (char *)name); +#if RTS_GENERALS if (strcmp(newname, "ccgenerals")) +#elif RTS_ZEROHOUR + if (strcmp(newname, "ccgenzh")) +#endif name = newname; DEBUG_LOG(("Game name is now '%s'", name)); } @@ -2845,6 +2894,7 @@ static void listingGamesCallback(PEER peer, PEERBool success, const char * name, { Bool hasPassword = (Bool)SBServerGetIntValue(server, PW_STR, FALSE); Bool allowObservers = (Bool)SBServerGetIntValue(server, OBS_STR, FALSE); + Bool usesStats = (Bool)SBServerGetIntValue(server, USE_STATS_STR, TRUE); const char *verStr = SBServerGetStringValue(server, "gamever", "000000"); const char *exeStr = SBServerGetStringValue(server, EXECRC_STR, "000000"); const char *iniStr = SBServerGetStringValue(server, INICRC_STR, "000000"); @@ -2856,6 +2906,7 @@ static void listingGamesCallback(PEER peer, PEERBool success, const char * name, UnsignedInt iniVal = strtoul(iniStr, NULL, 10); resp.stagingRoom.requiresPassword = hasPassword; resp.stagingRoom.allowObservers = allowObservers; + resp.stagingRoom.useStats = usesStats; resp.stagingRoom.version = verVal; resp.stagingRoom.exeCRC = exeVal; resp.stagingRoom.iniCRC = iniVal; @@ -2886,7 +2937,7 @@ static void listingGamesCallback(PEER peer, PEERBool success, const char * name, resp.stagingRoomPlayerNames[0] = hostName.str(); } DEBUG_ASSERTCRASH(resp.stagingRoomPlayerNames[0].empty() == false, ("No host!")); - DEBUG_LOG(("Raw stuff: [%s] [%s] [%s] [%d] [%d]", verStr, exeStr, iniStr, hasPassword, allowObservers)); + DEBUG_LOG(("Raw stuff: [%s] [%s] [%s] [%d] [%d] [%d]", verStr, exeStr, iniStr, hasPassword, allowObservers, usesStats)); DEBUG_LOG(("Raw stuff: [%s] [%s] [%d]", pingStr, ladIPStr, ladPort)); DEBUG_LOG(("Saw game with stuff %s %d %X %X %X %s", resp.stagingRoomMapName.c_str(), hasPassword, verVal, exeVal, iniVal, SBServerGetStringValue(server, "password", "missing"))); #ifdef PING_TEST diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PersistentStorageThread.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PersistentStorageThread.cpp index 3e4ea55ca7..85a768027e 100644 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PersistentStorageThread.cpp +++ b/Generals/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PersistentStorageThread.cpp @@ -34,6 +34,7 @@ #include "Common/UserPreferences.h" #include "Common/PlayerTemplate.h" #include "GameNetwork/GameSpy/PersistentStorageThread.h" +#include "GameNetwork/GameSpy/PeerDefs.h" #include "mutex.h" #include "thread.h" @@ -588,26 +589,11 @@ Bool PSThreadClass::tryConnect( void ) // this may block for 1-2 seconds (according to GS) so it's nice we're not in the UI thread :) result = InitStatsConnection(0); -#ifdef DEBUG_LOGGING - static const char *retValStrings[6] = { - "GE_NOERROR", - "GE_NOSOCKET", - "GE_NODNS", - "GE_NOCONNECT", - "GE_BUSY", - "GE_DATAERROR" - }; -#endif // DEBUG_LOGGING - if (result != GE_NOERROR) { - DEBUG_LOG(("InitStatsConnection() returned %d (%s)", result, retValStrings[result])); + DEBUG_LOG(("InitStatsConnection() returned %d", result)); return false; } - else - { - DEBUG_LOG(("InitStatsConnection() succeeded")); - } return true; } @@ -672,7 +658,6 @@ static void getPersistentDataCallback(int localid, int profileid, persisttype_t if (!t->getOpCount() && !t->sawLocalPlayerData()) { // we haven't gotten stats for ourselves - try again - DEBUG_LOG(("Requesting retry for reading local player's stats")); PSRequest req; req.requestType = PSRequest::PSREQUEST_READPLAYERSTATS; req.player.id = MESSAGE_QUEUE->getLocalPlayerID(); @@ -681,7 +666,7 @@ static void getPersistentDataCallback(int localid, int profileid, persisttype_t return; } - if (profileid == MESSAGE_QUEUE->getLocalPlayerID()) + if (profileid == MESSAGE_QUEUE->getLocalPlayerID() && TheGameSpyGame && TheGameSpyGame->getUseStats()) { t->gotLocalPlayerData(); DEBUG_LOG(("getPersistentDataCallback() - got local player info")); @@ -824,11 +809,19 @@ void PSThreadClass::Thread_Function() /********* First step, set our game authentication info We could do: + Generals: strcpy(gcd_gamename,"ccgenerals"); strcpy(gcd_secret_key,"h5T2f6"); + ZeroHour: + strcpy(gcd_gamename,"ccgenzh"); + strcpy(gcd_secret_key,"D6s9k3"); or + Generals: strcpy(gcd_gamename,"ccgeneralsb"); strcpy(gcd_secret_key,"g3T9s2"); + ZeroHour: + strcpy(gcd_gamename,"ccgeneralsb"); + strcpy(gcd_secret_key,"whatever the key is"); ...but this is more secure: **********/ /** @@ -838,11 +831,18 @@ void PSThreadClass::Thread_Function() gcd_secret_key[0]='g';gcd_secret_key[1]='3';gcd_secret_key[2]='T';gcd_secret_key[3]='9'; gcd_secret_key[4]='s';gcd_secret_key[5]='2';gcd_secret_key[6]='\0'; /**/ +#if RTS_GENERALS gcd_gamename[0]='c';gcd_gamename[1]='c';gcd_gamename[2]='g';gcd_gamename[3]='e'; gcd_gamename[4]='n';gcd_gamename[5]='e';gcd_gamename[6]='r';gcd_gamename[7]='a'; gcd_gamename[8]='l';gcd_gamename[9]='s';gcd_gamename[10]='\0'; gcd_secret_key[0]='h';gcd_secret_key[1]='5';gcd_secret_key[2]='T';gcd_secret_key[3]='2'; gcd_secret_key[4]='f';gcd_secret_key[5]='6';gcd_secret_key[6]='\0'; +#elif RTS_ZEROHOUR + gcd_gamename[0]='c';gcd_gamename[1]='c';gcd_gamename[2]='g';gcd_gamename[3]='e'; + gcd_gamename[4]='n';gcd_gamename[5]='z';gcd_gamename[6]='h';gcd_gamename[7]='\0'; + gcd_secret_key[0]='D';gcd_secret_key[1]='6';gcd_secret_key[2]='s';gcd_secret_key[3]='9'; + gcd_secret_key[4]='k';gcd_secret_key[5]='3';gcd_secret_key[6]='\0'; +#endif /**/ //strcpy(StatsServerHostname, "sdkdev.gamespy.com"); @@ -871,7 +871,6 @@ void PSThreadClass::Thread_Function() break; case PSRequest::PSREQUEST_READPLAYERSTATS: { - Bool initialConnection = FALSE; if (!MESSAGE_QUEUE->getLocalPlayerID()) { MESSAGE_QUEUE->setLocalPlayerID(req.player.id); // first request is for ourselves @@ -879,33 +878,14 @@ void PSThreadClass::Thread_Function() MESSAGE_QUEUE->setNick(req.nick); MESSAGE_QUEUE->setPassword(req.password); DEBUG_LOG(("Setting email/nick/password = %s/%s/%s", req.email.c_str(), req.nick.c_str(), req.password.c_str())); - initialConnection = TRUE; } DEBUG_LOG(("Processing PSRequest::PSREQUEST_READPLAYERSTATS")); if (tryConnect()) { - DEBUG_LOG(("Successful login")); incrOpCount(); gsi_char keys[] = ""; GetPersistDataValues(0, req.player.id, pd_public_rw, 0, keys, getPersistentDataCallback, this); } - else - { - DEBUG_LOG(("Unsuccessful login - retry=%d", initialConnection)); - PSResponse resp; - resp.responseType = PSResponse::PSRESPONSE_COULDNOTCONNECT; - resp.player.id = req.player.id; - TheGameSpyPSMessageQueue->addResponse(resp); - if (initialConnection) - { - // we haven't gotten stats for ourselves - try again - DEBUG_LOG(("Requesting retry for reading local player's stats")); - PSRequest req; - req.requestType = PSRequest::PSREQUEST_READPLAYERSTATS; - req.player.id = MESSAGE_QUEUE->getLocalPlayerID(); - TheGameSpyPSMessageQueue->addRequest(req); - } - } } break; case PSRequest::PSREQUEST_UPDATEPLAYERLOCALE: @@ -1333,6 +1313,8 @@ PSPlayerStats GameSpyPSMessageQueueInterface::parsePlayerKVPairs( std::string kv } \ } +#include "Common/PlayerTemplate.h" + std::string GameSpyPSMessageQueueInterface::formatPlayerKVPairs( PSPlayerStats stats ) { char kvbuf[256]; diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameSpy/GSConfig.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameSpy/GSConfig.h index 81b4207167..8645d8b445 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameSpy/GSConfig.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameSpy/GSConfig.h @@ -42,7 +42,7 @@ class GameSpyConfigInterface virtual Int getNumPingRepetitions(void) = 0; virtual Int getPingTimeoutInMs(void) = 0; virtual Int getPingCutoffGood( void ) = 0; - virtual Int getPingCutoffBad( void ) = 0; //Bryan sez, Maybe + virtual Int getPingCutoffBad( void ) = 0; // QM virtual std::list getQMMaps(void) = 0; @@ -60,8 +60,17 @@ class GameSpyConfigInterface // Ladder / Any other external parsing virtual AsciiString getLeftoverConfig(void) = 0; + // NAT Timeouts + virtual Int getTimeBetweenRetries() = 0; + virtual Int getMaxManglerRetries() = 0; + virtual time_t getRetryInterval() = 0; + virtual time_t getKeepaliveInterval() = 0; + virtual time_t getPortTimeout() = 0; + virtual time_t getRoundTimeout() = 0; + // Custom match virtual Bool restrictGamesToLobby() = 0; + static GameSpyConfigInterface* create(AsciiString config); }; diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameSpy/MainMenuUtils.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameSpy/MainMenuUtils.h index 44ca5bda7b..9aab07f760 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameSpy/MainMenuUtils.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameSpy/MainMenuUtils.h @@ -36,8 +36,30 @@ void CancelPatchCheckCallback( void ); void StartDownloadingPatches( void ); void HandleCanceledDownload( Bool resetDropDown = TRUE ); +#if RTS_GENERALS +enum OverallStatsPeriod CPP_11(: Int) +{ + STATS_TODAY = 0, + STATS_YESTERDAY, + STATS_ALLTIME, + STATS_LASTWEEK, + STATS_MAX +}; + +struct OverallStats +{ + OverallStats(); + Int wins[STATS_MAX]; + Int losses[STATS_MAX]; +}; +#endif + void CheckOverallStats( void ); +#if RTS_GENERALS +void HandleOverallStats( const OverallStats& USA, const OverallStats& China, const OverallStats& GLA ); +#else void HandleOverallStats( const char* szHTTPStats, unsigned len ); +#endif void CheckNumPlayersOnline( void ); void HandleNumPlayersOnline( Int numPlayersOnline ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Chat.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Chat.cpp index 398ab51ad4..05607eda30 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Chat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Chat.cpp @@ -89,12 +89,20 @@ Color GameSpyColor[GSCOLOR_MAX] = GameMakeColor(128,128,0,255), // GSCOLOR_GAME GameMakeColor(128,128,128,255), // GSCOLOR_GAME_FULL GameMakeColor(128,128,128,255), // GSCOLOR_GAME_CRCMISMATCH +#if RTS_GENERALS + GameMakeColor(255, 0, 0,255), // GSCOLOR_PLAYER_NORMAL +#else GameMakeColor(255,255,255,255), // GSCOLOR_PLAYER_NORMAL +#endif GameMakeColor(255, 0,255,255), // GSCOLOR_PLAYER_OWNER GameMakeColor(255, 0,128,255), // GSCOLOR_PLAYER_BUDDY GameMakeColor(255, 0, 0,255), // GSCOLOR_PLAYER_SELF GameMakeColor(128,128,128,255), // GSCOLOR_PLAYER_IGNORED +#if RTS_GENERALS + GameMakeColor(255,0,0,255), // GSCOLOR_CHAT_NORMAL +#else GameMakeColor(255,255,255,255), // GSCOLOR_CHAT_NORMAL +#endif GameMakeColor(255,128,0,255), // GSCOLOR_CHAT_EMOTE, GameMakeColor(255,255,0,255), // GSCOLOR_CHAT_OWNER, GameMakeColor(128,255,0,255), // GSCOLOR_CHAT_OWNER_EMOTE, diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/GSConfig.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/GSConfig.cpp index 2c13dcfbc3..a77af0e5a4 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/GSConfig.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/GSConfig.cpp @@ -332,11 +332,19 @@ m_qmChannel(0) mapName = TheGameState->portableMapPathToRealMapPath(TheGameState->realMapPathToPortableMapPath(mapName)); mapName.toLower(); + // [SKB: Jul 01 2003 @ 6:43pm] : + // German2 is missing some maps because of content. But, we need the m_qmMaps + // to contain same number of strings as the Retail version so that the + // QM Bot thinks that they have the same number of maps. + #if RTS_GENERALS + m_qmMaps.push_back(mapName); + #else const MapMetaData *md = TheMapCache->findMap(mapName); if (md) { m_qmMaps.push_back(mapName); } + #endif } else if (inQMBot) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/LadderDefs.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/LadderDefs.cpp index 470102c44e..ab8ea7cf33 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/LadderDefs.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/LadderDefs.cpp @@ -87,7 +87,7 @@ static LadderInfo *parseLadder(AsciiString raw) line.nextToken(&tokenHomepage, " "); lad->name = MultiByteToWideCharSingleLine(tokenName.str()).c_str(); - lad->name.truncateTo(20); // Per Harvard's request, ladder names are limited to 20 chars + lad->name.truncateTo(20); // Per Harvard's request, ladder names are limited to 20 chars lad->address = tokenAddr; lad->port = atoi(tokenPort.str()); lad->homepageURL = tokenHomepage; diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp index e24a7a5b7f..bb3b34ea1c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp @@ -72,7 +72,9 @@ enum { COLUMN_NUMPLAYERS, COLUMN_PASSWORD, COLUMN_OBSERVER, +#if !RTS_GENERALS COLUMN_USE_STATS, +#endif COLUMN_PING, }; @@ -247,6 +249,7 @@ static void gameTooltip(GameWindow *window, TheMouse->setCursorTooltip( UnicodeString::TheEmptyString ); return; } +#if !RTS_GENERALS if (col == COLUMN_USE_STATS) { if ( room->getUseStats() ) @@ -259,6 +262,7 @@ static void gameTooltip(GameWindow *window, } return; } +#endif UnicodeString tooltip; @@ -642,14 +646,17 @@ static Int insertGame( GameWindow *win, GameSpyStagingRoom *game, Bool showMap ) GadgetListBoxAddEntryText(win, UnicodeString(L" "), gameColor, index, COLUMN_OBSERVER); } +#if !RTS_GENERALS { if (game->getUseStats()) { - const Image *img = TheMappedImageCollection->findImageByName("GoodStatsIcon"); - GadgetListBoxAddEntryImage(win, img, index, COLUMN_USE_STATS, img->getImageHeight(), img->getImageWidth()); - } - + if (const Image *img = TheMappedImageCollection->findImageByName("GoodStatsIcon")) + { + GadgetListBoxAddEntryImage(win, img, index, COLUMN_USE_STATS, img->getImageHeight(), img->getImageWidth()); + } + } } +#endif s.format(L"%d", game->getPingAsInt()); GadgetListBoxAddEntryText(win, s, gameColor, index, COLUMN_PING); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/MainMenuUtils.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/MainMenuUtils.cpp index 95799a7534..d92743bc06 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/MainMenuUtils.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/MainMenuUtils.cpp @@ -582,7 +582,79 @@ static GHTTPBool overallStatsCallback( GHTTPRequest request, GHTTPResult result, return GHTTPTrue; } +#if RTS_GENERALS + OverallStats USA, China, GLA; + AsciiString message = buffer; + + Int state = STATS_MAX; // STATS_MAX == none + AsciiString line; + OverallStats *stats = NULL; + while (message.nextToken(&line, "\n")) + { + line.trim(); + line.toLower(); + if (strstr(line.str(), "today")) + { + state = STATS_TODAY; + } + else if (strstr(line.str(), "yesterday")) + { + state = STATS_YESTERDAY; + } + else if (strstr(line.str(), "all time")) + { + state = STATS_ALLTIME; + } + else if (strstr(line.str(), "last week")) + { + state = STATS_LASTWEEK; + } + else if (state != STATS_MAX && strstr(line.str(), "usa")) + { + stats = &USA; + } + else if (state != STATS_MAX && strstr(line.str(), "china")) + { + stats = &China; + } + else if (state != STATS_MAX && strstr(line.str(), "gla")) + { + stats = &GLA; + } + + if (stats) + { + AsciiString totalLine, winsLine, lossesLine; + message.nextToken(&totalLine, "\n"); + message.nextToken(&winsLine, "\n"); + message.nextToken(&lossesLine, "\n"); + while (totalLine.isNotEmpty() && !isdigit(totalLine.getCharAt(0))) + { + totalLine = totalLine.str()+1; + } + while (winsLine.isNotEmpty() && !isdigit(winsLine.getCharAt(0))) + { + winsLine = winsLine.str()+1; + } + while (lossesLine.isNotEmpty() && !isdigit(lossesLine.getCharAt(0))) + { + lossesLine = lossesLine.str()+1; + } + if (totalLine.isNotEmpty() && winsLine.isNotEmpty() && lossesLine.isNotEmpty()) + { + stats->wins[state] = atoi(winsLine.str()); + stats->losses[state] = atoi(lossesLine.str()); + } + + stats = NULL; + } + } + + HandleOverallStats(USA, China, GLA); +#elif RTS_ZEROHOUR HandleOverallStats( buffer, bufferLen ); +#endif + return GHTTPTrue; } @@ -617,16 +689,24 @@ static GHTTPBool numPlayersOnlineCallback( GHTTPRequest request, GHTTPResult res void CheckOverallStats( void ) { - ghttpGet("http://gamestats.gamespy.com/ccgenzh/display.html", - GHTTPFalse, overallStatsCallback, NULL); +#if RTS_GENERALS + const char *const url = "http://gamestats.gamespy.com/ccgenerals/display.html"; +#elif RTS_ZEROHOUR + const char *const url = "http://gamestats.gamespy.com/ccgenzh/display.html"; +#endif + ghttpGet(url, GHTTPFalse, overallStatsCallback, NULL); } /////////////////////////////////////////////////////////////////////////////////////// void CheckNumPlayersOnline( void ) { - ghttpGet("http://launch.gamespyarcade.com/software/launch/arcadecount2.dll?svcname=ccgenzh", - GHTTPFalse, numPlayersOnlineCallback, NULL); +#if RTS_GENERALS + const char *const url = "http://launch.gamespyarcade.com/software/launch/arcadecount2.dll?svcname=ccgenerals"; +#elif RTS_ZEROHOUR + const char *const url = "http://launch.gamespyarcade.com/software/launch/arcadecount2.dll?svcname=ccgenzh"; +#endif + ghttpGet(url, GHTTPFalse, numPlayersOnlineCallback, NULL); } /////////////////////////////////////////////////////////////////////////////////////// diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/StagingRoomGameInfo.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/StagingRoomGameInfo.cpp index a97cf33319..5a717d731b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/StagingRoomGameInfo.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/StagingRoomGameInfo.cpp @@ -726,7 +726,7 @@ AsciiString GameSpyStagingRoom::generateLadderGameResultsPacket( void ) endFrame, numPlayers, m_isQM, TheGameState->realMapPathToPortableMapPath(getMap()).str()); AsciiString tempStr; - tempStr.format("ladderIP=%s,ladderPort=%d", getLadderIP().str(), getLadderPort()); + tempStr.format(",ladderIP=%s,ladderPort=%d", getLadderIP().str(), getLadderPort()); results.concat(tempStr); Int playerID = 0; @@ -762,8 +762,13 @@ AsciiString GameSpyStagingRoom::generateLadderGameResultsPacket( void ) playerStr.format(",buildingsKilled%d=%d,buildingsLost%d=%d,buildingsBuilt%d=%d", playerID, buildingsKilled, playerID, buildingsLost, playerID, buildingsBuilt); results.concat(playerStr); +#if RTS_GENERALS + playerStr.format(",fps%d=%d,cash%d=%d,capturedTech%d=%d,discon%d=%d,side%d=%s,team%d=%d", + playerID, fps, playerID, earnings, playerID, techCaptured, playerID, disconnected, playerID, p[i]->getPlayerTemplate()->getSide().str(), playerID, slot->getTeamNumber()); +#elif RTS_ZEROHOUR playerStr.format(",fps%d=%d,cash%d=%d,capturedTech%d=%d,discon%d=%d,side%d=%s", playerID, fps, playerID, earnings, playerID, techCaptured, playerID, disconnected, playerID, p[i]->getPlayerTemplate()->getSide().str()); +#endif results.concat(playerStr); ++playerID; diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/BuddyThread.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/BuddyThread.cpp index a31644dca0..832eb912d5 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/BuddyThread.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/BuddyThread.cpp @@ -260,7 +260,12 @@ void BuddyThreadClass::Thread_Function() try { GPConnection gpCon; GPConnection *con = &gpCon; - gpInitialize( con, 823, 0, GP_PARTNERID_GAMESPY ); +#if RTS_GENERALS + const int productID = 675; +#elif RTS_ZEROHOUR + const int productID = 823; +#endif + gpInitialize( con, productID, 0, GP_PARTNERID_GAMESPY ); m_isConnected = m_isConnecting = false; gpSetCallback( con, GP_ERROR, callbackWrapper, (void *)CALLBACK_ERROR ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PeerThread.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PeerThread.cpp index 7798e45024..356f60723d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PeerThread.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PeerThread.cpp @@ -92,7 +92,9 @@ enum INICRC_KEY, PW_KEY, OBS_KEY, +#if !RTS_GENERALS USE_STATS_KEY, +#endif LADIP_KEY, LADPORT_KEY, PINGSTR_KEY, @@ -803,9 +805,11 @@ static void QRServerKeyCallback case OBS_KEY: ADDINT(t->allowObservers()); break; +#if !RTS_GENERALS case USE_STATS_KEY: ADDINT(t->useStats()); break; +#endif case LADIP_KEY: ADD(t->ladderIP().c_str()); break; @@ -950,7 +954,9 @@ static void QRKeyListCallback qr2_keybuffer_add(keyBuffer, INICRC_KEY); qr2_keybuffer_add(keyBuffer, PW_KEY); qr2_keybuffer_add(keyBuffer, OBS_KEY); +#if !RTS_GENERALS qr2_keybuffer_add(keyBuffer, USE_STATS_KEY); +#endif qr2_keybuffer_add(keyBuffer, LADIP_KEY); qr2_keybuffer_add(keyBuffer, LADPORT_KEY); qr2_keybuffer_add(keyBuffer, PINGSTR_KEY); @@ -1200,7 +1206,9 @@ void PeerThreadClass::Thread_Function() qr2_register_key(INICRC_KEY, INICRC_STR); qr2_register_key(PW_KEY, PW_STR); qr2_register_key(OBS_KEY, OBS_STR); +#if !RTS_GENERALS qr2_register_key(USE_STATS_KEY, USE_STATS_STR); +#endif qr2_register_key(LADIP_KEY, LADIP_STR); qr2_register_key(LADPORT_KEY, LADPORT_STR); qr2_register_key(PINGSTR_KEY, PINGSTR_STR); @@ -1213,7 +1221,11 @@ void PeerThreadClass::Thread_Function() qr2_register_key(FACTION__KEY, FACTION__STR "_"); qr2_register_key(COLOR__KEY, COLOR__STR "_"); +#if RTS_GENERALS + const Int NumKeys = 14; +#else const Int NumKeys = 15; +#endif unsigned char allKeysArray[NumKeys] = { /* PID__KEY, @@ -1230,7 +1242,9 @@ void PeerThreadClass::Thread_Function() INICRC_KEY, PW_KEY, OBS_KEY, +#if !RTS_GENERALS USE_STATS_KEY, +#endif LADIP_KEY, LADPORT_KEY, PINGSTR_KEY, @@ -1238,7 +1252,7 @@ void PeerThreadClass::Thread_Function() NUMPLAYER_KEY, MAXPLAYER_KEY, HOSTNAME_KEY - }; + }; /* const char *allKeys = "\\pid_\\mapname\\gamever\\gamename" \ @@ -1272,9 +1286,17 @@ void PeerThreadClass::Thread_Function() /********* First step, set our game authentication info We could do: + Generals: + strcpy(gcd_gamename,"ccgenerals"); + strcpy(gcd_secret_key,"h5T2f6"); + ZeroHour: strcpy(gcd_gamename,"ccgenzh"); strcpy(gcd_secret_key,"D6s9k3"); or + Generals: + strcpy(gcd_gamename,"ccgeneralsb"); + strcpy(gcd_secret_key,"g3T9s2"); + ZeroHour: strcpy(gcd_gamename,"ccgeneralsb"); strcpy(gcd_secret_key,"whatever the key is"); ...but this is more secure: @@ -1288,10 +1310,18 @@ void PeerThreadClass::Thread_Function() secretKey[0]='g';secretKey[1]='3';secretKey[2]='T';secretKey[3]='9'; secretKey[4]='s';secretKey[5]='2';secretKey[6]='\0'; /**/ +#if RTS_GENERALS + gameName[0]='c';gameName[1]='c';gameName[2]='g';gameName[3]='e'; + gameName[4]='n';gameName[5]='e';gameName[6]='r';gameName[7]='a'; + gameName[8]='l';gameName[9]='s';gameName[10]='\0'; + secretKey[0]='h';secretKey[1]='5';secretKey[2]='T';secretKey[3]='2'; + secretKey[4]='f';secretKey[5]='6';secretKey[6]='\0'; +#elif RTS_ZEROHOUR gameName[0]='c';gameName[1]='c';gameName[2]='g';gameName[3]='e'; gameName[4]='n';gameName[5]='z';gameName[6]='h';gameName[7]='\0'; secretKey[0]='D';secretKey[1]='6';secretKey[2]='s';secretKey[3]='9'; secretKey[4]='k';secretKey[5]='3';secretKey[6]='\0'; +#endif /**/ // Set the title. @@ -2681,8 +2711,6 @@ void playerLeftCallback(PEER peer, RoomType roomType, const char * nick, const c roomType, resp.player.flags); TheGameSpyPeerMessageQueue->addResponse(resp); -// PeerThreadClass *t = (PeerThreadClass *)param; -// DEBUG_ASSERTCRASH(t, ("No Peer thread!")); if (t->getQMStatus() != QM_IDLE && t->getQMStatus() != QM_STOPPED) { if (!stricmp(t->getQMBotName().c_str(), nick)) @@ -2824,7 +2852,11 @@ static void listingGamesCallback(PEER peer, PEERBool success, const char * name, { DEBUG_LOG(("Game name is '%s'", name)); const char *newname = SBServerGetStringValue(server, "gamename", (char *)name); +#if RTS_GENERALS + if (strcmp(newname, "ccgenerals")) +#elif RTS_ZEROHOUR if (strcmp(newname, "ccgenzh")) +#endif name = newname; DEBUG_LOG(("Game name is now '%s'", name)); } diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PersistentStorageThread.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PersistentStorageThread.cpp index 97c5b65352..b620d375ed 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PersistentStorageThread.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameSpy/Thread/PersistentStorageThread.cpp @@ -809,9 +809,17 @@ void PSThreadClass::Thread_Function() /********* First step, set our game authentication info We could do: + Generals: + strcpy(gcd_gamename,"ccgenerals"); + strcpy(gcd_secret_key,"h5T2f6"); + ZeroHour: strcpy(gcd_gamename,"ccgenzh"); strcpy(gcd_secret_key,"D6s9k3"); or + Generals: + strcpy(gcd_gamename,"ccgeneralsb"); + strcpy(gcd_secret_key,"g3T9s2"); + ZeroHour: strcpy(gcd_gamename,"ccgeneralsb"); strcpy(gcd_secret_key,"whatever the key is"); ...but this is more secure: @@ -823,10 +831,18 @@ void PSThreadClass::Thread_Function() gcd_secret_key[0]='g';gcd_secret_key[1]='3';gcd_secret_key[2]='T';gcd_secret_key[3]='9'; gcd_secret_key[4]='s';gcd_secret_key[5]='2';gcd_secret_key[6]='\0'; /**/ +#if RTS_GENERALS + gcd_gamename[0]='c';gcd_gamename[1]='c';gcd_gamename[2]='g';gcd_gamename[3]='e'; + gcd_gamename[4]='n';gcd_gamename[5]='e';gcd_gamename[6]='r';gcd_gamename[7]='a'; + gcd_gamename[8]='l';gcd_gamename[9]='s';gcd_gamename[10]='\0'; + gcd_secret_key[0]='h';gcd_secret_key[1]='5';gcd_secret_key[2]='T';gcd_secret_key[3]='2'; + gcd_secret_key[4]='f';gcd_secret_key[5]='6';gcd_secret_key[6]='\0'; +#elif RTS_ZEROHOUR gcd_gamename[0]='c';gcd_gamename[1]='c';gcd_gamename[2]='g';gcd_gamename[3]='e'; gcd_gamename[4]='n';gcd_gamename[5]='z';gcd_gamename[6]='h';gcd_gamename[7]='\0'; gcd_secret_key[0]='D';gcd_secret_key[1]='6';gcd_secret_key[2]='s';gcd_secret_key[3]='9'; gcd_secret_key[4]='k';gcd_secret_key[5]='3';gcd_secret_key[6]='\0'; +#endif /**/ //strcpy(StatsServerHostname, "sdkdev.gamespy.com");