Skip to content

Unbound modapi - #11

Open
skijer wants to merge 29 commits into
unboundfrom
unbound-modapi
Open

skijer wants to merge 29 commits into
unboundfrom
unbound-modapi

Conversation

@skijer

@skijer skijer commented Sep 21, 2026 •

Copy link
Copy Markdown
Owner

What is this:

This is a C Code mod support for Unbound build. Not affiliated to HM team.

What does it do?

As a Modder, it opens 3 things for code mods: *1 Read runtime variables and functions by creating a .lib *2 It opens Game Interactor Gamehooks to be used by a mod *3 It makes an always compatible mod support for the modders (unless a hookchange name) but if your mod is supported by 9.2.3 and the player wants to play it in 10.1.2 they can, even viceversa, as long as the functions exists
As a Player, basically opens a world of code mod supports, I will make a mod store for as many mods as we want in the future.

IF YOU ARE A MODDER

read this branch and pr for my implementations of my nei items and actors: NEI MODS FOR UNBOUND
If you want any hook, first make 100% sure it is not possible with current hooks, then ask in here why you need it and the use you will give it and I can add it

Build Artifacts

UNSAFE, MODDING ONLY

@skijer
skijer changed the base branch from develop to unbound September 21, 2026 14:55
Four unfixed defects: dungeon keys and items use a stale vanilla slot in
custom scenes, the registry title card is never shown, item restrictions
alias vanilla scenes past id 255, and per-actor object bank slots are
still signed bytes.
… the heap

The only check was an assert, which release builds drop: they allocated a
zero-size actor and wrote past it. Actor Viewer: Spawn as Child accepts ids
past the vanilla table, and the search results no longer loop forever at
256+ entries.
unbound/actors.json declares a type (model, collision, talk, look); each
type is its own ActorDB entry from 0x1000 running one shared driver. A room
actor's id may be a name. Test fixture: unbound-docs/examples/custom-actors.
An OoT skeleton has no usable rest pose: with every joint angle zero it
folds up. A type with a skeleton and no animation is now rejected; a still
model holds one frame with "frame".
Unnamed segments 8-12 are bound to an empty display list, as vanilla binds
on the segment character models call for their render mode, and the env
colour is opaque black. New model key hideLimbs hides the spare hands and
props vanilla code swaps in.
…till need their object

The glass pane stands on the ground (its model is centred on its origin).
A vanilla actor placed by name needs its object in the room, as by number:
the sign never spawned in Link's house, which does not load object_kanban.
… talk never sticks

Shadows draw: one floor raycast at spawn gives ActorShadow_Draw its floor, and
`shadow` is given at scale 0.01 so it no longer grows with `scale`.
Refused at spawn instead of crashing: skin-limb skeletons, animations for fewer
limbs than the skeleton, segment paths that are not textures. Loop speed is
clamped to the animation's length.
Talk reads the Player every frame instead of latching, and closes event-ended
boxes when advanced.
Unknown keys inside model/collision/talk/look reject the type. Room actors with
an id from 0x1000 or object params are skipped; a name in transitionActors
spawns nothing. New model.cullRadius and model.drawDistance.
ActorDB::TryAddEntry holds its checks in release builds; the registry loop is
shared with SceneDB (ForEachRegistryEntry). Spec text, design notes and the
fixture follow.
…ssets checked once

SPEC takes the actor registry: §7 becomes Registries (§7.1 scenes and
entrances, §7.2 actor types), §4.3 accepts a name as a room actor's id and
limits integer ids to 0-0xFFF, §9 and §10 record the limit and the addition.
actors.md becomes the how-doc; README row and a Prelude handoff entry added.

Actor_Spawn no longer asserts fewer than 255 live actors of one type, which
aborted Debug builds on a room of props. A talking actor keeps updating while
culled so it can close an event box. The shadow keeps a scene floor only: a
moving floor's polygon pointer drifts as dyna actors come and go.
Asset checks run once per type on its first spawn and are remembered, so a bad
path is logged once rather than per placement. A scale that is not positive
rejects the type; an animation with no frames and a negative actor id are
refused. The fixture covers both new rejections.
…rigs built unlike vanilla's

A head limb imported from another game (MM Skull Kid's head X points forward) rolled its head
instead of turning it. Both keys are optional [x, y, z] in the head limb's space, normalized;
defaults [1, 0, 0] and [0, 0, 1] keep Matrix_RotateX/Z, others use Matrix_RotateAxis. pivot now
lies along turnAxis. A malformed, zero or parallel axis turns head tracking off with an error,
as a bad look.limb does. SPEC 7.2 + 10, actors.md, fixture types 106/107 and short_look_axis.
….json, named by its path

The single layer-merged unbound/actors.json made a tool that keeps each type
as its own asset rewrite a shared document and track which keys it owned.
Each type is now its own document; the path between unbound/actors/ and
.json is its name. Each path still layer-merges on its own, so a mod can
patch or (with null) remove another mod's type, and a broken file loses
only its own type. Types number from 0x1000 in name order. The single
document was never released and is no longer read.
…th order

Paths were sorted with .json attached, so a name that starts another
(npc, npc-old) numbered in the wrong order against SPEC 7.2. The registry
file list now sorts by the derived name; the loader is split into list,
load and add steps. A JSON error inside a file logs the registry kind and
name again, ActorRegistry calls a type's name 'name', the glob caveat on
the reusable helper is stated, and the example README and actors.md no
longer miscount types or talk about keys.
@skijer
skijer marked this pull request as ready for review October 1, 2026 01:57
@skijer
skijer force-pushed the unbound-modapi branch 3 times, most recently from d5095f1 to 517932c Compare October 1, 2026 04:23
@skijer
skijer deployed to mod-signing October 1, 2026 22:36 — with GitHub Actions Active
- sign-mod workflow: requests by issue, queued one at a time in labels, new authors wait for
  approval; sources scanned, built with Unbound's own CMake, imports checked, packages signed with
  Ed25519 and published with unbound-mods-all.zip; without a release it builds against the current
  commit and the soh.lib of the last Windows build
- ModTrust: the game verifies signatures (Monocypher) and a revocation list; UNBOUND_MOD_POLICY
  open/ask/signed is fixed at compile time and only touches ModLoader.cpp
- ModPermissions: permissions declared in the manifest, caller identified by its module; microphone
  (SDL capture, asked once, on-screen icon) and files (always asked, writes only in mods/);
  AskPlayer, TellPlayer, RequestRestart
- soh-unbound-unsafe: every platform also builds a mod developers' edition that asks before each
  unsigned mod, attached to releases as SoH-<tag>-<platform>-unsafe-mods
- build-mods: mods released by tag with per-mod and all-mods downloads; the pull request table links
  the newest signed release; the full game build only runs for pull requests into release branches
- macOS: z_kaleido_setup.c declares GameInteractor_Should; kaleido case 0 is a block

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch was successfully deployed

1 active (outdated) deployment
mod-signing — 71ce78a3 Deployed Oct 1, 2026 by skijer via sign #3
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.

2 participants