feat: auto-repair sprite_id drift after every import
The emulator sends items_base.sprite_id to the client and Nitro resolves the furniture by looking up that id in FurnitureData.json. Legacy rows where sprite_id drifted from the id made the client render a completely different item (e.g. a wired or custom asset instead of the purchased furniture). Add verifyAndFixSpriteIds() which repairs any row whose sprite_id no longer matches its id while the local furnidata carries the item under that id, and run it after every import path (single, batch, batch-regen, clone) next to the offer_id rebuild.
This commit is contained in:
1 parent
92b0ba3b18
commit
984b5bf793
5 files changed
+84
No files matched your search
@@ -656,3 +656,61 @@ export async function rebuildCatalogOfferIds(): Promise<CatalogOfferRebuildResul
|
||||
return { checked: catalogRows.length, fixed, fallback };
|
||||
});
|
||||
}
|
||||
|
||||
export interface SpriteIdVerifyResult {
|
||||
/** items_base rows examined. */
|
||||
checked: number;
|
||||
/** Rows whose sprite_id was corrected to their id. */
|
||||
fixed: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that every items_base.sprite_id matches its id whenever the local
|
||||
* FurnitureData.json carries the item under that id.
|
||||
*
|
||||
* The emulator sends sprite_id to the client and Nitro resolves the furniture
|
||||
* by looking up that id in the furnidata — a stale sprite_id makes the client
|
||||
* render a completely different item (e.g. a wired or a custom in place of a
|
||||
* seaside sofa). Imports always write sprite_id = id, but legacy rows may
|
||||
* drift; this pass repairs them after every import.
|
||||
*/
|
||||
export async function verifyAndFixSpriteIds(): Promise<SpriteIdVerifyResult> {
|
||||
return withFurniDataLock(async () => {
|
||||
const furniData = (await readFurniData()) as Record<
|
||||
string,
|
||||
{ furnitype?: Array<Record<string, unknown>> }
|
||||
>;
|
||||
const classById = new Map<number, Set<string>>();
|
||||
for (const section of ["roomitemtypes", "wallitemtypes"] as const) {
|
||||
for (const e of furniData[section]?.furnitype ?? []) {
|
||||
const entryId = Number(e?.id);
|
||||
const cn = typeof e?.classname === "string" ? e.classname : "";
|
||||
if (!Number.isFinite(entryId) || !cn) continue;
|
||||
const set = classById.get(entryId) ?? new Set<string>();
|
||||
set.add(cn);
|
||||
classById.set(entryId, set);
|
||||
}
|
||||
}
|
||||
|
||||
const rows = await db
|
||||
.select({
|
||||
id: ItemsBase.id,
|
||||
spriteId: ItemsBase.spriteId,
|
||||
itemName: ItemsBase.itemName,
|
||||
})
|
||||
.from(ItemsBase);
|
||||
|
||||
let fixed = 0;
|
||||
for (const row of rows) {
|
||||
if (row.spriteId === row.id) continue;
|
||||
const entries = classById.get(row.id);
|
||||
if (!entries?.has(row.itemName)) continue;
|
||||
await db
|
||||
.update(ItemsBase)
|
||||
.set({ spriteId: row.id })
|
||||
.where(eq(ItemsBase.id, row.id));
|
||||
fixed++;
|
||||
}
|
||||
return { checked: rows.length, fixed };
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user