Skip to content

Furniture & Pets

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):

  • The definition JSON, under data/<namespace>/tiedup_furniture/<name>.json.
  • The GLB mesh, under assets/, exported with Blender’s built-in File → Export → glTF 2.0 (.glb) exporter (the same tool as item meshesnot the EF-JSON addon), plus the inventory icon and any seated-pose animation clips.
data/<ns>/tiedup_furniture/<name>.json ← the furniture definition
assets/<ns>/models/gltf/furniture/<name>.glb ← the mesh (baked texture inside)
assets/<ns>/animmodels/animations/<sit_clip>.json ← seated-pose clip(s), EF-JSON

This 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"
}
FieldRequiredDefaultNotes
idyesA namespace:path ResourceLocation. Invalid → the whole definition is rejected.
display_nameyesNon-empty fallback name; pair with translation_key for lang files.
translation_keynoLang key for the placer item’s name.
modelyesGLB ResourceLocation. Invalid → rejected.
tint_channelsno{}name → "#RRGGBB". Strict hex (must be # + 6 digits); a bad value rejects the whole furniture — see the caveat below.
supports_colornofalse⚠️ Partial Parsed and stored, but player dye-recolour is not wired (see below).
hitboxno1.0 × 1.0{ width, height }, each clamped to [0.1, 5.0].
placementnosnap_to_wall (default false), floor_only (default true).
lockablenofalseWhether the piece can be locked; also the default for each seat’s own lockable.
break_resistanceno100Stored float, clamped to [1, 10000].
drop_on_breaknotrueWhether the placer item drops when broken.
seatsyesNon-empty array, 1–8 entries. See below.
feedbacknoOptional sound bundle — see Feedback sounds.
categoryno"furniture"⚠️ Partial Parsed and stored, but creative-tab grouping is not yet wired — the label has no effect in-game yet.
iconnoInventory 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 fieldRequiredDefaultNotes
idyesUnique within this piece (e.g. "main", "left"). No :, |, ;.
armatureyesA 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_regionsno[]Body regions the seat physically controls. An unknown region name is fatal (rejects the furniture). See Bones & regions.
lockablenoinherits top-levelWhether this seat can be key-locked.
locked_difficultyno1Struggle resistance when locked, clamped to [1, 10000].
item_difficulty_bonusnofalseIf true, restraint items on the player’s non-blocked regions add to escape difficulty.
sit_animationnoThe seated pose. A FULL_BODY EF-JSON clip — see below.
✅ Available

This 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.

  1. Author the seated pose as an EF-JSON clip against the 20-bone biped — same workflow as any item animation (see Animations).
  2. Hand-add the constructor block to the clip JSON (the addon never emits it). 🔧 Manual / tool-gap
  3. Reference it from the seat: "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.

✅ Available

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"
}
⚠️ Partial

supports_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):

  • missing/invalid id, display_name, or model;
  • a malformed tint_channels hex value;
  • a missing or empty seats array, or more than 8 seats;
  • a seat missing 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.

❌ Planned

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 blockspet_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.