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.
This commit is contained in:
simoleo89
2026-06-13 17:08:17 +02:00
parent fdcd3a7323
commit aec61064ae
3 changed files with 57 additions and 18 deletions
@@ -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)) {
@@ -27,6 +27,7 @@ public class FurnitureTextProvider {
private final boolean enabled;
private volatile Map<String, FurniText> 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;
}
@@ -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());
}
}