From 0caf5c596b11cc47b9326b8eb53f9449be4a5b07 Mon Sep 17 00:00:00 2001 From: Architector #4 Date: Wed, 3 Dec 2025 14:15:21 +0300 Subject: [PATCH 1/6] Fix undefined behavior with type of WindowMan::HandleWindowExposedEvent Undefined sanitizer's complaint: ../external/sources/SDL3-3.2.10/src/events/SDL_eventwatch.c:72:17: runtime error: call to function RTE::WindowMan::HandleWindowExposedEvent(void*, SDL_Event*) through pointer to incorrect function type 'bool (*)(void *, union SDL_Event *)' /media/ext_hdd/nobackup/architector4/Downloads/Cortex-Command-Community-Project/builddebug/../Source/Managers/WindowMan.cpp:678: note: RTE::WindowMan::HandleWindowExposedEvent(void*, SDL_Event*) defined here SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../external/sources/SDL3-3.2.10/src/events/SDL_eventwatch.c:72:17 --- Source/Managers/WindowMan.cpp | 4 +++- Source/Managers/WindowMan.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Managers/WindowMan.cpp b/Source/Managers/WindowMan.cpp index bfd3a2da11..2b80a62be6 100644 --- a/Source/Managers/WindowMan.cpp +++ b/Source/Managers/WindowMan.cpp @@ -675,12 +675,14 @@ void WindowMan::DisplaySwitchOut() const { SDL_SetCursor(nullptr); } -void WindowMan::HandleWindowExposedEvent(void *userdata, SDL_Event *event) { +bool WindowMan::HandleWindowExposedEvent(void *userdata, SDL_Event *event) { if (event->type == SDL_EVENT_WINDOW_EXPOSED) { g_WindowMan.SetViewportLetterboxed(); g_WindowMan.ClearBackbuffer(false); g_WindowMan.UploadFrame(); } + + return true; } void WindowMan::QueueWindowEvent(const SDL_Event& windowEvent) { diff --git a/Source/Managers/WindowMan.h b/Source/Managers/WindowMan.h index 3295c1ef44..53452dbed2 100644 --- a/Source/Managers/WindowMan.h +++ b/Source/Managers/WindowMan.h @@ -168,8 +168,8 @@ namespace RTE { #pragma endregion #pragma region Concrete Methods - /// SDL_EventFilter to hadnle window exposed events for live resize. - static void HandleWindowExposedEvent(void* userdata, SDL_Event* event); + /// SDL_EventFilter to handle window exposed events for live resize. + static bool HandleWindowExposedEvent(void* userdata, SDL_Event* event); /// Adds an SDL_Event to the Event queue for processing on Update. /// @param windowEvent The SDL window event to queue. From 8deb20f144eda382a74c0f0eab290f20827a2902 Mon Sep 17 00:00:00 2001 From: Architector #4 Date: Wed, 3 Dec 2025 14:49:51 +0300 Subject: [PATCH 2/6] Explicitly initialize stuff in ScenarioActivityConfigGUI Undefined sanitizer's complaint: ../Source/Menus/ScenarioActivityConfigGUI.cpp:101:59: runtime error: load of value 190, which is not a valid value for type 'bool' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../Source/Menus/ScenarioActivityConfigGUI.cpp:101:59 ...While working on this one, I discovered that C++ "default-initializes" class members, which leaves primitives and pointers with undefined garbage values, but properly initializes classes. And also the [Most vexing parse](https://en.wikipedia.org/wiki/Most_vexing_parse). Wow. --- Source/Menus/ScenarioActivityConfigGUI.cpp | 2 -- Source/Menus/ScenarioActivityConfigGUI.h | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Source/Menus/ScenarioActivityConfigGUI.cpp b/Source/Menus/ScenarioActivityConfigGUI.cpp index 916e999746..6d675ce237 100644 --- a/Source/Menus/ScenarioActivityConfigGUI.cpp +++ b/Source/Menus/ScenarioActivityConfigGUI.cpp @@ -65,8 +65,6 @@ ScenarioActivityConfigGUI::ScenarioActivityConfigGUI(GUIControlManager* parentCo m_CPULockLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelCPUTeamLock")); m_StartErrorLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelStartError")); m_StartGameButton = dynamic_cast(m_GUIControlManager->GetControl("ButtonStartGame")); - - m_TechListFetched = false; } void ScenarioActivityConfigGUI::PopulateTechComboBoxes() { diff --git a/Source/Menus/ScenarioActivityConfigGUI.h b/Source/Menus/ScenarioActivityConfigGUI.h index ae9147221e..f6b881b23f 100644 --- a/Source/Menus/ScenarioActivityConfigGUI.h +++ b/Source/Menus/ScenarioActivityConfigGUI.h @@ -65,16 +65,16 @@ namespace RTE { GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. - const GameActivity* m_SelectedActivity; //!< The Activity this ScenarioActivityConfigGUI is configuring. - const GameActivity* m_PreviouslySelectedActivity; //!< The Activity this ScenarioActivityConfigGUI was configuring last, before it got was disabled. - Scene* m_SelectedScene; //!< The Scene the selected Activity will be using. + const GameActivity* m_SelectedActivity = nullptr; //!< The Activity this ScenarioActivityConfigGUI is configuring. + const GameActivity* m_PreviouslySelectedActivity = nullptr; //!< The Activity this ScenarioActivityConfigGUI was configuring last, before it got was disabled. + Scene* m_SelectedScene = nullptr; //!< The Scene the selected Activity will be using. int m_LockedCPUTeam = Activity::Teams::NoTeam; //!< Which team the CPU is locked to, if any. - bool m_StartingGoldAdjustedManually {}; //!< Whether the player adjusted the starting gold, meaning it should stop automatically adjusting to the difficulty setting default starting gold where applicable. + bool m_StartingGoldAdjustedManually = false; //!< Whether the player adjusted the starting gold, meaning it should stop automatically adjusting to the difficulty setting default starting gold where applicable. Timer m_StartGameButtonBlinkTimer; //!< Timer for blinking the start game button. - bool m_TechListFetched; //!< Whether the tech list was fetched and each team's ComboBox was populated with it, even if no valid tech modules were added. + bool m_TechListFetched = false; //!< Whether the tech list was fetched and each team's ComboBox was populated with it, even if no valid tech modules were added. /// GUI elements that compose the Activity setup box. GUICollectionBox* m_ActivityConfigBox; From 7b87c727f07ef972d7ec8bcb773ed7d2da39ea60 Mon Sep 17 00:00:00 2001 From: Architector #4 Date: Wed, 3 Dec 2025 16:28:44 +0300 Subject: [PATCH 3/6] Don't use FLT_MAX for Actor::EstimateJumpHeight Undefined sanitizer's complaint: ../Source/System/PathFinder.cpp:176:54: runtime error: 3.40282e+38 is outside the range of representable values of type 'int' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../Source/System/PathFinder.cpp:176:54 ...Also change a comment slightly lol --- Source/Entities/Actor.cpp | 3 ++- Source/System/PathFinder.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Entities/Actor.cpp b/Source/Entities/Actor.cpp index 1ea044ee05..6d7b26bf5a 100644 --- a/Source/Entities/Actor.cpp +++ b/Source/Entities/Actor.cpp @@ -1047,7 +1047,8 @@ float Actor::EstimateDigStrength() const { } float Actor::EstimateJumpHeight() const { - return FLT_MAX; + // By default, assume actors can jump as far as the scene stretches upward, twice over. + return g_SceneMan.GetSceneHeight() * c_MPP * 2; } void Actor::VerifyMOIDs() { diff --git a/Source/System/PathFinder.cpp b/Source/System/PathFinder.cpp index 843895c145..14f853a91e 100644 --- a/Source/System/PathFinder.cpp +++ b/Source/System/PathFinder.cpp @@ -173,7 +173,7 @@ int PathFinder::CalculatePath(Vector start, Vector end, std::list& pathR s_JumpHeight = jumpHeight; // How high up we can jump from this node - s_JumpHeightVertical = std::max(1, static_cast(jumpHeight / (m_NodeDimension * c_MPP))); // min of 1 so automovers work a bit better + s_JumpHeightVertical = std::max(1, static_cast(jumpHeight / (m_NodeDimension * c_MPP))); // at least 1 so automovers work a bit better s_JumpHeightDiagonal = std::max(1, static_cast((jumpHeight * 0.7F) / (m_NodeDimension * c_MPP))); // Actors capable of digging can use s_DigStrength to modify the node adjacency cost. From 6cef73fcb972d4f2c2f353d5c5d0a5546c570877 Mon Sep 17 00:00:00 2001 From: Architector #4 Date: Wed, 3 Dec 2025 17:03:11 +0300 Subject: [PATCH 4/6] ADoor - remove stale old bad code for door spinup The door hinge still spins up and explodes just fine with this patch applied, as code below lerps it downward properly anyway. Undefined sanitizer complaint: ../Source/Entities/ADoor.cpp:470:24: runtime error: signed integer overflow: 671088640 * 4 cannot be represented in type 'int' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../Source/Entities/ADoor.cpp:470:24 --- Source/Entities/ADoor.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Source/Entities/ADoor.cpp b/Source/Entities/ADoor.cpp index 8425c21942..095f601ba5 100644 --- a/Source/Entities/ADoor.cpp +++ b/Source/Entities/ADoor.cpp @@ -465,11 +465,6 @@ void ADoor::Update() { Actor::Update(); - // Start the spinning out of control animation for the motor, start it slow - if (!m_Door) { - m_SpriteAnimDuration *= 4; - } - if (m_SpriteAnimMode == LOOPWHENOPENCLOSE && m_FrameCount > 1 && (m_DoorState == OPENING || m_DoorState == CLOSING) && m_SpriteAnimTimer.IsPastSimMS(m_SpriteAnimDuration)) { m_Frame = (m_Frame + 1) % m_FrameCount; m_SpriteAnimTimer.Reset(); From e904b6023ebf5925c8511085e15904669feed42c Mon Sep 17 00:00:00 2001 From: Architector #4 Date: Sat, 6 Dec 2025 02:58:12 +0300 Subject: [PATCH 5/6] ScenarioActivityConfigGUI - change initializers from "= 0" to "{}" --- Source/Menus/ScenarioActivityConfigGUI.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Menus/ScenarioActivityConfigGUI.h b/Source/Menus/ScenarioActivityConfigGUI.h index f6b881b23f..62b2fea672 100644 --- a/Source/Menus/ScenarioActivityConfigGUI.h +++ b/Source/Menus/ScenarioActivityConfigGUI.h @@ -65,16 +65,16 @@ namespace RTE { GUIControlManager* m_GUIControlManager; //!< The GUIControlManager which holds all the GUIControls of this menu. Not owned by this. - const GameActivity* m_SelectedActivity = nullptr; //!< The Activity this ScenarioActivityConfigGUI is configuring. - const GameActivity* m_PreviouslySelectedActivity = nullptr; //!< The Activity this ScenarioActivityConfigGUI was configuring last, before it got was disabled. - Scene* m_SelectedScene = nullptr; //!< The Scene the selected Activity will be using. - int m_LockedCPUTeam = Activity::Teams::NoTeam; //!< Which team the CPU is locked to, if any. + const GameActivity* m_SelectedActivity {}; //!< The Activity this ScenarioActivityConfigGUI is configuring. + const GameActivity* m_PreviouslySelectedActivity {}; //!< The Activity this ScenarioActivityConfigGUI was configuring last, before it got was disabled. + Scene* m_SelectedScene {}; //!< The Scene the selected Activity will be using. + int m_LockedCPUTeam { Activity::Teams::NoTeam }; //!< Which team the CPU is locked to, if any. - bool m_StartingGoldAdjustedManually = false; //!< Whether the player adjusted the starting gold, meaning it should stop automatically adjusting to the difficulty setting default starting gold where applicable. + bool m_StartingGoldAdjustedManually {}; //!< Whether the player adjusted the starting gold, meaning it should stop automatically adjusting to the difficulty setting default starting gold where applicable. Timer m_StartGameButtonBlinkTimer; //!< Timer for blinking the start game button. - bool m_TechListFetched = false; //!< Whether the tech list was fetched and each team's ComboBox was populated with it, even if no valid tech modules were added. + bool m_TechListFetched {}; //!< Whether the tech list was fetched and each team's ComboBox was populated with it, even if no valid tech modules were added. /// GUI elements that compose the Activity setup box. GUICollectionBox* m_ActivityConfigBox; From 1a85d3899eea046b67ba78068c7779689fc90933 Mon Sep 17 00:00:00 2001 From: Architector #4 Date: Mon, 29 Dec 2025 06:16:19 +0300 Subject: [PATCH 6/6] Use FLT_MAX in Actor::EstimateJumpHeight but don't undefined behavior :v --- Source/Entities/Actor.cpp | 4 ++-- Source/Entities/Actor.h | 2 +- Source/System/PathFinder.cpp | 13 ++++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Source/Entities/Actor.cpp b/Source/Entities/Actor.cpp index 6d7b26bf5a..7ea36f424b 100644 --- a/Source/Entities/Actor.cpp +++ b/Source/Entities/Actor.cpp @@ -1047,8 +1047,8 @@ float Actor::EstimateDigStrength() const { } float Actor::EstimateJumpHeight() const { - // By default, assume actors can jump as far as the scene stretches upward, twice over. - return g_SceneMan.GetSceneHeight() * c_MPP * 2; + // Sentinel value that is explicitly checked for within pathfinder code. + return FLT_MAX; } void Actor::VerifyMOIDs() { diff --git a/Source/Entities/Actor.h b/Source/Entities/Actor.h index dfaa802979..00be2213e7 100644 --- a/Source/Entities/Actor.h +++ b/Source/Entities/Actor.h @@ -668,7 +668,7 @@ namespace RTE { /// @return The actor's dig strength. virtual float EstimateDigStrength() const; - /// Estimates how high this actor can jump. + /// Estimates how high this actor can jump. Default implementation returns FLT_MAX. /// @return The actor's jump height. virtual float EstimateJumpHeight() const; diff --git a/Source/System/PathFinder.cpp b/Source/System/PathFinder.cpp index 14f853a91e..c62653b9a1 100644 --- a/Source/System/PathFinder.cpp +++ b/Source/System/PathFinder.cpp @@ -172,9 +172,16 @@ int PathFinder::CalculatePath(Vector start, Vector end, std::list& pathR // Actors capable of jumping/jetpacking can jump upwards. s_JumpHeight = jumpHeight; - // How high up we can jump from this node - s_JumpHeightVertical = std::max(1, static_cast(jumpHeight / (m_NodeDimension * c_MPP))); // at least 1 so automovers work a bit better - s_JumpHeightDiagonal = std::max(1, static_cast((jumpHeight * 0.7F) / (m_NodeDimension * c_MPP))); + // How high up we can jump from this node. + if(jumpHeight == FLT_MAX) { + // Probably quite high. + s_JumpHeightVertical = INT_MAX; + s_JumpHeightDiagonal = INT_MAX; + } else { + // Assume at least 1 so automovers work a bit better + s_JumpHeightVertical = std::max(1, static_cast(jumpHeight / (m_NodeDimension * c_MPP))); + s_JumpHeightDiagonal = std::max(1, static_cast((jumpHeight * 0.7F) / (m_NodeDimension * c_MPP))); + } // Actors capable of digging can use s_DigStrength to modify the node adjacency cost. s_DigStrength = digStrength;