Skip to content

[3/9] Move ownership into features and share Unity helpers - #3

Open
friendly-ripper wants to merge 24 commits into
stack/02-il2cppfrom
stack/03-runtime-refactor
Open

friendly-ripper wants to merge 24 commits into
stack/02-il2cppfrom
stack/03-runtime-refactor

Conversation

@friendly-ripper

@friendly-ripper friendly-ripper commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Part 3 of 9 · base stack/02-il2cpp · 24 commits

Feature code, game bindings, and runtime helpers were tangled together. One hotkey file held the frame tick and the fleet state machine. Settings were a flat list of fields, and sync work ran on detached threads. This PR gives each feature ownership of its hooks, shortcuts, and state. Shared access moves into the IL2CPP and Unity libraries. The runtime fixes that this exposed land in the same stack.

What changes

Bindings and shared helpers:

  • About 120 single-class headers under prime collapse into ten domain headers (chat, fleet, inventory, navigation, server, UI, Unity, viewers, and a few more). Unused bindings are removed; binding bodies otherwise move unchanged.
  • Unity liveness, placement, and text helpers that features each spelled out move into the shared Unity binding header. In the last commit, Unity engine, UI, TextMeshPro, and input types move out of the game folder into a Unity library with the instance tracker; features call library helpers instead of the raw runtime API.
  • Ship-name matching splits from the game localisation call so its text half can be tested without the game. Warp, pinned ships, and the window code share that one matcher.

Settings, input, and installation:

  • Config held about 130 flat fields whose names had drifted from their TOML keys, with defaults declared a second time elsewhere. Each TOML section is now a struct whose members are named after their keys and carry their defaults. Keys, defaults, and vars output are unchanged.
  • The frame tick checks the settings file once a second and reloads only after a change has held for a whole check, so a half-written file cannot reset everything to defaults. A reload parses first and keeps the current settings on failure. Patch groups are decided at startup, so changing one logs that it waits for a restart.
  • Keyboard and mouse state move into an input module cached per frame. Hooks that read keys poll once per frame instead of asking Unity for the frame number on every query (about 190 input calls on an idle frame). Shortcuts gain one settings table with defaults, tiers, and deprecated aliases, and dispatch, fleet actions, and keybind badges become separate files.
  • File becomes a namespace that parses the command line once. Path joining and legacy-file migration move with it, and window state and UI scale handlers move to the files that use them.
  • Installation is centralised. The feature list is one table of name, install function, and patch-group member. Every feature exports a descriptor with its patch group, install function, and frame work. Hooks install in named groups, even when their setting starts disabled. Each hook reads the current switch at call time.

Sync:

  • Every entity group and realtime slot message used to start its own detached thread, so a delta could be applied before the full list. One thread now handles messages in arrival order, and each queue drops its oldest entry when full.
  • A game login resets sent state through the processing queue, in order, so an account or server switch is no longer diffed against the previous account. Record processors and their routing table move out of the game hooks into their own file.

Feature consolidation:

  • Toast queue hooks, Windows notification mirroring, and battle result formatting become one file; the three loading screens share one implementation. The warp label and cargo formatting merge into the features that own them. Fleet labels get one owner type in the camera code, and the anomaly timer gets one owner that resolves its view from the visible canvas.
  • Officer roster and assignment comparators, which differed only in predicate and tiebreaker sign, become one template that still picks the same four combinations.

Behavior changes

  • Settings now reload in place when the file changes, without a restart, except [patches] groups, which still need one.
  • -debug and -trace work as last arguments, and -ccm paths on macOS no longer get a doubled directory.
  • Donation caps apply only inside the donation popup, and never above what the player owns. Before, a use-items popup could offer the donation cap.
  • Camera fixes: entering a system while typing still jumps to the default system zoom, each flat renderable keeps its own scaled size, and a frame pans by keys or by glide, not both. A fleet-label preset of 0 no longer overwrites the default system zoom.
  • Chat preview no longer forces alliance chat on every panel change when a channel is disabled, and mission HUD updates follow the current setting after a reload.
  • Loading assets stay loaded while Unity reports them alive instead of decoding about 4 MB again on every login and transition. A JPG loader image now sizes its sprite from the loaded texture instead of a hand-parsed PNG header.

Testing

Built with xmake. Every commit was built on macOS arm64. Nothing was run in the game.

tashcan added 24 commits October 15, 2026 16:40
File becomes a namespace that parses the command line once
instead of a class re-checking an init flag in every accessor.
Path joining and legacy-file migration move with it, and the
settings reader shrinks to reading values through one small
reader type.

