When enabled (with the parent feature on), sorters are also hidden,
leaving only belts and their cargo visible. Hidden sorters also become
click-through so clicks reach the belts beneath them.
- RenderingPatch: gate InserterRenderer.Render and the RaycastLogic
inserter exemption on a new HideSorters flag
- FactoryPatch/UXAssist/FactoryConfigProvider: new
DoNotRenderEntitiesHideSorters config wired to the flag
- UIConfigWindow/I18NKeys: indented sub-checkbox + EN/zh label
- README/CHANGELOG: document the sub-option; bump to 1.5.9
- Rename ImmediateBuildPatch to FactoryBuildPatches
- Rename AutoConstructButton to AutoConstructPatch
- AutoConstructButtonEnabled now toggles the Harmony patch instead of only refreshing UI visibility
- Preserve AutoConstructEnabled state when the button option is toggled
ModFeatureRegistry is a static class with shared collections accumulating
features from all mods. Dependent mods (CheatEnabler, UniverseGenTweaks)
each independently called InitAll/StartAll/OnInputUpdateAll/OnUpdateAll/
UninitAll, re-running lifecycle for ALL accumulated features including
other mods' — per-frame Update ran 2-3x (breaking CheatEnabler key toggles
to net no-ops), Init/Start/Uninit ran 2-3x (double RegisterExporter causing
save corruption, double SettingChanged subscriptions, double keybind
registration).
Fix:
- Init now runs eagerly at Discover/Register time (Awake-phase), preserving
the original timing that keybind registration depends on (game's
UIOptionWindow._OnCreate copies keybinds only after all plugins load)
- InitAll removed entirely
- StartAll/UninitAll/OnInputUpdateAll/OnUpdateAll made internal so only
UXAssist (host, same assembly, no InternalsVisibleTo) can drive them
- Start deferred to UXAssist.Start (after all dependents' Awake complete;
BepInEx runs all Awakes before any Start)
- Per-feature Started idempotency + per-frame Time.frameCount guards as
defense-in-depth
- Dependent mods reduced to Discover-only in Awake
Document the Init/Start timing contract on IModFeature and
ModFeatureAttribute so future mods can rely on it.
Object.Destroy(stationTip) where stationTip is a MonoBehaviour destroys
only the component, leaving the cloned UI GameObject (icons, sliders, text)
orphaned and potentially visible under _stationTipsRoot. When the recycle
pool (128 slots) fills up during a planet with many stations, excess tips
were disposed this way; those orphaned GameObjects would reappear as frozen
'ghost' tips on every subsequent planet.
- Add ReleaseStationTip() helper: calls Object.Destroy(go) on the whole
GameObject after SetActive(false), replacing the broken Destroy(component)
- Simplify RecycleStationTips() and RecycleStationTip(int) to delegate to
ReleaseStationTip()
- Add HideAndRecycleStationTips() single cleanup entry point used by
Enable(false), OnGameBegin(), and new OnGameEnd()
- Add LocalPlanetWatcher (PatchImpl) hooking GameData.localPlanet setter:
triggers HideAndRecycleStationTips() on any planet id change, covering
frames where Update() is skipped by VFInput.inputing
- Subscribe OnGameEnd to clear tips when exiting a save/returning to menu
UXAssist:
- PlanetFunctions: fix infinite loop in constructStats cleanup (i++ -> i--)
- PlanetFunctions: skip entityPool slot 0 (sentinel) in DismantleAll foreach
- PlanetFunctions: fix belt buffer walk infinite loop when buffer[j]==250,
add cargoPool bounds check
- FactoryPatch: fix UnfixProto guard condition (< 3 -> < PowerPoleIds.Length)
- FactoryPatch: fix Array.Resize(index*2) -> (index+1)*2 to handle index==0
- LogisticsPatch: fix UpdateStorageMax early-exit to check both local and remote
- LogisticsPatch: fix storage max scan to use Math.Max instead of last-write
- LogisticsPatch: add divide-by-zero guard in station storage ratio calculation
- LogisticsPatch: fix station entry right-click delegates using per-instance
dictionary instead of shared static array to prevent unsubscribe mismatch
- LogisticsPatch: add null checks for GameObject.Find results in InitGUI
- GamePatch: log exception in previously empty catch block
- GameLogic: invoke event handlers individually with try/catch so one failing
subscriber does not abort subsequent ones
- DysonSpherePatch/LogisticsPatch/PersistPatch/PlayerPatch: replace
matcher.Labels = null with matcher.Labels = [] (5 sites)
- UIConfigWindow: remove duplicate I18N.Add for 'Outgoing integration count'
- UIConfigWindow: remove two orphaned y += 36f spacing increments
CheatEnabler:
- GamePatch: add null check for GameMain.gameScenario in AbnormalDisabler.OnEnable
- FactoryPatch: add null check for GameMain.mainPlayer in TakeTailItemsPatch
and GetItemCountPatch
- FactoryPatch: add null check for GameMain.mainPlayer in
ConstructionSystem_GameTick_Prefix
- FactoryPatch: fix _portalFrom.Remove(beltId) -> Remove(v) (wrong key)
- FactoryPatch: add null guard for GameMain.data before iterating factories
in WindTurbinesPowerGlobalCoverage
- DysonSphereFunctions: fix shell pool rebuild loop writing all shells to
slot 1 (id=1); now uses j+1 per iteration
- DysonSphereFunctions: replace GameMain.gameScenario.NotifyOnPlanDysonShell()
with null-conditional call (?.) at 4 sites
- DysonSpherePatch: fix SkipAbsorbPatch/QuickAbsorbPatch sharing _instantAbsorb:
OnDisable now recalculates the flag instead of unconditionally clearing it
- PlayerFunctions: add null check for GameMain.galaxy in TeleportToOuterSpace
- UIConfigWindow: add null guard in UpdateButtons before accessing button fields
- PlanetFunctions: add colliderId bounds check in BuryAllVeins