Fix badge icon detection in catalog audit and repair

Badge icons are stored as badge_<code>_icon.png (with badge_ prefix)
instead of <code>_icon.png like regular furni. Update the audit and
icon repair to check for both patterns so badge items are not falsely
flagged as missing icons.
This commit is contained in:
openhands committed 2026-08-03 21:34:11 +02:00
1 parent e97213e5d1
commit 40da24bd8c
2 files changed
+17 -19

No files matched your search

+5 -3
View File
@@ -344,9 +344,11 @@ export async function runCatalogAudit(
const missingIconClassnames: string[] = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
// Badge items use .gif icons, not .png icons.
if (item.interaction_type === "badge") continue;
if (!iconSet.has(`${item.item_name}_icon.png`)) {
// Badge items use .gif icons stored as badge_<code>_icon.png,
// not <code>_icon.png like regular furni.
const iconFileName = `${item.item_name}_icon.png`;
const badgeIconFileName = `badge_${item.item_name}_icon.png`;
if (!iconSet.has(iconFileName) && !iconSet.has(badgeIconFileName)) {
missingIconClassnames.push(item.item_name);
issues.push({
type: "missing_icon",
+12 -16
View File
@@ -140,14 +140,11 @@ export async function repairMissingIcons(
...targets.mirrorDirs.map((d) => d.nitroDir),
]);
let items: Array<{ id: number; item_name: string; interaction_type: string }>;
let items: Array<{ id: number; item_name: string }>;
try {
const [rows] = (await db.execute(sql`
SELECT id, item_name, interaction_type FROM items_base ORDER BY id
`)) as unknown as [
Array<{ id: number; item_name: string; interaction_type: string }>,
unknown,
];
SELECT id, item_name FROM items_base ORDER BY id
`)) as unknown as [Array<{ id: number; item_name: string }>, unknown];
items = rows;
} catch (err) {
onEvent?.({
@@ -160,17 +157,15 @@ export async function repairMissingIcons(
const total = items.length;
onEvent?.({ type: "started", total });
const processItem = async (classname: string, interactionType: string) => {
// Badge items use .gif icons, not .png icons.
if (interactionType === "badge") {
return { status: "skipped" } as const;
}
const processItem = async (classname: string) => {
const fileName = `${classname}_icon.png`;
const badgeFileName = `badge_${classname}_icon.png`;
// A dir may already have the icon (e.g. the full gamedata) while the
// webroot is missing it — treat the item as present when every target has it.
const presentDirs = iconDirs.filter((dir) =>
existsSync(path.join(dir, fileName)),
// Badge items store icons as badge_<code>_icon.png, not <code>_icon.png.
const presentDirs = iconDirs.filter(
(dir) =>
existsSync(path.join(dir, fileName)) ||
existsSync(path.join(dir, badgeFileName)),
);
const missingDirs = iconDirs.filter((dir) => !presentDirs.includes(dir));
@@ -244,6 +239,7 @@ export async function repairMissingIcons(
for (const source of sources) {
const iconCandidates = [
`${classname}_icon.png`,
`badge_${classname}_icon.png`,
// Variant classnames (e.g. `table_silo_med*10`) often only exist
// on sources under their base name (e.g. `table_silo_med_icon.png`)
// or with the `*` replaced by `_` (e.g. `table_silo_med_10_icon.png`).
@@ -333,7 +329,7 @@ export async function repairMissingIcons(
await runPool(items, REPAIR_CONCURRENCY, async (item, i) => {
const classname = item.item_name;
const result = await processItem(classname, item.interaction_type);
const result = await processItem(classname);
if (result.status === "skipped") {
skipped++;
onEvent?.({