Skip to content

API Reference

clockworktownsmp edited this page Oct 14, 2026 · 3 revisions

API reference

Start with Addon API for the basics, or follow the Lightning addon walkthrough to see a complete example. This page is the reference for the current development API.

Status and scope

This is experimental API revision 1, included in stable 0.2.0 and the local beta.6 checkpoint. The older 0.1 release contains neither this API nor optional-client fallback. Clients installing the core must match the server build; clients without it use native Warden presentation. Addons can still require their own client assets/networking.

BossAddon.API_REVISION and clockwork_bosses:api_revision metadata identify the contract, not a stable binary compatibility guarantee. Build addons against the exact core version they will run with. Versions and release status separates released, local beta and unfinished work.

The public Java package is clockwork.bosses.api. The contract provides registered actions with owned cleanup, data-only setting descriptions, encounter notifications with immutable UUID snapshots, and a controller/capability boundary. The current backend controls Wardens.

Core combat, melee, sonic boom, recovery, roar and burrow use the action catalog and public context/controller. Warden-specific animation stays in the controller backend. Addons can reuse phases, membership, native targeting, support and rewards without rebuilding an encounter. Legacy attack and recovery subphases still work. Existing templates do not gain extra phases or actions automatically.

Custom targeting strategies, conditions/triggers, arbitrary mob registration, custom client controls and a separately published API dependency remain future work. Native beta.6 checks do not establish a second mob adapter or arbitrary addon compatibility.

Register an addon

An addon is a separate Fabric mod with its own namespace. Declare a dependency on Clockwork Bosses, Minecraft 26.3, Fabric API and Java 25. Add this entrypoint to its fabric.mod.json:

"entrypoints": {
  "clockwork_bosses": ["your.addon.YourBossAddon"]
}

Implement BossAddon.register(BossActionRegistry.Registrar). The core invokes that entrypoint during initialization, before loading templates, then freezes the catalog. Use the provided registrar; do not register asynchronously or keep it for later. Duplicate IDs and IDs outside the provided addon namespace fail initialization visibly. That prevents an addon from silently replacing another mod's mechanic.

public final class YourBossAddon implements BossAddon {
    @Override public void register(BossActionRegistry.Registrar actions) {
        actions.register(new BossActionType(
            Identifier.fromNamespaceAndPath("your_addon", "wait"),
            "Example pause", "Pause native behavior for the chosen duration.",
            List.of(BossSetting.number("duration_ticks", "Duration ticks",
                "Pause length; 20 ticks = one second.", 40, 1, 200, true)),
            Set.of(), Wait::new));
    }
    private static final class Wait implements BossAction {
        @Override public Result tick(BossActionContext context) {
            return context.elapsedTicks() >= context.settings().integer("duration_ticks")
                ? Result.COMPLETE : Result.RUNNING;
        }
    }
}

Factories must return a fresh instance for every execution. Never return a singleton containing a target, countdown, world, cleanup callback or entity. Imports in addon code should use the public API, not ClockworkBossManager, ClockworkBossState, mixin contracts or other implementation classes.

Configure it in a subphase

Select the registered action in the subphase's Type dropdown. The GUI shows its declared Action settings section automatically. You don't need a custom screen, packet handler or core edit for a scalar setting.

"subphases": {
  "short_pause": {
    "type": "your_addon:wait",
    "action_settings": { "duration_ticks": 40 }
  }
}

Subphases still use the parent's sequential/random order and independent roar interludes. Returning COMPLETE enters that same transition path. For registered actions, action_settings is optional: leave it out or use {} for the declared defaults. If present, it must be an object, not null, an array or a scalar. Within that object, missing or null declared scalar fields use their defaults. Existing native combat overrides continue inheriting from the parent phase; custom action settings do not silently inherit a different action's values.

Changing Type resets action_settings to the new action's defaults. Other stored combat overrides are preserved, although inactive controls are hidden and do not affect noncombat actions. Unknown addon setting keys survive saves, but cannot become arbitrary editable/executable fields. If an action addon is missing, validation fails instead of silently replacing its mechanic.

Settings and editor contract

BossSetting declares a key, readable label, help, default and validation:

Factory Control Validation
number(..., min, max, integer) Number field Exact inclusive limits; optionally whole numbers
bool(...) True/false dropdown A JSON boolean
text(..., choices, maxLength) Dropdown when choices exist, otherwise text Declared choices and length

Numeric parsing reuses the core's bounded decimal parser: at most 128 text characters, 64 significant digits and scale -64 to 64. JSON strings are not coerced into numbers or booleans. integer(key) returns an exact 32-bit value; whole-number definitions must have bounds that fit it. number(key), bool(key) and text(key) read compiled values. Tick callbacks do not parse JSON.

Limits: 64 total registered actions, 16 settings and 16 required capabilities per action, 32 choices per setting, text length up to 512. Action IDs use flat lowercase names; setting keys use lowercase letters, digits and underscores. The total editor catalog is capped at 20,000 characters and the existing document/save packet size limits still apply.

