fix: furniture interaction detection and automatic catalog offer_id rebuild

Interaction detection:
- Trust SWF-derived sit/lay/stand flags only when the logic XML actually
  contains action data (new hasActions metadata); otherwise fall back to
  keyword detection so custom furni without <action> nodes are still
  classified correctly
- Add French/Dutch/German/Spanish/Italian keywords (chaise, banquette,
  stoel, silla, sedia, stuhl, tafel, mesa, ...) to sit/lay/stand detection
  with token-boundary matching to avoid false positives like bedside_table
- Unify interaction_modes_count priority: mechanic fixed modes, then raw
  animation state count, then sit/lay fallback, then keyword default
- Detect mechanic type even when real flags are not used

Catalog integrity:
- Skip duplicate catalog_items inserts in clone and upload imports
- Re-check live catalog rows before applying generated repair SQL
- Add rebuildCatalogOfferIds() which rebuilds every catalog_items.offer_id
  from the local FurnitureData.json (matched by entry id, then classname)
  and run it after every import path (single, batch, batch-regen, clone)
This commit is contained in:
openhands committed 2026-08-21 13:39:21 +02:00
1 parent 652d331402
commit 4b7cb519df
14 files changed
+418 -58

No files matched your search

+21 -10
View File
@@ -848,10 +848,21 @@ export async function importSingleFurni(params: {
: { x: 1, y: 1, z: 0 });
const meta = conversionResult?.metadata ?? null;
// Prefer .nitro flags, then the configured official Habbo furnidata.
const cansiton = meta?.cansiton ?? officialHabbo?.cansiton ?? false;
const canlayon = meta?.canlayon ?? officialHabbo?.canlayon ?? false;
const canstandon = meta?.canstandon ?? officialHabbo?.canstandon ?? false;
// Real sit/lay/stand flags: prefer .nitro logic flags, then the configured
// official Habbo furnidata. Only pass them when a real source exists —
// defaulted all-false values must not suppress keyword detection (custom
// SWFs often ship without <action> nodes even for sittable furniture).
const realSource = meta ?? officialHabbo;
const realFlags = realSource
? {
cansiton: realSource.cansiton ?? false,
canlayon: realSource.canlayon ?? false,
canstandon: realSource.canstandon ?? false,
// Official furnidata is curated → authoritative. SWF-derived flags
// are only authoritative when the logic actually had <action> nodes.
hasActionData: meta ? (meta.hasActions ?? true) : true,
}
: undefined;
// Derive interaction_type. Priority:
// 1. Wired LOGIC items (wf_act_/wf_trg_/wf_cnd_/wf_slc_/wf_xtra_/wf_var_)
@@ -903,9 +914,7 @@ export async function importSingleFurni(params: {
// no .nitro JSON available — keyword fallback only
}
const autoInteraction = autoDetectInteraction(classname, bestName, {
cansiton,
canlayon,
canstandon,
...realFlags,
logicType,
animationStates: nitroAnimationStatesCount(nitroJsonForModes),
});
@@ -929,9 +938,9 @@ export async function importSingleFurni(params: {
const update = buildItemsBaseEnrichmentUpdate({
id: spriteId,
dims,
cansiton,
canlayon,
canstandon,
cansiton: autoInteraction.canSit,
canlayon: autoInteraction.canLay,
canstandon: autoInteraction.canStand,
allowStack,
interactionType,
interactionModesCount,
@@ -1123,6 +1132,8 @@ export async function verifyAndFixInteractionModesCount(): Promise<{
cansiton: realFlags.cansiton,
canlayon: realFlags.canlayon,
canstandon: realFlags.canstandon,
// Furnidata flags are curated → authoritative over keywords.
hasActionData: true,
animationStates: animationModes,
});