Parsing the command line once fixes -debug and -trace being
ignored as last arguments and a doubled directory for -ccm
paths on macOS. The macOS Library path is copied before its
autorelease pool can free it. Window state and UI scale
handlers move to the files that use them.
About 120 single-class headers under prime become ten headers
split by what they model: chat, fleet, inventory, navigation,
server, UI, Unity, viewers and a few more. Binding bodies are
unchanged; only includes move, and bindings nobody used are
gone.

String-view trimming now respects explicit lengths, so a
trimmed ship name no longer keeps trailing text.
Keyboard and mouse state move into an input module cached per
frame, keyed on the frame count instead of an explicit reset
from the hotkey hook. Shortcuts gain one settings table with
defaults, tiers and deprecated aliases.

The shortcut tests link the production code against a fake
input and run on any host. Per-frame feature updates now run
whatever the hotkey mode, instead of only when the hotkey
patch happened to be installed.
The hotkey file held the frame tick, a long dispatch chain
and the fleet state machine in about 1500 lines. Dispatch,
fleet actions and keybind badges are separate files now, with
the same check order and the same keys swallowed.

Navigation helpers return quietly when no section manager
exists yet, as before login and during reloads, instead of
dereferencing a null pointer.
Every entity group and realtime slot message used to start
its own detached thread, so a delta could be applied before
the full list. One thread now handles messages in arrival
order, and each queue drops its oldest entry when full.

Server timestamps parse without local-time conversion, and
name caches overwrite stale entries, stay bounded and parse
profiles one at a time so one bad record keeps the rest.
Toast queue hooks, Windows notification mirroring and battle
result formatting served one feature from three files with
duplicated guards. They are one file now, and the three
loading screens share one implementation with explicit
per-screen state instead of globals reset across files.

Text formatting lives in a tested header. After a padded
placeholder is filled, the next search continues after the
inserted text instead of rescanning it.
The warp label lived in its own file and reached back into
the confirmation code through a two-function header. Both are
one feature now, and the resolver is private to the file.

Cargo formatting merges the same way: both hooks looked for
the same label and each carried its own clamp and string
comparison, which now lives on the text binding.
The feature list becomes one table of name, install function
and patch-group member, and logging setup and the startup
banner leave the init hook for their own functions. Order,
names and toggles are unchanged.

Early setup runs before the logger exists, so its failures go
to stderr where they can be seen. A failed executable-path
lookup is reported instead of ignored.
Fleet labels were a widget map plus loose globals mutated
from seven hooks. One owner type now tracks the widget
lifecycle and answers depth, zoom and level questions, and
the per-fleet switch collapses to player versus everyone
else, which is what it already did.

A preset of 0 no longer overwrites the default system zoom,
and the zoom-to-default flag is cleared on frames that store
a preset.
The roster and assignment comparators differed only in which
below-deck predicate they chained and the sign of the index
tiebreaker. One template takes both, and the delegate wiring
still picks the same four combinations.
Config held about 130 flat fields whose names had drifted
from the TOML keys, with defaults declared a second time
elsewhere. Each section is now a struct whose members are
named after their keys and carry their defaults.

Keys, defaults and vars output are unchanged. Shortcut
defaults live with the shortcut table, the only place they
were used.
The frame tick checks the settings file once a second and
reloads only after a change has held for a whole check, so a
half-written file cannot reset everything to defaults. A
reload parses first and keeps the current settings on
failure.

Replaced settings are never freed, because sync threads may
still read them. Patch groups are decided at startup, so a
change to them logs that it waits for a restart.
The per-frame toggle work gets one owner with plainly named
state, and the tick returns early when the toggle default is
off instead of relying on the hook not being installed.

The dailies filter logs one summary line instead of one line
per daily on every refresh, and a faction name is lower-cased
once where it is read.
Unity liveness, placement and text helpers that several
features each spelled out move into the shared Unity binding
header, and feature files call that one copy.

Bindings nothing used are removed. A stale binding is a
member that can drift unnoticed, so the surface stays at what
features actually call.
Ship-name matching splits from the game localisation call so
its text half can be tested without the game. Warp, pinned
ships and the window code share the one matcher.

Pinned ship sorting warns once per unmatched entry instead
of on every dock sort, and the window hook runs the original
update unless free resizing is on.
The timer resolves its view and its drawer from the canvases
the screen manager has registered for them while in system
navigation, instead of searching globally from loose globals
mutated by hooks. One owner holds the state.

The label rebuilds only when the displayed second changes.
Hooks install in named groups, so a feature can be switched
as one unit and a failure in early setup stays contained
instead of stopping the features after it. Hooks install even
when their setting starts disabled, and each hook reads the
current switch at call time.

The object tracker takes its lock when reading and tolerates
repeat tracking, so a pooled object is neither lost nor
double-counted.
The donation fix raised any slider whose maximum was 50 or
100 on one platform and a different hook on the other, so a
use-items popup could offer the donation cap. Both platforms
now adjust the popup context where its row renders: only
donation popups, and never more than the player owns.

Server address overrides are read once instead of on every
call they affect.
Every key query used to ask Unity for the frame number, so an
idle frame made about 190 input calls. Hooks that read keys
now poll once, and with nothing held no key is asked about at
all.

A key claimed by a modified binding stays claimed until
release, which stops a modified chord decaying into the plain
key. Dock assignment fires once per Enter press, not per
frame while held.
Entering a system while typing lost the jump to the default
system zoom, because the flag was cleared every update but
only keyboard zoom consumed it. The jump now happens in the
camera update whether or not a field has focus.

Each flat renderable remembers the size it was scaled to.
Sharing one value made two planets of different sizes rescale
each other on every zoom. The pan hook also moved the camera
twice in a frame; a frame now moves by keys or by glide, not
both.
Every login and transition decoded the background and logos
again, about 4 MB per screen. Assets now stay while Unity
reports them alive and decode again only after an unload.

Sprites size from the loaded texture instead of a hand-parsed
PNG header, so a JPG loader image no longer gets a cropped
sprite rect. The forced black-background value is recorded
before the vars file is written, so the file shows the value
actually used.
A game login resets the sent state through the processing
queue, in order. An account or server switch is no longer
diffed against the previous account, which skipped the first
full upload and reported old jobs as completed.

Record processors and their routing table move out of the
game hooks into their own file. Server JSON reads go through
checked lookups instead of indexing const values, which was
undefined for missing keys, and each record is read in full
before anything is marked as sent.
The chat preview forced alliance chat on every panel change
when a channel was disabled, so other chats could not be
reached. It now moves only when opening on, or swiping onto,
a disabled channel, and reads the setting per call.

Mission HUD updates read the active setting instead of
visibility kept from install time, so reloads take effect
without reinstalling hooks.
Unity engine, UI, TextMeshPro and input types move out of the
game folder into a Unity library, with the instance tracker
that used to be mod code. Features call library helpers for
strings, boxing and allocation instead of the raw runtime API.

Every feature exports a descriptor with its patch group,
install function and frame work:

- mod.cc owns install order and the frame tick, which watches
  the settings file and runs installed features only.
- The old per-mod object tracker, key code and Unity header
  are gone; tracked classes are listed with the features.
- The binding checker now scans the library sources too.

Keybind badges count as installed only when both detours went
in. A feature with no hooks still gets a descriptor, so frame
work never depends on a hook group.
@friendly-ripper
friendly-ripper added this pull request to stack #9 October 18, 2026 16:31
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch from 18c2448 to cb36809 Compare October 18, 2026 18:49
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch 2 times, most recently from 395f491 to 8e49f94 Compare October 19, 2026 22:19
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch 2 times, most recently from 4c0ce12 to bd49c49 Compare October 21, 2026 21:39
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch from bd49c49 to 6cc2c47 Compare October 21, 2026 23:46
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch from 6cc2c47 to 5f1b378 Compare October 22, 2026 00:43
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch from 5f1b378 to a4602c7 Compare October 22, 2026 02:44
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch from a4602c7 to c291d43 Compare October 24, 2026 23:26
@friendly-ripper friendly-ripper changed the title [3/8] Refactor mod ownership and share Unity runtime helpers [3/9] Move ownership into features and share Unity helpers Oct 24, 2026
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch from c291d43 to 6080e77 Compare October 25, 2026 01:36
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch from 6080e77 to 6cfe2b0 Compare October 28, 2026 18:15
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch from 6cfe2b0 to 5758a3f Compare November 2, 2026 14:23
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch from 5758a3f to 9bb115a Compare November 12, 2026 21:50
@friendly-ripper
friendly-ripper force-pushed the stack/03-runtime-refactor branch from 9bb115a to bde2809 Compare November 13, 2026 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant