Add some more resources-compatibility and tweak some values in configs and colors

This commit is contained in:
Blue (Lukas Rieger) 2020-01-04 20:04:47 +01:00
parent 22c2772c70
commit c60a62a6cd
14 changed files with 423 additions and 97 deletions

View File

@ -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();
}

View File

@ -7,3 +7,4 @@ web {
port: 8100
maxConnectionCount: 100
}
renderThreadCount: 0

View File

@ -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
}
}

View File

@ -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"));
}

View File

@ -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;

View File

@ -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<String, String> 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<Object, ? extends ConfigurationNode> entry : config.getNode("textures").getChildrenMap().entrySet()) {
if (entry.getKey().equals(JSON_COMMENT)) continue;
textures.putIfAbsent(entry.getKey().toString(), entry.getValue().getString(null));
}

View File

@ -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<Variant> variants = new ArrayList<>();
private Collection<Variant> multipart = new ArrayList<>();
private BlockStateResource() {}
public Collection<TransformedBlockModelResource> getModels(BlockState blockState){
private BlockStateResource() {
}
public Collection<TransformedBlockModelResource> getModels(BlockState blockState) {
return getModels(blockState, Vector3i.ZERO);
}
public Collection<TransformedBlockModelResource> getModels(BlockState blockState, Vector3i pos){
public Collection<TransformedBlockModelResource> getModels(BlockState blockState, Vector3i pos) {
Collection<TransformedBlockModelResource> 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<Weighted<TransformedBlockModelResource>> 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<TransformedBlockModelResource> 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<T> {
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<Object, ? extends ConfigurationNode> 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<Weighted<TransformedBlockModelResource>> loadModels(ConfigurationNode node, String blockstateFile) {
private Collection<Weighted<TransformedBlockModelResource>> loadModels(ConfigurationNode node, String blockstateFile, Map<String, String> overrideTextures) {
Collection<Weighted<TransformedBlockModelResource>> 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<TransformedBlockModelResource> 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<TransformedBlockModelResource> loadModel(ConfigurationNode node, Map<String, String> 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<TransformedBlockModelResource>(transformedModel, node.getNode("weight").getDouble(1d));
}
@ -226,32 +254,36 @@ private PropertyCondition parseCondition(ConfigurationNode conditionNode) {
List<PropertyCondition> andConditions = new ArrayList<>();
for (Entry<Object, ? extends ConfigurationNode> entry : conditionNode.getChildrenMap().entrySet()) {
String key = entry.getKey().toString();
if (key.equals(JSON_COMMENT)) continue;
if (key.equals("OR")) {
List<PropertyCondition> 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<PropertyCondition> 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<ForgeVariant> variants = new ArrayList<>();
for (Entry<Object, ? extends ConfigurationNode> entry : config.getNode("variants").getChildrenMap().entrySet()) {
if (entry.getKey().equals(JSON_COMMENT)) continue;
if (isForgeStraightVariant(entry.getValue())) continue;
// create variants for single property
List<ForgeVariant> propertyVariants = new ArrayList<>();
String key = entry.getKey().toString();
for (Entry<Object, ? extends ConfigurationNode> 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<ForgeVariant> 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<String, String> textures = new HashMap<>();
for (Entry<Object, ? extends ConfigurationNode> entry : modelNode.getNode("textures").getChildrenMap().entrySet()) {
if (entry.getKey().equals(JSON_COMMENT)) continue;
textures.putIfAbsent(entry.getKey().toString(), entry.getValue().getString(null));
}
List<PropertyCondition> conditions = new ArrayList<>(forgeVariant.properties.size());
for (Entry<String, String> 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<Object, ? extends ConfigurationNode> 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<String, String> textures = new HashMap<>();
for (Entry<Object, ? extends ConfigurationNode> 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<Object, ? extends ConfigurationNode> 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<Object, ? extends ConfigurationNode> entry : node.getChildrenMap().entrySet()) {
if (entry.getKey().equals(JSON_COMMENT)) continue;
if (!entry.getValue().hasMapChildren()) return true;
}
return false;
}
private class ForgeVariant {
public Map<String, String> 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;
}
}
}
}

View File

@ -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;
}

View File

@ -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<File> 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;
}

View File

@ -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;
}

View File

@ -0,0 +1,115 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* 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<String> listFiles(String path, boolean recursive) {
return parent.listFiles(path, recursive);
}
@Override
public Collection<String> listFolders(String path) {
return parent.listFolders(path);
}
private Collection<String> otherPathsToTry(String path){
path = FileAccess.normalize(path);
List<String> 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);
}
}

View File

@ -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");
}
}

View File

@ -7,3 +7,4 @@ web {
port: 8100
maxConnectionCount: 100
}
renderThreadCount: -2

View File

@ -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
}
}