fix: precise item classification in catalog audit and repair
Detect badges by items_base.type='b' (authoritative, album gifs as fallback), recognize pet/animals (a0 pet<N>, pet<N> interaction) and system items (effects, bots, sticky notes), and resolve asset names for dot/star classname variants so the audit no longer floods with false missing nitro/icon errors and repair skips non-furni items.
This commit is contained in:
1 parent
b1a1e5125c
commit
d587749b50
5 files changed
+445
-36
No files matched your search
@@ -14,6 +14,7 @@ import {
|
||||
getGamedataRoot,
|
||||
} from "@/lib/services/furni-asset-dirs";
|
||||
import { downloadFile } from "@/lib/services/import/core/download";
|
||||
import { classifyCatalogItem, NO_ASSET_KINDS } from "@/lib/services/item-kind";
|
||||
import { parseNitroBundle } from "@/lib/services/swf/nitro-builder";
|
||||
import { ensureDirectories } from "./furni-import";
|
||||
|
||||
@@ -137,28 +138,43 @@ export async function repairMissingNitros(
|
||||
...targets.mirrorDirs.map((d) => d.nitroDir),
|
||||
]);
|
||||
|
||||
// Load known badge codes from the album directory so we can skip badge
|
||||
// items — they use .gif files, not .nitro bundles.
|
||||
let badgeSet: Set<string> | null = null;
|
||||
// Badges and other special items (pets, effects, bots, sticky notes) never
|
||||
// ship furni `.nitro` bundles — resolve the skip set from the DB so badge
|
||||
// detection is precise (`items_base.type = 'b'`) instead of relying only on
|
||||
// which .gif files happen to exist in the album directory.
|
||||
let albumBadgeSet: Set<string> | null = null;
|
||||
try {
|
||||
const gamedataRoot = await getGamedataRoot();
|
||||
if (gamedataRoot) {
|
||||
const badgeFiles = await fs
|
||||
.readdir(path.join(gamedataRoot, "album1584"))
|
||||
.catch(() => [] as string[]);
|
||||
badgeSet = new Set(
|
||||
albumBadgeSet = new Set(
|
||||
badgeFiles.filter((f) => f.endsWith(".gif")).map((f) => f.slice(0, -4)),
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
badgeSet = null;
|
||||
albumBadgeSet = null;
|
||||
}
|
||||
|
||||
let items: Array<{ id: number; item_name: string }>;
|
||||
let items: Array<{
|
||||
id: number;
|
||||
item_name: string;
|
||||
type: string;
|
||||
interaction_type: string;
|
||||
}>;
|
||||
try {
|
||||
const [rows] = (await db.execute(sql`
|
||||
SELECT id, item_name FROM items_base ORDER BY id
|
||||
`)) as unknown as [Array<{ id: number; item_name: string }>, unknown];
|
||||
SELECT id, item_name, type, interaction_type FROM items_base ORDER BY id
|
||||
`)) as unknown as [
|
||||
Array<{
|
||||
id: number;
|
||||
item_name: string;
|
||||
type: string;
|
||||
interaction_type: string;
|
||||
}>,
|
||||
unknown,
|
||||
];
|
||||
items = rows;
|
||||
} catch (err) {
|
||||
onEvent?.({
|
||||
@@ -168,12 +184,24 @@ export async function repairMissingNitros(
|
||||
return { succeeded: 0, failed: 0, skipped: 0 };
|
||||
}
|
||||
|
||||
// Build the authoritative skip set from the DB, supplemented by album .gifs.
|
||||
const skipSet = new Set<string>();
|
||||
for (const item of items) {
|
||||
if (NO_ASSET_KINDS.has(classifyCatalogItem(item))) {
|
||||
skipSet.add(item.item_name);
|
||||
}
|
||||
}
|
||||
for (const code of albumBadgeSet ?? []) {
|
||||
skipSet.add(code);
|
||||
}
|
||||
|
||||
const total = items.length;
|
||||
onEvent?.({ type: "started", total });
|
||||
|
||||
const processItem = async (classname: string) => {
|
||||
// Badge items use .gif files, not .nitro bundles.
|
||||
if (badgeSet?.has(classname) ?? false) {
|
||||
// Special items (badges, pets, effects, bots, sticky notes) don't have
|
||||
// furni .nitro bundles to repair. Skip them.
|
||||
if (skipSet.has(classname)) {
|
||||
return { status: "skipped" } as const;
|
||||
}
|
||||
const fileName = `${classname}.nitro`;
|
||||
@@ -229,6 +257,11 @@ export async function repairMissingNitros(
|
||||
`${classname.replace(/\*/g, "_")}.nitro`,
|
||||
]
|
||||
: []),
|
||||
// Classnames with dots (`post.it`) are stored on disk with
|
||||
// underscores (`post_it.nitro`).
|
||||
...(classname.includes(".")
|
||||
? [`${classname.replace(/\./g, "_")}.nitro`]
|
||||
: []),
|
||||
];
|
||||
|
||||
for (const source of sources) {
|
||||
|
||||
Reference in new issue
Block a user