mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-22 02:26:00 +01:00
Refactored config management
This commit is contained in:
parent
3f634974bc
commit
e555d558b7
@ -24,17 +24,15 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common;
|
||||
|
||||
import de.bluecolored.bluemap.common.config.BlueMapConfigs;
|
||||
import de.bluecolored.bluemap.common.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.common.config.MapConfig;
|
||||
import de.bluecolored.bluemap.common.config.storage.StorageConfig;
|
||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerWorld;
|
||||
import de.bluecolored.bluemap.common.web.WebSettings;
|
||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||
import de.bluecolored.bluemap.core.config.ConfigManager;
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.core.config.old.CoreConfig;
|
||||
import de.bluecolored.bluemap.core.config.old.MapConfig;
|
||||
import de.bluecolored.bluemap.core.config.old.RenderConfig;
|
||||
import de.bluecolored.bluemap.core.config.old.WebServerConfig;
|
||||
import de.bluecolored.bluemap.core.config.storage.StorageConfig;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
@ -42,16 +40,16 @@
|
||||
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.storage.Storage;
|
||||
import de.bluecolored.bluemap.core.util.AtomicFileHelper;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@ -59,62 +57,70 @@
|
||||
*/
|
||||
@DebugDump
|
||||
public class BlueMapService {
|
||||
private final MinecraftVersion minecraftVersion;
|
||||
private final File configFolder;
|
||||
private final ThrowingFunction<File, UUID, IOException> worldUUIDProvider;
|
||||
private final ThrowingFunction<UUID, String, IOException> worldNameProvider;
|
||||
|
||||
private final ConfigManager configManager;
|
||||
private final de.bluecolored.bluemap.core.config.old.ConfigManager configManagerOld;
|
||||
|
||||
private CoreConfig coreConfig;
|
||||
private RenderConfig renderConfig;
|
||||
private WebServerConfig webServerConfig;
|
||||
private final ServerInterface serverInterface;
|
||||
private final BlueMapConfigs configs;
|
||||
|
||||
private final Map<Path, String> worldIds;
|
||||
private final Map<String, Storage> storages;
|
||||
|
||||
private Map<String, World> worlds;
|
||||
private Map<String, BmMap> maps;
|
||||
|
||||
private ResourcePack resourcePack;
|
||||
|
||||
private Map<UUID, World> worlds;
|
||||
private Map<String, BmMap> maps;
|
||||
private Map<String, Storage> mapStorages;
|
||||
|
||||
public BlueMapService(MinecraftVersion minecraftVersion, File configFolder) {
|
||||
this.minecraftVersion = minecraftVersion;
|
||||
this.configFolder = configFolder;
|
||||
|
||||
Map<File, UUID> uuids = new HashMap<>();
|
||||
this.worldUUIDProvider = file -> {
|
||||
UUID uuid = uuids.get(file);
|
||||
if (uuid == null) {
|
||||
uuid = UUID.randomUUID();
|
||||
uuids.put(file, uuid);
|
||||
}
|
||||
return uuid;
|
||||
};
|
||||
|
||||
this.worldNameProvider = uuid -> null;
|
||||
public BlueMapService(ServerInterface serverInterface) throws ConfigurationException {
|
||||
this.serverInterface = serverInterface;
|
||||
this.configs = new BlueMapConfigs(serverInterface);
|
||||
|
||||
this.worldIds = new HashMap<>();
|
||||
this.storages = new HashMap<>();
|
||||
|
||||
configManagerOld = new de.bluecolored.bluemap.core.config.old.ConfigManager();
|
||||
configManager = new ConfigManager(this.configFolder.toPath());
|
||||
}
|
||||
|
||||
public BlueMapService(MinecraftVersion minecraftVersion, ServerInterface serverInterface) {
|
||||
this.minecraftVersion = minecraftVersion;
|
||||
this.configFolder = serverInterface.getConfigFolder();
|
||||
this.worldUUIDProvider = serverInterface::getUUIDForWorld;
|
||||
this.worldNameProvider = serverInterface::getWorldName;
|
||||
public synchronized String getWorldId(Path worldFolder) throws IOException {
|
||||
// fast-path
|
||||
String id = worldIds.get(worldFolder);
|
||||
if (id != null) return id;
|
||||
|
||||
this.storages = new HashMap<>();
|
||||
// second try with normalized absolute path
|
||||
worldFolder = worldFolder.toAbsolutePath().normalize();
|
||||
id = worldIds.get(worldFolder);
|
||||
if (id != null) return id;
|
||||
|
||||
this.configManagerOld = new de.bluecolored.bluemap.core.config.old.ConfigManager();
|
||||
configManager = new ConfigManager(this.configFolder.toPath());
|
||||
// secure (slower) query with real path
|
||||
worldFolder = worldFolder.toRealPath();
|
||||
id = worldIds.get(worldFolder);
|
||||
if (id != null) return id;
|
||||
|
||||
// now we can be sure it wasn't loaded yet .. load
|
||||
|
||||
Path idFile = worldFolder.resolve("bluemap.id");
|
||||
id = this.serverInterface.getWorld(worldFolder)
|
||||
.flatMap(ServerWorld::getId)
|
||||
.orElse(null);
|
||||
|
||||
if (id != null) {
|
||||
// create/update id-file in worldfolder
|
||||
Files.writeString(idFile, id, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
|
||||
worldIds.put(worldFolder, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
if (!Files.exists(idFile)) {
|
||||
id = UUID.randomUUID().toString();
|
||||
Files.writeString(idFile, id, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
|
||||
worldIds.put(worldFolder, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
id = Files.readString(idFile);
|
||||
worldIds.put(worldFolder, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
public synchronized void createOrUpdateWebApp(boolean force) throws ConfigurationException {
|
||||
WebFilesManager webFilesManager = new WebFilesManager(getRenderConfig().getWebRoot());
|
||||
WebFilesManager webFilesManager = new WebFilesManager(configs.getWebappConfig().getWebroot());
|
||||
if (force || webFilesManager.needsUpdate()) {
|
||||
try {
|
||||
webFilesManager.updateFiles();
|
||||
@ -126,21 +132,19 @@ public synchronized void createOrUpdateWebApp(boolean force) throws Configuratio
|
||||
|
||||
public synchronized WebSettings updateWebAppSettings() throws ConfigurationException, InterruptedException {
|
||||
try {
|
||||
WebSettings webSettings = new WebSettings(new File(getRenderConfig().getWebRoot(),
|
||||
"data" + File.separator + "settings.json"));
|
||||
WebSettings webSettings = new WebSettings(configs.getWebappConfig().getWebroot().resolve("data").resolve("settings.json"));
|
||||
|
||||
webSettings.set(getRenderConfig().isUseCookies(), "useCookies");
|
||||
webSettings.set(getRenderConfig().isEnableFreeFlight(), "freeFlightEnabled");
|
||||
webSettings.set(configs.getWebappConfig().isUseCookies(), "useCookies");
|
||||
webSettings.set(configs.getWebappConfig().isEnableFreeFlight(), "freeFlightEnabled");
|
||||
webSettings.setAllMapsEnabled(false);
|
||||
for (BmMap map : getMaps().values()) {
|
||||
webSettings.setMapEnabled(true, map.getId());
|
||||
webSettings.setFrom(map);
|
||||
}
|
||||
int ordinal = 0;
|
||||
for (MapConfig map : getRenderConfig().getMapConfigs()) {
|
||||
if (!getMaps().containsKey(map.getId())) continue; //don't add not loaded maps
|
||||
webSettings.setOrdinal(ordinal++, map.getId());
|
||||
webSettings.setFrom(map);
|
||||
for (var entry : configs.getMapConfigs().entrySet()) {
|
||||
webSettings.setOrdinal(ordinal++, entry.getKey());
|
||||
webSettings.setFrom(entry.getValue(), entry.getKey());
|
||||
}
|
||||
webSettings.save();
|
||||
|
||||
@ -150,7 +154,7 @@ public synchronized WebSettings updateWebAppSettings() throws ConfigurationExcep
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized Map<UUID, World> getWorlds() throws ConfigurationException, InterruptedException {
|
||||
public synchronized Map<String, World> getWorlds() throws ConfigurationException, InterruptedException {
|
||||
if (worlds == null) loadWorldsAndMaps();
|
||||
return worlds;
|
||||
}
|
||||
@ -160,60 +164,52 @@ public synchronized Map<String, BmMap> getMaps() throws ConfigurationException,
|
||||
return maps;
|
||||
}
|
||||
|
||||
public synchronized Map<String, Storage> getMapStorages() throws ConfigurationException {
|
||||
if (mapStorages == null) {
|
||||
mapStorages = new HashMap<>();
|
||||
if (maps == null) {
|
||||
for (MapConfig mapConfig : getRenderConfig().getMapConfigs()) {
|
||||
mapStorages.put(mapConfig.getId(), getStorage(mapConfig.getStorage()));
|
||||
}
|
||||
} else {
|
||||
for (BmMap map : maps.values()) {
|
||||
mapStorages.put(map.getId(), map.getStorage());
|
||||
}
|
||||
}
|
||||
}
|
||||
return mapStorages;
|
||||
}
|
||||
|
||||
private synchronized void loadWorldsAndMaps() throws ConfigurationException, InterruptedException {
|
||||
maps = new HashMap<>();
|
||||
worlds = new HashMap<>();
|
||||
|
||||
for (MapConfig mapConfig : getRenderConfig().getMapConfigs()) {
|
||||
String id = mapConfig.getId();
|
||||
for (var entry : configs.getMapConfigs().entrySet()) {
|
||||
MapConfig mapConfig = entry.getValue();
|
||||
|
||||
String id = entry.getKey();
|
||||
String name = mapConfig.getName();
|
||||
|
||||
File worldFolder = new File(mapConfig.getWorldPath());
|
||||
if (!worldFolder.exists() || !worldFolder.isDirectory()) {
|
||||
Logger.global.logWarning("Failed to load map '" + id + "': '" + worldFolder.getAbsolutePath() + "' does not exist or is no directory!");
|
||||
continue;
|
||||
Path worldFolder = mapConfig.getWorld();
|
||||
if (!Files.exists(worldFolder) || !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.");
|
||||
}
|
||||
|
||||
UUID worldUUID;
|
||||
String worldId;
|
||||
try {
|
||||
worldUUID = worldUUIDProvider.apply(worldFolder);
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to load map '" + id + "': Failed to get UUID for the world!", e);
|
||||
continue;
|
||||
worldId = getWorldId(worldFolder);
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException("Failed to load map '" + id + "': \n" +
|
||||
"Could not load the ID for the world!\n" +
|
||||
"Make sure BlueMap has read and write access/permissions to the world-files for this map.",
|
||||
ex);
|
||||
}
|
||||
|
||||
World world = worlds.get(worldUUID);
|
||||
World world = worlds.get(worldId);
|
||||
if (world == null) {
|
||||
try {
|
||||
world = MCAWorld.load(worldFolder.toPath(), worldUUID, worldNameProvider.apply(worldUUID), mapConfig.getWorldSkyLight(), mapConfig.isIgnoreMissingLightData());
|
||||
worlds.put(worldUUID, world);
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to load map '" + id + "'!", e);
|
||||
continue;
|
||||
world = new MCAWorld(worldFolder, mapConfig.getWorldSkyLight(), mapConfig.isIgnoreMissingLightData());
|
||||
worlds.put(worldId, world);
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException("Failed to load world (" + worldId + ") for map '" + id + "'!\n" +
|
||||
"Is the level.dat of that world present and not corrupted?",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
Storage storage = getStorage(mapConfig.getStorage());
|
||||
|
||||
try {
|
||||
BmMap map = new BmMap(
|
||||
id,
|
||||
name,
|
||||
worldId,
|
||||
world,
|
||||
storage,
|
||||
getResourcePack(),
|
||||
@ -222,7 +218,7 @@ private synchronized void loadWorldsAndMaps() throws ConfigurationException, Int
|
||||
|
||||
maps.put(id, map);
|
||||
} catch (IOException ex) {
|
||||
Logger.global.logError("Failed to load map '" + id + "'!", ex);
|
||||
throw new ConfigurationException("Failed to load map '" + id + "'!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,65 +226,40 @@ private synchronized void loadWorldsAndMaps() throws ConfigurationException, Int
|
||||
maps = Collections.unmodifiableMap(maps);
|
||||
}
|
||||
|
||||
public synchronized Storage getStorage(String id) throws ConfigurationException {
|
||||
Storage storage = storages.get(id);
|
||||
public synchronized Storage getStorage(String storageId) throws ConfigurationException {
|
||||
Storage storage = storages.get(storageId);
|
||||
|
||||
if (storage == null) {
|
||||
storage = loadStorage(id);
|
||||
storages.put(id, storage);
|
||||
}
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
||||
private synchronized Storage loadStorage(String id) throws ConfigurationException {
|
||||
Logger.global.logInfo("Loading storage '" + id + "'...");
|
||||
|
||||
Path storageFolder = Paths.get("storages");
|
||||
Path storageConfigFolder = configManager.getConfigRoot().resolve(storageFolder);
|
||||
|
||||
if (!Files.exists(storageConfigFolder)){
|
||||
try {
|
||||
Files.createDirectories(storageConfigFolder);
|
||||
|
||||
Files.copy(
|
||||
Objects.requireNonNull(BlueMapService.class
|
||||
.getResourceAsStream("/de/bluecolored/bluemap/config/storages/file.conf")),
|
||||
storageConfigFolder.resolve("file.conf")
|
||||
);
|
||||
Files.copy(
|
||||
Objects.requireNonNull(BlueMapService.class
|
||||
.getResourceAsStream("/de/bluecolored/bluemap/config/storages/sql.conf")),
|
||||
storageConfigFolder.resolve("sql.conf")
|
||||
);
|
||||
} catch (IOException | NullPointerException ex) {
|
||||
Logger.global.logWarning("Failed to create default storage-configuration-files: " + ex);
|
||||
}
|
||||
StorageConfig storageConfig = getConfigs().getStorageConfigs().get(storageId);
|
||||
if (storageConfig == null) {
|
||||
throw new ConfigurationException("There is no storage-configuration for '" + storageId + "'!\n" +
|
||||
"You will either need to define that storage, or change the map-config to use a storage-config that exists.");
|
||||
}
|
||||
|
||||
try {
|
||||
ConfigurationNode node = configManager.loadConfig(storageFolder.resolve(id));
|
||||
StorageConfig storageConfig = Objects.requireNonNull(node.get(StorageConfig.class));
|
||||
Storage storage = storageConfig.getStorageType().create(node);
|
||||
storage = storageConfig.createStorage();
|
||||
storage.initialize();
|
||||
return storage;
|
||||
} catch (Exception ex) {
|
||||
throw new ConfigurationException(
|
||||
"BlueMap tried to create the storage '" + id + "' but something went wrong.\n" +
|
||||
"Check if that storage is configured correctly.",
|
||||
ex
|
||||
);
|
||||
throw new ConfigurationException("Failed to load and initialize the storage '" + storageId + "' for!",
|
||||
ex);
|
||||
}
|
||||
|
||||
storages.put(storageId, storage);
|
||||
}
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
||||
public synchronized ResourcePack getResourcePack() throws ConfigurationException, InterruptedException {
|
||||
if (resourcePack == null) {
|
||||
File defaultResourceFile = new File(getCoreConfig().getDataFolder(), "minecraft-client-" + minecraftVersion.getResource().getVersion().getVersionString() + ".jar");
|
||||
File resourceExtensionsFile = new File(getCoreConfig().getDataFolder(), "resourceExtensions.zip");
|
||||
MinecraftVersion minecraftVersion = serverInterface.getMinecraftVersion();
|
||||
|
||||
File textureExportFile = new File(getRenderConfig().getWebRoot(), "data" + File.separator + "textures.json");
|
||||
File defaultResourceFile = new File(configs.getCoreConfig().getData().toFile(), "minecraft-client-" + minecraftVersion.getResource().getVersion().getVersionString() + ".jar");
|
||||
File resourceExtensionsFile = new File(configs.getCoreConfig().getData().toFile(), "resourceExtensions.zip");
|
||||
|
||||
File resourcePackFolder = new File(configFolder, "resourcepacks");
|
||||
File textureExportFile = configs.getWebappConfig().getWebroot().resolve("data").resolve("textures.json").toFile();
|
||||
|
||||
File resourcePackFolder = serverInterface.getConfigFolder().resolve("resourcepacks").toFile();
|
||||
try {
|
||||
FileUtils.forceMkdir(resourcePackFolder);
|
||||
} catch (IOException ex) {
|
||||
@ -300,13 +271,16 @@ public synchronized ResourcePack getResourcePack() throws ConfigurationException
|
||||
}
|
||||
|
||||
if (!defaultResourceFile.exists()) {
|
||||
if (getCoreConfig().isDownloadAccepted()) {
|
||||
|
||||
if (configs.getCoreConfig().isAcceptDownload()) {
|
||||
//download file
|
||||
try {
|
||||
Logger.global.logInfo("Downloading " + minecraftVersion.getResource().getClientUrl() + " to " + defaultResourceFile + " ...");
|
||||
|
||||
FileUtils.forceMkdirParent(defaultResourceFile);
|
||||
FileUtils.copyURLToFile(new URL(minecraftVersion.getResource().getClientUrl()), defaultResourceFile, 10000, 10000);
|
||||
File tempResourceFile = new File(defaultResourceFile.getParentFile(), defaultResourceFile.getName() + ".filepart");
|
||||
if (tempResourceFile.exists()) FileUtils.forceDelete(tempResourceFile);
|
||||
FileUtils.copyURLToFile(new URL(minecraftVersion.getResource().getClientUrl()), tempResourceFile, 10000, 10000);
|
||||
AtomicFileHelper.move(tempResourceFile.toPath(), defaultResourceFile.toPath());
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException("Failed to download resources!", ex);
|
||||
}
|
||||
@ -350,7 +324,8 @@ public synchronized ResourcePack getResourcePack() throws ConfigurationException
|
||||
resourcePack.load(resources);
|
||||
resourcePack.saveTextureFile(textureExportFile);
|
||||
} catch (IOException | ParseResourceException e) {
|
||||
throw new ConfigurationException("Failed to parse resources!", e);
|
||||
throw new ConfigurationException("Failed to parse resources!\n" +
|
||||
"Is one of your resource-packs corrupted?", e);
|
||||
}
|
||||
|
||||
}
|
||||
@ -358,71 +333,8 @@ public synchronized ResourcePack getResourcePack() throws ConfigurationException
|
||||
return resourcePack;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public synchronized de.bluecolored.bluemap.core.config.old.ConfigManager getConfigManagerOld() {
|
||||
return configManagerOld;
|
||||
}
|
||||
|
||||
public File getCoreConfigFile() {
|
||||
return new File(configFolder, "core.conf");
|
||||
}
|
||||
|
||||
public synchronized CoreConfig getCoreConfig() throws ConfigurationException {
|
||||
if (coreConfig == null) {
|
||||
coreConfig = new CoreConfig(configManagerOld.loadOrCreate(
|
||||
getCoreConfigFile(),
|
||||
Plugin.class.getResource("/de/bluecolored/bluemap/core.conf"),
|
||||
Plugin.class.getResource("/de/bluecolored/bluemap/core-defaults.conf"),
|
||||
true,
|
||||
true
|
||||
));
|
||||
}
|
||||
|
||||
return coreConfig;
|
||||
}
|
||||
|
||||
public File getRenderConfigFile() {
|
||||
return new File(configFolder, "render.conf");
|
||||
}
|
||||
|
||||
public synchronized RenderConfig getRenderConfig() throws ConfigurationException {
|
||||
if (renderConfig == null) {
|
||||
renderConfig = new RenderConfig(configManagerOld.loadOrCreate(
|
||||
getRenderConfigFile(),
|
||||
Plugin.class.getResource("/de/bluecolored/bluemap/render.conf"),
|
||||
Plugin.class.getResource("/de/bluecolored/bluemap/render-defaults.conf"),
|
||||
true,
|
||||
true
|
||||
));
|
||||
}
|
||||
|
||||
return renderConfig;
|
||||
}
|
||||
|
||||
public File getWebServerConfigFile() {
|
||||
return new File(configFolder, "webserver.conf");
|
||||
}
|
||||
|
||||
public synchronized WebServerConfig getWebServerConfig() throws ConfigurationException {
|
||||
if (webServerConfig == null) {
|
||||
webServerConfig = new WebServerConfig(configManagerOld.loadOrCreate(
|
||||
getWebServerConfigFile(),
|
||||
Plugin.class.getResource("/de/bluecolored/bluemap/webserver.conf"),
|
||||
Plugin.class.getResource("/de/bluecolored/bluemap/webserver-defaults.conf"),
|
||||
true,
|
||||
true
|
||||
));
|
||||
}
|
||||
|
||||
return webServerConfig;
|
||||
}
|
||||
|
||||
public File getConfigFolder() {
|
||||
return configFolder;
|
||||
}
|
||||
|
||||
private interface ThrowingFunction<T, R, E extends Throwable> {
|
||||
R apply(T t) throws E;
|
||||
public BlueMapConfigs getConfigs() {
|
||||
return configs;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common;
|
||||
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.common.config.ConfigurationException;
|
||||
|
||||
public class MissingResourcesException extends ConfigurationException {
|
||||
private static final long serialVersionUID = 2084565069965755048L;
|
||||
|
@ -30,26 +30,30 @@
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Enumeration;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public class WebFilesManager {
|
||||
|
||||
private final File webRoot;
|
||||
private final Path webRoot;
|
||||
|
||||
public WebFilesManager(File webRoot) {
|
||||
public WebFilesManager(Path webRoot) {
|
||||
this.webRoot = webRoot;
|
||||
}
|
||||
|
||||
public boolean needsUpdate() {
|
||||
return !new File(webRoot, "index.html").exists();
|
||||
return !Files.exists(webRoot.resolve("index.html"));
|
||||
}
|
||||
|
||||
public void updateFiles() throws IOException {
|
||||
URL fileResource = getClass().getResource("/de/bluecolored/bluemap/webapp.zip");
|
||||
File tempFile = File.createTempFile("bluemap_webroot_extraction", null);
|
||||
|
||||
if (fileResource == null) throw new IOException("Failed to open bundled webapp.");
|
||||
|
||||
try {
|
||||
FileUtils.copyURLToFile(fileResource, tempFile, 10000, 10000);
|
||||
try (ZipFile zipFile = new ZipFile(tempFile)){
|
||||
@ -57,10 +61,10 @@ public void updateFiles() throws IOException {
|
||||
while(entries.hasMoreElements()) {
|
||||
ZipEntry zipEntry = entries.nextElement();
|
||||
if (zipEntry.isDirectory()) {
|
||||
File dir = new File(webRoot, zipEntry.getName());
|
||||
File dir = webRoot.resolve(zipEntry.getName()).toFile();
|
||||
FileUtils.forceMkdir(dir);
|
||||
} else {
|
||||
File target = new File(webRoot, zipEntry.getName());
|
||||
File target = webRoot.resolve(zipEntry.getName()).toFile();
|
||||
FileUtils.forceMkdirParent(target);
|
||||
FileUtils.copyInputStreamToFile(zipFile.getInputStream(zipEntry), target);
|
||||
}
|
||||
|
@ -1,202 +0,0 @@
|
||||
/*
|
||||
* 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.common.api;
|
||||
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.BlueMapWorld;
|
||||
import de.bluecolored.bluemap.common.api.marker.MarkerAPIImpl;
|
||||
import de.bluecolored.bluemap.common.api.render.RenderAPIImpl;
|
||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class BlueMapAPIImpl extends BlueMapAPI {
|
||||
|
||||
private static final String IMAGE_ROOT_PATH = "images";
|
||||
|
||||
public Plugin plugin;
|
||||
public RenderAPIImpl renderer;
|
||||
|
||||
public Map<UUID, BlueMapWorldImpl> worlds;
|
||||
public Map<String, BlueMapMapImpl> maps;
|
||||
|
||||
public BlueMapAPIImpl(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
this.renderer = new RenderAPIImpl(this, plugin);
|
||||
|
||||
worlds = new HashMap<>();
|
||||
for (World world : plugin.getWorlds()) {
|
||||
BlueMapWorldImpl w = new BlueMapWorldImpl(this, world);
|
||||
worlds.put(w.getUuid(), w);
|
||||
}
|
||||
|
||||
maps = new HashMap<>();
|
||||
for (BmMap map : plugin.getMapTypes()) {
|
||||
BlueMapMapImpl m = new BlueMapMapImpl(this, map);
|
||||
maps.put(m.getId(), m);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderAPIImpl getRenderAPI() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkerAPIImpl getMarkerAPI() throws IOException {
|
||||
return new MarkerAPIImpl(this, new File(plugin.getRenderConfig().getWebRoot(), "data" + File.separator + "markers.json"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<BlueMapMap> getMaps() {
|
||||
return Collections.unmodifiableCollection(maps.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<BlueMapWorld> getWorlds() {
|
||||
return Collections.unmodifiableCollection(worlds.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createImage(BufferedImage image, String path) throws IOException {
|
||||
path = path.replaceAll("[^a-zA-Z0-9_.\\-/]", "_");
|
||||
|
||||
Path webRoot = plugin.getRenderConfig().getWebRoot().toPath().toAbsolutePath();
|
||||
String separator = webRoot.getFileSystem().getSeparator();
|
||||
|
||||
Path webDataRoot = webRoot.resolve("data");
|
||||
Path imagePath = webDataRoot.resolve(Paths.get(IMAGE_ROOT_PATH, path.replace("/", separator) + ".png")).toAbsolutePath();
|
||||
|
||||
File imageFile = imagePath.toFile();
|
||||
if (imageFile.exists()) FileUtils.forceDelete(imageFile);
|
||||
de.bluecolored.bluemap.core.util.FileUtils.createFile(imageFile);
|
||||
|
||||
if (!ImageIO.write(image, "png", imagePath.toFile()))
|
||||
throw new IOException("The format 'png' is not supported!");
|
||||
|
||||
return webRoot.relativize(imagePath).toString().replace(separator, "/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> availableImages() throws IOException {
|
||||
Path webRoot = plugin.getRenderConfig().getWebRoot().toPath().toAbsolutePath();
|
||||
String separator = webRoot.getFileSystem().getSeparator();
|
||||
|
||||
Path imageRootPath = webRoot.resolve("data").resolve(IMAGE_ROOT_PATH).toAbsolutePath();
|
||||
|
||||
Map<String, String> availableImagesMap = new HashMap<>();
|
||||
|
||||
try (Stream<Path> fileStream = Files.walk(imageRootPath)){
|
||||
fileStream
|
||||
.filter(p -> !Files.isDirectory(p))
|
||||
.filter(p -> p.getFileName().toString().endsWith(".png"))
|
||||
.map(Path::toAbsolutePath)
|
||||
.forEach(p -> {
|
||||
try {
|
||||
String key = imageRootPath.relativize(p).toString();
|
||||
key = key
|
||||
.substring(0, key.length() - 4) //remove .png
|
||||
.replace(separator, "/");
|
||||
|
||||
String value = webRoot.relativize(p).toString()
|
||||
.replace(separator, "/");
|
||||
|
||||
availableImagesMap.put(key, value);
|
||||
} catch (IllegalArgumentException ignore) {}
|
||||
});
|
||||
}
|
||||
|
||||
return availableImagesMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getWebRoot() {
|
||||
return plugin.getRenderConfig().getWebRoot().toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBlueMapVersion() {
|
||||
return BlueMap.VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BlueMapWorld> getWorld(UUID uuid) {
|
||||
return Optional.ofNullable(worlds.get(uuid));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BlueMapMap> getMap(String id) {
|
||||
return Optional.ofNullable(maps.get(id));
|
||||
}
|
||||
|
||||
public BlueMapWorldImpl getWorldForUuid(UUID uuid) {
|
||||
BlueMapWorldImpl world = worlds.get(uuid);
|
||||
|
||||
if (world == null) throw new IllegalArgumentException("There is no world loaded with this UUID: " + uuid.toString());
|
||||
|
||||
return world;
|
||||
}
|
||||
|
||||
public BlueMapMapImpl getMapForId(String mapId) {
|
||||
BlueMapMapImpl map = maps.get(mapId);
|
||||
|
||||
if (map == null) throw new IllegalArgumentException("There is no map loaded with this id: " + mapId);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public void register() {
|
||||
try {
|
||||
BlueMapAPI.registerInstance(this);
|
||||
} catch (ExecutionException ex) {
|
||||
Logger.global.logError("BlueMapAPI: A BlueMapAPIListener threw an exception (onEnable)!", ex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister() {
|
||||
try {
|
||||
BlueMapAPI.unregisterInstance(this);
|
||||
} catch (ExecutionException ex) {
|
||||
Logger.global.logError("BlueMapAPI: A BlueMapAPIListener threw an exception (onDisable)!", ex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
/*
|
||||
* 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.common.api;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask;
|
||||
import de.bluecolored.bluemap.common.rendermanager.WorldRegionRenderTask;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class BlueMapMapImpl implements BlueMapMap {
|
||||
|
||||
private BlueMapAPIImpl api;
|
||||
private BmMap delegate;
|
||||
|
||||
protected BlueMapMapImpl(BlueMapAPIImpl api, BmMap delegate) {
|
||||
this.api = api;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return delegate.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return delegate.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlueMapWorldImpl getWorld() {
|
||||
return api.getWorldForUuid(delegate.getWorld().getUUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2i getTileSize() {
|
||||
return delegate.getHiresModelManager().getTileGrid().getGridSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2i getTileOffset() {
|
||||
return delegate.getHiresModelManager().getTileGrid().getOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTileFilter(Predicate<Vector2i> filter) {
|
||||
delegate.setTileFilter(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<Vector2i> getTileFilter() {
|
||||
return delegate.getTileFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFrozen() {
|
||||
return !api.plugin.getPluginState().getMapState(delegate).isUpdateEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setFrozen(boolean frozen) {
|
||||
if (isFrozen()) unfreeze();
|
||||
else freeze();
|
||||
}
|
||||
|
||||
private synchronized void unfreeze() {
|
||||
api.plugin.startWatchingMap(delegate);
|
||||
api.plugin.getPluginState().getMapState(delegate).setUpdateEnabled(true);
|
||||
api.plugin.getRenderManager().scheduleRenderTask(new MapUpdateTask(delegate));
|
||||
}
|
||||
|
||||
private synchronized void freeze() {
|
||||
api.plugin.stopWatchingMap(delegate);
|
||||
api.plugin.getPluginState().getMapState(delegate).setUpdateEnabled(false);
|
||||
api.plugin.getRenderManager().removeRenderTasksIf(task -> {
|
||||
if (task instanceof MapUpdateTask)
|
||||
return ((MapUpdateTask) task).getMap().equals(delegate);
|
||||
|
||||
if (task instanceof WorldRegionRenderTask)
|
||||
return ((WorldRegionRenderTask) task).getMap().equals(delegate);
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public BmMap getMapType() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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.common.api;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.BlueMapWorld;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
public class BlueMapWorldImpl implements BlueMapWorld {
|
||||
|
||||
private BlueMapAPIImpl api;
|
||||
private World delegate;
|
||||
|
||||
private Collection<BlueMapMap> maps;
|
||||
|
||||
protected BlueMapWorldImpl(BlueMapAPIImpl api, World delegate) {
|
||||
this.api = api;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getSaveFolder() {
|
||||
return delegate.getSaveFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUuid() {
|
||||
return delegate.getUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<BlueMapMap> getMaps() {
|
||||
if (maps == null) {
|
||||
maps = Collections.unmodifiableCollection(
|
||||
api.getMaps().stream()
|
||||
.filter(map -> map.getWorld().equals(this))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
return maps;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return delegate;
|
||||
}
|
||||
}
|
@ -1,242 +0,0 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2d;
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.marker.ExtrudeMarker;
|
||||
import de.bluecolored.bluemap.api.marker.Shape;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ExtrudeMarkerImpl extends ObjectMarkerImpl implements ExtrudeMarker {
|
||||
public static final String MARKER_TYPE = "extrude";
|
||||
|
||||
private Shape shape;
|
||||
private float shapeMinY;
|
||||
private float shapeMaxY;
|
||||
private boolean depthTest;
|
||||
private int lineWidth;
|
||||
private Color lineColor, fillColor;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public ExtrudeMarkerImpl(String id, BlueMapMap map, Vector3d position, Shape shape, float shapeMinY, float shapeMaxY) {
|
||||
super(id, map, position);
|
||||
|
||||
Objects.requireNonNull(shape);
|
||||
|
||||
this.shape = shape;
|
||||
this.shapeMinY = shapeMinY;
|
||||
this.shapeMaxY = shapeMaxY;
|
||||
this.lineWidth = 2;
|
||||
this.lineColor = new Color(255, 0, 0, 200);
|
||||
this.fillColor = new Color(200, 0, 0, 100);
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return MARKER_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getShape() {
|
||||
return this.shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShapeMinY() {
|
||||
return shapeMinY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShapeMaxY() {
|
||||
return shapeMaxY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setShape(Shape shape, float shapeMinY, float shapeMaxY) {
|
||||
Objects.requireNonNull(shape);
|
||||
|
||||
this.shape = shape;
|
||||
this.shapeMinY = shapeMinY;
|
||||
this.shapeMaxY = shapeMaxY;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDepthTestEnabled() {
|
||||
return this.depthTest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDepthTestEnabled(boolean enabled) {
|
||||
this.depthTest = enabled;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineWidth() {
|
||||
return lineWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLineWidth(int lineWidth) {
|
||||
this.lineWidth = lineWidth;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getLineColor() {
|
||||
return this.lineColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setLineColor(Color color) {
|
||||
Objects.requireNonNull(color);
|
||||
|
||||
this.lineColor = color;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getFillColor() {
|
||||
return this.fillColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setFillColor(Color color) {
|
||||
Objects.requireNonNull(color);
|
||||
|
||||
this.fillColor = color;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(BlueMapAPI api, ConfigurationNode markerNode, boolean overwriteChanges) throws MarkerFileFormatException {
|
||||
super.load(api, markerNode, overwriteChanges);
|
||||
|
||||
if (!overwriteChanges && hasUnsavedChanges) return;
|
||||
this.hasUnsavedChanges = false;
|
||||
|
||||
this.shape = readShape(markerNode.node("shape"));
|
||||
this.shapeMinY = markerNode.node("shapeMinY").getFloat(0);
|
||||
this.shapeMaxY = (float) markerNode.node("shapeMaxY").getDouble(255);
|
||||
this.depthTest = markerNode.node("depthTest").getBoolean(true);
|
||||
this.lineWidth = markerNode.node("lineWidth").getInt(2);
|
||||
this.lineColor = readColor(markerNode.node("lineColor"));
|
||||
this.fillColor = readColor(markerNode.node("fillColor"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(ConfigurationNode markerNode) throws SerializationException {
|
||||
super.save(markerNode);
|
||||
|
||||
writeShape(markerNode.node("shape"), this.shape);
|
||||
markerNode.node("shapeMinY").set(Math.round(shapeMinY * 1000f) / 1000f);
|
||||
markerNode.node("shapeMaxY").set(Math.round(shapeMaxY * 1000f) / 1000f);
|
||||
markerNode.node("depthTest").set(this.depthTest);
|
||||
markerNode.node("lineWidth").set(this.lineWidth);
|
||||
writeColor(markerNode.node("lineColor"), this.lineColor);
|
||||
writeColor(markerNode.node("fillColor"), this.fillColor);
|
||||
|
||||
hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
private Shape readShape(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
List<? extends ConfigurationNode> posNodes = node.childrenList();
|
||||
|
||||
if (posNodes.size() < 3) throw new MarkerFileFormatException("Failed to read shape: point-list has fewer than 3 entries!");
|
||||
|
||||
Vector2d[] positions = new Vector2d[posNodes.size()];
|
||||
for (int i = 0; i < positions.length; i++) {
|
||||
positions[i] = readShapePos(posNodes.get(i));
|
||||
}
|
||||
|
||||
return new Shape(positions);
|
||||
}
|
||||
|
||||
private static Vector2d readShapePos(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
ConfigurationNode nx, nz;
|
||||
nx = node.node("x");
|
||||
nz = node.node("z");
|
||||
|
||||
if (nx.virtual() || nz.virtual()) throw new MarkerFileFormatException("Failed to read shape position: Node x or z is not set!");
|
||||
|
||||
return new Vector2d(
|
||||
nx.getDouble(),
|
||||
nz.getDouble()
|
||||
);
|
||||
}
|
||||
|
||||
private static Color readColor(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
ConfigurationNode nr, ng, nb, na;
|
||||
nr = node.node("r");
|
||||
ng = node.node("g");
|
||||
nb = node.node("b");
|
||||
na = node.node("a");
|
||||
|
||||
if (nr.virtual() || ng.virtual() || nb.virtual()) throw new MarkerFileFormatException("Failed to read color: Node r,g or b is not set!");
|
||||
|
||||
float alpha = (float) na.getDouble(1);
|
||||
if (alpha < 0 || alpha > 1) throw new MarkerFileFormatException("Failed to read color: alpha value out of range (0-1)!");
|
||||
|
||||
try {
|
||||
return new Color(nr.getInt(), ng.getInt(), nb.getInt(), (int)(alpha * 255));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new MarkerFileFormatException("Failed to read color: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeShape(ConfigurationNode node, Shape shape) throws SerializationException {
|
||||
for (int i = 0; i < shape.getPointCount(); i++) {
|
||||
ConfigurationNode pointNode = node.appendListNode();
|
||||
Vector2d point = shape.getPoint(i);
|
||||
pointNode.node("x").set(Math.round(point.getX() * 1000d) / 1000d);
|
||||
pointNode.node("z").set(Math.round(point.getY() * 1000d) / 1000d);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeColor(ConfigurationNode node, Color color) throws SerializationException {
|
||||
int r = color.getRed();
|
||||
int g = color.getGreen();
|
||||
int b = color.getBlue();
|
||||
float a = color.getAlpha() / 255f;
|
||||
|
||||
node.node("r").set(r);
|
||||
node.node("g").set(g);
|
||||
node.node("b").set(b);
|
||||
node.node("a").set(a);
|
||||
}
|
||||
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.marker.HtmlMarker;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
public class HtmlMarkerImpl extends MarkerImpl implements HtmlMarker {
|
||||
public static final String MARKER_TYPE = "html";
|
||||
|
||||
private String html;
|
||||
private Vector2i anchor;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public HtmlMarkerImpl(String id, BlueMapMap map, Vector3d position, String html) {
|
||||
super(id, map, position);
|
||||
|
||||
this.html = html;
|
||||
this.anchor = new Vector2i(25, 45);
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return MARKER_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2i getAnchor() {
|
||||
return anchor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAnchor(Vector2i anchor) {
|
||||
this.anchor = anchor;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHtml() {
|
||||
return html;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setHtml(String html) {
|
||||
this.html = html;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void load(BlueMapAPI api, ConfigurationNode markerNode, boolean overwriteChanges) throws MarkerFileFormatException {
|
||||
super.load(api, markerNode, overwriteChanges);
|
||||
|
||||
if (!overwriteChanges && hasUnsavedChanges) return;
|
||||
this.hasUnsavedChanges = false;
|
||||
|
||||
this.html = markerNode.node("html").getString("");
|
||||
this.anchor = readAnchor(markerNode.node("anchor"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void save(ConfigurationNode markerNode) throws SerializationException {
|
||||
super.save(markerNode);
|
||||
|
||||
markerNode.node("html").set(this.html);
|
||||
writeAnchor(markerNode.node("anchor"), this.anchor);
|
||||
|
||||
hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
private static Vector2i readAnchor(ConfigurationNode node) {
|
||||
return new Vector2i(
|
||||
node.node("x").getInt(0),
|
||||
node.node("y").getInt(0)
|
||||
);
|
||||
}
|
||||
|
||||
private static void writeAnchor(ConfigurationNode node, Vector2i anchor) throws SerializationException {
|
||||
node.node("x").set(anchor.getX());
|
||||
node.node("y").set(anchor.getY());
|
||||
}
|
||||
|
||||
}
|
@ -1,208 +0,0 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.marker.Line;
|
||||
import de.bluecolored.bluemap.api.marker.LineMarker;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class LineMarkerImpl extends ObjectMarkerImpl implements LineMarker {
|
||||
public static final String MARKER_TYPE = "line";
|
||||
|
||||
private Line line;
|
||||
private boolean depthTest;
|
||||
private int lineWidth;
|
||||
private Color lineColor;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public LineMarkerImpl(String id, BlueMapMap map, Vector3d position, Line line) {
|
||||
super(id, map, position);
|
||||
|
||||
Objects.requireNonNull(line);
|
||||
|
||||
this.line = line;
|
||||
this.lineWidth = 2;
|
||||
this.lineColor = new Color(255, 0, 0, 200);
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return MARKER_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Line getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setLine(Line line) {
|
||||
Objects.requireNonNull(line);
|
||||
|
||||
this.line = line;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDepthTestEnabled() {
|
||||
return this.depthTest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDepthTestEnabled(boolean enabled) {
|
||||
this.depthTest = enabled;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineWidth() {
|
||||
return lineWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLineWidth(int lineWidth) {
|
||||
this.lineWidth = lineWidth;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getLineColor() {
|
||||
return this.lineColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setLineColor(Color color) {
|
||||
Objects.requireNonNull(color);
|
||||
|
||||
this.lineColor = color;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(BlueMapAPI api, ConfigurationNode markerNode, boolean overwriteChanges) throws MarkerFileFormatException {
|
||||
super.load(api, markerNode, overwriteChanges);
|
||||
|
||||
if (!overwriteChanges && hasUnsavedChanges) return;
|
||||
this.hasUnsavedChanges = false;
|
||||
|
||||
this.line = readLine(markerNode.node("line"));
|
||||
this.depthTest = markerNode.node("depthTest").getBoolean(true);
|
||||
this.lineWidth = markerNode.node("lineWidth").getInt(2);
|
||||
this.lineColor = readColor(markerNode.node("lineColor"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(ConfigurationNode markerNode) throws SerializationException {
|
||||
super.save(markerNode);
|
||||
|
||||
writeLine(markerNode.node("line"), this.line);
|
||||
markerNode.node("depthTest").set(this.depthTest);
|
||||
markerNode.node("lineWidth").set(this.lineWidth);
|
||||
writeColor(markerNode.node("lineColor"), this.lineColor);
|
||||
|
||||
hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
private Line readLine(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
List<? extends ConfigurationNode> posNodes = node.childrenList();
|
||||
|
||||
if (posNodes.size() < 3) throw new MarkerFileFormatException("Failed to read line: point-list has fewer than 2 entries!");
|
||||
|
||||
Vector3d[] positions = new Vector3d[posNodes.size()];
|
||||
for (int i = 0; i < positions.length; i++) {
|
||||
positions[i] = readLinePos(posNodes.get(i));
|
||||
}
|
||||
|
||||
return new Line(positions);
|
||||
}
|
||||
|
||||
private static Vector3d readLinePos(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
ConfigurationNode nx, ny, nz;
|
||||
nx = node.node("x");
|
||||
ny = node.node("y");
|
||||
nz = node.node("z");
|
||||
|
||||
if (nx.virtual() || ny.virtual() || nz.virtual()) throw new MarkerFileFormatException("Failed to read line position: Node x, y or z is not set!");
|
||||
|
||||
return new Vector3d(
|
||||
nx.getDouble(),
|
||||
ny.getDouble(),
|
||||
nz.getDouble()
|
||||
);
|
||||
}
|
||||
|
||||
private static Color readColor(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
ConfigurationNode nr, ng, nb, na;
|
||||
nr = node.node("r");
|
||||
ng = node.node("g");
|
||||
nb = node.node("b");
|
||||
na = node.node("a");
|
||||
|
||||
if (nr.virtual() || ng.virtual() || nb.virtual()) throw new MarkerFileFormatException("Failed to read color: Node r,g or b is not set!");
|
||||
|
||||
float alpha = (float) na.getDouble(1);
|
||||
if (alpha < 0 || alpha > 1) throw new MarkerFileFormatException("Failed to read color: alpha value out of range (0-1)!");
|
||||
|
||||
try {
|
||||
return new Color(nr.getInt(), ng.getInt(), nb.getInt(), (int)(alpha * 255));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new MarkerFileFormatException("Failed to read color: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeLine(ConfigurationNode node, Line line) throws SerializationException {
|
||||
for (int i = 0; i < line.getPointCount(); i++) {
|
||||
ConfigurationNode pointNode = node.appendListNode();
|
||||
Vector3d point = line.getPoint(i);
|
||||
pointNode.node("x").set(Math.round(point.getX() * 1000d) / 1000d);
|
||||
pointNode.node("y").set(Math.round(point.getY() * 1000d) / 1000d);
|
||||
pointNode.node("z").set(Math.round(point.getZ() * 1000d) / 1000d);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeColor(ConfigurationNode node, Color color) throws SerializationException {
|
||||
int r = color.getRed();
|
||||
int g = color.getGreen();
|
||||
int b = color.getBlue();
|
||||
float a = color.getAlpha() / 255f;
|
||||
|
||||
node.node("r").set(r);
|
||||
node.node("g").set(g);
|
||||
node.node("b").set(b);
|
||||
node.node("a").set(a);
|
||||
}
|
||||
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import de.bluecolored.bluemap.api.marker.MarkerAPI;
|
||||
import de.bluecolored.bluemap.api.marker.MarkerSet;
|
||||
import de.bluecolored.bluemap.common.api.BlueMapAPIImpl;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class MarkerAPIImpl implements MarkerAPI {
|
||||
|
||||
private BlueMapAPIImpl api;
|
||||
private File markerFile;
|
||||
private Map<String, MarkerSetImpl> markerSets;
|
||||
private Set<String> removedMarkerSets;
|
||||
|
||||
public MarkerAPIImpl(BlueMapAPIImpl api, File markerFile) throws IOException {
|
||||
this.api = api;
|
||||
this.markerFile = markerFile;
|
||||
|
||||
this.markerSets = new ConcurrentHashMap<>();
|
||||
this.removedMarkerSets = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MarkerSet> getMarkerSets() {
|
||||
return Collections.unmodifiableCollection(this.markerSets.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MarkerSet> getMarkerSet(String id) {
|
||||
return Optional.ofNullable(this.markerSets.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized MarkerSet createMarkerSet(String id) {
|
||||
MarkerSetImpl set = this.markerSets.get(id);
|
||||
|
||||
if (set == null) {
|
||||
set = new MarkerSetImpl(id);
|
||||
this.markerSets.put(id, set);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean removeMarkerSet(String id) {
|
||||
if (this.markerSets.remove(id) != null) {
|
||||
this.removedMarkerSets.add(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void load() throws IOException {
|
||||
load(true);
|
||||
}
|
||||
|
||||
private synchronized void load(boolean overwriteChanges) throws IOException {
|
||||
synchronized (MarkerAPIImpl.class) {
|
||||
Set<String> externallyRemovedSets = new HashSet<>(markerSets.keySet());
|
||||
|
||||
if (markerFile.exists() && markerFile.isFile()) {
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder().file(markerFile).build();
|
||||
ConfigurationNode node = loader.load();
|
||||
|
||||
for (ConfigurationNode markerSetNode : node.node("markerSets").childrenList()) {
|
||||
String setId = markerSetNode.node("id").getString();
|
||||
if (setId == null) {
|
||||
Logger.global.logDebug("Marker-API: Failed to load a markerset: No id defined!");
|
||||
continue;
|
||||
}
|
||||
|
||||
externallyRemovedSets.remove(setId);
|
||||
if (!overwriteChanges && removedMarkerSets.contains(setId)) continue;
|
||||
|
||||
MarkerSetImpl set = markerSets.get(setId);
|
||||
|
||||
try {
|
||||
if (set == null) {
|
||||
set = new MarkerSetImpl(setId);
|
||||
set.load(api, markerSetNode, true);
|
||||
} else {
|
||||
set.load(api, markerSetNode, overwriteChanges);
|
||||
}
|
||||
markerSets.put(setId, set);
|
||||
} catch (MarkerFileFormatException ex) {
|
||||
Logger.global.logDebug("Marker-API: Failed to load marker-set '" + setId + ": " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (overwriteChanges) {
|
||||
for (String setId : externallyRemovedSets) {
|
||||
markerSets.remove(setId);
|
||||
}
|
||||
|
||||
removedMarkerSets.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void save() throws IOException {
|
||||
synchronized (MarkerAPIImpl.class) {
|
||||
load(false);
|
||||
|
||||
FileUtils.createFile(markerFile);
|
||||
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder().file(markerFile).build();
|
||||
ConfigurationNode node = loader.createNode();
|
||||
|
||||
for (MarkerSetImpl set : markerSets.values()) {
|
||||
set.save(node.node("markerSets").appendListNode());
|
||||
}
|
||||
|
||||
loader.save(node);
|
||||
|
||||
removedMarkerSets.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class MarkerFileFormatException extends IOException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MarkerFileFormatException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public MarkerFileFormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MarkerFileFormatException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
@ -1,222 +0,0 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.marker.Marker;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class MarkerImpl implements Marker {
|
||||
|
||||
private final String id;
|
||||
private BlueMapMap map;
|
||||
private Vector3d postition;
|
||||
private double minDistance, maxDistance;
|
||||
private String label, link;
|
||||
private boolean newTab;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public MarkerImpl(String id, BlueMapMap map, Vector3d position) {
|
||||
Objects.requireNonNull(id);
|
||||
Objects.requireNonNull(map);
|
||||
Objects.requireNonNull(position);
|
||||
|
||||
this.id = id;
|
||||
this.map = map;
|
||||
this.postition = position;
|
||||
this.minDistance = 0;
|
||||
this.maxDistance = 100000;
|
||||
this.label = id;
|
||||
this.link = null;
|
||||
this.newTab = true;
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public abstract String getType();
|
||||
|
||||
@Override
|
||||
public BlueMapMap getMap() {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setMap(BlueMapMap map) {
|
||||
this.map = map;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3d getPosition() {
|
||||
return this.postition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setPosition(Vector3d position) {
|
||||
this.postition = position;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getMinDistance() {
|
||||
return this.minDistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setMinDistance(double minDistance) {
|
||||
this.minDistance = minDistance;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getMaxDistance() {
|
||||
return this.maxDistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setMaxDistance(double maxDistance) {
|
||||
this.maxDistance = maxDistance;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setLabel(String label) {
|
||||
this.label = label;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getLink() {
|
||||
return Optional.ofNullable(this.link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNewTab() {
|
||||
return this.newTab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setLink(String link, boolean newTab) {
|
||||
this.link = link;
|
||||
this.newTab = newTab;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void removeLink() {
|
||||
this.link = null;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
public synchronized void load(BlueMapAPI api, ConfigurationNode markerNode, boolean overwriteChanges) throws MarkerFileFormatException {
|
||||
if (!overwriteChanges && hasUnsavedChanges) return;
|
||||
hasUnsavedChanges = false;
|
||||
|
||||
//map
|
||||
String mapId = markerNode.node("map").getString();
|
||||
if (mapId == null) throw new MarkerFileFormatException("There is no map defined!");
|
||||
this.map = api.getMap(mapId).orElseThrow(() -> new MarkerFileFormatException("Could not resolve map with id: " + mapId));
|
||||
|
||||
//position
|
||||
this.postition = readPos(markerNode.node("position"));
|
||||
|
||||
//minmaxDistance
|
||||
this.minDistance = markerNode.node("minDistance").getDouble(0);
|
||||
this.maxDistance = markerNode.node("maxDistance").getDouble(100000);
|
||||
|
||||
//label
|
||||
this.label = markerNode.node("label").getString(this.id);
|
||||
|
||||
//link
|
||||
this.link = markerNode.node("link").getString();
|
||||
this.newTab = markerNode.node("newTab").getBoolean(true);
|
||||
}
|
||||
|
||||
public synchronized void save(ConfigurationNode markerNode) throws SerializationException {
|
||||
markerNode.node("id").set(this.id);
|
||||
markerNode.node("type").set(this.getType());
|
||||
markerNode.node("map").set(this.map.getId());
|
||||
writePos(markerNode.node("position"), this.postition);
|
||||
markerNode.node("minDistance").set(Math.round(this.minDistance * 1000d) / 1000d);
|
||||
markerNode.node("maxDistance").set(Math.round(this.maxDistance * 1000d) / 1000d);
|
||||
markerNode.node("label").set(this.label);
|
||||
markerNode.node("link").set(this.link);
|
||||
markerNode.node("newTab").set(this.newTab);
|
||||
|
||||
hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
private static Vector3d readPos(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
ConfigurationNode nx, ny, nz;
|
||||
nx = node.node("x");
|
||||
ny = node.node("y");
|
||||
nz = node.node("z");
|
||||
|
||||
if (nx.virtual() || ny.virtual() || nz.virtual()) throw new MarkerFileFormatException("Failed to read position: One of the nodes x,y or z is missing!");
|
||||
|
||||
return new Vector3d(
|
||||
nx.getDouble(),
|
||||
ny.getDouble(),
|
||||
nz.getDouble()
|
||||
);
|
||||
}
|
||||
|
||||
private static void writePos(ConfigurationNode node, Vector3d pos) throws SerializationException {
|
||||
node.node("x").set(Math.round(pos.getX() * 1000d) / 1000d);
|
||||
node.node("y").set(Math.round(pos.getY() * 1000d) / 1000d);
|
||||
node.node("z").set(Math.round(pos.getZ() * 1000d) / 1000d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MarkerImpl marker = (MarkerImpl) o;
|
||||
return id.equals(marker.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
}
|
@ -1,273 +0,0 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.marker.Line;
|
||||
import de.bluecolored.bluemap.api.marker.Marker;
|
||||
import de.bluecolored.bluemap.api.marker.MarkerSet;
|
||||
import de.bluecolored.bluemap.api.marker.Shape;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class MarkerSetImpl implements MarkerSet {
|
||||
|
||||
private final String id;
|
||||
private String label;
|
||||
private boolean toggleable;
|
||||
private boolean isDefaultHidden;
|
||||
private final Map<String, MarkerImpl> markers;
|
||||
|
||||
private final Set<String> removedMarkers;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public MarkerSetImpl(String id) {
|
||||
this.id = id;
|
||||
this.label = id;
|
||||
this.toggleable = true;
|
||||
this.isDefaultHidden = false;
|
||||
this.markers = new ConcurrentHashMap<>();
|
||||
|
||||
this.removedMarkers = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setLabel(String label) {
|
||||
this.label = label;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToggleable() {
|
||||
return this.toggleable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setToggleable(boolean toggleable) {
|
||||
this.toggleable = toggleable;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefaultHidden() {
|
||||
return this.isDefaultHidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setDefaultHidden(boolean defaultHide) {
|
||||
this.isDefaultHidden = defaultHide;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Marker> getMarkers() {
|
||||
return Collections.unmodifiableCollection(markers.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Marker> getMarker(String id) {
|
||||
return Optional.ofNullable(markers.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized POIMarkerImpl createPOIMarker(String id, BlueMapMap map, Vector3d position) {
|
||||
removeMarker(id);
|
||||
|
||||
POIMarkerImpl marker = new POIMarkerImpl(id, map, position);
|
||||
markers.put(id, marker);
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HtmlMarkerImpl createHtmlMarker(String id, BlueMapMap map, Vector3d position, String html) {
|
||||
removeMarker(id);
|
||||
|
||||
HtmlMarkerImpl marker = new HtmlMarkerImpl(id, map, position, html);
|
||||
markers.put(id, marker);
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ShapeMarkerImpl createShapeMarker(String id, BlueMapMap map, Vector3d position, Shape shape, float y) {
|
||||
removeMarker(id);
|
||||
|
||||
ShapeMarkerImpl marker = new ShapeMarkerImpl(id, map, position, shape, y);
|
||||
markers.put(id, marker);
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtrudeMarkerImpl createExtrudeMarker(String id, BlueMapMap map, Vector3d position, Shape shape, float minY, float maxY) {
|
||||
removeMarker(id);
|
||||
|
||||
ExtrudeMarkerImpl marker = new ExtrudeMarkerImpl(id, map, position, shape, minY, maxY);
|
||||
markers.put(id, marker);
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineMarkerImpl createLineMarker(String id, BlueMapMap map, Vector3d position, Line line) {
|
||||
removeMarker(id);
|
||||
|
||||
LineMarkerImpl marker = new LineMarkerImpl(id, map, position, line);
|
||||
markers.put(id, marker);
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean removeMarker(String id) {
|
||||
if (markers.remove(id) != null) {
|
||||
removedMarkers.add(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized void load(BlueMapAPI api, ConfigurationNode node, boolean overwriteChanges) throws MarkerFileFormatException {
|
||||
BlueMapMap dummyMap = api.getMaps().iterator().next();
|
||||
Shape dummyShape = Shape.createRect(0d, 0d, 1d, 1d);
|
||||
Line dummyLine = new Line(Vector3d.ZERO, Vector3d.ONE);
|
||||
|
||||
Set<String> externallyRemovedMarkers = new HashSet<>(this.markers.keySet());
|
||||
for (ConfigurationNode markerNode : node.node("marker").childrenList()) {
|
||||
String id = markerNode.node("id").getString();
|
||||
String type = markerNode.node("type").getString();
|
||||
|
||||
if (id == null || type == null) {
|
||||
Logger.global.logDebug("Marker-API: Failed to load a marker in the set '" + this.id + "': No id or type defined!");
|
||||
continue;
|
||||
}
|
||||
|
||||
externallyRemovedMarkers.remove(id);
|
||||
if (!overwriteChanges && removedMarkers.contains(id)) continue;
|
||||
|
||||
MarkerImpl marker = markers.get(id);
|
||||
|
||||
try {
|
||||
if (marker == null || !marker.getType().equals(type)) {
|
||||
switch (type) {
|
||||
case HtmlMarkerImpl.MARKER_TYPE :
|
||||
marker = new HtmlMarkerImpl(id, dummyMap, Vector3d.ZERO, "");
|
||||
break;
|
||||
case POIMarkerImpl.MARKER_TYPE:
|
||||
marker = new POIMarkerImpl(id, dummyMap, Vector3d.ZERO);
|
||||
break;
|
||||
case ShapeMarkerImpl.MARKER_TYPE:
|
||||
marker = new ShapeMarkerImpl(id, dummyMap, Vector3d.ZERO, dummyShape, 0f);
|
||||
break;
|
||||
case ExtrudeMarkerImpl.MARKER_TYPE:
|
||||
marker = new ExtrudeMarkerImpl(id, dummyMap, Vector3d.ZERO, dummyShape, 0f, 1f);
|
||||
break;
|
||||
case LineMarkerImpl.MARKER_TYPE:
|
||||
marker = new LineMarkerImpl(id, dummyMap, Vector3d.ZERO, dummyLine);
|
||||
break;
|
||||
default:
|
||||
Logger.global.logDebug("Marker-API: Failed to load marker '" + id + "' in the set '" + this.id + "': Unknown marker-type '" + type + "'!");
|
||||
continue;
|
||||
}
|
||||
|
||||
marker.load(api, markerNode, true);
|
||||
} else {
|
||||
marker.load(api, markerNode, overwriteChanges);
|
||||
}
|
||||
|
||||
if (overwriteChanges) {
|
||||
markers.put(id, marker);
|
||||
} else {
|
||||
markers.putIfAbsent(id, marker);
|
||||
}
|
||||
} catch (MarkerFileFormatException ex) {
|
||||
Logger.global.logDebug("Marker-API: Failed to load marker '" + id + "' in the set '" + this.id + "': " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (overwriteChanges) {
|
||||
for (String id : externallyRemovedMarkers) {
|
||||
markers.remove(id);
|
||||
}
|
||||
|
||||
this.removedMarkers.clear();
|
||||
}
|
||||
|
||||
if (!overwriteChanges && hasUnsavedChanges) return;
|
||||
hasUnsavedChanges = false;
|
||||
|
||||
this.label = node.node("label").getString(id);
|
||||
this.toggleable = node.node("toggleable").getBoolean(true);
|
||||
this.isDefaultHidden = node.node("defaultHide").getBoolean(false);
|
||||
}
|
||||
|
||||
public synchronized void save(ConfigurationNode node) throws SerializationException {
|
||||
node.node("id").set(this.id);
|
||||
node.node("label").set(this.label);
|
||||
node.node("toggleable").set(this.toggleable);
|
||||
node.node("defaultHide").set(this.isDefaultHidden);
|
||||
|
||||
for (MarkerImpl marker : markers.values()) {
|
||||
marker.save(node.node("marker").appendListNode());
|
||||
}
|
||||
|
||||
removedMarkers.clear();
|
||||
this.hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MarkerSetImpl markerSet = (MarkerSetImpl) o;
|
||||
return id.equals(markerSet.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.marker.ObjectMarker;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
public abstract class ObjectMarkerImpl extends MarkerImpl implements ObjectMarker {
|
||||
|
||||
private String detail;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public ObjectMarkerImpl(String id, BlueMapMap map, Vector3d position) {
|
||||
super(id, map, position);
|
||||
|
||||
this.detail = null;
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDetail() {
|
||||
if (detail == null) return getLabel();
|
||||
return detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDetail(String detail) {
|
||||
this.detail = detail;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(BlueMapAPI api, ConfigurationNode markerNode, boolean overwriteChanges) throws MarkerFileFormatException {
|
||||
super.load(api, markerNode, overwriteChanges);
|
||||
|
||||
if (!overwriteChanges && hasUnsavedChanges) return;
|
||||
this.hasUnsavedChanges = false;
|
||||
|
||||
this.detail = markerNode.node("detail").getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(ConfigurationNode markerNode) throws SerializationException {
|
||||
super.save(markerNode);
|
||||
|
||||
if (this.detail != null) markerNode.node("detail").set(this.detail);
|
||||
|
||||
hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.marker.POIMarker;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
public class POIMarkerImpl extends MarkerImpl implements POIMarker {
|
||||
public static final String MARKER_TYPE = "poi";
|
||||
|
||||
private String iconAddress;
|
||||
private Vector2i anchor;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public POIMarkerImpl(String id, BlueMapMap map, Vector3d position) {
|
||||
super(id, map, position);
|
||||
|
||||
this.iconAddress = "assets/poi.svg";
|
||||
this.anchor = new Vector2i(25, 45);
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return MARKER_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconAddress() {
|
||||
return iconAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2i getAnchor() {
|
||||
return anchor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setIcon(String iconAddress, Vector2i anchor) {
|
||||
this.iconAddress = iconAddress;
|
||||
this.anchor = anchor;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void load(BlueMapAPI api, ConfigurationNode markerNode, boolean overwriteChanges) throws MarkerFileFormatException {
|
||||
super.load(api, markerNode, overwriteChanges);
|
||||
|
||||
if (!overwriteChanges && hasUnsavedChanges) return;
|
||||
this.hasUnsavedChanges = false;
|
||||
|
||||
this.iconAddress = markerNode.node("icon").getString("assets/poi.svg");
|
||||
|
||||
ConfigurationNode anchorNode = markerNode.node("anchor");
|
||||
if (anchorNode.virtual()) anchorNode = markerNode.node("iconAnchor"); //fallback to deprecated "iconAnchor"
|
||||
this.anchor = readAnchor(anchorNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void save(ConfigurationNode markerNode) throws SerializationException {
|
||||
super.save(markerNode);
|
||||
|
||||
markerNode.node("icon").set(this.iconAddress);
|
||||
writeAnchor(markerNode.node("anchor"), this.anchor);
|
||||
|
||||
hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
private static Vector2i readAnchor(ConfigurationNode node) {
|
||||
return new Vector2i(
|
||||
node.node("x").getInt(0),
|
||||
node.node("y").getInt(0)
|
||||
);
|
||||
}
|
||||
|
||||
private static void writeAnchor(ConfigurationNode node, Vector2i anchor) throws SerializationException {
|
||||
node.node("x").set(anchor.getX());
|
||||
node.node("y").set(anchor.getY());
|
||||
}
|
||||
|
||||
}
|
@ -1,236 +0,0 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2d;
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.marker.Shape;
|
||||
import de.bluecolored.bluemap.api.marker.ShapeMarker;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ShapeMarkerImpl extends ObjectMarkerImpl implements ShapeMarker {
|
||||
public static final String MARKER_TYPE = "shape";
|
||||
|
||||
private Shape shape;
|
||||
private float shapeY;
|
||||
private boolean depthTest;
|
||||
private int lineWidth;
|
||||
private Color lineColor, fillColor;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public ShapeMarkerImpl(String id, BlueMapMap map, Vector3d position, Shape shape, float shapeY) {
|
||||
super(id, map, position);
|
||||
|
||||
Objects.requireNonNull(shape);
|
||||
|
||||
this.shape = shape;
|
||||
this.shapeY = shapeY;
|
||||
this.lineWidth = 2;
|
||||
this.lineColor = new Color(255, 0, 0, 200);
|
||||
this.fillColor = new Color(200, 0, 0, 100);
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return MARKER_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getShape() {
|
||||
return this.shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShapeY() {
|
||||
return this.shapeY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setShape(Shape shape, float shapeY) {
|
||||
Objects.requireNonNull(shape);
|
||||
|
||||
this.shape = shape;
|
||||
this.shapeY = shapeY;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDepthTestEnabled() {
|
||||
return this.depthTest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDepthTestEnabled(boolean enabled) {
|
||||
this.depthTest = enabled;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineWidth() {
|
||||
return lineWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLineWidth(int lineWidth) {
|
||||
this.lineWidth = lineWidth;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getLineColor() {
|
||||
return this.lineColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setLineColor(Color color) {
|
||||
Objects.requireNonNull(color);
|
||||
|
||||
this.lineColor = color;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getFillColor() {
|
||||
return this.fillColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setFillColor(Color color) {
|
||||
Objects.requireNonNull(color);
|
||||
|
||||
this.fillColor = color;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(BlueMapAPI api, ConfigurationNode markerNode, boolean overwriteChanges) throws MarkerFileFormatException {
|
||||
super.load(api, markerNode, overwriteChanges);
|
||||
|
||||
if (!overwriteChanges && hasUnsavedChanges) return;
|
||||
this.hasUnsavedChanges = false;
|
||||
|
||||
this.shape = readShape(markerNode.node("shape"));
|
||||
this.shapeY = (float) markerNode.node("shapeY").getDouble(markerNode.node("height").getDouble(64)); // fallback to deprecated "height"
|
||||
this.depthTest = markerNode.node("depthTest").getBoolean(true);
|
||||
this.lineWidth = markerNode.node("lineWidth").getInt(2);
|
||||
|
||||
ConfigurationNode lineColorNode = markerNode.node("lineColor");
|
||||
if (lineColorNode.virtual()) lineColorNode = markerNode.node("borderColor"); // fallback to deprecated "borderColor"
|
||||
this.lineColor = readColor(lineColorNode);
|
||||
|
||||
this.fillColor = readColor(markerNode.node("fillColor"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(ConfigurationNode markerNode) throws SerializationException {
|
||||
super.save(markerNode);
|
||||
|
||||
writeShape(markerNode.node("shape"), this.shape);
|
||||
markerNode.node("shapeY").set(Math.round(shapeY * 1000f) / 1000f);
|
||||
markerNode.node("depthTest").set(this.depthTest);
|
||||
markerNode.node("lineWidth").set(this.lineWidth);
|
||||
writeColor(markerNode.node("lineColor"), this.lineColor);
|
||||
writeColor(markerNode.node("fillColor"), this.fillColor);
|
||||
|
||||
hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
private Shape readShape(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
List<? extends ConfigurationNode> posNodes = node.childrenList();
|
||||
|
||||
if (posNodes.size() < 3) throw new MarkerFileFormatException("Failed to read shape: point-list has fewer than 3 entries!");
|
||||
|
||||
Vector2d[] positions = new Vector2d[posNodes.size()];
|
||||
for (int i = 0; i < positions.length; i++) {
|
||||
positions[i] = readShapePos(posNodes.get(i));
|
||||
}
|
||||
|
||||
return new Shape(positions);
|
||||
}
|
||||
|
||||
private static Vector2d readShapePos(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
ConfigurationNode nx, nz;
|
||||
nx = node.node("x");
|
||||
nz = node.node("z");
|
||||
|
||||
if (nx.virtual() || nz.virtual()) throw new MarkerFileFormatException("Failed to read shape position: Node x or z is not set!");
|
||||
|
||||
return new Vector2d(
|
||||
nx.getDouble(),
|
||||
nz.getDouble()
|
||||
);
|
||||
}
|
||||
|
||||
private static Color readColor(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
ConfigurationNode nr, ng, nb, na;
|
||||
nr = node.node("r");
|
||||
ng = node.node("g");
|
||||
nb = node.node("b");
|
||||
na = node.node("a");
|
||||
|
||||
if (nr.virtual() || ng.virtual() || nb.virtual()) throw new MarkerFileFormatException("Failed to read color: Node r,g or b is not set!");
|
||||
|
||||
float alpha = (float) na.getDouble(1);
|
||||
if (alpha < 0 || alpha > 1) throw new MarkerFileFormatException("Failed to read color: alpha value out of range (0-1)!");
|
||||
|
||||
try {
|
||||
return new Color(nr.getInt(), ng.getInt(), nb.getInt(), (int)(alpha * 255));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new MarkerFileFormatException("Failed to read color: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeShape(ConfigurationNode node, Shape shape) throws SerializationException {
|
||||
for (int i = 0; i < shape.getPointCount(); i++) {
|
||||
ConfigurationNode pointNode = node.appendListNode();
|
||||
Vector2d point = shape.getPoint(i);
|
||||
pointNode.node("x").set(Math.round(point.getX() * 1000d) / 1000d);
|
||||
pointNode.node("z").set(Math.round(point.getY() * 1000d) / 1000d);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeColor(ConfigurationNode node, Color color) throws SerializationException {
|
||||
int r = color.getRed();
|
||||
int g = color.getGreen();
|
||||
int b = color.getBlue();
|
||||
float a = color.getAlpha() / 255f;
|
||||
|
||||
node.node("r").set(r);
|
||||
node.node("g").set(g);
|
||||
node.node("b").set(b);
|
||||
node.node("a").set(a);
|
||||
}
|
||||
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
/*
|
||||
* 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.common.api.render;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.renderer.RenderAPI;
|
||||
import de.bluecolored.bluemap.common.api.BlueMapAPIImpl;
|
||||
import de.bluecolored.bluemap.common.api.BlueMapMapImpl;
|
||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||
import de.bluecolored.bluemap.common.rendermanager.MapPurgeTask;
|
||||
import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask;
|
||||
import de.bluecolored.bluemap.common.rendermanager.RenderManager;
|
||||
import de.bluecolored.bluemap.common.rendermanager.WorldRegionRenderTask;
|
||||
import de.bluecolored.bluemap.core.world.Grid;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
public class RenderAPIImpl implements RenderAPI {
|
||||
|
||||
private final BlueMapAPIImpl api;
|
||||
private final Plugin plugin;
|
||||
private final RenderManager renderManager;
|
||||
|
||||
public RenderAPIImpl(BlueMapAPIImpl api, Plugin plugin) {
|
||||
this.api = api;
|
||||
this.plugin = plugin;
|
||||
this.renderManager = plugin.getRenderManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(UUID world, Vector3i blockPosition) {
|
||||
render(api.getWorldForUuid(world), blockPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(String mapId, Vector3i blockPosition) {
|
||||
render(api.getMapForId(mapId), blockPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(String mapId, Vector2i tile) {
|
||||
render(api.getMapForId(mapId), tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(BlueMapMap map, Vector2i tile) {
|
||||
BlueMapMapImpl cmap = castMap(map);
|
||||
|
||||
Grid regionGrid = cmap.getWorld().getWorld().getRegionGrid();
|
||||
Grid tileGrid = cmap.getMapType().getHiresModelManager().getTileGrid();
|
||||
|
||||
for (Vector2i region : tileGrid.getIntersecting(tile, regionGrid)) {
|
||||
renderManager.scheduleRenderTask(new WorldRegionRenderTask(cmap.getMapType(), region));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean scheduleMapUpdateTask(BlueMapMap map, boolean force) {
|
||||
BlueMapMapImpl cmap = castMap(map);
|
||||
return renderManager.scheduleRenderTask(new MapUpdateTask(cmap.getMapType(), force));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean scheduleMapUpdateTask(BlueMapMap map, Collection<Vector2i> regions, boolean force) {
|
||||
BlueMapMapImpl cmap = castMap(map);
|
||||
return renderManager.scheduleRenderTask(new MapUpdateTask(cmap.getMapType(), regions, force));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean scheduleMapPurgeTask(BlueMapMap map) throws IOException {
|
||||
BlueMapMapImpl cmap = castMap(map);
|
||||
return renderManager.scheduleRenderTask(MapPurgeTask.create(cmap.getMapType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int renderQueueSize() {
|
||||
return renderManager.getScheduledRenderTasks().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int renderThreadCount() {
|
||||
return renderManager.getWorkerThreadCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return renderManager.isRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (!isRunning()){
|
||||
renderManager.start(api.plugin.getCoreConfig().getRenderThreadCount());
|
||||
}
|
||||
plugin.getPluginState().setRenderThreadsEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
renderManager.stop();
|
||||
plugin.getPluginState().setRenderThreadsEnabled(false);
|
||||
}
|
||||
|
||||
private BlueMapMapImpl castMap(BlueMapMap map) {
|
||||
BlueMapMapImpl cmap;
|
||||
if (map instanceof BlueMapMapImpl) {
|
||||
cmap = (BlueMapMapImpl) map;
|
||||
} else {
|
||||
cmap = api.getMapForId(map.getId());
|
||||
}
|
||||
|
||||
return cmap;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,340 @@
|
||||
package de.bluecolored.bluemap.common.config;
|
||||
|
||||
import de.bluecolored.bluemap.common.config.storage.StorageConfig;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
|
||||
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.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@DebugDump
|
||||
public class BlueMapConfigs {
|
||||
|
||||
private final ServerInterface serverInterface;
|
||||
private final ConfigManager configManager;
|
||||
|
||||
private final CoreConfig coreConfig;
|
||||
private final WebserverConfig webserverConfig;
|
||||
private final WebappConfig webappConfig;
|
||||
private final PluginConfig pluginConfig;
|
||||
private final Map<String, MapConfig> mapConfigs;
|
||||
private final Map<String, StorageConfig> storageConfigs;
|
||||
|
||||
public BlueMapConfigs(ServerInterface serverInterface) throws ConfigurationException {
|
||||
this.serverInterface = serverInterface;
|
||||
this.configManager = new ConfigManager(serverInterface.getConfigFolder());
|
||||
|
||||
this.coreConfig = loadCoreConfig();
|
||||
this.webserverConfig = loadWebserverConfig();
|
||||
this.webappConfig = loadWebappConfig();
|
||||
this.pluginConfig = loadPluginConfig();
|
||||
this.storageConfigs = Collections.unmodifiableMap(loadStorageConfigs());
|
||||
this.mapConfigs = Collections.unmodifiableMap(loadMapConfigs());
|
||||
}
|
||||
|
||||
public ConfigManager getConfigManager() {
|
||||
return configManager;
|
||||
}
|
||||
|
||||
public CoreConfig getCoreConfig() {
|
||||
return coreConfig;
|
||||
}
|
||||
|
||||
public WebappConfig getWebappConfig() {
|
||||
return webappConfig;
|
||||
}
|
||||
|
||||
public WebserverConfig getWebserverConfig() {
|
||||
return webserverConfig;
|
||||
}
|
||||
|
||||
public PluginConfig getPluginConfig() {
|
||||
return pluginConfig;
|
||||
}
|
||||
|
||||
public Map<String, MapConfig> getMapConfigs() {
|
||||
return mapConfigs;
|
||||
}
|
||||
|
||||
public Map<String, StorageConfig> getStorageConfigs() {
|
||||
return storageConfigs;
|
||||
}
|
||||
|
||||
private synchronized CoreConfig loadCoreConfig() throws ConfigurationException {
|
||||
Path configFileRaw = Path.of("core");
|
||||
Path configFile = configManager.findConfigPath(configFileRaw);
|
||||
Path configFolder = configFile.getParent();
|
||||
|
||||
if (!Files.exists(configFile)) {
|
||||
try {
|
||||
Files.createDirectories(configFolder);
|
||||
Files.writeString(
|
||||
configFolder.resolve("core.conf"),
|
||||
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/core.conf")
|
||||
.build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
} catch (IOException | NullPointerException ex) {
|
||||
Logger.global.logWarning("Failed to create default core-configuration-file: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
return configManager.loadConfig(configFileRaw, CoreConfig.class);
|
||||
}
|
||||
|
||||
private synchronized WebserverConfig loadWebserverConfig() throws ConfigurationException {
|
||||
Path configFileRaw = Path.of("webserver");
|
||||
Path configFile = configManager.findConfigPath(configFileRaw);
|
||||
Path configFolder = configFile.getParent();
|
||||
|
||||
if (!Files.exists(configFile)) {
|
||||
try {
|
||||
Files.createDirectories(configFolder);
|
||||
Files.writeString(
|
||||
configFolder.resolve("webserver.conf"),
|
||||
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/webserver.conf")
|
||||
.build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
} catch (IOException | NullPointerException ex) {
|
||||
Logger.global.logWarning("Failed to create default webserver-configuration-file: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
return configManager.loadConfig(configFileRaw, WebserverConfig.class);
|
||||
}
|
||||
|
||||
private synchronized WebappConfig loadWebappConfig() throws ConfigurationException {
|
||||
Path configFileRaw = Path.of("webapp");
|
||||
Path configFile = configManager.findConfigPath(configFileRaw);
|
||||
Path configFolder = configFile.getParent();
|
||||
|
||||
if (!Files.exists(configFile)) {
|
||||
try {
|
||||
Files.createDirectories(configFolder);
|
||||
Files.writeString(
|
||||
configFolder.resolve("webapp.conf"),
|
||||
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/webapp.conf")
|
||||
.build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
} catch (IOException | NullPointerException ex) {
|
||||
Logger.global.logWarning("Failed to create default webapp-configuration-file: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
return configManager.loadConfig(configFileRaw, WebappConfig.class);
|
||||
}
|
||||
|
||||
private synchronized PluginConfig loadPluginConfig() throws ConfigurationException {
|
||||
Path configFileRaw = Path.of("plugin");
|
||||
Path configFile = configManager.findConfigPath(configFileRaw);
|
||||
Path configFolder = configFile.getParent();
|
||||
|
||||
if (!Files.exists(configFile)) {
|
||||
try {
|
||||
Files.createDirectories(configFolder);
|
||||
Files.writeString(
|
||||
configFolder.resolve("plugin.conf"),
|
||||
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/plugin.conf")
|
||||
.build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
} catch (IOException | NullPointerException ex) {
|
||||
Logger.global.logWarning("Failed to create default webapp-configuration-file: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
return configManager.loadConfig(configFileRaw, PluginConfig.class);
|
||||
}
|
||||
|
||||
private synchronized Map<String, MapConfig> loadMapConfigs() throws ConfigurationException {
|
||||
Map<String, MapConfig> mapConfigs = new HashMap<>();
|
||||
|
||||
Path mapFolder = Paths.get("maps");
|
||||
Path mapConfigFolder = configManager.getConfigRoot().resolve(mapFolder);
|
||||
|
||||
if (!Files.exists(mapConfigFolder)){
|
||||
try {
|
||||
Files.createDirectories(mapConfigFolder);
|
||||
var worlds = serverInterface.getLoadedWorlds();
|
||||
if (worlds.isEmpty()) {
|
||||
Files.writeString(
|
||||
mapConfigFolder.resolve("overworld.conf"),
|
||||
createOverworldMapTemplate("Overworld", Path.of("world")).build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
Files.writeString(
|
||||
mapConfigFolder.resolve("nether.conf"),
|
||||
createNetherMapTemplate("Nether", Path.of("world", "DIM-1")).build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
Files.writeString(
|
||||
mapConfigFolder.resolve("end.conf"),
|
||||
createEndMapTemplate("End", Path.of("world", "DIM1")).build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
} else {
|
||||
for (var world : worlds) {
|
||||
String name = world.getName().orElse(world.getDimension().getName());
|
||||
Path worldFolder = world.getSaveFolder();
|
||||
ConfigTemplate template;
|
||||
switch (world.getDimension()) {
|
||||
case NETHER: template = createNetherMapTemplate(name, worldFolder); break;
|
||||
case END: template = createEndMapTemplate(name, worldFolder); break;
|
||||
default: template = createOverworldMapTemplate(name, worldFolder); break;
|
||||
}
|
||||
|
||||
Path configFile = mapConfigFolder.resolve(sanitiseMapId(name.toLowerCase(Locale.ROOT)) + ".conf");
|
||||
int i = 1;
|
||||
while (Files.exists(configFile)) {
|
||||
configFile = mapConfigFolder.resolve(sanitiseMapId(name.toLowerCase(Locale.ROOT)) + '_' + (i++) + ".conf");
|
||||
}
|
||||
|
||||
Files.writeString(
|
||||
configFile,
|
||||
template.build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (IOException | NullPointerException ex) {
|
||||
throw new ConfigurationException("BlueMap failed to create default map-configuration-files in\n" +
|
||||
mapConfigFolder.toAbsolutePath().normalize() + "\n" +
|
||||
"Check if BlueMap has the permission to create and read from this folder.",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
for (var configFile : Files.list(mapConfigFolder).toArray(Path[]::new)) {
|
||||
if (!configManager.isConfigFile(configFile)) continue;
|
||||
Path rawConfig = configManager.getRaw(configFile);
|
||||
String id = sanitiseMapId(rawConfig.getFileName().toString());
|
||||
|
||||
if (mapConfigs.containsKey(id)) {
|
||||
throw new ConfigurationException("At least two of your map-config file-names result in ambiguous map-id's!\n" +
|
||||
configFile.toAbsolutePath().normalize() + "\n" +
|
||||
"To resolve this issue, rename this file to something else.");
|
||||
}
|
||||
|
||||
MapConfig mapConfig = configManager.loadConfig(rawConfig, MapConfig.class);
|
||||
mapConfigs.put(id, mapConfig);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException("BlueMap failed to read your map configuration from\n" +
|
||||
mapConfigFolder.toAbsolutePath().normalize() + "\n" +
|
||||
"Check if BlueMap has the permission to create and read from this folder.",
|
||||
ex);
|
||||
}
|
||||
|
||||
return mapConfigs;
|
||||
}
|
||||
|
||||
private synchronized Map<String, StorageConfig> loadStorageConfigs() throws ConfigurationException {
|
||||
Map<String, StorageConfig> storageConfigs = new HashMap<>();
|
||||
|
||||
Path storageFolder = Paths.get("storages");
|
||||
Path storageConfigFolder = configManager.getConfigRoot().resolve(storageFolder);
|
||||
|
||||
if (!Files.exists(storageConfigFolder)){
|
||||
try {
|
||||
Files.createDirectories(storageConfigFolder);
|
||||
Files.writeString(
|
||||
storageConfigFolder.resolve("file.conf"),
|
||||
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/storages/file.conf").build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
Files.writeString(
|
||||
storageConfigFolder.resolve("sql.conf"),
|
||||
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/storages/sql.conf").build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
} catch (IOException | NullPointerException ex) {
|
||||
throw new ConfigurationException("BlueMap failed to create default storage-configuration-files in\n" +
|
||||
storageConfigFolder.toAbsolutePath().normalize() + "\n" +
|
||||
"Check if BlueMap has the permission to create and read from this folder.",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
for (var configFile : Files.list(storageConfigFolder).toArray(Path[]::new)) {
|
||||
if (!configManager.isConfigFile(configFile)) continue;
|
||||
Path rawConfig = configManager.getRaw(configFile);
|
||||
String id = rawConfig.getFileName().toString();
|
||||
|
||||
StorageConfig storageConfig = configManager.loadConfig(rawConfig, StorageConfig.class); // load superclass
|
||||
storageConfig = configManager.loadConfig(rawConfig, storageConfig.getStorageType().getConfigType()); // load actual config type
|
||||
|
||||
storageConfigs.put(id, storageConfig);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException("BlueMap failed to read your map configuration from\n" +
|
||||
storageConfigFolder.toAbsolutePath().normalize() + "\n" +
|
||||
"Check if BlueMap has the permission to create and read from this folder.",
|
||||
ex);
|
||||
}
|
||||
|
||||
return storageConfigs;
|
||||
}
|
||||
|
||||
private String sanitiseMapId(String id) {
|
||||
return id.replaceAll("[^a-zA-Z0-9_]", "_");
|
||||
}
|
||||
|
||||
private ConfigTemplate createOverworldMapTemplate(String name, Path worldFolder) throws IOException {
|
||||
return configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/maps/map.conf")
|
||||
.setVariable("name", name)
|
||||
.setVariable("world", formatPath(worldFolder))
|
||||
.setVariable("sky-color", "#7dabff")
|
||||
.setVariable("ambient-light", "0.1")
|
||||
.setVariable("world-sky-light", "15")
|
||||
.setVariable("remove-caves-below-y", "55")
|
||||
.setConditional("max-y-comment", true)
|
||||
.setVariable("max-y", "100");
|
||||
}
|
||||
|
||||
private ConfigTemplate createNetherMapTemplate(String name, Path worldFolder) throws IOException {
|
||||
return configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/maps/map.conf")
|
||||
.setVariable("name", name)
|
||||
.setVariable("world", formatPath(worldFolder))
|
||||
.setVariable("sky-color", "#290000")
|
||||
.setVariable("ambient-light", "0.6")
|
||||
.setVariable("world-sky-light", "0")
|
||||
.setVariable("remove-caves-below-y", "-10000")
|
||||
.setConditional("max-y-comment", false)
|
||||
.setVariable("max-y", "90");
|
||||
}
|
||||
|
||||
private ConfigTemplate createEndMapTemplate(String name, Path worldFolder) throws IOException {
|
||||
return configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/maps/map.conf")
|
||||
.setVariable("name", name)
|
||||
.setVariable("world", formatPath(worldFolder))
|
||||
.setVariable("sky-color", "#080010")
|
||||
.setVariable("ambient-light", "0.6")
|
||||
.setVariable("world-sky-light", "0")
|
||||
.setVariable("remove-caves-below-y", "-10000")
|
||||
.setConditional("max-y-comment", true)
|
||||
.setVariable("max-y", "100");
|
||||
}
|
||||
|
||||
private String formatPath(Path path) {
|
||||
return Path.of("")
|
||||
.toAbsolutePath()
|
||||
.relativize(path.toAbsolutePath())
|
||||
.normalize()
|
||||
.toString()
|
||||
.replace("\\", "\\\\");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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.common.config;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import de.bluecolored.bluemap.common.config.typeserializer.Vector2iTypeSerializer;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.spongepowered.configurate.ConfigurateException;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
|
||||
import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
|
||||
import org.spongepowered.configurate.loader.AbstractConfigurationLoader;
|
||||
import org.spongepowered.configurate.loader.ConfigurationLoader;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public class ConfigManager {
|
||||
|
||||
private static final String[] CONFIG_FILE_ENDINGS = new String[] {
|
||||
".conf",
|
||||
".json"
|
||||
};
|
||||
|
||||
private final Path configRoot;
|
||||
|
||||
public ConfigManager(Path configRoot) {
|
||||
this.configRoot = configRoot;
|
||||
}
|
||||
|
||||
public <T> T loadConfig(Path rawPath, Class<T> type) throws ConfigurationException {
|
||||
Path path = findConfigPath(rawPath);
|
||||
ConfigurationNode configNode = loadConfigFile(path);
|
||||
try {
|
||||
return Objects.requireNonNull(configNode.get(type));
|
||||
} catch (SerializationException | NullPointerException ex) {
|
||||
throw new ConfigurationException(
|
||||
"BlueMap failed to parse this file:\n" +
|
||||
path + "\n" +
|
||||
"Check if the file is correctly formatted and all values are correct!",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigurationNode loadConfig(Path rawPath) throws ConfigurationException {
|
||||
Path path = findConfigPath(rawPath);
|
||||
return loadConfigFile(path);
|
||||
}
|
||||
|
||||
public ConfigTemplate loadConfigTemplate(String resource) throws IOException {
|
||||
InputStream in = BlueMap.class.getResourceAsStream(resource);
|
||||
if (in == null) throw new IOException("Resource not found: " + resource);
|
||||
String configTemplate = IOUtils.toString(in, StandardCharsets.UTF_8);
|
||||
return new ConfigTemplate(configTemplate);
|
||||
}
|
||||
|
||||
private ConfigurationNode loadConfigFile(Path path) throws ConfigurationException {
|
||||
if (!Files.exists(path)) {
|
||||
throw new ConfigurationException(
|
||||
"BlueMap tried to find this file, but it does not exist:\n" +
|
||||
path);
|
||||
}
|
||||
|
||||
if (!Files.isReadable(path)) {
|
||||
throw new ConfigurationException(
|
||||
"BlueMap tried to read this file, but can not access it:\n" +
|
||||
path + "\n" +
|
||||
"Check if BlueMap has the permission to read this file.");
|
||||
}
|
||||
|
||||
try {
|
||||
return getLoader(path).load();
|
||||
} catch (ConfigurateException ex) {
|
||||
throw new ConfigurationException(
|
||||
"BlueMap failed to parse this file:\n" +
|
||||
path + "\n" +
|
||||
"Check if the file is correctly formatted.\n" +
|
||||
"(for example there might be a } or ] or , missing somewhere)",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Path getConfigRoot() {
|
||||
return configRoot;
|
||||
}
|
||||
|
||||
public Path findConfigPath(Path rawPath) {
|
||||
if (!rawPath.startsWith(configRoot))
|
||||
rawPath = configRoot.resolve(rawPath);
|
||||
|
||||
for (String fileEnding : CONFIG_FILE_ENDINGS) {
|
||||
if (rawPath.getFileName().endsWith(fileEnding)) return rawPath;
|
||||
}
|
||||
|
||||
for (String fileEnding : CONFIG_FILE_ENDINGS) {
|
||||
Path path = rawPath.getParent().resolve(rawPath.getFileName() + fileEnding);
|
||||
if (Files.exists(path)) return path;
|
||||
}
|
||||
|
||||
return rawPath.getParent().resolve(rawPath.getFileName() + CONFIG_FILE_ENDINGS[0]);
|
||||
}
|
||||
|
||||
public boolean isConfigFile(Path path) {
|
||||
if (!Files.isRegularFile(path)) return false;
|
||||
|
||||
String fileName = path.getFileName().toString();
|
||||
for (String fileEnding : CONFIG_FILE_ENDINGS) {
|
||||
if (fileName.endsWith(fileEnding)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Path getRaw(Path path) {
|
||||
String fileName = path.getFileName().toString();
|
||||
String rawName = null;
|
||||
|
||||
for (String fileEnding : CONFIG_FILE_ENDINGS) {
|
||||
if (fileName.endsWith(fileEnding)) {
|
||||
rawName = fileName.substring(0, fileName.length() - fileEnding.length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rawName == null) return path;
|
||||
return path.getParent().resolve(rawName);
|
||||
}
|
||||
|
||||
private ConfigurationLoader<? extends ConfigurationNode> getLoader(Path path){
|
||||
AbstractConfigurationLoader.Builder<?, ?> builder;
|
||||
if (path.getFileName().endsWith(".json"))
|
||||
builder = GsonConfigurationLoader.builder();
|
||||
else
|
||||
builder = HoconConfigurationLoader.builder();
|
||||
|
||||
return builder
|
||||
.path(path)
|
||||
.defaultOptions(o -> o.serializers(b -> {
|
||||
b.register(Vector2i.class, new Vector2iTypeSerializer());
|
||||
}))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package de.bluecolored.bluemap.common.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ConfigTemplate {
|
||||
|
||||
private static final Pattern TEMPLATE_VARIABLE = Pattern.compile("\\$\\{([\\w\\-.]+)}"); // ${variable}
|
||||
private static final Pattern TEMPLATE_CONDITIONAL = Pattern.compile("\\$\\{([\\w\\-.]+)<<([\\s\\S]*?)>>}"); // ${conditionid<< ... >>}
|
||||
|
||||
private final String template;
|
||||
|
||||
private final Set<String> enabledConditionals;
|
||||
private final Map<String, String> variables;
|
||||
|
||||
public ConfigTemplate(String template) {
|
||||
this.template = template;
|
||||
this.enabledConditionals = new HashSet<>();
|
||||
this.variables = new HashMap<>();
|
||||
}
|
||||
|
||||
public ConfigTemplate setConditional(String conditional, boolean enabled) {
|
||||
if (enabled) enabledConditionals.add(conditional);
|
||||
else enabledConditionals.remove(conditional);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigTemplate setVariable(String variable, String value) {
|
||||
if (value == null) variables.remove(variable);
|
||||
else variables.put(variable, replacerEscape(value));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String build() {
|
||||
return build(this.template, enabledConditionals::contains, s -> variables.getOrDefault(s, "?"));
|
||||
}
|
||||
|
||||
private String build(String template, Predicate<? super String> conditionalResolver, Function<? super String, String> variableResolver) {
|
||||
String result = TEMPLATE_CONDITIONAL.matcher(template).replaceAll(match ->
|
||||
conditionalResolver.test(match.group(1)) ? replacerEscape(build(match.group(2), conditionalResolver, variableResolver)) : ""
|
||||
);
|
||||
return TEMPLATE_VARIABLE.matcher(result).replaceAll(match -> variableResolver.apply(match.group(1)));
|
||||
}
|
||||
|
||||
private String replacerEscape(String raw) {
|
||||
return raw
|
||||
.replace("\\", "\\\\")
|
||||
.replace("$", "\\$");
|
||||
}
|
||||
|
||||
}
|
@ -22,7 +22,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.config;
|
||||
package de.bluecolored.bluemap.common.config;
|
||||
|
||||
public class ConfigurationException extends Exception {
|
||||
|
@ -0,0 +1,37 @@
|
||||
package de.bluecolored.bluemap.common.config;
|
||||
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"})
|
||||
@DebugDump
|
||||
@ConfigSerializable
|
||||
public class CoreConfig {
|
||||
|
||||
private boolean acceptDownload = false;
|
||||
|
||||
private int renderThreadCount = 1;
|
||||
|
||||
private boolean metrics = true;
|
||||
|
||||
private Path data = Path.of("bluemap");
|
||||
|
||||
public boolean isAcceptDownload() {
|
||||
return acceptDownload;
|
||||
}
|
||||
|
||||
public int getRenderThreadCount() {
|
||||
return renderThreadCount;
|
||||
}
|
||||
|
||||
public boolean isMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public Path getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package de.bluecolored.bluemap.common.config;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.map.MapSettings;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Required;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"})
|
||||
@DebugDump
|
||||
@ConfigSerializable
|
||||
public class MapConfig implements MapSettings {
|
||||
private transient Path configFile = null;
|
||||
|
||||
private String name = null;
|
||||
|
||||
@Required
|
||||
private Path world = Path.of("world");
|
||||
|
||||
private Vector2i startPos = null;
|
||||
|
||||
private String skyColor = "#7dabff";
|
||||
|
||||
private float ambientLight = 0;
|
||||
|
||||
private int worldSkyLight = 15;
|
||||
|
||||
private int removeCavesBelowY = 55;
|
||||
|
||||
private boolean caveDetectionUsesBlockLight = false;
|
||||
|
||||
private int minX = Integer.MIN_VALUE;
|
||||
private int maxX = Integer.MAX_VALUE;
|
||||
private int minZ = Integer.MIN_VALUE;
|
||||
private int maxZ = Integer.MAX_VALUE;
|
||||
private int minY = Integer.MIN_VALUE;
|
||||
private int maxY = Integer.MAX_VALUE;
|
||||
|
||||
private boolean renderEdges = true;
|
||||
|
||||
private String storage = "file";
|
||||
|
||||
private boolean ignoreMissingLightData = false;
|
||||
|
||||
// hidden config fields
|
||||
private int hiresTileSize = 32;
|
||||
private int lowresPointsPerHiresTile = 4;
|
||||
private int lowresPointsPerLowresTile = 50;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Path getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public Vector2i getStartPos() {
|
||||
return startPos;
|
||||
}
|
||||
|
||||
public String getSkyColor() {
|
||||
return skyColor;
|
||||
}
|
||||
|
||||
public float getAmbientLight() {
|
||||
return ambientLight;
|
||||
}
|
||||
|
||||
public int getWorldSkyLight() {
|
||||
return worldSkyLight;
|
||||
}
|
||||
|
||||
public int getRemoveCavesBelowY() {
|
||||
return removeCavesBelowY;
|
||||
}
|
||||
|
||||
public boolean isCaveDetectionUsesBlockLight() {
|
||||
return caveDetectionUsesBlockLight;
|
||||
}
|
||||
|
||||
public Vector3i getMinPos() {
|
||||
return new Vector3i(minX, minY, minZ);
|
||||
}
|
||||
|
||||
public Vector3i getMaxPos() {
|
||||
return new Vector3i(maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
public boolean isRenderEdges() {
|
||||
return renderEdges;
|
||||
}
|
||||
|
||||
public String getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public boolean isIgnoreMissingLightData() {
|
||||
return ignoreMissingLightData;
|
||||
}
|
||||
|
||||
public int getHiresTileSize() {
|
||||
return hiresTileSize;
|
||||
}
|
||||
|
||||
public int getLowresPointsPerHiresTile() {
|
||||
return lowresPointsPerHiresTile;
|
||||
}
|
||||
|
||||
public int getLowresPointsPerLowresTile() {
|
||||
return lowresPointsPerLowresTile;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package de.bluecolored.bluemap.common.config;
|
||||
|
||||
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;
|
||||
|
||||
@SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"})
|
||||
@DebugDump
|
||||
@ConfigSerializable
|
||||
public class PluginConfig {
|
||||
|
||||
private boolean livePlayerMarkers = true;
|
||||
|
||||
private boolean skinDownload = true;
|
||||
|
||||
private List<String> hiddenGameModes = new ArrayList<>();
|
||||
private boolean hideVanished = true;
|
||||
private boolean hideInvisible = true;
|
||||
private boolean hideSneaking = true;
|
||||
|
||||
private int playerRenderLimit = -1;
|
||||
|
||||
private int fullUpdateInterval = 1440;
|
||||
|
||||
public boolean isLivePlayerMarkers() {
|
||||
return livePlayerMarkers;
|
||||
}
|
||||
|
||||
public boolean isSkinDownload() {
|
||||
return skinDownload;
|
||||
}
|
||||
|
||||
public List<String> getHiddenGameModes() {
|
||||
return hiddenGameModes;
|
||||
}
|
||||
|
||||
public boolean isHideVanished() {
|
||||
return hideVanished;
|
||||
}
|
||||
|
||||
public boolean isHideInvisible() {
|
||||
return hideInvisible;
|
||||
}
|
||||
|
||||
public boolean isHideSneaking() {
|
||||
return hideSneaking;
|
||||
}
|
||||
|
||||
public int getPlayerRenderLimit() {
|
||||
return playerRenderLimit;
|
||||
}
|
||||
|
||||
public int getFullUpdateInterval() {
|
||||
return fullUpdateInterval;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package de.bluecolored.bluemap.common.config;
|
||||
|
||||
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;
|
||||
|
||||
@SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"})
|
||||
@DebugDump
|
||||
@ConfigSerializable
|
||||
public class WebappConfig {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
private Path webroot = Path.of("bluemap", "web");
|
||||
|
||||
private boolean useCookies = true;
|
||||
|
||||
private boolean enableFreeFlight = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public Path getWebroot() {
|
||||
return webroot;
|
||||
}
|
||||
|
||||
public boolean isUseCookies() {
|
||||
return useCookies;
|
||||
}
|
||||
|
||||
public boolean isEnableFreeFlight() {
|
||||
return enableFreeFlight;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package de.bluecolored.bluemap.common.config;
|
||||
|
||||
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;
|
||||
|
||||
@SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"})
|
||||
@DebugDump
|
||||
@ConfigSerializable
|
||||
public class WebserverConfig {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
private Path webroot = Path.of("bluemap", "web");
|
||||
|
||||
private String ip = "0.0.0.0";
|
||||
|
||||
private short port = 8100;
|
||||
|
||||
private int maxConnectionCount = 100;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public Path getWebroot() {
|
||||
return webroot;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public InetAddress resolveIp() throws UnknownHostException {
|
||||
if (ip.isEmpty() || ip.equals("0.0.0.0") || ip.equals("::0")) {
|
||||
return new InetSocketAddress(0).getAddress();
|
||||
} else if (ip.equals("#getLocalHost")) {
|
||||
return InetAddress.getLocalHost();
|
||||
} else {
|
||||
return InetAddress.getByName(ip);
|
||||
}
|
||||
}
|
||||
|
||||
public short getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public int getMaxConnectionCount() {
|
||||
return maxConnectionCount;
|
||||
}
|
||||
|
||||
}
|
@ -22,28 +22,30 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.config.storage;
|
||||
package de.bluecolored.bluemap.common.config.storage;
|
||||
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.storage.Compression;
|
||||
import de.bluecolored.bluemap.core.storage.file.FileStorageSettings;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
@DebugDump
|
||||
@ConfigSerializable
|
||||
public class FileConfig extends StorageConfig {
|
||||
public class FileConfig extends StorageConfig implements FileStorageSettings {
|
||||
|
||||
private Path root = Paths.get("bluemap", "web", "data");
|
||||
private Path root = Path.of("bluemap", "web", "data");
|
||||
|
||||
private Compression compression = Compression.GZIP;
|
||||
|
||||
@Override
|
||||
public Path getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Compression getCompression() {
|
||||
return compression;
|
||||
}
|
@ -22,10 +22,11 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.config.storage;
|
||||
package de.bluecolored.bluemap.common.config.storage;
|
||||
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.storage.Compression;
|
||||
import de.bluecolored.bluemap.core.storage.sql.SQLStorageSettings;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
@ -35,7 +36,7 @@
|
||||
|
||||
@SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"})
|
||||
@ConfigSerializable
|
||||
public class SQLConfig extends StorageConfig {
|
||||
public class SQLConfig extends StorageConfig implements SQLStorageSettings {
|
||||
|
||||
@DebugDump private String driverJar = null;
|
||||
@DebugDump private String driverClass = null;
|
||||
@ -47,6 +48,7 @@ public class SQLConfig extends StorageConfig {
|
||||
|
||||
@DebugDump private transient URL driverJarURL = null;
|
||||
|
||||
@Override
|
||||
public Optional<URL> getDriverJar() throws MalformedURLException {
|
||||
if (driverJar == null) return Optional.empty();
|
||||
|
||||
@ -57,22 +59,27 @@ public Optional<URL> getDriverJar() throws MalformedURLException {
|
||||
return Optional.of(driverJarURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getDriverClass() {
|
||||
return Optional.ofNullable(driverClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDbUrl() {
|
||||
return dbUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Compression getCompression() {
|
||||
return compression;
|
||||
}
|
@ -22,12 +22,14 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.config.storage;
|
||||
package de.bluecolored.bluemap.common.config.storage;
|
||||
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.storage.StorageType;
|
||||
import de.bluecolored.bluemap.core.storage.Storage;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
@DebugDump
|
||||
@ConfigSerializable
|
||||
@ -39,4 +41,11 @@ public StorageType getStorageType() {
|
||||
return storageType;
|
||||
}
|
||||
|
||||
public Storage createStorage() throws Exception {
|
||||
if (this.getClass().equals(StorageConfig.class))
|
||||
throw new UnsupportedOperationException("Can not create a Storage from the StorageConfig superclass.");
|
||||
|
||||
return storageType.getStorageFactory(this.getClass()).provide(this);
|
||||
}
|
||||
|
||||
}
|
@ -22,34 +22,43 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.storage;
|
||||
package de.bluecolored.bluemap.common.config.storage;
|
||||
|
||||
import de.bluecolored.bluemap.core.config.storage.FileConfig;
|
||||
import de.bluecolored.bluemap.core.config.storage.SQLConfig;
|
||||
import de.bluecolored.bluemap.core.storage.Storage;
|
||||
import de.bluecolored.bluemap.core.storage.file.FileStorage;
|
||||
import de.bluecolored.bluemap.core.storage.sql.SQLStorage;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public enum StorageType {
|
||||
|
||||
FILE (node -> new FileStorage(Objects.requireNonNull(node.get(FileConfig.class)))),
|
||||
SQL (node -> new SQLStorage(Objects.requireNonNull(node.get(SQLConfig.class))));
|
||||
FILE (FileConfig.class, FileStorage::new),
|
||||
SQL (SQLConfig.class, SQLStorage::new);
|
||||
|
||||
private final StorageProvider storageProvider;
|
||||
private final Class<? extends StorageConfig> configType;
|
||||
private final StorageFactory<? extends StorageConfig> storageFactory;
|
||||
|
||||
StorageType(StorageProvider storageProvider) {
|
||||
this.storageProvider = storageProvider;
|
||||
<C extends StorageConfig> StorageType(Class<C> configType, StorageFactory<C> storageFactory) {
|
||||
this.configType = configType;
|
||||
this.storageFactory = storageFactory;
|
||||
}
|
||||
|
||||
public Storage create(ConfigurationNode node) throws Exception {
|
||||
return storageProvider.provide(node);
|
||||
public Class<? extends StorageConfig> getConfigType() {
|
||||
return configType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends StorageConfig> StorageFactory<C> getStorageFactory(Class<C> configType) {
|
||||
if (!configType.isAssignableFrom(this.configType)) throw new ClassCastException(this.configType + " can not be cast to " + configType);
|
||||
return (StorageFactory<C>) storageFactory;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface StorageProvider {
|
||||
Storage provide(ConfigurationNode node) throws Exception;
|
||||
public interface StorageFactory<C extends StorageConfig> {
|
||||
Storage provideRaw(C config) throws Exception;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default Storage provide(StorageConfig config) throws Exception {
|
||||
return provideRaw((C) config);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package de.bluecolored.bluemap.common.config.typeserializer;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.serialize.TypeSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class Vector2iTypeSerializer implements TypeSerializer<Vector2i> {
|
||||
|
||||
@Override
|
||||
public Vector2i deserialize(Type type, ConfigurationNode node) throws SerializationException {
|
||||
var xNode = node.node("x");
|
||||
var yNode = node.node("y");
|
||||
|
||||
if (xNode.virtual() || yNode.virtual()) throw new SerializationException("Cannot parse Vector2i: value x or y missing");
|
||||
|
||||
return Vector2i.from(
|
||||
xNode.getInt(),
|
||||
yNode.getInt()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Type type, @Nullable Vector2i obj, ConfigurationNode node) throws SerializationException {
|
||||
if (obj != null) {
|
||||
node.node("x").set(obj.getX());
|
||||
node.node("y").set(obj.getY());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -24,22 +24,21 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common.live;
|
||||
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import de.bluecolored.bluemap.common.config.PluginConfig;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Player;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpRequest;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpResponse;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpStatusCode;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import de.bluecolored.bluemap.common.plugin.PluginConfig;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Player;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpRequest;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpResponse;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpStatusCode;
|
||||
|
||||
public class LiveAPIRequestHandler implements HttpRequestHandler {
|
||||
|
||||
private HttpRequestHandler notFoundHandler;
|
||||
@ -62,7 +61,7 @@ public LiveAPIRequestHandler(ServerInterface server, PluginConfig config, HttpRe
|
||||
|
||||
@Override
|
||||
public HttpResponse handle(HttpRequest request) {
|
||||
if (!config.isLiveUpdatesEnabled()) return this.notFoundHandler.handle(request);
|
||||
if (!config.isLivePlayerMarkers()) return this.notFoundHandler.handle(request);
|
||||
|
||||
String path = request.getPath();
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
import de.bluecolored.bluemap.common.BlueMapService;
|
||||
import de.bluecolored.bluemap.common.InterruptableReentrantLock;
|
||||
import de.bluecolored.bluemap.common.MissingResourcesException;
|
||||
import de.bluecolored.bluemap.common.api.BlueMapAPIImpl;
|
||||
import de.bluecolored.bluemap.common.config.*;
|
||||
import de.bluecolored.bluemap.common.live.LiveAPIRequestHandler;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||
@ -36,28 +36,23 @@
|
||||
import de.bluecolored.bluemap.common.rendermanager.RenderManager;
|
||||
import de.bluecolored.bluemap.common.web.FileRequestHandler;
|
||||
import de.bluecolored.bluemap.common.web.MapStorageRequestHandler;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.common.webserver.WebServer;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.core.config.old.CoreConfig;
|
||||
import de.bluecolored.bluemap.core.config.old.RenderConfig;
|
||||
import de.bluecolored.bluemap.core.config.old.WebServerConfig;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.debug.StateDumper;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
import de.bluecolored.bluemap.core.metrics.Metrics;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
||||
import de.bluecolored.bluemap.core.util.MappableFunction;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.core.webserver.WebServer;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -69,21 +64,14 @@ public class Plugin implements ServerEventListener {
|
||||
|
||||
private final InterruptableReentrantLock loadingLock = new InterruptableReentrantLock();
|
||||
|
||||
private final MinecraftVersion minecraftVersion;
|
||||
private final String implementationType;
|
||||
private final ServerInterface serverInterface;
|
||||
|
||||
private BlueMapService blueMap;
|
||||
private BlueMapAPIImpl api;
|
||||
|
||||
private CoreConfig coreConfig;
|
||||
private RenderConfig renderConfig;
|
||||
private WebServerConfig webServerConfig;
|
||||
private PluginConfig pluginConfig;
|
||||
|
||||
private PluginState pluginState;
|
||||
|
||||
private Map<UUID, World> worlds;
|
||||
private Map<String, World> worlds;
|
||||
private Map<String, BmMap> maps;
|
||||
|
||||
private RenderManager renderManager;
|
||||
@ -97,8 +85,7 @@ public class Plugin implements ServerEventListener {
|
||||
|
||||
private boolean loaded = false;
|
||||
|
||||
public Plugin(MinecraftVersion minecraftVersion, String implementationType, ServerInterface serverInterface) {
|
||||
this.minecraftVersion = minecraftVersion;
|
||||
public Plugin(String implementationType, ServerInterface serverInterface) {
|
||||
this.implementationType = implementationType.toLowerCase();
|
||||
this.serverInterface = serverInterface;
|
||||
|
||||
@ -113,27 +100,17 @@ public void load() throws IOException, ParseResourceException {
|
||||
if (loaded) return;
|
||||
unload(); //ensure nothing is left running (from a failed load or something)
|
||||
|
||||
blueMap = new BlueMapService(minecraftVersion, serverInterface);
|
||||
|
||||
//load configs
|
||||
coreConfig = blueMap.getCoreConfig();
|
||||
renderConfig = blueMap.getRenderConfig();
|
||||
webServerConfig = blueMap.getWebServerConfig();
|
||||
blueMap.getMapStorages();
|
||||
|
||||
//load plugin config
|
||||
pluginConfig = new PluginConfig(blueMap.getConfigManagerOld().loadOrCreate(
|
||||
new File(serverInterface.getConfigFolder(), "plugin.conf"),
|
||||
Plugin.class.getResource("/de/bluecolored/bluemap/plugin.conf"),
|
||||
Plugin.class.getResource("/de/bluecolored/bluemap/plugin-defaults.conf"),
|
||||
true,
|
||||
true
|
||||
));
|
||||
blueMap = new BlueMapService(serverInterface);
|
||||
CoreConfig coreConfig = getConfigs().getCoreConfig();
|
||||
WebserverConfig webserverConfig = getConfigs().getWebserverConfig();
|
||||
WebappConfig webappConfig = getConfigs().getWebappConfig();
|
||||
PluginConfig pluginConfig = getConfigs().getPluginConfig();
|
||||
|
||||
//load plugin state
|
||||
try {
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
|
||||
.file(new File(getCoreConfig().getDataFolder(), "pluginState.json"))
|
||||
.path(coreConfig.getData().resolve("pluginState.json"))
|
||||
.build();
|
||||
pluginState = loader.load().get(PluginState.class);
|
||||
} catch (SerializationException ex) {
|
||||
@ -147,7 +124,7 @@ public void load() throws IOException, ParseResourceException {
|
||||
} catch (MissingResourcesException ex) {
|
||||
Logger.global.logWarning("BlueMap is missing important resources!");
|
||||
Logger.global.logWarning("You must accept the required file download in order for BlueMap to work!");
|
||||
try { Logger.global.logWarning("Please check: " + blueMap.getCoreConfigFile().getCanonicalPath()); } catch (IOException ignored) {}
|
||||
Logger.global.logWarning("Please check: " + blueMap.getConfigs().getConfigManager().findConfigPath(Path.of("core")).toAbsolutePath().normalize());
|
||||
Logger.global.logInfo("If you have changed the config you can simply reload the plugin using: /bluemap reload");
|
||||
|
||||
unload();
|
||||
@ -167,27 +144,34 @@ public void load() throws IOException, ParseResourceException {
|
||||
}
|
||||
|
||||
//create and start webserver
|
||||
if (webServerConfig.isWebserverEnabled()) {
|
||||
FileUtils.mkDirs(webServerConfig.getWebRoot());
|
||||
HttpRequestHandler requestHandler = new FileRequestHandler(webServerConfig.getWebRoot().toPath(), "BlueMap v" + BlueMap.VERSION);
|
||||
if (webserverConfig.isEnabled()) {
|
||||
Path webroot = webserverConfig.getWebroot();
|
||||
Files.createDirectories(webroot);
|
||||
HttpRequestHandler requestHandler = new FileRequestHandler(webroot,
|
||||
"BlueMap " + BlueMap.VERSION + " " + BlueMap.GIT_HASH + " " + BlueMap.GIT_CLEAN);
|
||||
|
||||
//use map-storage to provide map-tiles
|
||||
requestHandler = new MapStorageRequestHandler(
|
||||
MappableFunction.of(maps::get).mapNullable(BmMap::getStorage),
|
||||
id -> maps.get(id).getStorage(),
|
||||
requestHandler);
|
||||
|
||||
//inject live api if enabled
|
||||
if (pluginConfig.isLiveUpdatesEnabled()) {
|
||||
if (pluginConfig.isLivePlayerMarkers()) {
|
||||
requestHandler = new LiveAPIRequestHandler(serverInterface, pluginConfig, requestHandler);
|
||||
}
|
||||
|
||||
try {
|
||||
webServer = new WebServer(
|
||||
webServerConfig.getWebserverBindAddress(),
|
||||
webServerConfig.getWebserverPort(),
|
||||
webServerConfig.getWebserverMaxConnections(),
|
||||
webserverConfig.resolveIp(),
|
||||
webserverConfig.getPort(),
|
||||
webserverConfig.getMaxConnectionCount(),
|
||||
requestHandler,
|
||||
false
|
||||
);
|
||||
} catch (UnknownHostException ex) {
|
||||
throw new ConfigurationException("BlueMap failed to resolve the ip in your webserver-config.\n" +
|
||||
"Check if that is correctly configured.", ex);
|
||||
}
|
||||
webServer.start();
|
||||
}
|
||||
|
||||
@ -213,10 +197,10 @@ public void load() throws IOException, ParseResourceException {
|
||||
blueMap.updateWebAppSettings();
|
||||
|
||||
//start skin updater
|
||||
if (pluginConfig.isLiveUpdatesEnabled()) {
|
||||
if (pluginConfig.isLivePlayerMarkers()) {
|
||||
this.skinUpdater = new PlayerSkinUpdater(
|
||||
new File(renderConfig.getWebRoot(), "assets" + File.separator + "playerheads"),
|
||||
new File(renderConfig.getWebRoot(), "assets" + File.separator + "steve.png")
|
||||
webappConfig.getWebroot().resolve("assets").resolve("playerheads").toFile(),
|
||||
webappConfig.getWebroot().resolve("assets").resolve("steve.png").toFile()
|
||||
);
|
||||
serverInterface.registerListener(skinUpdater);
|
||||
}
|
||||
@ -245,8 +229,8 @@ public void run() {
|
||||
daemonTimer.schedule(fileWatcherRestartTask, TimeUnit.HOURS.toMillis(1), TimeUnit.HOURS.toMillis(1));
|
||||
|
||||
//periodically update all (non frozen) maps
|
||||
if (pluginConfig.getFullUpdateIntervalMinutes() > 0) {
|
||||
long fullUpdateTime = TimeUnit.MINUTES.toMillis(pluginConfig.getFullUpdateIntervalMinutes());
|
||||
if (pluginConfig.getFullUpdateInterval() > 0) {
|
||||
long fullUpdateTime = TimeUnit.MINUTES.toMillis(pluginConfig.getFullUpdateInterval());
|
||||
TimerTask updateAllMapsTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -264,7 +248,7 @@ public void run() {
|
||||
TimerTask metricsTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (Plugin.this.serverInterface.isMetricsEnabled(coreConfig.isMetricsEnabled()))
|
||||
if (Plugin.this.serverInterface.isMetricsEnabled().getOr(coreConfig::isMetrics))
|
||||
Metrics.sendReport(Plugin.this.implementationType);
|
||||
}
|
||||
};
|
||||
@ -277,9 +261,9 @@ public void run() {
|
||||
//register listener
|
||||
serverInterface.registerListener(this);
|
||||
|
||||
//enable api
|
||||
this.api = new BlueMapAPIImpl(this);
|
||||
this.api.register();
|
||||
//enable api TODO
|
||||
//this.api = new BlueMapAPIImpl(this);
|
||||
//this.api.register();
|
||||
|
||||
//done
|
||||
loaded = true;
|
||||
@ -302,9 +286,9 @@ public void unload() {
|
||||
//save
|
||||
save();
|
||||
|
||||
//disable api
|
||||
if (api != null) api.unregister();
|
||||
api = null;
|
||||
//disable api TODO
|
||||
//if (api != null) api.unregister();
|
||||
//api = null;
|
||||
|
||||
//unregister listeners
|
||||
serverInterface.unregisterAllListeners();
|
||||
@ -351,11 +335,6 @@ public void unload() {
|
||||
worlds = null;
|
||||
maps = null;
|
||||
|
||||
coreConfig = null;
|
||||
renderConfig = null;
|
||||
webServerConfig = null;
|
||||
pluginConfig = null;
|
||||
|
||||
pluginState = null;
|
||||
|
||||
//done
|
||||
@ -375,7 +354,7 @@ public synchronized void save() {
|
||||
if (pluginState != null) {
|
||||
try {
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
|
||||
.file(new File(getCoreConfig().getDataFolder(), "pluginState.json"))
|
||||
.path(blueMap.getConfigs().getCoreConfig().getData().resolve("pluginState.json"))
|
||||
.build();
|
||||
loader.save(loader.createNode().set(PluginState.class, pluginState));
|
||||
} catch (IOException ex) {
|
||||
@ -409,8 +388,10 @@ public synchronized void stopWatchingMap(BmMap map) {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean flushWorldUpdates(UUID worldUUID) throws IOException {
|
||||
return serverInterface.persistWorldChanges(worldUUID);
|
||||
public boolean flushWorldUpdates(World world) throws IOException {
|
||||
var implWorld = serverInterface.getWorld(world.getSaveFolder()).orElse(null);
|
||||
if (implWorld != null) return implWorld.persistWorldChanges();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -424,15 +405,18 @@ public void onPlayerLeave(UUID playerUuid) {
|
||||
}
|
||||
|
||||
public boolean checkPausedByPlayerCount() {
|
||||
CoreConfig coreConfig = getConfigs().getCoreConfig();
|
||||
PluginConfig pluginConfig = getConfigs().getPluginConfig();
|
||||
|
||||
if (
|
||||
getPluginConfig().getPlayerRenderLimit() > 0 &&
|
||||
getServerInterface().getOnlinePlayers().size() >= getPluginConfig().getPlayerRenderLimit()
|
||||
pluginConfig.getPlayerRenderLimit() > 0 &&
|
||||
getServerInterface().getOnlinePlayers().size() >= pluginConfig.getPlayerRenderLimit()
|
||||
) {
|
||||
if (renderManager.isRunning()) renderManager.stop();
|
||||
return true;
|
||||
} else {
|
||||
if (!renderManager.isRunning() && getPluginState().isRenderThreadsEnabled())
|
||||
renderManager.start(getCoreConfig().getRenderThreadCount());
|
||||
renderManager.start(coreConfig.getRenderThreadCount());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -441,36 +425,24 @@ public ServerInterface getServerInterface() {
|
||||
return serverInterface;
|
||||
}
|
||||
|
||||
public CoreConfig getCoreConfig() {
|
||||
return coreConfig;
|
||||
public BlueMapService getBlueMap() {
|
||||
return blueMap;
|
||||
}
|
||||
|
||||
public RenderConfig getRenderConfig() {
|
||||
return renderConfig;
|
||||
}
|
||||
|
||||
public WebServerConfig getWebServerConfig() {
|
||||
return webServerConfig;
|
||||
}
|
||||
|
||||
public PluginConfig getPluginConfig() {
|
||||
return pluginConfig;
|
||||
public BlueMapConfigs getConfigs() {
|
||||
return blueMap.getConfigs();
|
||||
}
|
||||
|
||||
public PluginState getPluginState() {
|
||||
return pluginState;
|
||||
}
|
||||
|
||||
public World getWorld(UUID uuid){
|
||||
return worlds.get(uuid);
|
||||
public Map<String, World> getWorlds(){
|
||||
return worlds;
|
||||
}
|
||||
|
||||
public Collection<World> getWorlds(){
|
||||
return worlds.values();
|
||||
}
|
||||
|
||||
public Collection<BmMap> getMapTypes(){
|
||||
return maps.values();
|
||||
public Map<String, BmMap> getMaps(){
|
||||
return maps;
|
||||
}
|
||||
|
||||
public RenderManager getRenderManager() {
|
||||
@ -489,10 +461,6 @@ public String getImplementationType() {
|
||||
return implementationType;
|
||||
}
|
||||
|
||||
public MinecraftVersion getMinecraftVersion() {
|
||||
return minecraftVersion;
|
||||
}
|
||||
|
||||
private void initFileWatcherTasks() {
|
||||
for (BmMap map : maps.values()) {
|
||||
if (pluginState.getMapState(map).isUpdateEnabled()) {
|
||||
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* 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.common.plugin;
|
||||
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@DebugDump
|
||||
public class PluginConfig {
|
||||
|
||||
private boolean liveUpdatesEnabled;
|
||||
private boolean skinDownloadEnabled;
|
||||
private Collection<String> hiddenGameModes;
|
||||
private boolean hideInvisible;
|
||||
private boolean hideSneaking;
|
||||
private int playerRenderLimit;
|
||||
private long fullUpdateIntervalMinutes;
|
||||
|
||||
public PluginConfig(ConfigurationNode node) {
|
||||
|
||||
//liveUpdates
|
||||
liveUpdatesEnabled = node.node("liveUpdates").getBoolean(true);
|
||||
|
||||
//skinDownloadEnabled
|
||||
skinDownloadEnabled = node.node("skinDownload").getBoolean(true);
|
||||
|
||||
//hiddenGameModes
|
||||
hiddenGameModes = new ArrayList<>();
|
||||
for (ConfigurationNode gameModeNode : node.node("hiddenGameModes").childrenList()) {
|
||||
hiddenGameModes.add(gameModeNode.getString());
|
||||
}
|
||||
hiddenGameModes = Collections.unmodifiableCollection(hiddenGameModes);
|
||||
|
||||
//hideInvisible
|
||||
hideInvisible = node.node("hideInvisible").getBoolean(true);
|
||||
|
||||
//hideSneaking
|
||||
hideSneaking = node.node("hideSneaking").getBoolean(false);
|
||||
|
||||
//playerRenderLimit
|
||||
playerRenderLimit = node.node("playerRenderLimit").getInt(-1);
|
||||
|
||||
//periodic map updates
|
||||
fullUpdateIntervalMinutes = node.node("fullUpdateInterval").getLong(TimeUnit.HOURS.toMinutes(24));
|
||||
|
||||
}
|
||||
|
||||
public boolean isLiveUpdatesEnabled() {
|
||||
return this.liveUpdatesEnabled;
|
||||
}
|
||||
|
||||
public boolean isSkinDownloadEnabled() {
|
||||
return this.skinDownloadEnabled;
|
||||
}
|
||||
|
||||
public Collection<String> getHiddenGameModes() {
|
||||
return this.hiddenGameModes;
|
||||
}
|
||||
|
||||
public boolean isHideInvisible() {
|
||||
return this.hideInvisible;
|
||||
}
|
||||
|
||||
public boolean isHideSneaking() {
|
||||
return this.hideSneaking;
|
||||
}
|
||||
|
||||
public int getPlayerRenderLimit() {
|
||||
return playerRenderLimit;
|
||||
}
|
||||
|
||||
public long getFullUpdateIntervalMinutes() {
|
||||
return fullUpdateIntervalMinutes;
|
||||
}
|
||||
|
||||
}
|
@ -94,7 +94,7 @@ public List<Text> createStatusMessage(){
|
||||
if (plugin.checkPausedByPlayerCount()) {
|
||||
lines.add(Text.of(TextColor.WHITE, " Render-Threads are ",
|
||||
Text.of(TextColor.GOLD, "paused")));
|
||||
lines.add(Text.of(TextColor.GRAY, TextFormat.ITALIC, " (there are " + plugin.getPluginConfig().getPlayerRenderLimit() + " or more players online)"));
|
||||
lines.add(Text.of(TextColor.GRAY, TextFormat.ITALIC, " (there are " + plugin.getConfigs().getPluginConfig().getPlayerRenderLimit() + " or more players online)"));
|
||||
} else {
|
||||
lines.add(Text.of(TextColor.WHITE, " Render-Threads are ",
|
||||
Text.of(TextColor.RED, "stopped")
|
||||
@ -122,7 +122,7 @@ public List<Text> createStatusMessage(){
|
||||
|
||||
public Text worldHelperHover() {
|
||||
StringJoiner joiner = new StringJoiner("\n");
|
||||
for (World world : plugin.getWorlds()) {
|
||||
for (World world : plugin.getWorlds().values()) {
|
||||
joiner.add(world.getName());
|
||||
}
|
||||
|
||||
@ -131,8 +131,8 @@ public Text worldHelperHover() {
|
||||
|
||||
public Text mapHelperHover() {
|
||||
StringJoiner joiner = new StringJoiner("\n");
|
||||
for (BmMap map : plugin.getMapTypes()) {
|
||||
joiner.add(map.getId());
|
||||
for (String mapId : plugin.getMaps().keySet()) {
|
||||
joiner.add(mapId);
|
||||
}
|
||||
|
||||
return Text.of("map").setHoverText(Text.of(TextColor.WHITE, "Available maps: \n", TextColor.GRAY, joiner.toString()));
|
||||
|
@ -59,7 +59,6 @@
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
import de.bluecolored.bluemap.core.map.MapRenderState;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
@ -318,7 +317,7 @@ private <T> Optional<T> getOptionalArgument(CommandContext<S> context, String ar
|
||||
}
|
||||
|
||||
private Optional<World> parseWorld(String worldName) {
|
||||
for (World world : plugin.getWorlds()) {
|
||||
for (World world : plugin.getWorlds().values()) {
|
||||
if (world.getName().equalsIgnoreCase(worldName)) {
|
||||
return Optional.of(world);
|
||||
}
|
||||
@ -328,7 +327,7 @@ private Optional<World> parseWorld(String worldName) {
|
||||
}
|
||||
|
||||
private Optional<BmMap> parseMap(String mapId) {
|
||||
for (BmMap map : plugin.getMapTypes()) {
|
||||
for (BmMap map : plugin.getMaps().values()) {
|
||||
if (map.getId().equalsIgnoreCase(mapId)) {
|
||||
return Optional.of(map);
|
||||
}
|
||||
@ -360,23 +359,25 @@ public int versionCommand(CommandContext<S> context) {
|
||||
renderThreadCount = plugin.getRenderManager().getWorkerThreadCount();
|
||||
}
|
||||
|
||||
MinecraftVersion minecraftVersion = plugin.getServerInterface().getMinecraftVersion();
|
||||
|
||||
source.sendMessage(Text.of(TextFormat.BOLD, TextColor.BLUE, "Version: ", TextColor.WHITE, BlueMap.VERSION));
|
||||
source.sendMessage(Text.of(TextColor.GRAY, "Commit: ", TextColor.WHITE, BlueMap.GIT_HASH + " (" + BlueMap.GIT_CLEAN + ")"));
|
||||
source.sendMessage(Text.of(TextColor.GRAY, "Implementation: ", TextColor.WHITE, plugin.getImplementationType()));
|
||||
source.sendMessage(Text.of(
|
||||
TextColor.GRAY, "Minecraft compatibility: ", TextColor.WHITE, plugin.getMinecraftVersion().getVersionString(),
|
||||
TextColor.GRAY, " (" + plugin.getMinecraftVersion().getResource().getVersion().getVersionString() + ")"
|
||||
TextColor.GRAY, "Minecraft compatibility: ", TextColor.WHITE, minecraftVersion.getVersionString(),
|
||||
TextColor.GRAY, " (" + minecraftVersion.getResource().getVersion().getVersionString() + ")"
|
||||
));
|
||||
source.sendMessage(Text.of(TextColor.GRAY, "Render-threads: ", TextColor.WHITE, renderThreadCount));
|
||||
source.sendMessage(Text.of(TextColor.GRAY, "Available processors: ", TextColor.WHITE, Runtime.getRuntime().availableProcessors()));
|
||||
source.sendMessage(Text.of(TextColor.GRAY, "Available memory: ", TextColor.WHITE, (Runtime.getRuntime().maxMemory() / 1024L / 1024L) + " MiB"));
|
||||
|
||||
if (plugin.getMinecraftVersion().isAtLeast(new MinecraftVersion(1, 15))) {
|
||||
if (minecraftVersion.isAtLeast(new MinecraftVersion(1, 15))) {
|
||||
String clipboardValue =
|
||||
"Version: " + BlueMap.VERSION + "\n" +
|
||||
"Commit: " + BlueMap.GIT_HASH + " (" + BlueMap.GIT_CLEAN + ")\n" +
|
||||
"Implementation: " + plugin.getImplementationType() + "\n" +
|
||||
"Minecraft compatibility: " + plugin.getMinecraftVersion().getVersionString() + " (" + plugin.getMinecraftVersion().getResource().getVersion().getVersionString() + ")\n" +
|
||||
"Minecraft compatibility: " + minecraftVersion.getVersionString() + " (" + minecraftVersion.getResource().getVersion().getVersionString() + ")\n" +
|
||||
"Render-threads: " + renderThreadCount + "\n" +
|
||||
"Available processors: " + Runtime.getRuntime().availableProcessors() + "\n" +
|
||||
"Available memory: " + Runtime.getRuntime().maxMemory() / 1024L / 1024L + " MiB";
|
||||
@ -443,7 +444,7 @@ public int reloadCommand(CommandContext<S> context) {
|
||||
public int debugClearCacheCommand(CommandContext<S> context) {
|
||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
|
||||
for (World world : plugin.getWorlds()) {
|
||||
for (World world : plugin.getWorlds().values()) {
|
||||
world.invalidateChunkCache();
|
||||
}
|
||||
|
||||
@ -478,7 +479,7 @@ public int debugFlushCommand(CommandContext<S> context) {
|
||||
new Thread(() -> {
|
||||
source.sendMessage(Text.of(TextColor.GOLD, "Saving world and flushing changes..."));
|
||||
try {
|
||||
if (plugin.flushWorldUpdates(world.getUUID())) {
|
||||
if (plugin.flushWorldUpdates(world)) {
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Successfully saved and flushed all changes."));
|
||||
} else {
|
||||
source.sendMessage(Text.of(TextColor.RED, "This operation is not supported by this implementation (" + plugin.getImplementationType() + ")"));
|
||||
@ -550,7 +551,7 @@ public int debugDumpCommand(CommandContext<S> context) {
|
||||
final CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
|
||||
try {
|
||||
Path file = plugin.getCoreConfig().getDataFolder().toPath().resolve("dump.json");
|
||||
Path file = plugin.getConfigs().getCoreConfig().getData().resolve("dump.json");
|
||||
StateDumper.global().dump(file);
|
||||
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Dump created at: " + file));
|
||||
@ -589,7 +590,7 @@ public int startCommand(CommandContext<S> context) {
|
||||
new Thread(() -> {
|
||||
plugin.getPluginState().setRenderThreadsEnabled(true);
|
||||
|
||||
plugin.getRenderManager().start(plugin.getCoreConfig().getRenderThreadCount());
|
||||
plugin.getRenderManager().start(plugin.getConfigs().getCoreConfig().getRenderThreadCount());
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Render-Threads started!"));
|
||||
|
||||
plugin.save();
|
||||
@ -741,12 +742,16 @@ public int updateCommand(CommandContext<S> context, boolean force) {
|
||||
try {
|
||||
List<BmMap> maps = new ArrayList<>();
|
||||
if (worldToRender != null) {
|
||||
plugin.getServerInterface().persistWorldChanges(worldToRender.getUUID());
|
||||
for (BmMap map : plugin.getMapTypes()) {
|
||||
if (map.getWorld().getUUID().equals(worldToRender.getUUID())) maps.add(map);
|
||||
var world = plugin.getServerInterface().getWorld(worldToRender.getSaveFolder()).orElse(null);
|
||||
if (world != null) world.persistWorldChanges();
|
||||
|
||||
for (BmMap map : plugin.getMaps().values()) {
|
||||
if (map.getWorld().getSaveFolder().equals(worldToRender.getSaveFolder())) maps.add(map);
|
||||
}
|
||||
} else {
|
||||
plugin.getServerInterface().persistWorldChanges(mapToRender.getWorld().getUUID());
|
||||
var world = plugin.getServerInterface().getWorld(mapToRender.getWorld().getSaveFolder()).orElse(null);
|
||||
if (world != null) world.persistWorldChanges();
|
||||
|
||||
maps.add(mapToRender);
|
||||
}
|
||||
|
||||
@ -781,7 +786,7 @@ public int cancelCommand(CommandContext<S> context) {
|
||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
|
||||
Optional<String> ref = getOptionalArgument(context,"task-ref", String.class);
|
||||
if (!ref.isPresent()) {
|
||||
if (ref.isEmpty()) {
|
||||
plugin.getRenderManager().removeAllRenderTasks();
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "All tasks cancelled!"));
|
||||
source.sendMessage(Text.of(TextColor.GRAY, "(Note, that an already started task might not be removed immediately. Some tasks needs to do some tidying-work first)"));
|
||||
@ -790,7 +795,7 @@ public int cancelCommand(CommandContext<S> context) {
|
||||
|
||||
Optional<RenderTask> task = helper.getTaskForRef(ref.get());
|
||||
|
||||
if (!task.isPresent()) {
|
||||
if (task.isEmpty()) {
|
||||
source.sendMessage(Text.of(TextColor.RED, "There is no task with this reference '" + ref.get() + "'!"));
|
||||
return 0;
|
||||
}
|
||||
@ -846,8 +851,8 @@ public int worldsCommand(CommandContext<S> context) {
|
||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
|
||||
source.sendMessage(Text.of(TextColor.BLUE, "Worlds loaded by BlueMap:"));
|
||||
for (World world : plugin.getWorlds()) {
|
||||
source.sendMessage(Text.of(TextColor.GRAY, " - ", TextColor.WHITE, world.getName()).setHoverText(Text.of(world.getSaveFolder(), TextColor.GRAY, " (" + world.getUUID() + ")")));
|
||||
for (var entry : plugin.getWorlds().entrySet()) {
|
||||
source.sendMessage(Text.of(TextColor.GRAY, " - ", TextColor.WHITE, entry.getValue().getName()).setHoverText(Text.of(entry.getValue().getSaveFolder(), TextColor.GRAY, " (" + entry.getKey() + ")")));
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -857,7 +862,7 @@ public int mapsCommand(CommandContext<S> context) {
|
||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
|
||||
source.sendMessage(Text.of(TextColor.BLUE, "Maps loaded by BlueMap:"));
|
||||
for (BmMap map : plugin.getMapTypes()) {
|
||||
for (BmMap map : plugin.getMaps().values()) {
|
||||
boolean unfrozen = plugin.getPluginState().getMapState(map).isUpdateEnabled();
|
||||
if (unfrozen) {
|
||||
source.sendMessage(Text.of(
|
||||
@ -922,7 +927,7 @@ public int createMarkerCommand(CommandContext<S> context) {
|
||||
|
||||
// resolve api-map
|
||||
Optional<BlueMapMap> apiMap = api.getMap(map.getId());
|
||||
if (!apiMap.isPresent()) {
|
||||
if (apiMap.isEmpty()) {
|
||||
source.sendMessage(Text.of(TextColor.RED, "Failed to get map from API, try ", TextColor.GRAY, "/bluemap reload"));
|
||||
return 0;
|
||||
}
|
||||
|
@ -24,15 +24,15 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common.plugin.commands;
|
||||
|
||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||
|
||||
public class MapSuggestionProvider<S> extends AbstractSuggestionProvider<S> {
|
||||
|
||||
private Plugin plugin;
|
||||
private final Plugin plugin;
|
||||
|
||||
public MapSuggestionProvider(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
@ -40,13 +40,7 @@ public MapSuggestionProvider(Plugin plugin) {
|
||||
|
||||
@Override
|
||||
public Collection<String> getPossibleValues() {
|
||||
Collection<String> values = new HashSet<>();
|
||||
|
||||
for (BmMap map : plugin.getMapTypes()) {
|
||||
values.add(map.getId());
|
||||
}
|
||||
|
||||
return values;
|
||||
return new HashSet<>(plugin.getMaps().keySet());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,13 +43,11 @@ public WorldOrMapSuggestionProvider(Plugin plugin) {
|
||||
public Collection<String> getPossibleValues() {
|
||||
Collection<String> values = new HashSet<>();
|
||||
|
||||
for (World world : plugin.getWorlds()) {
|
||||
for (World world : plugin.getWorlds().values()) {
|
||||
values.add(world.getName());
|
||||
}
|
||||
|
||||
for (BmMap map : plugin.getMapTypes()) {
|
||||
values.add(map.getId());
|
||||
}
|
||||
values.addAll(plugin.getMaps().keySet());
|
||||
|
||||
return values;
|
||||
}
|
||||
|
@ -24,15 +24,15 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common.plugin.commands;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class WorldSuggestionProvider<S> extends AbstractSuggestionProvider<S> {
|
||||
|
||||
private Plugin plugin;
|
||||
private final Plugin plugin;
|
||||
|
||||
public WorldSuggestionProvider(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
@ -42,7 +42,7 @@ public WorldSuggestionProvider(Plugin plugin) {
|
||||
public Collection<String> getPossibleValues() {
|
||||
Collection<String> values = new HashSet<>();
|
||||
|
||||
for (World world : plugin.getWorlds()) {
|
||||
for (World world : plugin.getWorlds().values()) {
|
||||
values.add(world.getName());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
package de.bluecolored.bluemap.common.plugin.serverinterface;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public enum Dimension {
|
||||
|
||||
OVERWORLD ("Overworld", Path.of("")),
|
||||
NETHER ("Nether", Path.of("DIM-1")),
|
||||
END ("End", Path.of("DIM1"));
|
||||
|
||||
private final String name;
|
||||
private final Path dimensionSubPath;
|
||||
|
||||
Dimension(String name, Path dimensionSubPath) {
|
||||
this.name = name;
|
||||
this.dimensionSubPath = dimensionSubPath;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Path getDimensionSubPath() {
|
||||
return dimensionSubPath;
|
||||
}
|
||||
|
||||
}
|
@ -24,41 +24,40 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common.plugin.serverinterface;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
|
||||
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface Player {
|
||||
|
||||
public UUID getUuid();
|
||||
UUID getUuid();
|
||||
|
||||
public Text getName();
|
||||
Text getName();
|
||||
|
||||
public UUID getWorld();
|
||||
String getWorld();
|
||||
|
||||
public Vector3d getPosition();
|
||||
Vector3d getPosition();
|
||||
|
||||
public boolean isOnline();
|
||||
boolean isOnline();
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if the player is sneaking.
|
||||
* <p><i>If the player is offline the value of this method is undetermined.</i></p>
|
||||
* @return
|
||||
*/
|
||||
public boolean isSneaking();
|
||||
boolean isSneaking();
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the player has an invisibillity effect
|
||||
* <p><i>If the player is offline the value of this method is undetermined.</i></p>
|
||||
*/
|
||||
public boolean isInvisible();
|
||||
boolean isInvisible();
|
||||
|
||||
/**
|
||||
* Returns the {@link Gamemode} this player is in
|
||||
* <p><i>If the player is offline the value of this method is undetermined.</i></p>
|
||||
*/
|
||||
public Gamemode getGamemode();
|
||||
Gamemode getGamemode();
|
||||
|
||||
}
|
||||
|
@ -24,14 +24,18 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common.plugin.serverinterface;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||
import de.bluecolored.bluemap.core.util.Tristate;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ServerInterface {
|
||||
|
||||
MinecraftVersion getMinecraftVersion();
|
||||
|
||||
/**
|
||||
* Registers a ServerEventListener, every method of this interface should be called on the specified events
|
||||
*/
|
||||
@ -42,50 +46,25 @@ public interface ServerInterface {
|
||||
*/
|
||||
void unregisterAllListeners();
|
||||
|
||||
/**
|
||||
* Returns an {@link UUID} for the given world.
|
||||
* The UUID does not need to persist over multiple runtime, but has to be always the same for this runtime.
|
||||
*
|
||||
* @param worldFolder The folder of the world
|
||||
* @return The worlds {@link UUID}
|
||||
* @throws IOException If the uuid is read from some file and there was an exception reading this file
|
||||
*/
|
||||
UUID getUUIDForWorld(File worldFolder) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the name of the world with that UUID, the name is used in commands and should therefore be unique.<br>
|
||||
* A return-value of <code>null</code> makes bluemap load the world-name from the level.dat and dimension-folder.
|
||||
*
|
||||
* @param worldUUID the uuid of the world
|
||||
* @return the worlds name
|
||||
*/
|
||||
default String getWorldName(UUID worldUUID) {
|
||||
return null;
|
||||
default Optional<ServerWorld> getWorld(Path worldFolder) {
|
||||
Path normalizedWorldFolder = worldFolder.toAbsolutePath().normalize();
|
||||
return getLoadedWorlds().stream()
|
||||
.filter(world -> world.getSaveFolder().toAbsolutePath().normalize().equals(normalizedWorldFolder))
|
||||
.findAny();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to persist all changes that have been made in a world to disk.
|
||||
*
|
||||
* @param worldUUID The {@link UUID} of the world to be persisted.
|
||||
* @return <code>true</code> if the changes have been successfully persisted, <code>false</code> if this operation is not supported by the implementation
|
||||
*
|
||||
* @throws IOException if something went wrong trying to persist the changes
|
||||
* @throws IllegalArgumentException if there is no world with this UUID
|
||||
*/
|
||||
default boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException {
|
||||
return false;
|
||||
}
|
||||
Collection<ServerWorld> getLoadedWorlds();
|
||||
|
||||
/**
|
||||
* Returns the Folder containing the configurations for the plugin
|
||||
*/
|
||||
File getConfigFolder();
|
||||
Path getConfigFolder();
|
||||
|
||||
/**
|
||||
* Gives the possibility to override the metrics-setting in the config
|
||||
*/
|
||||
default boolean isMetricsEnabled(boolean configValue) {
|
||||
return configValue;
|
||||
default Tristate isMetricsEnabled() {
|
||||
return Tristate.UNDEFINED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,38 @@
|
||||
package de.bluecolored.bluemap.common.plugin.serverinterface;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ServerWorld {
|
||||
|
||||
default Optional<String> getId() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
default Optional<String> getName() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Path getSaveFolder();
|
||||
|
||||
default Dimension getDimension() {
|
||||
Path saveFolder = getSaveFolder();
|
||||
String lastName = saveFolder.getFileName().toString();
|
||||
if (lastName.equals("DIM-1")) return Dimension.NETHER;
|
||||
if (lastName.equals("DIM1")) return Dimension.END;
|
||||
return Dimension.OVERWORLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to persist all changes that have been made in a world to disk.
|
||||
*
|
||||
* @return <code>true</code> if the changes have been successfully persisted, <code>false</code> if this operation is not supported by the implementation
|
||||
*
|
||||
* @throws IOException if something went wrong trying to persist the changes
|
||||
*/
|
||||
default boolean persistWorldChanges() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -24,10 +24,10 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common.web;
|
||||
|
||||
import de.bluecolored.bluemap.core.webserver.HttpRequest;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpResponse;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpStatusCode;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpRequest;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpResponse;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpStatusCode;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -27,10 +27,10 @@
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.storage.*;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpRequest;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpResponse;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpStatusCode;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpRequest;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpResponse;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpStatusCode;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
|
@ -25,18 +25,18 @@
|
||||
package de.bluecolored.bluemap.common.web;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
import de.bluecolored.bluemap.core.config.old.MapConfig;
|
||||
import de.bluecolored.bluemap.common.config.MapConfig;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
||||
import de.bluecolored.bluemap.core.util.MathUtils;
|
||||
import de.bluecolored.bluemap.core.util.ConfigUtils;
|
||||
import de.bluecolored.bluemap.core.util.math.Color;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
|
||||
import org.spongepowered.configurate.loader.ConfigurationLoader;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -45,11 +45,11 @@ public class WebSettings {
|
||||
private final ConfigurationLoader<? extends ConfigurationNode> configLoader;
|
||||
private ConfigurationNode rootNode;
|
||||
|
||||
public WebSettings(File settingsFile) throws IOException {
|
||||
FileUtils.createFile(settingsFile);
|
||||
public WebSettings(Path settingsFile) throws IOException {
|
||||
if (!Files.exists(settingsFile)) Files.createFile(settingsFile);
|
||||
|
||||
configLoader = GsonConfigurationLoader.builder()
|
||||
.file(settingsFile)
|
||||
.path(settingsFile)
|
||||
.build();
|
||||
|
||||
load();
|
||||
@ -130,24 +130,24 @@ public void setFrom(BmMap map) throws SerializationException {
|
||||
|
||||
set(map.getWorld().getSpawnPoint().getX(), "maps", map.getId(), "startPos", "x");
|
||||
set(map.getWorld().getSpawnPoint().getZ(), "maps", map.getId(), "startPos", "z");
|
||||
set(map.getWorld().getUUID().toString(), "maps", map.getId(), "world");
|
||||
set(map.getWorldId(), "maps", map.getId(), "world");
|
||||
}
|
||||
|
||||
public void setFrom(MapConfig mapConfig) throws SerializationException {
|
||||
public void setFrom(MapConfig mapConfig, String mapId) throws SerializationException {
|
||||
Vector2i startPos = mapConfig.getStartPos();
|
||||
if (startPos != null) {
|
||||
set(startPos.getX(), "maps", mapConfig.getId(), "startPos", "x");
|
||||
set(startPos.getY(), "maps", mapConfig.getId(), "startPos", "z");
|
||||
set(startPos.getX(), "maps", mapId, "startPos", "x");
|
||||
set(startPos.getY(), "maps", mapId, "startPos", "z");
|
||||
}
|
||||
|
||||
Vector3f skyColor = MathUtils.color3FromInt(mapConfig.getSkyColor());
|
||||
set(skyColor.getX(), "maps", mapConfig.getId(), "skyColor", "r");
|
||||
set(skyColor.getY(), "maps", mapConfig.getId(), "skyColor", "g");
|
||||
set(skyColor.getZ(), "maps", mapConfig.getId(), "skyColor", "b");
|
||||
Color skyColor = new Color().set(ConfigUtils.parseColorFromString(mapConfig.getSkyColor()));
|
||||
set(skyColor.r, "maps", mapId, "skyColor", "r");
|
||||
set(skyColor.g, "maps", mapId, "skyColor", "g");
|
||||
set(skyColor.b, "maps", mapId, "skyColor", "b");
|
||||
|
||||
set(mapConfig.getAmbientLight(), "maps", mapConfig.getId(), "ambientLight");
|
||||
set(mapConfig.getAmbientLight(), "maps", mapId, "ambientLight");
|
||||
|
||||
setName(mapConfig.getName(), mapConfig.getId());
|
||||
setName(mapConfig.getName(), mapId);
|
||||
}
|
||||
|
||||
public void setOrdinal(int ordinal, String mapId) throws SerializationException {
|
||||
|
@ -22,7 +22,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
package de.bluecolored.bluemap.common.webserver;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
|
@ -22,10 +22,10 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
package de.bluecolored.bluemap.common.webserver;
|
||||
|
||||
import de.bluecolored.bluemap.core.webserver.HttpConnection.ConnectionClosedException;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpConnection.InvalidRequestException;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpConnection.ConnectionClosedException;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpConnection.InvalidRequestException;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
@ -22,7 +22,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
package de.bluecolored.bluemap.common.webserver;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface HttpRequestHandler {
|
@ -22,7 +22,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
package de.bluecolored.bluemap.common.webserver;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -22,7 +22,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
package de.bluecolored.bluemap.common.webserver;
|
||||
|
||||
public enum HttpStatusCode {
|
||||
|
@ -22,7 +22,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
package de.bluecolored.bluemap.common.webserver;
|
||||
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
@ -86,6 +86,7 @@ public synchronized void start() {
|
||||
public void run(){
|
||||
if (server == null) return;
|
||||
|
||||
Logger.global.logInfo("WebServer bound to: " + server.getLocalSocketAddress());
|
||||
Logger.global.logInfo("WebServer started.");
|
||||
|
||||
while (!server.isClosed() && server.isBound()){
|
||||
@ -112,7 +113,16 @@ public void run(){
|
||||
}
|
||||
|
||||
public synchronized void close(){
|
||||
if (connectionThreads != null) connectionThreads.shutdown();
|
||||
if (connectionThreads != null) {
|
||||
connectionThreads.shutdown();
|
||||
try {
|
||||
if (!connectionThreads.awaitTermination(10, TimeUnit.SECONDS)) {
|
||||
Logger.global.logWarning("Webserver connections didn't close after 10 seconds!");
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (server != null && !server.isClosed()){
|
@ -8,7 +8,7 @@
|
||||
# and you agree that BlueMap will download and use a minecraft-client file (depending on the minecraft-version) from mojangs servers (https://launcher.mojang.com/) for you.
|
||||
# This file contains resources that belong to mojang and you must not redistribute it or do anything else that is not compliant with mojang's EULA.
|
||||
# BlueMap uses resources in this file to generate the 3D-Models used for the map and texture them. (BlueMap will not work without those resources.)
|
||||
# %datetime-iso%
|
||||
# ${timestamp}
|
||||
accept-download: false
|
||||
|
||||
# This changes the amount of threads that BlueMap will use to render the maps.
|
||||
@ -16,15 +16,15 @@ accept-download: false
|
||||
# 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 -2
|
||||
renderThreadCount: -2
|
||||
|
||||
# If this is true, BlueMap might send really basic metrics reports containg only the implementation-type and the version that is being used to https://metrics.bluecolored.de/bluemap/
|
||||
# Default is 1
|
||||
render-thread-count: 1
|
||||
${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":"bukkit","version":"%version%"}
|
||||
# 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"
|
@ -0,0 +1,83 @@
|
||||
## ##
|
||||
## BlueMap ##
|
||||
## Map-Config ##
|
||||
## ##
|
||||
|
||||
# The name of this map
|
||||
# This defines the display name of this map, you can change this at any time.
|
||||
# Default is the id of this map
|
||||
name: "${name}"
|
||||
|
||||
# The path to the save-folder of the world to render.
|
||||
world: "${world}"
|
||||
|
||||
# The position on the world where the map will be centered if you open it.
|
||||
# You can change this at any time.
|
||||
# This defaults to the world-spawn if you don't set it.
|
||||
#start-pos: [500, -820]
|
||||
|
||||
# The color of thy sky as a hex-color
|
||||
# You can change this at any time.
|
||||
# Default is "#7dabff"
|
||||
sky-color: "${sky-color}"
|
||||
|
||||
# Defines the ambient light-strength that every block is receiving, regardless of the sunlight/blocklight.
|
||||
# 0 is no ambient light, 1 is fully lighted.
|
||||
# You can change this at any time.
|
||||
# Default is 0
|
||||
ambient-light: ${ambient-light}
|
||||
|
||||
# Defines the skylight level that the sky of the world is emitting.
|
||||
# This should always be equivalent to the maximum in-game sky-light for that world!
|
||||
# If this is a normal overworld dimension, set this to 15 (max).
|
||||
# If this is a normal nether or end dimension, set this to 0 (min).
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Default is 15
|
||||
world-sky-light: ${world-sky-light}
|
||||
|
||||
# BlueMap tries to omit all blocks that are below this Y-level and are not visible from above-ground.
|
||||
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
|
||||
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Set to a very high value to remove caves everywhere (e.g. 10000)
|
||||
# Set to a very low value to remove nothing and render all caves (e.g. -10000)
|
||||
# Default is 55 (slightly below water-level)
|
||||
remove-caves-below-y: ${remove-caves-below-y}
|
||||
|
||||
# With this value set to true, BlueMap uses the block-light value instead of the sky-light value to "detect caves".
|
||||
# (See: remove-caves-below-y)
|
||||
# Default is false
|
||||
cave-detection-uses-block-light: false
|
||||
|
||||
# With the below values you can limit the map-render.
|
||||
# This can be used to ignore the nethers ceiling or render only a certain part of a world.
|
||||
# Changing this values might require a re-render of the map, already rendered tiles outside the limits will not be deleted.
|
||||
# Default is no min or max value (= infinite bounds)
|
||||
#min-x: -4000
|
||||
#max-x: 4000
|
||||
#min-z: -4000
|
||||
#max-z: 4000
|
||||
#min-y: 50
|
||||
${max-y-comment<<#>>}max-y: ${max-y}
|
||||
|
||||
# Using this, BlueMap pretends that every Block out of the defined render-bounds is AIR,
|
||||
# this means you can see the blocks where the world is cut (instead of having a see-through/xray view).
|
||||
# This has only an effect if you set some render-bounds above.
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Default is true
|
||||
render-edges: true
|
||||
|
||||
# This defines the storage-config that will be used to save this map.
|
||||
# You can find your storage configs next to this config file in the 'storages'-folder.
|
||||
# Default is "file"
|
||||
storage: "file"
|
||||
|
||||
# Normally BlueMap detects if a chunk has not yet generated it's light-data and omits rendering those chunks.
|
||||
# If this is set to true BlueMap will render Chunks even if there is no light-data!
|
||||
# This can be useful for example if some mod prevents light-data from being saved correctly.
|
||||
# However, this also has a few drawbacks:
|
||||
# - For those chunks, every block will always be fully lit
|
||||
# - Night-mode might not work correctly
|
||||
# - Caves will always be rendered (ignoring the 'renderCaves' setting)
|
||||
# Default is false
|
||||
ignore-missing-light-data: false
|
@ -3,37 +3,42 @@
|
||||
## Plugin-Config ##
|
||||
## ##
|
||||
|
||||
# If the server should send live-updates and player-positions.
|
||||
# If the server should send player-positions to the webapp.
|
||||
# This only works if the integrated webserver is enabled.
|
||||
# Default is true
|
||||
liveUpdates: 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
|
||||
skinDownload: 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
|
||||
hiddenGameModes: [
|
||||
hidden-gamemodes: [
|
||||
"spectator"
|
||||
]
|
||||
|
||||
# If this is true, players that are vanished (by a plugin) will be hidden on the map.
|
||||
# Default is true
|
||||
hide-vanished: true
|
||||
|
||||
# If this is true, players that have an invisibility (potion-)effect will be hidden on the map.
|
||||
# Default is true
|
||||
hideInvisible: true
|
||||
hide-invisible: true
|
||||
|
||||
# If this is true, players that are sneaking will be hidden on the map.
|
||||
# Default is false
|
||||
hideSneaking: false
|
||||
hide-sneaking: false
|
||||
|
||||
# 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
|
||||
# Setting this to 0 or -1 will disable this feature -> bluemap will not pause rendering
|
||||
# Default is -1
|
||||
playerRenderLimit: -1
|
||||
player-render-limit: -1
|
||||
|
||||
# The interval in minutes in which a full map-update will be triggered.
|
||||
# This is additionally!! to the normal map-update process (in case that fails to detect any file-changes).
|
||||
# The interval in minutes in which a map-update will be triggered.
|
||||
# This is additionally to the normal map-update process (in case that fails to detect any file-changes).
|
||||
# ! This DOESN'T re-render the entire map each time, it only checks if there are some changes that have not been rendered yet!
|
||||
# Default is 1440 (24 hours)
|
||||
fullUpdateInterval: 1440
|
||||
map-update-interval: 1440
|
@ -0,0 +1,21 @@
|
||||
## ##
|
||||
## BlueMap ##
|
||||
## Webapp-Config ##
|
||||
## ##
|
||||
|
||||
# With this setting you can disable the creation and updating of all web-app related files
|
||||
# Default is true
|
||||
enabled: true
|
||||
|
||||
# The webroot where the web-application files will be created.
|
||||
# Usually this should be set to the same directory like in the webserver.conf!
|
||||
# Default is "bluemap/web"
|
||||
webroot: "bluemap/web"
|
||||
|
||||
# If the web-application should use cookies to save the configurations of a user.
|
||||
# Default is true
|
||||
use-cookies: true
|
||||
|
||||
# If the free-flight-mode in the web-application is enabled or not.
|
||||
# Default is true
|
||||
enable-free-flight: true
|
@ -0,0 +1,22 @@
|
||||
## ##
|
||||
## BlueMap ##
|
||||
## Webserver-Config ##
|
||||
## ##
|
||||
|
||||
# With this setting you can disable the integrated web-server.
|
||||
# This is useful if you want to only render the map-data for later use, or if you setup your own webserver.
|
||||
# Default is enabled
|
||||
enabled: true
|
||||
|
||||
# The webroot that the server will host to the web.
|
||||
# Usually this should be set to the same directory like in the webapp.conf!
|
||||
# Default is "bluemap/web"
|
||||
webroot: "bluemap/web"
|
||||
|
||||
# The port that the webserver listens to.
|
||||
# Default is 8100
|
||||
port: 8100
|
||||
|
||||
# Max number of simultaneous connections that the webserver allows
|
||||
# Default is 100
|
||||
max-connection-count: 100
|
@ -1,99 +0,0 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import org.spongepowered.configurate.ConfigurateException;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
|
||||
import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
|
||||
import org.spongepowered.configurate.loader.ConfigurationLoader;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ConfigManager {
|
||||
|
||||
private static final String[] CONFIG_FILE_ENDINGS = new String[] {
|
||||
".conf",
|
||||
".json"
|
||||
};
|
||||
|
||||
private final Path configRoot;
|
||||
|
||||
public ConfigManager(Path configRoot) {
|
||||
this.configRoot = configRoot;
|
||||
}
|
||||
|
||||
public ConfigurationNode loadConfig(Path rawPath) throws ConfigurationException {
|
||||
Path path = findConfigPath(configRoot.resolve(rawPath));
|
||||
|
||||
if (!Files.exists(path)) {
|
||||
throw new ConfigurationException(
|
||||
"BlueMap tried to find this file, but it does not exist:\n" +
|
||||
path);
|
||||
}
|
||||
|
||||
if (!Files.isReadable(path)) {
|
||||
throw new ConfigurationException(
|
||||
"BlueMap tried to read this file, but can not access it:\n" +
|
||||
path + "\n" +
|
||||
"Check if BlueMap has the permission to read this file.");
|
||||
}
|
||||
|
||||
try {
|
||||
return getLoader(path).load();
|
||||
} catch (ConfigurateException ex) {
|
||||
throw new ConfigurationException(
|
||||
"BlueMap failed to parse this file:\n" +
|
||||
path + "\n" +
|
||||
"Check if the file is correctly formatted.\n" +
|
||||
"(for example there might be a } or ] or , missing somewhere)",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Path getConfigRoot() {
|
||||
return configRoot;
|
||||
}
|
||||
|
||||
private Path findConfigPath(Path rawPath) {
|
||||
for (String fileEnding : CONFIG_FILE_ENDINGS) {
|
||||
if (rawPath.getFileName().endsWith(fileEnding)) return rawPath;
|
||||
}
|
||||
|
||||
for (String fileEnding : CONFIG_FILE_ENDINGS) {
|
||||
Path path = rawPath.getParent().resolve(rawPath.getFileName() + fileEnding);
|
||||
if (Files.exists(path)) return path;
|
||||
}
|
||||
|
||||
return rawPath.getParent().resolve(rawPath.getFileName() + CONFIG_FILE_ENDINGS[0]);
|
||||
}
|
||||
|
||||
private ConfigurationLoader<? extends ConfigurationNode> getLoader(Path path){
|
||||
if (path.getFileName().endsWith(".json")) return GsonConfigurationLoader.builder().path(path).build();
|
||||
else return HoconConfigurationLoader.builder().path(path).build();
|
||||
}
|
||||
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
/*
|
||||
* 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.config.old;
|
||||
|
||||
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
|
||||
import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
|
||||
import org.spongepowered.configurate.loader.ConfigurationLoader;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Deprecated
|
||||
public class ConfigManager {
|
||||
|
||||
private static final Set<Placeholder> CONFIG_PLACEHOLDERS = new HashSet<>();
|
||||
|
||||
static {
|
||||
CONFIG_PLACEHOLDERS.add(new Placeholder("version", BlueMap.VERSION));
|
||||
CONFIG_PLACEHOLDERS.add(new Placeholder("datetime-iso", () -> LocalDateTime.now().withNano(0).toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads or creates a config file for BlueMap.
|
||||
*
|
||||
* @param configFile The config file to load
|
||||
* @param defaultConfig The default config that is used as a template if the config file does not exist (can be null)
|
||||
* @param defaultValues The default values used if a key is not present in the config (can be null)
|
||||
* @param usePlaceholders Whether to replace placeholders from the defaultConfig if it is newly generated
|
||||
* @param generateEmptyConfig Whether to generate an empty config file if no default config is provided
|
||||
* @return The loaded configuration node
|
||||
* @throws ConfigurationException if an IOException occurs while loading
|
||||
*/
|
||||
public ConfigurationNode loadOrCreate(File configFile, URL defaultConfig, URL defaultValues, boolean usePlaceholders, boolean generateEmptyConfig) throws ConfigurationException {
|
||||
|
||||
ConfigurationNode configNode;
|
||||
if (!configFile.exists()) {
|
||||
try {
|
||||
FileUtils.mkDirsParent(configFile);
|
||||
|
||||
if (defaultConfig != null) {
|
||||
//load content of default config
|
||||
String content;
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(defaultConfig.openStream(), StandardCharsets.UTF_8))) {
|
||||
content = reader.lines().collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
//replace placeholders if enabled
|
||||
if (usePlaceholders) {
|
||||
for (Placeholder placeholder : CONFIG_PLACEHOLDERS) {
|
||||
content = placeholder.apply(content);
|
||||
}
|
||||
}
|
||||
|
||||
//create the config file
|
||||
Files.write(configFile.toPath(), content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
//load
|
||||
configNode = getLoader(configFile).load();
|
||||
} else {
|
||||
//create empty config
|
||||
ConfigurationLoader<? extends ConfigurationNode> loader = getLoader(configFile);
|
||||
configNode = loader.createNode();
|
||||
|
||||
//save to create file
|
||||
if (generateEmptyConfig) loader.save(configNode);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException(
|
||||
"BlueMap tried to create this file:\n" +
|
||||
configFile +
|
||||
"but something went wrong!\n" +
|
||||
"Does BlueMap has sufficient write permissions?",
|
||||
ex);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
//load config
|
||||
configNode = getLoader(configFile).load();
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException(
|
||||
"BlueMap tried to load this file:\n" +
|
||||
configFile +
|
||||
"but something went wrong!\n" +
|
||||
"Is the config-file formatted correctly?\n" +
|
||||
"Maybe there is a } or ] or , missing?" +
|
||||
"Does BlueMap has sufficient read permissions to this file?",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
//populate missing values with default values
|
||||
if (defaultValues != null) {
|
||||
try {
|
||||
ConfigurationNode defaultValuesNode = getLoader(defaultValues).load();
|
||||
configNode.mergeFrom(defaultValuesNode);
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException(
|
||||
"Something went wrong trying to load this config:\n" +
|
||||
configFile,
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
return configNode;
|
||||
}
|
||||
|
||||
private ConfigurationLoader<? extends ConfigurationNode> getLoader(URL url){
|
||||
if (url.getFile().endsWith(".json")) return GsonConfigurationLoader.builder().url(url).build();
|
||||
else return HoconConfigurationLoader.builder().url(url).build();
|
||||
}
|
||||
|
||||
private ConfigurationLoader<? extends ConfigurationNode> getLoader(File file){
|
||||
if (file.getName().endsWith(".json")) return GsonConfigurationLoader.builder().file(file).build();
|
||||
else return HoconConfigurationLoader.builder().file(file).build();
|
||||
}
|
||||
|
||||
public static File toFolder(String pathString) throws ConfigurationException {
|
||||
Objects.requireNonNull(pathString);
|
||||
|
||||
File file = new File(pathString);
|
||||
if (file.exists() && !file.isDirectory()) throw new ConfigurationException("Invalid configuration: Path '" + file.getAbsolutePath() + "' is a file (should be a directory)");
|
||||
return file;
|
||||
}
|
||||
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* 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.config.old;
|
||||
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
@DebugDump
|
||||
public class CoreConfig {
|
||||
|
||||
private boolean downloadAccepted = false;
|
||||
private int renderThreadCount = 0;
|
||||
private boolean metricsEnabled = false;
|
||||
private File dataFolder = new File("data");
|
||||
|
||||
public CoreConfig(ConfigurationNode node) throws ConfigurationException {
|
||||
|
||||
//accept-download
|
||||
downloadAccepted = node.node("accept-download").getBoolean(false);
|
||||
|
||||
//renderThreadCount
|
||||
int processors = Runtime.getRuntime().availableProcessors();
|
||||
renderThreadCount = node.node("renderThreadCount").getInt(0);
|
||||
if (renderThreadCount <= 0) renderThreadCount = processors + renderThreadCount;
|
||||
if (renderThreadCount <= 0) renderThreadCount = 1;
|
||||
|
||||
//metrics
|
||||
metricsEnabled = node.node("metrics").getBoolean(false);
|
||||
|
||||
//data
|
||||
dataFolder = ConfigManager.toFolder(node.node("data").getString("data"));
|
||||
|
||||
}
|
||||
|
||||
public File getDataFolder() {
|
||||
return dataFolder;
|
||||
}
|
||||
|
||||
public boolean isDownloadAccepted() {
|
||||
return downloadAccepted;
|
||||
}
|
||||
|
||||
public boolean isMetricsEnabled() {
|
||||
return metricsEnabled;
|
||||
}
|
||||
|
||||
public int getRenderThreadCount() {
|
||||
return renderThreadCount;
|
||||
}
|
||||
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
/*
|
||||
* 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.config.old;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.map.MapSettings;
|
||||
import de.bluecolored.bluemap.core.util.ConfigUtils;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@DebugDump
|
||||
public class MapConfig implements MapSettings {
|
||||
private static final Pattern VALID_ID_PATTERN = Pattern.compile("[a-zA-Z0-9_]+");
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private String world;
|
||||
|
||||
private Vector2i startPos;
|
||||
private int skyColor;
|
||||
private float ambientLight;
|
||||
private int worldSkyLight;
|
||||
|
||||
private int removeCavesBelowY;
|
||||
private boolean caveDetectionUsesBlockLight;
|
||||
|
||||
private Vector3i min, max;
|
||||
private boolean renderEdges;
|
||||
|
||||
private String storage;
|
||||
private boolean ignoreMissingLightData;
|
||||
|
||||
private int hiresTileSize;
|
||||
|
||||
private int lowresPointsPerHiresTile;
|
||||
private int lowresPointsPerLowresTile;
|
||||
|
||||
public MapConfig(ConfigurationNode node) throws ConfigurationException {
|
||||
|
||||
//id
|
||||
this.id = node.node("id").getString("");
|
||||
if (id.isEmpty()) throw new ConfigurationException("Invalid configuration: Node maps[?].id is not defined");
|
||||
if (!VALID_ID_PATTERN.matcher(id).matches()) throw new ConfigurationException("Invalid configuration: Node maps[?].id '" + id + "' has invalid characters in it");
|
||||
|
||||
//name
|
||||
this.name = node.node("name").getString(id);
|
||||
|
||||
//world
|
||||
this.world = node.node("world").getString("");
|
||||
if (world.isEmpty()) throw new ConfigurationException("Invalid configuration: Node maps[?].world is not defined");
|
||||
|
||||
//startPos
|
||||
if (!node.node("startPos").virtual()) this.startPos = ConfigUtils.readVector2i(node.node("startPos"));
|
||||
|
||||
//skyColor
|
||||
if (!node.node("skyColor").virtual()) this.skyColor = ConfigUtils.readColorInt(node.node("skyColor"));
|
||||
else this.skyColor = 0x7dabff;
|
||||
|
||||
//ambientLight
|
||||
this.ambientLight = node.node("ambientLight").getFloat(0f);
|
||||
|
||||
//worldSkyLight
|
||||
this.worldSkyLight = node.node("worldSkyLight").getInt(15);
|
||||
|
||||
//renderCaves
|
||||
this.removeCavesBelowY = node.node("removeCavesBelowY").getInt(55);
|
||||
this.caveDetectionUsesBlockLight = node.node("caveDetectionUsesBlockLight").getBoolean(false);
|
||||
|
||||
//bounds
|
||||
int minX = node.node("minX").getInt(MapSettings.super.getMin().getX());
|
||||
int maxX = node.node("maxX").getInt(MapSettings.super.getMax().getX());
|
||||
int minZ = node.node("minZ").getInt(MapSettings.super.getMin().getZ());
|
||||
int maxZ = node.node("maxZ").getInt(MapSettings.super.getMax().getZ());
|
||||
int minY = node.node("minY").getInt(MapSettings.super.getMin().getY());
|
||||
int maxY = node.node("maxY").getInt(MapSettings.super.getMax().getY());
|
||||
this.min = new Vector3i(minX, minY, minZ);
|
||||
this.max = new Vector3i(maxX, maxY, maxZ);
|
||||
|
||||
//renderEdges
|
||||
this.renderEdges = node.node("renderEdges").getBoolean(true);
|
||||
|
||||
//storage
|
||||
this.storage = node.node("storage").getString("file");
|
||||
|
||||
//ignoreMissingLightData
|
||||
this.ignoreMissingLightData = node.node("ignoreMissingLightData").getBoolean(false);
|
||||
|
||||
//tile-settings
|
||||
this.hiresTileSize = node.node("hires", "tileSize").getInt(32);
|
||||
this.lowresPointsPerHiresTile = node.node("lowres", "pointsPerHiresTile").getInt(4);
|
||||
this.lowresPointsPerLowresTile = node.node("lowres", "pointsPerLowresTile").getInt(50);
|
||||
|
||||
//check valid tile configuration values
|
||||
double blocksPerPoint = (double) this.hiresTileSize / (double) this.lowresPointsPerHiresTile;
|
||||
if (blocksPerPoint != Math.floor(blocksPerPoint)) throw new ConfigurationException("Invalid configuration: Invalid map resolution settings of map " + id + ": hires.tileSize / lowres.pointsPerTile has to be an integer result");
|
||||
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getWorldPath() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public Vector2i getStartPos() {
|
||||
return startPos;
|
||||
}
|
||||
|
||||
public int getSkyColor() {
|
||||
return skyColor;
|
||||
}
|
||||
|
||||
public String getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAmbientLight() {
|
||||
return ambientLight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWorldSkyLight() {
|
||||
return worldSkyLight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemoveCavesBelowY() {
|
||||
return removeCavesBelowY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCaveDetectionUsesBlockLight() {
|
||||
return caveDetectionUsesBlockLight;
|
||||
}
|
||||
|
||||
public boolean isIgnoreMissingLightData() {
|
||||
return ignoreMissingLightData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHiresTileSize() {
|
||||
return hiresTileSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLowresPointsPerHiresTile() {
|
||||
return lowresPointsPerHiresTile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLowresPointsPerLowresTile() {
|
||||
return lowresPointsPerLowresTile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3i getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3i getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRenderEdges() {
|
||||
return renderEdges;
|
||||
}
|
||||
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.config.old;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Placeholder {
|
||||
|
||||
private String name;
|
||||
private Supplier<String> valueSupplier;
|
||||
|
||||
public Placeholder(String name, String value) {
|
||||
this(name, () -> value);
|
||||
}
|
||||
|
||||
public Placeholder(String name, Supplier<String> valueSupplier) {
|
||||
this.name = name;
|
||||
this.valueSupplier = valueSupplier;
|
||||
}
|
||||
|
||||
public String apply(String config) {
|
||||
return config.replaceAll(Pattern.quote("%" + name + "%"), valueSupplier.get());
|
||||
}
|
||||
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* 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.config.old;
|
||||
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@DebugDump
|
||||
public class RenderConfig {
|
||||
|
||||
private File webRoot;
|
||||
private boolean useCookies;
|
||||
private boolean enableFreeFlight;
|
||||
private List<MapConfig> mapConfigs;
|
||||
|
||||
public RenderConfig(ConfigurationNode node) throws ConfigurationException {
|
||||
|
||||
//webroot
|
||||
String webRootString = node.node("webroot").getString();
|
||||
if (webRootString == null) throw new ConfigurationException("Invalid configuration: Node webroot is not defined");
|
||||
webRoot = ConfigManager.toFolder(webRootString);
|
||||
|
||||
//cookies
|
||||
useCookies = node.node("useCookies").getBoolean(true);
|
||||
|
||||
// free-flight mode
|
||||
enableFreeFlight = node.node("enableFreeFlight").getBoolean(true);
|
||||
|
||||
//maps
|
||||
mapConfigs = new ArrayList<>();
|
||||
for (ConfigurationNode mapConfigNode : node.node("maps").childrenList()) {
|
||||
mapConfigs.add(new MapConfig(mapConfigNode));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public File getWebRoot() {
|
||||
return webRoot;
|
||||
}
|
||||
|
||||
public boolean isUseCookies() {
|
||||
return useCookies;
|
||||
}
|
||||
|
||||
public boolean isEnableFreeFlight() {
|
||||
return enableFreeFlight;
|
||||
}
|
||||
|
||||
public List<MapConfig> getMapConfigs(){
|
||||
return mapConfigs;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
/*
|
||||
* 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.config.old;
|
||||
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
@DebugDump
|
||||
public class WebServerConfig {
|
||||
|
||||
private boolean enabled = true;
|
||||
private File webRoot = new File("web");
|
||||
|
||||
private InetAddress bindAddress = null;
|
||||
private int port = 8100;
|
||||
private int maxConnections = 100;
|
||||
|
||||
public WebServerConfig(ConfigurationNode node) throws ConfigurationException {
|
||||
|
||||
//enabled
|
||||
enabled = node.node("enabled").getBoolean(false);
|
||||
|
||||
if (enabled) {
|
||||
//webroot
|
||||
String webRootString = node.node("webroot").getString();
|
||||
if (webRootString == null) throw new ConfigurationException("Invalid configuration: Node webroot is not defined");
|
||||
webRoot = ConfigManager.toFolder(webRootString);
|
||||
|
||||
//ip
|
||||
String bindAddressString = node.node("ip").getString("");
|
||||
try {
|
||||
if (bindAddressString.isEmpty() || bindAddressString.equals("0.0.0.0") ||
|
||||
bindAddressString.equals("::0")) {
|
||||
bindAddress = new InetSocketAddress(0).getAddress(); // 0.0.0.0
|
||||
} else if (bindAddressString.equals("#getLocalHost")) {
|
||||
bindAddress = InetAddress.getLocalHost();
|
||||
} else {
|
||||
bindAddress = InetAddress.getByName(bindAddressString);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException("Failed to parse ip: '" + bindAddressString + "'", ex);
|
||||
}
|
||||
|
||||
//port
|
||||
port = node.node("port").getInt(8100);
|
||||
|
||||
//maxConnectionCount
|
||||
maxConnections = node.node("maxConnectionCount").getInt(100);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isWebserverEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public File getWebRoot() {
|
||||
return webRoot;
|
||||
}
|
||||
|
||||
public InetAddress getWebserverBindAddress() {
|
||||
return bindAddress;
|
||||
}
|
||||
|
||||
public int getWebserverPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public int getWebserverMaxConnections() {
|
||||
return maxConnections;
|
||||
}
|
||||
|
||||
}
|
@ -50,6 +50,7 @@ public class BmMap {
|
||||
|
||||
private final String id;
|
||||
private final String name;
|
||||
private final String worldId;
|
||||
private final World world;
|
||||
private final Storage storage;
|
||||
|
||||
@ -63,9 +64,10 @@ public class BmMap {
|
||||
private long renderTimeSumNanos;
|
||||
private long tilesRendered;
|
||||
|
||||
public BmMap(String id, String name, World world, Storage storage, ResourcePack resourcePack, MapSettings settings) throws IOException {
|
||||
public BmMap(String id, String name, String worldId, World world, Storage storage, ResourcePack resourcePack, MapSettings settings) throws IOException {
|
||||
this.id = Objects.requireNonNull(id);
|
||||
this.name = Objects.requireNonNull(name);
|
||||
this.worldId = Objects.requireNonNull(worldId);
|
||||
this.world = Objects.requireNonNull(world);
|
||||
this.storage = Objects.requireNonNull(storage);
|
||||
|
||||
@ -135,6 +137,10 @@ public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getWorldId() {
|
||||
return worldId;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ public HiresModelRenderer(ResourcePack resourcePack, RenderSettings renderSettin
|
||||
}
|
||||
|
||||
public HiresTileMeta render(World world, Vector3i modelMin, Vector3i modelMax, HiresTileModel model) {
|
||||
Vector3i min = modelMin.max(renderSettings.getMin());
|
||||
Vector3i max = modelMax.min(renderSettings.getMax());
|
||||
Vector3i min = modelMin.max(renderSettings.getMinPos());
|
||||
Vector3i max = modelMax.min(renderSettings.getMaxPos());
|
||||
Vector3i modelAnchor = new Vector3i(modelMin.getX(), 0, modelMin.getZ());
|
||||
|
||||
HiresTileMeta tileMeta = new HiresTileMeta(modelMin.getX(), modelMin.getZ(), modelMax.getX(), modelMax.getZ()); //TODO: recycle tilemeta instances?
|
||||
|
@ -44,14 +44,14 @@ public interface RenderSettings {
|
||||
/**
|
||||
* The minimum position of blocks to render
|
||||
*/
|
||||
default Vector3i getMin() {
|
||||
default Vector3i getMinPos() {
|
||||
return DEFAULT_MIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum position of blocks to render
|
||||
*/
|
||||
default Vector3i getMax() {
|
||||
default Vector3i getMaxPos() {
|
||||
return DEFAULT_MAX;
|
||||
}
|
||||
|
||||
@ -74,8 +74,8 @@ default boolean isRenderEdges() {
|
||||
}
|
||||
|
||||
default boolean isInsideRenderBoundaries(int x, int z) {
|
||||
Vector3i min = getMin();
|
||||
Vector3i max = getMax();
|
||||
Vector3i min = getMinPos();
|
||||
Vector3i max = getMaxPos();
|
||||
|
||||
return
|
||||
x >= min.getX() &&
|
||||
@ -85,8 +85,8 @@ default boolean isInsideRenderBoundaries(int x, int z) {
|
||||
}
|
||||
|
||||
default boolean isInsideRenderBoundaries(int x, int y, int z) {
|
||||
Vector3i min = getMin();
|
||||
Vector3i max = getMax();
|
||||
Vector3i min = getMinPos();
|
||||
Vector3i max = getMaxPos();
|
||||
|
||||
return
|
||||
x >= min.getX() &&
|
||||
|
@ -40,39 +40,33 @@
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@DebugDump
|
||||
public class MCAWorld implements World {
|
||||
|
||||
private static final Grid CHUNK_GRID = new Grid(16);
|
||||
private static final Grid REGION_GRID = new Grid(32).multiply(CHUNK_GRID);
|
||||
|
||||
@DebugDump private final UUID uuid;
|
||||
@DebugDump private final Path worldFolder;
|
||||
@DebugDump private final String name;
|
||||
@DebugDump private final Vector3i spawnPoint;
|
||||
private final Path worldFolder;
|
||||
|
||||
@DebugDump private final int skyLight;
|
||||
@DebugDump private final boolean ignoreMissingLightData;
|
||||
private final String name;
|
||||
private final Vector3i spawnPoint;
|
||||
|
||||
private final int skyLight;
|
||||
private final boolean ignoreMissingLightData;
|
||||
|
||||
private final LoadingCache<Vector2i, MCARegion> regionCache;
|
||||
private final LoadingCache<Vector2i, MCAChunk> chunkCache;
|
||||
|
||||
private MCAWorld(
|
||||
Path worldFolder,
|
||||
UUID uuid,
|
||||
String name,
|
||||
Vector3i spawnPoint,
|
||||
int skyLight,
|
||||
boolean ignoreMissingLightData
|
||||
) {
|
||||
this.uuid = uuid;
|
||||
this.worldFolder = worldFolder;
|
||||
this.name = name;
|
||||
this.spawnPoint = spawnPoint;
|
||||
|
||||
public MCAWorld(Path worldFolder, int skyLight, boolean ignoreMissingLightData) throws IOException {
|
||||
this.worldFolder = worldFolder.toRealPath();
|
||||
this.skyLight = skyLight;
|
||||
this.ignoreMissingLightData = ignoreMissingLightData;
|
||||
|
||||
@ -87,6 +81,22 @@ private MCAWorld(
|
||||
.maximumSize(500)
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build(this::loadChunk);
|
||||
|
||||
try {
|
||||
Path levelFile = resolveLevelFile(worldFolder);
|
||||
CompoundTag level = (CompoundTag) NBTUtil.readTag(levelFile.toFile());
|
||||
CompoundTag levelData = level.getCompoundTag("Data");
|
||||
|
||||
this.name = levelData.getString("LevelName");
|
||||
|
||||
this.spawnPoint = new Vector3i(
|
||||
levelData.getInt("SpawnX"),
|
||||
levelData.getInt("SpawnY"),
|
||||
levelData.getInt("SpawnZ")
|
||||
);
|
||||
} catch (ClassCastException | NullPointerException ex) {
|
||||
throw new IOException("Invalid level.dat format!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockState getBlockState(Vector3i pos) {
|
||||
@ -144,11 +154,6 @@ public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getSaveFolder() {
|
||||
return worldFolder;
|
||||
@ -256,62 +261,38 @@ private MCAChunk loadChunk(int x, int z) {
|
||||
return MCAChunk.empty();
|
||||
}
|
||||
|
||||
public static MCAWorld load(Path worldFolder, UUID uuid, String name, int skyLight, boolean ignoreMissingLightData) throws IOException {
|
||||
try {
|
||||
StringBuilder subDimensionName = new StringBuilder();
|
||||
|
||||
File levelFolder = worldFolder.toFile();
|
||||
File levelFile = new File(levelFolder, "level.dat");
|
||||
int searchDepth = 0;
|
||||
|
||||
while (!levelFile.exists() && searchDepth < 4) {
|
||||
searchDepth++;
|
||||
subDimensionName.insert(0, "/").insert(1, levelFolder.getName());
|
||||
levelFolder = levelFolder.getParentFile();
|
||||
if (levelFolder == null) break;
|
||||
|
||||
levelFile = new File(levelFolder, "level.dat");
|
||||
}
|
||||
|
||||
if (!levelFile.exists()) {
|
||||
throw new FileNotFoundException("Could not find a level.dat file for this world!");
|
||||
}
|
||||
|
||||
CompoundTag level = (CompoundTag) NBTUtil.readTag(levelFile);
|
||||
CompoundTag levelData = level.getCompoundTag("Data");
|
||||
|
||||
if (name == null) {
|
||||
name = levelData.getString("LevelName") + subDimensionName;
|
||||
}
|
||||
|
||||
Vector3i spawnPoint = new Vector3i(
|
||||
levelData.getInt("SpawnX"),
|
||||
levelData.getInt("SpawnY"),
|
||||
levelData.getInt("SpawnZ")
|
||||
);
|
||||
|
||||
return new MCAWorld(
|
||||
worldFolder,
|
||||
uuid,
|
||||
name,
|
||||
spawnPoint,
|
||||
skyLight,
|
||||
ignoreMissingLightData
|
||||
);
|
||||
} catch (ClassCastException | NullPointerException ex) {
|
||||
throw new IOException("Invaid level.dat format!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MCAWorld{" +
|
||||
"uuid=" + uuid +
|
||||
", worldFolder=" + worldFolder +
|
||||
"worldFolder=" + worldFolder +
|
||||
", name='" + name + '\'' +
|
||||
", spawnPoint=" + spawnPoint +
|
||||
", skyLight=" + skyLight +
|
||||
", ignoreMissingLightData=" + ignoreMissingLightData +
|
||||
'}';
|
||||
}
|
||||
|
||||
private static Path resolveLevelFile(Path worldFolder) throws IOException {
|
||||
Path levelFolder = worldFolder.toRealPath();
|
||||
Path levelFile = levelFolder.resolve("level.dat");
|
||||
int searchDepth = 0;
|
||||
|
||||
while (!Files.isRegularFile(levelFile) && searchDepth < 4) {
|
||||
searchDepth++;
|
||||
levelFolder = levelFolder.getParent();
|
||||
if (levelFolder == null) break;
|
||||
|
||||
levelFile = levelFolder.resolve("level.dat");
|
||||
}
|
||||
|
||||
if (!Files.isRegularFile(levelFile))
|
||||
throw new FileNotFoundException("Could not find a level.dat file for this world!");
|
||||
|
||||
return levelFile;
|
||||
}
|
||||
|
||||
private static final int VEC_2I_CACHE_SIZE = 0x4000;
|
||||
private static final int VEC_2I_CACHE_MASK = VEC_2I_CACHE_SIZE - 1;
|
||||
private static final Vector2i[] VEC_2I_CACHE = new Vector2i[VEC_2I_CACHE_SIZE];
|
||||
|
@ -25,7 +25,6 @@
|
||||
package de.bluecolored.bluemap.core.storage.file;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import de.bluecolored.bluemap.core.config.storage.FileConfig;
|
||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.storage.*;
|
||||
import de.bluecolored.bluemap.core.util.AtomicFileHelper;
|
||||
@ -43,7 +42,7 @@ public class FileStorage extends Storage {
|
||||
private final Path root;
|
||||
private final Compression compression;
|
||||
|
||||
public FileStorage(FileConfig config) {
|
||||
public FileStorage(FileStorageSettings config) {
|
||||
this.root = config.getRoot();
|
||||
this.compression = config.getCompression();
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package de.bluecolored.bluemap.core.storage.file;
|
||||
|
||||
import de.bluecolored.bluemap.core.storage.Compression;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface FileStorageSettings {
|
||||
|
||||
Path getRoot();
|
||||
|
||||
Compression getCompression();
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package de.bluecolored.bluemap.core.storage.sql;
|
||||
|
||||
public class SQLDriverException extends Exception {
|
||||
|
||||
public SQLDriverException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLDriverException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SQLDriverException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public SQLDriverException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
@ -28,8 +28,6 @@
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.core.config.storage.SQLConfig;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.storage.*;
|
||||
import de.bluecolored.bluemap.core.util.WrappedOutputStream;
|
||||
@ -68,7 +66,7 @@ public class SQLStorage extends Storage {
|
||||
.executor(BlueMap.THREAD_POOL)
|
||||
.build(this::loadMapTileCompressionFK);
|
||||
|
||||
public SQLStorage(SQLConfig config) throws ConfigurationException {
|
||||
public SQLStorage(SQLStorageSettings config) throws MalformedURLException, SQLDriverException {
|
||||
try {
|
||||
if (config.getDriverClass().isPresent()) {
|
||||
if (config.getDriverJar().isPresent()) {
|
||||
@ -79,11 +77,12 @@ public SQLStorage(SQLConfig config) throws ConfigurationException {
|
||||
try {
|
||||
driver = (Driver) driverClass.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception ex) {
|
||||
throw new ConfigurationException(
|
||||
throw new SQLDriverException("Failed to create an instance of the driver-class", ex);
|
||||
/*throw new ConfigurationException(
|
||||
"BlueMap is not able to create an instance of the configured Driver-Class.\n" +
|
||||
"This means that BlueMap can not load this Driver at runtime.\n" +
|
||||
"Instead you'll need to add your driver-jar to the classpath when starting your server," +
|
||||
"e.g. using the '-classpath' command-line argument", ex);
|
||||
"e.g. using the '-classpath' command-line argument", ex);*/
|
||||
}
|
||||
this.dataSource = createDataSource(config.getDbUrl(), config.getUser(), config.getPassword(), driver);
|
||||
} else {
|
||||
@ -93,12 +92,10 @@ public SQLStorage(SQLConfig config) throws ConfigurationException {
|
||||
} else {
|
||||
this.dataSource = createDataSource(config.getDbUrl(), config.getUser(), config.getPassword());
|
||||
}
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new ConfigurationException(
|
||||
"The path to your driver-jar is invalid. Check your sql-storage-config!", ex);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new ConfigurationException(
|
||||
"The driver-class does not exist. Check your sql-storage-config!", ex);
|
||||
throw new SQLDriverException("The driver-class does not exist.", ex);
|
||||
//throw new ConfigurationException("The path to your driver-jar is invalid. Check your sql-storage-config!", ex);
|
||||
//throw new ConfigurationException("The driver-class does not exist. Check your sql-storage-config!", ex);
|
||||
}
|
||||
|
||||
this.compression = config.getCompression();
|
||||
|
@ -0,0 +1,23 @@
|
||||
package de.bluecolored.bluemap.core.storage.sql;
|
||||
|
||||
import de.bluecolored.bluemap.core.storage.Compression;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface SQLStorageSettings {
|
||||
|
||||
Optional<URL> getDriverJar() throws MalformedURLException;
|
||||
|
||||
Optional<String> getDriverClass();
|
||||
|
||||
String getDbUrl();
|
||||
|
||||
String getUser();
|
||||
|
||||
String getPassword();
|
||||
|
||||
Compression getCompression();
|
||||
|
||||
}
|
@ -47,15 +47,19 @@ public static OutputStream createFilepartOutputStream(final Path file) throws IO
|
||||
Files.deleteIfExists(file);
|
||||
Files.createDirectories(file.getParent());
|
||||
|
||||
AtomicFileHelper.move(partFile, file);
|
||||
});
|
||||
}
|
||||
|
||||
public static void move(Path from, Path to) throws IOException {
|
||||
try {
|
||||
Files.move(partFile, file, StandardCopyOption.ATOMIC_MOVE);
|
||||
Files.move(from, to, StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (FileNotFoundException | NoSuchFileException ignore) {
|
||||
} catch (IOException ex) {
|
||||
try {
|
||||
Files.move(partFile, file);
|
||||
Files.move(from, to);
|
||||
} catch (FileNotFoundException | NoSuchFileException ignore) {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static Path getPartFile(Path file) {
|
||||
|
@ -130,7 +130,7 @@ public static void writeVector4f(ConfigurationNode vectorNode, Vector4f v) throw
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an color-integer. The value can be a normal integer, an integer in String-Format, or a string in hexadecimal format prefixed with # (css-style: e.g. #f16 becomes #ff1166).
|
||||
* Returns a color-integer. The value can be a normal integer, an integer in String-Format, or a string in hexadecimal format prefixed with # (css-style: e.g. #f16 becomes #ff1166).
|
||||
* @param node The Configuration Node with the value
|
||||
* @return The parsed Integer
|
||||
* @throws NumberFormatException If the value is not formatted correctly or if there is no value present.
|
||||
@ -145,7 +145,16 @@ public static int readColorInt(ConfigurationNode node) throws NumberFormatExcept
|
||||
}
|
||||
|
||||
String val = value.toString();
|
||||
return parseColorFromString(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color-integer. The value can be an integer in String-Format or a string in hexadecimal format prefixed with # (css-style: e.g. #f16 becomes #ff1166).
|
||||
* @param val The String to parse
|
||||
* @return The parsed Integer
|
||||
* @throws NumberFormatException If the value is not formatted correctly or if there is no value present.
|
||||
*/
|
||||
public static int parseColorFromString(String val) {
|
||||
if (val.charAt(0) == '#') {
|
||||
val = val.substring(1);
|
||||
if (val.length() == 3) val = "f" + val;
|
||||
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MappableFunction<T, R> extends Function<T, R> {
|
||||
|
||||
default <S> MappableFunction<T, S> map(Function<R, S> mapping) {
|
||||
return t -> mapping.apply(this.apply(t));
|
||||
}
|
||||
|
||||
default <S> MappableFunction<T, S> mapNullable(Function<R, S> mapping) {
|
||||
return t -> {
|
||||
R r = this.apply(t);
|
||||
if (r == null) return null;
|
||||
return mapping.apply(r);
|
||||
};
|
||||
}
|
||||
|
||||
static <T, R> MappableFunction<T, R> of(Function<T, R> function) {
|
||||
return function::apply;
|
||||
}
|
||||
|
||||
}
|
@ -38,12 +38,10 @@
|
||||
*/
|
||||
public interface World {
|
||||
|
||||
String getName();
|
||||
|
||||
UUID getUUID();
|
||||
|
||||
Path getSaveFolder();
|
||||
|
||||
String getName();
|
||||
|
||||
int getSkyLight();
|
||||
|
||||
Vector3i getSpawnPoint();
|
||||
|
@ -21,3 +21,9 @@ tasks.register("test") {
|
||||
dependsOn(it.task(":test"))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("spotlessApply") {
|
||||
gradle.includedBuilds.forEach {
|
||||
dependsOn(it.task(":spotlessApply"))
|
||||
}
|
||||
}
|
||||
|
@ -26,38 +26,48 @@
|
||||
|
||||
import de.bluecolored.bluemap.common.BlueMapService;
|
||||
import de.bluecolored.bluemap.common.MissingResourcesException;
|
||||
import de.bluecolored.bluemap.common.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.common.config.MapConfig;
|
||||
import de.bluecolored.bluemap.common.config.WebserverConfig;
|
||||
import de.bluecolored.bluemap.common.plugin.RegionFileWatchService;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Player;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerWorld;
|
||||
import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask;
|
||||
import de.bluecolored.bluemap.common.rendermanager.RenderManager;
|
||||
import de.bluecolored.bluemap.common.rendermanager.RenderTask;
|
||||
import de.bluecolored.bluemap.common.web.FileRequestHandler;
|
||||
import de.bluecolored.bluemap.common.web.MapStorageRequestHandler;
|
||||
import de.bluecolored.bluemap.common.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.common.webserver.WebServer;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.core.config.old.WebServerConfig;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.logger.LoggerLogger;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
import de.bluecolored.bluemap.core.metrics.Metrics;
|
||||
import de.bluecolored.bluemap.core.storage.Storage;
|
||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.core.webserver.WebServer;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.apache.commons.lang3.time.DurationFormatUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BlueMapCLI {
|
||||
public class BlueMapCLI implements ServerInterface {
|
||||
|
||||
private MinecraftVersion minecraftVersion = MinecraftVersion.LATEST_SUPPORTED;
|
||||
private Path configFolder;
|
||||
|
||||
public void renderMaps(BlueMapService blueMap, boolean watch, boolean forceRender, boolean forceGenerateWebapp) throws ConfigurationException, IOException, InterruptedException {
|
||||
|
||||
//metrics report
|
||||
if (blueMap.getCoreConfig().isMetricsEnabled()) Metrics.sendReportAsync("cli");
|
||||
if (blueMap.getConfigs().getCoreConfig().isMetrics()) Metrics.sendReportAsync("cli");
|
||||
|
||||
blueMap.createOrUpdateWebApp(forceGenerateWebapp);
|
||||
blueMap.updateWebAppSettings();
|
||||
@ -97,7 +107,7 @@ public void renderMaps(BlueMapService blueMap, boolean watch, boolean forceRende
|
||||
Logger.global.logInfo("Start updating " + maps.size() + " maps (" + totalRegions + " regions, ~" + totalRegions * 1024L + " chunks)...");
|
||||
|
||||
// start rendering
|
||||
renderManager.start(blueMap.getCoreConfig().getRenderThreadCount());
|
||||
renderManager.start(blueMap.getConfigs().getCoreConfig().getRenderThreadCount());
|
||||
|
||||
Timer timer = new Timer("BlueMap-CLI-Timer", true);
|
||||
TimerTask updateInfoTask = new TimerTask() {
|
||||
@ -171,13 +181,21 @@ public void run() {
|
||||
public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOException, ConfigurationException, InterruptedException {
|
||||
Logger.global.logInfo("Starting webserver ...");
|
||||
|
||||
WebServerConfig config = blueMap.getWebServerConfig();
|
||||
FileUtils.mkDirs(config.getWebRoot());
|
||||
HttpRequestHandler requestHandler = new FileRequestHandler(config.getWebRoot().toPath(), "BlueMap v" + BlueMap.VERSION);
|
||||
WebserverConfig config = blueMap.getConfigs().getWebserverConfig();
|
||||
FileUtils.mkDirs(config.getWebroot().toFile());
|
||||
HttpRequestHandler requestHandler = new FileRequestHandler(config.getWebroot(), "BlueMap v" + BlueMap.VERSION);
|
||||
|
||||
try {
|
||||
//use map-storage to provide map-tiles
|
||||
Map<String, Storage> mapStorages = blueMap.getMapStorages();
|
||||
Map<String, MapConfig> mapConfigs = blueMap.getConfigs().getMapConfigs();
|
||||
Map<String, Storage> mapStorages = new HashMap<>();
|
||||
for (var entry : mapConfigs.entrySet()) {
|
||||
mapStorages.put(
|
||||
entry.getKey(),
|
||||
blueMap.getStorage(entry.getValue().getStorage())
|
||||
);
|
||||
}
|
||||
|
||||
requestHandler = new MapStorageRequestHandler(mapStorages::get, requestHandler);
|
||||
} catch (ConfigurationException ex) {
|
||||
Logger.global.logWarning(ex.getFormattedExplanation());
|
||||
@ -187,15 +205,51 @@ public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOExc
|
||||
}
|
||||
|
||||
WebServer webServer = new WebServer(
|
||||
config.getWebserverBindAddress(),
|
||||
config.getWebserverPort(),
|
||||
config.getWebserverMaxConnections(),
|
||||
config.resolveIp(),
|
||||
config.getPort(),
|
||||
config.getMaxConnectionCount(),
|
||||
requestHandler,
|
||||
verbose
|
||||
);
|
||||
webServer.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftVersion getMinecraftVersion() {
|
||||
return minecraftVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerListener(ServerEventListener listener) {}
|
||||
|
||||
@Override
|
||||
public void unregisterAllListeners() {}
|
||||
|
||||
@Override
|
||||
public Optional<ServerWorld> getWorld(Path worldFolder) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ServerWorld> getLoadedWorlds() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getConfigFolder() {
|
||||
return configFolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Player> getOnlinePlayers() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Player> getPlayer(UUID uuid) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
|
||||
@ -217,18 +271,17 @@ public static void main(String[] args) {
|
||||
}
|
||||
|
||||
//config folder
|
||||
File configFolder = new File(".");
|
||||
cli.configFolder = Path.of(".");
|
||||
if (cmd.hasOption("c")) {
|
||||
configFolder = new File(cmd.getOptionValue("c"));
|
||||
FileUtils.mkDirs(configFolder);
|
||||
cli.configFolder = Path.of(cmd.getOptionValue("c"));
|
||||
FileUtils.mkDirs(cli.configFolder.toFile());
|
||||
}
|
||||
|
||||
//minecraft version
|
||||
MinecraftVersion version = MinecraftVersion.LATEST_SUPPORTED;
|
||||
if (cmd.hasOption("v")) {
|
||||
String versionString = cmd.getOptionValue("v");
|
||||
try {
|
||||
version = MinecraftVersion.of(versionString);
|
||||
cli.minecraftVersion = MinecraftVersion.of(versionString);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Logger.global.logWarning("Could not determine a version from the provided version-string: '" + versionString + "'");
|
||||
System.exit(1);
|
||||
@ -236,7 +289,7 @@ public static void main(String[] args) {
|
||||
}
|
||||
}
|
||||
|
||||
blueMap = new BlueMapService(version, configFolder);
|
||||
blueMap = new BlueMapService(cli);
|
||||
boolean noActions = true;
|
||||
|
||||
if (cmd.hasOption("w")) {
|
||||
@ -266,23 +319,12 @@ public static void main(String[] args) {
|
||||
|
||||
// if nothing has been defined to do
|
||||
if (noActions) {
|
||||
|
||||
if (
|
||||
!blueMap.getCoreConfigFile().exists() ||
|
||||
!blueMap.getRenderConfigFile().exists() ||
|
||||
!blueMap.getWebServerConfigFile().exists()
|
||||
) {
|
||||
Logger.global.logInfo("Generating default config files for you, here: " + configFolder.getCanonicalPath() + "\n");
|
||||
if (!Files.exists(blueMap.getConfigs().getConfigManager().findConfigPath(Path.of("core")))) {
|
||||
Logger.global.logInfo("Generating default config files for you, here: " + cli.configFolder.toAbsolutePath().normalize() + "\n");
|
||||
}
|
||||
|
||||
//generate all configs
|
||||
blueMap.getCoreConfig();
|
||||
blueMap.getRenderConfig();
|
||||
blueMap.getWebServerConfig();
|
||||
blueMap.getMapStorages();
|
||||
|
||||
//create resourcepacks folder
|
||||
FileUtils.mkDirs(new File(configFolder, "resourcepacks"));
|
||||
FileUtils.mkDirs(cli.configFolder.resolve( "resourcepacks").toFile());
|
||||
|
||||
//print help
|
||||
BlueMapCLI.printHelp();
|
||||
@ -292,7 +334,7 @@ public static void main(String[] args) {
|
||||
} catch (MissingResourcesException e) {
|
||||
Logger.global.logWarning("BlueMap is missing important resources!");
|
||||
Logger.global.logWarning("You must accept the required file download in order for BlueMap to work!");
|
||||
try { if (blueMap != null) Logger.global.logWarning("Please check: " + blueMap.getCoreConfigFile().getCanonicalPath()); } catch (IOException ignored) {}
|
||||
if (blueMap != null) Logger.global.logWarning("Please check: " + blueMap.getConfigs().getConfigManager().findConfigPath(Path.of("core")).toAbsolutePath().normalize());
|
||||
System.exit(2);
|
||||
} catch (ParseException e) {
|
||||
Logger.global.logError("Failed to parse provided arguments!", e);
|
||||
@ -397,5 +439,4 @@ private static void printHelp() {
|
||||
|
||||
formatter.printHelp(command + " [options]", "\nOptions:", createOptions(), "\n" + footer.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
accept-download: false
|
||||
renderThreadCount: 0
|
||||
metrics: true
|
||||
data: "data"
|
@ -1,30 +0,0 @@
|
||||
## ##
|
||||
## BlueMap ##
|
||||
## Core-Config ##
|
||||
## ##
|
||||
|
||||
# By changing the setting (accept-download) below to TRUE you are indicating that you have accepted mojang's EULA (https://account.mojang.com/documents/minecraft_eula),
|
||||
# you confirm that you own a license to Minecraft (Java Edition)
|
||||
# and you agree that BlueMap will download and use a minecraft-client file (depending on the minecraft-version) from mojangs servers (https://launcher.mojang.com/) for you.
|
||||
# This file contains resources that belong to mojang and you must not redistribute it or do anything else that is not compliant with mojang's EULA.
|
||||
# BlueMap uses resources in this file to generate the 3D-Models used for the map and texture them. (BlueMap will not work without those resources.)
|
||||
# %datetime-iso%
|
||||
accept-download: false
|
||||
|
||||
# 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 0
|
||||
renderThreadCount: 0
|
||||
|
||||
# If this is true, BlueMap might send really basic metrics reports containg 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":"bukkit","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 "data"
|
||||
data: "data"
|
@ -1,4 +0,0 @@
|
||||
webroot: "web"
|
||||
useCookies: true
|
||||
enableFreeFlight: true
|
||||
maps: []
|
@ -1,144 +0,0 @@
|
||||
## ##
|
||||
## BlueMap ##
|
||||
## Render-Config ##
|
||||
## ##
|
||||
|
||||
# The folder (webroot) where the map-data and web-application files will be saved.
|
||||
# Default is "web"
|
||||
webroot: "web"
|
||||
|
||||
# If the web-application should use cookies to save the configurations of a user.
|
||||
# Default is true
|
||||
useCookies: true
|
||||
|
||||
# If the free-flight-mode in the web-application is enabled or not.
|
||||
# Default is true
|
||||
enableFreeFlight: true
|
||||
|
||||
# This is an array with multiple configured maps.
|
||||
# You can define multiple maps, for different worlds with different render-settings here
|
||||
maps: [
|
||||
|
||||
{
|
||||
# The id of this map
|
||||
# Should only contain word-charactes: [a-zA-Z0-9_]
|
||||
# Changing this value breaks your existing renders.
|
||||
id: "world"
|
||||
|
||||
# The name of this map
|
||||
# This defines the display name of this map, you can change this at any time.
|
||||
# Default is the id of this map
|
||||
name: "World"
|
||||
|
||||
# The path to the save-folder of the world to render.
|
||||
world: "world"
|
||||
|
||||
# The position on the world where the map will be centered if you open it.
|
||||
# You can change this at any time.
|
||||
# This defaults to the world-spawn if you don't set it.
|
||||
#startPos: [500, -820]
|
||||
|
||||
# The color of thy sky as a hex-color
|
||||
# You can change this at any time.
|
||||
# Default is "#7dabff"
|
||||
skyColor: "#7dabff"
|
||||
|
||||
# Defines the ambient light-strength that every block is recieving, regardless of the sunlight/blocklight.
|
||||
# 0 is no ambient light, 1 is fully lighted.
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Default is 0
|
||||
ambientLight: 0.1
|
||||
|
||||
# Defines the skylight level that the sky of the world is emitting.
|
||||
# This should always be equivalent to the maximum ingame sky-light for that world!
|
||||
# If this is a normal overworld dimension, set this to 15 (max).
|
||||
# If this is a normal nether or end dimension, set this to 0 (min).
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Default is 15
|
||||
worldSkyLight: 15
|
||||
|
||||
# BlueMap tries to omit all blocks that are below this Y-level and are not visible from above-ground.
|
||||
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
|
||||
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Set to a very high value to remove caves everywhere (e.g. 10000)
|
||||
# Set to a very low value to remove nothing and render all caves (e.g. -10000)
|
||||
# Default is 55 (slightly below water-level)
|
||||
removeCavesBelowY: 55
|
||||
|
||||
# With this value set to true, BlueMap uses the block-light value instead of the sky-light value to "detect caves".
|
||||
# (See: removeCavesBelowY)
|
||||
# Default is false
|
||||
caveDetectionUsesBlockLight: false
|
||||
|
||||
# With the below values you can limit the map-render.
|
||||
# This can be used to ignore the nethers ceiling or render only a certain part of a world.
|
||||
# Changing this values might require a re-render of the map, already rendered tiles outside the limits will not be deleted.
|
||||
# Default is no min or max value (= infinite bounds)
|
||||
#minX: -4000
|
||||
#maxX: 4000
|
||||
#minZ: -4000
|
||||
#maxZ: 4000
|
||||
#minY: 50
|
||||
#maxY: 126
|
||||
|
||||
# Using this, BlueMap pretends that every Block out of the defined render-bounds is AIR,
|
||||
# this means you can see the blocks where the world is cut (instead of having a see-through/xray view).
|
||||
# This has only an effect if you set some render-bounds above.
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Default is true
|
||||
renderEdges: true
|
||||
|
||||
# This defines the storage-config that will be used to save this map.
|
||||
# You can find your storage configs next to this config file in the 'storages'-folder.
|
||||
# Default is "file"
|
||||
storage: "file"
|
||||
|
||||
# Normally BlueMap detects if a chunk has not yet generated it's light-data and omits rendering those chunks.
|
||||
# If this is set to true BlueMap will render Chunks even if there is no light-data!
|
||||
# This can be usefull for example if some mod prevents light-data from being saved correctly.
|
||||
# However, this also has a few drawbacks:
|
||||
# - For those chunks, every block will always be fully lit
|
||||
# - Night-mode might not work correctly
|
||||
# - Caves will always be rendered (ignoring the 'renderCaves' setting)
|
||||
# Default is false
|
||||
ignoreMissingLightData: false
|
||||
}
|
||||
|
||||
# Here another example for the End-Map
|
||||
# Things we don't want to change from default we can just omit
|
||||
{
|
||||
id: "end"
|
||||
name: "End"
|
||||
world: "world/DIM1"
|
||||
|
||||
# We dont want a blue sky in the end
|
||||
skyColor: "#080010"
|
||||
|
||||
# In the end is no sky-light, so we need to set this or we won't see anything.
|
||||
removeCavesBelowY: -10000
|
||||
worldSkyLight: 0
|
||||
|
||||
# Same here, we don't want a dark map. But not completely lighted, so we see the effect of e.g torches.
|
||||
ambientLight: 0.6
|
||||
}
|
||||
|
||||
# Here another example for the Nether-Map
|
||||
{
|
||||
id: "nether"
|
||||
name: "Nether"
|
||||
world: "world/DIM-1"
|
||||
|
||||
skyColor: "#290000"
|
||||
worldSkyLight: 0
|
||||
|
||||
removeCavesBelowY: -10000
|
||||
ambientLight: 0.6
|
||||
|
||||
# We slice the whole world at y:90 so every block above 90 will be air.
|
||||
# This way we don't render the nethers ceiling.
|
||||
maxY: 90
|
||||
renderEdges: true
|
||||
}
|
||||
|
||||
]
|
@ -1,4 +0,0 @@
|
||||
enabled: true
|
||||
webroot: "web"
|
||||
port: 8100
|
||||
maxConnectionCount: 100
|
@ -1,28 +0,0 @@
|
||||
## ##
|
||||
## BlueMap ##
|
||||
## Webserver-Config ##
|
||||
## ##
|
||||
|
||||
# The webroot that the server will host to the web.
|
||||
# Usually this should be set to the same directory like in the render.conf!
|
||||
# Default is "web"
|
||||
webroot: "web"
|
||||
|
||||
# The IP-Address that the webserver binds to.
|
||||
# Use "0.0.0.0" to bind to all available local addresses.
|
||||
# If you only want to access it locally use "localhost".
|
||||
#
|
||||
# LEAVE THIS SETTING AS IT IS if you don't have a good reason to change it!
|
||||
# The default setting (0.0.0.0) is working on almost all servers.
|
||||
#
|
||||
# Default is "0.0.0.0"
|
||||
#ip: "localhost"
|
||||
#ip: "123.45.6.78"
|
||||
|
||||
# The port that the webserver listens to.
|
||||
# Default is 8100
|
||||
port: 8100
|
||||
|
||||
# Max number of simultaneous connections that the webserver allows
|
||||
# Default is 100
|
||||
maxConnectionCount: 100
|
@ -39,9 +39,9 @@
|
||||
|
||||
public class FabricCommandSource implements CommandSource {
|
||||
|
||||
private FabricMod mod;
|
||||
private Plugin plugin;
|
||||
private ServerCommandSource delegate;
|
||||
private final FabricMod mod;
|
||||
private final Plugin plugin;
|
||||
private final ServerCommandSource delegate;
|
||||
|
||||
public FabricCommandSource(FabricMod mod, Plugin plugin, ServerCommandSource delegate) {
|
||||
this.mod = mod;
|
||||
@ -77,10 +77,9 @@ public Optional<Vector3d> getPosition() {
|
||||
@Override
|
||||
public Optional<World> getWorld() {
|
||||
try {
|
||||
ServerWorld world = delegate.getWorld();
|
||||
if (world != null) {
|
||||
return Optional.ofNullable(plugin.getWorld(mod.getUUIDForWorld(world)));
|
||||
}
|
||||
var serverWorld = mod.getWorld(delegate.getWorld());
|
||||
String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder());
|
||||
return Optional.ofNullable(plugin.getWorlds().get(worldId));
|
||||
} catch (IOException ignore) {}
|
||||
|
||||
return Optional.empty();
|
||||
|
@ -31,6 +31,7 @@
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Player;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerWorld;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
@ -43,29 +44,25 @@
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class FabricMod implements ModInitializer, ServerInterface {
|
||||
|
||||
private Plugin pluginInstance = null;
|
||||
private final Plugin pluginInstance;
|
||||
private MinecraftServer serverInstance = null;
|
||||
|
||||
private Map<File, UUID> worldUUIDs;
|
||||
private FabricEventForwarder eventForwarder;
|
||||
private final FabricEventForwarder eventForwarder;
|
||||
|
||||
private LoadingCache<ServerWorld, UUID> worldUuidCache;
|
||||
private final LoadingCache<net.minecraft.server.world.ServerWorld, ServerWorld> worlds;
|
||||
|
||||
private int playerUpdateIndex = 0;
|
||||
private Map<UUID, Player> onlinePlayerMap;
|
||||
private List<FabricPlayer> onlinePlayerList;
|
||||
private final Map<UUID, Player> onlinePlayerMap;
|
||||
private final List<FabricPlayer> onlinePlayerList;
|
||||
|
||||
public FabricMod() {
|
||||
Logger.global = new Log4jLogger(LogManager.getLogger(Plugin.PLUGIN_NAME));
|
||||
@ -73,24 +70,25 @@ public FabricMod() {
|
||||
this.onlinePlayerMap = new ConcurrentHashMap<>();
|
||||
this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
pluginInstance = new Plugin(new MinecraftVersion(1, 15, 2), "fabric-1.15.2", this);
|
||||
pluginInstance = new Plugin("fabric-1.15.2", this);
|
||||
|
||||
this.worldUUIDs = new ConcurrentHashMap<>();
|
||||
this.eventForwarder = new FabricEventForwarder(this);
|
||||
this.worldUuidCache = Caffeine.newBuilder()
|
||||
this.worlds = Caffeine.newBuilder()
|
||||
.executor(BlueMap.THREAD_POOL)
|
||||
.weakKeys()
|
||||
.maximumSize(1000)
|
||||
.build(this::loadUUIDForWorld);
|
||||
.build(FabricWorld::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
|
||||
//register commands
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
|
||||
new Commands<>(pluginInstance, dispatcher, fabricSource -> new FabricCommandSource(this, pluginInstance, fabricSource));
|
||||
});
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) ->
|
||||
new Commands<>(pluginInstance, dispatcher, fabricSource ->
|
||||
new FabricCommandSource(this, pluginInstance, fabricSource)
|
||||
)
|
||||
);
|
||||
|
||||
ServerLifecycleEvents.SERVER_STARTED.register((MinecraftServer server) -> {
|
||||
this.serverInstance = server;
|
||||
@ -121,6 +119,11 @@ public void onInitialize() {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftVersion getMinecraftVersion() {
|
||||
return new MinecraftVersion(1, 15, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerListener(ServerEventListener listener) {
|
||||
eventForwarder.addEventListener(listener);
|
||||
@ -132,73 +135,27 @@ public void unregisterAllListeners() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUIDForWorld(File worldFolder) throws IOException {
|
||||
worldFolder = worldFolder.getCanonicalFile();
|
||||
|
||||
UUID uuid = worldUUIDs.get(worldFolder);
|
||||
if (uuid == null) {
|
||||
uuid = UUID.randomUUID();
|
||||
worldUUIDs.put(worldFolder, uuid);
|
||||
public Collection<ServerWorld> getLoadedWorlds() {
|
||||
Collection<ServerWorld> loadedWorlds = new ArrayList<>(3);
|
||||
for (net.minecraft.server.world.ServerWorld serverWorld : serverInstance.getWorlds()) {
|
||||
loadedWorlds.add(worlds.get(serverWorld));
|
||||
}
|
||||
return loadedWorlds;
|
||||
}
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public UUID getUUIDForWorld(ServerWorld world) throws IOException {
|
||||
try {
|
||||
return worldUuidCache.get(world);
|
||||
} catch (RuntimeException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof IOException) throw (IOException) cause;
|
||||
else throw new IOException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
private UUID loadUUIDForWorld(ServerWorld world) throws IOException {
|
||||
File dimensionDir = world.getDimension().getType().getSaveDirectory(world.getSaveHandler().getWorldDir());
|
||||
return getUUIDForWorld(dimensionDir.getCanonicalFile());
|
||||
public ServerWorld getWorld(net.minecraft.server.world.ServerWorld serverWorld) {
|
||||
return worlds.get(serverWorld);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException {
|
||||
final CompletableFuture<Boolean> taskResult = new CompletableFuture<>();
|
||||
|
||||
serverInstance.execute(() -> {
|
||||
try {
|
||||
for (ServerWorld world : serverInstance.getWorlds()) {
|
||||
if (getUUIDForWorld(world).equals(worldUUID)) {
|
||||
world.save(null, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
taskResult.complete(true);
|
||||
} catch (Exception e) {
|
||||
taskResult.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
return taskResult.get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IOException(e);
|
||||
} catch (ExecutionException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof IOException) throw (IOException) t;
|
||||
if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t;
|
||||
throw new IOException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getConfigFolder() {
|
||||
return new File("config/bluemap");
|
||||
public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity playerInstance) {
|
||||
if (this.serverInstance != server) return;
|
||||
|
||||
FabricPlayer player = new FabricPlayer(this, playerInstance.getUuid());
|
||||
FabricPlayer player = new FabricPlayer(playerInstance.getUuid(), this, pluginInstance.getBlueMap());
|
||||
onlinePlayerMap.put(player.getUuid(), player);
|
||||
onlinePlayerList.add(player);
|
||||
}
|
||||
|
@ -24,13 +24,8 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.fabric;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
|
||||
import de.bluecolored.bluemap.common.BlueMapService;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Player;
|
||||
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||
@ -41,9 +36,12 @@
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.GameMode;
|
||||
|
||||
public class FabricPlayer implements Player {
|
||||
import java.io.IOException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
private static final UUID UNKNOWN_WORLD_UUID = UUID.randomUUID();
|
||||
public class FabricPlayer implements Player {
|
||||
|
||||
private static final Map<GameMode, Gamemode> GAMEMODE_MAP = new EnumMap<>(GameMode.class);
|
||||
static {
|
||||
@ -54,20 +52,22 @@ public class FabricPlayer implements Player {
|
||||
GAMEMODE_MAP.put(GameMode.NOT_SET, Gamemode.SURVIVAL);
|
||||
}
|
||||
|
||||
private UUID uuid;
|
||||
private final UUID uuid;
|
||||
private Text name;
|
||||
private UUID world;
|
||||
private String world;
|
||||
private Vector3d position;
|
||||
private boolean online;
|
||||
private boolean sneaking;
|
||||
private boolean invisible;
|
||||
private Gamemode gamemode;
|
||||
|
||||
private FabricMod mod;
|
||||
private final FabricMod mod;
|
||||
private final BlueMapService blueMap;
|
||||
|
||||
public FabricPlayer(FabricMod mod, UUID playerUuid) {
|
||||
public FabricPlayer(UUID playerUuid, FabricMod mod, BlueMapService blueMap) {
|
||||
this.uuid = playerUuid;
|
||||
this.mod = mod;
|
||||
this.blueMap = blueMap;
|
||||
|
||||
update();
|
||||
}
|
||||
@ -83,7 +83,7 @@ public Text getName() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getWorld() {
|
||||
public String getWorld() {
|
||||
return this.world;
|
||||
}
|
||||
|
||||
@ -142,9 +142,10 @@ public void update() {
|
||||
this.sneaking = player.isSneaking();
|
||||
|
||||
try {
|
||||
this.world = mod.getUUIDForWorld(player.getServerWorld());
|
||||
var world = mod.getWorld(player.getServerWorld());
|
||||
this.world = blueMap.getWorldId(world.getSaveFolder());
|
||||
} catch (IOException e) {
|
||||
this.world = UNKNOWN_WORLD_UUID;
|
||||
this.world = "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.fabric;
|
||||
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Dimension;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerWorld;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class FabricWorld implements ServerWorld {
|
||||
|
||||
private final WeakReference<net.minecraft.server.world.ServerWorld> delegate;
|
||||
private final Path saveFolder;
|
||||
|
||||
public FabricWorld(net.minecraft.server.world.ServerWorld delegate) {
|
||||
this.delegate = new WeakReference<>(delegate);
|
||||
this.saveFolder = delegate.getDimension().getType()
|
||||
.getSaveDirectory(delegate.getSaveHandler().getWorldDir()).toPath()
|
||||
.toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getDimension() {
|
||||
net.minecraft.server.world.ServerWorld world = delegate.get();
|
||||
if (world != null) {
|
||||
if (world.getDimension().getType().equals(DimensionType.THE_NETHER)) return Dimension.NETHER;
|
||||
if (world.getDimension().getType().equals(DimensionType.THE_END)) return Dimension.END;
|
||||
if (world.getDimension().getType().equals(DimensionType.OVERWORLD)) return Dimension.OVERWORLD;
|
||||
}
|
||||
|
||||
return ServerWorld.super.getDimension();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persistWorldChanges() throws IOException {
|
||||
net.minecraft.server.world.ServerWorld world = delegate.get();
|
||||
if (world == null) return false;
|
||||
|
||||
var taskResult = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
world.save(null, true, false);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, world.getServer());
|
||||
|
||||
try {
|
||||
return taskResult.get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IOException(e);
|
||||
} catch (ExecutionException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof IOException) throw (IOException) t;
|
||||
if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t;
|
||||
throw new IOException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getSaveFolder() {
|
||||
return this.saveFolder;
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
accept-download: false
|
||||
renderThreadCount: -2
|
||||
metrics: true
|
||||
data: "bluemap"
|
@ -1,7 +0,0 @@
|
||||
liveUpdates: true
|
||||
skinDownload: true
|
||||
hiddenGameModes: []
|
||||
hideInvisible: true
|
||||
hideSneaking: false
|
||||
playerRenderLimit: -1
|
||||
fullUpdateInterval: 1440
|
@ -1,39 +0,0 @@
|
||||
## ##
|
||||
## BlueMap ##
|
||||
## Plugin-Config ##
|
||||
## ##
|
||||
|
||||
# If the server should send live-updates and player-positions.
|
||||
# This only works if the integrated webserver is enabled.
|
||||
# Default is true
|
||||
liveUpdates: 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
|
||||
skinDownload: true
|
||||
|
||||
# A list of gamemodes that will prevent a player from appearing on the map.
|
||||
# Possible values are: survival, creative, spectator, adventure
|
||||
hiddenGameModes: [
|
||||
"spectator"
|
||||
]
|
||||
|
||||
# If this is true, players that have an invisibility (potion-)effect will be hidden on the map.
|
||||
# Default is true
|
||||
hideInvisible: true
|
||||
|
||||
# If this is true, players that are sneaking will be hidden on the map.
|
||||
# Default is false
|
||||
hideSneaking: false
|
||||
|
||||
# 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
|
||||
# Setting this to 0 or -1 will disable this feature -> bluemap will not pause rendering
|
||||
# Default is -1
|
||||
playerRenderLimit: -1
|
||||
|
||||
# The interval in minutes in which a full map-update will be triggered.
|
||||
# This is additionally!! to the normal map-update process (in case that fails to detect any file-changes).
|
||||
# Default is 1440 (24 hours)
|
||||
fullUpdateInterval: 1440
|
@ -1,3 +0,0 @@
|
||||
webroot: "bluemap/web"
|
||||
useCookies: true
|
||||
maps: []
|
@ -1,147 +0,0 @@
|
||||
## ##
|
||||
## BlueMap ##
|
||||
## Render-Config ##
|
||||
## ##
|
||||
|
||||
# The folder (webroot) where the map-data and web-application files will be saved.
|
||||
# Default is "bluemap/web"
|
||||
webroot: "bluemap/web"
|
||||
|
||||
# If the web-application should use cookies to save the configurations of a user.
|
||||
# Default is true
|
||||
useCookies: true
|
||||
|
||||
# If the free-flight-mode in the web-application is enabled or not.
|
||||
# Default is true
|
||||
enableFreeFlight: true
|
||||
|
||||
# This is an array with multiple configured maps.
|
||||
# You can define multiple maps, for different worlds with different render-settings here
|
||||
maps: [
|
||||
|
||||
{
|
||||
# The id of this map
|
||||
# Should only contain word-charactes: [a-zA-Z0-9_]
|
||||
# Changing this value breaks your existing renders.
|
||||
id: "world"
|
||||
|
||||
# The name of this map
|
||||
# This defines the display name of this map, you can change this at any time.
|
||||
# Default is the id of this map
|
||||
name: "World"
|
||||
|
||||
# The path to the save-folder of the world to render.
|
||||
world: "world"
|
||||
|
||||
# The position on the world where the map will be centered if you open it.
|
||||
# You can change this at any time.
|
||||
# This defaults to the world-spawn if you don't set it.
|
||||
#startPos: [500, -820]
|
||||
|
||||
# The color of thy sky as a hex-color
|
||||
# You can change this at any time.
|
||||
# Default is "#7dabff"
|
||||
skyColor: "#7dabff"
|
||||
|
||||
# Defines the ambient light-strength that every block is recieving, regardless of the sunlight/blocklight.
|
||||
# 0 is no ambient light, 1 is fully lighted.
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Default is 0
|
||||
ambientLight: 0
|
||||
|
||||
# Defines the skylight level that the sky of the world is emitting.
|
||||
# This should always be equivalent to the maximum ingame sky-light for that world!
|
||||
# If this is a normal overworld dimension, set this to 15 (max).
|
||||
# If this is a normal nether or end dimension, set this to 0 (min).
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Default is 15
|
||||
worldSkyLight: 15
|
||||
|
||||
# BlueMap tries to omit all blocks that are below this Y-level and are not visible from above-ground.
|
||||
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
|
||||
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Set to a very high value to remove caves everywhere (e.g. 10000)
|
||||
# Set to a very low value to remove nothing and render all caves (e.g. -10000)
|
||||
# Default is 55 (slightly below water-level)
|
||||
removeCavesBelowY: 55
|
||||
|
||||
# With this value set to true, BlueMap uses the block-light value instead of the sky-light value to "detect caves".
|
||||
# (See: removeCavesBelowY)
|
||||
# Default is false
|
||||
caveDetectionUsesBlockLight: false
|
||||
|
||||
# With the below values you can limit the map-render.
|
||||
# This can be used to ignore the nethers ceiling or render only a certain part of a world.
|
||||
# Changing this values might require a re-render of the map, already rendered tiles outside the limits will not be deleted.
|
||||
# Default is no min or max value (= infinite bounds)
|
||||
#minX: -4000
|
||||
#maxX: 4000
|
||||
#minZ: -4000
|
||||
#maxZ: 4000
|
||||
#minY: 50
|
||||
#maxY: 126
|
||||
|
||||
# Using this, BlueMap pretends that every Block out of the defined render-bounds is AIR,
|
||||
# this means you can see the blocks where the world is cut (instead of having a see-through/xray view).
|
||||
# This has only an effect if you set some render-bounds above.
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Default is true
|
||||
renderEdges: true
|
||||
|
||||
# With this set to true, the generated files for this world are compressed using gzip to save A LOT of space.
|
||||
# Files will be only 5% as big with compression!
|
||||
# Note: If you are using NGINX or Apache to host your map, you can configure them to serve the compressed files directly.
|
||||
# This is much better than disabling the compression.
|
||||
# Changing this value requires a re-render of the map.
|
||||
# Default is true
|
||||
useCompression: true
|
||||
|
||||
# Normally BlueMap detects if a chunk has not yet generated it's light-data and omits rendering those chunks.
|
||||
# If this is set to true BlueMap will render Chunks even if there is no light-data!
|
||||
# This can be usefull for example if some mod prevents light-data from being saved correctly.
|
||||
# However, this also has a few drawbacks:
|
||||
# - For those chunks, every block will always be fully lit
|
||||
# - Night-mode might not work correctly
|
||||
# - Caves will always be rendered (ignoring the 'renderCaves' setting)
|
||||
# Default is false
|
||||
ignoreMissingLightData: false
|
||||
}
|
||||
|
||||
# Here another example for the End-Map
|
||||
# Things we don't want to change from default we can just omit
|
||||
{
|
||||
id: "end"
|
||||
name: "End"
|
||||
world: "world/DIM1"
|
||||
|
||||
# We dont want a blue sky in the end
|
||||
skyColor: "#080010"
|
||||
|
||||
# In the end is no sky-light, so we need to set this or we won't see anything.
|
||||
removeCavesBelowY: -10000
|
||||
worldSkyLight: 0
|
||||
|
||||
# Same here, we don't want a dark map. But not completely lighted, so we see the effect of e.g torches.
|
||||
ambientLight: 0.6
|
||||
}
|
||||
|
||||
# Here another example for the Nether-Map
|
||||
{
|
||||
id: "nether"
|
||||
name: "Nether"
|
||||
world: "world/DIM-1"
|
||||
|
||||
skyColor: "#290000"
|
||||
worldSkyLight: 0
|
||||
|
||||
removeCavesBelowY: -10000
|
||||
ambientLight: 0.6
|
||||
|
||||
# We slice the whole world at y:90 so every block above 90 will be air.
|
||||
# This way we don't render the nethers ceiling.
|
||||
maxY: 90
|
||||
renderEdges: true
|
||||
}
|
||||
|
||||
]
|
@ -1,4 +0,0 @@
|
||||
enabled: true
|
||||
webroot: "bluemap/web"
|
||||
port: 8100
|
||||
maxConnectionCount: 100
|
@ -1,29 +0,0 @@
|
||||
## ##
|
||||
## BlueMap ##
|
||||
## Webserver-Config ##
|
||||
## ##
|
||||
|
||||
# With this setting you can disable the integrated web-server.
|
||||
# This is usefull if you want to only render the map-data for later use, or if you setup your own webserver.
|
||||
# Default is enabled
|
||||
enabled: true
|
||||
|
||||
# The webroot that the server will host to the web.
|
||||
# Usually this should be set to the same directory like in the render.conf!
|
||||
# Default is "bluemap/web"
|
||||
webroot: "bluemap/web"
|
||||
|
||||
# The IP-Adress that the webserver binds to.
|
||||
# Use "0.0.0.0" to bind to all available local adresses.
|
||||
# If you only want to access it locally use "localhost".
|
||||
# Default is "0.0.0.0"
|
||||
#ip: "localhost"
|
||||
#ip: "123.45.6.78"
|
||||
|
||||
# The port that the webserver listenes to.
|
||||
# Default is 8100
|
||||
port: 8100
|
||||
|
||||
# Max number of simultaneous connections that the webserver allows
|
||||
# Default is 100
|
||||
maxConnectionCount: 100
|
@ -39,9 +39,9 @@
|
||||
|
||||
public class FabricCommandSource implements CommandSource {
|
||||
|
||||
private FabricMod mod;
|
||||
private Plugin plugin;
|
||||
private ServerCommandSource delegate;
|
||||
private final FabricMod mod;
|
||||
private final Plugin plugin;
|
||||
private final ServerCommandSource delegate;
|
||||
|
||||
public FabricCommandSource(FabricMod mod, Plugin plugin, ServerCommandSource delegate) {
|
||||
this.mod = mod;
|
||||
@ -77,10 +77,9 @@ public Optional<Vector3d> getPosition() {
|
||||
@Override
|
||||
public Optional<World> getWorld() {
|
||||
try {
|
||||
ServerWorld world = delegate.getWorld();
|
||||
if (world != null) {
|
||||
return Optional.ofNullable(plugin.getWorld(mod.getUUIDForWorld(world)));
|
||||
}
|
||||
var serverWorld = mod.getWorld(delegate.getWorld());
|
||||
String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder());
|
||||
return Optional.ofNullable(plugin.getWorlds().get(worldId));
|
||||
} catch (IOException ignore) {}
|
||||
|
||||
return Optional.empty();
|
||||
|
@ -31,6 +31,7 @@
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Player;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerWorld;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
@ -43,31 +44,25 @@
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.WorldSavePath;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class FabricMod implements ModInitializer, ServerInterface {
|
||||
|
||||
private Plugin pluginInstance = null;
|
||||
private final Plugin pluginInstance;
|
||||
private MinecraftServer serverInstance = null;
|
||||
|
||||
private Map<File, UUID> worldUUIDs;
|
||||
private FabricEventForwarder eventForwarder;
|
||||
private final FabricEventForwarder eventForwarder;
|
||||
|
||||
private LoadingCache<ServerWorld, UUID> worldUuidCache;
|
||||
private final LoadingCache<net.minecraft.server.world.ServerWorld, ServerWorld> worlds;
|
||||
|
||||
private int playerUpdateIndex = 0;
|
||||
private Map<UUID, Player> onlinePlayerMap;
|
||||
private List<FabricPlayer> onlinePlayerList;
|
||||
private final Map<UUID, Player> onlinePlayerMap;
|
||||
private final List<FabricPlayer> onlinePlayerList;
|
||||
|
||||
public FabricMod() {
|
||||
Logger.global = new Log4jLogger(LogManager.getLogger(Plugin.PLUGIN_NAME));
|
||||
@ -75,24 +70,25 @@ public FabricMod() {
|
||||
this.onlinePlayerMap = new ConcurrentHashMap<>();
|
||||
this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
pluginInstance = new Plugin(new MinecraftVersion(1, 16, 1), "fabric-1.16.1", this);
|
||||
pluginInstance = new Plugin("fabric-1.16.1", this);
|
||||
|
||||
this.worldUUIDs = new ConcurrentHashMap<>();
|
||||
this.eventForwarder = new FabricEventForwarder(this);
|
||||
this.worldUuidCache = Caffeine.newBuilder()
|
||||
this.worlds = Caffeine.newBuilder()
|
||||
.executor(BlueMap.THREAD_POOL)
|
||||
.weakKeys()
|
||||
.maximumSize(1000)
|
||||
.build(this::loadUUIDForWorld);
|
||||
.build(FabricWorld::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
|
||||
//register commands
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
|
||||
new Commands<>(pluginInstance, dispatcher, fabricSource -> new FabricCommandSource(this, pluginInstance, fabricSource));
|
||||
});
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) ->
|
||||
new Commands<>(pluginInstance, dispatcher, fabricSource ->
|
||||
new FabricCommandSource(this, pluginInstance, fabricSource)
|
||||
)
|
||||
);
|
||||
|
||||
ServerLifecycleEvents.SERVER_STARTED.register((MinecraftServer server) -> {
|
||||
this.serverInstance = server;
|
||||
@ -123,6 +119,11 @@ public void onInitialize() {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftVersion getMinecraftVersion() {
|
||||
return new MinecraftVersion(1, 16, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerListener(ServerEventListener listener) {
|
||||
eventForwarder.addEventListener(listener);
|
||||
@ -134,76 +135,27 @@ public void unregisterAllListeners() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUIDForWorld(File worldFolder) throws IOException {
|
||||
worldFolder = worldFolder.getCanonicalFile();
|
||||
|
||||
UUID uuid = worldUUIDs.get(worldFolder);
|
||||
if (uuid == null) {
|
||||
uuid = UUID.randomUUID();
|
||||
worldUUIDs.put(worldFolder, uuid);
|
||||
public Collection<ServerWorld> getLoadedWorlds() {
|
||||
Collection<ServerWorld> loadedWorlds = new ArrayList<>(3);
|
||||
for (net.minecraft.server.world.ServerWorld serverWorld : serverInstance.getWorlds()) {
|
||||
loadedWorlds.add(worlds.get(serverWorld));
|
||||
}
|
||||
return loadedWorlds;
|
||||
}
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public UUID getUUIDForWorld(ServerWorld world) throws IOException {
|
||||
try {
|
||||
return worldUuidCache.get(world);
|
||||
} catch (RuntimeException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof IOException) throw (IOException) cause;
|
||||
else throw new IOException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
private UUID loadUUIDForWorld(ServerWorld world) throws IOException {
|
||||
MinecraftServer server = world.getServer();
|
||||
File worldFolder = world.getServer().getRunDirectory().toPath().resolve(server.getSavePath(WorldSavePath.ROOT)).toFile();
|
||||
File dimensionFolder = DimensionType.getSaveDirectory(world.getRegistryKey(), worldFolder);
|
||||
File dimensionDir = dimensionFolder.getCanonicalFile();
|
||||
return getUUIDForWorld(dimensionDir);
|
||||
public ServerWorld getWorld(net.minecraft.server.world.ServerWorld serverWorld) {
|
||||
return worlds.get(serverWorld);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException {
|
||||
final CompletableFuture<Boolean> taskResult = new CompletableFuture<>();
|
||||
|
||||
serverInstance.execute(() -> {
|
||||
try {
|
||||
for (ServerWorld world : serverInstance.getWorlds()) {
|
||||
if (getUUIDForWorld(world).equals(worldUUID)) {
|
||||
world.save(null, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
taskResult.complete(true);
|
||||
} catch (Exception e) {
|
||||
taskResult.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
return taskResult.get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IOException(e);
|
||||
} catch (ExecutionException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof IOException) throw (IOException) t;
|
||||
if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t;
|
||||
throw new IOException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getConfigFolder() {
|
||||
return new File("config/bluemap");
|
||||
public Path getConfigFolder() {
|
||||
return Path.of("config", "bluemap");
|
||||
}
|
||||
|
||||
public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity playerInstance) {
|
||||
if (this.serverInstance != server) return;
|
||||
|
||||
FabricPlayer player = new FabricPlayer(this, playerInstance.getUuid());
|
||||
FabricPlayer player = new FabricPlayer(playerInstance.getUuid(), this, pluginInstance.getBlueMap());
|
||||
onlinePlayerMap.put(player.getUuid(), player);
|
||||
onlinePlayerList.add(player);
|
||||
}
|
||||
|
@ -24,13 +24,8 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.fabric;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
|
||||
import de.bluecolored.bluemap.common.BlueMapService;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Player;
|
||||
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||
@ -41,9 +36,12 @@
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.GameMode;
|
||||
|
||||
public class FabricPlayer implements Player {
|
||||
import java.io.IOException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
private static final UUID UNKNOWN_WORLD_UUID = UUID.randomUUID();
|
||||
public class FabricPlayer implements Player {
|
||||
|
||||
private static final Map<GameMode, Gamemode> GAMEMODE_MAP = new EnumMap<>(GameMode.class);
|
||||
static {
|
||||
@ -54,20 +52,22 @@ public class FabricPlayer implements Player {
|
||||
GAMEMODE_MAP.put(GameMode.NOT_SET, Gamemode.SURVIVAL);
|
||||
}
|
||||
|
||||
private UUID uuid;
|
||||
private final UUID uuid;
|
||||
private Text name;
|
||||
private UUID world;
|
||||
private String world;
|
||||
private Vector3d position;
|
||||
private boolean online;
|
||||
private boolean sneaking;
|
||||
private boolean invisible;
|
||||
private Gamemode gamemode;
|
||||
|
||||
private FabricMod mod;
|
||||
private final FabricMod mod;
|
||||
private final BlueMapService blueMap;
|
||||
|
||||
public FabricPlayer(FabricMod mod, UUID playerUuid) {
|
||||
public FabricPlayer(UUID playerUuid, FabricMod mod, BlueMapService blueMap) {
|
||||
this.uuid = playerUuid;
|
||||
this.mod = mod;
|
||||
this.blueMap = blueMap;
|
||||
|
||||
update();
|
||||
}
|
||||
@ -83,7 +83,7 @@ public Text getName() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getWorld() {
|
||||
public String getWorld() {
|
||||
return this.world;
|
||||
}
|
||||
|
||||
@ -142,9 +142,10 @@ public void update() {
|
||||
this.sneaking = player.isSneaking();
|
||||
|
||||
try {
|
||||
this.world = mod.getUUIDForWorld(player.getServerWorld());
|
||||
var world = mod.getWorld(player.getServerWorld());
|
||||
this.world = blueMap.getWorldId(world.getSaveFolder());
|
||||
} catch (IOException e) {
|
||||
this.world = UNKNOWN_WORLD_UUID;
|
||||
this.world = "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.fabric;
|
||||
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Dimension;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerWorld;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.WorldSavePath;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class FabricWorld implements ServerWorld {
|
||||
|
||||
private final WeakReference<net.minecraft.server.world.ServerWorld> delegate;
|
||||
private final Path saveFolder;
|
||||
|
||||
public FabricWorld(net.minecraft.server.world.ServerWorld delegate) {
|
||||
this.delegate = new WeakReference<>(delegate);
|
||||
|
||||
MinecraftServer server = delegate.getServer();
|
||||
Path worldFolder = delegate.getServer().getRunDirectory().toPath().resolve(server.getSavePath(WorldSavePath.ROOT));
|
||||
this.saveFolder = DimensionType.getSaveDirectory(delegate.getRegistryKey(), worldFolder.toFile()).toPath()
|
||||
.toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getDimension() {
|
||||
net.minecraft.server.world.ServerWorld world = delegate.get();
|
||||
if (world != null) {
|
||||
if (world.getDimensionRegistryKey().equals(DimensionType.THE_NETHER_REGISTRY_KEY)) return Dimension.NETHER;
|
||||
if (world.getDimensionRegistryKey().equals(DimensionType.THE_END_REGISTRY_KEY)) return Dimension.END;
|
||||
if (world.getDimensionRegistryKey().equals(DimensionType.OVERWORLD_REGISTRY_KEY)) return Dimension.OVERWORLD;
|
||||
}
|
||||
|
||||
return ServerWorld.super.getDimension();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persistWorldChanges() throws IOException {
|
||||
net.minecraft.server.world.ServerWorld world = delegate.get();
|
||||
if (world == null) return false;
|
||||
|
||||
var taskResult = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
world.save(null, true, false);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, world.getServer());
|
||||
|
||||
try {
|
||||
return taskResult.get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IOException(e);
|
||||
} catch (ExecutionException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof IOException) throw (IOException) t;
|
||||
if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t;
|
||||
throw new IOException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getSaveFolder() {
|
||||
return this.saveFolder;
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user