From c60a62a6cdd57ef6ec3a1cc9ebc599a332e041c3 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Sat, 4 Jan 2020 20:04:47 +0100 Subject: [PATCH] Add some more resources-compatibility and tweak some values in configs and colors --- .../bluecolored/bluemap/cli/BlueMapCLI.java | 12 +- .../main/resources/bluemap-cli-defaults.conf | 1 + .../src/main/resources/bluemap-cli.conf | 14 +- .../bluemap/core/config/MainConfig.java | 7 +- .../hires/blockmodel/LiquidModelBuilder.java | 5 +- .../core/resourcepack/BlockModelResource.java | 12 +- .../core/resourcepack/BlockStateResource.java | 291 +++++++++++++----- .../core/resourcepack/PropertyCondition.java | 22 +- .../core/resourcepack/ResourcePack.java | 14 +- .../resourcepack/fileaccess/FileAccess.java | 2 + .../ResourcePackOldFormatFileAccess.java | 115 +++++++ .../bluemap/sponge/SpongePlugin.java | 10 +- .../resources/bluemap-sponge-defaults.conf | 1 + .../src/main/resources/bluemap-sponge.conf | 14 +- 14 files changed, 423 insertions(+), 97 deletions(-) create mode 100644 BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/fileaccess/ResourcePackOldFormatFileAccess.java diff --git a/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java b/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java index 75d1437e..510af72b 100644 --- a/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java +++ b/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java @@ -64,6 +64,7 @@ import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; import de.bluecolored.bluemap.core.resourcepack.ResourcePack; import de.bluecolored.bluemap.core.web.BlueMapWebServer; +import de.bluecolored.bluemap.core.web.WebFilesManager; import de.bluecolored.bluemap.core.web.WebSettings; import de.bluecolored.bluemap.core.world.World; @@ -192,7 +193,6 @@ public void startWebserver() throws IOException { Logger.global.logInfo("Starting webserver..."); BlueMapWebServer webserver = new BlueMapWebServer(configManager.getMainConfig()); - webserver.updateWebfiles(); webserver.start(); } @@ -240,7 +240,7 @@ private boolean loadResources() throws IOException, ParseResourceException { private boolean handleMissingResources(File resourceFile) { if (configManager.getMainConfig().isDownloadAccepted()) { try { - Logger.global.logInfo("Downloading " + ResourcePack.MINECRAFT_CLIENT_URL + " to " + resourceFile + " ..."); + Logger.global.logInfo("Downloading " + ResourcePack.MINECRAFT_CLIENT_URL + " to " + resourceFile.getCanonicalPath() + " ..."); ResourcePack.downloadDefaultResource(resourceFile); return true; } catch (IOException e) { @@ -286,6 +286,12 @@ public static void main(String[] args) throws IOException, ParseResourceExceptio return; } + WebFilesManager webFilesManager = new WebFilesManager(config.getMainConfig().getWebRoot()); + if (webFilesManager.needsUpdate()) { + Logger.global.logInfo("Updating webfiles in " + config.getMainConfig().getWebRoot().normalize() + "..."); + webFilesManager.updateFiles(); + } + BlueMapCLI bluemap = new BlueMapCLI(config, configFolder, cmd.hasOption("f")); if (config.getMainConfig().isWebserverEnabled()) { @@ -352,7 +358,7 @@ private static void printHelp() { if (file.isFile()) { try { - filename = "." + File.separator + new File("").getAbsoluteFile().toPath().relativize(file.toPath()).toString(); + filename = "." + File.separator + new File("").getCanonicalFile().toPath().relativize(file.toPath()).toString(); } catch (IllegalArgumentException ex) { filename = file.getAbsolutePath(); } diff --git a/BlueMapCLI/src/main/resources/bluemap-cli-defaults.conf b/BlueMapCLI/src/main/resources/bluemap-cli-defaults.conf index 8513d06a..e78c2aa9 100644 --- a/BlueMapCLI/src/main/resources/bluemap-cli-defaults.conf +++ b/BlueMapCLI/src/main/resources/bluemap-cli-defaults.conf @@ -7,3 +7,4 @@ web { port: 8100 maxConnectionCount: 100 } +renderThreadCount: 0 diff --git a/BlueMapCLI/src/main/resources/bluemap-cli.conf b/BlueMapCLI/src/main/resources/bluemap-cli.conf index f5373c25..877f8cfa 100644 --- a/BlueMapCLI/src/main/resources/bluemap-cli.conf +++ b/BlueMapCLI/src/main/resources/bluemap-cli.conf @@ -56,8 +56,10 @@ web { # This changes the amount of threads that BlueMap will use to render the maps. # A higher value can improve render-speed but could impact performance on the host machine. # This should be always below or equal to the number of available processor-cores. -# If this value is commented out BlueMap tries to find the optimal thread count to max out render-performance -#renderThreadCount: 2 +# Zero or a negative value means the amount of of available processor-cores subtracted by the value. +# (So a value of -2 with 6 cores results in 4 render-processes) +# Default is 0 +renderThreadCount: 0 # This is an array with multiple configured maps. # You can define multiple maps, for different worlds with different render-settings here @@ -112,8 +114,8 @@ maps: [ tileSize: 32 # The View-Distance for hires tiles on the web-map (the value is the radius in tiles) - # Default is 3.5 - viewDistance: 3.5 + # Default is 4.5 + viewDistance: 4.5 } # LOWRES is the low-resolution render of the map. THats the model that you see if you zoom far out to get an overview. @@ -129,8 +131,8 @@ maps: [ pointsPerLowresTile: 50 # The View-Distance for lowres tiles on the web-map (the value is the radius in tiles) - # Default is 4 - viewDistance: 4 + # Default is 7 + viewDistance: 7 } } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MainConfig.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MainConfig.java index 7e052361..f7bc32ce 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MainConfig.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MainConfig.java @@ -68,9 +68,10 @@ public MainConfig(ConfigurationNode node) throws IOException { loadWebConfig(node.getNode("web")); - int defaultCount = Runtime.getRuntime().availableProcessors(); - renderThreadCount = node.getNode("renderThreadCount").getInt(defaultCount); - if (renderThreadCount <= 0) renderThreadCount = defaultCount; + int processors = Runtime.getRuntime().availableProcessors(); + renderThreadCount = node.getNode("renderThreadCount").getInt(0); + if (renderThreadCount <= 0) renderThreadCount = processors + renderThreadCount; + if (renderThreadCount <= 0) renderThreadCount = 1; loadMapConfigs(node.getNode("maps")); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/LiquidModelBuilder.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/LiquidModelBuilder.java index f86edfb6..7a07ba92 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/LiquidModelBuilder.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/LiquidModelBuilder.java @@ -72,6 +72,7 @@ public BlockStateModel build(BlockState blockState, BlockModelResource bmr) { int level = getLiquidLevel(blockState); float[] heights = new float[]{16f, 16f, 16f, 16f}; + float coloralpha = 0.2f; if (level < 8 && !(level == 0 && isLiquid(context.getRelativeBlock(0, 1, 0)))){ heights = new float[]{ @@ -80,6 +81,8 @@ public BlockStateModel build(BlockState blockState, BlockModelResource bmr) { getLiquidCornerHeight(0, 0, -1), getLiquidCornerHeight(0, 0, 0) }; + + coloralpha = 0.8f; } BlockStateModel model = new BlockStateModel(); @@ -114,7 +117,7 @@ public BlockStateModel build(BlockState blockState, BlockModelResource bmr) { //calculate mapcolor Vector4f mapcolor = texture.getColor(); - mapcolor = mapcolor.mul(tintcolor.toVector4(0.5)); + mapcolor = mapcolor.mul(tintcolor.toVector4(coloralpha)); model.setMapColor(mapcolor); return model; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/BlockModelResource.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/BlockModelResource.java index 6673ff0d..5e7cbfee 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/BlockModelResource.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/BlockModelResource.java @@ -31,6 +31,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.List; @@ -213,6 +214,8 @@ public static Builder builder(FileAccess sourcesAccess, ResourcePack resourcePac } public static class Builder { + + private static final String JSON_COMMENT = "__comment"; private FileAccess sourcesAccess; private ResourcePack resourcePack; @@ -227,7 +230,12 @@ private Builder(FileAccess sourcesAccess, ResourcePack resourcePack) { } public synchronized BlockModelResource build(String modelPath) throws IOException, ParseResourceException { - textures.clear(); + return build(modelPath, Collections.emptyMap()); + } + + public synchronized BlockModelResource build(String modelPath, Map textures) throws IOException, ParseResourceException { + this.textures.clear(); + this.textures.putAll(textures); return buildNoReset(modelPath, true, modelPath); } @@ -239,6 +247,8 @@ private BlockModelResource buildNoReset(String modelPath, boolean renderElements .load(); for (Entry entry : config.getNode("textures").getChildrenMap().entrySet()) { + if (entry.getKey().equals(JSON_COMMENT)) continue; + textures.putIfAbsent(entry.getKey().toString(), entry.getValue().getString(null)); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/BlockStateResource.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/BlockStateResource.java index ff665c8c..1272cd3a 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/BlockStateResource.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/BlockStateResource.java @@ -30,7 +30,9 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import org.apache.commons.lang3.StringUtils; @@ -39,6 +41,7 @@ import com.flowpowered.math.vector.Vector3i; import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.resourcepack.PropertyCondition.All; import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess; import de.bluecolored.bluemap.core.util.MathUtils; import de.bluecolored.bluemap.core.world.BlockState; @@ -46,113 +49,138 @@ import ninja.leaping.configurate.gson.GsonConfigurationLoader; public class BlockStateResource { - + private List variants = new ArrayList<>(); private Collection multipart = new ArrayList<>(); - - private BlockStateResource() {} - - public Collection getModels(BlockState blockState){ + + private BlockStateResource() { + } + + public Collection getModels(BlockState blockState) { return getModels(blockState, Vector3i.ZERO); } - - public Collection getModels(BlockState blockState, Vector3i pos){ + + public Collection getModels(BlockState blockState, Vector3i pos) { Collection models = new ArrayList<>(); + + Variant allMatch = null; for (Variant variant : variants) { if (variant.condition.matches(blockState)) { + if (variant.condition instanceof All) { //only use "all" conditioned if nothing else matched + if (allMatch == null) allMatch = variant; + continue; + } + models.add(variant.getModel(pos)); return models; } } + if (allMatch != null) { + models.add(allMatch.getModel(pos)); + return models; + } + for (Variant variant : multipart) { if (variant.condition.matches(blockState)) { models.add(variant.getModel(pos)); } } - + return models; } - + private class Variant { - + private PropertyCondition condition = PropertyCondition.all(); private Collection> models = new ArrayList<>(); - + private double totalWeight; - - private Variant() {} - + + private Variant() { + } + public TransformedBlockModelResource getModel(Vector3i pos) { - double selection = MathUtils.hashToFloat(pos, 827364) * totalWeight; //random based on position + double selection = MathUtils.hashToFloat(pos, 827364) * totalWeight; // random based on position for (Weighted w : models) { selection -= w.weight; - if (selection < 0) return w.value; + if (selection < 0) + return w.value; } - + throw new RuntimeException("This line should never be reached!"); } - + public void updateTotalWeight() { totalWeight = 0d; for (Weighted w : models) { totalWeight += w.weight; } } - + } - + private static class Weighted { - + private T value; private double weight; - + public Weighted(T value, double weight) { this.value = value; this.weight = weight; } - + } public static Builder builder(FileAccess sourcesAccess, ResourcePack resourcePack) { return new Builder(sourcesAccess, resourcePack); } - + public static class Builder { + + private static final String JSON_COMMENT = "__comment"; + private final FileAccess sourcesAccess; private final ResourcePack resourcePack; - + private Builder(FileAccess sourcesAccess, ResourcePack resourcePack) { this.sourcesAccess = sourcesAccess; this.resourcePack = resourcePack; } - + public BlockStateResource build(String blockstateFile) throws IOException { - BlockStateResource blockState = new BlockStateResource(); ConfigurationNode config = GsonConfigurationLoader.builder() - .setSource(() -> new BufferedReader(new InputStreamReader(sourcesAccess.readFile(blockstateFile), StandardCharsets.UTF_8))) - .build() - .load(); - - //create variants + .setSource(() -> new BufferedReader( + new InputStreamReader(sourcesAccess.readFile(blockstateFile), StandardCharsets.UTF_8))) + .build().load(); + + if (!config.getNode("forge_marker").isVirtual()) { + return buildForge(config, blockstateFile); + } + + BlockStateResource blockState = new BlockStateResource(); + + // create variants for (Entry entry : config.getNode("variants").getChildrenMap().entrySet()) { + if (entry.getKey().equals(JSON_COMMENT)) continue; + try { String conditionString = entry.getKey().toString(); ConfigurationNode transformedModelNode = entry.getValue(); - + Variant variant = blockState.new Variant(); variant.condition = parseConditionString(conditionString); - variant.models = loadModels(transformedModelNode, blockstateFile); - + variant.models = loadModels(transformedModelNode, blockstateFile, null); + variant.updateTotalWeight(); - + blockState.variants.add(variant); } catch (Exception ex) { Logger.global.logWarning("Failed to parse a variant of " + blockstateFile + ": " + ex); } } - - //create multipart + + // create multipart for (ConfigurationNode partNode : config.getNode("multipart").getChildrenList()) { try { Variant variant = blockState.new Variant(); @@ -160,64 +188,64 @@ public BlockStateResource build(String blockstateFile) throws IOException { if (!whenNode.isVirtual()) { variant.condition = parseCondition(whenNode); } - variant.models = loadModels(partNode.getNode("apply"), blockstateFile); - + variant.models = loadModels(partNode.getNode("apply"), blockstateFile, null); + variant.updateTotalWeight(); - + blockState.multipart.add(variant); } catch (Exception ex) { Logger.global.logWarning("Failed to parse a multipart-part of " + blockstateFile + ": " + ex); } } - + return blockState; } - - private Collection> loadModels(ConfigurationNode node, String blockstateFile) { + + private Collection> loadModels(ConfigurationNode node, String blockstateFile, Map overrideTextures) { Collection> models = new ArrayList<>(); - + if (node.hasListChildren()) { for (ConfigurationNode modelNode : node.getChildrenList()) { try { - models.add(loadModel(modelNode)); + models.add(loadModel(modelNode, overrideTextures)); } catch (ParseResourceException ex) { Logger.global.logWarning("Failed to load a model trying to parse " + blockstateFile + ": " + ex); } } } else if (node.hasMapChildren()) { try { - models.add(loadModel(node)); + models.add(loadModel(node, overrideTextures)); } catch (ParseResourceException ex) { Logger.global.logWarning("Failed to load a model trying to parse " + blockstateFile + ": " + ex); } } - + return models; } - - private Weighted loadModel(ConfigurationNode node) throws ParseResourceException { - String modelPath = node.getNode("model").getString(); - if (modelPath == null) throw new ParseResourceException("No model defined!"); - - modelPath = ResourcePack.namespacedToAbsoluteResourcePath(modelPath, "models") + ".json"; - + + private Weighted loadModel(ConfigurationNode node, Map overrideTextures) throws ParseResourceException { + String namespacedModelPath = node.getNode("model").getString(); + if (namespacedModelPath == null) + throw new ParseResourceException("No model defined!"); + + String modelPath = ResourcePack.namespacedToAbsoluteResourcePath(namespacedModelPath, "models") + ".json"; + BlockModelResource model = resourcePack.blockModelResources.get(modelPath); if (model == null) { + BlockModelResource.Builder builder = BlockModelResource.builder(sourcesAccess, resourcePack); try { - model = BlockModelResource.builder(sourcesAccess, resourcePack).build(modelPath); + if (overrideTextures != null) model = builder.build(modelPath, overrideTextures); + else model = builder.build(modelPath); } catch (IOException e) { throw new ParseResourceException("Failed to load model " + modelPath, e); } - + resourcePack.blockModelResources.put(modelPath, model); } - - Vector2i rotation = new Vector2i( - node.getNode("x").getInt(0), - node.getNode("y").getInt(0) - ); + + Vector2i rotation = new Vector2i(node.getNode("x").getInt(0), node.getNode("y").getInt(0)); boolean uvLock = node.getNode("uvlock").getBoolean(false); - + TransformedBlockModelResource transformedModel = new TransformedBlockModelResource(rotation, uvLock, model); return new Weighted(transformedModel, node.getNode("weight").getDouble(1d)); } @@ -226,32 +254,36 @@ private PropertyCondition parseCondition(ConfigurationNode conditionNode) { List andConditions = new ArrayList<>(); for (Entry entry : conditionNode.getChildrenMap().entrySet()) { String key = entry.getKey().toString(); + if (key.equals(JSON_COMMENT)) continue; + if (key.equals("OR")) { List orConditions = new ArrayList<>(); for (ConfigurationNode orConditionNode : entry.getValue().getChildrenList()) { orConditions.add(parseCondition(orConditionNode)); } - andConditions.add(PropertyCondition.or(orConditions.toArray(new PropertyCondition[orConditions.size()]))); + andConditions.add( + PropertyCondition.or(orConditions.toArray(new PropertyCondition[orConditions.size()]))); } else { String[] values = StringUtils.split(entry.getValue().getString(""), '|'); andConditions.add(PropertyCondition.property(key, values)); } } - + return PropertyCondition.and(andConditions.toArray(new PropertyCondition[andConditions.size()])); } - + private PropertyCondition parseConditionString(String conditionString) throws IllegalArgumentException { List conditions = new ArrayList<>(); if (!conditionString.isEmpty() && !conditionString.equals("default") && !conditionString.equals("normal")) { String[] conditionSplit = StringUtils.split(conditionString, ','); for (String element : conditionSplit) { String[] keyval = StringUtils.split(element, "=", 2); - if (keyval.length < 2) throw new IllegalArgumentException("Condition-String '" + conditionString + "' is invalid!"); + if (keyval.length < 2) + throw new IllegalArgumentException("Condition-String '" + conditionString + "' is invalid!"); conditions.add(PropertyCondition.property(keyval[0], keyval[1])); } } - + PropertyCondition condition; if (conditions.isEmpty()) { condition = PropertyCondition.all(); @@ -260,8 +292,129 @@ private PropertyCondition parseConditionString(String conditionString) throws Il } else { condition = PropertyCondition.and(conditions.toArray(new PropertyCondition[conditions.size()])); } - + return condition; } + + private BlockStateResource buildForge(ConfigurationNode config, String blockstateFile) { + ConfigurationNode modelDefaults = config.getNode("defaults"); + + List variants = new ArrayList<>(); + for (Entry entry : config.getNode("variants").getChildrenMap().entrySet()) { + if (entry.getKey().equals(JSON_COMMENT)) continue; + if (isForgeStraightVariant(entry.getValue())) continue; + + // create variants for single property + List propertyVariants = new ArrayList<>(); + String key = entry.getKey().toString(); + for (Entry value : entry.getValue().getChildrenMap().entrySet()) { + if (value.getKey().equals(JSON_COMMENT)) continue; + + ForgeVariant variant = new ForgeVariant(); + variant.properties.put(key, value.getKey().toString()); + variant.node = value.getValue(); + propertyVariants.add(variant); + } + + // join variants + List oldVariants = variants; + variants = new ArrayList<>(oldVariants.size() * propertyVariants.size()); + for (ForgeVariant oldVariant : oldVariants) { + for (ForgeVariant addVariant : propertyVariants) { + variants.add(oldVariant.createMerge(addVariant)); + } + } + } + + //create all possible property-variants + BlockStateResource blockState = new BlockStateResource(); + for (ForgeVariant forgeVariant : variants) { + Variant variant = blockState.new Variant(); + + ConfigurationNode modelNode = forgeVariant.node.mergeValuesFrom(modelDefaults); + + Map textures = new HashMap<>(); + for (Entry entry : modelNode.getNode("textures").getChildrenMap().entrySet()) { + if (entry.getKey().equals(JSON_COMMENT)) continue; + + textures.putIfAbsent(entry.getKey().toString(), entry.getValue().getString(null)); + } + + List conditions = new ArrayList<>(forgeVariant.properties.size()); + for (Entry property : forgeVariant.properties.entrySet()) { + conditions.add(PropertyCondition.property(property.getKey(), property.getValue())); + } + variant.condition = PropertyCondition.and(conditions.toArray(new PropertyCondition[conditions.size()])); + + variant.models.addAll(loadModels(modelNode, blockstateFile, textures)); + + for (Entry entry : modelNode.getNode("submodel").getChildrenMap().entrySet()) { + if (entry.getKey().equals(JSON_COMMENT)) continue; + + variant.models.addAll(loadModels(entry.getValue(), blockstateFile, textures)); + } + + variant.updateTotalWeight(); + blockState.variants.add(variant); + } + + //create default straight variant + ConfigurationNode normalNode = config.getNode("variants", "normal"); + if (normalNode.isVirtual() || isForgeStraightVariant(normalNode)) { + normalNode.mergeValuesFrom(modelDefaults); + + Map textures = new HashMap<>(); + for (Entry entry : normalNode.getNode("textures").getChildrenMap().entrySet()) { + if (entry.getKey().equals(JSON_COMMENT)) continue; + + textures.putIfAbsent(entry.getKey().toString(), entry.getValue().getString(null)); + } + + Variant variant = blockState.new Variant(); + variant.condition = PropertyCondition.all(); + variant.models.addAll(loadModels(normalNode, blockstateFile, textures)); + + for (Entry entry : normalNode.getNode("submodel").getChildrenMap().entrySet()) { + if (entry.getKey().equals(JSON_COMMENT)) continue; + + variant.models.addAll(loadModels(entry.getValue(), blockstateFile, textures)); + } + + variant.updateTotalWeight(); + blockState.variants.add(variant); + } + + return blockState; + } + + private boolean isForgeStraightVariant(ConfigurationNode node) { + if (node.hasListChildren()) + return true; + + for (Entry entry : node.getChildrenMap().entrySet()) { + if (entry.getKey().equals(JSON_COMMENT)) continue; + if (!entry.getValue().hasMapChildren()) return true; + } + + return false; + } + + private class ForgeVariant { + public Map properties = new HashMap<>(); + public ConfigurationNode node = GsonConfigurationLoader.builder().build().createEmptyNode(); + + public ForgeVariant createMerge(ForgeVariant other) { + ForgeVariant merge = new ForgeVariant(); + + merge.properties.putAll(this.properties); + merge.properties.putAll(other.properties); + + merge.node.mergeValuesFrom(this.node); + merge.node.mergeValuesFrom(other.node); + + return merge; + } + } + } } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/PropertyCondition.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/PropertyCondition.java index e4f3c8f9..7923b743 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/PropertyCondition.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/PropertyCondition.java @@ -31,8 +31,8 @@ @FunctionalInterface public interface PropertyCondition { - static final PropertyCondition MATCH_ALL = state -> true; - static final PropertyCondition MATCH_NONE = state -> false; + static final PropertyCondition MATCH_ALL = new All(); + static final PropertyCondition MATCH_NONE = new None(); boolean matches(BlockState state); @@ -95,6 +95,24 @@ public boolean matches(BlockState state) { } + class All implements PropertyCondition { + + @Override + public boolean matches(BlockState state) { + return true; + } + + } + + class None implements PropertyCondition { + + @Override + public boolean matches(BlockState state) { + return false; + } + + } + static PropertyCondition all() { return MATCH_ALL; } 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 8a9d57ce..8b526292 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 @@ -38,6 +38,7 @@ import de.bluecolored.bluemap.core.resourcepack.BlockStateResource.Builder; import de.bluecolored.bluemap.core.resourcepack.fileaccess.CombinedFileAccess; import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess; +import de.bluecolored.bluemap.core.resourcepack.fileaccess.ResourcePackOldFormatFileAccess; import de.bluecolored.bluemap.core.world.BlockState; /** @@ -109,14 +110,15 @@ public void load(Collection sources) throws IOException { * @param sources The list of {@link File} sources. Each can be a folder or any zip-compressed file. (E.g. .zip or .jar) */ public void load(File... sources) { - try (CombinedFileAccess sourcesAccess = new CombinedFileAccess()){ + try (CombinedFileAccess combinedSourcesAccess = new CombinedFileAccess()){ for (File file : sources) { try { - sourcesAccess.addFileAccess(FileAccess.of(file)); + combinedSourcesAccess.addFileAccess(FileAccess.of(file)); } catch (IOException e) { Logger.global.logError("Failed to read ResourcePack: " + file, e); } } + FileAccess sourcesAccess = ResourcePackOldFormatFileAccess.from(combinedSourcesAccess); textures.reloadAllTextures(sourcesAccess); @@ -183,6 +185,8 @@ public static void downloadDefaultResource(File file) throws IOException { protected static String namespacedToAbsoluteResourcePath(String namespacedPath, String resourceTypeFolder) { String path = namespacedPath; + resourceTypeFolder = FileAccess.normalize(resourceTypeFolder); + int namespaceIndex = path.indexOf(':'); String namespace = "minecraft"; if (namespaceIndex != -1) { @@ -190,7 +194,11 @@ protected static String namespacedToAbsoluteResourcePath(String namespacedPath, path = path.substring(namespaceIndex + 1); } - path = "assets/" + namespace + "/" + resourceTypeFolder + "/" + FileAccess.normalize(path); + if (resourceTypeFolder.isEmpty()) { + path = "assets/" + namespace + "/" + FileAccess.normalize(path); + } else { + path = "assets/" + namespace + "/" + resourceTypeFolder + "/" + FileAccess.normalize(path); + } return path; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/fileaccess/FileAccess.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/fileaccess/FileAccess.java index 92e5a262..c75d35fd 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/fileaccess/FileAccess.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/fileaccess/FileAccess.java @@ -57,7 +57,9 @@ static String getFileName(String path) { } static String normalize(String path) { + if (path.isEmpty()) return path; if (path.charAt(path.length() - 1) == '/') path = path.substring(0, path.length() - 1); + if (path.isEmpty()) return path; if (path.charAt(0) == '/') path = path.substring(1); return path; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/fileaccess/ResourcePackOldFormatFileAccess.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/fileaccess/ResourcePackOldFormatFileAccess.java new file mode 100644 index 00000000..bfb71a1d --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/fileaccess/ResourcePackOldFormatFileAccess.java @@ -0,0 +1,115 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.core.resourcepack.fileaccess; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.regex.Pattern; + +/** + * This {@link FileAccess} tries to make 1.12/1.13/1.14 ResourcePacks compatible with each other + */ +public class ResourcePackOldFormatFileAccess implements FileAccess { + + private FileAccess parent; + + protected ResourcePackOldFormatFileAccess(FileAccess parent) { + this.parent = parent; + } + + @Override + public void close() throws IOException { + parent.close(); + } + + @Override + public InputStream readFile(String path) throws FileNotFoundException, IOException { + try { + return parent.readFile(path); + } catch (FileNotFoundException ex) { + for (String altPath : otherPathsToTry(path)) { + try { + return parent.readFile(altPath); + } catch (FileNotFoundException ex2) { + ex.addSuppressed(ex2); + } catch (IOException ex2) { + ex.addSuppressed(ex2); + throw ex; + } + } + + throw ex; + } + } + + @Override + public Collection listFiles(String path, boolean recursive) { + return parent.listFiles(path, recursive); + } + + @Override + public Collection listFolders(String path) { + return parent.listFolders(path); + } + + private Collection otherPathsToTry(String path){ + path = FileAccess.normalize(path); + List paths = new ArrayList<>(); + String[] parts = path.split(Pattern.quote("/")); + + //handle block/blocks folder-differences + if (parts.length >= 4 && parts[0].equals("assets") && parts[2].equals("models")) { + if (parts[3].equals("block")) { + parts[3] = "blocks"; + paths.add(String.join("/", parts)); + } else if (parts[3].equals("blocks")) { + parts[3] = "block"; + paths.add(String.join("/", parts)); + } else { + String[] newParts = new String[parts.length + 1]; + System.arraycopy(parts, 0, newParts, 0, 3); + System.arraycopy(parts, 3, newParts, 4, parts.length - 3); + + newParts[3] = "blocks"; + paths.add(String.join("/", newParts)); + + newParts[3] = "block"; + paths.add(String.join("/", newParts)); + } + } + + return paths; + } + + public static ResourcePackOldFormatFileAccess from(FileAccess source) { + if (source instanceof ResourcePackOldFormatFileAccess) return (ResourcePackOldFormatFileAccess) source; + return new ResourcePackOldFormatFileAccess(source); + } + +} 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 af326251..428e7a82 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -131,6 +131,9 @@ public synchronized void load() throws ExecutionException, IOException, Interrup if (loaded) return; unload(); //ensure nothing is left running (from a failed load or something) + //register reload command in case bluemap crashes during loading + Sponge.getCommandManager().register(this, new Commands(this).createStandaloneReloadCommand(), "bluemap"); + //load configs URL defaultSpongeConfig = SpongePlugin.class.getResource("/bluemap-sponge.conf"); URL spongeConfigDefaults = SpongePlugin.class.getResource("/bluemap-sponge-defaults.conf"); @@ -151,7 +154,7 @@ public synchronized void load() throws ExecutionException, IOException, Interrup if (!defaultResourceFile.exists()) { handleMissingResources(defaultResourceFile, configManager.getMainConfigFile()); unload(); - + Sponge.getCommandManager().register(this, new Commands(this).createStandaloneReloadCommand(), "bluemap"); return; } @@ -183,7 +186,7 @@ public synchronized void load() throws ExecutionException, IOException, Interrup File worldFolder = new File(mapConfig.getWorldPath()); if (!worldFolder.exists() || !worldFolder.isDirectory()) { - Logger.global.logError("Failed to load map '" + id + "': '" + worldFolder + "' does not exist or is no directory!", new IOException()); + Logger.global.logError("Failed to load map '" + id + "': '" + worldFolder.getCanonicalPath() + "' does not exist or is no directory!", new IOException()); continue; } @@ -276,6 +279,7 @@ public synchronized void load() throws ExecutionException, IOException, Interrup } //init commands + Sponge.getCommandManager().getOwnedBy(this).forEach(Sponge.getCommandManager()::removeMapping); Sponge.getCommandManager().register(this, new Commands(this).createRootCommand(), "bluemap"); //metrics @@ -410,7 +414,7 @@ private void handleMissingResources(File resourceFile, File mainConfigFile) { } else { Logger.global.logWarning("BlueMap is missing important resources!"); Logger.global.logWarning("You need to accept the download of the required files in order of BlueMap to work!"); - Logger.global.logWarning("Please check: " + mainConfigFile); + try { Logger.global.logWarning("Please check: " + mainConfigFile.getCanonicalPath()); } catch (IOException ignored) {} Logger.global.logInfo("If you have changed the config you can simply reload the plugin using: /bluemap reload"); } } diff --git a/BlueMapSponge/src/main/resources/bluemap-sponge-defaults.conf b/BlueMapSponge/src/main/resources/bluemap-sponge-defaults.conf index 62e34554..815ae48d 100644 --- a/BlueMapSponge/src/main/resources/bluemap-sponge-defaults.conf +++ b/BlueMapSponge/src/main/resources/bluemap-sponge-defaults.conf @@ -7,3 +7,4 @@ web { port: 8100 maxConnectionCount: 100 } +renderThreadCount: -2 diff --git a/BlueMapSponge/src/main/resources/bluemap-sponge.conf b/BlueMapSponge/src/main/resources/bluemap-sponge.conf index a0d5481a..3e68720e 100644 --- a/BlueMapSponge/src/main/resources/bluemap-sponge.conf +++ b/BlueMapSponge/src/main/resources/bluemap-sponge.conf @@ -52,8 +52,10 @@ web { # This changes the amount of threads that BlueMap will use to render the maps. # A higher value can improve render-speed but could impact performance on the host machine. # This should be always below or equal to the number of available processor-cores. -# If this value is commented out BlueMap tries to find the optimal thread count to max out render-performance -#renderThreadCount: 2 +# Zero or a negative value means the amount of of available processor-cores subtracted by the value. +# (So a value of -2 with 6 cores results in 4 render-processes) +# Default is -2 +renderThreadCount: -2 # This is an array with multiple configured maps. # You can define multiple maps, for different worlds with different render-settings here @@ -108,8 +110,8 @@ maps: [ tileSize: 32 # The View-Distance for hires tiles on the web-map (the value is the radius in tiles) - # Default is 3.5 - viewDistance: 3.5 + # Default is 4.5 + viewDistance: 4.5 } # LOWRES is the low-resolution render of the map. THats the model that you see if you zoom far out to get an overview. @@ -125,8 +127,8 @@ maps: [ pointsPerLowresTile: 50 # The View-Distance for lowres tiles on the web-map (the value is the radius in tiles) - # Default is 4 - viewDistance: 4 + # Default is 7 + viewDistance: 7 } }