diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/Configuration.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/Configuration.java index 5145613f..33a64f9d 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/Configuration.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/Configuration.java @@ -92,6 +92,10 @@ private Path toFolder(String pathString) throws IOException { if (!file.exists() && !file.mkdirs()) throw new IOException("Invalid configuration: Folders to path '" + file.getAbsolutePath() + "' could not be created"); return file.toPath(); } + + public Path getDataPath() { + return dataPath; + } public boolean isWebserverEnabled() { return webserverEnabled; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/ConfigurationFile.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/ConfigurationFile.java index 98349a2b..cf949883 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/ConfigurationFile.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/ConfigurationFile.java @@ -49,6 +49,7 @@ public class ConfigurationFile { CONFIG_PLACEHOLDERS.add(new Placeholder("version", BlueMap.VERSION)); CONFIG_PLACEHOLDERS.add(new Placeholder("datetime-iso", () -> LocalDateTime.now().withNano(0).toString())); CONFIG_PLACEHOLDERS.add(new Placeholder("minecraft-client-url", ResourcePack.MINECRAFT_CLIENT_URL)); + CONFIG_PLACEHOLDERS.add(new Placeholder("minecraft-client-version", ResourcePack.MINECRAFT_CLIENT_VERSION)); } private Configuration config; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java index 8e647d6f..8021ca85 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java @@ -380,6 +380,8 @@ public static MCAWorld load(Path worldFolder, UUID uuid) throws IOException { levelData.getInt("SpawnZ") ); + + CHUNK_CACHE.invalidateAll(); return new MCAWorld( worldFolder, uuid, diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/ResourcePack.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/ResourcePack.java index 37027a6b..cd3e873d 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/ResourcePack.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/ResourcePack.java @@ -47,10 +47,12 @@ import com.google.common.cache.CacheBuilder; import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.util.FileUtil; import de.bluecolored.bluemap.core.world.BlockState; public class ResourcePack { - + + public static final String MINECRAFT_CLIENT_VERSION = "1.14.4"; public static final String MINECRAFT_CLIENT_URL = "https://launcher.mojang.com/v1/objects/8c325a0c5bd674dd747d6ebaa4c791fd363ad8a9/client.jar"; private Map resources; @@ -62,37 +64,31 @@ public class ResourcePack { public ResourcePack(List dataSources, File textureExportFile) throws IOException, NoSuchResourceException { this.resources = new HashMap<>(); - load(dataSources); + //load resources in order + for (File resource : dataSources) overrideResourcesWith(resource); blockStateResourceCache = CacheBuilder.newBuilder() .maximumSize(10000) .build(); textureProvider = new TextureProvider(); + if (textureExportFile.exists()){ textureProvider.load(textureExportFile); - } else { - textureProvider.generate(this); - textureProvider.save(textureExportFile); } + + textureProvider.generate(this); //if loaded add missing textures + textureProvider.save(textureExportFile); blockColorProvider = new BlockColorProvider(this); } - private void load(List dataSources) throws IOException { - resources.clear(); - - //load resourcepacks in order - for (File resourcePath : dataSources) overrideResourcesWith(resourcePath); - } - - private void overrideResourcesWith(File resourcePath){ - if (resourcePath.isFile() && resourcePath.getName().endsWith(".zip") || resourcePath.getName().endsWith(".jar")){ - overrideResourcesWithZipFile(resourcePath); - return; + private void overrideResourcesWith(File resource){ + if (resource.isFile() && resource.getName().endsWith(".zip") || resource.getName().endsWith(".jar")){ + overrideResourcesWithZipFile(resource); + } else { + overrideResourcesWith(resource, Paths.get("")); } - - overrideResourcesWith(resourcePath, Paths.get("")); } private void overrideResourcesWith(File resource, Path resourcePath){ @@ -103,7 +99,7 @@ private void overrideResourcesWith(File resource, Path resourcePath){ return; } - if (resource.isFile()){ + if (resource.isFile() && isActualResourcePath(resourcePath)){ try { byte[] bytes = Files.readAllBytes(resource.toPath()); resources.put(resourcePath, new Resource(bytes)); @@ -124,15 +120,9 @@ private void overrideResourcesWithZipFile(File resourceFile){ if (file.isDirectory()) continue; Path resourcePath = Paths.get("", file.getName().split("/")); - if ( - !resourcePath.startsWith(Paths.get("assets", "minecraft", "blockstates")) && - !resourcePath.startsWith(Paths.get("assets", "minecraft", "models", "block")) && - !resourcePath.startsWith(Paths.get("assets", "minecraft", "textures", "block")) && - !resourcePath.startsWith(Paths.get("assets", "minecraft", "textures", "colormap")) - ) continue; + if (!isActualResourcePath(resourcePath)) continue; InputStream fileInputStream = zipFile.getInputStream(file); - ByteArrayOutputStream bos = new ByteArrayOutputStream(Math.max(8, (int) file.getSize())); int bytesRead; while ((bytesRead = fileInputStream.read(buffer)) != -1){ @@ -146,6 +136,17 @@ private void overrideResourcesWithZipFile(File resourceFile){ } } + private boolean isActualResourcePath(Path path) { + String[] blockstatesPattern = {"assets", ".*", "blockstates", "*"}; + String[] modelsPattern = {"assets", ".*", "models", "blocks?", "*"}; + String[] texturesPattern = {"assets", ".*", "textures", "block|colormap", "*"}; + + return + FileUtil.matchPath(path, blockstatesPattern) || + FileUtil.matchPath(path, modelsPattern) || + FileUtil.matchPath(path, texturesPattern); + } + public BlockStateResource getBlockStateResource(BlockState block) throws NoSuchResourceException, InvalidResourceDeclarationException { BlockStateResource bsr = blockStateResourceCache.getIfPresent(block); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/TextureProvider.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/TextureProvider.java index 51babbd8..3ab6bbec 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/TextureProvider.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/TextureProvider.java @@ -30,7 +30,6 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Base64; import java.util.List; import java.util.Map; @@ -44,6 +43,7 @@ import de.bluecolored.bluemap.core.resourcepack.ResourcePack.Resource; import de.bluecolored.bluemap.core.util.ConfigUtil; +import de.bluecolored.bluemap.core.util.FileUtil; import de.bluecolored.bluemap.core.util.MathUtil; import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.gson.GsonConfigurationLoader; @@ -77,29 +77,34 @@ public Texture getTexture(int index){ } public void generate(ResourcePack resources) throws IOException { - indexMap.clear(); - textures.clear(); - - Path textureRoot = Paths.get("assets", "minecraft", "textures"); + String[] texturesPathPattern = {"assets", ".*", "textures", "block", "*"}; + for (Entry entry : resources.getAllResources().entrySet()){ - if (entry.getKey().startsWith(textureRoot) && entry.getKey().toString().endsWith(".png")){ - BufferedImage image = ImageIO.read(entry.getValue().getStream()); - if (image == null) throw new IOException("Failed to read Image: " + entry.getKey()); - - String path = textureRoot.relativize(entry.getKey()).normalize().toString(); + Path key = entry.getKey(); + if (FileUtil.matchPath(key, texturesPathPattern) && key.toString().endsWith(".png")){ + String path = key.subpath(3, key.getNameCount()).normalize().toString(); String id = path .substring(0, path.length() - ".png".length()) .replace(File.separatorChar, '/'); + + BufferedImage image = ImageIO.read(entry.getValue().getStream()); + if (image == null) throw new IOException("Failed to read Image: " + key); Texture texture = new Texture(id, image); - textures.add(texture); - indexMap.put(id, textures.size() - 1); + + //update if existing else add new + if (indexMap.containsKey(id)) { + int index = indexMap.get(id); + textures.set(index, texture); + } else { + textures.add(texture); + indexMap.put(id, textures.size() - 1); + } } } } public void load(File file) throws IOException { - indexMap.clear(); textures.clear(); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileUtil.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileUtil.java index 355dd0bb..ddbb48da 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileUtil.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileUtil.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; import com.flowpowered.math.vector.Vector2i; @@ -73,4 +74,30 @@ public static void waitForFile(File file, long time, TimeUnit unit) throws Inter } } + /** + * The path-elements are being matched to the pattern-elements, + * each pattern-element can be a regex pattern to match against one path-element or "*" to represent any number of arbitrary elements (lazy: until the next pattern matches). + */ + public static boolean matchPath(Path path, String... pattern) { + int p = 0; + for (int i = 0; i < path.getNameCount(); i++) { + while (pattern[p].equals("*")) { + p++; + + if (pattern.length >= p) return true; + } + + if (Pattern.matches(pattern[p], path.getName(i).toString())) { + p++; + continue; + } + + if (p > 0 && pattern[p-1].equals("*")) continue; + + return false; + } + + return true; + } + } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Block.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Block.java index b085e011..22c8dad4 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Block.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Block.java @@ -96,6 +96,7 @@ public String toString() { .add("biome", getBiome()) .add("blocklight", getBlockLightLevel()) .add("sunlight", getSunLightLevel()) + .add("state", getBlock()) .toString(); } diff --git a/BlueMapCore/src/main/resources/bluemap.conf b/BlueMapCore/src/main/resources/bluemap.conf index 548fcffa..e9d759a4 100644 --- a/BlueMapCore/src/main/resources/bluemap.conf +++ b/BlueMapCore/src/main/resources/bluemap.conf @@ -13,7 +13,7 @@ version: "%version%" # By changing the setting (accept-download) below to TRUE you are indicating that you have accepted mojang's EULA (https://account.mojang.com/documents/minecraft_eula), # you confirm that you own a license to Minecraft (Java Edition) # and you agree that BlueMap will download and use this file for you: %minecraft-client-url% -# (Alternatively you can download the file yourself and store it here: /minecraft-client.jar) +# (Alternatively you can download the file yourself and store it here: /minecraft-client-%minecraft-client-version%.jar) # This file contains resources that belong to mojang and you must not redistribute it or do anything else that is not compilant with mojang's EULA. # BlueMap uses resources in this file to generate the 3D-Models used for the map and texture them. (BlueMap will not work without those resources.) # %datetime-iso% @@ -28,9 +28,8 @@ metrics: true data: "data" web { - # With this setting you can disable the web-server. - # This is usefull if you want to only render the map-data for later use, or if you setup your own webserver. - enabled: true + # With this setting you can enable the integrated web-server. + enabled: false # The webroot of the website that displays the map. webroot: "web" diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java index 2fcb69ac..6bbf5939 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java @@ -97,6 +97,7 @@ public CommandSpec createReloadCommand() { .permission("bluemap.reload") .executor((source, args) -> { try { + source.sendMessage(Text.of(TextColors.GOLD, "Reloading BlueMap...")); plugin.reload(); if (plugin.isLoaded()) { diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java index 6e1be6dc..1a8b6a28 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -31,8 +31,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -52,12 +55,11 @@ import org.spongepowered.api.scheduler.SpongeExecutorService; import com.flowpowered.math.vector.Vector2i; -import com.google.common.collect.Lists; import de.bluecolored.bluemap.core.BlueMap; -import de.bluecolored.bluemap.core.config.ConfigurationFile; import de.bluecolored.bluemap.core.config.Configuration; import de.bluecolored.bluemap.core.config.Configuration.MapConfig; +import de.bluecolored.bluemap.core.config.ConfigurationFile; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.mca.MCAWorld; import de.bluecolored.bluemap.core.metrics.Metrics; @@ -130,10 +132,10 @@ public synchronized void load() throws IOException, NoSuchResourceException { //load configs File configFile = getConfigPath().resolve("bluemap.conf").toFile(); - config = ConfigurationFile.loadOrCreate(configFile).getConfig(); + config = ConfigurationFile.loadOrCreate(configFile, SpongePlugin.class.getResource("/bluemap-sponge.conf")).getConfig(); //load resources - File defaultResourceFile = getConfigPath().resolve("resourcepacks").resolve("minecraft-client.jar").toFile(); + File defaultResourceFile = config.getDataPath().resolve("minecraft-client-" + ResourcePack.MINECRAFT_CLIENT_VERSION + ".jar").toFile(); File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile(); if (!defaultResourceFile.exists()) { @@ -146,7 +148,17 @@ public synchronized void load() throws IOException, NoSuchResourceException { return; } - resourcePack = new ResourcePack(Lists.newArrayList(defaultResourceFile), textureExportFile); + //find more resource packs + File resourcePackFolder = getConfigPath().resolve("resourcepacks").toFile(); + resourcePackFolder.mkdirs(); + File[] resourcePacks = resourcePackFolder.listFiles(); + Arrays.sort(resourcePacks); + + List resources = new ArrayList<>(resourcePacks.length + 1); + resources.add(defaultResourceFile); + for (File file : resourcePacks) resources.add(file); + + resourcePack = new ResourcePack(resources, textureExportFile); //load maps for (MapConfig mapConfig : config.getMapConfigs()) { diff --git a/BlueMapSponge/src/main/resources/bluemap-sponge.conf b/BlueMapSponge/src/main/resources/bluemap-sponge.conf index 4811f4bb..a0d5481a 100644 --- a/BlueMapSponge/src/main/resources/bluemap-sponge.conf +++ b/BlueMapSponge/src/main/resources/bluemap-sponge.conf @@ -13,7 +13,7 @@ version: "%version%" # By changing the setting (accept-download) below to TRUE you are indicating that you have accepted mojang's EULA (https://account.mojang.com/documents/minecraft_eula), # you confirm that you own a license to Minecraft (Java Edition) # and you agree that BlueMap will download and use this file for you: %minecraft-client-url% -# (Alternatively you can download the file yourself and store it here: /minecraft-client.jar) +# (Alternatively you can download the file yourself and store it here: /minecraft-client-%minecraft-client-version%.jar) # This file contains resources that belong to mojang and you must not redistribute it or do anything else that is not compilant with mojang's EULA. # BlueMap uses resources in this file to generate the 3D-Models used for the map and texture them. (BlueMap will not work without those resources.) # %datetime-iso% @@ -23,7 +23,7 @@ accept-download: false data: "bluemap" web { - # With this setting you can disable the web-server. + # With this setting you can disable the integrated web-server. # This is usefull if you want to only render the map-data for later use, or if you setup your own webserver. enabled: true