API Reference - clockworktownsmp/clockwork-bosses GitHub Wiki
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
- The controller creates the instance and establishes its ownership.
- It samples
pausesNativeBehaviors()once; default true pauses native behavior execution and navigation. Native sensors and memory expiry still run. start(context)acquires resources and registers restoration.tick(context)runs from the existing marked boss AI callback. ReturnRUNNINGorCOMPLETE. There is no additional global action watcher.stop(context, reason)runs once, even after a partially failed start.- 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.