experimental/curios-ui-fork #22

Merged
NotEvil merged 35 commits from experimental/curios-ui-fork into develop 2026-07-06 20:05:30 +00:00
Owner
No description provided.
The pre-rig clothes overlay (ClothesRenderHelper) was never mounted — its
host layer never existed — so GenericClothes had zero visual effect since
the patched-renderer switch. New PatchedClothesLayer re-draws the biped
mesh (BIPED/ALEX, CPU-posed for per-part hidden flags) with the item's
downloaded texture, legacy applyLayerVisibility semantics preserved
(keepHead, 6-part bitfield, smallArms). Config is read straight from the
equipped stack NBT, which already reaches every client (capability packet
for players, SynchedEntityData for NPCs) — no dedicated sync needed.
ClothesRenderHelper (613L, zero callers since the patched-renderer switch)
and ClothesModelCache go away with the PatchedClothesLayer port. The
dedicated clothes sync channel (PacketSyncClothesConfig + ClothesClientCache
+ SyncManager.syncClothesConfig + IPlayerBindStateHost.syncClothesConfig)
was a second pipe for data the equipment sync already carries in the stack
NBT — every call site fired right after an equipment sync on the same
change. Network protocol 6 -> 7 (packet removed).
Fork of Curios 5.14.1 GUI machinery (LGPL-3 -> GPLv3, attribution headers)
rewired onto the bondage equipment capability. The player gets a small button
in the inventory (and the existing keybind) that opens a vanilla-inventory
look-alike with a left panel of one slot per BodyRegion:

- BondageRegionItemHandler: IItemHandler VIEW over IBondageEquipmentStore,
  same rules as the packet paths (region occupancy/blocking on place;
  locked / self-ARMS / InteractionGuard FREE on take). Locked items refuse
  the drag-out at the slot level.
- BondageInventoryMenu/-Screen: crafting 2x2 + recipe book + armor/offhand
  kept, so it feels like the vanilla inventory; fixed 2x7 region panel.
- Open/close via PacketOpenBondageInventory (doCloseContainer on the way
  back, protocol 7 -> 8). MASTER mode keeps UnifiedBondageScreen for now.
- Panel/button textures copied from Curios (see file headers).

Build + tests + quality gate green. Not yet validated in-game.
- padlock overlay (8x8, from a pixel-art icon) on locked items + red
  tooltip line ; slider glyph on items with an adjustable position
  (MOUTH/EYES, same rule as the ActionPanel) + gray tooltip line
- empty slots show their region as a monochrome silhouette glyph
  (vanilla empty-armor-slot style, 14 hand-drawn 16x16 sprites on one
  sheet) — deliberately NOT a ghost item, which could be mistaken for
  real content
- region blocked by another item (e.g. hood -> mouth/ears) = translucent
  red cell + 'Blocked by <item>' tooltip
- bondage button on the creative inventory tab (10px sprite, hidden on
  other tabs)

Build + tests + quality gate green.
- PLUG absorbs the former TAIL (tail items are plug-mounted), WINGS was
  never used. BodyRegion.fromName already null-skips stale names, so old
  save NBT with TAIL/WINGS entries is dropped silently.
- COSTUME lands the dedicated cosmetic region decided in the worn-
  cosmetics scoping (non-restraining overlay slot).
- bondage inventory panel goes 2x7 -> 2x8 (texture already carries 8
  cells per column) ; region tab bar regroups LOWER and SPECIAL.
- region glyph sheet regenerated: briefs (CROTCH), venus (PUSSY), plug
  silhouette (PLUG), hanger (COSTUME).

Build + tests + quality gate green.
Clothes (GenericClothes) are a pure skin swap, not a garment: they now live
in a dedicated SKIN region instead of squatting TORSO, so a re-skinned
player can still wear torso bondage. COSTUME stays the slot for actual
outfit items — data-driven like every other restraint (bunny_suit.json
pilot, occupies COSTUME, blocks nothing, renders through the worn-GLB
path once its asset lands). Neither cosmetic region blocks anything.
Legacy NPC NBT 'Clothes' migrates to SKIN; old saves with clothes still
in TORSO keep rendering via the layer's instanceof scan.
New optional keys on item and furniture definitions:
  "texture": "..."              static override
  "texture_frames": [...]        one PNG per frame, cycled on the carrier's tickCount
  "texture_frame_ticks": 2       frame duration
