fix: normalize cross-section duplicate ids in furnidata reconcile
Classnames listed in both roomitemtypes and wallitemtypes keep a canonical copy with the correct id/offerid while the stray wall copy can carry a legacy wrong id. reconcileFurniDataWithItemsBase now normalizes those stray copies to the offerid whenever the offerid maps to an items_base row whose classname matches exactly, so id === offerid === sprite id holds for every uniquely-mapped classname. The sandbox rehearsal asserts this invariant and the live database has been reconciled (76 ids fixed, 0 remaining).
This commit is contained in:
1 parent
3a63152ee3
commit
b811086de6
2 files changed
+48
No files matched your search
@@ -230,6 +230,36 @@ describeGuard("catalog studio live repair rehearsal (sandbox)", () => {
|
|||||||
const offer = await reconcileImportedOfferIds();
|
const offer = await reconcileImportedOfferIds();
|
||||||
console.log("REHEARSAL offer reconcile:", JSON.stringify(offer));
|
console.log("REHEARSAL offer reconcile:", JSON.stringify(offer));
|
||||||
|
|
||||||
|
// Every furnidata entry whose classname maps to a unique items_base row
|
||||||
|
// must have id === offerid === db id (incl. cross-section copies).
|
||||||
|
const [uniqueRows] = (await db.execute(sql`
|
||||||
|
SELECT id, item_name FROM items_base
|
||||||
|
`)) as unknown as [Array<{ id: number; item_name: string }>, unknown];
|
||||||
|
const dbById = new Map<number, string>();
|
||||||
|
for (const r of uniqueRows) dbById.set(Number(r.id), r.item_name);
|
||||||
|
const byName = new Map<string, number>();
|
||||||
|
for (const r of uniqueRows)
|
||||||
|
byName.set(r.item_name, (byName.get(r.item_name) ?? 0) + 1);
|
||||||
|
const finalFd = JSON.parse(
|
||||||
|
await fs.readFile(
|
||||||
|
path.join(gamedataDir, "config", "FurnitureData.json"),
|
||||||
|
"utf8",
|
||||||
|
),
|
||||||
|
) as Record<string, { furnitype: Array<Record<string, unknown>> }>;
|
||||||
|
let badCrossSection = 0;
|
||||||
|
for (const section of ["roomitemtypes", "wallitemtypes"] as const) {
|
||||||
|
for (const e of finalFd[section]?.furnitype ?? []) {
|
||||||
|
const cn = String(e.classname ?? "");
|
||||||
|
if (byName.get(cn) !== 1) continue;
|
||||||
|
const offer = Number(e.offerid);
|
||||||
|
if (!Number.isFinite(offer) || offer <= 0) continue;
|
||||||
|
if (dbById.get(offer) !== cn) continue;
|
||||||
|
if (Number(e.id) !== offer) badCrossSection++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("REHEARSAL cross-section id mismatches:", badCrossSection);
|
||||||
|
expect(badCrossSection).toBe(0);
|
||||||
|
|
||||||
const afterState = await audit();
|
const afterState = await audit();
|
||||||
console.log("REHEARSAL after:", JSON.stringify(afterState));
|
console.log("REHEARSAL after:", JSON.stringify(afterState));
|
||||||
|
|
||||||
|
|||||||
@@ -536,6 +536,24 @@ export async function reconcileFurniDataWithItemsBase(): Promise<FurniDataReconc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize cross-section duplicates (e.g. a wall item listed in both
|
||||||
|
// roomitemtypes and wallitemtypes). The canonical copy already has the
|
||||||
|
// correct id/offerid; a stray copy may carry a legacy wrong `id` while
|
||||||
|
// its offerid still points at the right sprite. Only fix when the
|
||||||
|
// offerid maps to an items_base row whose classname matches this entry
|
||||||
|
// exactly, so we never guess.
|
||||||
|
const dbById = new Map<number, string>();
|
||||||
|
for (const row of rows) dbById.set(row.id, row.itemName);
|
||||||
|
for (const section of ["roomitemtypes", "wallitemtypes"] as const) {
|
||||||
|
for (const e of furniData[section]?.furnitype ?? []) {
|
||||||
|
const offer = entryOfferId(e);
|
||||||
|
if (offer <= 0 || Number(e.id) === offer) continue;
|
||||||
|
if (dbById.get(offer) !== e.classname) continue;
|
||||||
|
e.id = offer;
|
||||||
|
fixedIds++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fixedIds > 0 || fixedOfferIds > 0) {
|
if (fixedIds > 0 || fixedOfferIds > 0) {
|
||||||
await writeFurniData(furniData as Record<string, unknown>);
|
await writeFurniData(furniData as Record<string, unknown>);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user