From eb4363ed900d589b0ab26a778dddda0f74a3cfe9 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Mon, 20 Oct 2025 22:20:16 +0200 Subject: [PATCH 1/3] bugfix(client): Fix crash in WorldBuilder scene --- Core/GameEngine/Include/Common/GameUtility.h | 3 ++ Core/GameEngine/Source/Common/GameUtility.cpp | 32 +++++++++++++++++++ .../W3DDevice/GameClient/W3DPropBuffer.cpp | 2 +- .../Source/W3DDevice/GameClient/W3DScene.cpp | 10 +++--- .../W3DDevice/GameClient/W3DTreeBuffer.cpp | 2 +- .../Source/W3DDevice/GameClient/W3DView.cpp | 2 +- 6 files changed, 43 insertions(+), 8 deletions(-) diff --git a/Core/GameEngine/Include/Common/GameUtility.h b/Core/GameEngine/Include/Common/GameUtility.h index 63f1cbf417d..12995a38ce4 100644 --- a/Core/GameEngine/Include/Common/GameUtility.h +++ b/Core/GameEngine/Include/Common/GameUtility.h @@ -21,12 +21,15 @@ // For miscellaneous game utility functions. class Player; +typedef Int PlayerIndex; namespace rts { Bool localPlayerIsObserving(); Player* getObservedOrLocalPlayer(); ///< Get the current observed or local player. Is never null. +Player* getObservedOrLocalPlayer_Safe(); ///< Get the current observed or local player. Is never null, except when the application does not have players. +PlayerIndex getObservedOrLocalPlayerIndex_Safe(); ///< Get the current observed or local player index. Returns 0 when the application does not have players. void changeLocalPlayer(Player* player); //< Change local player during game void changeObservedPlayer(Player* player); ///< Change observed player during game diff --git a/Core/GameEngine/Source/Common/GameUtility.cpp b/Core/GameEngine/Source/Common/GameUtility.cpp index de4fff17e12..f85a6fdf382 100644 --- a/Core/GameEngine/Source/Common/GameUtility.cpp +++ b/Core/GameEngine/Source/Common/GameUtility.cpp @@ -62,12 +62,44 @@ Bool localPlayerIsObserving() Player* getObservedOrLocalPlayer() { + DEBUG_ASSERTCRASH(TheControlBar != NULL, ("TheControlBar is NULL")); Player* player = TheControlBar->getObservedPlayer(); if (player == NULL) + { + DEBUG_ASSERTCRASH(ThePlayerList != NULL, ("ThePlayerList is NULL")); player = ThePlayerList->getLocalPlayer(); + } + return player; +} + +Player* getObservedOrLocalPlayer_Safe() +{ + Player* player = NULL; + + if (TheControlBar != NULL) + { + player = TheControlBar->getObservedPlayer(); + } + + if (player == NULL) + { + if (ThePlayerList != NULL) + { + player = ThePlayerList->getLocalPlayer(); + } + } return player; } +PlayerIndex getObservedOrLocalPlayerIndex_Safe() +{ + if (Player* player = getObservedOrLocalPlayer_Safe()) + { + return player->getPlayerIndex(); + } + return 0; +} + void changeLocalPlayer(Player* player) { ThePlayerList->setLocalPlayer(player); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DPropBuffer.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DPropBuffer.cpp index 150486810b4..3124a16206e 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DPropBuffer.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DPropBuffer.cpp @@ -365,7 +365,7 @@ void W3DPropBuffer::drawProps(RenderInfoClass &rinfo) m_props[i].ss = OBJECTSHROUD_CLEAR; } if (m_props[i].ss == OBJECTSHROUD_INVALID) { - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); m_props[i].ss = ThePartitionManager->getPropShroudStatusForPlayer(localPlayerIndex, &m_props[i].location); } if (m_props[i].ss >= OBJECTSHROUD_SHROUDED) { diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp index 63755c1b16f..c0733c4061a 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp @@ -537,7 +537,7 @@ void RTS3DScene::renderSpecificDrawables(RenderInfoClass &rinfo, Int numDrawable #ifdef DIRTY_CONDITION_FLAGS StDrawableDirtyStuffLocker lockDirtyStuff; #endif - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); RefRenderObjListIterator it(&UpdateList); // loop through all render objects in the list: for (it.First(&RenderList); !it.Is_Done();) @@ -1089,7 +1089,7 @@ void RTS3DScene::Customized_Render( RenderInfoClass &rinfo ) m_translucentObjectsCount = 0; //start of new frame so no translucent objects m_occludedObjectsCount = 0; - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); #define USE_LIGHT_ENV 1 @@ -1339,7 +1339,7 @@ void RTS3DScene::flushOccludedObjectsIntoStencil(RenderInfoClass & rinfo) //Assume no player colors are visible and all stencil bits are free for use by shadows. TheW3DShadowManager->setStencilShadowMask(0); - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); if (m_numPotentialOccludees && m_numPotentialOccluders) { @@ -1533,7 +1533,7 @@ void RTS3DScene::flushOccludedObjects(RenderInfoClass & rinfo) if (m_occludedObjectsCount) { - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); if (DX8Wrapper::Has_Stencil()) //just in case we have shadows, disable them over occluded pixels. { @@ -1603,7 +1603,7 @@ void RTS3DScene::flushTranslucentObjects(RenderInfoClass & rinfo) if (m_translucentObjectsCount) { - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); for (Int i=0; itreeType].m_data; - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); Coord3D pos; pos.set(tree->location.X, tree->location.Y, tree->location.Z); ObjectShroudStatus ss = ThePartitionManager->getPropShroudStatusForPlayer(localPlayerIndex, &pos); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp index 096e64d0d51..78302052c7d 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp @@ -980,7 +980,7 @@ static void drawablePostDraw( Drawable *draw, void *userData ) return; Object* obj = draw->getObject(); - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); #if ENABLE_CONFIGURABLE_SHROUD ObjectShroudStatus ss = (!obj || !TheGlobalData->m_shroudOn) ? OBJECTSHROUD_CLEAR : obj->getShroudedStatus(localPlayerIndex); #else From 1f58c3703e51824209423f0ca569f020133d7ca4 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Tue, 21 Oct 2025 22:08:02 +0200 Subject: [PATCH 2/3] Format --- Core/GameEngine/Source/Common/GameUtility.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Core/GameEngine/Source/Common/GameUtility.cpp b/Core/GameEngine/Source/Common/GameUtility.cpp index f85a6fdf382..2cf6c0a2ddc 100644 --- a/Core/GameEngine/Source/Common/GameUtility.cpp +++ b/Core/GameEngine/Source/Common/GameUtility.cpp @@ -77,26 +77,20 @@ Player* getObservedOrLocalPlayer_Safe() Player* player = NULL; if (TheControlBar != NULL) - { player = TheControlBar->getObservedPlayer(); - } if (player == NULL) - { if (ThePlayerList != NULL) - { player = ThePlayerList->getLocalPlayer(); - } - } + return player; } PlayerIndex getObservedOrLocalPlayerIndex_Safe() { if (Player* player = getObservedOrLocalPlayer_Safe()) - { return player->getPlayerIndex(); - } + return 0; } From 15340f23547e46c33ba9b23d0dcb615f4dcada86 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:28:36 +0200 Subject: [PATCH 3/3] Replicate in Generals --- .../Source/W3DDevice/GameClient/W3DScene.cpp | 10 +++++----- .../Source/W3DDevice/GameClient/W3DView.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp index 9e8a032fe07..f0bf636f650 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp @@ -513,7 +513,7 @@ void RTS3DScene::renderSpecificDrawables(RenderInfoClass &rinfo, Int numDrawable #ifdef DIRTY_CONDITION_FLAGS StDrawableDirtyStuffLocker lockDirtyStuff; #endif - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); RefRenderObjListIterator it(&UpdateList); // loop through all render objects in the list: for (it.First(&RenderList); !it.Is_Done();) @@ -1041,7 +1041,7 @@ void RTS3DScene::Customized_Render( RenderInfoClass &rinfo ) m_translucentObjectsCount = 0; //start of new frame so no translucent objects m_occludedObjectsCount = 0; - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); #define USE_LIGHT_ENV 1 @@ -1292,7 +1292,7 @@ void RTS3DScene::flushOccludedObjectsIntoStencil(RenderInfoClass & rinfo) //Assume no player colors are visible and all stencil bits are free for use by shadows. TheW3DShadowManager->setStencilShadowMask(0); - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); if (m_numPotentialOccludees && m_numPotentialOccluders) { @@ -1470,7 +1470,7 @@ void RTS3DScene::flushOccludedObjects(RenderInfoClass & rinfo) if (m_occludedObjectsCount) { - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); if (DX8Wrapper::Has_Stencil()) //just in case we have shadows, disable them over occluded pixels. { @@ -1540,7 +1540,7 @@ void RTS3DScene::flushTranslucentObjects(RenderInfoClass & rinfo) if (m_translucentObjectsCount) { - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); for (Int i=0; igetObject(); - const Int localPlayerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex(); + const Int localPlayerIndex = rts::getObservedOrLocalPlayerIndex_Safe(); #if ENABLE_CONFIGURABLE_SHROUD ObjectShroudStatus ss = (!obj || !TheGlobalData->m_shroudOn) ? OBJECTSHROUD_CLEAR : obj->getShroudedStatus(localPlayerIndex); #else