The descriptions sent to operators contain data, never factories or executable Java. A server-only addon can therefore expose standard controls to a client that has Clockwork Bosses but not the addon. Addons with new entities, renderers, assets or their own network channels can still require installation on clients. Native-only actions can also run for players without the core client mod, who have no editor, custom file media or enhanced visuals. The core's adapter covers its own Warden fields through native tracking; it cannot make an addon's custom registries or direct packet formats optional. Mixed-client runtime checks remain pending. The server rebuilds and validates action changes against its own catalog; a client cannot replace descriptors. Virtual catalog fields are never saved.

Generated file comments group setting descriptions above action_settings, including defaults and limits. An addon that opens files or accepts identifiers must implement its own domain validation; a bounded text field alone does not validate a filesystem path or authorize access to it.

Development source: template schema export

The shared schema exporter discovers action settings through the same registered BossSetting descriptors as the compiler, GUI and file comments. Addon authors do not maintain another website field list. /boss template schema exports the installed server's startup catalog; gradlew exportTemplateSchema exports core definitions without launching Minecraft. Exports contain data, never factories or executable addon code.

The portable format codec preserves unknown settings even when an addon is absent. The encounter still cannot run that missing action. The GUI's Action default choice writes null, matching the API rule that missing/null scalar settings use their declared default; a present action_settings must remain an object. Template format revision 1, schema revision 2 and experimental API revision 1 are separate contracts. See Templates and presets and TEMPLATE_SCHEMA.md in the source repository. This work follows packaging of 0.1-client-optional.1. The schema is now included in stable 0.2.0 and local beta.6; packaging does not publish the separate website.

Action lifecycle

  1. The controller creates the instance and establishes its ownership.
  2. It samples pausesNativeBehaviors() once; default true pauses native behavior execution and navigation. Native sensors and memory expiry still run.
  3. start(context) acquires resources and registers restoration.
  4. tick(context) runs from the existing marked boss AI callback. Return RUNNING or COMPLETE. There is no additional global action watcher.
  5. stop(context, reason) runs once, even after a partially failed start.
  6. Cleanup callbacks run in reverse acquisition order; runtime references to entities, worlds, action objects and state are then cleared.

The stop reasons distinguish completion, phase/subphase changes, reload, death, removal, unload, shutdown and failure. An old generation cannot complete a later phase. A failed execution remains stopped until a transition or reload; it is not restarted and logged every tick. Runtime exceptions, linkage errors and assertion failures are isolated and cleanup continues. Fatal VM errors are not swallowed.

All callbacks and live context access belong on the server thread; live getters enforce this before accessing encounter/world state. Immutable settings may be copied to a worker, and snapshots may be captured on the server thread. Do not put a context in a static collection, future, timer or background job. In stop, read-only settings/snapshots and direct restoration are available, but mutating helpers reject starting new work in a closed action. Cleanup callbacks must be short and safe when entities are dead or already removed. Fatal process stops cannot execute callbacks; journal persistent mutations or keep them transient.

allowsNativeAttack(id) is a cheap, side-effect-free permission query. To allow native combat alongside an action, also return false from pausesNativeBehaviors(). The controller services pending melee and sonic work for permitted actions. Phase enable flags, charge/recovery/cooldown rules and native target checks still apply. Core native action IDs are:

ID Behavior
clockwork_bosses:combat Both enabled native attacks; configured attack budget
clockwork_bosses:melee Melee only; configured attack budget
clockwork_bosses:sonic_boom Sonic only; close range does not yield to disallowed melee
clockwork_bosses:recovery Timed movement/attack pause
clockwork_bosses:roar Native roar, optional sound; waits for native completion
clockwork_bosses:burrow Dig, hold underground, emerge near an eligible player target

Roar waits for a valid native target and the preceding attack's recoil. It does not fabricate a target when no player is available. Burrow uses bounded depth and duration, captures original flags/position, and saves a small surface-position journal. When the hold ends, it resolves the current target's latest position; an invalid target falls back to an eligible arena player using the existing targeting policy. It searches at most eighty nearby floor candidates, using the scaled standing body, loaded chunks, world border, arena bounds, native walkable terrain classification, solid support, collision and liquid checks. No safe destination means emergence at the starting surface. No blocks are destroyed.

The boss faces its target, performs the native emergence, and advances the subphase sequence. Put a combat action after burrow to attack after emergence; burrow does not fabricate a hit or override a following recovery/roar action. Native targeting vetoes and attack cooldowns are retained. While underground, radius-arena membership stays anchored at the saved surface. Once emergence commits its destination, the radius follows that new surface. Cleanup and saved interruption recovery use the latest committed surface and original flags, so normal completion does not teleport the boss back. Neither action replaces general Warden AI. In-game verification remains pending.

Context, capabilities and resource ownership

