From 7565a9970383c12689a04820f846337bf3efa5c2 Mon Sep 17 00:00:00 2001 From: "sentry[bot]" <39604003+sentry[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2026 15:46:08 +0000 Subject: [PATCH] Fix null stat_panel read in subsystem fire (#23066) * Please describe the intent of your changes in a clear fashion. This PR addresses the `Cannot read null.stat_panel` error occurring in `SSstatpanels/fire()`. **Root Cause:** The primary cause was a race condition during client login. A `client` object was added to `GLOB.clients` in `client/Login()` before its `stat_panel` member had been fully initialized. This allowed the `SSstatpanels/fire()` subsystem, which iterates over `GLOB.clients`, to attempt to access `target.stat_panel.is_ready()` on a `null` `stat_panel`, leading to a runtime error. Recent changes increasing the frequency of `stat_panel` updates exacerbated this issue. **Solution:** 1. **Reordered Client Initialization:** In `code/modules/client/client_procs.dm`, the line `GLOB.clients += src` has been moved to occur *after* `stat_panel = new(src, "statbrowser")` and `stat_panel.subscribe(...)`. This ensures that a client is only added to the global list once its `stat_panel` is properly instantiated, eliminating the race condition. 2. **Defensive Null Checks:** Additional null checks for `stat_panel` have been added in `code/controllers/subsystems/statpanel.dm`: * In `fire()`, the condition `!target.stat_panel.is_ready()` was updated to `!target.stat_panel || !target.stat_panel.is_ready()`. * In `refresh_client_obj_view()`, an early return `if(!refresh.stat_panel) return` was added. These defensive checks provide robustness against any future reordering issues or unexpected scenarios where `stat_panel` might be null. * Please make sure that, in the case of mapping changes, you include images of these changes in the PR's description. * Please make sure to mark your PR as wip or review required by making a comment with !wip or !review required * If you include sprites/sounds/... (assets) that you have not created yourself specify the license and original author below. * Ensure that you also credit them in the appropriate location / changelog as specified in the contributor guidelines ### Asset Licenses The following assets that **have not** been created by myself are included in this PR: | Path | Original Author | License | | --- | --- | --- | | icons/example.dmi | ExamplePerson (Example Station) | CC0 | Fixes [SERVER-PROD-1M4](https://aurorastation.sentry.io/issues/7547575486/?seerDrawer=true) --------- Co-authored-by: VMSolidus --- code/controllers/subsystems/statpanel.dm | 4 +++- code/modules/client/client_procs.dm | 2 +- html/changelogs/hellfirejag-statpanel-runtime-fix.yml | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 html/changelogs/hellfirejag-statpanel-runtime-fix.yml diff --git a/code/controllers/subsystems/statpanel.dm b/code/controllers/subsystems/statpanel.dm index 4365927b632..c8241d9b5d2 100644 --- a/code/controllers/subsystems/statpanel.dm +++ b/code/controllers/subsystems/statpanel.dm @@ -59,7 +59,7 @@ SUBSYSTEM_DEF(statpanels) var/client/target = currentrun[length(currentrun)] currentrun.len-- - if(!target.stat_panel.is_ready()) + if(!target?.stat_panel?.is_ready()) continue if(target.stat_tab == "Status" && num_fires % status_wait == 0) @@ -217,6 +217,8 @@ SUBSYSTEM_DEF(statpanels) refresh_client_obj_view(target) /datum/controller/subsystem/statpanels/proc/refresh_client_obj_view(client/refresh) + if(!refresh.stat_panel) + return var/list/turf_items = return_object_images(refresh) if(!length(turf_items) || !refresh.mob?.listed_turf) return diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 04e976dacbd..fb072c886ed 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -364,7 +364,6 @@ GLOBAL_LIST_INIT(localhost_addresses, list( dir = NORTH - GLOB.clients += src GLOB.directory[ckey] = src connection_time = world.time connection_realtime = world.realtime @@ -382,6 +381,7 @@ GLOBAL_LIST_INIT(localhost_addresses, list( // Instantiate stat panel stat_panel = new(src, "statbrowser") stat_panel.subscribe(src, PROC_REF(on_stat_panel_message)) + GLOB.clients += src // Instantiate tgui panel tgui_panel = new(src, "browseroutput") tgui_say = new(src, "tgui_say") diff --git a/html/changelogs/hellfirejag-statpanel-runtime-fix.yml b/html/changelogs/hellfirejag-statpanel-runtime-fix.yml new file mode 100644 index 00000000000..4d89ec62cdf --- /dev/null +++ b/html/changelogs/hellfirejag-statpanel-runtime-fix.yml @@ -0,0 +1,4 @@ +author: Hellfirejag +delete-after: True +changes: + - bugfix: "Fixed a runtime error caused by a player attempting to view the statpanel before its ready."