This commit is contained in:
1 parent
8efd032cc6
commit
df38dccbf1
735 files changed
+128321
-120870
No files matched your search
@@ -18,145 +18,149 @@ let loading: Promise<Map<string, HabboItFurniEntry>> | null = null;
|
||||
* Fetches and parses the full habbo.it furnidata JSON.
|
||||
* Results are cached in memory for 30 minutes.
|
||||
*/
|
||||
export async function getHabboItFurnidata(): Promise<Map<string, HabboItFurniEntry>> {
|
||||
const now = Date.now();
|
||||
export async function getHabboItFurnidata(): Promise<
|
||||
Map<string, HabboItFurniEntry>
|
||||
> {
|
||||
const now = Date.now();
|
||||
|
||||
// Return cache if still valid
|
||||
if (cache && now - cacheTimestamp < CACHE_TTL) {
|
||||
return cache;
|
||||
}
|
||||
// Return cache if still valid
|
||||
if (cache && now - cacheTimestamp < CACHE_TTL) {
|
||||
return cache;
|
||||
}
|
||||
|
||||
// Deduplicate concurrent requests
|
||||
if (loading) return loading;
|
||||
// Deduplicate concurrent requests
|
||||
if (loading) return loading;
|
||||
|
||||
loading = (async () => {
|
||||
try {
|
||||
console.debug("[habbo-it] Fetching furnidata from habbo.it...");
|
||||
const res = await fetch(FURNIDATA_URL, {
|
||||
signal: AbortSignal.timeout(20000),
|
||||
headers: { Accept: "application/json" },
|
||||
});
|
||||
loading = (async () => {
|
||||
try {
|
||||
console.debug("[habbo-it] Fetching furnidata from habbo.it...");
|
||||
const res = await fetch(FURNIDATA_URL, {
|
||||
signal: AbortSignal.timeout(20000),
|
||||
headers: { Accept: "application/json" },
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.warn(`[habbo-it] Fetch failed: ${res.status}`);
|
||||
return cache || new Map();
|
||||
}
|
||||
if (!res.ok) {
|
||||
console.warn(`[habbo-it] Fetch failed: ${res.status}`);
|
||||
return cache || new Map();
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
const map = new Map<string, HabboItFurniEntry>();
|
||||
const data = await res.json();
|
||||
const map = new Map<string, HabboItFurniEntry>();
|
||||
|
||||
// Process room items (floor furniture)
|
||||
const roomTypes = data?.roomitemtypes?.furnitype;
|
||||
if (Array.isArray(roomTypes)) {
|
||||
for (const item of roomTypes) {
|
||||
if (!item.classname) continue;
|
||||
map.set(item.classname, {
|
||||
id: item.id ?? 0,
|
||||
classname: item.classname,
|
||||
revision: item.revision ?? 0,
|
||||
category: item.category ?? "",
|
||||
defaultdir: item.defaultdir ?? 0,
|
||||
xdim: item.xdim ?? 1,
|
||||
ydim: item.ydim ?? 1,
|
||||
partcolors: item.partcolors ?? { color: [] },
|
||||
name: item.name ?? "",
|
||||
description: item.description ?? "",
|
||||
specialtype: item.specialtype ?? 0,
|
||||
canstandon: item.canstandon ?? false,
|
||||
cansiton: item.cansiton ?? false,
|
||||
canlayon: item.canlayon ?? false,
|
||||
furniline: item.furniline ?? "",
|
||||
environment: item.environment ?? "",
|
||||
rare: item.rare ?? false,
|
||||
tradeable: item.tradeable,
|
||||
bc: item.bc,
|
||||
customparams: item.customparams,
|
||||
itemType: "s",
|
||||
});
|
||||
}
|
||||
}
|
||||
// Process room items (floor furniture)
|
||||
const roomTypes = data?.roomitemtypes?.furnitype;
|
||||
if (Array.isArray(roomTypes)) {
|
||||
for (const item of roomTypes) {
|
||||
if (!item.classname) continue;
|
||||
map.set(item.classname, {
|
||||
id: item.id ?? 0,
|
||||
classname: item.classname,
|
||||
revision: item.revision ?? 0,
|
||||
category: item.category ?? "",
|
||||
defaultdir: item.defaultdir ?? 0,
|
||||
xdim: item.xdim ?? 1,
|
||||
ydim: item.ydim ?? 1,
|
||||
partcolors: item.partcolors ?? { color: [] },
|
||||
name: item.name ?? "",
|
||||
description: item.description ?? "",
|
||||
specialtype: item.specialtype ?? 0,
|
||||
canstandon: item.canstandon ?? false,
|
||||
cansiton: item.cansiton ?? false,
|
||||
canlayon: item.canlayon ?? false,
|
||||
furniline: item.furniline ?? "",
|
||||
environment: item.environment ?? "",
|
||||
rare: item.rare ?? false,
|
||||
tradeable: item.tradeable,
|
||||
bc: item.bc,
|
||||
customparams: item.customparams,
|
||||
itemType: "s",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Process wall items
|
||||
const wallTypes = data?.wallitemtypes?.furnitype;
|
||||
if (Array.isArray(wallTypes)) {
|
||||
for (const item of wallTypes) {
|
||||
if (!item.classname) continue;
|
||||
map.set(item.classname, {
|
||||
id: item.id ?? 0,
|
||||
classname: item.classname,
|
||||
revision: item.revision ?? 0,
|
||||
category: item.category ?? "",
|
||||
defaultdir: item.defaultdir ?? 0,
|
||||
xdim: item.xdim ?? 1,
|
||||
ydim: item.ydim ?? 1,
|
||||
partcolors: item.partcolors ?? { color: [] },
|
||||
name: item.name ?? "",
|
||||
description: item.description ?? "",
|
||||
specialtype: item.specialtype ?? 0,
|
||||
canstandon: item.canstandon ?? false,
|
||||
cansiton: item.cansiton ?? false,
|
||||
canlayon: item.canlayon ?? false,
|
||||
furniline: item.furniline ?? "",
|
||||
environment: item.environment ?? "",
|
||||
rare: item.rare ?? false,
|
||||
tradeable: item.tradeable,
|
||||
bc: item.bc,
|
||||
customparams: item.customparams,
|
||||
itemType: "i",
|
||||
});
|
||||
}
|
||||
}
|
||||
// Process wall items
|
||||
const wallTypes = data?.wallitemtypes?.furnitype;
|
||||
if (Array.isArray(wallTypes)) {
|
||||
for (const item of wallTypes) {
|
||||
if (!item.classname) continue;
|
||||
map.set(item.classname, {
|
||||
id: item.id ?? 0,
|
||||
classname: item.classname,
|
||||
revision: item.revision ?? 0,
|
||||
category: item.category ?? "",
|
||||
defaultdir: item.defaultdir ?? 0,
|
||||
xdim: item.xdim ?? 1,
|
||||
ydim: item.ydim ?? 1,
|
||||
partcolors: item.partcolors ?? { color: [] },
|
||||
name: item.name ?? "",
|
||||
description: item.description ?? "",
|
||||
specialtype: item.specialtype ?? 0,
|
||||
canstandon: item.canstandon ?? false,
|
||||
cansiton: item.cansiton ?? false,
|
||||
canlayon: item.canlayon ?? false,
|
||||
furniline: item.furniline ?? "",
|
||||
environment: item.environment ?? "",
|
||||
rare: item.rare ?? false,
|
||||
tradeable: item.tradeable,
|
||||
bc: item.bc,
|
||||
customparams: item.customparams,
|
||||
itemType: "i",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.debug(
|
||||
`[habbo-it] Cached ${map.size} items (${roomTypes?.length ?? 0} room + ${wallTypes?.length ?? 0} wall)`,
|
||||
);
|
||||
cache = map;
|
||||
cacheTimestamp = now;
|
||||
return map;
|
||||
} catch (err) {
|
||||
console.warn("[habbo-it] Fetch error:", (err as Error).message);
|
||||
return cache || new Map();
|
||||
} finally {
|
||||
loading = null;
|
||||
}
|
||||
})();
|
||||
console.debug(
|
||||
`[habbo-it] Cached ${map.size} items (${roomTypes?.length ?? 0} room + ${wallTypes?.length ?? 0} wall)`,
|
||||
);
|
||||
cache = map;
|
||||
cacheTimestamp = now;
|
||||
return map;
|
||||
} catch (err) {
|
||||
console.warn("[habbo-it] Fetch error:", (err as Error).message);
|
||||
return cache || new Map();
|
||||
} finally {
|
||||
loading = null;
|
||||
}
|
||||
})();
|
||||
|
||||
return loading;
|
||||
return loading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a single item by classname from habbo.it furnidata.
|
||||
* Tries exact match first, then base classname (without color variant).
|
||||
*/
|
||||
export async function lookupHabboIt(classname: string): Promise<HabboItFurniEntry | null> {
|
||||
const map = await getHabboItFurnidata();
|
||||
export async function lookupHabboIt(
|
||||
classname: string,
|
||||
): Promise<HabboItFurniEntry | null> {
|
||||
const map = await getHabboItFurnidata();
|
||||
|
||||
// 1. Exact match
|
||||
const exact = map.get(classname);
|
||||
if (exact) return exact;
|
||||
// 1. Exact match
|
||||
const exact = map.get(classname);
|
||||
if (exact) return exact;
|
||||
|
||||
// 2. If classname has color variant (e.g. "chair*4"), try base
|
||||
if (classname.includes("*")) {
|
||||
const base = classname.split("*")[0];
|
||||
// Try base with *0 first (most common default)
|
||||
const variant0 = map.get(`${base}*0`);
|
||||
if (variant0) return variant0;
|
||||
// Try just the base
|
||||
const baseMatch = map.get(base);
|
||||
if (baseMatch) return baseMatch;
|
||||
}
|
||||
// 2. If classname has color variant (e.g. "chair*4"), try base
|
||||
if (classname.includes("*")) {
|
||||
const base = classname.split("*")[0];
|
||||
// Try base with *0 first (most common default)
|
||||
const variant0 = map.get(`${base}*0`);
|
||||
if (variant0) return variant0;
|
||||
// Try just the base
|
||||
const baseMatch = map.get(base);
|
||||
if (baseMatch) return baseMatch;
|
||||
}
|
||||
|
||||
// 3. If no variant in classname, try *0
|
||||
if (!classname.includes("*")) {
|
||||
const withVariant = map.get(`${classname}*0`);
|
||||
if (withVariant) return withVariant;
|
||||
}
|
||||
// 3. If no variant in classname, try *0
|
||||
if (!classname.includes("*")) {
|
||||
const withVariant = map.get(`${classname}*0`);
|
||||
if (withVariant) return withVariant;
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Clear the cache (useful after manual edits) */
|
||||
export function clearHabboItCache(): void {
|
||||
cache = null;
|
||||
cacheTimestamp = 0;
|
||||
cache = null;
|
||||
cacheTimestamp = 0;
|
||||
}
|
||||
Reference in new issue
Block a user