Refactored config management

This commit is contained in:
Lukas Rieger (Blue) 2022-04-20 22:54:27 +02:00
parent 3f634974bc
commit e555d558b7
No known key found for this signature in database
GPG Key ID: 2D09EC5ED2687FF2
234 changed files with 3598 additions and 8374 deletions

View File

@ -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);
try {
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.");
}
storage = storageConfig.createStorage();
storage.initialize();
} catch (Exception ex) {
throw new ConfigurationException("Failed to load and initialize the storage '" + storageId + "' for!",
ex);
}
storages.put(storageId, 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);
}
}
try {
ConfigurationNode node = configManager.loadConfig(storageFolder.resolve(id));
StorageConfig storageConfig = Objects.requireNonNull(node.get(StorageConfig.class));
Storage storage = storageConfig.getStorageType().create(node);
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
);
}
}
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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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("$", "\\$");
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}
webServer = new WebServer(
webServerConfig.getWebserverBindAddress(),
webServerConfig.getWebserverPort(),
webServerConfig.getWebserverMaxConnections(),
requestHandler,
false
);
try {
webServer = new WebServer(
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()) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()){

View File

@ -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"
data: "bluemap"

View File

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

View File

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

View File

@ -17,4 +17,4 @@ root: "bluemap/web/data"
# - GZIP
# - NONE
# The default is: GZIP
compression: GZIP
compression: GZIP

View File

@ -36,4 +36,4 @@ password: ""
# - GZIP
# - NONE
# The default is: GZIP
compression: GZIP
compression: GZIP

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() &&

View File

@ -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,60 +261,36 @@ 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 +
", name='" + name + '\'' +
'}';
"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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -47,17 +47,21 @@ public static OutputStream createFilepartOutputStream(final Path file) throws IO
Files.deleteIfExists(file);
Files.createDirectories(file.getParent());
try {
Files.move(partFile, file, StandardCopyOption.ATOMIC_MOVE);
} catch (FileNotFoundException | NoSuchFileException ignore) {
} catch (IOException ex) {
try {
Files.move(partFile, file);
} catch (FileNotFoundException | NoSuchFileException ignore) {}
}
AtomicFileHelper.move(partFile, file);
});
}
public static void move(Path from, Path to) throws IOException {
try {
Files.move(from, to, StandardCopyOption.ATOMIC_MOVE);
} catch (FileNotFoundException | NoSuchFileException ignore) {
} catch (IOException ex) {
try {
Files.move(from, to);
} catch (FileNotFoundException | NoSuchFileException ignore) {}
}
}
private static Path getPartFile(Path file) {
return file.normalize().getParent().resolve(file.getFileName() + ".filepart");
}

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

View File

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

View File

@ -38,12 +38,10 @@
*/
public interface World {
String getName();
UUID getUUID();
Path getSaveFolder();
String getName();
int getSkyLight();
Vector3i getSpawnPoint();

View File

@ -21,3 +21,9 @@ tasks.register("test") {
dependsOn(it.task(":test"))
}
}
tasks.register("spotlessApply") {
gradle.includedBuilds.forEach {
dependsOn(it.task(":spotlessApply"))
}
}

View File

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

View File

@ -1,4 +0,0 @@
accept-download: false
renderThreadCount: 0
metrics: true
data: "data"

View File

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

View File

@ -1,4 +0,0 @@
webroot: "web"
useCookies: true
enableFreeFlight: true
maps: []

View File

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

View File

@ -1,4 +0,0 @@
enabled: true
webroot: "web"
port: 8100
maxConnectionCount: 100

View File

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

View File

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

View File

@ -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 uuid;
return loadedWorlds;
}
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);
}

View File

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

View File

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

View File

@ -1,4 +0,0 @@
accept-download: false
renderThreadCount: -2
metrics: true
data: "bluemap"

View File

@ -1,7 +0,0 @@
liveUpdates: true
skinDownload: true
hiddenGameModes: []
hideInvisible: true
hideSneaking: false
playerRenderLimit: -1
fullUpdateInterval: 1440

View File

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

View File

@ -1,3 +0,0 @@
webroot: "bluemap/web"
useCookies: true
maps: []

View File

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

View File

@ -1,4 +0,0 @@
enabled: true
webroot: "bluemap/web"
port: 8100
maxConnectionCount: 100

View File

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

View File

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

View File

@ -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 uuid;
return loadedWorlds;
}
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);
}

Some files were not shown because too many files have changed in this diff Show More