Loading-screen panel for every player: live load states with stuck detection, talking bars, local voice volume; kick/ban and stuck-load lobby rescue for moderators. Works standalone; full SharePermissions integration when installed
SharePermissions is now optional. The mod detects it at startup: with SharePermissions
v1.17.0+ installed, everything works exactly as before through its LoadingWidgetBridge
(moderators get the panel's actions, its rules/wire protocol drive kick/ban, role tiers, its
config toggle). Without it, the panel works standalone on the game's own machinery: host-only
moderation with native kick/ban (bans enforced by the room's exclusion list), the Lobby button
with the same QUEUED/FORCE stuck-load rescue, talking bars, and the per-player local voice
volume slider.
Standalone panel toggle: UI / ShowLoadingPanel in BepInEx/config/RED.LoadingWidget.cfg
(bound only when SharePermissions is absent; its own toggle governs otherwise).
With an older SharePermissions (≤ 1.16.x, which ships this panel itself) the mod stays dormant
with a clean log line instead of drawing a second panel.
The Thunderstore package no longer hard-depends on SharePermissions.
The panel is visible to EVERY client running the mod, not only the host and moderators - the
load display is informational, so anyone stuck in a load can see who is holding it up. It is
strictly scoped to loading between levels in the game itself: it requires an active Photon room
and never appears on the boot/splash/menu loads or in singleplayer (in-room, the local player is
always listed, so it can never render empty). Moderation actions stay gated: Kick/Ban per-target
and the Lobby/FORCE bail only for the host (and SharePermissions moderators when bridged).
Load-state truth (a decompile-verified audit of what each per-player tier really means):
BUILDING now reads the real per-client build barriers (LevelGenerator.SpawnedReadyPlayerList
and, on enemy levels, EnemyReadyPlayerList - each sent by a client only after real local
work) instead of ModulesReadyPlayerList, which a non-master reports on a ~0.3s timer
regardless of progress. A client genuinely stuck building the level used to show LOADED and
count toward "N/N loaded"; it now stays BUILDING and drops the header count, for every viewer.
The final loading-animation barrier is recognized as a real all-players gate (it blocks every
client's Start->Main), not a cosmetic flourish.
STUCK detection: the panel surfaces the game's own ~25s stuck verdict and adds its own
long-window backstop for the phases that verdict never covers (the Start animation barrier,
the unbounded Reload gap). While stuck, the header flags STUCK and the rows the room is
actually waiting on burn in the blocker accent - the vanilla stuck prompt only offers leaving
the room; the panel points at the room-intact rescue instead.
The panel no longer vanishes during lobby-bound transitions (arena/game-over and its own
LOBBY/FORCE rescue): the window gate keyed on the transition DESTINATION, hiding the rescue UI
the moment it launched. It now rides the whole transition, and the game-over return opens the
panel properly through its outro.
The FORCE rescue is native to this mod in both modes (StandaloneLobby): it acts on the
game's transition pipeline, not on backend state. In bridged mode the panel no longer calls
SharePermissions' ForceLobbyReload; SharePermissions still owns the queued return that gates
FORCE and finishes the trip after the reboot.
Hardening against game updates and backend failures: a failing patch no longer kills the mod at
startup (per-patch guards, degrade to a log line); SemiFunc checks joined the JIT-isolated raw
helpers; the outro phase check resolves gameState values by name, so a reordered enum can no
longer silently mislabel players; and an exception from the backend now hides the panel instead
of freezing it or leaving it stuck over gameplay.
Fixed: the plugin no longer stamps HideAndDontSave on the shared BepInEx manager object (which
hid every mod's plugin component from debug tools); an expanded row no longer survives a mid-load
toggle-off / moderation revoke to reopen by itself on the next load; the volume slider now arms
on the thumb's full height instead of only the thin track; the loading-screen check reads
activeInHierarchy so a parent-level deactivation can't leave the panel stuck visible.
Performance: the per-frame redraw is allocation-free in the steady state (header, name, and
percent strings rebuilt only on change; the Photon player list fetched once per frame),
per-player load states are computed once per frame instead of twice, and unbounded per-frame
retries (FindObjectOfType<ReloadScene> in the scene-swap gap, the font resolve walk) are
throttled to timers.
New config Debug / StateDiagnostics (default off): the [LoadPanel diag] load-state dump no
longer spams the log every 2 seconds - enable it only when diagnosing wrong
LOADING/BUILDING/LOADED/READY states.
Dev/packaging: pinned the floating NuGet versions (build SDK 0.2.2, gamelibs 0.4.4-ngd.0) so a
game update can't silently shift the compile-time surface; per-machine paths now go in
Directory.Repo.props.user instead of editing the committed file; filled in the LICENSE
copyright line.
1.0.0
Initial release: the loading-screen moderation panel, extracted from SharePermissions (it shipped
inside SharePermissions through v1.16.0; from v1.17.0 it lives here). Requires SharePermissions
v1.17.0+ and talks to it through SharePermissions.Api.LoadingWidgetBridge.
Same panel, unchanged: per-player load states (LOADING / BUILDING / LOADED / READY across normal
loads, the outro, and the Reload barrier), talking bars, per-player local voice volume (0–200%,
this machine only), Kick/Ban with SharePermissions' rules, and the Lobby button with the host-side
QUEUED/FORCE stuck-load rescue.
The panel's on/off toggle stays in SharePermissions (Settings tab "Loading panel" /
UI / ShowLoadingPanel), read live through the bridge.
Thunderstore development is made possible with ads. Please consider making an exception to your adblock.