Furniture ✅ moddable
Definition JSON + a .glb mesh (built-in glTF exporter) + 1–8 seats. The seated pose is the
seat’s sit_animation clip. No Java required.
Furniture is fully data-driven — a definition JSON plus a GLB mesh, no Java. Pets are not — they are hardcoded blocks. This page covers both honestly so you know where the line is.
✅ Available works ⚠️ Partial caveats ❌ Planned not yet 🔧 Manual / tool-gap manual step
Like items, a furniture piece is two halves — both in your one addon pack (see Packaging):
data/<namespace>/tiedup_furniture/<name>.json.assets/, exported with Blender’s built-in File → Export → glTF 2.0 (.glb) exporter (the same tool as item meshes — not the EF-JSON addon), plus the inventory
icon and any seated-pose animation clips.data/<ns>/tiedup_furniture/<name>.json ← the furniture definitionassets/<ns>/models/gltf/furniture/<name>.glb ← the mesh (baked texture inside)assets/<ns>/animmodels/animations/<sit_clip>.json ← seated-pose clip(s), EF-JSONThis is the shipped tiedup:test_cross definition — copy it and adapt. Required fields are marked;
everything else falls back to a safe default.
{ "id": "tiedup:test_cross", "display_name": "Test St. Andrew's Cross", "translation_key": "furniture.tiedup.test_cross", "model": "tiedup:models/gltf/furniture/test_cross.glb", "tint_channels": {}, "supports_color": false, "hitbox": { "width": 1.2, "height": 2.4 }, "placement": { "snap_to_wall": true, "floor_only": true }, "lockable": true, "break_resistance": 100, "drop_on_break": true, "seats": [ { "id": "main", "armature": "Player_main", "blocked_regions": ["ARMS", "HANDS", "LEGS", "FEET"], "lockable": true, "locked_difficulty": 150, "item_difficulty_bonus": true, "sit_animation": "tiedup:furniture_cross_sit" } ], "category": "restraint", "icon": "tiedup:item/chain"}| Field | Required | Default | Notes |
|---|---|---|---|
id | yes | — | A namespace:path ResourceLocation. Invalid → the whole definition is rejected. |
display_name | yes | — | Non-empty fallback name; pair with translation_key for lang files. |
translation_key | no | — | Lang key for the placer item’s name. |
model | yes | — | GLB ResourceLocation. Invalid → rejected. |
tint_channels | no | {} | name → "#RRGGBB". Strict hex (must be # + 6 digits); a bad value rejects the whole furniture — see the caveat below. |
supports_color | no | false | ⚠️ Partial Parsed and stored, but player dye-recolour is not wired (see below). |
hitbox | no | 1.0 × 1.0 | { width, height }, each clamped to [0.1, 5.0]. |
placement | no | — | snap_to_wall (default false), floor_only (default true). |
lockable | no | false | Whether the piece can be locked; also the default for each seat’s own lockable. |
break_resistance | no | 100 | Stored float, clamped to [1, 10000]. |
drop_on_break | no | true | Whether the placer item drops when broken. |
seats | yes | — | Non-empty array, 1–8 entries. See below. |
feedback | no | — | Optional sound bundle — see Feedback sounds. |
category | no | "furniture" | ⚠️ Partial Parsed and stored, but creative-tab grouping is not yet wired — the label has no effect in-game yet. |
icon | no | — | Inventory sprite (an item-model ResourceLocation, e.g. tiedup:item/chain). |
Each entry in seats is one place a player can sit. Up to 8 seats per piece (a hard limit). A seat
id must not contain :, |, or ; — those characters are reserved by the seat-assignment sync
encoding and will get the whole furniture rejected.
| Seat field | Required | Default | Notes |
|---|---|---|---|
id | yes | — | Unique within this piece (e.g. "main", "left"). No :, |, ;. |
armature | yes | — | A GLB node-name prefix (e.g. "Player_main") used to locate the seat’s placement node inside the mesh. This is not a rig/armature id. |
blocked_regions | no | [] | Body regions the seat physically controls. An unknown region name is fatal (rejects the furniture). See Bones & regions. |
lockable | no | inherits top-level | Whether this seat can be key-locked. |
locked_difficulty | no | 1 | Struggle resistance when locked, clamped to [1, 10000]. |
item_difficulty_bonus | no | false | If true, restraint items on the player’s non-blocked regions add to escape difficulty. |
sit_animation | no | — | The seated pose. A FULL_BODY EF-JSON clip — see below. |
sit_animationThis is the part most people get wrong. The pose a player snaps into when they mount a seat is not
baked into the furniture mesh — it comes from the seat’s sit_animation: a FULL_BODY rig clip,
authored against the EF biped exactly like a bondage item’s animation, and bound to the
POSE_FURNITURE_SEAT motion when the player sits.
constructor block to the clip JSON (the addon never emits it). 🔧 Manual / tool-gap"sit_animation": "tiedup:furniture_cross_sit".If you omit sit_animation, the seated player falls back to the standing IDLE pose — they will
appear to stand inside the furniture, not sit. There is no automatic seat pose; if you want a
sitting figure, you must author and reference a clip.
The optional feedback block attaches sound effects to seat events. All six keys are wired and play
at runtime: mount_sound, lock_sound/unlock_sound, denied_sound from the furniture entity, and
struggle_loop_sound/escape_sound from the struggle minigame. Each value is a plain ResourceLocation.
"feedback": { "mount_sound": "minecraft:block.chain.place", "lock_sound": "minecraft:block.iron_door.close", "unlock_sound": "minecraft:block.iron_door.open", "struggle_loop_sound": "minecraft:block.chain.hit", "escape_sound": "minecraft:entity.player.attack.sweep", "denied_sound": "minecraft:block.note_block.bass"}supports_color) is not wiredsupports_color and the tint_channels defaults are parsed and stored, and the mesh renders with those
default channel colours. But player-driven dye recolouring of a placed piece is not implemented —
setting supports_color: true does not yet let a player change the colour in-world. Treat
tint_channels as author-time defaults only. (As with items, author tintable textures in grayscale
so the tint reads correctly, and name the tinted material tintable_*.)
Validation is strict. Anything in this list rejects the entire definition (logged as an error, the piece simply doesn’t load):
id, display_name, or model;tint_channels hex value;seats array, or more than 8 seats;id or armature, a seat id containing : | ;, or an unknown body region.Optional numeric fields (hitbox, break_resistance, locked_difficulty) are clamped rather than
rejected, so out-of-range values are quietly pulled into bounds.
To be blunt so nobody wastes time: pets cannot be modded from a datapack. There is no pet definition format, no JSON schema, and no registry hook.
The mod ships exactly four hardcoded blocks — pet_bowl, pet_bed, pet_cage, and
pet_cage_part — each with its model, texture, and scale baked into a Java block-entity subclass.
There is no data path to add a new pet block, repoint an existing one at a different model, or change its
behaviour without writing Java and recompiling the mod.
See the Planned / Not yet page for the full list of code-only subsystems.
Furniture ✅ moddable
Definition JSON + a .glb mesh (built-in glTF exporter) + 1–8 seats. The seated pose is the
seat’s sit_animation clip. No Java required.
Pets ❌ code-only
Four hardcoded blocks with baked models. Not datapack-moddable; only resourcepack texture/geometry overrides of existing paths.