Skip to content

bugfix(lan): Fix crash when changing settings in the LAN lobby#2676

Open
Caball009 wants to merge 4 commits into
TheSuperHackers:mainfrom
Caball009:fix_crash_lan_options_menu
Open

bugfix(lan): Fix crash when changing settings in the LAN lobby#2676
Caball009 wants to merge 4 commits into
TheSuperHackers:mainfrom
Caball009:fix_crash_lan_options_menu

Conversation

@Caball009
Copy link
Copy Markdown

@Caball009 Caball009 commented May 2, 2026

It's currently possible to crash the game with a well-timed option change in the LAN lobby / options menu. The game calls DeinitLanGameGadgets, which sets a few button pointers to nullptr, when the countdown is somewhere between 1 and game start. If the options are changed after this (but before the game the actually starts), the game crashes because it attempts to dereference the pointers.

This PR adds a few checks in LanGameOptionsMenuSystem to ignore such changes. This PR disables the LAN buttons early, before they're deinitialized, to prevent the host from using them at the last moment.

Callstack for DeinitLanGameGadgets:

generalszh.exe!DeinitLanGameGadgets()
generalszh.exe!shutdownComplete(WindowLayout * layout)
generalszh.exe!LanGameOptionsMenuShutdown(WindowLayout * layout, void * userData)
generalszh.exe!WindowLayout::runShutdown(void * userData)
generalszh.exe!Shell::hideShell()
generalszh.exe!GameLogic::prepareNewGame(GameMode gameMode, GameDifficulty diff, int rankPoints)
generalszh.exe!GameLogic::logicMessageDispatcher(GameMessage * msg, void * userData)
generalszh.exe!GameLogic::processCommandList(CommandList * list)
generalszh.exe!GameLogic::update()
generalszh.exe!SubsystemInterface::UPDATE()
generalszh.exe!GameEngine::update()
generalszh.exe!Win32GameEngine::update()
generalszh.exe!GameEngine::execute()
generalszh.exe!GameMain()
generalszh.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow)

Callstack for crash (in this case changing the super weapon limit):

generalszh.exe!GadgetCheckBoxIsChecked(GameWindow * g)
generalszh.exe!handleLimitSuperweaponsClick()
generalszh.exe!LanGameOptionsMenuSystem(GameWindow * window, unsigned int msg, unsigned int mData1, unsigned int mData2)
generalszh.exe!GameWindowManager::winSendSystemMsg(GameWindow * window, unsigned int msg, unsigned int mData1, unsigned int mData2)
generalszh.exe!PassMessagesToParentSystem(GameWindow * window, unsigned int msg, unsigned int mData1, unsigned int mData2)
generalszh.exe!GameWindowManager::winSendSystemMsg(GameWindow * window, unsigned int msg, unsigned int mData1, unsigned int mData2)
generalszh.exe!GadgetCheckBoxInput(GameWindow * window, unsigned int msg, unsigned int mData1, unsigned int mData2)
generalszh.exe!GameWindowManager::winSendInputMsg(GameWindow * window, unsigned int msg, unsigned int mData1, unsigned int mData2)
generalszh.exe!GameWindowManager::winProcessMouseEvent(GameWindowMessage msg, ICoord2D * mousePos, void * data)
generalszh.exe!WindowTranslator::translateGameMessage(const GameMessage * msg)
generalszh.exe!MessageStream::propagateMessages()
generalszh.exe!GameEngine::update()
generalszh.exe!Win32GameEngine::update()
generalszh.exe!GameEngine::execute()
generalszh.exe!GameMain()
generalszh.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow)

TODO:

  • Replicate in Generals.

@Caball009 Caball009 added Bug Something is not working right, typically is user facing Minor Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour Crash This is a crash, very bad labels May 2, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 2, 2026

Greptile Summary

This PR fixes a race-condition crash in the LAN lobby by disabling all interactive GUI elements exactly one second before the game starts, preventing any click messages from being enqueued after DeinitLanGameGadgets nulls the button pointers.

  • LanGameOptionsMenu.cpp — adds LANDisableButtons(), which disables the start, back, map-select, superweapon checkbox, starting-cash combo, and all per-slot controls (player, color, template, team, accept, start-position).
  • LANAPI.cpp — calls LANDisableButtons() inside the existing countdown loop when m_gameStartSeconds == 1, one tick before ResetGameStartTimer() / RequestGameStart() chain runs and nulls the pointers.
  • LANGameInfo.h — exposes the new LANDisableButtons() declaration alongside the existing LANEnableStartButton prototype.

Confidence Score: 5/5

Safe to merge — the fix is minimal, targets a confirmed crash path, and the timer-loop analysis confirms the disable call cannot fire after the pointers are nulled.

The change is tightly scoped: one new function, one new call site, one header declaration. The Generals build is unaffected because its LAN source files are commented out in CMake.

No files require special attention.

Important Files Changed

Filename Overview
Core/GameEngine/Include/GameNetwork/LANGameInfo.h Adds LANDisableButtons() declaration alongside the pre-existing LANEnableStartButton prototype; minimal, consistent change.
Core/GameEngine/Source/GameNetwork/LANAPI.cpp Calls LANDisableButtons() when m_gameStartSeconds == 1, one update tick before the game-start chain that nulls the GUI pointers; timing analysis confirms no null-deref risk.
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp Implements LANDisableButtons(), disabling all lobby controls that could trigger a crash if clicked after DeinitLanGameGadgets runs; follows the same pattern as LANEnableStartButton.

Reviews (4): Last reviewed commit: "Tweaked comment." | Re-trigger Greptile

break;

LANGameInfo* myGame = TheLAN->GetMyGame();
if (myGame->isGameInProgress())
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is very suspicious that the menu can still be interacted with after it was shutdown. Why is this possible? Is there maybe a general issue with menus?

Copy link
Copy Markdown
Author

@Caball009 Caball009 May 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is because of a general issues with menus. The game just nulls the button pointers before it changes the window / takes away control from the user.

Perhaps from user perspective the cleanest solution would be to disable (grey out) the buttons when the timer has zero seconds remaining. I don't know how to access the buttons in the code that has access to the timer, though.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good if the window is changed or control is taken before the button pointers are nulled. That way no special safe guards need to be placed in multiple handlers.

Is the issue maybe that the handling of a button press is delayed by 1 or multiple frames because of input system updates, and so it can arrive after the menu screen was destroyed? If so, maybe the solution here is to add a condition into every menu input handler and check that the owning menu is still active, and if not, return early and do not handle the input.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the implementation to disable the buttons when the countdown is at one second remaining.

@Caball009 Caball009 force-pushed the fix_crash_lan_options_menu branch from b23c39e to beea96f Compare June 2, 2026 14:50
@Caball009 Caball009 force-pushed the fix_crash_lan_options_menu branch from 0951279 to c6beb06 Compare June 3, 2026 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something is not working right, typically is user facing Crash This is a crash, very bad Gen Relates to Generals Minor Severity: Minor < Major < Critical < Blocker ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants