Skip to content

Modding system: settings, hot-reload, dependencies, and notifications#2001

Open
DevL0rd wants to merge 12 commits into
TwilitRealm:modsfrom
DevL0rd:mods
Open

Modding system: settings, hot-reload, dependencies, and notifications#2001
DevL0rd wants to merge 12 commits into
TwilitRealm:modsfrom
DevL0rd:mods

Conversation

@DevL0rd

@DevL0rd DevL0rd commented Jun 6, 2026

Copy link
Copy Markdown

Hello @PJB3005 Love your work <3

I was building my own modding system, and then saw a little late that you already started one! I saw we had the exact same methods in mind to implement modding! So i scrapped my branch and went ahead and put my upgrades here since your branch seemed a bit more progressed and cleaner.

I hope you find this helpful and that i'm not stepping on toes here. There are so many file changes because I merged current main into your mods branch, sorry if that makes it a little messy to track.

I am PR-ing directly to your wip mods branch, and have 2 mods of my own running on this. and I ported your cat mod on over also! I retained the menu system you were tinkering with in there in the mods menu also.

Here is a screen shot:
image

A few example mods and your ported cat mod:
https://github.com/DevL0rd/Dusklight-Mortal-NPCs
https://github.com/DevL0rd/Dusklight-Bottled-NPCS
(Let me know, i can transfer this repo to you and you can use the layout and deploy scripts)
https://github.com/DevL0rd/Dusklight-Chloe-The-Cat

Changes:

  • Merged with current main to keep up to date, resolved some simple conflicts there.
  • Declarative mod settings (bool/int/float) with a generated schema; values persist per mod and survive reloads.
  • Dedicated Mods window (master-detail): mod list on the left, per-mod enable toggle and settings on the right. Opened from the title screen and the in-game quick menu.
  • Mod enable/disable state stored as a game CVar, separate from the mod's own config.
  • Hot-reload: rebuilt mod libraries reload in place via a file watcher (Linux inotify, Windows, macOS FSEvents) — no restart. This helps me develop a mod without restarting over and over and over.
  • Mod dependencies and load order: mod.json gains dependencies (mod ids) and priority. Mods load dependencies-first then by priority (topological sort); cycles are flagged; a mod won't enable until its dependencies are active. The UI lists each mod's dependencies and their status.
  • On-screen toasts for hot-reload success and for load/enable/reload/crash errors.
  • Failed hook installs now mark the mod as failed (shown in the UI) instead of silently running with a dead hook.
  • Game functions are built with -fno-partial-inlining when code mods are enabled, so hooked functions keep a prologue funchook can patch.
  • Failures never auto-disable a mod; it stays enabled and retries on rebuild or relaunch.

Untested:

  • I can't test basically any of the ios code or the fixes to compile on the github actions. But figured I would let claude have a go at them and let someone with those platforms try it out. Maybe can we could somehow mark them as untested so no one forgets about them.

@DevL0rd

DevL0rd commented Jun 6, 2026

Copy link
Copy Markdown
Author

Also if you ever have GitHub secrets I would really recommend NOT running these checks / builds for PRs. People can abuse that, and say force GitHub secrets to be printed out into the GitHub actions. If you ever use any later.

You can however run these checks only when someone approves the PR. I would recommend nipping that soon so no one can abuse your GitHub actions <3

Even if you don't have secrets right now, I would still recommend it. <3

For now it was good to see that apple and android build doesn't like the mod loader haha, so I will fix that right now.

Love yah fellow humans. Thanks for the smiles :)

@MelonSpeedruns

Copy link
Copy Markdown
Collaborator

This feature set looks INSANE! I'll go through it with PJB when I'm not dead asleep, but awesome job it seems so far!

@DevL0rd

DevL0rd commented Jun 6, 2026

Copy link
Copy Markdown
Author

This feature set looks INSANE! I'll go through it with PJB when I'm not dead asleep, but awesome job it seems so far!

haha thanks. I've been a software engineer for 15 years now, but I'd like to disclose use of claude for organizing and execution, and hope it doesn't detract from my care and experience I put into my code.

​While I kept the code clean organized and standardized, and planned the architectural changes, claude did the heavy lifting. I hope this is acceptable.

​If there is anyway I can help more on mods system or own that bit, or manage helper library side of things—specifically, building out a shared toolkit of mid-level abstractions to eliminate boilerplate for common game-side interactions—let me know, I will be doing it for myself anyways, and don't mind offloading that kinda work when I have time.

Anyways PJB clearly knew what was up when starting with mod integration. If I'm not stepping on toes and PJB finds it useful, I certainly have more to add to the mod system, and things I'd wanna change like how hot reload re extracts. But if you find this kind of help useful, let me know and I'll continue ♥️

@encounter

Copy link
Copy Markdown
Member

Also if you ever have GitHub secrets I would really recommend NOT running these checks / builds for PRs.

No secrets in the repository, I keep our release configs totally separate because of this!

I can't test basically any of the ios code

I was under the impression that iOS support would be impossible due to code signing, is there a workaround?

I just merged latest main into the mods branch, so would you mind rebasing just your commits on top of that? It will fix the diff so we can review.

Thanks for the work on this so far!

@PJB3005

PJB3005 commented Jun 6, 2026

