Automatic mod-resource-loading, loading fabric netsted jars and more
@ -176,7 +176,7 @@ private synchronized void loadWorldsAndMaps() throws ConfigurationException, Int
|
||||
String name = mapConfig.getName();
|
||||
|
||||
Path worldFolder = mapConfig.getWorld();
|
||||
if (!Files.exists(worldFolder) || !Files.isDirectory(worldFolder)) {
|
||||
if (!Files.isDirectory(worldFolder)) {
|
||||
throw new ConfigurationException("Failed to load map '" + id + "': \n" +
|
||||
"'" + worldFolder.toAbsolutePath().normalize() + "' does not exist or is no directory!\n" +
|
||||
"Check if the 'world' setting in the config-file for that map is correct, or remove the entire config-file if you don't want that map.");
|
||||
@ -323,10 +323,48 @@ public synchronized ResourcePack getResourcePack() throws ConfigurationException
|
||||
});
|
||||
}
|
||||
|
||||
if (configs.getCoreConfig().isScanForModResources()) {
|
||||
|
||||
// load from mods folder
|
||||
Path modsFolder = serverInterface.getModsFolder().orElse(null);
|
||||
if (modsFolder != null && Files.isDirectory(modsFolder)) {
|
||||
try (Stream<Path> resourcepackFiles = Files.list(modsFolder)) {
|
||||
resourcepackFiles
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(file -> file.getFileName().toString().endsWith(".jar"))
|
||||
.forEach(resourcepackFile -> {
|
||||
try {
|
||||
resourcePack.loadResources(resourcepackFile);
|
||||
} catch (IOException e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// load from datapacks
|
||||
for (Path worldFolder : getWorldFolders()) {
|
||||
Path datapacksFolder = worldFolder.resolve("datapacks");
|
||||
if (!Files.isDirectory(datapacksFolder)) continue;
|
||||
|
||||
try (Stream<Path> resourcepackFiles = Files.list(worldFolder.resolve("datapacks"))) {
|
||||
resourcepackFiles
|
||||
.forEach(resourcepackFile -> {
|
||||
try {
|
||||
resourcePack.loadResources(resourcepackFile);
|
||||
} catch (IOException e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
resourcePack.loadResources(resourceExtensionsFile);
|
||||
resourcePack.loadResources(defaultResourceFile);
|
||||
|
||||
resourcePack.bake();
|
||||
Logger.global.logInfo("Resources loaded.");
|
||||
} catch (IOException | RuntimeException e) {
|
||||
throw new ConfigurationException("Failed to parse resources!\n" +
|
||||
"Is one of your resource-packs corrupted?", e);
|
||||
@ -337,6 +375,17 @@ public synchronized ResourcePack getResourcePack() throws ConfigurationException
|
||||
return resourcePack;
|
||||
}
|
||||
|
||||
private Collection<Path> getWorldFolders() {
|
||||
Set<Path> folders = new HashSet<>();
|
||||
for (MapConfig mapConfig : configs.getMapConfigs().values()) {
|
||||
Path folder = mapConfig.getWorld().toAbsolutePath().normalize();
|
||||
if (Files.isDirectory(folder)) {
|
||||
folders.add(folder);
|
||||
}
|
||||
}
|
||||
return folders;
|
||||
}
|
||||
|
||||
public BlueMapConfigs getConfigs() {
|
||||
return configs;
|
||||
}
|
||||
|
@ -2,14 +2,17 @@
|
||||
|
||||
import de.bluecolored.bluemap.common.config.storage.StorageConfig;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.util.Tristate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
@ -36,7 +39,7 @@ public BlueMapConfigs(ServerInterface serverInterface) throws ConfigurationExcep
|
||||
this.coreConfig = loadCoreConfig();
|
||||
this.webserverConfig = loadWebserverConfig();
|
||||
this.webappConfig = loadWebappConfig();
|
||||
this.pluginConfig = loadPluginConfig();
|
||||
this.pluginConfig = serverInterface.isPluginConfigEnabled() ? loadPluginConfig() : new PluginConfig();
|
||||
this.storageConfigs = Collections.unmodifiableMap(loadStorageConfigs());
|
||||
this.mapConfigs = Collections.unmodifiableMap(loadMapConfigs());
|
||||
}
|
||||
@ -75,11 +78,27 @@ private synchronized CoreConfig loadCoreConfig() throws ConfigurationException {
|
||||
Path configFolder = configFile.getParent();
|
||||
|
||||
if (!Files.exists(configFile)) {
|
||||
|
||||
// determine render-thread preset (very pessimistic, rather let people increase it themselves)
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
int availableCores = runtime.availableProcessors();
|
||||
long availableMemoryMiB = runtime.maxMemory() / 1024L / 1024L;
|
||||
int presetRenderThreadCount = 1;
|
||||
if (availableCores >= 6 && availableMemoryMiB >= 4096)
|
||||
presetRenderThreadCount = 2;
|
||||
if (availableCores >= 10 && availableMemoryMiB >= 8192)
|
||||
presetRenderThreadCount = 3;
|
||||
|
||||
try {
|
||||
Files.createDirectories(configFolder);
|
||||
Files.writeString(
|
||||
configFolder.resolve("core.conf"),
|
||||
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/core.conf")
|
||||
.setConditional("metrics", serverInterface.isMetricsEnabled() == Tristate.UNDEFINED)
|
||||
.setVariable("timestamp", LocalDateTime.now().withNano(0).toString())
|
||||
.setVariable("version", BlueMap.VERSION)
|
||||
.setVariable("implementation", "bukkit")
|
||||
.setVariable("render-thread-count", Integer.toString(presetRenderThreadCount))
|
||||
.build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
|
@ -18,6 +18,8 @@ public class CoreConfig {
|
||||
|
||||
private Path data = Path.of("bluemap");
|
||||
|
||||
private boolean scanForModResources = true;
|
||||
|
||||
public boolean isAcceptDownload() {
|
||||
return acceptDownload;
|
||||
}
|
||||
@ -39,4 +41,8 @@ public Path getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public boolean isScanForModResources() {
|
||||
return scanForModResources;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,10 +3,6 @@
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -60,6 +60,11 @@ default Optional<ServerWorld> getWorld(Path worldFolder) {
|
||||
*/
|
||||
Path getConfigFolder();
|
||||
|
||||
/**
|
||||
* Returns the folder that contains the mod-jars
|
||||
*/
|
||||
Optional<Path> getModsFolder();
|
||||
|
||||
/**
|
||||
* Gives the possibility to override the metrics-setting in the config
|
||||
*/
|
||||
@ -78,5 +83,8 @@ default Tristate isMetricsEnabled() {
|
||||
*/
|
||||
Optional<Player> getPlayer(UUID uuid);
|
||||
|
||||
default boolean isPluginConfigEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,20 +11,25 @@
|
||||
# ${timestamp}
|
||||
accept-download: false
|
||||
|
||||
# The folder where bluemap saves data-files it needs during runtime or to save e.g. the render-progress to resume it later.
|
||||
# Default is "bluemap"
|
||||
data: "bluemap"
|
||||
|
||||
# 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.
|
||||
# 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 1
|
||||
render-thread-count: 1
|
||||
render-thread-count: ${render-thread-count}
|
||||
|
||||
# Controls whether BlueMap should try to find and load mod-resources and datapacks from the server/world-directories.
|
||||
# Default is true
|
||||
scan-for-mod-resources: true
|
||||
${metrics<<
|
||||
# If this is true, BlueMap might send really basic metrics reports containing only the implementation-type and the version that is being used to https://metrics.bluecolored.de/bluemap/
|
||||
# This allows me to track the basic usage of BlueMap and helps me stay motivated to further develop this tool! Please leave it on :)
|
||||
# An example report looks like this: {"implementation":"${implementation}","version":"${version}"}
|
||||
# Default is true
|
||||
metrics: true
|
||||
>>}
|
||||
# The folder where bluemap saves data-files it needs during runtime or to save e.g. the render-progress to resume it later.
|
||||
# Default is "bluemap"
|
||||
data: "bluemap"
|
||||
>>}
|
@ -8,10 +8,6 @@
|
||||
# Default is true
|
||||
live-player-markers: true
|
||||
|
||||
# Download the skin from mojang-serves when a player joins your server, so it can be used for the player-markers.
|
||||
# Default is true
|
||||
skin-download: true
|
||||
|
||||
# A list of gamemodes that will prevent a player from appearing on the map.
|
||||
# Possible values are: survival, creative, spectator, adventure
|
||||
hidden-gamemodes: [
|
||||
@ -30,6 +26,10 @@ hide-invisible: true
|
||||
# Default is false
|
||||
hide-sneaking: false
|
||||
|
||||
# Download the skin from mojang-serves when a player joins your server, so it can be used for the player-markers.
|
||||
# Default is true
|
||||
skin-download: true
|
||||
|
||||
# The amount of players that is needed to pause BlueMap's render-threads.
|
||||
# -> If this amount of players or more is online, bluemap will stop rendering map-updates until enough players
|
||||
# have logged off again
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinWorkerThread;
|
||||
|
||||
public class BlueMap {
|
||||
|
||||
@ -60,7 +61,12 @@ public class BlueMap {
|
||||
|
||||
public static final ForkJoinPool THREAD_POOL = new ForkJoinPool(
|
||||
Runtime.getRuntime().availableProcessors(),
|
||||
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
|
||||
pool -> {
|
||||
ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
|
||||
thread.setContextClassLoader(BlueMap.class.getClassLoader()); // use plugin-intended classloader
|
||||
thread.setName("BlueMap-FJ-" + thread.getPoolIndex());
|
||||
return thread;
|
||||
},
|
||||
(thread, ex) -> {
|
||||
if (ex instanceof ClassNotFoundException && ex.getMessage().contains("RemovalCause")) {
|
||||
Logger.global.noFloodWarning("RemovalCauseError", ex.getMessage());
|
||||
|
@ -60,7 +60,10 @@ public static TextureGallery readTexturesFile(InputStream in) throws IOException
|
||||
Texture[] textures = ResourcesGson.INSTANCE.fromJson(reader, Texture[].class);
|
||||
gallery.nextId = textures.length;
|
||||
for (int ordinal = 0; ordinal < textures.length; ordinal++) {
|
||||
gallery.ordinalMap.put(textures[ordinal].getResourcePath(), ordinal);
|
||||
Texture texture = textures[ordinal];
|
||||
if (texture != null) {
|
||||
gallery.ordinalMap.put(textures[ordinal].getResourcePath(), ordinal);
|
||||
}
|
||||
}
|
||||
} catch (JsonIOException ex) {
|
||||
throw new IOException(ex);
|
||||
|
@ -19,6 +19,7 @@ public void write(JsonWriter out, Direction value) throws IOException {
|
||||
public Direction read(JsonReader in) throws IOException {
|
||||
String name = in.nextString();
|
||||
if (name.equalsIgnoreCase("bottom")) return Direction.DOWN;
|
||||
if (name.equalsIgnoreCase("top")) return Direction.UP;
|
||||
return Direction.fromString(name);
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,14 @@
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import de.bluecolored.bluemap.core.resources.resourcepack.blockmodel.Face;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import de.bluecolored.bluemap.core.util.math.Axis;
|
||||
import de.bluecolored.bluemap.core.util.math.Color;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumMap;
|
||||
|
||||
public class ResourcesGson {
|
||||
@ -37,4 +40,9 @@ private static Gson createGson() {
|
||||
|
||||
}
|
||||
|
||||
public static String nextStringOrBoolean(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.BOOLEAN) return Boolean.toString(in.nextBoolean());
|
||||
return in.nextString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
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.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
@ -31,13 +33,14 @@
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@DebugDump
|
||||
public class ResourcePack {
|
||||
public static final ResourcePath<BlockState> MISSING_BLOCK_STATE = new ResourcePath<>("bluemap", "missing");
|
||||
public static final ResourcePath<BlockModel> MISSING_BLOCK_MODEL = new ResourcePath<>("bluemap", "missing");
|
||||
public static final ResourcePath<Texture> MISSING_TEXTURE = new ResourcePath<>("bluemap", "missing");
|
||||
public static final ResourcePath<BlockModel> MISSING_BLOCK_MODEL = new ResourcePath<>("bluemap", "block/missing");
|
||||
public static final ResourcePath<Texture> MISSING_TEXTURE = new ResourcePath<>("bluemap", "block/missing");
|
||||
|
||||
private final Map<String, ResourcePath<BlockState>> blockStatePaths;
|
||||
private final Map<ResourcePath<BlockState>, BlockState> blockStates;
|
||||
@ -155,18 +158,38 @@ private BlockProperties loadBlockProperties(de.bluecolored.bluemap.core.world.Bl
|
||||
}
|
||||
|
||||
public synchronized void loadResources(Path root) throws IOException {
|
||||
Logger.global.logInfo("Loading resources from: " + root);
|
||||
Logger.global.logDebug("Loading resources from: " + root + " ...");
|
||||
loadResourcesInternal(root);
|
||||
}
|
||||
|
||||
private synchronized void loadResourcesInternal(Path root) 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.loadResources(fsRoot);
|
||||
this.loadResourcesInternal(fsRoot);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.global.logDebug("Failed to read '" + root + "': " + ex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// load nested jars from fabric.mod.json if present
|
||||
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);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.global.logDebug("Failed to read fabric.mod.json: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// do those in parallel
|
||||
CompletableFuture.allOf(
|
||||
@ -184,12 +207,14 @@ public synchronized void loadResources(Path root) throws IOException {
|
||||
return ResourcesGson.INSTANCE.fromJson(reader, BlockState.class);
|
||||
}
|
||||
}, blockStates));
|
||||
}),
|
||||
}, BlueMap.THREAD_POOL),
|
||||
|
||||
// load blockmodels
|
||||
CompletableFuture.runAsync(() -> {
|
||||
list(root.resolve("assets"))
|
||||
.map(path -> path.resolve("models").resolve("block"))
|
||||
.map(path -> path.resolve("models"))
|
||||
.flatMap(ResourcePack::list)
|
||||
.filter(path -> Pattern.matches("blocks?", path.getFileName().toString()))
|
||||
.filter(Files::isDirectory)
|
||||
.flatMap(ResourcePack::walk)
|
||||
.filter(path -> path.getFileName().toString().endsWith(".json"))
|
||||
@ -199,12 +224,14 @@ public synchronized void loadResources(Path root) throws IOException {
|
||||
return ResourcesGson.INSTANCE.fromJson(reader, BlockModel.class);
|
||||
}
|
||||
}, blockModels));
|
||||
}),
|
||||
}, BlueMap.THREAD_POOL),
|
||||
|
||||
// load textures
|
||||
CompletableFuture.runAsync(() -> {
|
||||
list(root.resolve("assets"))
|
||||
.map(path -> path.resolve("textures").resolve("block"))
|
||||
.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"))
|
||||
@ -215,7 +242,7 @@ public synchronized void loadResources(Path root) throws IOException {
|
||||
return Texture.from(resourcePath, ImageIO.read(in));
|
||||
}
|
||||
}, textures));
|
||||
}),
|
||||
}, BlueMap.THREAD_POOL),
|
||||
|
||||
// load colormaps
|
||||
CompletableFuture.runAsync(() -> {
|
||||
@ -227,7 +254,7 @@ public synchronized void loadResources(Path root) throws IOException {
|
||||
return ImageIO.read(in);
|
||||
}
|
||||
}, colormaps));
|
||||
}),
|
||||
}, BlueMap.THREAD_POOL),
|
||||
|
||||
// load block-color configs
|
||||
CompletableFuture.runAsync(() -> {
|
||||
@ -241,7 +268,7 @@ public synchronized void loadResources(Path root) throws IOException {
|
||||
Logger.global.logDebug("Failed to parse resource-file '" + file + "': " + ex);
|
||||
}
|
||||
});
|
||||
}),
|
||||
}, BlueMap.THREAD_POOL),
|
||||
|
||||
// load biome configs
|
||||
CompletableFuture.runAsync(() -> {
|
||||
@ -269,7 +296,7 @@ public synchronized void loadResources(Path root) throws IOException {
|
||||
}
|
||||
})
|
||||
);
|
||||
}),
|
||||
}, BlueMap.THREAD_POOL),
|
||||
|
||||
// load block-properties configs
|
||||
CompletableFuture.runAsync(() -> {
|
||||
@ -283,7 +310,7 @@ public synchronized void loadResources(Path root) throws IOException {
|
||||
Logger.global.logDebug("Failed to parse resource-file '" + file + "': " + ex);
|
||||
}
|
||||
});
|
||||
})
|
||||
}, BlueMap.THREAD_POOL)
|
||||
|
||||
).join();
|
||||
|
||||
@ -296,7 +323,7 @@ public synchronized void loadResources(Path root) throws IOException {
|
||||
}
|
||||
|
||||
public synchronized void bake() throws IOException {
|
||||
Logger.global.logInfo("Baking resources...");
|
||||
Logger.global.logDebug("Baking resources...");
|
||||
|
||||
// fill path maps
|
||||
blockStates.keySet().forEach(path -> blockStatePaths.put(path.getFormatted(), path));
|
||||
|
@ -5,6 +5,7 @@
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.resources.AbstractTypeAdapterFactory;
|
||||
import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@ -52,8 +53,11 @@ public Multipart read(JsonReader in, Gson gson) throws IOException {
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
String key = in.nextName();
|
||||
if (key.equals("when")) condition = readCondition(in);
|
||||
if (key.equals("apply")) variantSet = gson.fromJson(in, VariantSet.class);
|
||||
switch (key) {
|
||||
case "when": condition = readCondition(in); break;
|
||||
case "apply": variantSet = gson.fromJson(in, VariantSet.class); break;
|
||||
default: in.skipValue(); break;
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
|
||||
@ -71,7 +75,10 @@ public BlockStateCondition readCondition(JsonReader in) throws IOException {
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
String name = in.nextName();
|
||||
if (name.equals(JSON_COMMENT)) continue;
|
||||
if (name.equals(JSON_COMMENT)) {
|
||||
in.skipValue();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name.equals("OR")) {
|
||||
List<BlockStateCondition> orConditions = new ArrayList<>();
|
||||
@ -82,8 +89,17 @@ public BlockStateCondition readCondition(JsonReader in) throws IOException {
|
||||
in.endArray();
|
||||
andConditions.add(
|
||||
BlockStateCondition.or(orConditions.toArray(new BlockStateCondition[0])));
|
||||
} else if (name.equals("AND")) {
|
||||
List<BlockStateCondition> andArray = new ArrayList<>();
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
andArray.add(readCondition(in));
|
||||
}
|
||||
in.endArray();
|
||||
andConditions.add(
|
||||
BlockStateCondition.and(andArray.toArray(new BlockStateCondition[0])));
|
||||
} else {
|
||||
String[] values = StringUtils.split(in.nextString(), '|');
|
||||
String[] values = StringUtils.split(ResourcesGson.nextStringOrBoolean(in), '|');
|
||||
andConditions.add(BlockStateCondition.property(name, values));
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.resources.AbstractTypeAdapterFactory;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -60,7 +61,10 @@ public Variants read(JsonReader in, Gson gson) throws IOException {
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
String name = in.nextName();
|
||||
if (name.equals(JSON_COMMENT)) continue;
|
||||
if (name.equals(JSON_COMMENT)) {
|
||||
in.skipValue();
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockStateCondition condition = parseConditionString(name);
|
||||
VariantSet variantSet = gson.fromJson(in, VariantSet.class);
|
||||
@ -68,7 +72,7 @@ public Variants read(JsonReader in, Gson gson) throws IOException {
|
||||
|
||||
if (variantSet.getCondition() == BlockStateCondition.all()) {
|
||||
result.defaultVariant = variantSet;
|
||||
} else {
|
||||
} else if (variantSet.getCondition() != BlockStateCondition.none()) {
|
||||
result.variants.add(variantSet);
|
||||
}
|
||||
}
|
||||
@ -79,19 +83,23 @@ public Variants read(JsonReader in, Gson gson) throws IOException {
|
||||
|
||||
private BlockStateCondition parseConditionString(String conditionString) {
|
||||
List<BlockStateCondition> conditions = new ArrayList<>();
|
||||
boolean invalid = false;
|
||||
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) {
|
||||
Logger.global.logDebug("Failed to parse condition: Condition-String '" + conditionString + "' is invalid!");
|
||||
invalid = true;
|
||||
continue;
|
||||
}
|
||||
conditions.add(BlockStateCondition.property(keyval[0], keyval[1]));
|
||||
}
|
||||
}
|
||||
|
||||
BlockStateCondition condition;
|
||||
if (conditions.isEmpty()) {
|
||||
condition = BlockStateCondition.all();
|
||||
condition = invalid ? BlockStateCondition.none() : BlockStateCondition.all();
|
||||
} else if (conditions.size() == 1) {
|
||||
condition = conditions.get(0);
|
||||
} else {
|
||||
|
@ -623,7 +623,7 @@ private int loadMapTileCompressionFK(Compression compression) throws SQLExceptio
|
||||
return lookupFK("bluemap_map_tile_compression", "id", "compression", compression.getTypeId());
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
@SuppressWarnings({"SameParameterValue", "SqlResolve"})
|
||||
private int lookupFK(String table, String idField, String valueField, String value) throws SQLException, IOException {
|
||||
return recoveringConnection(connection -> {
|
||||
int key;
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
@ -239,6 +239,11 @@ public Path getConfigFolder() {
|
||||
return configFolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Player> getOnlinePlayers() {
|
||||
return Collections.emptyList();
|
||||
@ -249,6 +254,11 @@ public Optional<Player> getPlayer(UUID uuid) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPluginConfigEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
|
||||
|
@ -151,6 +151,11 @@ public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods"));
|
||||
}
|
||||
|
||||
public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity playerInstance) {
|
||||
if (this.serverInstance != server) return;
|
||||
|
||||
|
@ -151,6 +151,11 @@ public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods"));
|
||||
}
|
||||
|
||||
public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity playerInstance) {
|
||||
if (this.serverInstance != server) return;
|
||||
|
||||
|
@ -151,6 +151,11 @@ public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods"));
|
||||
}
|
||||
|
||||
public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity playerInstance) {
|
||||
if (this.serverInstance != server) return;
|
||||
|
||||
|
@ -151,6 +151,11 @@ public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods"));
|
||||
}
|
||||
|
||||
public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity playerInstance) {
|
||||
if (this.serverInstance != server) return;
|
||||
|
||||
|
@ -151,6 +151,11 @@ public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods"));
|
||||
}
|
||||
|
||||
public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity playerInstance) {
|
||||
if (this.serverInstance != server) return;
|
||||
|
||||
|
@ -164,6 +164,11 @@ public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods"));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerJoin(PlayerLoggedInEvent evt) {
|
||||
PlayerEntity playerInstance = evt.getPlayer();
|
||||
|
@ -164,6 +164,11 @@ public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods"));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerJoin(PlayerLoggedInEvent evt) {
|
||||
PlayerEntity playerInstance = evt.getPlayer();
|
||||
|
@ -164,6 +164,11 @@ public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods"));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerJoin(PlayerLoggedInEvent evt) {
|
||||
PlayerEntity playerInstance = evt.getPlayer();
|
||||
|
@ -163,6 +163,11 @@ public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods"));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerJoin(PlayerLoggedInEvent evt) {
|
||||
var playerInstance = evt.getPlayer();
|
||||
|
@ -163,6 +163,11 @@ public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods"));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerJoin(PlayerLoggedInEvent evt) {
|
||||
var playerInstance = evt.getPlayer();
|
||||
|
@ -199,6 +199,11 @@ public Path getConfigFolder() {
|
||||
return getDataFolder().toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods")); // in case this is a Bukkit/Forge hybrid
|
||||
}
|
||||
|
||||
public Plugin getPlugin() {
|
||||
return pluginInstance;
|
||||
}
|
||||
|
@ -235,6 +235,11 @@ public Path getConfigFolder() {
|
||||
return configurationDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getModsFolder() {
|
||||
return Optional.of(Path.of("mods"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Player> getOnlinePlayers() {
|
||||
return onlinePlayerMap.values();
|
||||
|