/** * Official Habbo furnidata cache — hotel chosen in CMS settings * (`habbo_gamedata_hotel`). Downloads once per hotel, keeps in memory. */ export type { HabboItFurniEntry } from "@/types/furni"; import type { HabboItFurniEntry } from "@/types/furni"; import { habboFurnidataUrl, type HabboGamedataHotel, } from "@/lib/habbo-gamedata-hotel"; import { getHabboGamedataHotel } from "@/lib/services/habbo-gamedata-hotel"; const CACHE_TTL = 30 * 60 * 1000; // 30 minutes let cache: Map | null = null; let cacheHotel: HabboGamedataHotel | null = null; let cacheTimestamp = 0; let loading: Promise> | null = null; /** * Fetches and parses the full official Habbo furnidata JSON for the * configured hotel. Results are cached in memory for 30 minutes. */ export async function getHabboItFurnidata(): Promise< Map > { const hotel = await getHabboGamedataHotel(); const now = Date.now(); if ( cache && cacheHotel === hotel && now - cacheTimestamp < CACHE_TTL ) { return cache; } if (loading) return loading; loading = (async () => { const url = habboFurnidataUrl(hotel); try { console.debug(`[habbo-furnidata] Fetching furnidata from ${url}...`); const res = await fetch(url, { signal: AbortSignal.timeout(20000), headers: { Accept: "application/json" }, }); if (!res.ok) { console.warn(`[habbo-furnidata] Fetch failed: ${res.status}`); return cache || new Map(); } const data = await res.json(); const map = new Map(); 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", }); } } 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-furnidata] Cached ${map.size} items from habbo.${hotel} (${roomTypes?.length ?? 0} room + ${wallTypes?.length ?? 0} wall)`, ); cache = map; cacheHotel = hotel; cacheTimestamp = Date.now(); return map; } catch (err) { console.warn( "[habbo-furnidata] Fetch error:", (err as Error).message, ); return cache || new Map(); } finally { loading = null; } })(); return loading; } /** * Lookup a single item by classname from official Habbo furnidata. * Tries exact match first, then base classname (without color variant). */ export async function lookupHabboIt( classname: string, ): Promise { const map = await getHabboItFurnidata(); const exact = map.get(classname); if (exact) return exact; if (classname.includes("*")) { const base = classname.split("*")[0]; const variant0 = map.get(`${base}*0`); if (variant0) return variant0; const baseMatch = map.get(base); if (baseMatch) return baseMatch; } if (!classname.includes("*")) { const withVariant = map.get(`${classname}*0`); if (withVariant) return withVariant; } return null; } /** Clear the cache (e.g. after changing habbo_gamedata_hotel). */ export function clearHabboItCache(): void { cache = null; cacheHotel = null; cacheTimestamp = 0; }