Copy link
Copy Markdown
Member

I was under the impression that iOS support would be impossible due to code signing, is there a workaround?

Asset mods are still an option.

DevL0rd added 9 commits June 7, 2026 01:04
- Add define_settings/setting_get/setting_set to the mod API; the loader stores
  typed settings per mod, persists them + enabled state in mods/.config, and
  generates a schema so disabled mods still show their settings
- Replace the per-mod enabled CVar with config.json; add live enable/disable
- Add a file-watcher (inotify/Win32) hot-reload that reloads a single native mod
  in place when its library changes, plus refresh() for newly added mods
- Add a master-detail 'Mods' tab to the settings window (mod list + Refresh,
  per-mod enable toggle and auto-generated setting controls)
- Add an FSEvents file watcher for hot-reload on macOS (+ link CoreServices)
- Remove the Mods button from the prelaunch/home menu (the Mods settings tab
  is the primary mod UI now)
- Remove the bundled tools/cat_mod example (relocated to its own repo)
- Replace the per-mod-tab Mods window with a master-detail UI (list +
  Refresh on the left; description, Enabled toggle, and settings on the
  right), still rendering a mod's own custom panel
- Remove the Mods tab from Settings; open the Mods window from the title
  screen and the in-game quick menu instead
Enable/disable is now a ConfigVar<bool> the loader registers and persists via
config::Save(), like the game's own settings. The per-mod config.json holds only
setting values (no enabled key), so the settings system can never clobber the
enabled flag. The CVar key embeds the mod id verbatim (no escaping).
- mod.json gains 'priority' (load order, lower first) and 'dependencies' (mod
  ids); mods load dependency-first then by priority (topological sort), cycles
  flagged. A mod won't enable until its dependencies are installed + active.
- Mods UI: dedicated window lists each mod's dependencies + status, greys out the
  enable toggle when deps are unmet, and tags failed mods '[failed]'.
- On-screen toasts for hot-reload success and for load/enable/reload/crash
  errors; a failed hook install now marks the mod failed instead of silently
  running with a dead hook.
- Build the game with -fno-partial-inlining when code mods are enabled so hooked
  functions keep a prologue funchook can patch (fixes silent hook failures).
- Failures never disable a mod (it stays enabled and retries on rebuild/relaunch);
  the watched library path is recorded before load so even a failed load retries.
- Gate RTLD_DEEPBIND on the macro, not __linux__ (Android defines __linux__ but
  Bionic has no RTLD_DEEPBIND; macOS/iOS lack it too).
- Drop the in-game-TU CoreServices/FSEvents watcher: its header collides with the
  game headers (Carbon MachineExceptions) and FSEvents is macOS-only. macOS/iOS
  fall back to no file watcher (mods still load + Refresh works).
- macOS x86_64: pre-set FUNCHOOK_CPU from the Apple target arch. funchook picks
  its backend from the build host's arch (CMAKE_SYSTEM_PROCESSOR), which is wrong
  when an arm64 runner cross-compiles to x86_64 (it built the arm64 backend).
- iOS: quote the MACOSX_BUNDLE_* / entitlement values in set_target_properties so
  an empty bundle var on iOS doesn't make the call arg-count invalid.
Re-add Apple hot-reload using kqueue (EVFILT_VNODE) instead of FSEvents: it's
plain BSD (no CoreServices/Carbon header clash) and works on both macOS and iOS.
Watches the mods dir, each mod entry, and files within mod folders; an
EVFILT_USER event wakes the kevent wait for clean shutdown.
funchook's arm64 trampolines call the __clear_cache libcall, but clang's
compiler-rt builtins aren't linked on iOS/tvOS (macOS provides it). Supply it via
sys_icache_invalidate, guarded to non-macOS Apple to avoid a duplicate symbol.
@DevL0rd

DevL0rd commented Jun 6, 2026

Copy link
Copy Markdown
Author

Also if you ever have GitHub secrets I would really recommend NOT running these checks / builds for PRs.

No secrets in the repository, I keep our release configs totally separate because of this!

I can't test basically any of the ios code

I was under the impression that iOS support would be impossible due to code signing, is there a workaround?

I just merged latest main into the mods branch, so would you mind rebasing just your commits on top of that? It will fix the diff so we can review.

Thanks for the work on this so far!

OK awesome! So, I went ahead and cherry picked and re-based and did the whole dance. all looking good now!

So I have my main machine on Linux, cause I want grey hairs as fast as possible, have a Windows VM with VFIO pass-through so those 2 cases are tested.
Next I can try building mods for android.
And I also have a clockwork uConsole with a cm5, a arm linux machine, and I kinda wanna play with that and make sure mods work there.
I also saw you set up Tracy so I'll use that to profile it and see how fast it is!

But yeah i'm not really sure about Apple side of things, how do you guys validate builds on apple devices?
Same for windows on arm? If you trust linux x86 and msvc x86, do you automatically trust arm variants?

Anyways, thanks for reviewing guys!

@DevL0rd

DevL0rd commented Jun 13, 2026

Copy link
Copy Markdown
Author

Quick additions, I added some simple changes to UI and menu bar so mods can add new menu items.

Needed it for my experiment doing multiplayer mod. And will likely expand more on this when you decide on how mods is going to look.

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.

4 participants