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();
|
||||
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();
|
||||
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) {
|
||||
await writeFurniData(furniData as Record<string, unknown>);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user