Mod and Framework
The lifecycle you implement, the handle you are given, and the result type that everything in the surface returns.
Mod
The class you implement. Every method is optional; implement the ones you need.
struct MyMod : axiom::Mod { void init(Framework& f) override; // acquire, build panels void post_init(Framework& f) override; // other mods exist now void frame(Framework& f) override; // once per presented frame void event(Event& e) override; // binds, buttons, config void shutdown() override; // optional; cleanup is automatic }; AXIOM_MOD(MyMod);
Hooks detach, asset layers drop, input capture releases and the widget tree is
removed when a mod unloads. shutdown is for your own state, not
for undoing anything the surface gave you.
Framework
The handle you are given. Each accessor returns a question or primitive the mod declared.
Everything about what is in the world.
View, projection, position, field of view.
World to screen and back.
Named access to the game's own structs.
Panels, rows, controls.
Your own draw list.
Binds, state, capture.
Your declared settings, typed and clamped.
Goes to the overlay console with your mod id attached.
A question asked by a pack rather than by Axiom. Comes back empty, not denied, when no installed pack asks it, so a mod can light up extra panels where they exist and stay useful elsewhere.
alice.mygame/spellbookif (auto sb = f.ext<Spellbook>("alice.mygame/spellbook")) for (auto& s : sb->known()) panel.row(s.name, s.cooldown_ms);
Result, Denied and Failed
Everything fallible returns Result. The difference between the two failures is the most useful
thing in the API.
DeniedFailedauto cam = f.camera().view(); if (cam.denied()) shown_ = false; // never on this game else if (cam.failed()) return; // transient, try next frame else draw_with(*cam);
Denied is decided at attach and never changes during a session, which is what makes the “stop
asking” branch safe.
Every failure carries a reason that is safe to paste into a bug report.
SymbolUnresolved, ReadFault, StaleHandle, Unsupported, CaptureHeldFailed{ SymbolUnresolved, "entity_pool not found on build 1.4.3" } Failed{ StaleHandle, "entity 412:7 generation is now 9" }
Config
Declared in the manifest, which generates the overlay tab and the manager panel. You never write settings UI.
Typed and clamped to the declared range.
Fires on the mod’s thread, next frame after the user changes it.
float range = f.config().get<float>("range"); f.config().on_change("range", [&](float v) { range = v; });