diff --git a/Generals/Code/GameEngine/Include/Common/GlobalData.h b/Generals/Code/GameEngine/Include/Common/GlobalData.h index 5e2bf47be9..c56d1b4816 100644 --- a/Generals/Code/GameEngine/Include/Common/GlobalData.h +++ b/Generals/Code/GameEngine/Include/Common/GlobalData.h @@ -399,6 +399,9 @@ class GlobalData : public SubsystemInterface Bool m_saveCameraInReplay; Bool m_useCameraInReplay; + // TheSuperHackers @feature L3-M 05/09/2025 allow the network latency counter and render fps counter font size to be set, a size of zero disables them + Int m_networkLatencyFontSize; + Int m_renderFpsFontSize; // TheSuperHackers @feature Mauller 21/06/2025 allow the system time and game time font size to be set, a size of zero disables them Int m_systemTimeFontSize; Int m_gameTimeFontSize; diff --git a/Generals/Code/GameEngine/Include/Common/UserPreferences.h b/Generals/Code/GameEngine/Include/Common/UserPreferences.h index 5f1b033aed..9c5c70af6b 100644 --- a/Generals/Code/GameEngine/Include/Common/UserPreferences.h +++ b/Generals/Code/GameEngine/Include/Common/UserPreferences.h @@ -135,6 +135,8 @@ class OptionPreferences : public UserPreferences Int getCampaignDifficulty(void); void setCampaignDifficulty( Int diff ); + Int getNetworkLatencyFontSize(void); + Int getRenderFpsFontSize(void); Int getSystemTimeFontSize(void); Int getGameTimeFontSize(void); diff --git a/Generals/Code/GameEngine/Include/GameClient/InGameUI.h b/Generals/Code/GameEngine/Include/GameClient/InGameUI.h index 8e712e5961..f5d0a3c7f6 100644 --- a/Generals/Code/GameEngine/Include/GameClient/InGameUI.h +++ b/Generals/Code/GameEngine/Include/GameClient/InGameUI.h @@ -554,6 +554,8 @@ friend class Drawable; // for selection/deselection transactions virtual void recreateControlBar( void ); virtual void refreshCustomUiResources( void ); + virtual void refreshNetworkLatencyResources(void); + virtual void refreshRenderFpsResources(void); virtual void refreshSystemTimeResources( void ); virtual void refreshGameTimeResources( void ); @@ -575,7 +577,10 @@ friend class Drawable; // for selection/deselection transactions virtual void updateIdleWorker( void ); virtual void resetIdleWorker( void ); - void drawSystemTime(); + void updateRenderFpsString(); + void drawNetworkLatency(Int &x, Int &y); + void drawRenderFps(Int &x, Int &y); + void drawSystemTime(Int &x, Int &y); void drawGameTime(); public: @@ -731,6 +736,31 @@ friend class Drawable; // for selection/deselection transactions VideoBuffer* m_cameoVideoBuffer;///< video playback buffer VideoStreamInterface* m_cameoVideoStream;///< Video stream; + // Network Latency Counter + DisplayString * m_networkLatencyString; + AsciiString m_networkLatencyFont; + Int m_networkLatencyPointSize; + Bool m_networkLatencyBold; + Coord2D m_networkLatencyPosition; + Color m_networkLatencyColor; + Color m_networkLatencyDropColor; + UnsignedInt m_lastNetworkLatencyFrames; + + // Render FPS Counter + DisplayString * m_renderFpsString; + DisplayString * m_renderFpsLimitString; + AsciiString m_renderFpsFont; + Int m_renderFpsPointSize; + Bool m_renderFpsBold; + Coord2D m_renderFpsPosition; + Color m_renderFpsColor; + Color m_renderFpsLimitColor; + Color m_renderFpsDropColor; + UnsignedInt m_renderFpsRefreshMs; + UnsignedInt m_lastRenderFps; + UnsignedInt m_lastRenderFpsLimit; + UnsignedInt m_lastRenderFpsUpdateMs; + // System Time DisplayString * m_systemTimeString; AsciiString m_systemTimeFont; diff --git a/Generals/Code/GameEngine/Source/Common/GlobalData.cpp b/Generals/Code/GameEngine/Source/Common/GlobalData.cpp index a44456ef55..28f5250f91 100644 --- a/Generals/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/Generals/Code/GameEngine/Source/Common/GlobalData.cpp @@ -933,6 +933,8 @@ GlobalData::GlobalData() m_saveCameraInReplay = FALSE; m_useCameraInReplay = FALSE; + m_networkLatencyFontSize = 8; + m_renderFpsFontSize = 8; m_systemTimeFontSize = 8; m_gameTimeFontSize = 8; @@ -1184,6 +1186,8 @@ void GlobalData::parseGameDataDefinition( INI* ini ) TheWritableGlobalData->m_saveCameraInReplay = optionPref.saveCameraInReplays(); TheWritableGlobalData->m_useCameraInReplay = optionPref.useCameraInReplays(); + TheWritableGlobalData->m_networkLatencyFontSize = optionPref.getNetworkLatencyFontSize(); + TheWritableGlobalData->m_renderFpsFontSize = optionPref.getRenderFpsFontSize(); TheWritableGlobalData->m_systemTimeFontSize = optionPref.getSystemTimeFontSize(); TheWritableGlobalData->m_gameTimeFontSize = optionPref.getGameTimeFontSize(); diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp index 041dc6850e..ee5b192597 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp @@ -814,6 +814,34 @@ Real OptionPreferences::getMoneyTransactionVolume(void) const return volume; } +Int OptionPreferences::getNetworkLatencyFontSize(void) +{ + OptionPreferences::const_iterator it = find("NetworkLatencyFontSize"); + if (it == end()) + return 8; + + Int fontSize = atoi(it->second.str()); + if (fontSize < 0) + { + fontSize = 0; + } + return fontSize; +} + +Int OptionPreferences::getRenderFpsFontSize(void) +{ + OptionPreferences::const_iterator it = find("RenderFpsFontSize"); + if (it == end()) + return 8; + + Int fontSize = atoi(it->second.str()); + if (fontSize < 0) + { + fontSize = 0; + } + return fontSize; +} + Int OptionPreferences::getSystemTimeFontSize(void) { OptionPreferences::const_iterator it = find("SystemTimeFontSize"); @@ -1370,6 +1398,28 @@ static void saveOptions( void ) } } + //------------------------------------------------------------------------------------------------- + // Set Network Latency Font Size + val = pref->getNetworkLatencyFontSize(); + if (val >= 0) + { + AsciiString prefString; + prefString.format("%d", val); + (*pref)["NetworkLatencyFontSize"] = prefString; + TheInGameUI->refreshNetworkLatencyResources(); + } + + //------------------------------------------------------------------------------------------------- + // Set Render FPS Font Size + val = pref->getRenderFpsFontSize(); + if (val >= 0) + { + AsciiString prefString; + prefString.format("%d", val); + (*pref)["RenderFpsFontSize"] = prefString; + TheInGameUI->refreshRenderFpsResources(); + } + //------------------------------------------------------------------------------------------------- // Set System Time Font Size val = pref->getSystemTimeFontSize(); // TheSuperHackers @todo replace with options input when applicable diff --git a/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp b/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp index 881f74b053..ed5de55706 100644 --- a/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -46,6 +46,7 @@ #include "Common/BuildAssistant.h" #include "Common/Recorder.h" #include "Common/SpecialPower.h" +#include "Common/FrameRateLimit.h" #include "GameClient/Anim2D.h" #include "GameClient/ControlBar.h" @@ -71,6 +72,7 @@ #include "GameClient/SelectionXlat.h" #include "GameClient/Shadow.h" #include "GameClient/GlobalLanguage.h" +#include "GameClient/Display.h" #include "GameLogic/AIGuard.h" #include "GameLogic/Weapon.h" @@ -84,6 +86,8 @@ #include "GameLogic/Module/SupplyWarehouseDockUpdate.h" #include "GameLogic/Module/MobMemberSlavedUpdate.h"//ML +#include "GameNetwork/NetworkInterface.h" + // ------------------------------------------------------------------------------------------------ @@ -840,6 +844,20 @@ const FieldParse InGameUI::s_fieldParseTable[] = { "SpyDroneRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_SPYDRONE] ) }, // TheSuperHackers @info ui enhancement configuration + { "NetworkLatencyFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_networkLatencyFont ) }, + { "NetworkLatencyBold", INI::parseBool, NULL, offsetof( InGameUI, m_networkLatencyBold ) }, + { "NetworkLatencyPosition", INI::parseCoord2D, NULL, offsetof( InGameUI, m_networkLatencyPosition ) }, + { "NetworkLatencyColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_networkLatencyColor ) }, + { "NetworkLatencyDropColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_networkLatencyDropColor ) }, + + { "RenderFpsFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_renderFpsFont ) }, + { "RenderFpsBold", INI::parseBool, NULL, offsetof( InGameUI, m_renderFpsBold ) }, + { "RenderFpsPosition", INI::parseCoord2D, NULL, offsetof( InGameUI, m_renderFpsPosition ) }, + { "RenderFpsColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_renderFpsColor ) }, + { "RenderFpsLimitColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_renderFpsLimitColor ) }, + { "RenderFpsDropColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_renderFpsDropColor ) }, + { "RenderFpsRefreshMs", INI::parseUnsignedInt, NULL, offsetof( InGameUI, m_renderFpsRefreshMs ) }, + { "SystemTimeFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_systemTimeFont ) }, { "SystemTimeBold", INI::parseBool, NULL, offsetof( InGameUI, m_systemTimeBold ) }, { "SystemTimePosition", INI::parseCoord2D, NULL, offsetof( InGameUI, m_systemTimePosition ) }, @@ -867,6 +885,16 @@ void INI::parseInGameUIDefinition( INI* ini ) } } +//------------------------------------------------------------------------------------------------- +namespace +{ + // helpers for inline counters + constexpr const Int kHudAnchorX = 3; + constexpr const Int kHudAnchorY = -1; + constexpr const Int kHudGapPx = 6; + inline Bool isAtHudAnchorPos(const Coord2D &p) { return p.x == kHudAnchorX && p.y == kHudAnchorY; } +} + //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- InGameUI::InGameUI() @@ -976,13 +1004,38 @@ InGameUI::InGameUI() m_replayWindow = NULL; m_messagesOn = TRUE; - // TheSuperHackers @info the default font, size and positions of the system and game times were chosen based on GenTools implementation + // TheSuperHackers @info the default font, size and positions of the various counters were chosen based on GenTools implementation + m_networkLatencyString = NULL; + m_networkLatencyFont = "Tahoma"; + m_networkLatencyPointSize = TheGlobalData->m_networkLatencyFontSize; + m_networkLatencyBold = TRUE; + m_networkLatencyPosition.x = kHudAnchorX; + m_networkLatencyPosition.y = kHudAnchorY; + m_networkLatencyColor = GameMakeColor( 173, 216, 255, 255 ); + m_networkLatencyDropColor = GameMakeColor( 0, 0, 0, 255 ); + m_lastNetworkLatencyFrames = ~0u; + + m_renderFpsString = NULL; + m_renderFpsLimitString = NULL; + m_renderFpsFont = "Tahoma"; + m_renderFpsPointSize = TheGlobalData->m_renderFpsFontSize; + m_renderFpsBold = TRUE; + m_renderFpsPosition.x = kHudAnchorX; + m_renderFpsPosition.y = kHudAnchorY; + m_renderFpsColor = GameMakeColor( 255, 255, 0, 255 ); + m_renderFpsLimitColor = GameMakeColor(119, 119, 119, 255); + m_renderFpsDropColor = GameMakeColor( 0, 0, 0, 255 ); + m_renderFpsRefreshMs = 1000; + m_lastRenderFps = ~0u; + m_lastRenderFpsLimit = ~0u; + m_lastRenderFpsUpdateMs = 0u; + m_systemTimeString = NULL; m_systemTimeFont = "Tahoma"; m_systemTimePointSize = TheGlobalData->m_systemTimeFontSize; m_systemTimeBold = TRUE; - m_systemTimePosition.x = 3; // TheSuperHackers @info relative to the left of the screen - m_systemTimePosition.y = -1; + m_systemTimePosition.x = kHudAnchorX; // TheSuperHackers @info relative to the left of the screen + m_systemTimePosition.y = kHudAnchorY; m_systemTimeColor = GameMakeColor( 255, 255, 255, 255 ); m_systemTimeDropColor = GameMakeColor( 0, 0, 0, 255 ); @@ -991,8 +1044,8 @@ InGameUI::InGameUI() m_gameTimeFont = "Tahoma"; m_gameTimePointSize = TheGlobalData->m_gameTimeFontSize; m_gameTimeBold = TRUE; - m_gameTimePosition.x = 3; // TheSuperHackers @info relative to the right of the screen - m_gameTimePosition.y = -1; + m_gameTimePosition.x = kHudAnchorX; // TheSuperHackers @info relative to the right of the screen + m_gameTimePosition.y = kHudAnchorY; m_gameTimeColor = GameMakeColor( 255, 255, 255, 255 ); m_gameTimeDropColor = GameMakeColor( 0, 0, 0, 255 ); @@ -2006,6 +2059,12 @@ void InGameUI::freeMessageResources( void ) void InGameUI::freeCustomUiResources( void ) { + TheDisplayStringManager->freeDisplayString(m_networkLatencyString); + m_networkLatencyString = NULL; + TheDisplayStringManager->freeDisplayString(m_renderFpsString); + m_renderFpsString = NULL; + TheDisplayStringManager->freeDisplayString(m_renderFpsLimitString); + m_renderFpsLimitString = NULL; TheDisplayStringManager->freeDisplayString(m_systemTimeString); m_systemTimeString = NULL; TheDisplayStringManager->freeDisplayString(m_gameTimeString); @@ -3459,9 +3518,22 @@ void InGameUI::disregardDrawable( Drawable *draw ) //------------------------------------------------------------------------------------------------- void InGameUI::postWindowDraw( void ) { + Int hudOffsetX = 0; + Int hudOffsetY = 0; + + if (m_networkLatencyPointSize > 0 && TheGameLogic->isInMultiplayerGame()) + { + drawNetworkLatency(hudOffsetX, hudOffsetY); + } + + if (m_renderFpsPointSize > 0) + { + drawRenderFps(hudOffsetX, hudOffsetY); + } + if (m_systemTimePointSize > 0) { - drawSystemTime(); + drawSystemTime(hudOffsetX, hudOffsetY); } if ( (m_gameTimePointSize > 0) && !TheGameLogic->isInShellGame() && TheGameLogic->isInGame() ) @@ -5613,10 +5685,49 @@ void InGameUI::recreateControlBar( void ) void InGameUI::refreshCustomUiResources(void) { + refreshNetworkLatencyResources(); + refreshRenderFpsResources(); refreshSystemTimeResources(); refreshGameTimeResources(); } +void InGameUI::refreshNetworkLatencyResources(void) +{ + if (!m_networkLatencyString) + { + m_networkLatencyString = TheDisplayStringManager->newDisplayString(); + m_lastNetworkLatencyFrames = ~0u; + } + + m_networkLatencyPointSize = TheGlobalData->m_networkLatencyFontSize; + Int adjustedNetworkLatencyFontSize = TheGlobalLanguageData->adjustFontSize(m_networkLatencyPointSize); + GameFont* latencyFont = TheWindowManager->winFindFont(m_networkLatencyFont, adjustedNetworkLatencyFontSize, m_networkLatencyBold); + m_networkLatencyString->setFont(latencyFont); +} + +void InGameUI::refreshRenderFpsResources(void) +{ + if (!m_renderFpsString) + { + m_renderFpsString = TheDisplayStringManager->newDisplayString(); + m_lastRenderFps = ~0u; + m_lastRenderFpsUpdateMs = 0u; + updateRenderFpsString(); + } + + if (!m_renderFpsLimitString) + { + m_renderFpsLimitString = TheDisplayStringManager->newDisplayString(); + m_lastRenderFpsLimit = ~0u; + } + + m_renderFpsPointSize = TheGlobalData->m_renderFpsFontSize; + Int adjustedRenderFpsFontSize = TheGlobalLanguageData->adjustFontSize(m_renderFpsPointSize); + GameFont *fpsFont = TheWindowManager->winFindFont(m_renderFpsFont, adjustedRenderFpsFontSize, m_renderFpsBold); + m_renderFpsString->setFont(fpsFont); + m_renderFpsLimitString->setFont(fpsFont); +} + void InGameUI::refreshSystemTimeResources(void) { if (!m_systemTimeString) @@ -5703,17 +5814,114 @@ WindowMsgHandledType IdleWorkerSystem( GameWindow *window, UnsignedInt msg, } -void InGameUI::drawSystemTime() + +void InGameUI::updateRenderFpsString() +{ + const UnsignedInt renderFps = (UnsignedInt)(TheDisplay->getAverageFPS() + 0.5f); + if (renderFps != m_lastRenderFps) + { + UnicodeString fpsStr; + fpsStr.format(L"%u", renderFps); + m_renderFpsString->setText(fpsStr); + m_lastRenderFps = renderFps; + } +} + +void InGameUI::drawNetworkLatency(Int &x, Int &y) +{ + const UnsignedInt networkLatencyFrames = TheNetwork->getRunAhead(); + + if (networkLatencyFrames != m_lastNetworkLatencyFrames) + { + UnicodeString latencyStr; + latencyStr.format(L"%u", networkLatencyFrames); + m_networkLatencyString->setText(latencyStr); + m_lastNetworkLatencyFrames = networkLatencyFrames; + } + + // TheSuperHackers @info at the HUD anchor this draws inline and advances x otherwise uses configured position + if (isAtHudAnchorPos(m_networkLatencyPosition)) + { + m_networkLatencyString->draw(kHudAnchorX + x, kHudAnchorY + y, m_networkLatencyColor, m_networkLatencyDropColor); + x += m_networkLatencyString->getWidth() + kHudGapPx; + } + else + { + m_networkLatencyString->draw(m_networkLatencyPosition.x, m_networkLatencyPosition.y, m_networkLatencyColor, m_networkLatencyDropColor); + } +} + +void InGameUI::drawRenderFps(Int &x, Int &y) +{ + if (m_renderFpsRefreshMs > 0u) + { + const UnsignedInt nowMs = timeGetTime(); + const UnsignedInt deltaMs = nowMs - m_lastRenderFpsUpdateMs; + if (deltaMs >= m_renderFpsRefreshMs) + { + m_lastRenderFpsUpdateMs = nowMs; + updateRenderFpsString(); + } + } + else + { + updateRenderFpsString(); + } + + UnsignedInt renderFpsLimit = 0u; + if (TheGlobalData->m_useFpsLimit) + { + renderFpsLimit = (UnsignedInt)TheGameEngine->getFramesPerSecondLimit(); + if (renderFpsLimit == RenderFpsPreset::UncappedFpsValue) + { + renderFpsLimit = 0u; + } + } + if (renderFpsLimit != m_lastRenderFpsLimit) + { + UnicodeString fpsLimitStr; + fpsLimitStr.format(L"[%u]", renderFpsLimit); + m_renderFpsLimitString->setText(fpsLimitStr); + m_lastRenderFpsLimit = renderFpsLimit; + } + + // TheSuperHackers @info at the HUD anchor this draws inline and advances x otherwise uses configured position + if (isAtHudAnchorPos(m_renderFpsPosition)) + { + const Int drawY = kHudAnchorY + y; + + m_renderFpsString->draw(kHudAnchorX + x, drawY, m_renderFpsColor, m_renderFpsDropColor); + x += m_renderFpsString->getWidth(); + m_renderFpsLimitString->draw(kHudAnchorX + x, drawY, m_renderFpsLimitColor, m_renderFpsDropColor); + x += m_renderFpsLimitString->getWidth() + kHudGapPx; + } + else + { + m_renderFpsString->draw(m_renderFpsPosition.x, m_renderFpsPosition.y, m_renderFpsColor, m_renderFpsDropColor); + m_renderFpsLimitString->draw(m_renderFpsPosition.x + m_renderFpsString->getWidth(), m_renderFpsPosition.y, m_renderFpsLimitColor, m_renderFpsDropColor); + } +} + +void InGameUI::drawSystemTime(Int &x, Int &y) { // current system time SYSTEMTIME systemTime; GetLocalTime( &systemTime ); - UnicodeString TimeString; - TimeString.format(L"%2.2d:%2.2d:%2.2d", systemTime.wHour, systemTime.wMinute, systemTime.wSecond); - m_systemTimeString->setText(TimeString); + UnicodeString TimeString; + TimeString.format(L"%2.2d:%2.2d:%2.2d", systemTime.wHour, systemTime.wMinute, systemTime.wSecond); + m_systemTimeString->setText(TimeString); - m_systemTimeString->draw(m_systemTimePosition.x, m_systemTimePosition.y, m_systemTimeColor, m_systemTimeDropColor); + // TheSuperHackers @info at the HUD anchor this draws inline and advances x otherwise uses configured position + if (isAtHudAnchorPos(m_systemTimePosition)) + { + m_systemTimeString->draw(kHudAnchorX + x, kHudAnchorY + y, m_systemTimeColor, m_systemTimeDropColor); + x += m_systemTimeString->getWidth() + kHudGapPx; + } + else + { + m_systemTimeString->draw(m_systemTimePosition.x, m_systemTimePosition.y, m_systemTimeColor, m_systemTimeDropColor); + } } void InGameUI::drawGameTime() diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h index 190f2ac35a..7bb3e5a0b4 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h @@ -407,6 +407,9 @@ class GlobalData : public SubsystemInterface Bool m_saveCameraInReplay; Bool m_useCameraInReplay; + // TheSuperHackers @feature L3-M 05/09/2025 allow the network latency counter and render fps counter font size to be set, a size of zero disables them + Int m_networkLatencyFontSize; + Int m_renderFpsFontSize; // TheSuperHackers @feature Mauller 21/06/2025 allow the system time and game time font size to be set, a size of zero disables them Int m_systemTimeFontSize; Int m_gameTimeFontSize; diff --git a/GeneralsMD/Code/GameEngine/Include/Common/UserPreferences.h b/GeneralsMD/Code/GameEngine/Include/Common/UserPreferences.h index 369d16f627..d748fdc4cd 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/UserPreferences.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/UserPreferences.h @@ -139,6 +139,8 @@ class OptionPreferences : public UserPreferences Int getCampaignDifficulty(void); void setCampaignDifficulty( Int diff ); + Int getNetworkLatencyFontSize(void); + Int getRenderFpsFontSize(void); Int getSystemTimeFontSize(void); Int getGameTimeFontSize(void); diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h b/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h index 631f371fc5..4b12206c40 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h @@ -571,6 +571,8 @@ friend class Drawable; // for selection/deselection transactions virtual void recreateControlBar( void ); virtual void refreshCustomUiResources( void ); + virtual void refreshNetworkLatencyResources(void); + virtual void refreshRenderFpsResources(void); virtual void refreshSystemTimeResources( void ); virtual void refreshGameTimeResources( void ); @@ -592,7 +594,10 @@ friend class Drawable; // for selection/deselection transactions virtual void updateIdleWorker( void ); virtual void resetIdleWorker( void ); - void drawSystemTime(); + void updateRenderFpsString(); + void drawNetworkLatency(Int &x, Int &y); + void drawRenderFps(Int &x, Int &y); + void drawSystemTime(Int &x, Int &y); void drawGameTime(); public: @@ -753,6 +758,31 @@ friend class Drawable; // for selection/deselection transactions VideoBuffer* m_cameoVideoBuffer;///< video playback buffer VideoStreamInterface* m_cameoVideoStream;///< Video stream; + // Network Latency Counter + DisplayString * m_networkLatencyString; + AsciiString m_networkLatencyFont; + Int m_networkLatencyPointSize; + Bool m_networkLatencyBold; + Coord2D m_networkLatencyPosition; + Color m_networkLatencyColor; + Color m_networkLatencyDropColor; + UnsignedInt m_lastNetworkLatencyFrames; + + // Render FPS Counter + DisplayString * m_renderFpsString; + DisplayString * m_renderFpsLimitString; + AsciiString m_renderFpsFont; + Int m_renderFpsPointSize; + Bool m_renderFpsBold; + Coord2D m_renderFpsPosition; + Color m_renderFpsColor; + Color m_renderFpsLimitColor; + Color m_renderFpsDropColor; + UnsignedInt m_renderFpsRefreshMs; + UnsignedInt m_lastRenderFps; + UnsignedInt m_lastRenderFpsLimit; + UnsignedInt m_lastRenderFpsUpdateMs; + // System Time DisplayString * m_systemTimeString; AsciiString m_systemTimeFont; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp index de74c70fb0..bc4ac4f2bb 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp @@ -942,6 +942,8 @@ GlobalData::GlobalData() m_saveCameraInReplay = FALSE; m_useCameraInReplay = FALSE; + m_networkLatencyFontSize = 8; + m_renderFpsFontSize = 8; m_systemTimeFontSize = 8; m_gameTimeFontSize = 8; @@ -1212,6 +1214,8 @@ void GlobalData::parseGameDataDefinition( INI* ini ) TheWritableGlobalData->m_saveCameraInReplay = optionPref.saveCameraInReplays(); TheWritableGlobalData->m_useCameraInReplay = optionPref.useCameraInReplays(); + TheWritableGlobalData->m_networkLatencyFontSize = optionPref.getNetworkLatencyFontSize(); + TheWritableGlobalData->m_renderFpsFontSize = optionPref.getRenderFpsFontSize(); TheWritableGlobalData->m_systemTimeFontSize = optionPref.getSystemTimeFontSize(); TheWritableGlobalData->m_gameTimeFontSize = optionPref.getGameTimeFontSize(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp index 85952fe57b..cd247b0bb0 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp @@ -858,6 +858,34 @@ Real OptionPreferences::getMoneyTransactionVolume(void) const return volume; } +Int OptionPreferences::getNetworkLatencyFontSize(void) +{ + OptionPreferences::const_iterator it = find("NetworkLatencyFontSize"); + if (it == end()) + return 8; + + Int fontSize = atoi(it->second.str()); + if (fontSize < 0) + { + fontSize = 0; + } + return fontSize; +} + +Int OptionPreferences::getRenderFpsFontSize(void) +{ + OptionPreferences::const_iterator it = find("RenderFpsFontSize"); + if (it == end()) + return 8; + + Int fontSize = atoi(it->second.str()); + if (fontSize < 0) + { + fontSize = 0; + } + return fontSize; +} + Int OptionPreferences::getSystemTimeFontSize(void) { OptionPreferences::const_iterator it = find("SystemTimeFontSize"); @@ -1430,6 +1458,28 @@ static void saveOptions( void ) } } + //------------------------------------------------------------------------------------------------- + // Set Network Latency Font Size + val = pref->getNetworkLatencyFontSize(); + if (val >= 0) + { + AsciiString prefString; + prefString.format("%d", val); + (*pref)["NetworkLatencyFontSize"] = prefString; + TheInGameUI->refreshNetworkLatencyResources(); + } + + //------------------------------------------------------------------------------------------------- + // Set Render FPS Font Size + val = pref->getRenderFpsFontSize(); + if (val >= 0) + { + AsciiString prefString; + prefString.format("%d", val); + (*pref)["RenderFpsFontSize"] = prefString; + TheInGameUI->refreshRenderFpsResources(); + } + //------------------------------------------------------------------------------------------------- // Set System Time Font Size val = pref->getSystemTimeFontSize(); // TheSuperHackers @todo replace with options input when applicable diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 03859cf913..70f367d029 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -46,6 +46,7 @@ #include "Common/BuildAssistant.h" #include "Common/Recorder.h" #include "Common/SpecialPower.h" +#include "Common/FrameRateLimit.h" #include "GameClient/Anim2D.h" #include "GameClient/ControlBar.h" @@ -72,6 +73,7 @@ #include "GameClient/SelectionXlat.h" #include "GameClient/Shadow.h" #include "GameClient/GlobalLanguage.h" +#include "GameClient/Display.h" #include "GameLogic/AIGuard.h" #include "GameLogic/Weapon.h" @@ -86,6 +88,8 @@ #include "GameLogic/Module/SupplyWarehouseDockUpdate.h" #include "GameLogic/Module/MobMemberSlavedUpdate.h"//ML +#include "GameNetwork/NetworkInterface.h" + #include "Common/UnitTimings.h" //Contains the DO_UNIT_TIMINGS define jba. @@ -869,6 +873,20 @@ const FieldParse InGameUI::s_fieldParseTable[] = { "AmbulanceRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_AMBULANCE] ) }, // TheSuperHackers @info ui enhancement configuration + { "NetworkLatencyFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_networkLatencyFont ) }, + { "NetworkLatencyBold", INI::parseBool, NULL, offsetof( InGameUI, m_networkLatencyBold ) }, + { "NetworkLatencyPosition", INI::parseCoord2D, NULL, offsetof( InGameUI, m_networkLatencyPosition ) }, + { "NetworkLatencyColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_networkLatencyColor ) }, + { "NetworkLatencyDropColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_networkLatencyDropColor ) }, + + { "RenderFpsFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_renderFpsFont ) }, + { "RenderFpsBold", INI::parseBool, NULL, offsetof( InGameUI, m_renderFpsBold ) }, + { "RenderFpsPosition", INI::parseCoord2D, NULL, offsetof( InGameUI, m_renderFpsPosition ) }, + { "RenderFpsColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_renderFpsColor ) }, + { "RenderFpsLimitColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_renderFpsLimitColor ) }, + { "RenderFpsDropColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_renderFpsDropColor ) }, + { "RenderFpsRefreshMs", INI::parseUnsignedInt, NULL, offsetof( InGameUI, m_renderFpsRefreshMs ) }, + { "SystemTimeFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_systemTimeFont ) }, { "SystemTimeBold", INI::parseBool, NULL, offsetof( InGameUI, m_systemTimeBold ) }, { "SystemTimePosition", INI::parseCoord2D, NULL, offsetof( InGameUI, m_systemTimePosition ) }, @@ -896,6 +914,16 @@ void INI::parseInGameUIDefinition( INI* ini ) } } +//------------------------------------------------------------------------------------------------- +namespace +{ + // helpers for inline counters + constexpr const Int kHudAnchorX = 3; + constexpr const Int kHudAnchorY = -1; + constexpr const Int kHudGapPx = 6; + inline Bool isAtHudAnchorPos(const Coord2D &p) { return p.x == kHudAnchorX && p.y == kHudAnchorY; } +} + //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- InGameUI::InGameUI() @@ -1006,13 +1034,38 @@ InGameUI::InGameUI() m_replayWindow = NULL; m_messagesOn = TRUE; - // TheSuperHackers @info the default font, size and positions of the system and game times were chosen based on GenTools implementation + // TheSuperHackers @info the default font, size and positions of the various counters were chosen based on GenTools implementation + m_networkLatencyString = NULL; + m_networkLatencyFont = "Tahoma"; + m_networkLatencyPointSize = TheGlobalData->m_networkLatencyFontSize; + m_networkLatencyBold = TRUE; + m_networkLatencyPosition.x = kHudAnchorX; + m_networkLatencyPosition.y = kHudAnchorY; + m_networkLatencyColor = GameMakeColor( 173, 216, 255, 255 ); + m_networkLatencyDropColor = GameMakeColor( 0, 0, 0, 255 ); + m_lastNetworkLatencyFrames = ~0u; + + m_renderFpsString = NULL; + m_renderFpsLimitString = NULL; + m_renderFpsFont = "Tahoma"; + m_renderFpsPointSize = TheGlobalData->m_renderFpsFontSize; + m_renderFpsBold = TRUE; + m_renderFpsPosition.x = kHudAnchorX; + m_renderFpsPosition.y = kHudAnchorY; + m_renderFpsColor = GameMakeColor( 255, 255, 0, 255 ); + m_renderFpsLimitColor = GameMakeColor(119, 119, 119, 255); + m_renderFpsDropColor = GameMakeColor( 0, 0, 0, 255 ); + m_renderFpsRefreshMs = 1000; + m_lastRenderFps = ~0u; + m_lastRenderFpsLimit = ~0u; + m_lastRenderFpsUpdateMs = 0u; + m_systemTimeString = NULL; m_systemTimeFont = "Tahoma"; m_systemTimePointSize = TheGlobalData->m_systemTimeFontSize; m_systemTimeBold = TRUE; - m_systemTimePosition.x = 3; // TheSuperHackers @info relative to the left of the screen - m_systemTimePosition.y = -1; + m_systemTimePosition.x = kHudAnchorX; // TheSuperHackers @info relative to the left of the screen + m_systemTimePosition.y = kHudAnchorY; m_systemTimeColor = GameMakeColor( 255, 255, 255, 255 ); m_systemTimeDropColor = GameMakeColor( 0, 0, 0, 255 ); @@ -1021,8 +1074,8 @@ InGameUI::InGameUI() m_gameTimeFont = "Tahoma"; m_gameTimePointSize = TheGlobalData->m_gameTimeFontSize; m_gameTimeBold = TRUE; - m_gameTimePosition.x = 3; // TheSuperHackers @info relative to the right of the screen - m_gameTimePosition.y = -1; + m_gameTimePosition.x = kHudAnchorX; // TheSuperHackers @info relative to the right of the screen + m_gameTimePosition.y = kHudAnchorY; m_gameTimeColor = GameMakeColor( 255, 255, 255, 255 ); m_gameTimeDropColor = GameMakeColor( 0, 0, 0, 255 ); @@ -2063,6 +2116,12 @@ void InGameUI::freeMessageResources( void ) void InGameUI::freeCustomUiResources( void ) { + TheDisplayStringManager->freeDisplayString(m_networkLatencyString); + m_networkLatencyString = NULL; + TheDisplayStringManager->freeDisplayString(m_renderFpsString); + m_renderFpsString = NULL; + TheDisplayStringManager->freeDisplayString(m_renderFpsLimitString); + m_renderFpsLimitString = NULL; TheDisplayStringManager->freeDisplayString(m_systemTimeString); m_systemTimeString = NULL; TheDisplayStringManager->freeDisplayString(m_gameTimeString); @@ -3550,9 +3609,22 @@ void InGameUI::disregardDrawable( Drawable *draw ) //------------------------------------------------------------------------------------------------- void InGameUI::postWindowDraw( void ) { + Int hudOffsetX = 0; + Int hudOffsetY = 0; + + if (m_networkLatencyPointSize > 0 && TheGameLogic->isInMultiplayerGame()) + { + drawNetworkLatency(hudOffsetX, hudOffsetY); + } + + if (m_renderFpsPointSize > 0) + { + drawRenderFps(hudOffsetX, hudOffsetY); + } + if (m_systemTimePointSize > 0) { - drawSystemTime(); + drawSystemTime(hudOffsetX, hudOffsetY); } if ( (m_gameTimePointSize > 0) && !TheGameLogic->isInShellGame() && TheGameLogic->isInGame() ) @@ -5786,10 +5858,49 @@ void InGameUI::recreateControlBar( void ) void InGameUI::refreshCustomUiResources(void) { + refreshNetworkLatencyResources(); + refreshRenderFpsResources(); refreshSystemTimeResources(); refreshGameTimeResources(); } +void InGameUI::refreshNetworkLatencyResources(void) +{ + if (!m_networkLatencyString) + { + m_networkLatencyString = TheDisplayStringManager->newDisplayString(); + m_lastNetworkLatencyFrames = ~0u; + } + + m_networkLatencyPointSize = TheGlobalData->m_networkLatencyFontSize; + Int adjustedNetworkLatencyFontSize = TheGlobalLanguageData->adjustFontSize(m_networkLatencyPointSize); + GameFont* latencyFont = TheWindowManager->winFindFont(m_networkLatencyFont, adjustedNetworkLatencyFontSize, m_networkLatencyBold); + m_networkLatencyString->setFont(latencyFont); +} + +void InGameUI::refreshRenderFpsResources(void) +{ + if (!m_renderFpsString) + { + m_renderFpsString = TheDisplayStringManager->newDisplayString(); + m_lastRenderFps = ~0u; + m_lastRenderFpsUpdateMs = 0u; + updateRenderFpsString(); + } + + if (!m_renderFpsLimitString) + { + m_renderFpsLimitString = TheDisplayStringManager->newDisplayString(); + m_lastRenderFpsLimit = ~0u; + } + + m_renderFpsPointSize = TheGlobalData->m_renderFpsFontSize; + Int adjustedRenderFpsFontSize = TheGlobalLanguageData->adjustFontSize(m_renderFpsPointSize); + GameFont *fpsFont = TheWindowManager->winFindFont(m_renderFpsFont, adjustedRenderFpsFontSize, m_renderFpsBold); + m_renderFpsString->setFont(fpsFont); + m_renderFpsLimitString->setFont(fpsFont); +} + void InGameUI::refreshSystemTimeResources(void) { if (!m_systemTimeString) @@ -5876,17 +5987,114 @@ WindowMsgHandledType IdleWorkerSystem( GameWindow *window, UnsignedInt msg, } -void InGameUI::drawSystemTime() + +void InGameUI::updateRenderFpsString() +{ + const UnsignedInt renderFps = (UnsignedInt)(TheDisplay->getAverageFPS() + 0.5f); + if (renderFps != m_lastRenderFps) + { + UnicodeString fpsStr; + fpsStr.format(L"%u", renderFps); + m_renderFpsString->setText(fpsStr); + m_lastRenderFps = renderFps; + } +} + +void InGameUI::drawNetworkLatency(Int &x, Int &y) +{ + const UnsignedInt networkLatencyFrames = TheNetwork->getRunAhead(); + + if (networkLatencyFrames != m_lastNetworkLatencyFrames) + { + UnicodeString latencyStr; + latencyStr.format(L"%u", networkLatencyFrames); + m_networkLatencyString->setText(latencyStr); + m_lastNetworkLatencyFrames = networkLatencyFrames; + } + + // TheSuperHackers @info at the HUD anchor this draws inline and advances x otherwise uses configured position + if (isAtHudAnchorPos(m_networkLatencyPosition)) + { + m_networkLatencyString->draw(kHudAnchorX + x, kHudAnchorY + y, m_networkLatencyColor, m_networkLatencyDropColor); + x += m_networkLatencyString->getWidth() + kHudGapPx; + } + else + { + m_networkLatencyString->draw(m_networkLatencyPosition.x, m_networkLatencyPosition.y, m_networkLatencyColor, m_networkLatencyDropColor); + } +} + +void InGameUI::drawRenderFps(Int &x, Int &y) +{ + if (m_renderFpsRefreshMs > 0u) + { + const UnsignedInt nowMs = timeGetTime(); + const UnsignedInt deltaMs = nowMs - m_lastRenderFpsUpdateMs; + if (deltaMs >= m_renderFpsRefreshMs) + { + m_lastRenderFpsUpdateMs = nowMs; + updateRenderFpsString(); + } + } + else + { + updateRenderFpsString(); + } + + UnsignedInt renderFpsLimit = 0u; + if (TheGlobalData->m_useFpsLimit) + { + renderFpsLimit = (UnsignedInt)TheGameEngine->getFramesPerSecondLimit(); + if (renderFpsLimit == RenderFpsPreset::UncappedFpsValue) + { + renderFpsLimit = 0u; + } + } + if (renderFpsLimit != m_lastRenderFpsLimit) + { + UnicodeString fpsLimitStr; + fpsLimitStr.format(L"[%u]", renderFpsLimit); + m_renderFpsLimitString->setText(fpsLimitStr); + m_lastRenderFpsLimit = renderFpsLimit; + } + + // TheSuperHackers @info at the HUD anchor this draws inline and advances x otherwise uses configured position + if (isAtHudAnchorPos(m_renderFpsPosition)) + { + const Int drawY = kHudAnchorY + y; + + m_renderFpsString->draw(kHudAnchorX + x, drawY, m_renderFpsColor, m_renderFpsDropColor); + x += m_renderFpsString->getWidth(); + m_renderFpsLimitString->draw(kHudAnchorX + x, drawY, m_renderFpsLimitColor, m_renderFpsDropColor); + x += m_renderFpsLimitString->getWidth() + kHudGapPx; + } + else + { + m_renderFpsString->draw(m_renderFpsPosition.x, m_renderFpsPosition.y, m_renderFpsColor, m_renderFpsDropColor); + m_renderFpsLimitString->draw(m_renderFpsPosition.x + m_renderFpsString->getWidth(), m_renderFpsPosition.y, m_renderFpsLimitColor, m_renderFpsDropColor); + } +} + +void InGameUI::drawSystemTime(Int &x, Int &y) { // current system time SYSTEMTIME systemTime; GetLocalTime( &systemTime ); - UnicodeString TimeString; - TimeString.format(L"%2.2d:%2.2d:%2.2d", systemTime.wHour, systemTime.wMinute, systemTime.wSecond); - m_systemTimeString->setText(TimeString); + UnicodeString TimeString; + TimeString.format(L"%2.2d:%2.2d:%2.2d", systemTime.wHour, systemTime.wMinute, systemTime.wSecond); + m_systemTimeString->setText(TimeString); - m_systemTimeString->draw(m_systemTimePosition.x, m_systemTimePosition.y, m_systemTimeColor, m_systemTimeDropColor); + // TheSuperHackers @info at the HUD anchor this draws inline and advances x otherwise uses configured position + if (isAtHudAnchorPos(m_systemTimePosition)) + { + m_systemTimeString->draw(kHudAnchorX + x, kHudAnchorY + y, m_systemTimeColor, m_systemTimeDropColor); + x += m_systemTimeString->getWidth() + kHudGapPx; + } + else + { + m_systemTimeString->draw(m_systemTimePosition.x, m_systemTimePosition.y, m_systemTimeColor, m_systemTimeDropColor); + } } void InGameUI::drawGameTime()