From ffc472ce84a480b9cd14652803154276dcec014e Mon Sep 17 00:00:00 2001 From: "Lukas Rieger (Blue)" Date: Mon, 1 Aug 2022 00:15:39 +0200 Subject: [PATCH] Fix not loading textures if they are outside the block(s) folder --- .../bluemap/common/BlueMapService.java | 36 ++---- .../resources/resourcepack/ResourcePack.java | 109 +++++++++++++----- 2 files changed, 85 insertions(+), 60 deletions(-) diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java index 2f9ba1b8..1f61b304 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java @@ -59,7 +59,6 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; -import java.util.concurrent.CompletionException; import java.util.stream.Stream; /** @@ -329,20 +328,14 @@ public synchronized ResourcePack getResourcePack() throws ConfigurationException } try { - Logger.global.logInfo("Loading resources..."); resourcePack = new ResourcePack(); + List resourcePackRoots = new ArrayList<>(); // load from resourcepack folder try (Stream resourcepackFiles = Files.list(resourcePackFolder)) { resourcepackFiles .sorted(Comparator.reverseOrder()) - .forEach(resourcepackFile -> { - try { - resourcePack.loadResources(resourcepackFile); - } catch (IOException e) { - throw new CompletionException(e); - } - }); + .forEach(resourcePackRoots::add); } if (configs.getCoreConfig().isScanForModResources()) { @@ -354,13 +347,7 @@ public synchronized ResourcePack getResourcePack() throws ConfigurationException resourcepackFiles .filter(Files::isRegularFile) .filter(file -> file.getFileName().toString().endsWith(".jar")) - .forEach(resourcepackFile -> { - try { - resourcePack.loadResources(resourcepackFile); - } catch (IOException e) { - throw new CompletionException(e); - } - }); + .forEach(resourcePackRoots::add); } } @@ -370,23 +357,16 @@ public synchronized ResourcePack getResourcePack() throws ConfigurationException if (!Files.isDirectory(datapacksFolder)) continue; try (Stream resourcepackFiles = Files.list(worldFolder.resolve("datapacks"))) { - resourcepackFiles - .forEach(resourcepackFile -> { - try { - resourcePack.loadResources(resourcepackFile); - } catch (IOException e) { - throw new CompletionException(e); - } - }); + resourcepackFiles.forEach(resourcePackRoots::add); } } } - resourcePack.loadResources(resourceExtensionsFile); - resourcePack.loadResources(defaultResourceFile); - resourcePack.bake(); - Logger.global.logInfo("Resources loaded."); + resourcePackRoots.add(resourceExtensionsFile); + resourcePackRoots.add(defaultResourceFile); + + resourcePack.loadResources(resourcePackRoots); } catch (IOException | RuntimeException e) { throw new ConfigurationException("Failed to parse resources!\n" + "Is one of your resource-packs corrupted?", e); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/ResourcePack.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/ResourcePack.java index 49c0d3ca..7492d781 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/ResourcePack.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/ResourcePack.java @@ -4,8 +4,8 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.api.debug.DebugDump; +import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.resources.BlockColorCalculatorFactory; import de.bluecolored.bluemap.core.resources.BlockPropertiesConfig; @@ -13,6 +13,7 @@ import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson; import de.bluecolored.bluemap.core.resources.biome.BiomeConfig; import de.bluecolored.bluemap.core.resources.resourcepack.blockmodel.BlockModel; +import de.bluecolored.bluemap.core.resources.resourcepack.blockmodel.TextureVariable; import de.bluecolored.bluemap.core.resources.resourcepack.blockstate.BlockState; import de.bluecolored.bluemap.core.resources.resourcepack.texture.Texture; import de.bluecolored.bluemap.core.util.Tristate; @@ -30,7 +31,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.regex.Pattern; @@ -157,18 +160,33 @@ private BlockProperties loadBlockProperties(de.bluecolored.bluemap.core.world.Bl return props.build(); } - public synchronized void loadResources(Path root) throws IOException { - Logger.global.logDebug("Loading resources from: " + root + " ..."); - loadResourcesInternal(root); + public synchronized void loadResources(Iterable roots) throws IOException { + Logger.global.logInfo("Loading resources..."); + + for (Path root : roots) { + Logger.global.logDebug("Loading resources from: " + root + " ..."); + loadResourcePath(root, this::loadResources); + } + + Logger.global.logInfo("Loading textures..."); + for (Path root : roots) { + Logger.global.logDebug("Loading textures from: " + root + " ..."); + loadResourcePath(root, this::loadTextures); + } + + Logger.global.logInfo("Baking resources..."); + bake(); + + + Logger.global.logInfo("Resources loaded."); } - private synchronized void loadResourcesInternal(Path root) throws IOException { - + private void loadResourcePath(Path root, PathLoader resourceLoader) throws IOException { if (!Files.isDirectory(root)) { try (FileSystem fileSystem = FileSystems.newFileSystem(root, (ClassLoader) null)) { for (Path fsRoot : fileSystem.getRootDirectories()) { if (!Files.isDirectory(fsRoot)) continue; - this.loadResourcesInternal(fsRoot); + loadResourcePath(fsRoot, resourceLoader); } } catch (Exception ex) { Logger.global.logDebug("Failed to read '" + root + "': " + ex); @@ -180,16 +198,20 @@ private synchronized void loadResourcesInternal(Path root) throws IOException { Path fabricModJson = root.resolve("fabric.mod.json"); if (Files.isRegularFile(fabricModJson)) { try (BufferedReader reader = Files.newBufferedReader(fabricModJson)) { - JsonObject rootElement = ResourcesGson.INSTANCE.fromJson(reader, JsonObject.class); - for (JsonElement element : rootElement.getAsJsonArray("jars")) { - Path file = root.resolve(element.getAsJsonObject().get("file").getAsString()); - if (Files.exists(file)) loadResourcesInternal(file); - } + JsonObject rootElement = ResourcesGson.INSTANCE.fromJson(reader, JsonObject.class); + for (JsonElement element : rootElement.getAsJsonArray("jars")) { + Path file = root.resolve(element.getAsJsonObject().get("file").getAsString()); + if (Files.exists(file)) loadResourcePath(file, resourceLoader); + } } catch (Exception ex) { Logger.global.logDebug("Failed to read fabric.mod.json: " + ex); } } + resourceLoader.load(root); + } + + private void loadResources(Path root) throws IOException { try { // do those in parallel CompletableFuture.allOf( @@ -226,24 +248,6 @@ private synchronized void loadResourcesInternal(Path root) throws IOException { }, blockModels)); }, BlueMap.THREAD_POOL), - // load textures - CompletableFuture.runAsync(() -> { - list(root.resolve("assets")) - .map(path -> path.resolve("textures")) - .flatMap(ResourcePack::list) - .filter(path -> Pattern.matches("blocks?", path.getFileName().toString())) - .filter(Files::isDirectory) - .flatMap(ResourcePack::walk) - .filter(path -> path.getFileName().toString().endsWith(".png")) - .filter(Files::isRegularFile) - .forEach(file -> loadResource(root, file, () -> { - ResourcePath resourcePath = new ResourcePath<>(root.relativize(file)); - try (InputStream in = Files.newInputStream(file)) { - return Texture.from(resourcePath, ImageIO.read(in)); - } - }, textures)); - }, BlueMap.THREAD_POOL), - // load colormaps CompletableFuture.runAsync(() -> { walk(root.resolve("assets").resolve("minecraft").resolve("textures").resolve("colormap")) @@ -322,8 +326,42 @@ private synchronized void loadResourcesInternal(Path root) throws IOException { } } - public synchronized void bake() throws IOException { - Logger.global.logDebug("Baking resources..."); + private void loadTextures(Path root) throws IOException { + try { + + // collect all used textures + Set> usedTextures = new HashSet<>(); + for (BlockModel model : blockModels.values()) { + for (TextureVariable textureVariable : model.getTextures().values()) { + if (textureVariable.isReference()) continue; + usedTextures.add(textureVariable.getTexturePath()); + } + } + + // load textures + list(root.resolve("assets")) + .map(path -> path.resolve("textures")) + .flatMap(ResourcePack::walk) + .filter(path -> path.getFileName().toString().endsWith(".png")) + .filter(Files::isRegularFile) + .forEach(file -> loadResource(root, file, () -> { + ResourcePath resourcePath = new ResourcePath<>(root.relativize(file)); + if (!usedTextures.contains(resourcePath)) return null; // don't load unused textures + + try (InputStream in = Files.newInputStream(file)) { + return Texture.from(resourcePath, ImageIO.read(in)); + } + }, textures)); + + } catch (RuntimeException ex) { + Throwable cause = ex.getCause(); + if (cause instanceof IOException) throw (IOException) cause; + if (cause != null) throw new IOException(cause); + throw new IOException(ex); + } + } + + private void bake() throws IOException { // fill path maps blockStates.keySet().forEach(path -> blockStatePaths.put(path.getFormatted(), path)); @@ -352,6 +390,7 @@ public synchronized void bake() throws IOException { BufferedImage grass = new ResourcePath("minecraft:colormap/grass").getResource(colormaps::get); if (grass == null) throw new IOException("Failed to bake resource-pack: No grass-colormap found!"); this.colorCalculatorFactory.setGrassMap(grass); + } private void loadResource(Path root, Path file, Loader loader, Map, T> resultMap) { @@ -360,6 +399,8 @@ private void loadResource(Path root, Path file, Loader loader, Map { T load() throws IOException; } + private interface PathLoader { + void load(Path root) throws IOException; + } + }