Synced actions & events
The general model — clip-only one-shots, the closed event enum, and action_events.json.
A coop action choreographs two entities at once — an initiator and a target — along
authored, anchor-local motion paths, with an optional cinematic camera. The shipped example is the
takedown: aim at a bondage-eligible entity, press a key, and the target is pulled in and tackled
while both bodies follow scripted curves and the camera frames the scene.
The whole thing is datapack-authorable: one JSON file per action defines duration, the proximity gate, both roles’ waypoint paths, and the camera. The two visual poses are a pair of EF-JSON clips (authored exactly like any other animation).
The lifecycle is server-authoritative and wired end-to-end:
LivingEntity and presses the V key
(default, rebindable under key.categories.tiedup). A request packet is sent to the server with
the target’s id and the action id (takedown).proximity_gate, reserves both entities atomically, snapshots the anchor
(the initiator’s position + yaw at start), snaps both participants to t=0, and — only on a
successful gate — broadcasts each role’s clip and the position lock to all viewers.elapsed >= duration_ticks, the session ends and control returns to both entities.
Lost packets, death, disconnect, or dimension change abort cleanly; late joiners re-sync.A coop action is a single JSON file, in the canonical synced-actions directory:
data/<namespace>/synced_actions/<id>.jsonThe action id is the file name (without .json). data/tiedup/synced_actions/takedown.json →
the action id takedown. The loader is a server data-reload listener, so actions reload on world
load and on /reload, and are synced to clients on login and on /reload.
Verified field-by-field against the parser. Top-level fields:
| Field | Type | Required? | Notes |
|---|---|---|---|
duration_ticks | int ≥ 1 | required | Total session length in server ticks (20 = 1 s). Values ≤ 0 are rejected at load. |
proximity_gate | double | required | Max start distance (blocks) between initiator and target. Farther → the start is silently refused. |
cinematic_camera | bool | optional (default false) | Enables the cheap behind-player cinematic when no camera block is present. See Cinematic camera. |
camera | object {x,y,z} | optional | Anchor-local cinematic camera offset (doubles). See Cinematic camera. |
priority | string | optional (default MIDDLE) | LOWEST/LOW/MIDDLE/HIGH/HIGHEST — informational only; it does not change where the clip renders. Shared with all synced actions. |
roles | list | required | For a coop (waypointed) action: both an initiator and a target, each with a waypoints path. A waypointed action missing a role is rejected at load. (Clip-only synced actions relax this — see Synced actions.) |
Each entry in roles:
| Field | Type | Notes |
|---|---|---|
role | "initiator" | "target" | "self" | Lowercase string in JSON. A coop action uses initiator + target; self exists for single-entity clip-only one-shots (see Synced actions). |
clip | string | A full ResourceLocation (e.g. "tiedup:takedown_target") naming the per-role visual clip. See The two clips. |
waypoints | list (≥ 2) | The anchor-local motion path, sampled by the server. Optional in general — a role with no waypoints is clip-only (cosmetic). For a coop action both roles must carry one; it is the presence of waypoints that makes the action position-driving. |
Each waypoint:
| Field | Type | Notes |
|---|---|---|
t | double, 0.0–1.0 | Normalized time over the session. Strictly increasing across the list — validated at load. |
x, y, z | double | Anchor-local offset in blocks (Minecraft Y-up). Rotated by the anchor yaw and added to the anchor position at runtime. |
yaw | double (degrees) | Added to the anchor yaw to set the participant’s facing at that waypoint. |
The anchor is the initiator’s position and yaw captured when the session starts. Every waypoint
(x,y,z) is relative to that anchor (rotated into world space by the anchor yaw), and every yaw is
added to the anchor yaw — so an action authored facing “forward” works regardless of which way
the initiator was looking.
Each waypointed role’s list is validated when the datapack loads. An action is skipped (logged as an error) if a role that declares waypoints has:
t outside [0.0, 1.0],t (catches unsorted lists and duplicate t values),…or if the def is waypointed but missing a role (a position-driving action needs both
initiator and target), or has a decode failure (a wrong type / missing required field).
This is the shipped takedown (data/tiedup/synced_actions/takedown.json) — a 2-second action where
the initiator stays put and the target is pulled in from 1.5 blocks ahead, eased to 0.4 blocks (the
tackle), facing the initiator (yaw: 180):
{ "duration_ticks": 40, "proximity_gate": 2.5, "cinematic_camera": true, "roles": [ { "role": "initiator", "clip": "tiedup:takedown_initiator", "waypoints": [ { "t": 0.0, "x": 0, "y": 0, "z": 0, "yaw": 0 }, { "t": 1.0, "x": 0, "y": 0, "z": 0, "yaw": 0 } ] }, { "role": "target", "clip": "tiedup:takedown_target", "waypoints": [ { "t": 0.0, "x": 0, "y": 0, "z": 1.5, "yaw": 180 }, { "t": 0.5, "x": 0, "y": 0, "z": 0.8, "yaw": 180 }, { "t": 1.0, "x": 0, "y": 0.2, "z": 0.4, "yaw": 180 } ] } ]}To author your own action, drop a new file at data/<yourns>/synced_actions/<id>.json
with the same shape. The id (<id>) becomes the action key; the built-in takedown keybind always
requests the takedown id, so a brand-new id is loaded and synced but needs its own trigger to fire
(the keybind is hard-wired to takedown).
Each role’s clip field is a full ResourceLocation that must match a registered animation clip.
A complete coop action therefore needs two EF-JSON clips shipped as resourcepack assets — one for
the initiator, one for the target — authored exactly like any other pose clip (Blender EF-JSON addon
export + the hand-added top-level constructor block that registers it under a namespace:id).
The string in clip must equal the registered id from the clip’s constructor block — not
its filename. For the shipped takedown:
| Role | clip value | The clip’s constructor id must be |
|---|---|---|
initiator | "tiedup:takedown_initiator" | tiedup:takedown_initiator |
target | "tiedup:takedown_target" | tiedup:takedown_target |
See Animations for the full clip-authoring workflow (the constructor
block, format: "ATTRIBUTES", and the properties unlocks).
A coop action can move the camera. There are two modes; the renderer is client-side (🖥️ Client / singleplayer only the camera math runs only on the participating client).
camera blockAdd an optional top-level camera object with x, y, z doubles. This is an anchor-local
offset (same space and rotation convention as the waypoints):
{ "duration_ticks": 60, "proximity_gate": 2.5, "camera": { "x": 0.0, "y": 1.5, "z": -3.0 }, "roles": [ /* initiator + target as above */ ]}While the action runs, the camera is placed at anchor + rotateYaw(camera, anchorYaw) and aimed at
the live midpoint of both participants’ eye positions — so it tracks the action as the bodies move.
A terrain clamp pulls the camera in if a block sits between it and the look target, so it never
clips through walls. The local participant’s view is forced to third-person for the duration and
restored when the session ends.
cinematic_cameraIf you set cinematic_camera: true but provide no camera block, the action gets a cheap
behind-the-player cinematic: third-person view, yaw locked to the authored facing, pitch neutral. The
shipped takedown uses exactly this (cinematic_camera: true, no camera).
| What you author | Camera behaviour |
|---|---|
camera: { x, y, z } present | Static authored shot, live look-at on the participant midpoint, terrain-clamped. |
cinematic_camera: true, no camera | Behind-player third-person, yaw locked to facing, pitch 0. |
| Neither | Default view (no camera takeover). |
Synced actions & events
The general model — clip-only one-shots, the closed event enum, and action_events.json.
Animations
Author the two per-role clips: the required constructor block, format, and pose modifiers.
Bones & regions
The joint names you can pose in those clips, and the body-region vocabulary.
Item JSON
The per-item camera_offset (distinct from the coop camera) and the bind schema.
Planned & partial
Mob-initiated coop (AI auto-initiate) and other not-yet-wired features.