Shared TextureConfig record (parse + per-tick resolve); RenderTypes are
already cached per ResourceLocation so cycling costs nothing. Worn items
finally have a working texture path — the broken embedded base-color stays
disabled, an explicit override bypasses it. Furniture keeps its embedded
texture as fallback. Furniture sync codec extended (items re-sync raw JSON,
no codec change needed).
A worn GLB can now play its OWN internal clip (pumping piston, spinning
gears) while riding one biped bone rigidly, instead of being skinned onto
the wearer's armature:

  "model_animation": {
    "clip": "Pump",          // default: the GLB's first clip
    "mount_joint": "Chest",  // required
    "speed": 1.0              // frames per tick, like furniture
  }

- ModelAnimation record + lenient fromJson (mount_joint required, speed
  clamped, parser warns on malformed blocks instead of silently eating
  them) ; IBondageItem.getModelAnimation hook
- PatchedBondageLayer: animated branch reuses the furniture time model
  (wearer tickCount, frame-space loop) + RenderMathUtils.mulStack on the
  mount bone ; named clip falls back to first clip, no clip = static
  rigid mount (free props) ; unknown joint = warn-once + skip
- previewer: setItemModelAnimation — the item clip follows play/scrub so
  artists can validate without launching the game
- 12 unit tests ; authoring convention documented (GLB modeled in the
  mount bone's local frame, origin = attach point)

Build + tests + quality gate green. Needs a pilot GLB with a named clip
for in-game validation (previewtest pack).
Adverse review of the whole branch (8 finder angles + verification) found
the 17th region (SKIN) overflowing everything sized for 16, plus a batch
of smaller issues:

- FINGERS region removed (never used by any item) — SKIN takes its slot,
  back to 16 regions ; PANEL_ROWS is now DERIVED from the region count so
  a future region cannot fall off the drawn backplate again, and the
  region-glyph blit is bounds-guarded against the sheet capacity
- region_icons.png rebuilt: FINGERS ring dropped, paintbrush glyph for
  SKIN, ordinals re-aligned
- silent-refusal UX: the padlock overlay now covers EVERYTHING the
  extract gate refuses (locked or self-ARMS) with a distinct
  'Struggle to remove' tooltip line for the non-locked case
- PatchedClothesLayer: two O(1) getInRegion reads (SKIN, legacy TORSO)
  instead of getAllEquipped()'s 3 map allocations per wearer per frame,
  and ClothesProperties parsed once per stack instance instead of per
  frame (identity cache, safe client-side)
- bondage screen: blocked regions computed once per frame instead of one
  full store scan per empty slot
- BondageRegionItemHandler: insertItem funnels through setStackInSlot
  (single write path) and the no-validation contract of setStackInSlot
  is documented
- TextureConfig: frame-count cap enforced in the constructor — the
  furniture sync encoder wrote the count verbatim while the decoder
  clamped, a latent packet-desync if any non-fromJson source appeared
- previewer C1-a branch reverted (no caller; needs pack-editor fields)

Deliberately NOT fixed (user call): legacy TORSO->SKIN clothes NBT and
removed-region item loss on old saves.

Build + tests + quality gate green.
Player-facing, the two cosmetic regions are 'Clothes' (skin swap) and
'Costume' (3D outfit) — 'Skin' was the internal mechanic name leaking
into the UI. Enum stays SKIN.
Shift-right-click a clothes item (in the air) opens a small dialog with an
EditBox pre-filled with the current URL + Save/Reset/Cancel, instead of
having to type /tiedup clothes url set <url>.

Permission keeps the command's security rationale (an arbitrary URL is
fetched by every other client that sees the wearer, host-whitelisted
client-side): the screen opens for everyone but the server only accepts
the set from operators UNLESS the new server config allowPlayerClothesUrl
is true (default false). The server re-validates https + length; the
client screen is convenience only, never the boundary.

- ModConfig.SERVER.allowPlayerClothesUrl (default false)
- PacketSetClothesUrl (C2S set/reset, permission + https/length gate,
  writes the held stack — vanilla inventory sync + the equip path carry
  the NBT to trackers)
- ClothesUrlScreen + ClientClothesActions (client-only, referenced from
  GenericClothes.use only behind isClientSide)
- protocol 8 -> 9

Build + tests + quality gate green.
NotEvil merged commit 32d282a57c into develop 2026-07-06 20:05:30 +00:00
Sign in to join this conversation.
No reviewers
No labels
Bug
Suggestion
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
NotEvil/TiedUp-!22
No description provided.