context.controller() exposes a Mob and declared capability IDs. Registrations requiring unsupported capabilities cannot compile for the current Warden backend. The Warden declares clockwork_bosses:melee, sonic_boom, roar and burrow. An empty requirement set is appropriate for an action using only generic mob and participant operations. Do not infer a Warden animation from a generic mob.

The public controller also provides startRoar(BossRoarSound)/roarFinished() and startBurrow(undergroundTicks, depth)/burrowFinished(). Core actions use these same operations. Acquire one native animation per action, normally from start(). Roar requires an unpaused native brain; burrow requires a paused one. BossRoarSound.VANILLA keeps the default roar. A custom cue uses the selected template's validated OGG delivery. Custom range 0 resolves the arena radius or inclusive box diagonal at the trigger, with a 32-block fallback and 512-block cap. Positive ranges are manual; playback keeps its one-block minimum. Core registered roar defaults use 0; omitted legacy inherited roar ranges keep 32. Vanilla roar audio and encounter music are unchanged. Animation interruption is owned by the controller, including burrow's saved return-position journal.

target() validates the current native target. participants() resolves live, eligible players from the existing arena membership; isParticipant(player) also rechecks current position, life and dimension at use time. Store UUIDs and re-resolve them before delayed hits. The helpers do not load chunks.

Register onCleanup(...) before changing a temporary flag or attribute. Prefer namespaced native attribute modifiers and remove only your own modifier; blindly restoring an attribute's old base can overwrite another mod's changes.

Call ownEntity(entity) only for an entity your action created, preferably before addFreshEntity. Ownership excludes native persistence, including passenger saves, so an earlier chunk unload cannot leave a saved minion or projectile orphan. Cleanup discards that exact entity if still loaded and never loads its chunk. It rejects players, bosses and already-owned entities. Scope capacity is 64 resources per execution; it is not a persistent minion system. Owned entities reject portals and cross-dimension teleport because native travel creates a replacement outside the cleanup identity. Same-world teleport remains available.

damage(player, source, amount) revalidates arena eligibility, uses native server damage and reports an accepted hit. Armor, invulnerability and other mods' damage vetoes still apply. It also uses existing voice/target-rotation handling for that attempt. Health damage in the hit notification is the observed health delta, not requested damage. An action decides its own completion rather than advancing its subphase once for every victim of an area attack.

Encounter notifications

Register Fabric callbacks on BossEncounterEvents during addon initialization. They are synchronous notifications, not cancellation or mutation hooks. Keep these callbacks short. Register once during initialization; Fabric retains listeners for the process lifetime. Never capture live encounter/world/player objects in a process-wide listener. Immutable snapshots contain boss/template/phase identity, game time and participant UUIDs without retaining an entity or world. The snapshot UUID set represents arena viewers; live context queries additionally filter untargetable players before an addon attempts damage.

Event When it fires
STARTED Dormant boss activates, before initial arena joins; not command spawn
PLAYER_JOINED Membership/support and boss bar are committed by the existing scan
PLAYER_LEFT Arena exit, disconnect, player death/respawn or encounter cleanup
PHASE_CHANGED A real health phase transition commits; not initial assignment/reload
ATTACK_LANDED Accepted native or context-helper damage; no event for rejected attacks
ENDED Loaded encounter terminates or suspends, with an explicit reason

ENDED(DIED) is committed boss death. UNLOADED means suspension, not a reward or quest completion. Loaded-state end emits once; its snapshot retains the final audience before removal, while departure snapshots show cleared membership. Loading the boss creates a fresh runtime; it does not repeat activation or restore an interrupted action mid-flight. Native saves retain boss identity and existing player-heal/reward ledgers.

A callback failure cannot skip later listeners. Failure logging is globally limited to once per minute. Installed Java addons are trusted code: this API's validation and scopes are not a security sandbox for a malicious mod. Slow or looping callbacks can still stall the server. Factories must return fresh instances; the core does not prove freshness. See API safety and resource limits.

Standalone example and verification

The lightning example is its own Gradle project. It registers one action using only the public API, supplies number and dropdown fields, uses native warning particles and a visual-only lightning bolt, and routes damage through the encounter context. It has no mixins or client entrypoint and is not bundled or automatically installed with the core.

Current example source identifies addon 0.1.8 and reads the core build's configured version/archive name. Its runtime dependency pins that exact version, currently 0.3.0-beta.6. This is not a new addon packaging/runtime result. Rebuild the addon after changing core versions. See the lightning checks for registration, editor integration, execution and cleanup.

Earlier verification recorded core/client and independent addon compilation in the Java 25 / Minecraft 26.3 environment. The later beta.6 core checkpoint has native lifecycle checks, but does not establish a newly built addon 0.1.8, server-only addon editor interaction, real multiplayer or a second mob adapter. Inspect the actual installed pair and run the addon checklist. Earlier 0.1 release results do not cover this API.

Clone this wiki locally