From aec61064aea93adc49cf3183bc32f370239e146d Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 13 Jun 2026 17:08:17 +0200 Subject: [PATCH] fix(furnidata): prefer renderer config source Resolve furnidata from the renderer config and asset base before falling back to the legacy items.furnidata.path override. This keeps the emulator aligned with the same furnidata URL the UI/renderer already consume. Keep the legacy path as a compatibility fallback for older installs, but stop exposing absolute furnidata file paths in the startup log. The provider now reports a compact manager-style source label instead. Add coverage proving renderer-config furnidata.url wins over the legacy path when both are present. --- .../items/FurnidataSourceResolver.java | 36 +++++++++++-------- .../items/FurnitureTextProvider.java | 10 ++++-- .../furnieditor/FurniDataManagerTest.java | 29 +++++++++++++++ 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnidataSourceResolver.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnidataSourceResolver.java index 6d1f758f..85707871 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnidataSourceResolver.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnidataSourceResolver.java @@ -36,30 +36,36 @@ public final class FurnidataSourceResolver { public static Source resolve() { try { String override = Emulator.getConfig().getValue("items.furnidata.path", ""); - if (!override.isEmpty()) { - Path p = Paths.get(override); - if (Files.exists(p)) return new Source(p, Files.isDirectory(p), Status.RESOLVED, "items.furnidata.path"); - return new Source(p, Files.isDirectory(p), Status.SOURCE_MISSING, "items.furnidata.path does not exist"); - } - String rendererConfigPath = Emulator.getConfig().getValue("furni.editor.renderer.config.path", ""); String assetBasePath = Emulator.getConfig().getValue("furni.editor.asset.base.path", ""); - if (!rendererConfigPath.isEmpty()) { - Source fromRenderer = resolveFromRendererConfig(Paths.get(rendererConfigPath), assetBasePath.isEmpty() ? null : Paths.get(assetBasePath)); - if (fromRenderer.ok() || fromRenderer.status() == Status.UNRESOLVED_PLACEHOLDER) return fromRenderer; - } - - Source fallback = resolveFromAssetBase(assetBasePath); - if (fallback != null) return fallback; - - return new Source(null, false, Status.CONFIG_MISSING, "No furnidata source config found"); + return resolveConfigured(override, rendererConfigPath, assetBasePath); } catch (Exception e) { LOGGER.warn("FurnidataSourceResolver failed", e); return new Source(null, false, Status.ERROR, e.getMessage() != null ? e.getMessage() : "Resolver error"); } } + public static Source resolveConfigured(String legacyOverridePath, String rendererConfigPath, String assetBasePath) { + if (rendererConfigPath != null && !rendererConfigPath.isEmpty()) { + Source fromRenderer = resolveFromRendererConfig(Paths.get(rendererConfigPath), assetBasePath == null || assetBasePath.isEmpty() ? null : Paths.get(assetBasePath)); + if (fromRenderer.ok() || fromRenderer.status() == Status.UNRESOLVED_PLACEHOLDER) return fromRenderer; + } + + Source fromAssetBase = resolveFromAssetBase(assetBasePath); + if (fromAssetBase != null && fromAssetBase.ok()) return fromAssetBase; + + if (legacyOverridePath != null && !legacyOverridePath.isEmpty()) { + Path p = Paths.get(legacyOverridePath); + if (Files.exists(p)) return new Source(p, Files.isDirectory(p), Status.RESOLVED, "items.furnidata.path fallback"); + return new Source(p, Files.isDirectory(p), Status.SOURCE_MISSING, "items.furnidata.path fallback does not exist"); + } + + if (fromAssetBase != null) return fromAssetBase; + + return new Source(null, false, Status.CONFIG_MISSING, "No furnidata source config found"); + } + public static Source resolveFromRendererConfig(Path rendererConfig, Path assetBase) { try { if (rendererConfig == null || !Files.exists(rendererConfig)) { diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnitureTextProvider.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnitureTextProvider.java index 6e9c6c92..df333dc7 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnitureTextProvider.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnitureTextProvider.java @@ -27,6 +27,7 @@ public class FurnitureTextProvider { private final boolean enabled; private volatile Map index = Map.of(); private volatile Path source; + private volatile String sourceDescription = "unknown"; private FurnidataWatcher watcher; public FurnitureTextProvider(boolean enabled) { @@ -47,7 +48,7 @@ public class FurnitureTextProvider { return; } reindex(new FurnidataReader(this.source, DEFAULT_MAX_BYTES).read()); - LOGGER.info("FurnitureTextProvider: indexed {} furnidata names from {}", this.index.size(), this.source); + LOGGER.info("Furniture Text Provider -> Indexed! ({} names, source: {})", this.index.size(), this.sourceDescription); if (Boolean.parseBoolean(Emulator.getConfig().getValue("items.furnidata.watch.enabled", "true"))) { if (this.watcher != null) this.watcher.stop(); @@ -88,9 +89,12 @@ public class FurnitureTextProvider { } } - private static Path resolveSource() { + private Path resolveSource() { FurnidataSourceResolver.Source source = FurnidataSourceResolver.resolve(); - if (source.ok()) return source.path(); + if (source.ok()) { + this.sourceDescription = source.message(); + return source.path(); + } LOGGER.warn("FurnitureTextProvider: no furnidata source resolved ({}) - {}", source.status(), source.message()); return null; } diff --git a/Emulator/src/test/java/com/eu/habbo/messages/incoming/furnieditor/FurniDataManagerTest.java b/Emulator/src/test/java/com/eu/habbo/messages/incoming/furnieditor/FurniDataManagerTest.java index fa3db546..32770ae3 100644 --- a/Emulator/src/test/java/com/eu/habbo/messages/incoming/furnieditor/FurniDataManagerTest.java +++ b/Emulator/src/test/java/com/eu/habbo/messages/incoming/furnieditor/FurniDataManagerTest.java @@ -78,4 +78,33 @@ class FurniDataManagerTest { assertEquals(assetBase.resolve("gamedata").resolve("FurnitureData.json"), source.path()); assertFalse(source.directory()); } + + @Test + void prefersRendererConfigOverLegacyFurnidataPath(@TempDir Path dir) throws Exception { + Path legacy = dir.resolve("legacy").resolve("FurnitureData.json"); + Files.createDirectories(legacy.getParent()); + Files.writeString(legacy, "{}"); + + Path assetBase = dir.resolve("nitro-assets"); + Path rendererSource = assetBase.resolve("gamedata").resolve("FurnitureData.json"); + Files.createDirectories(rendererSource.getParent()); + Files.writeString(rendererSource, "{}"); + + Path rendererConfig = dir.resolve("renderer-config.json"); + Files.writeString(rendererConfig, """ + { + "gamedata.url": "http://localhost:5173/nitro-assets/gamedata", + "furnidata.url": "${gamedata.url}/FurnitureData.json?t=%timestamp%" + } + """); + + FurnidataSourceResolver.Source source = FurnidataSourceResolver.resolveConfigured( + legacy.toString(), + rendererConfig.toString(), + assetBase.toString()); + + assertTrue(source.ok()); + assertEquals(rendererSource, source.path()); + assertEquals("renderer-config furnidata.url", source.message()); + } }