From 81aef6ad19697870861bf799b74d8f37bf617386 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Sat, 11 Jan 2020 11:32:47 +0100 Subject: [PATCH 1/6] Basic Bukkit plugin setup --- BlueMapBukkit/build.gradle | 17 +++++ .../bluemap/bukkit/BukkitPlugin.java | 23 ++++++ .../bluemap/bukkit/JavaLogger.java | 70 +++++++++++++++++++ BlueMapBukkit/src/main/resources/plugin.yml | 7 ++ settings.gradle | 4 +- src/main/resources/mcmod.info | 2 +- 6 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 BlueMapBukkit/build.gradle create mode 100644 BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java create mode 100644 BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/JavaLogger.java create mode 100644 BlueMapBukkit/src/main/resources/plugin.yml diff --git a/BlueMapBukkit/build.gradle b/BlueMapBukkit/build.gradle new file mode 100644 index 00000000..1dff7203 --- /dev/null +++ b/BlueMapBukkit/build.gradle @@ -0,0 +1,17 @@ +repositories { + maven { + url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' + + // As of Gradle 5.1, you can limit this to only those + // dependencies you expect from it + content { + includeGroup 'org.bukkit' + } + } +} + +dependencies { + shadow "org.bukkit:bukkit:1.14.4-R0.1-SNAPSHOT" + //compile group: 'org.bstats', name: 'bstats-sponge-lite', version: '1.5' + compile project(':BlueMapCore') +} diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java new file mode 100644 index 00000000..725931e8 --- /dev/null +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -0,0 +1,23 @@ +package de.bluecolored.bluemap.bukkit; + +import org.bukkit.plugin.java.JavaPlugin; + +public class BukkitPlugin extends JavaPlugin { + + private static BukkitPlugin instance; + + @Override + public void onEnable() { + + } + + @Override + public void onDisable() { + + } + + public static BukkitPlugin getInstance() { + return instance; + } + +} diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/JavaLogger.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/JavaLogger.java new file mode 100644 index 00000000..433bf667 --- /dev/null +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/JavaLogger.java @@ -0,0 +1,70 @@ +/* + * This file is part of BlueMapSponge, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.bukkit; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import de.bluecolored.bluemap.core.logger.AbstractLogger; + +public class JavaLogger extends AbstractLogger { + + private Logger out; + + public JavaLogger(Logger out) { + this.out = out; + } + + @Override + public void logError(String message, Throwable throwable) { + out.log(Level.SEVERE, message, throwable); + } + + @Override + public void logWarning(String message) { + out.log(Level.WARNING, message); + } + + @Override + public void logInfo(String message) { + out.log(Level.INFO, message); + } + + @Override + public void logDebug(String message) { + if (out.isLoggable(Level.FINE)) out.log(Level.FINE, message); + } + + @Override + public void noFloodDebug(String message) { + if (out.isLoggable(Level.FINE)) super.noFloodDebug(message); + } + + @Override + public void noFloodDebug(String key, String message) { + if (out.isLoggable(Level.FINE)) super.noFloodDebug(key, message); + } + +} diff --git a/BlueMapBukkit/src/main/resources/plugin.yml b/BlueMapBukkit/src/main/resources/plugin.yml new file mode 100644 index 00000000..7a3565e9 --- /dev/null +++ b/BlueMapBukkit/src/main/resources/plugin.yml @@ -0,0 +1,7 @@ +name: BlueMap +main: de.bluecolored.bluemap.bukkit.BukkitPlugin +version: 0.1.0 +author: Blue (TBlueF / Lukas Rieger) +authors: [Blue (TBlueF / Lukas Rieger)] +website: "https://github.com/BlueMap-Minecraft" +commands: diff --git a/settings.gradle b/settings.gradle index 1a85cd28..3c519531 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,7 +2,9 @@ rootProject.name = 'BlueMap' include ':BlueMapCore' include ':BlueMapCLI' include ':BlueMapSponge' +include ':BlueMapBukkit' project(':BlueMapCore').projectDir = "$rootDir/BlueMapCore" as File project(':BlueMapCLI').projectDir = "$rootDir/BlueMapCLI" as File -project(':BlueMapSponge').projectDir = "$rootDir/BlueMapSponge" as File \ No newline at end of file +project(':BlueMapSponge').projectDir = "$rootDir/BlueMapSponge" as File +project(':BlueMapBukkit').projectDir = "$rootDir/BlueMapBukkit" as File \ No newline at end of file diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index a572a988..63cdb779 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -4,7 +4,7 @@ "name": "BlueMap", "version": "0.1.0", "description": "A 3d-map of your Minecraft worlds view-able in your browser using three.js (WebGL)", - "url": "https://ore.spongepowered.org/Blue/BlueMap", + "url": "https://github.com/BlueMap-Minecraft", "authorList": [ "Blue (TBlueF, Lukas Rieger)" ], From 35d7827611e430a1dc3af9b9fa9aa79ded466c59 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Mon, 13 Jan 2020 17:13:20 +0100 Subject: [PATCH 2/6] Start implemention a common plugin solution --- BlueMapBukkit/build.gradle | 2 +- BlueMapPlugin/build.gradle | 3 + .../bluecolored/bluemap/plugin}/MapType.java | 2 +- .../bluemap/plugin/MapUpdateHandler.java | 102 ++++++ .../de/bluecolored/bluemap/plugin/Plugin.java | 295 ++++++++++++++++++ .../bluemap/plugin}/RenderManager.java | 8 +- .../bluemap/plugin}/RenderTask.java | 6 +- .../bluemap/plugin}/RenderTicket.java | 2 +- .../plugin/serverinterface/CommandSource.java | 106 +++++++ .../serverinterface/ServerEventListener.java | 16 + .../serverinterface/ServerInterface.java | 24 ++ .../plugin/serverinterface/TextColor.java | 47 +++ BlueMapSponge/build.gradle | 2 +- .../bluecolored/bluemap/sponge/Commands.java | 3 + .../bluemap/sponge/MapUpdateHandler.java | 3 + .../bluemap/sponge/SpongePlugin.java | 19 +- settings.gradle | 2 + 17 files changed, 620 insertions(+), 22 deletions(-) create mode 100644 BlueMapPlugin/build.gradle rename {BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge => BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin}/MapType.java (98%) create mode 100644 BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/MapUpdateHandler.java create mode 100644 BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/Plugin.java rename {BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge => BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin}/RenderManager.java (96%) rename {BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge => BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin}/RenderTask.java (96%) rename {BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge => BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin}/RenderTicket.java (96%) create mode 100644 BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/CommandSource.java create mode 100644 BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerEventListener.java create mode 100644 BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerInterface.java create mode 100644 BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/TextColor.java diff --git a/BlueMapBukkit/build.gradle b/BlueMapBukkit/build.gradle index 1dff7203..ae8fa2fd 100644 --- a/BlueMapBukkit/build.gradle +++ b/BlueMapBukkit/build.gradle @@ -13,5 +13,5 @@ repositories { dependencies { shadow "org.bukkit:bukkit:1.14.4-R0.1-SNAPSHOT" //compile group: 'org.bstats', name: 'bstats-sponge-lite', version: '1.5' - compile project(':BlueMapCore') + compile project(':BlueMapPlugin') } diff --git a/BlueMapPlugin/build.gradle b/BlueMapPlugin/build.gradle new file mode 100644 index 00000000..d8406125 --- /dev/null +++ b/BlueMapPlugin/build.gradle @@ -0,0 +1,3 @@ +dependencies { + compile project(':BlueMapCore') +} diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/MapType.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/MapType.java similarity index 98% rename from BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/MapType.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/MapType.java index 5789e42d..68dd4f89 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/MapType.java +++ b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/MapType.java @@ -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.sponge; +package de.bluecolored.bluemap.plugin; import java.io.IOException; diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/MapUpdateHandler.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/MapUpdateHandler.java new file mode 100644 index 00000000..6d16977a --- /dev/null +++ b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/MapUpdateHandler.java @@ -0,0 +1,102 @@ +package de.bluecolored.bluemap.plugin; + +import java.util.Iterator; +import java.util.UUID; + +import com.flowpowered.math.vector.Vector2i; +import com.flowpowered.math.vector.Vector3i; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; + +import de.bluecolored.bluemap.plugin.serverinterface.ServerEventListener; + +public class MapUpdateHandler implements ServerEventListener { + + public Multimap updateBuffer; + + public MapUpdateHandler() { + updateBuffer = MultimapBuilder.hashKeys().hashSetValues().build(); + } + + @Override + public void onWorldSaveToDisk(UUID world) { + RenderManager renderManager = Plugin.getInstance().getRenderManager(); + + synchronized (updateBuffer) { + Iterator iterator = updateBuffer.keys().iterator(); + while (iterator.hasNext()) { + MapType map = iterator.next(); + if (map.getWorld().getUUID().equals(world)) { + renderManager.createTickets(map, updateBuffer.get(map)); + iterator.remove(); + } + } + + } + } + + @Override + public void onBlockChange(UUID world, Vector3i blockPos) { + synchronized (updateBuffer) { + updateBlock(world, blockPos); + } + } + + @Override + public void onChunkFinishedGeneration(UUID world, Vector2i chunkPos) { + int x = chunkPos.getX(); + int z = chunkPos.getY(); + + // also update the chunks around, because they might be modified or not rendered yet due to finalizations + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + updateChunk(world, new Vector2i(x + dx, z + dz)); + } + } + } + + private void updateChunk(UUID world, Vector2i chunkPos) { + Vector3i min = new Vector3i(chunkPos.getX() * 16, 0, chunkPos.getY() * 16); + Vector3i max = min.add(15, 255, 15); + + Vector3i xmin = new Vector3i(min.getX(), 0, max.getY()); + Vector3i xmax = new Vector3i(max.getX(), 255, min.getY()); + + //update all corners so we always update all tiles containing this chunk + synchronized (updateBuffer) { + updateBlock(world, min); + updateBlock(world, max); + updateBlock(world, xmin); + updateBlock(world, xmax); + } + } + + private void updateBlock(UUID world, Vector3i pos){ + synchronized (updateBuffer) { + for (MapType mapType : Plugin.getInstance().getMapTypes()) { + if (mapType.getWorld().getUUID().equals(world)) { + mapType.getWorld().invalidateChunkCache(mapType.getWorld().blockPosToChunkPos(pos)); + + Vector2i tile = mapType.getTileRenderer().getHiresModelManager().posToTile(pos); + updateBuffer.put(mapType, tile); + } + } + } + } + + public int getUpdateBufferCount() { + return updateBuffer.size(); + } + + public void flushTileBuffer() { + RenderManager renderManager = Plugin.getInstance().getRenderManager(); + + synchronized (updateBuffer) { + for (MapType map : updateBuffer.keySet()) { + renderManager.createTickets(map, updateBuffer.get(map)); + } + updateBuffer.clear(); + } + } + +} diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/Plugin.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/Plugin.java new file mode 100644 index 00000000..118c7d9e --- /dev/null +++ b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/Plugin.java @@ -0,0 +1,295 @@ +package de.bluecolored.bluemap.plugin; + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.zip.GZIPInputStream; + +import org.apache.commons.io.FileUtils; +import org.spongepowered.api.Sponge; + +import com.flowpowered.math.vector.Vector2i; + +import de.bluecolored.bluemap.core.BlueMap; +import de.bluecolored.bluemap.core.config.ConfigManager; +import de.bluecolored.bluemap.core.config.MainConfig; +import de.bluecolored.bluemap.core.config.MainConfig.MapConfig; +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.mca.MCAWorld; +import de.bluecolored.bluemap.core.metrics.Metrics; +import de.bluecolored.bluemap.core.render.RenderSettings; +import de.bluecolored.bluemap.core.render.TileRenderer; +import de.bluecolored.bluemap.core.render.hires.HiresModelManager; +import de.bluecolored.bluemap.core.render.lowres.LowresModelManager; +import de.bluecolored.bluemap.core.resourcepack.ResourcePack; +import de.bluecolored.bluemap.core.web.BlueMapWebServer; +import de.bluecolored.bluemap.core.web.WebFilesManager; +import de.bluecolored.bluemap.core.web.WebSettings; +import de.bluecolored.bluemap.core.world.SlicedWorld; +import de.bluecolored.bluemap.core.world.World; +import de.bluecolored.bluemap.plugin.serverinterface.ServerInterface; +import de.bluecolored.bluemap.sponge.Commands; +import de.bluecolored.bluemap.sponge.MapUpdateHandler; +import de.bluecolored.bluemap.sponge.SpongePlugin; +import net.querz.nbt.CompoundTag; +import net.querz.nbt.NBTUtil; + +public class Plugin { + + public static final String PLUGIN_ID = "bluemap"; + public static final String PLUGIN_NAME = "BlueMap"; + public static final String PLUGIN_VERSION = BlueMap.VERSION; + + private static Plugin instance; + private ServerInterface serverInterface; + + private MainConfig config; + private ResourcePack resourcePack; + + private Map worlds; + private Map maps; + + private MapUpdateHandler updateHandler; + + private RenderManager renderManager; + private BlueMapWebServer webServer; + + private boolean loaded = false; + + public Plugin() { + instance = this; + } + + public synchronized void load() { + if (loaded) return; + unload(); //ensure nothing is left running (from a failed load or something) + + //register reload command in case bluemap crashes during loading + Sponge.getCommandManager().register(this, new Commands(this).createStandaloneReloadCommand(), "bluemap"); + + //load configs + URL defaultSpongeConfig = SpongePlugin.class.getResource("/bluemap-sponge.conf"); + URL spongeConfigDefaults = SpongePlugin.class.getResource("/bluemap-sponge-defaults.conf"); + ConfigManager configManager = new ConfigManager(getConfigPath().toFile(), defaultSpongeConfig, spongeConfigDefaults); + configManager.loadMainConfig(); + config = configManager.getMainConfig(); + + //load resources + File defaultResourceFile = config.getDataPath().resolve("minecraft-client-" + ResourcePack.MINECRAFT_CLIENT_VERSION + ".jar").toFile(); + File resourceExtensionsFile = config.getDataPath().resolve("resourceExtensions.zip").toFile(); + File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile(); + + if (!defaultResourceFile.exists()) { + handleMissingResources(defaultResourceFile, configManager.getMainConfigFile()); + unload(); + + Sponge.getCommandManager().register(this, new Commands(this).createStandaloneReloadCommand(), "bluemap"); + return; + } + + resourceExtensionsFile.delete(); + FileUtils.copyURLToFile(SpongePlugin.class.getResource("/resourceExtensions.zip"), resourceExtensionsFile, 10000, 10000); + + //find more resource packs + File resourcePackFolder = getConfigPath().resolve("resourcepacks").toFile(); + resourcePackFolder.mkdirs(); + File[] resourcePacks = resourcePackFolder.listFiles(); + Arrays.sort(resourcePacks); //load resource packs in alphabetical order so you can reorder them by renaming + + List resources = new ArrayList<>(resourcePacks.length + 1); + resources.add(defaultResourceFile); + for (File file : resourcePacks) resources.add(file); + resources.add(resourceExtensionsFile); + + resourcePack = new ResourcePack(); + if (textureExportFile.exists()) resourcePack.loadTextureFile(textureExportFile); + resourcePack.load(resources); + resourcePack.saveTextureFile(textureExportFile); + + configManager.loadResourceConfigs(resourcePack); + + //load maps + for (MapConfig mapConfig : config.getMapConfigs()) { + String id = mapConfig.getId(); + String name = mapConfig.getName(); + + File worldFolder = new File(mapConfig.getWorldPath()); + if (!worldFolder.exists() || !worldFolder.isDirectory()) { + Logger.global.logError("Failed to load map '" + id + "': '" + worldFolder.getCanonicalPath() + "' does not exist or is no directory!", new IOException()); + continue; + } + + UUID worldUUID; + try { + CompoundTag levelSponge = (CompoundTag) NBTUtil.readTag(new File(worldFolder, "level_sponge.dat")); + CompoundTag spongeData = levelSponge.getCompoundTag("SpongeData"); + long most = spongeData.getLong("UUIDMost"); + long least = spongeData.getLong("UUIDLeast"); + worldUUID = new UUID(most, least); + } catch (Exception e) { + Logger.global.logError("Failed to load map '" + id + "': Failed to read level_sponge.dat", e); + continue; + } + + World world = worlds.get(worldUUID); + if (world == null) { + try { + world = MCAWorld.load(worldFolder.toPath(), worldUUID, configManager.getBlockIdConfig(), configManager.getBlockPropertiesConfig(), configManager.getBiomeConfig()); + worlds.put(worldUUID, world); + } catch (IOException e) { + Logger.global.logError("Failed to load map '" + id + "': Failed to read level.dat", e); + continue; + } + } + + //slice world to render edges if configured + if (mapConfig.isRenderEdges() && !(mapConfig.getMin().equals(RenderSettings.DEFAULT_MIN) && mapConfig.getMax().equals(RenderSettings.DEFAULT_MAX))) { + world = new SlicedWorld(world, mapConfig.getMin(), mapConfig.getMax()); + } + + HiresModelManager hiresModelManager = new HiresModelManager( + config.getWebDataPath().resolve(id).resolve("hires"), + resourcePack, + mapConfig, + new Vector2i(mapConfig.getHiresTileSize(), mapConfig.getHiresTileSize()), + getAsyncExecutor() + ); + + LowresModelManager lowresModelManager = new LowresModelManager( + config.getWebDataPath().resolve(id).resolve("lowres"), + new Vector2i(mapConfig.getLowresPointsPerLowresTile(), mapConfig.getLowresPointsPerLowresTile()), + new Vector2i(mapConfig.getLowresPointsPerHiresTile(), mapConfig.getLowresPointsPerHiresTile()) + ); + + TileRenderer tileRenderer = new TileRenderer(hiresModelManager, lowresModelManager); + + MapType mapType = new MapType(id, name, world, tileRenderer); + maps.put(id, mapType); + } + + //initialize render manager + renderManager = new RenderManager(config.getRenderThreadCount()); + renderManager.start(); + + //load render-manager state + try { + File saveFile = config.getDataPath().resolve("rmstate").toFile(); + saveFile.getParentFile().mkdirs(); + if (saveFile.exists()) { + try (DataInputStream in = new DataInputStream(new GZIPInputStream(new FileInputStream(saveFile)))) { + renderManager.readState(in, getMapTypes()); + } + } + saveFile.delete(); + } catch (IOException ex) { + Logger.global.logError("Failed to load render-manager state!", ex); + } + + //start map updater + this.updateHandler = new MapUpdateHandler(); + + //create/update webfiles + WebFilesManager webFilesManager = new WebFilesManager(config.getWebRoot()); + if (webFilesManager.needsUpdate()) { + webFilesManager.updateFiles(); + } + + WebSettings webSettings = new WebSettings(config.getWebDataPath().resolve("settings.json").toFile()); + webSettings.setAllEnabled(false); + for (MapType map : maps.values()) { + webSettings.setEnabled(true, map.getId()); + webSettings.setName(map.getName(), map.getId()); + webSettings.setFrom(map.getTileRenderer(), map.getId()); + } + int ordinal = 0; + for (MapConfig map : config.getMapConfigs()) { + if (!maps.containsKey(map.getId())) continue; //don't add not loaded maps + webSettings.setOrdinal(ordinal++, map.getId()); + webSettings.setHiresViewDistance(map.getHiresViewDistance(), map.getId()); + webSettings.setLowresViewDistance(map.getLowresViewDistance(), map.getId()); + } + webSettings.save(); + + //start webserver + if (config.isWebserverEnabled()) { + webServer = new BlueMapWebServer(config); + webServer.updateWebfiles(); + webServer.start(); + } + + //init commands + Sponge.getCommandManager().getOwnedBy(this).forEach(Sponge.getCommandManager()::removeMapping); + Sponge.getCommandManager().register(this, new Commands(this).createRootCommand(), "bluemap"); + + //metrics + Sponge.getScheduler().createTaskBuilder() + .async() + .delay(1, TimeUnit.MINUTES) + .interval(30, TimeUnit.MINUTES) + .execute(() -> { + if (Sponge.getMetricsConfigManager().areMetricsEnabled(this)) Metrics.sendReport("Sponge"); + }) + .submit(this); + + loaded = true; + } + + public synchronized void unload() { + + } + + public synchronized void reload() { + unload(); + load(); + } + + public ServerInterface getServerInterface() { + return serverInterface; + } + + public MainConfig getMainConfig() { + return config; + } + + public ResourcePack getResourcePack() { + return resourcePack; + } + + public World getWorld(UUID uuid){ + return worlds.get(uuid); + } + + public Collection getMapTypes(){ + return maps.values(); + } + + public RenderManager getRenderManager() { + return renderManager; + } + + public MapUpdateHandler getUpdateHandler() { + return updateHandler; + } + + public BlueMapWebServer getWebServer() { + return webServer; + } + + public boolean isLoaded() { + return loaded; + } + + public static Plugin getInstance() { + return instance; + } + +} diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/RenderManager.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderManager.java similarity index 96% rename from BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/RenderManager.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderManager.java index 43e7be3b..abcbbd59 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/RenderManager.java +++ b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderManager.java @@ -1,4 +1,4 @@ -package de.bluecolored.bluemap.sponge; +package de.bluecolored.bluemap.plugin; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -194,14 +194,14 @@ public void writeState(DataOutputStream out) throws IOException { } } - public void readState(DataInputStream in) throws IOException { + public void readState(DataInputStream in, Collection mapTypes) throws IOException { //read renderTickets int mapCount = in.readInt(); for (int i = 0; i < mapCount; i++) { String mapId = in.readUTF(); MapType mapType = null; - for (MapType map : SpongePlugin.getInstance().getMapTypes()) { + for (MapType map : mapTypes) { if (map.getId().equals(mapId)) { mapType = map; break; @@ -227,7 +227,7 @@ public void readState(DataInputStream in) throws IOException { int taskCount = in.readInt(); for (int i = 0; i < taskCount; i++) { try { - RenderTask task = RenderTask.read(in); + RenderTask task = RenderTask.read(in, mapTypes); addRenderTask(task); } catch (IOException ex) { Logger.global.logWarning("A render-task can not be loaded. It will be discared. (Error message: " + ex.toString() + ")"); diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/RenderTask.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderTask.java similarity index 96% rename from BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/RenderTask.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderTask.java index 28710a29..8c43b0e9 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/RenderTask.java +++ b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderTask.java @@ -1,4 +1,4 @@ -package de.bluecolored.bluemap.sponge; +package de.bluecolored.bluemap.plugin; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -159,12 +159,12 @@ public void write(DataOutputStream out) throws IOException { } } - public static RenderTask read(DataInputStream in) throws IOException { + public static RenderTask read(DataInputStream in, Collection mapTypes) throws IOException { String name = in.readUTF(); String mapId = in.readUTF(); MapType mapType = null; - for (MapType map : SpongePlugin.getInstance().getMapTypes()) { + for (MapType map : mapTypes) { if (map.getId().equals(mapId)) { mapType = map; break; diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/RenderTicket.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderTicket.java similarity index 96% rename from BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/RenderTicket.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderTicket.java index 2f4d8cd9..2ec13bad 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/RenderTicket.java +++ b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderTicket.java @@ -1,4 +1,4 @@ -package de.bluecolored.bluemap.sponge; +package de.bluecolored.bluemap.plugin; import java.io.IOException; import java.util.Objects; diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/CommandSource.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/CommandSource.java new file mode 100644 index 00000000..e4a5d58f --- /dev/null +++ b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/CommandSource.java @@ -0,0 +1,106 @@ +package de.bluecolored.bluemap.plugin.serverinterface; + +import org.apache.commons.lang3.StringUtils; + +import net.querz.nbt.CompoundTag; +import net.querz.nbt.ListTag; +import net.querz.nbt.Tag; + +public interface CommandSource { + + default void sendMessage(String plainMessage) { + CompoundTag textTag = new CompoundTag(); + textTag.putString("text", plainMessage); + sendSerializedMessage(textTag); + } + + default void sendMessage(String plainMessage, char colorCodePrefix) { + sendSerializedMessage(parseColorCodes(plainMessage, colorCodePrefix)); + } + + default void sendMessage(Iterable> textTags) { + ListTag> textListTag = new ListTag<>(Tag.class); + for (Tag textTag : textTags) { + textListTag.add(textTag); + } + sendSerializedMessage(textListTag); + } + + default void sendMessage(Tag... textTags) { + ListTag> textListTag = new ListTag<>(Tag.class); + for (Tag textTag : textTags) { + textListTag.add(textTag); + } + sendSerializedMessage(textListTag); + } + + void sendSerializedMessage(Tag textListTag); + + static Tag parseColorCodes(String plainMessage, char colorCodePrefix) { + ListTag> tagList = new ListTag<>(Tag.class); + tagList.addString(""); + + if (plainMessage.isEmpty()) return tagList; + + TextColor color = TextColor.UNDEFINED; + boolean bold = false; + boolean italic = false; + boolean obfuscated = false; + boolean strikethrough = false; + boolean underlined = false; + + plainMessage = "r" + plainMessage; + + String[] parts = StringUtils.split(plainMessage, colorCodePrefix); + + for (String part : parts) { + if (part.isEmpty()) throw new IllegalArgumentException("There is a color code with no color-char!"); + String message = part.substring(1); + char code = part.charAt(0); + + switch (code) { + case 'r': + color = TextColor.UNDEFINED; + bold = false; + italic = false; + obfuscated = false; + strikethrough = false; + underlined = false; + case 'k': + obfuscated = true; + break; + case 'l': + bold = true; + break; + case 'm': + strikethrough = true; + break; + case 'n': + underlined = true; + break; + case 'o': + italic = true; + break; + default: + color = TextColor.ofColorCode(code); + break; + } + + if (message.isEmpty()) continue; + + CompoundTag textTag = new CompoundTag(); + textTag.putString("text", message); + if (color != TextColor.UNDEFINED) textTag.putString("color", color.getId()); + if (bold) textTag.putBoolean("bold", true); + if (italic) textTag.putBoolean("italic", true); + if (underlined) textTag.putBoolean("underlined", true); + if (strikethrough) textTag.putBoolean("strikethrough", true); + if (obfuscated) textTag.putBoolean("obfuscated", true); + + + } + + return tagList; + } + +} diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerEventListener.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerEventListener.java new file mode 100644 index 00000000..ee0679cf --- /dev/null +++ b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerEventListener.java @@ -0,0 +1,16 @@ +package de.bluecolored.bluemap.plugin.serverinterface; + +import java.util.UUID; + +import com.flowpowered.math.vector.Vector2i; +import com.flowpowered.math.vector.Vector3i; + +public interface ServerEventListener { + + void onWorldSaveToDisk(UUID world); + + void onBlockChange(UUID world, Vector3i blockPos); + + void onChunkFinishedGeneration(UUID world, Vector2i chunkPos); + +} diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerInterface.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerInterface.java new file mode 100644 index 00000000..df42f064 --- /dev/null +++ b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerInterface.java @@ -0,0 +1,24 @@ +package de.bluecolored.bluemap.plugin.serverinterface; + +import java.io.File; +import java.util.concurrent.ExecutorService; + +import de.bluecolored.bluemap.core.world.World; + +public interface ServerInterface { + + void registerListener(ServerEventListener listener); + + void unregisterAllListeners(); + + World createWorld(File worldFolder); + + ExecutorService getSyncExecutorService(); + + ExecutorService getAsyncExecutorService(); + + File getConfigFolder(); + + boolean isMetricsEnabled(boolean configValue); + +} diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/TextColor.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/TextColor.java new file mode 100644 index 00000000..c5e3281a --- /dev/null +++ b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/TextColor.java @@ -0,0 +1,47 @@ +package de.bluecolored.bluemap.plugin.serverinterface; + +public enum TextColor { + + UNDEFINED ("undefined", 'r'), + WHITE ("white", 'f'), + BLACK ("black", '0'), + YELLOW ("yellow", 'e'), + GOLD ("gold", '6'), + AQUA ("aqua", 'b'), + DARK_AQUA ("dark_aqua", '3'), + BLUE ("blue", '9'), + DARK_BLUE ("dark_blue", '1'), + LIGHT_PURPLE ("light_purple", 'd'), + DARK_PURPLE ("dark_purple", '5'), + RED ("red", 'c'), + DARK_RED ("dark_red", '4'), + GREEN ("green", 'a'), + DARK_GREEN ("dark_green", '2'), + GRAY ("gray", '7'), + DARK_GRAY ("dark_gray", '8'); + + private final String id; + private final char colorCode; + + private TextColor(String id, char colorCode) { + this.id = id; + this.colorCode = colorCode; + } + + public String getId() { + return id; + } + + public char getColorCode() { + return colorCode; + } + + public static TextColor ofColorCode(char code) { + for (TextColor color : values()) { + if (color.colorCode == code) return color; + } + + throw new IllegalArgumentException("'" + code + "' isn't a valid color-code!"); + } + +} diff --git a/BlueMapSponge/build.gradle b/BlueMapSponge/build.gradle index a1dbf64d..0c96c272 100644 --- a/BlueMapSponge/build.gradle +++ b/BlueMapSponge/build.gradle @@ -1,5 +1,5 @@ dependencies { shadow "org.spongepowered:spongeapi:7.1.0-SNAPSHOT" compile group: 'org.bstats', name: 'bstats-sponge-lite', version: '1.5' - compile project(':BlueMapCore') + compile project(':BlueMapPlugin') } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java index d7d7221e..e4d6cec4 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java @@ -34,6 +34,9 @@ import de.bluecolored.bluemap.core.render.hires.HiresModelManager; import de.bluecolored.bluemap.core.world.Block; import de.bluecolored.bluemap.core.world.World; +import de.bluecolored.bluemap.plugin.MapType; +import de.bluecolored.bluemap.plugin.RenderManager; +import de.bluecolored.bluemap.plugin.RenderTask; public class Commands { diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/MapUpdateHandler.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/MapUpdateHandler.java index 529b6d0e..6cf59554 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/MapUpdateHandler.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/MapUpdateHandler.java @@ -20,6 +20,9 @@ import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; +import de.bluecolored.bluemap.plugin.MapType; +import de.bluecolored.bluemap.plugin.RenderManager; + public class MapUpdateHandler { public Multimap updateBuffer; diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java index 96458db5..4438baa4 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -54,13 +54,11 @@ import org.spongepowered.api.event.game.GameReloadEvent; import org.spongepowered.api.event.game.state.GameStartingServerEvent; import org.spongepowered.api.event.game.state.GameStoppingEvent; -import org.spongepowered.api.plugin.Plugin; import org.spongepowered.api.scheduler.SpongeExecutorService; import org.spongepowered.api.world.storage.WorldProperties; import com.flowpowered.math.vector.Vector2i; -import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.config.ConfigManager; import de.bluecolored.bluemap.core.config.MainConfig; import de.bluecolored.bluemap.core.config.MainConfig.MapConfig; @@ -78,22 +76,21 @@ import de.bluecolored.bluemap.core.web.WebSettings; import de.bluecolored.bluemap.core.world.SlicedWorld; import de.bluecolored.bluemap.core.world.World; +import de.bluecolored.bluemap.plugin.MapType; +import de.bluecolored.bluemap.plugin.Plugin; +import de.bluecolored.bluemap.plugin.RenderManager; import net.querz.nbt.CompoundTag; import net.querz.nbt.NBTUtil; -@Plugin( - id = SpongePlugin.PLUGIN_ID, - name = SpongePlugin.PLUGIN_NAME, +@org.spongepowered.api.plugin.Plugin( + id = Plugin.PLUGIN_ID, + name = Plugin.PLUGIN_NAME, authors = { "Blue (Lukas Rieger)" }, description = "This plugin provides a fully 3D map of your world for your browser!", - version = SpongePlugin.PLUGIN_VERSION + version = Plugin.PLUGIN_VERSION ) public class SpongePlugin { - public static final String PLUGIN_ID = "bluemap"; - public static final String PLUGIN_NAME = "BlueMap"; - public static final String PLUGIN_VERSION = BlueMap.VERSION; - private static SpongePlugin instance; @Inject @@ -246,7 +243,7 @@ public synchronized void load() throws ExecutionException, IOException, Interrup saveFile.getParentFile().mkdirs(); if (saveFile.exists()) { try (DataInputStream in = new DataInputStream(new GZIPInputStream(new FileInputStream(saveFile)))) { - renderManager.readState(in); + renderManager.readState(in, getMapTypes()); } } saveFile.delete(); diff --git a/settings.gradle b/settings.gradle index 3c519531..261e1aa9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,10 +1,12 @@ rootProject.name = 'BlueMap' include ':BlueMapCore' include ':BlueMapCLI' +include ':BlueMapPlugin' include ':BlueMapSponge' include ':BlueMapBukkit' project(':BlueMapCore').projectDir = "$rootDir/BlueMapCore" as File project(':BlueMapCLI').projectDir = "$rootDir/BlueMapCLI" as File +project(':BlueMapPlugin').projectDir = "$rootDir/BlueMapPlugin" as File project(':BlueMapSponge').projectDir = "$rootDir/BlueMapSponge" as File project(':BlueMapBukkit').projectDir = "$rootDir/BlueMapBukkit" as File \ No newline at end of file From e156c818b21b6874fe9d5b97f3a036b1710a6d24 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Wed, 15 Jan 2020 20:19:57 +0100 Subject: [PATCH 3/6] More common/plugin refactoring --- .../java/de/bluecolored/bluemap/{plugin => common}/MapType.java | 0 .../de/bluecolored/bluemap/{plugin => common}/RenderManager.java | 0 .../de/bluecolored/bluemap/{plugin => common}/RenderTask.java | 0 .../de/bluecolored/bluemap/{plugin => common}/RenderTicket.java | 0 .../bluecolored/bluemap/{ => common}/plugin/MapUpdateHandler.java | 0 .../java/de/bluecolored/bluemap/{ => common}/plugin/Plugin.java | 0 .../{ => common}/plugin/serverinterface/CommandSource.java | 0 .../{ => common}/plugin/serverinterface/ServerEventListener.java | 0 .../{ => common}/plugin/serverinterface/ServerInterface.java | 0 .../bluemap/{ => common}/plugin/serverinterface/TextColor.java | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename BlueMapPlugin/src/main/java/de/bluecolored/bluemap/{plugin => common}/MapType.java (100%) rename BlueMapPlugin/src/main/java/de/bluecolored/bluemap/{plugin => common}/RenderManager.java (100%) rename BlueMapPlugin/src/main/java/de/bluecolored/bluemap/{plugin => common}/RenderTask.java (100%) rename BlueMapPlugin/src/main/java/de/bluecolored/bluemap/{plugin => common}/RenderTicket.java (100%) rename BlueMapPlugin/src/main/java/de/bluecolored/bluemap/{ => common}/plugin/MapUpdateHandler.java (100%) rename BlueMapPlugin/src/main/java/de/bluecolored/bluemap/{ => common}/plugin/Plugin.java (100%) rename BlueMapPlugin/src/main/java/de/bluecolored/bluemap/{ => common}/plugin/serverinterface/CommandSource.java (100%) rename BlueMapPlugin/src/main/java/de/bluecolored/bluemap/{ => common}/plugin/serverinterface/ServerEventListener.java (100%) rename BlueMapPlugin/src/main/java/de/bluecolored/bluemap/{ => common}/plugin/serverinterface/ServerInterface.java (100%) rename BlueMapPlugin/src/main/java/de/bluecolored/bluemap/{ => common}/plugin/serverinterface/TextColor.java (100%) diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/MapType.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/MapType.java similarity index 100% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/MapType.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/MapType.java diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderManager.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderManager.java similarity index 100% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderManager.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderManager.java diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderTask.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderTask.java similarity index 100% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderTask.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderTask.java diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderTicket.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java similarity index 100% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/RenderTicket.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/MapUpdateHandler.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java similarity index 100% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/MapUpdateHandler.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/Plugin.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java similarity index 100% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/Plugin.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/CommandSource.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java similarity index 100% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/CommandSource.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerEventListener.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java similarity index 100% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerEventListener.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerInterface.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java similarity index 100% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/ServerInterface.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/TextColor.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/TextColor.java similarity index 100% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/plugin/serverinterface/TextColor.java rename to BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/TextColor.java From 49148147447408daf169479ae62d2b7567004360 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Wed, 15 Jan 2020 20:20:22 +0100 Subject: [PATCH 4/6] More common/plugin refactoring 2 --- BlueMapBukkit/build.gradle | 6 +- .../bluemap/bukkit/BukkitPlugin.java | 68 ++- .../bluecolored/bluemap/bukkit/Commands.java | 26 ++ .../bluemap/bukkit/EventForwarder.java | 133 ++++++ .../bluecolored/bluemap/cli/BlueMapCLI.java | 12 +- {BlueMapPlugin => BlueMapCommon}/build.gradle | 0 .../bluecolored/bluemap/common/MapType.java | 2 +- .../bluemap/common/RenderManager.java | 2 +- .../bluemap/common/RenderTask.java | 2 +- .../bluemap/common/RenderTicket.java | 2 +- .../common/plugin/MapUpdateHandler.java | 6 +- .../bluemap/common/plugin/Plugin.java | 155 +++++-- .../serverinterface/ServerEventListener.java | 2 +- .../serverinterface/ServerInterface.java | 42 ++ .../bluemap/common/plugin/text/Text.java | 202 +++++++++ .../bluemap/common/plugin/text/TextColor.java | 38 ++ .../common/plugin/text/TextFormat.java | 27 ++ .../bluemap/core/mca/MCAWorld.java | 5 +- .../core/render/hires/HiresModelManager.java | 26 ++ .../plugin/serverinterface/CommandSource.java | 106 ----- .../serverinterface/ServerInterface.java | 24 -- .../plugin/serverinterface/TextColor.java | 47 --- BlueMapSponge/build.gradle | 2 +- .../bluecolored/bluemap/sponge/Commands.java | 53 +-- .../bluemap/sponge/EventForwarder.java | 76 ++++ .../bluemap/sponge/MapUpdateHandler.java | 128 ------ .../bluemap/sponge/SpongePlugin.java | 394 ++---------------- build.gradle | 5 +- settings.gradle | 4 +- 29 files changed, 834 insertions(+), 761 deletions(-) create mode 100644 BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/Commands.java create mode 100644 BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java rename {BlueMapPlugin => BlueMapCommon}/build.gradle (100%) rename {BlueMapPlugin => BlueMapCommon}/src/main/java/de/bluecolored/bluemap/common/MapType.java (98%) rename {BlueMapPlugin => BlueMapCommon}/src/main/java/de/bluecolored/bluemap/common/RenderManager.java (99%) rename {BlueMapPlugin => BlueMapCommon}/src/main/java/de/bluecolored/bluemap/common/RenderTask.java (99%) rename {BlueMapPlugin => BlueMapCommon}/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java (96%) rename {BlueMapPlugin => BlueMapCommon}/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java (92%) rename {BlueMapPlugin => BlueMapCommon}/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java (65%) rename {BlueMapPlugin => BlueMapCommon}/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java (83%) create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/Text.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/TextColor.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/TextFormat.java delete mode 100644 BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java delete mode 100644 BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java delete mode 100644 BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/TextColor.java create mode 100644 BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java delete mode 100644 BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/MapUpdateHandler.java diff --git a/BlueMapBukkit/build.gradle b/BlueMapBukkit/build.gradle index ae8fa2fd..c8d6a719 100644 --- a/BlueMapBukkit/build.gradle +++ b/BlueMapBukkit/build.gradle @@ -2,8 +2,6 @@ repositories { maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' - // As of Gradle 5.1, you can limit this to only those - // dependencies you expect from it content { includeGroup 'org.bukkit' } @@ -12,6 +10,6 @@ repositories { dependencies { shadow "org.bukkit:bukkit:1.14.4-R0.1-SNAPSHOT" - //compile group: 'org.bstats', name: 'bstats-sponge-lite', version: '1.5' - compile project(':BlueMapPlugin') + compile group: 'org.bstats', name: 'bstats-bukkit-lite', version: '1.5' + compile project(':BlueMapCommon') } diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index 725931e8..ec7a0cbb 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -1,23 +1,83 @@ package de.bluecolored.bluemap.bukkit; +import java.io.File; +import java.io.IOException; +import java.util.UUID; + +import org.bstats.bukkit.MetricsLite; +import org.bukkit.World; import org.bukkit.plugin.java.JavaPlugin; -public class BukkitPlugin extends JavaPlugin { +import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; +import de.bluecolored.bluemap.core.logger.Logger; - private static BukkitPlugin instance; +public class BukkitPlugin extends JavaPlugin implements ServerInterface { + + private Plugin bluemap; + private EventForwarder eventForwarder; + private Commands commands; + + public BukkitPlugin() { + Logger.global = new JavaLogger(getLogger()); + + this.eventForwarder = new EventForwarder(); + this.commands = new Commands(this); + this.bluemap = new Plugin("bukkit", this); + } @Override public void onEnable() { + new MetricsLite(this); + getServer().getPluginManager().registerEvents(eventForwarder, this); + getCommand("bluemap").setExecutor(commands); + + getServer().getScheduler().runTaskAsynchronously(this, () -> { + try { + Logger.global.logInfo("Loading..."); + this.bluemap.load(); + if (bluemap.isLoaded()) Logger.global.logInfo("Loaded!"); + } catch (Throwable t) { + Logger.global.logError("Failed to load!", t); + } + }); } @Override public void onDisable() { + Logger.global.logInfo("Stopping..."); + bluemap.unload(); + Logger.global.logInfo("Saved and stopped!"); + } + + @Override + public void registerListener(ServerEventListener listener) { + eventForwarder.addListener(listener); + } + + @Override + public void unregisterAllListeners() { + eventForwarder.removeAllListeners(); + } + + @Override + public UUID getUUIDForWorld(File worldFolder) throws IOException { + for (World world : getServer().getWorlds()) { + if (worldFolder.equals(world.getWorldFolder())) return world.getUID(); + } + throw new IOException("There is no world with this folder loaded!"); + } + + @Override + public File getConfigFolder() { + return getConfigFolder(); } - public static BukkitPlugin getInstance() { - return instance; + public Plugin getBlueMap() { + return bluemap; } } diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/Commands.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/Commands.java new file mode 100644 index 00000000..6d261925 --- /dev/null +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/Commands.java @@ -0,0 +1,26 @@ +package de.bluecolored.bluemap.bukkit; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +import de.bluecolored.bluemap.common.plugin.Plugin; + +public class Commands implements CommandExecutor { + + private BukkitPlugin plugin; + private Plugin bluemap; + + public Commands(BukkitPlugin plugin) { + this.plugin = plugin; + this.bluemap = plugin.getBlueMap(); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + + + return true; + } + +} diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java new file mode 100644 index 00000000..a307437c --- /dev/null +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java @@ -0,0 +1,133 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.bukkit; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; + +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockExplodeEvent; +import org.bukkit.event.block.BlockFadeEvent; +import org.bukkit.event.block.BlockFertilizeEvent; +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.block.BlockGrowEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.world.ChunkPopulateEvent; +import org.bukkit.event.world.WorldSaveEvent; + +import com.flowpowered.math.vector.Vector2i; +import com.flowpowered.math.vector.Vector3i; + +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; + +public class EventForwarder implements Listener { + + private Collection listeners; + + public EventForwarder() { + listeners = new ArrayList<>(); + } + + public synchronized void addListener(ServerEventListener listener) { + listeners.add(listener); + } + + public synchronized void removeAllListeners() { + listeners.clear(); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public synchronized void onWorldSaveToDisk(WorldSaveEvent evt) { + listeners.forEach(l -> l.onWorldSaveToDisk(evt.getWorld().getUID())); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockChange(BlockPlaceEvent evt) { + onBlockChange(evt.getBlock().getLocation()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockChange(BlockBreakEvent evt) { + onBlockChange(evt.getBlock().getLocation()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockChange(BlockGrowEvent evt) { + onBlockChange(evt.getBlock().getLocation()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockChange(BlockBurnEvent evt) { + onBlockChange(evt.getBlock().getLocation()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockChange(BlockExplodeEvent evt) { + onBlockChange(evt.getBlock().getLocation()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockChange(BlockFadeEvent evt) { + onBlockChange(evt.getBlock().getLocation()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockChange(BlockSpreadEvent evt) { + onBlockChange(evt.getBlock().getLocation()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockChange(BlockFormEvent evt) { + onBlockChange(evt.getBlock().getLocation()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockChange(BlockFertilizeEvent evt) { + onBlockChange(evt.getBlock().getLocation()); + } + + private synchronized void onBlockChange(Location loc) { + UUID world = loc.getWorld().getUID(); + Vector3i pos = new Vector3i(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); + listeners.forEach(l -> l.onBlockChange(world, pos)); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public synchronized void onChunkFinishedGeneration(ChunkPopulateEvent evt) { + Chunk chunk = evt.getChunk(); + UUID world = chunk.getWorld().getUID(); + Vector2i chunkPos = new Vector2i(chunk.getX(), chunk.getZ()); + listeners.forEach(l -> l.onChunkFinishedGeneration(world, chunkPos)); + } + +} diff --git a/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java b/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java index b3c58e76..2b55dfb2 100644 --- a/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java +++ b/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java @@ -31,10 +31,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.UUID; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; @@ -49,7 +47,6 @@ import org.apache.commons.io.FileUtils; import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; import com.google.common.base.Preconditions; import de.bluecolored.bluemap.core.config.ConfigManager; @@ -162,14 +159,7 @@ public void renderMaps() throws IOException { } HiresModelManager hiresModelManager = map.getTileRenderer().getHiresModelManager(); - Set tiles = new HashSet<>(); - for (Vector2i chunk : chunks) { - Vector3i minBlockPos = new Vector3i(chunk.getX() * 16, 0, chunk.getY() * 16); - tiles.add(hiresModelManager.posToTile(minBlockPos)); - tiles.add(hiresModelManager.posToTile(minBlockPos.add(0, 0, 15))); - tiles.add(hiresModelManager.posToTile(minBlockPos.add(15, 0, 0))); - tiles.add(hiresModelManager.posToTile(minBlockPos.add(15, 0, 15))); - } + Collection tiles = hiresModelManager.getTilesForChunks(chunks); Logger.global.logInfo("Found " + tiles.size() + " tiles to render! (" + chunks.size() + " chunks)"); if (!forceRender && chunks.size() == 0) { Logger.global.logInfo("(This is normal if nothing has changed in the world since the last render. Use -f on the command-line to force a render of all chunks)"); diff --git a/BlueMapPlugin/build.gradle b/BlueMapCommon/build.gradle similarity index 100% rename from BlueMapPlugin/build.gradle rename to BlueMapCommon/build.gradle diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/MapType.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/MapType.java similarity index 98% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/MapType.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/MapType.java index 68dd4f89..5d3a11d3 100644 --- a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/MapType.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/MapType.java @@ -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.plugin; +package de.bluecolored.bluemap.common; import java.io.IOException; diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderManager.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderManager.java similarity index 99% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderManager.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderManager.java index abcbbd59..b0236457 100644 --- a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderManager.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderManager.java @@ -1,4 +1,4 @@ -package de.bluecolored.bluemap.plugin; +package de.bluecolored.bluemap.common; import java.io.DataInputStream; import java.io.DataOutputStream; diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderTask.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTask.java similarity index 99% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderTask.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTask.java index 8c43b0e9..5f6f9cd6 100644 --- a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderTask.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTask.java @@ -1,4 +1,4 @@ -package de.bluecolored.bluemap.plugin; +package de.bluecolored.bluemap.common; import java.io.DataInputStream; import java.io.DataOutputStream; diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java similarity index 96% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java index 2ec13bad..545a7bdc 100644 --- a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java @@ -1,4 +1,4 @@ -package de.bluecolored.bluemap.plugin; +package de.bluecolored.bluemap.common; import java.io.IOException; import java.util.Objects; diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java similarity index 92% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java index 6d16977a..8e485732 100644 --- a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java @@ -1,4 +1,4 @@ -package de.bluecolored.bluemap.plugin; +package de.bluecolored.bluemap.common.plugin; import java.util.Iterator; import java.util.UUID; @@ -8,7 +8,9 @@ import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; -import de.bluecolored.bluemap.plugin.serverinterface.ServerEventListener; +import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.common.RenderManager; +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; public class MapUpdateHandler implements ServerEventListener { diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java similarity index 65% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java index 118c7d9e..ff74856b 100644 --- a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java @@ -1,24 +1,31 @@ -package de.bluecolored.bluemap.plugin; +package de.bluecolored.bluemap.common.plugin; import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import org.apache.commons.io.FileUtils; -import org.spongepowered.api.Sponge; import com.flowpowered.math.vector.Vector2i; +import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.common.RenderManager; +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.config.ConfigManager; import de.bluecolored.bluemap.core.config.MainConfig; @@ -30,18 +37,13 @@ import de.bluecolored.bluemap.core.render.TileRenderer; import de.bluecolored.bluemap.core.render.hires.HiresModelManager; import de.bluecolored.bluemap.core.render.lowres.LowresModelManager; +import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; import de.bluecolored.bluemap.core.resourcepack.ResourcePack; import de.bluecolored.bluemap.core.web.BlueMapWebServer; import de.bluecolored.bluemap.core.web.WebFilesManager; import de.bluecolored.bluemap.core.web.WebSettings; import de.bluecolored.bluemap.core.world.SlicedWorld; import de.bluecolored.bluemap.core.world.World; -import de.bluecolored.bluemap.plugin.serverinterface.ServerInterface; -import de.bluecolored.bluemap.sponge.Commands; -import de.bluecolored.bluemap.sponge.MapUpdateHandler; -import de.bluecolored.bluemap.sponge.SpongePlugin; -import net.querz.nbt.CompoundTag; -import net.querz.nbt.NBTUtil; public class Plugin { @@ -50,6 +52,9 @@ public class Plugin { public static final String PLUGIN_VERSION = BlueMap.VERSION; private static Plugin instance; + + private String implementationType; + private ServerInterface serverInterface; private MainConfig config; @@ -63,23 +68,28 @@ public class Plugin { private RenderManager renderManager; private BlueMapWebServer webServer; + private Thread metricsThread; + private boolean loaded = false; - public Plugin() { + public Plugin(String implementationType, ServerInterface serverInterface) { + this.implementationType = implementationType.toLowerCase(); + this.serverInterface = serverInterface; + + this.maps = new HashMap<>(); + this.worlds = new HashMap<>(); + instance = this; } - public synchronized void load() { + public synchronized void load() throws IOException, ParseResourceException { if (loaded) return; unload(); //ensure nothing is left running (from a failed load or something) - //register reload command in case bluemap crashes during loading - Sponge.getCommandManager().register(this, new Commands(this).createStandaloneReloadCommand(), "bluemap"); - //load configs - URL defaultSpongeConfig = SpongePlugin.class.getResource("/bluemap-sponge.conf"); - URL spongeConfigDefaults = SpongePlugin.class.getResource("/bluemap-sponge-defaults.conf"); - ConfigManager configManager = new ConfigManager(getConfigPath().toFile(), defaultSpongeConfig, spongeConfigDefaults); + URL defaultSpongeConfig = Plugin.class.getResource("/bluemap-" + implementationType + ".conf"); + URL spongeConfigDefaults = Plugin.class.getResource("/bluemap-" + implementationType + "-defaults.conf"); + ConfigManager configManager = new ConfigManager(serverInterface.getConfigFolder(), defaultSpongeConfig, spongeConfigDefaults); configManager.loadMainConfig(); config = configManager.getMainConfig(); @@ -89,18 +99,32 @@ public synchronized void load() { File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile(); if (!defaultResourceFile.exists()) { - handleMissingResources(defaultResourceFile, configManager.getMainConfigFile()); - unload(); - - Sponge.getCommandManager().register(this, new Commands(this).createStandaloneReloadCommand(), "bluemap"); - return; + if (config.isDownloadAccepted()) { + + //download file + try { + Logger.global.logInfo("Downloading " + ResourcePack.MINECRAFT_CLIENT_URL + " to " + defaultResourceFile + " ..."); + ResourcePack.downloadDefaultResource(defaultResourceFile); + } catch (IOException e) { + Logger.global.logError("Failed to download resources!", e); + return; + } + + } else { + Logger.global.logWarning("BlueMap is missing important resources!"); + Logger.global.logWarning("You need to accept the download of the required files in order of BlueMap to work!"); + try { Logger.global.logWarning("Please check: " + configManager.getMainConfigFile().getCanonicalPath()); } catch (IOException ignored) {} + Logger.global.logInfo("If you have changed the config you can simply reload the plugin using: /bluemap reload"); + + return; + } } resourceExtensionsFile.delete(); - FileUtils.copyURLToFile(SpongePlugin.class.getResource("/resourceExtensions.zip"), resourceExtensionsFile, 10000, 10000); + FileUtils.copyURLToFile(Plugin.class.getResource("/resourceExtensions.zip"), resourceExtensionsFile, 10000, 10000); //find more resource packs - File resourcePackFolder = getConfigPath().resolve("resourcepacks").toFile(); + File resourcePackFolder = new File(serverInterface.getConfigFolder(), "resourcepacks"); resourcePackFolder.mkdirs(); File[] resourcePacks = resourcePackFolder.listFiles(); Arrays.sort(resourcePacks); //load resource packs in alphabetical order so you can reorder them by renaming @@ -130,13 +154,9 @@ public synchronized void load() { UUID worldUUID; try { - CompoundTag levelSponge = (CompoundTag) NBTUtil.readTag(new File(worldFolder, "level_sponge.dat")); - CompoundTag spongeData = levelSponge.getCompoundTag("SpongeData"); - long most = spongeData.getLong("UUIDMost"); - long least = spongeData.getLong("UUIDLeast"); - worldUUID = new UUID(most, least); - } catch (Exception e) { - Logger.global.logError("Failed to load map '" + id + "': Failed to read level_sponge.dat", e); + worldUUID = serverInterface.getUUIDForWorld(worldFolder); + } catch (IOException e) { + Logger.global.logError("Failed to load map '" + id + "': Failed to get UUID for the world!", e); continue; } @@ -161,7 +181,7 @@ public synchronized void load() { resourcePack, mapConfig, new Vector2i(mapConfig.getHiresTileSize(), mapConfig.getHiresTileSize()), - getAsyncExecutor() + ForkJoinPool.commonPool() ); LowresModelManager lowresModelManager = new LowresModelManager( @@ -196,6 +216,7 @@ public synchronized void load() { //start map updater this.updateHandler = new MapUpdateHandler(); + serverInterface.registerListener(updateHandler); //create/update webfiles WebFilesManager webFilesManager = new WebFilesManager(config.getWebRoot()); @@ -225,29 +246,73 @@ public synchronized void load() { webServer.updateWebfiles(); webServer.start(); } - - //init commands - Sponge.getCommandManager().getOwnedBy(this).forEach(Sponge.getCommandManager()::removeMapping); - Sponge.getCommandManager().register(this, new Commands(this).createRootCommand(), "bluemap"); //metrics - Sponge.getScheduler().createTaskBuilder() - .async() - .delay(1, TimeUnit.MINUTES) - .interval(30, TimeUnit.MINUTES) - .execute(() -> { - if (Sponge.getMetricsConfigManager().areMetricsEnabled(this)) Metrics.sendReport("Sponge"); - }) - .submit(this); - + metricsThread = new Thread(() -> { + try { + Thread.sleep(TimeUnit.MINUTES.toMillis(1)); + + while (true) { + if (serverInterface.isMetricsEnabled(config.isMetricsEnabled())) Metrics.sendReport("Sponge"); + Thread.sleep(TimeUnit.MINUTES.toMillis(30)); + } + } catch (InterruptedException ex){ + return; + } + }); + metricsThread.start(); + loaded = true; } public synchronized void unload() { + //unregister listeners + serverInterface.unregisterAllListeners(); + + //stop scheduled threads + if (metricsThread != null) metricsThread.interrupt(); + metricsThread = null; + + //stop services + if (renderManager != null) renderManager.stop(); + if (webServer != null) webServer.close(); + + //save render-manager state + if (updateHandler != null) updateHandler.flushTileBuffer(); //first write all buffered tiles to the render manager to save them too + if (renderManager != null) { + try { + File saveFile = config.getDataPath().resolve("rmstate").toFile(); + saveFile.getParentFile().mkdirs(); + if (saveFile.exists()) saveFile.delete(); + saveFile.createNewFile(); + + try (DataOutputStream out = new DataOutputStream(new GZIPOutputStream(new FileOutputStream(saveFile)))) { + renderManager.writeState(out); + } + } catch (IOException ex) { + Logger.global.logError("Failed to save render-manager state!", ex); + } + } + + //save renders + for (MapType map : maps.values()) { + map.getTileRenderer().save(); + } + + //clear resources and configs + renderManager = null; + webServer = null; + updateHandler = null; + resourcePack = null; + config = null; + maps.clear(); + worlds.clear(); + + loaded = false; } - public synchronized void reload() { + public synchronized void reload() throws IOException, ParseResourceException { unload(); load(); } diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java similarity index 83% rename from BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java index ee0679cf..ee8ead13 100644 --- a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java @@ -1,4 +1,4 @@ -package de.bluecolored.bluemap.plugin.serverinterface; +package de.bluecolored.bluemap.common.plugin.serverinterface; import java.util.UUID; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java new file mode 100644 index 00000000..126be964 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java @@ -0,0 +1,42 @@ +package de.bluecolored.bluemap.common.plugin.serverinterface; + +import java.io.File; +import java.io.IOException; +import java.util.UUID; + +public interface ServerInterface { + + /** + * Registers a ServerEventListener, every method of this interface should be called on the specified events + */ + void registerListener(ServerEventListener listener); + + /** + * Removes all registered listeners + */ + 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 Folder containing the configurations for the plugin + */ + File getConfigFolder(); + + /** + * Gives the possibility to override the metrics-setting in the config + */ + default boolean isMetricsEnabled(boolean configValue) { + return configValue; + } + + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/Text.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/Text.java new file mode 100644 index 00000000..ebc3dea9 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/Text.java @@ -0,0 +1,202 @@ +package de.bluecolored.bluemap.common.plugin.text; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Text { + + private String content = ""; + private TextColor color; + private Set formats = new HashSet<>(); + private Text hoverText; + private String clickCommand; + private List children = new ArrayList<>(); + + public void setHoverText(Text hoverText) { + this.hoverText = hoverText; + } + + public void setClickCommand(String clickCommand) { + this.clickCommand = clickCommand; + } + + public void addChild(Text child) { + children.add(child); + } + + public String toJSONString() { + StringBuilder sb = new StringBuilder(); + sb.append("{"); + + if (!content.isEmpty()) { + sb.append(quote("text")).append(":").append(quote(content)).append(','); + } + + if (color != null) { + sb.append(quote("color")).append(":").append(quote(color.getId())).append(','); + } + + for (TextFormat format : formats) { + sb.append(quote(format.getId())).append(":").append(true).append(','); + } + + if (hoverText != null) { + sb.append(quote("hoverEvent")).append(":{"); + sb.append(quote("action")).append(":").append(quote("show_text")).append(','); + sb.append(quote("value")).append(":").append(quote(hoverText.toFormattingCodedString('§'))); + sb.append("}"); + } + + if (clickCommand != null) { + sb.append(quote("clickEvent")).append(":{"); + sb.append(quote("action")).append(":").append(quote("run_command")).append(','); + sb.append(quote("value")).append(":").append(quote(clickCommand)); + sb.append("}"); + } + + if (!children.isEmpty()) { + sb.append(quote("")).append(":["); + for (Text child : children) { + sb.append(child.toJSONString()).append(','); + } + sb.deleteCharAt(sb.length() - 1); //delete last , + sb.append("]"); + } + + if (sb.charAt(sb.length() - 1) == ',') sb.deleteCharAt(sb.length() - 1); //delete last , + + sb.append("}"); + return sb.toString(); + } + + public String toFormattingCodedString(char escapeChar) { + StringBuilder sb = new StringBuilder(); + + if (!content.isEmpty()) { + if (color != null) { + sb.append(escapeChar).append(color.getFormattingCode()); + } + + for (TextFormat format : formats) { + sb.append(escapeChar).append(format.getFormattingCode()); + } + + sb.append(content); + } + + for (Text child : children) { + sb.append(escapeChar).append('r').append(child.withParentFormat(this).toFormattingCodedString(escapeChar)); + } + + return sb.toString(); + } + + public String toPlainString() { + StringBuilder sb = new StringBuilder(); + + if (content != null) sb.append(content); + for (Text child : children) { + sb.append(child.toPlainString()); + } + + return sb.toString(); + } + + private Text withParentFormat(Text parent) { + Text text = new Text(); + + text.content = this.content; + text.clickCommand = this.clickCommand; + text.children = this.children; + + text.color = this.color != null ? this.color : parent.color; + + text.formats.addAll(this.formats); + text.formats.addAll(parent.formats); + + return text; + } + + private String quote(String value) { + return '"' + escape(value) + '"'; + } + + private String escape(String value) { + value = value.replace("\\", "\\\\"); + value = value.replace("\"", "\\\""); + value = value.replace("§", "\\u00a76"); + value = value.replace("\n", "\\n"); + return value; + } + + @Override + public String toString() { + return getClass().getSimpleName() + ":" + toJSONString(); + } + + public static Text of(String message) { + Text text = new Text(); + text.content = message; + return text; + } + + public static Text of(TextColor color, String message) { + Text text = new Text(); + text.content = message; + text.color = color; + return text; + } + + public static Text of(Object... objects) { + Text text = new Text(); + + Text currentChild = new Text(); + for (Object object : objects) { + + if (object instanceof Text) { + if (!currentChild.content.isEmpty()) { + text.addChild(currentChild); + currentChild = new Text().withParentFormat(currentChild); + } + + text.addChild((Text) object); + continue; + } + + if (object instanceof TextColor) { + if (!currentChild.content.isEmpty()) { + text.addChild(currentChild); + currentChild = new Text(); + } + + currentChild.color = (TextColor) object; + continue; + } + + if (object instanceof TextFormat) { + if (!currentChild.content.isEmpty()) { + text.addChild(currentChild); + currentChild = new Text().withParentFormat(currentChild); + } + + currentChild.formats.add((TextFormat) object); + continue; + } + + currentChild.content += object.toString(); + + } + + if (!currentChild.content.isEmpty()) { + text.addChild(currentChild); + } + + if (text.children.isEmpty()) return text; + if (text.children.size() == 1) return text.children.get(1); + + return text; + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/TextColor.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/TextColor.java new file mode 100644 index 00000000..cf4c7ce5 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/TextColor.java @@ -0,0 +1,38 @@ +package de.bluecolored.bluemap.common.plugin.text; + +public enum TextColor { + + BLACK ("black", '0'), + DARK_BLUE ("dark_blue", '1'), + DARK_GREEN ("dark_green", '2'), + DARK_AQUA ("dark_aqua", '3'), + DARK_RED ("dark_red", '4'), + DARK_PURPLE ("dark_purple", '5'), + GOLD ("gold", '6'), + GRAY ("gray", '7'), + DARK_GRAY ("dark_gray", '8'), + BLUE ("blue", '9'), + GREEN ("green", 'a'), + AQUA ("aqua", 'b'), + RED ("red", 'c'), + LIGHT_PURPLE ("light_purple", 'd'), + YELLOW ("yellow", 'e'), + WHITE ("white", 'f'); + + private final String id; + private final char formattingCode; + + private TextColor(String id, char formattingCode) { + this.id = id; + this.formattingCode = formattingCode; + } + + public String getId() { + return id; + } + + public char getFormattingCode() { + return formattingCode; + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/TextFormat.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/TextFormat.java new file mode 100644 index 00000000..d7da1d4a --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/TextFormat.java @@ -0,0 +1,27 @@ +package de.bluecolored.bluemap.common.plugin.text; + +public enum TextFormat { + + OBFUSCATED ("obfuscated", 'k'), + BOLD ("bold", 'l'), + STRIKETHROUGH ("strikethrough", 'm'), + UNDERLINED ("underlined", 'n'), + ITALIC ("italic", 'o'); + + private final String id; + private final char formattingCode; + + private TextFormat(String id, char formattingCode) { + this.id = id; + this.formattingCode = formattingCode; + } + + public String getId() { + return id; + } + + public char getFormattingCode() { + return formattingCode; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java index 0dd3a79c..e020c3c3 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java @@ -385,7 +385,10 @@ public static MCAWorld load(Path worldFolder, UUID uuid, BlockIdMapper blockIdMa ); try { - ListTag> blockIdReg = level.getCompoundTag("FML").getCompoundTag("Registries").getCompoundTag("minecraft:blocks").getListTag("ids"); + CompoundTag fmlTag = level.getCompoundTag("FML"); + if (fmlTag == null) fmlTag = level.getCompoundTag("fml"); + + ListTag> blockIdReg = fmlTag.getCompoundTag("Registries").getCompoundTag("minecraft:blocks").getListTag("ids"); for (Tag tag : blockIdReg) { if (tag instanceof CompoundTag) { CompoundTag entry = (CompoundTag) tag; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModelManager.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModelManager.java index 2f487667..137308dc 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModelManager.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModelManager.java @@ -31,6 +31,9 @@ import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.zip.GZIPOutputStream; @@ -107,6 +110,29 @@ private void save(HiresModel model, String modelJson){ } } + /** + * Returns all tiles that the provided chunks are intersecting + */ + public Collection getTilesForChunks(Iterable chunks){ + Set tiles = new HashSet<>(); + for (Vector2i chunk : chunks) { + Vector3i minBlockPos = new Vector3i(chunk.getX() * 16, 0, chunk.getY() * 16); + + //loop to cover the case that a tile is smaller then a chunk, should normally only add one tile (at 0, 0) + for (int x = 0; x < 15; x += getTileSize().getX()) { + for (int z = 0; z < 15; z += getTileSize().getY()) { + tiles.add(posToTile(minBlockPos.add(x, 0, z))); + } + } + + tiles.add(posToTile(minBlockPos.add(0, 0, 15))); + tiles.add(posToTile(minBlockPos.add(15, 0, 0))); + tiles.add(posToTile(minBlockPos.add(15, 0, 15))); + } + + return tiles; + } + /** * Returns the region of blocks that a tile includes */ diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java deleted file mode 100644 index e4a5d58f..00000000 --- a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java +++ /dev/null @@ -1,106 +0,0 @@ -package de.bluecolored.bluemap.plugin.serverinterface; - -import org.apache.commons.lang3.StringUtils; - -import net.querz.nbt.CompoundTag; -import net.querz.nbt.ListTag; -import net.querz.nbt.Tag; - -public interface CommandSource { - - default void sendMessage(String plainMessage) { - CompoundTag textTag = new CompoundTag(); - textTag.putString("text", plainMessage); - sendSerializedMessage(textTag); - } - - default void sendMessage(String plainMessage, char colorCodePrefix) { - sendSerializedMessage(parseColorCodes(plainMessage, colorCodePrefix)); - } - - default void sendMessage(Iterable> textTags) { - ListTag> textListTag = new ListTag<>(Tag.class); - for (Tag textTag : textTags) { - textListTag.add(textTag); - } - sendSerializedMessage(textListTag); - } - - default void sendMessage(Tag... textTags) { - ListTag> textListTag = new ListTag<>(Tag.class); - for (Tag textTag : textTags) { - textListTag.add(textTag); - } - sendSerializedMessage(textListTag); - } - - void sendSerializedMessage(Tag textListTag); - - static Tag parseColorCodes(String plainMessage, char colorCodePrefix) { - ListTag> tagList = new ListTag<>(Tag.class); - tagList.addString(""); - - if (plainMessage.isEmpty()) return tagList; - - TextColor color = TextColor.UNDEFINED; - boolean bold = false; - boolean italic = false; - boolean obfuscated = false; - boolean strikethrough = false; - boolean underlined = false; - - plainMessage = "r" + plainMessage; - - String[] parts = StringUtils.split(plainMessage, colorCodePrefix); - - for (String part : parts) { - if (part.isEmpty()) throw new IllegalArgumentException("There is a color code with no color-char!"); - String message = part.substring(1); - char code = part.charAt(0); - - switch (code) { - case 'r': - color = TextColor.UNDEFINED; - bold = false; - italic = false; - obfuscated = false; - strikethrough = false; - underlined = false; - case 'k': - obfuscated = true; - break; - case 'l': - bold = true; - break; - case 'm': - strikethrough = true; - break; - case 'n': - underlined = true; - break; - case 'o': - italic = true; - break; - default: - color = TextColor.ofColorCode(code); - break; - } - - if (message.isEmpty()) continue; - - CompoundTag textTag = new CompoundTag(); - textTag.putString("text", message); - if (color != TextColor.UNDEFINED) textTag.putString("color", color.getId()); - if (bold) textTag.putBoolean("bold", true); - if (italic) textTag.putBoolean("italic", true); - if (underlined) textTag.putBoolean("underlined", true); - if (strikethrough) textTag.putBoolean("strikethrough", true); - if (obfuscated) textTag.putBoolean("obfuscated", true); - - - } - - return tagList; - } - -} diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java deleted file mode 100644 index df42f064..00000000 --- a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java +++ /dev/null @@ -1,24 +0,0 @@ -package de.bluecolored.bluemap.plugin.serverinterface; - -import java.io.File; -import java.util.concurrent.ExecutorService; - -import de.bluecolored.bluemap.core.world.World; - -public interface ServerInterface { - - void registerListener(ServerEventListener listener); - - void unregisterAllListeners(); - - World createWorld(File worldFolder); - - ExecutorService getSyncExecutorService(); - - ExecutorService getAsyncExecutorService(); - - File getConfigFolder(); - - boolean isMetricsEnabled(boolean configValue); - -} diff --git a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/TextColor.java b/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/TextColor.java deleted file mode 100644 index c5e3281a..00000000 --- a/BlueMapPlugin/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/TextColor.java +++ /dev/null @@ -1,47 +0,0 @@ -package de.bluecolored.bluemap.plugin.serverinterface; - -public enum TextColor { - - UNDEFINED ("undefined", 'r'), - WHITE ("white", 'f'), - BLACK ("black", '0'), - YELLOW ("yellow", 'e'), - GOLD ("gold", '6'), - AQUA ("aqua", 'b'), - DARK_AQUA ("dark_aqua", '3'), - BLUE ("blue", '9'), - DARK_BLUE ("dark_blue", '1'), - LIGHT_PURPLE ("light_purple", 'd'), - DARK_PURPLE ("dark_purple", '5'), - RED ("red", 'c'), - DARK_RED ("dark_red", '4'), - GREEN ("green", 'a'), - DARK_GREEN ("dark_green", '2'), - GRAY ("gray", '7'), - DARK_GRAY ("dark_gray", '8'); - - private final String id; - private final char colorCode; - - private TextColor(String id, char colorCode) { - this.id = id; - this.colorCode = colorCode; - } - - public String getId() { - return id; - } - - public char getColorCode() { - return colorCode; - } - - public static TextColor ofColorCode(char code) { - for (TextColor color : values()) { - if (color.colorCode == code) return color; - } - - throw new IllegalArgumentException("'" + code + "' isn't a valid color-code!"); - } - -} diff --git a/BlueMapSponge/build.gradle b/BlueMapSponge/build.gradle index 0c96c272..88c19eb9 100644 --- a/BlueMapSponge/build.gradle +++ b/BlueMapSponge/build.gradle @@ -1,5 +1,5 @@ dependencies { shadow "org.spongepowered:spongeapi:7.1.0-SNAPSHOT" compile group: 'org.bstats', name: 'bstats-sponge-lite', version: '1.5' - compile project(':BlueMapPlugin') + compile project(':BlueMapCommon') } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java index e4d6cec4..b067cb8f 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java @@ -3,10 +3,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.UUID; import org.apache.commons.lang3.time.DurationFormatUtils; @@ -24,9 +22,12 @@ import com.flowpowered.math.GenericMath; import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; import com.google.common.collect.Lists; +import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.common.RenderManager; +import de.bluecolored.bluemap.common.RenderTask; +import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.mca.Chunk; import de.bluecolored.bluemap.core.mca.ChunkAnvil112; @@ -34,16 +35,15 @@ import de.bluecolored.bluemap.core.render.hires.HiresModelManager; import de.bluecolored.bluemap.core.world.Block; import de.bluecolored.bluemap.core.world.World; -import de.bluecolored.bluemap.plugin.MapType; -import de.bluecolored.bluemap.plugin.RenderManager; -import de.bluecolored.bluemap.plugin.RenderTask; public class Commands { private SpongePlugin plugin; + private Plugin bluemap; public Commands(SpongePlugin plugin) { this.plugin = plugin; + this.bluemap = plugin.getBlueMap(); } public CommandSpec createRootCommand() { @@ -56,7 +56,7 @@ public CommandSpec createRootCommand() { if (source instanceof Locatable) { Location loc = ((Locatable) source).getLocation(); UUID worldUuid = loc.getExtent().getUniqueId(); - World world = plugin.getWorld(worldUuid); + World world = bluemap.getWorld(worldUuid); Block block = world.getBlock(loc.getBlockPosition()); Block blockBelow = world.getBlock(loc.getBlockPosition().add(0, -1, 0)); @@ -118,9 +118,9 @@ public CommandSpec createReloadCommand() { plugin.getAsyncExecutor().submit(() -> { try { - plugin.reload(); + bluemap.reload(); - if (plugin.isLoaded()) { + if (bluemap.isLoaded()) { source.sendMessage(Text.of(TextColors.GREEN, "BlueMap reloaded!")); } else { source.sendMessage(Text.of(TextColors.RED, "Could not load BlueMap! See the console for details!")); @@ -143,8 +143,8 @@ public CommandSpec createPauseRenderCommand() { .description(Text.of("Pauses all rendering")) .permission("bluemap.pause") .executor((source, args) -> { - if (plugin.getRenderManager().isRunning()) { - plugin.getRenderManager().stop(); + if (bluemap.getRenderManager().isRunning()) { + bluemap.getRenderManager().stop(); source.sendMessage(Text.of(TextColors.GREEN, "BlueMap rendering paused!")); return CommandResult.success(); } else { @@ -160,8 +160,8 @@ public CommandSpec createResumeRenderCommand() { .description(Text.of("Resumes all paused rendering")) .permission("bluemap.resume") .executor((source, args) -> { - if (!plugin.getRenderManager().isRunning()) { - plugin.getRenderManager().start(); + if (!bluemap.getRenderManager().isRunning()) { + bluemap.getRenderManager().start(); source.sendMessage(Text.of(TextColors.GREEN, "BlueMap renders resumed!")); return CommandResult.success(); } else { @@ -193,7 +193,7 @@ public CommandSpec createRenderCommand() { return CommandResult.empty(); } - World world = plugin.getWorld(spongeWorld.getUniqueId()); + World world = bluemap.getWorld(spongeWorld.getUniqueId()); if (world == null) { source.sendMessage(Text.of(TextColors.RED, "This world is not loaded with BlueMap! Maybe it is not configured?")); } @@ -222,9 +222,9 @@ public CommandSpec createPrioritizeTaskCommand() { return CommandResult.empty(); } - for (RenderTask task : plugin.getRenderManager().getRenderTasks()) { + for (RenderTask task : bluemap.getRenderManager().getRenderTasks()) { if (task.getUuid().equals(uuid.get())) { - plugin.getRenderManager().prioritizeRenderTask(task); + bluemap.getRenderManager().prioritizeRenderTask(task); break; } } @@ -247,9 +247,9 @@ public CommandSpec createRemoveTaskCommand() { return CommandResult.empty(); } - for (RenderTask task : plugin.getRenderManager().getRenderTasks()) { + for (RenderTask task : bluemap.getRenderManager().getRenderTasks()) { if (task.getUuid().equals(uuid.get())) { - plugin.getRenderManager().removeRenderTask(task); + bluemap.getRenderManager().removeRenderTask(task); break; } } @@ -263,7 +263,7 @@ public CommandSpec createRemoveTaskCommand() { private List createStatusMessage(){ List lines = new ArrayList<>(); - RenderManager renderer = plugin.getRenderManager(); + RenderManager renderer = bluemap.getRenderManager(); lines.add(Text.EMPTY); lines.add(Text.of(TextColors.BLUE, "Tile-Updates:")); @@ -274,7 +274,7 @@ private List createStatusMessage(){ lines.add(Text.of(TextColors.WHITE, " Render-Threads are ", Text.of(TextActions.runCommand("/bluemap resume"), TextActions.showText(Text.of("click to resume rendering")), TextColors.RED, "paused"), TextColors.GRAY, "!")); } - lines.add(Text.of(TextColors.WHITE, " Scheduled tile-updates: ", Text.of(TextActions.showText(Text.of("tiles waiting for a free render-thread")), TextColors.GOLD, renderer.getQueueSize()), Text.of(TextActions.showText(Text.of("tiles waiting for world-save")), TextColors.GRAY, " + " + plugin.getUpdateHandler().getUpdateBufferCount()))); + lines.add(Text.of(TextColors.WHITE, " Scheduled tile-updates: ", Text.of(TextActions.showText(Text.of("tiles waiting for a free render-thread")), TextColors.GOLD, renderer.getQueueSize()), Text.of(TextActions.showText(Text.of("tiles waiting for world-save")), TextColors.GRAY, " + " + bluemap.getUpdateHandler().getUpdateBufferCount()))); RenderTask[] tasks = renderer.getRenderTasks(); if (tasks.length > 0) { @@ -319,25 +319,18 @@ private void createWorldRenderTask(CommandSource source, World world) { Collection chunks = world.getChunkList(); source.sendMessage(Text.of(TextColors.GREEN, chunks.size() + " chunks found!")); - for (MapType map : SpongePlugin.getInstance().getMapTypes()) { + for (MapType map : bluemap.getMapTypes()) { if (!map.getWorld().getUUID().equals(world.getUUID())) continue; source.sendMessage(Text.of(TextColors.GOLD, "Collecting tiles for map '" + map.getId() + "'")); HiresModelManager hmm = map.getTileRenderer().getHiresModelManager(); - Set tiles = new HashSet<>(); - for (Vector2i chunk : chunks) { - Vector3i minBlockPos = new Vector3i(chunk.getX() * 16, 0, chunk.getY() * 16); - tiles.add(hmm.posToTile(minBlockPos)); - tiles.add(hmm.posToTile(minBlockPos.add(0, 0, 15))); - tiles.add(hmm.posToTile(minBlockPos.add(15, 0, 0))); - tiles.add(hmm.posToTile(minBlockPos.add(15, 0, 15))); - } + Collection tiles = hmm.getTilesForChunks(chunks); RenderTask task = new RenderTask("world-render", map); task.addTiles(tiles); task.optimizeQueue(); - plugin.getRenderManager().addRenderTask(task); + bluemap.getRenderManager().addRenderTask(task); source.sendMessage(Text.of(TextColors.GREEN, tiles.size() + " tiles found! Task created.")); } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java new file mode 100644 index 00000000..06bd3022 --- /dev/null +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java @@ -0,0 +1,76 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.sponge; + +import java.util.Optional; + +import org.spongepowered.api.block.BlockSnapshot; +import org.spongepowered.api.data.Transaction; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.block.ChangeBlockEvent; +import org.spongepowered.api.event.filter.type.Exclude; +import org.spongepowered.api.event.world.SaveWorldEvent; +import org.spongepowered.api.event.world.chunk.PopulateChunkEvent; +import org.spongepowered.api.world.Location; + +import com.flowpowered.math.vector.Vector2i; +import com.flowpowered.math.vector.Vector3i; + +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; + +public class EventForwarder { + + private ServerEventListener listener; + + public EventForwarder(ServerEventListener listener) { + this.listener = listener; + } + + @Listener(order = Order.POST) + public void onWorldSaveToDisk(SaveWorldEvent evt) { + listener.onWorldSaveToDisk(evt.getTargetWorld().getUniqueId()); + } + + @Listener(order = Order.POST) + @Exclude({ChangeBlockEvent.Post.class, ChangeBlockEvent.Pre.class, ChangeBlockEvent.Modify.class}) + public void onBlockChange(ChangeBlockEvent evt) { + for (Transaction tr : evt.getTransactions()) { + if(!tr.isValid()) continue; + + Optional> ow = tr.getFinal().getLocation(); + if (ow.isPresent()) { + listener.onBlockChange(ow.get().getExtent().getUniqueId(), ow.get().getPosition().toInt()); + } + } + } + + @Listener(order = Order.POST) + public void onChunkFinishedGeneration(PopulateChunkEvent.Post evt) { + Vector3i chunkPos = evt.getTargetChunk().getPosition(); + listener.onChunkFinishedGeneration(evt.getTargetChunk().getWorld().getUniqueId(), new Vector2i(chunkPos.getX(), chunkPos.getZ())); + } + +} diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/MapUpdateHandler.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/MapUpdateHandler.java deleted file mode 100644 index 6cf59554..00000000 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/MapUpdateHandler.java +++ /dev/null @@ -1,128 +0,0 @@ -package de.bluecolored.bluemap.sponge; - -import java.util.Iterator; -import java.util.Optional; -import java.util.UUID; - -import org.spongepowered.api.Sponge; -import org.spongepowered.api.block.BlockSnapshot; -import org.spongepowered.api.data.Transaction; -import org.spongepowered.api.event.Listener; -import org.spongepowered.api.event.Order; -import org.spongepowered.api.event.block.ChangeBlockEvent; -import org.spongepowered.api.event.filter.type.Exclude; -import org.spongepowered.api.event.world.SaveWorldEvent; -import org.spongepowered.api.event.world.chunk.PopulateChunkEvent; -import org.spongepowered.api.world.Location; - -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; -import com.google.common.collect.Multimap; -import com.google.common.collect.MultimapBuilder; - -import de.bluecolored.bluemap.plugin.MapType; -import de.bluecolored.bluemap.plugin.RenderManager; - -public class MapUpdateHandler { - - public Multimap updateBuffer; - - public MapUpdateHandler() { - updateBuffer = MultimapBuilder.hashKeys().hashSetValues().build(); - - Sponge.getEventManager().registerListeners(SpongePlugin.getInstance(), this); - } - - @Listener(order = Order.POST) - public void onWorldSave(SaveWorldEvent.Post evt) { - UUID worldUuid = evt.getTargetWorld().getUniqueId(); - RenderManager renderManager = SpongePlugin.getInstance().getRenderManager(); - - synchronized (updateBuffer) { - Iterator iterator = updateBuffer.keys().iterator(); - while (iterator.hasNext()) { - MapType map = iterator.next(); - if (map.getWorld().getUUID().equals(worldUuid)) { - renderManager.createTickets(map, updateBuffer.get(map)); - iterator.remove(); - } - } - - } - } - - @Listener(order = Order.POST) - @Exclude({ChangeBlockEvent.Post.class, ChangeBlockEvent.Pre.class, ChangeBlockEvent.Modify.class}) - public void onBlockChange(ChangeBlockEvent evt) { - synchronized (updateBuffer) { - for (Transaction tr : evt.getTransactions()) { - if (!tr.isValid()) continue; - - Optional> ow = tr.getFinal().getLocation(); - if (ow.isPresent()) { - updateBlock(ow.get().getExtent().getUniqueId(), ow.get().getPosition().toInt()); - } - } - } - } - - @Listener(order = Order.POST) - public void onChunkPopulate(PopulateChunkEvent.Post evt) { - UUID world = evt.getTargetChunk().getWorld().getUniqueId(); - - int x = evt.getTargetChunk().getPosition().getX(); - int z = evt.getTargetChunk().getPosition().getZ(); - - // also update the chunks around, because they might be modified or not rendered yet due to finalizations - for (int dx = -1; dx <= 1; dx++) { - for (int dz = -1; dz <= 1; dz++) { - updateChunk(world, new Vector2i(x + dx, z + dz)); - } - } - } - - private void updateChunk(UUID world, Vector2i chunkPos) { - Vector3i min = new Vector3i(chunkPos.getX() * 16, 0, chunkPos.getY() * 16); - Vector3i max = min.add(15, 255, 15); - - Vector3i xmin = new Vector3i(min.getX(), 0, max.getY()); - Vector3i xmax = new Vector3i(max.getX(), 255, min.getY()); - - //update all corners so we always update all tiles containing this chunk - synchronized (updateBuffer) { - updateBlock(world, min); - updateBlock(world, max); - updateBlock(world, xmin); - updateBlock(world, xmax); - } - } - - private void updateBlock(UUID world, Vector3i pos){ - synchronized (updateBuffer) { - for (MapType mapType : SpongePlugin.getInstance().getMapTypes()) { - if (mapType.getWorld().getUUID().equals(world)) { - mapType.getWorld().invalidateChunkCache(mapType.getWorld().blockPosToChunkPos(pos)); - - Vector2i tile = mapType.getTileRenderer().getHiresModelManager().posToTile(pos); - updateBuffer.put(mapType, tile); - } - } - } - } - - public int getUpdateBufferCount() { - return updateBuffer.size(); - } - - public void flushTileBuffer() { - RenderManager renderManager = SpongePlugin.getInstance().getRenderManager(); - - synchronized (updateBuffer) { - for (MapType map : updateBuffer.keySet()) { - renderManager.createTickets(map, updateBuffer.get(map)); - } - updateBuffer.clear(); - } - } - -} diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java index 4438baa4..c7e88a9e 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -24,29 +24,13 @@ */ package de.bluecolored.bluemap.sponge; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.net.URL; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; import javax.inject.Inject; -import org.apache.commons.io.FileUtils; import org.bstats.sponge.MetricsLite2; import org.spongepowered.api.Sponge; import org.spongepowered.api.config.ConfigDir; @@ -57,28 +41,10 @@ import org.spongepowered.api.scheduler.SpongeExecutorService; import org.spongepowered.api.world.storage.WorldProperties; -import com.flowpowered.math.vector.Vector2i; - -import de.bluecolored.bluemap.core.config.ConfigManager; -import de.bluecolored.bluemap.core.config.MainConfig; -import de.bluecolored.bluemap.core.config.MainConfig.MapConfig; +import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.mca.MCAWorld; -import de.bluecolored.bluemap.core.metrics.Metrics; -import de.bluecolored.bluemap.core.render.RenderSettings; -import de.bluecolored.bluemap.core.render.TileRenderer; -import de.bluecolored.bluemap.core.render.hires.HiresModelManager; -import de.bluecolored.bluemap.core.render.lowres.LowresModelManager; -import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; -import de.bluecolored.bluemap.core.resourcepack.ResourcePack; -import de.bluecolored.bluemap.core.web.BlueMapWebServer; -import de.bluecolored.bluemap.core.web.WebFilesManager; -import de.bluecolored.bluemap.core.web.WebSettings; -import de.bluecolored.bluemap.core.world.SlicedWorld; -import de.bluecolored.bluemap.core.world.World; -import de.bluecolored.bluemap.plugin.MapType; -import de.bluecolored.bluemap.plugin.Plugin; -import de.bluecolored.bluemap.plugin.RenderManager; import net.querz.nbt.CompoundTag; import net.querz.nbt.NBTUtil; @@ -89,10 +55,8 @@ description = "This plugin provides a fully 3D map of your world for your browser!", version = Plugin.PLUGIN_VERSION ) -public class SpongePlugin { +public class SpongePlugin implements ServerInterface { - private static SpongePlugin instance; - @Inject @ConfigDir(sharedRoot = false) private Path configurationDir; @@ -101,261 +65,19 @@ public class SpongePlugin { @Inject private MetricsLite2 metrics; - private MainConfig config; - private ResourcePack resourcePack; - - private Map worlds; - private Map maps; - - private RenderManager renderManager; - private MapUpdateHandler updateHandler; - private BlueMapWebServer webServer; - - private SpongeExecutorService syncExecutor; - private SpongeExecutorService asyncExecutor; + private Plugin bluemap; - private boolean loaded = false; + private SpongeExecutorService asyncExecutor; @Inject public SpongePlugin(org.slf4j.Logger logger) { Logger.global = new Slf4jLogger(logger); - this.maps = new HashMap<>(); - this.worlds = new HashMap<>(); - - instance = this; - } - - public synchronized void load() throws ExecutionException, IOException, InterruptedException, ParseResourceException { - if (loaded) return; - unload(); //ensure nothing is left running (from a failed load or something) - - //register reload command in case bluemap crashes during loading - Sponge.getCommandManager().register(this, new Commands(this).createStandaloneReloadCommand(), "bluemap"); - - //load configs - URL defaultSpongeConfig = SpongePlugin.class.getResource("/bluemap-sponge.conf"); - URL spongeConfigDefaults = SpongePlugin.class.getResource("/bluemap-sponge-defaults.conf"); - ConfigManager configManager = new ConfigManager(getConfigPath().toFile(), defaultSpongeConfig, spongeConfigDefaults); - configManager.loadMainConfig(); - config = configManager.getMainConfig(); - - //load resources - File defaultResourceFile = config.getDataPath().resolve("minecraft-client-" + ResourcePack.MINECRAFT_CLIENT_VERSION + ".jar").toFile(); - File resourceExtensionsFile = config.getDataPath().resolve("resourceExtensions.zip").toFile(); - File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile(); - - if (!defaultResourceFile.exists()) { - handleMissingResources(defaultResourceFile, configManager.getMainConfigFile()); - unload(); - - Sponge.getCommandManager().register(this, new Commands(this).createStandaloneReloadCommand(), "bluemap"); - return; - } - - resourceExtensionsFile.delete(); - FileUtils.copyURLToFile(SpongePlugin.class.getResource("/resourceExtensions.zip"), resourceExtensionsFile, 10000, 10000); - - //find more resource packs - File resourcePackFolder = getConfigPath().resolve("resourcepacks").toFile(); - resourcePackFolder.mkdirs(); - File[] resourcePacks = resourcePackFolder.listFiles(); - Arrays.sort(resourcePacks); //load resource packs in alphabetical order so you can reorder them by renaming - - List resources = new ArrayList<>(resourcePacks.length + 1); - resources.add(defaultResourceFile); - for (File file : resourcePacks) resources.add(file); - resources.add(resourceExtensionsFile); - - resourcePack = new ResourcePack(); - if (textureExportFile.exists()) resourcePack.loadTextureFile(textureExportFile); - resourcePack.load(resources); - resourcePack.saveTextureFile(textureExportFile); - - configManager.loadResourceConfigs(resourcePack); - - //load maps - for (MapConfig mapConfig : config.getMapConfigs()) { - String id = mapConfig.getId(); - String name = mapConfig.getName(); - - File worldFolder = new File(mapConfig.getWorldPath()); - if (!worldFolder.exists() || !worldFolder.isDirectory()) { - Logger.global.logError("Failed to load map '" + id + "': '" + worldFolder.getCanonicalPath() + "' does not exist or is no directory!", new IOException()); - continue; - } - - UUID worldUUID; - try { - CompoundTag levelSponge = (CompoundTag) NBTUtil.readTag(new File(worldFolder, "level_sponge.dat")); - CompoundTag spongeData = levelSponge.getCompoundTag("SpongeData"); - long most = spongeData.getLong("UUIDMost"); - long least = spongeData.getLong("UUIDLeast"); - worldUUID = new UUID(most, least); - } catch (Exception e) { - Logger.global.logError("Failed to load map '" + id + "': Failed to read level_sponge.dat", e); - continue; - } - - World world = worlds.get(worldUUID); - if (world == null) { - try { - world = MCAWorld.load(worldFolder.toPath(), worldUUID, configManager.getBlockIdConfig(), configManager.getBlockPropertiesConfig(), configManager.getBiomeConfig()); - worlds.put(worldUUID, world); - } catch (IOException e) { - Logger.global.logError("Failed to load map '" + id + "': Failed to read level.dat", e); - continue; - } - } - - //slice world to render edges if configured - if (mapConfig.isRenderEdges() && !(mapConfig.getMin().equals(RenderSettings.DEFAULT_MIN) && mapConfig.getMax().equals(RenderSettings.DEFAULT_MAX))) { - world = new SlicedWorld(world, mapConfig.getMin(), mapConfig.getMax()); - } - - HiresModelManager hiresModelManager = new HiresModelManager( - config.getWebDataPath().resolve(id).resolve("hires"), - resourcePack, - mapConfig, - new Vector2i(mapConfig.getHiresTileSize(), mapConfig.getHiresTileSize()), - getAsyncExecutor() - ); - - LowresModelManager lowresModelManager = new LowresModelManager( - config.getWebDataPath().resolve(id).resolve("lowres"), - new Vector2i(mapConfig.getLowresPointsPerLowresTile(), mapConfig.getLowresPointsPerLowresTile()), - new Vector2i(mapConfig.getLowresPointsPerHiresTile(), mapConfig.getLowresPointsPerHiresTile()) - ); - - TileRenderer tileRenderer = new TileRenderer(hiresModelManager, lowresModelManager); - - MapType mapType = new MapType(id, name, world, tileRenderer); - maps.put(id, mapType); - } - - //initialize render manager - renderManager = new RenderManager(config.getRenderThreadCount()); - renderManager.start(); - - //load render-manager state - try { - File saveFile = config.getDataPath().resolve("rmstate").toFile(); - saveFile.getParentFile().mkdirs(); - if (saveFile.exists()) { - try (DataInputStream in = new DataInputStream(new GZIPInputStream(new FileInputStream(saveFile)))) { - renderManager.readState(in, getMapTypes()); - } - } - saveFile.delete(); - } catch (IOException ex) { - Logger.global.logError("Failed to load render-manager state!", ex); - } - - //start map updater - this.updateHandler = new MapUpdateHandler(); - - //create/update webfiles - WebFilesManager webFilesManager = new WebFilesManager(config.getWebRoot()); - if (webFilesManager.needsUpdate()) { - webFilesManager.updateFiles(); - } - - WebSettings webSettings = new WebSettings(config.getWebDataPath().resolve("settings.json").toFile()); - webSettings.setAllEnabled(false); - for (MapType map : maps.values()) { - webSettings.setEnabled(true, map.getId()); - webSettings.setName(map.getName(), map.getId()); - webSettings.setFrom(map.getTileRenderer(), map.getId()); - } - int ordinal = 0; - for (MapConfig map : config.getMapConfigs()) { - if (!maps.containsKey(map.getId())) continue; //don't add not loaded maps - webSettings.setOrdinal(ordinal++, map.getId()); - webSettings.setHiresViewDistance(map.getHiresViewDistance(), map.getId()); - webSettings.setLowresViewDistance(map.getLowresViewDistance(), map.getId()); - } - webSettings.save(); - - //start webserver - if (config.isWebserverEnabled()) { - webServer = new BlueMapWebServer(config); - webServer.updateWebfiles(); - webServer.start(); - } - - //init commands - Sponge.getCommandManager().getOwnedBy(this).forEach(Sponge.getCommandManager()::removeMapping); - Sponge.getCommandManager().register(this, new Commands(this).createRootCommand(), "bluemap"); - - //metrics - Sponge.getScheduler().createTaskBuilder() - .async() - .delay(1, TimeUnit.MINUTES) - .interval(30, TimeUnit.MINUTES) - .execute(() -> { - if (Sponge.getMetricsConfigManager().areMetricsEnabled(this)) Metrics.sendReport("Sponge"); - }) - .submit(this); - - loaded = true; - } - - public synchronized void unload() { - //unregister commands - Sponge.getCommandManager().getOwnedBy(this).forEach(Sponge.getCommandManager()::removeMapping); - - //unregister listeners - if (updateHandler != null) Sponge.getEventManager().unregisterListeners(updateHandler); - - //stop scheduled tasks - Sponge.getScheduler().getScheduledTasks(this).forEach(t -> t.cancel()); - - //stop services - if (renderManager != null) renderManager.stop(); - if (webServer != null) webServer.close(); - - //save render-manager state - if (updateHandler != null) updateHandler.flushTileBuffer(); //first write all buffered tiles to the render manager to save them too - if (renderManager != null) { - try { - File saveFile = config.getDataPath().resolve("rmstate").toFile(); - saveFile.getParentFile().mkdirs(); - if (saveFile.exists()) saveFile.delete(); - saveFile.createNewFile(); - - try (DataOutputStream out = new DataOutputStream(new GZIPOutputStream(new FileOutputStream(saveFile)))) { - renderManager.writeState(out); - } - } catch (IOException ex) { - Logger.global.logError("Failed to save render-manager state!", ex); - } - } - - //save renders - for (MapType map : maps.values()) { - map.getTileRenderer().save(); - } - - //clear resources and configs - renderManager = null; - webServer = null; - updateHandler = null; - resourcePack = null; - config = null; - maps.clear(); - worlds.clear(); - - loaded = false; - } - - public synchronized void reload() throws IOException, ExecutionException, InterruptedException, ParseResourceException { - unload(); - load(); + this.bluemap = new Plugin("sponge", this); } @Listener public void onServerStart(GameStartingServerEvent evt) { - syncExecutor = Sponge.getScheduler().createSyncExecutor(this); asyncExecutor = Sponge.getScheduler().createAsyncExecutor(this); //save all world properties to generate level_sponge.dat files @@ -363,19 +85,23 @@ public void onServerStart(GameStartingServerEvent evt) { Sponge.getServer().saveWorldProperties(properties); } + Sponge.getCommandManager().register(this, new Commands(this).createRootCommand(), "bluemap"); + asyncExecutor.execute(() -> { try { - load(); - if (isLoaded()) Logger.global.logInfo("Loaded!"); - } catch (Exception e) { - Logger.global.logError("Failed to load!", e); + Logger.global.logInfo("Loading..."); + bluemap.load(); + if (bluemap.isLoaded()) Logger.global.logInfo("Loaded!"); + } catch (Throwable t) { + Logger.global.logError("Failed to load!", t); } }); } @Listener public void onServerStop(GameStoppingEvent evt) { - unload(); + Logger.global.logInfo("Stopping..."); + bluemap.unload(); Logger.global.logInfo("Saved and stopped!"); } @@ -384,80 +110,48 @@ public void onServerReload(GameReloadEvent evt) { asyncExecutor.execute(() -> { try { Logger.global.logInfo("Reloading..."); - reload(); + bluemap.reload(); Logger.global.logInfo("Reloaded!"); } catch (Exception e) { Logger.global.logError("Failed to load!", e); } }); } - - private void handleMissingResources(File resourceFile, File mainConfigFile) { - if (config.isDownloadAccepted()) { - - //download file async - asyncExecutor.execute(() -> { - try { - Logger.global.logInfo("Downloading " + ResourcePack.MINECRAFT_CLIENT_URL + " to " + resourceFile + " ..."); - ResourcePack.downloadDefaultResource(resourceFile); - } catch (IOException e) { - Logger.global.logError("Failed to download resources!", e); - return; - } - // and reload - Logger.global.logInfo("Download finished! Reloading..."); - try { - reload(); - } catch (Exception e) { - Logger.global.logError("Failed to reload Bluemap!", e); - return; - } - Logger.global.logInfo("Reloaded!"); - }); - - } else { - Logger.global.logWarning("BlueMap is missing important resources!"); - Logger.global.logWarning("You need to accept the download of the required files in order of BlueMap to work!"); - try { Logger.global.logWarning("Please check: " + mainConfigFile.getCanonicalPath()); } catch (IOException ignored) {} - Logger.global.logInfo("If you have changed the config you can simply reload the plugin using: /bluemap reload"); + @Override + public void registerListener(ServerEventListener listener) { + Sponge.getEventManager().registerListeners(this, new EventForwarder(listener)); + } + + @Override + public void unregisterAllListeners() { + Sponge.getEventManager().unregisterPluginListeners(this); + } + + @Override + public UUID getUUIDForWorld(File worldFolder) throws IOException { + try { + CompoundTag levelSponge = (CompoundTag) NBTUtil.readTag(new File(worldFolder, "level_sponge.dat")); + CompoundTag spongeData = levelSponge.getCompoundTag("SpongeData"); + long most = spongeData.getLong("UUIDMost"); + long least = spongeData.getLong("UUIDLeast"); + return new UUID(most, least); + } catch (Throwable t) { + throw new IOException("Failed to read level_sponge.dat", t); } } - - public SpongeExecutorService getSyncExecutor(){ - return syncExecutor; + + @Override + public File getConfigFolder() { + return configurationDir.toFile(); } - public SpongeExecutorService getAsyncExecutor(){ + public SpongeExecutorService getAsyncExecutor() { return asyncExecutor; } - public World getWorld(UUID uuid){ - return worlds.get(uuid); - } - - public Collection getMapTypes(){ - return maps.values(); - } - - public RenderManager getRenderManager() { - return renderManager; - } - - public MapUpdateHandler getUpdateHandler() { - return updateHandler; - } - - public boolean isLoaded() { - return loaded; - } - - public Path getConfigPath(){ - return configurationDir; - } - - public static SpongePlugin getInstance() { - return instance; + public Plugin getBlueMap() { + return bluemap; } } diff --git a/build.gradle b/build.gradle index 704cb407..018caaed 100644 --- a/build.gradle +++ b/build.gradle @@ -28,10 +28,13 @@ allprojects { dependencies { compile project(':BlueMapCLI') - //compile project(':BlueMapSponge') + compile project(':BlueMapBukkit') + compile project(':BlueMapSponge') } shadowJar { + relocate 'org.bstats.bukkit', 'de.bluecolored.bluemap.bstats.bukkit' + baseName = 'BlueMap' version = null classifier = null diff --git a/settings.gradle b/settings.gradle index 261e1aa9..a481f48e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,12 +1,12 @@ rootProject.name = 'BlueMap' include ':BlueMapCore' include ':BlueMapCLI' -include ':BlueMapPlugin' +include ':BlueMapCommon' include ':BlueMapSponge' include ':BlueMapBukkit' project(':BlueMapCore').projectDir = "$rootDir/BlueMapCore" as File project(':BlueMapCLI').projectDir = "$rootDir/BlueMapCLI" as File -project(':BlueMapPlugin').projectDir = "$rootDir/BlueMapPlugin" as File +project(':BlueMapCommon').projectDir = "$rootDir/BlueMapCommon" as File project(':BlueMapSponge').projectDir = "$rootDir/BlueMapSponge" as File project(':BlueMapBukkit').projectDir = "$rootDir/BlueMapBukkit" as File \ No newline at end of file From 3daab62714f200562d884b0fb03e86d009ac701c Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Fri, 17 Jan 2020 00:21:46 +0100 Subject: [PATCH 5/6] Move commands to common and start implementing spigot plugin --- .../bluemap/bukkit/BukkitCommandSource.java | 31 ++ .../{Commands.java => BukkitCommands.java} | 16 +- .../bluemap/bukkit/BukkitPlugin.java | 12 +- .../resources/bluemap-bukkit-defaults.conf | 10 + .../src/main/resources/bluemap-bukkit.conf | 171 +++++++++ BlueMapBukkit/src/main/resources/plugin.yml | 3 +- .../bluemap/common/plugin/Commands.java | 298 +++++++++++++++ .../bluemap/common/plugin/Plugin.java | 7 + .../plugin/serverinterface/CommandSource.java | 15 + .../bluemap/common/plugin/text/Text.java | 31 +- .../bluecolored/bluemap/sponge/Commands.java | 341 ------------------ .../bluemap/sponge/SpongeCommandSource.java | 46 +++ .../bluemap/sponge/SpongeCommands.java | 170 +++++++++ .../bluemap/sponge/SpongePlugin.java | 10 +- 14 files changed, 783 insertions(+), 378 deletions(-) create mode 100644 BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java rename BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/{Commands.java => BukkitCommands.java} (51%) create mode 100644 BlueMapBukkit/src/main/resources/bluemap-bukkit-defaults.conf create mode 100644 BlueMapBukkit/src/main/resources/bluemap-bukkit.conf create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Commands.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java delete mode 100644 BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java create mode 100644 BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java create mode 100644 BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommands.java diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java new file mode 100644 index 00000000..6b68c6e3 --- /dev/null +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java @@ -0,0 +1,31 @@ +package de.bluecolored.bluemap.bukkit; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.plugin.text.Text; + +public class BukkitCommandSource implements CommandSource { + + private CommandSender delegate; + + public BukkitCommandSource(CommandSender delegate) { + this.delegate = delegate; + } + + @Override + public void sendMessage(Text text) { + if (delegate instanceof Player) { + Player player = (Player) delegate; + + //kinda hacky but works + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + player.getName() + " " + text.toJSONString()); + return; + } + + delegate.sendMessage(text.toFormattingCodedString('§')); + } + +} diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/Commands.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java similarity index 51% rename from BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/Commands.java rename to BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java index 6d261925..ca94fe58 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/Commands.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java @@ -4,21 +4,19 @@ import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; -import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.Commands; -public class Commands implements CommandExecutor { - - private BukkitPlugin plugin; - private Plugin bluemap; +public class BukkitCommands implements CommandExecutor { - public Commands(BukkitPlugin plugin) { - this.plugin = plugin; - this.bluemap = plugin.getBlueMap(); + private Commands commands; + + public BukkitCommands(Commands commands) { + this.commands = commands; } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - + commands.executeRootCommand(new BukkitCommandSource(sender)); return true; } diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index ec7a0cbb..41421fe8 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -17,14 +17,14 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface { private Plugin bluemap; private EventForwarder eventForwarder; - private Commands commands; + private BukkitCommands commands; public BukkitPlugin() { Logger.global = new JavaLogger(getLogger()); this.eventForwarder = new EventForwarder(); - this.commands = new Commands(this); this.bluemap = new Plugin("bukkit", this); + this.commands = new BukkitCommands(bluemap.getCommands()); } @Override @@ -64,16 +64,18 @@ public void unregisterAllListeners() { @Override public UUID getUUIDForWorld(File worldFolder) throws IOException { + worldFolder = worldFolder.getCanonicalFile(); for (World world : getServer().getWorlds()) { - if (worldFolder.equals(world.getWorldFolder())) return world.getUID(); + Logger.global.logInfo("Found world-folder: " + world.getWorldFolder().getCanonicalPath()); + if (worldFolder.equals(world.getWorldFolder().getCanonicalFile())) return world.getUID(); } - throw new IOException("There is no world with this folder loaded!"); + throw new IOException("There is no world with this folder loaded: " + worldFolder.getCanonicalPath()); } @Override public File getConfigFolder() { - return getConfigFolder(); + return getDataFolder(); } public Plugin getBlueMap() { diff --git a/BlueMapBukkit/src/main/resources/bluemap-bukkit-defaults.conf b/BlueMapBukkit/src/main/resources/bluemap-bukkit-defaults.conf new file mode 100644 index 00000000..d8404ad8 --- /dev/null +++ b/BlueMapBukkit/src/main/resources/bluemap-bukkit-defaults.conf @@ -0,0 +1,10 @@ +accept-download: false +metrics: true +renderThreadCount: -2 +data: "bluemap" +webroot: "bluemap/web" +webserver { + enabled: true + port: 8100 + maxConnectionCount: 100 +} diff --git a/BlueMapBukkit/src/main/resources/bluemap-bukkit.conf b/BlueMapBukkit/src/main/resources/bluemap-bukkit.conf new file mode 100644 index 00000000..34d64029 --- /dev/null +++ b/BlueMapBukkit/src/main/resources/bluemap-bukkit.conf @@ -0,0 +1,171 @@ +## ## +## BlueMap ## +## ## +## by Blue (Lukas Rieger) ## +## http://bluecolored.de/ ## +## ## + +# 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 this file for you: %minecraft-client-url% +# (Alternatively you can download the file yourself and store it here: /minecraft-client-%minecraft-client-version%.jar) +# 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 -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/ +# 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":"CLI","version":"%version%"} +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. +data: "bluemap" + +# The webroot of the website that displays the map. +webroot: "bluemap/web" + +# Unncomment this to override the path where bluemap stores the data-files. +# Default is "/data" +#webdata: "path/to/data/folder" + +webserver { + # 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 IP-Adress that the webserver binds to. + # If this setting is commented out, bluemap tries to find the default ip-adress of your system. + # If you only want to access it locally use "localhost". + #ip: "localhost" + #ip: "127.0.0.1" + + # 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 +} + +# 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_] + 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" + + # If this is false, BlueMap tries to omit all blocks that 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. + # Default is false + renderCaves: false + + # AmbientOcclusion adds soft shadows into corners, which gives the map a much better look. + # This has only a small impact on render-time and has no impact on the web-performance of the map. + # The value defines the strength of the shading, a value of 0 disables ambientOcclusion. + # Default is 0.25 + ambientOcclusion: 0.25 + + # Lighting uses the light-data in minecraft to shade each block-face. + # If this is enabled, caves and inside buildings without torches will be darker. + # The value defines the strength of the shading and a value of 0 disables lighting (every block will be fully lit). + # Default is 0.8 + lighting: 0.8 + + # 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. + # 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. + # Default is enabled + renderEdges: true + + # HIRES is the high-resolution render of the map. Where you see every block. + hires { + # Defines the size of one map-tile in blocks. + # If you change this value, the lowres values might need adjustment as well! + # Default is 32 + tileSize: 32 + + # The View-Distance for hires tiles on the web-map (the value is the radius in tiles) + # Default is 4.5 + viewDistance: 4.5 + } + + # LOWRES is the low-resolution render of the map. Thats the model that you see if you zoom far out to get an overview. + lowres { + # Defines resolution of the lowres model. E.g. If the hires.tileSize is 32, a value of 4 means that every 8*8 blocks will be summarized by one point on the lowres map. + # Calculation: 32 / 4 = 8 + # You can only use values that result in an integer if you use the above calculation! + # Default is 4 + pointsPerHiresTile: 4 + + # Defines the size of one lowres-map-tile in points. + # Default is 50 + pointsPerLowresTile: 50 + + # The View-Distance for lowres tiles on the web-map (the value is the radius in tiles) + # Default is 7 + viewDistance: 7 + } + } + + # 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" + + # In the end is no sky-light, so we need to enable this or we won't see anything. + renderCaves: true + + # Same here, we don't want a dark map. But not completely disabled, so we see the effect of e.g torches. + lighting: 0.4 + } + + # Here another example for the Nether-Map + { + id: "nether" + name: "Nether" + world: "world/DIM-1" + + renderCaves: true + lighting: 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 + } + +] diff --git a/BlueMapBukkit/src/main/resources/plugin.yml b/BlueMapBukkit/src/main/resources/plugin.yml index 7a3565e9..71cfbf31 100644 --- a/BlueMapBukkit/src/main/resources/plugin.yml +++ b/BlueMapBukkit/src/main/resources/plugin.yml @@ -1,7 +1,8 @@ name: BlueMap main: de.bluecolored.bluemap.bukkit.BukkitPlugin -version: 0.1.0 +version: 0.2.1 author: Blue (TBlueF / Lukas Rieger) authors: [Blue (TBlueF / Lukas Rieger)] website: "https://github.com/BlueMap-Minecraft" commands: + bluemap: \ No newline at end of file diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Commands.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Commands.java new file mode 100644 index 00000000..82d1b576 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Commands.java @@ -0,0 +1,298 @@ +package de.bluecolored.bluemap.common.plugin; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.UUID; + +import org.apache.commons.lang3.time.DurationFormatUtils; + +import com.flowpowered.math.GenericMath; +import com.flowpowered.math.vector.Vector2i; +import com.flowpowered.math.vector.Vector3i; +import com.google.common.collect.Lists; + +import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.common.RenderManager; +import de.bluecolored.bluemap.common.RenderTask; +import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.plugin.text.TextColor; +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.mca.Chunk; +import de.bluecolored.bluemap.core.mca.ChunkAnvil112; +import de.bluecolored.bluemap.core.mca.MCAWorld; +import de.bluecolored.bluemap.core.render.hires.HiresModelManager; +import de.bluecolored.bluemap.core.world.Block; +import de.bluecolored.bluemap.core.world.World; + +/** + * Commands: + * + *
    + *
  • /bluemap
  • + *
  • /bluemap reload
  • + *
  • /bluemap pause
  • + *
  • /bluemap resume
  • + *
  • /bluemap render [world]
  • + *
  • /bluemap render prioritize [task-uuid]
  • + *
  • /bluemap render remove [task-uuid]
  • + *
  • /bluemap debug
  • + *
+ */ +public class Commands { + + private Plugin bluemap; + + public Commands(Plugin bluemap) { + this.bluemap = bluemap; + } + + /** + * Command: /bluemap + */ + public void executeRootCommand(CommandSource source) { + if (!checkLoaded(source)) return; + + source.sendMessages(createStatusMessage()); + } + + /** + * Command: /bluemap debug + */ + public boolean executeDebugCommand(CommandSource source, UUID worldUuid, Vector3i playerPosition) { + if (!checkLoaded(source)) return false; + + World world = bluemap.getWorld(worldUuid); + + if (world == null) { + source.sendMessage(Text.of(TextColor.RED, "This world is not loaded with BlueMap! Maybe it is not configured?")); + return false; + } + + Block block = world.getBlock(playerPosition); + Block blockBelow = world.getBlock(playerPosition.add(0, -1, 0)); + + String blockIdMeta = ""; + String blockBelowIdMeta = ""; + + if (world instanceof MCAWorld) { + try { + Chunk chunk = ((MCAWorld) world).getChunk(MCAWorld.blockToChunk(playerPosition)); + if (chunk instanceof ChunkAnvil112) { + blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(playerPosition) + ")"; + blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(playerPosition.add(0, -1, 0)) + ")"; + } + } catch (IOException ex) { + Logger.global.logError("Failed to read chunk for debug!", ex); + } + } + + source.sendMessages(Lists.newArrayList( + Text.of(TextColor.GOLD, "Block at you: ", TextColor.WHITE, block, TextColor.GRAY, blockIdMeta), + Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow, TextColor.GRAY, blockBelowIdMeta) + )); + + return true; + } + + /** + * Command: /bluemap reload + */ + public void executeReloadCommand(CommandSource source) { + source.sendMessage(Text.of(TextColor.GOLD, "Reloading BlueMap...")); + + new Thread(() -> { + try { + bluemap.reload(); + + if (bluemap.isLoaded()) { + source.sendMessage(Text.of(TextColor.GREEN, "BlueMap reloaded!")); + } else { + source.sendMessage(Text.of(TextColor.RED, "Could not load BlueMap! See the console for details!")); + } + + } catch (Exception ex) { + Logger.global.logError("Failed to reload BlueMap!", ex); + + source.sendMessage(Text.of(TextColor.RED, "There was an error reloading BlueMap! See the console for details!")); + } + }).start(); + + } + + /** + * Command: /bluemap pause + */ + public boolean executePauseCommand(CommandSource source) { + if (!checkLoaded(source)) return false; + + if (bluemap.getRenderManager().isRunning()) { + bluemap.getRenderManager().stop(); + source.sendMessage(Text.of(TextColor.GREEN, "BlueMap rendering paused!")); + return true; + } else { + source.sendMessage(Text.of(TextColor.RED, "BlueMap rendering are already paused!")); + return false; + } + } + + /** + * Command: /bluemap resume + */ + public boolean executeResumeCommand(CommandSource source) { + if (!checkLoaded(source)) return false; + + if (!bluemap.getRenderManager().isRunning()) { + bluemap.getRenderManager().start(); + source.sendMessage(Text.of(TextColor.GREEN, "BlueMap renders resumed!")); + return true; + } else { + source.sendMessage(Text.of(TextColor.RED, "BlueMap renders are already running!")); + return false; + } + } + + /** + * Command: /bluemap render [world] + */ + public boolean executeRenderWorldCommand(CommandSource source, UUID worldUuid) { + if (!checkLoaded(source)) return false; + + World world = bluemap.getWorld(worldUuid); + + if (world == null) { + source.sendMessage(Text.of(TextColor.RED, "This world is not loaded with BlueMap! Maybe it is not configured?")); + return false; + } + + world.invalidateChunkCache(); + + new Thread(() -> { + createWorldRenderTask(source, world); + }).start(); + + return true; + } + + /** + * Command: /bluemap render prioritize [task-uuid] + */ + public void executePrioritizeRenderTaskCommand(CommandSource source, UUID taskUUID) { + if (!checkLoaded(source)) return; + + for (RenderTask task : bluemap.getRenderManager().getRenderTasks()) { + if (task.getUuid().equals(taskUUID)) { + bluemap.getRenderManager().prioritizeRenderTask(task); + break; + } + } + + source.sendMessages(createStatusMessage()); + } + + /** + * Command: /bluemap render remove [task-uuid] + */ + public void executeRemoveRenderTaskCommand(CommandSource source, UUID taskUUID) { + if (!checkLoaded(source)) return; + + for (RenderTask task : bluemap.getRenderManager().getRenderTasks()) { + if (task.getUuid().equals(taskUUID)) { + bluemap.getRenderManager().removeRenderTask(task); + break; + } + } + + source.sendMessages(createStatusMessage()); + } + + private List createStatusMessage(){ + List lines = new ArrayList<>(); + + RenderManager renderer = bluemap.getRenderManager(); + + lines.add(Text.of()); + lines.add(Text.of(TextColor.BLUE, "Tile-Updates:")); + + if (renderer.isRunning()) { + lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.GREEN, "running").setHoverText(Text.of("click to pause rendering")).setClickCommand("/bluemap pause"), TextColor.GRAY, "!")); + } else { + lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.RED, "paused").setHoverText(Text.of("click to resume rendering")).setClickCommand("/bluemap resume"), TextColor.GRAY, "!")); + } + + lines.add(Text.of(TextColor.WHITE, " Scheduled tile-updates: ", Text.of(TextColor.GOLD, renderer.getQueueSize()).setHoverText(Text.of("tiles waiting for a free render-thread")), TextColor.GRAY, " + " , Text.of(TextColor.GRAY, bluemap.getUpdateHandler().getUpdateBufferCount()).setHoverText(Text.of("tiles waiting for world-save")))); + + RenderTask[] tasks = renderer.getRenderTasks(); + if (tasks.length > 0) { + RenderTask task = tasks[0]; + + long time = task.getActiveTime(); + String durationString = DurationFormatUtils.formatDurationWords(time, true, true); + double pct = (double)task.getRenderedTileCount() / (double)(task.getRenderedTileCount() + task.getRemainingTileCount()); + + long ert = (long)((time / pct) * (1d - pct)); + String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true); + + double tps = task.getRenderedTileCount() / (time / 1000.0); + + lines.add(Text.of(TextColor.BLUE, "Current task:")); + lines.add(Text.of(" ", createCancelTaskText(task), TextColor.WHITE, " Task ", TextColor.GOLD, task.getName(), TextColor.WHITE, " for map ", Text.of(TextColor.GOLD, task.getMapType().getName()).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GOLD, task.getMapType().getWorld().getName())))); + lines.add(Text.of(TextColor.WHITE, " rendered ", TextColor.GOLD, task.getRenderedTileCount(), TextColor.WHITE, " tiles ", TextColor.GRAY, "(" + (Math.round(pct * 1000)/10.0) + "% | " + GenericMath.round(tps, 1) + "t/s)", TextColor.WHITE, " in ", TextColor.GOLD, durationString)); + lines.add(Text.of(TextColor.WHITE, " with ", TextColor.GOLD, task.getRemainingTileCount(), TextColor.WHITE, " tiles to go. ETA: ", TextColor.GOLD, ertDurationString)); + } + + if (tasks.length > 1) { + lines.add(Text.of(TextColor.BLUE, "Waiting tasks:")); + for (int i = 1; i < tasks.length; i++) { + RenderTask task = tasks[i]; + lines.add(Text.of(" ", createCancelTaskText(task), createPrioritizeTaskText(task), TextColor.WHITE, " Task ", TextColor.GOLD, task.getName(), TextColor.WHITE, " for map ", Text.of(TextColor.GOLD, task.getMapType().getName()).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GOLD, task.getMapType().getWorld().getName())), TextColor.GRAY, " (" + task.getRemainingTileCount() + " tiles)")); + } + } + + return lines; + } + + private Text createCancelTaskText(RenderTask task) { + return Text.of(TextColor.RED, "[X]").setHoverText(Text.of(TextColor.GRAY, "click to remove this render-task")).setClickCommand("/bluemap render remove " + task.getUuid()); + } + + private Text createPrioritizeTaskText(RenderTask task) { + return Text.of(TextColor.GREEN, "[^]").setHoverText(Text.of(TextColor.GRAY, "click to prioritize this render-task")).setClickCommand("/bluemap render prioritize " + task.getUuid()); + } + + private void createWorldRenderTask(CommandSource source, World world) { + source.sendMessage(Text.of(TextColor.GOLD, "Collecting chunks to render...")); + Collection chunks = world.getChunkList(); + source.sendMessage(Text.of(TextColor.GREEN, chunks.size() + " chunks found!")); + + for (MapType map : bluemap.getMapTypes()) { + if (!map.getWorld().getUUID().equals(world.getUUID())) continue; + + source.sendMessage(Text.of(TextColor.GOLD, "Collecting tiles for map '" + map.getId() + "'")); + + HiresModelManager hmm = map.getTileRenderer().getHiresModelManager(); + Collection tiles = hmm.getTilesForChunks(chunks); + + RenderTask task = new RenderTask("world-render", map); + task.addTiles(tiles); + task.optimizeQueue(); + bluemap.getRenderManager().addRenderTask(task); + + source.sendMessage(Text.of(TextColor.GREEN, tiles.size() + " tiles found! Task created.")); + } + + source.sendMessage(Text.of(TextColor.GREEN, "All render tasks created! Use /bluemap to view the progress!")); + } + + private boolean checkLoaded(CommandSource source) { + if (!bluemap.isLoaded()) { + source.sendMessage(Text.of(TextColor.RED, "BlueMap is not loaded!", TextColor.GRAY, "(Try /bluemap reload)")); + return false; + } + + return true; + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java index ff74856b..4a2e4064 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java @@ -56,6 +56,7 @@ public class Plugin { private String implementationType; private ServerInterface serverInterface; + private Commands commands; private MainConfig config; private ResourcePack resourcePack; @@ -74,7 +75,9 @@ public class Plugin { public Plugin(String implementationType, ServerInterface serverInterface) { this.implementationType = implementationType.toLowerCase(); + this.serverInterface = serverInterface; + this.commands = new Commands(this); this.maps = new HashMap<>(); this.worlds = new HashMap<>(); @@ -321,6 +324,10 @@ public ServerInterface getServerInterface() { return serverInterface; } + public Commands getCommands() { + return commands; + } + public MainConfig getMainConfig() { return config; } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java new file mode 100644 index 00000000..f59900ca --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java @@ -0,0 +1,15 @@ +package de.bluecolored.bluemap.common.plugin.serverinterface; + +import de.bluecolored.bluemap.common.plugin.text.Text; + +public interface CommandSource { + + void sendMessage(Text text); + + default void sendMessages(Iterable textLines) { + for (Text text : textLines) { + sendMessage(text); + } + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/Text.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/Text.java index ebc3dea9..e7d54ac2 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/Text.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/text/Text.java @@ -14,25 +14,29 @@ public class Text { private String clickCommand; private List children = new ArrayList<>(); - public void setHoverText(Text hoverText) { + public Text setHoverText(Text hoverText) { this.hoverText = hoverText; + + return this; } - public void setClickCommand(String clickCommand) { + public Text setClickCommand(String clickCommand) { this.clickCommand = clickCommand; + + return this; } - public void addChild(Text child) { + public Text addChild(Text child) { children.add(child); + + return this; } public String toJSONString() { StringBuilder sb = new StringBuilder(); sb.append("{"); - if (!content.isEmpty()) { - sb.append(quote("text")).append(":").append(quote(content)).append(','); - } + sb.append(quote("text")).append(":").append(quote(content)).append(','); if (color != null) { sb.append(quote("color")).append(":").append(quote(color.getId())).append(','); @@ -46,23 +50,23 @@ public String toJSONString() { sb.append(quote("hoverEvent")).append(":{"); sb.append(quote("action")).append(":").append(quote("show_text")).append(','); sb.append(quote("value")).append(":").append(quote(hoverText.toFormattingCodedString('§'))); - sb.append("}"); + sb.append("},"); } if (clickCommand != null) { sb.append(quote("clickEvent")).append(":{"); sb.append(quote("action")).append(":").append(quote("run_command")).append(','); sb.append(quote("value")).append(":").append(quote(clickCommand)); - sb.append("}"); + sb.append("},"); } if (!children.isEmpty()) { - sb.append(quote("")).append(":["); + sb.append(quote("extra")).append(":["); for (Text child : children) { sb.append(child.toJSONString()).append(','); } sb.deleteCharAt(sb.length() - 1); //delete last , - sb.append("]"); + sb.append("],"); } if (sb.charAt(sb.length() - 1) == ',') sb.deleteCharAt(sb.length() - 1); //delete last , @@ -87,7 +91,8 @@ public String toFormattingCodedString(char escapeChar) { } for (Text child : children) { - sb.append(escapeChar).append('r').append(child.withParentFormat(this).toFormattingCodedString(escapeChar)); + if (sb.length() > 0) sb.append(escapeChar).append('r'); + sb.append(child.withParentFormat(this).toFormattingCodedString(escapeChar)); } return sb.toString(); @@ -126,7 +131,7 @@ private String quote(String value) { private String escape(String value) { value = value.replace("\\", "\\\\"); value = value.replace("\"", "\\\""); - value = value.replace("§", "\\u00a76"); + value = value.replace("§", "\\u00a7"); value = value.replace("\n", "\\n"); return value; } @@ -194,7 +199,7 @@ public static Text of(Object... objects) { } if (text.children.isEmpty()) return text; - if (text.children.size() == 1) return text.children.get(1); + if (text.children.size() == 1) return text.children.get(0); return text; } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java deleted file mode 100644 index b067cb8f..00000000 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/Commands.java +++ /dev/null @@ -1,341 +0,0 @@ -package de.bluecolored.bluemap.sponge; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.UUID; - -import org.apache.commons.lang3.time.DurationFormatUtils; -import org.spongepowered.api.Sponge; -import org.spongepowered.api.command.CommandResult; -import org.spongepowered.api.command.CommandSource; -import org.spongepowered.api.command.args.GenericArguments; -import org.spongepowered.api.command.spec.CommandSpec; -import org.spongepowered.api.text.Text; -import org.spongepowered.api.text.action.TextActions; -import org.spongepowered.api.text.format.TextColors; -import org.spongepowered.api.world.Locatable; -import org.spongepowered.api.world.Location; -import org.spongepowered.api.world.storage.WorldProperties; - -import com.flowpowered.math.GenericMath; -import com.flowpowered.math.vector.Vector2i; -import com.google.common.collect.Lists; - -import de.bluecolored.bluemap.common.MapType; -import de.bluecolored.bluemap.common.RenderManager; -import de.bluecolored.bluemap.common.RenderTask; -import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.mca.Chunk; -import de.bluecolored.bluemap.core.mca.ChunkAnvil112; -import de.bluecolored.bluemap.core.mca.MCAWorld; -import de.bluecolored.bluemap.core.render.hires.HiresModelManager; -import de.bluecolored.bluemap.core.world.Block; -import de.bluecolored.bluemap.core.world.World; - -public class Commands { - - private SpongePlugin plugin; - private Plugin bluemap; - - public Commands(SpongePlugin plugin) { - this.plugin = plugin; - this.bluemap = plugin.getBlueMap(); - } - - public CommandSpec createRootCommand() { - - CommandSpec debugCommand = CommandSpec.builder() - .permission("bluemap.debug") - .description(Text.of("Prints some debug info")) - .extendedDescription(Text.of("Prints some information about how bluemap sees the blocks at and below your position")) - .executor((source, args) -> { - if (source instanceof Locatable) { - Location loc = ((Locatable) source).getLocation(); - UUID worldUuid = loc.getExtent().getUniqueId(); - World world = bluemap.getWorld(worldUuid); - Block block = world.getBlock(loc.getBlockPosition()); - Block blockBelow = world.getBlock(loc.getBlockPosition().add(0, -1, 0)); - - String blockIdMeta = ""; - String blockBelowIdMeta = ""; - - if (world instanceof MCAWorld) { - try { - Chunk chunk = ((MCAWorld) world).getChunk(MCAWorld.blockToChunk(loc.getBlockPosition())); - if (chunk instanceof ChunkAnvil112) { - blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(loc.getBlockPosition()) + ")"; - blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(loc.getBlockPosition().add(0, -1, 0)) + ")"; - } - } catch (IOException ex) { - Logger.global.logError("Failed to read chunk for debug!", ex); - } - } - - source.sendMessages(Lists.newArrayList( - Text.of(TextColors.GOLD, "Block at you: ", TextColors.RESET, block, TextColors.GRAY, blockIdMeta), - Text.of(TextColors.GOLD, "Block below you: ", TextColors.RESET, blockBelow, TextColors.GRAY, blockBelowIdMeta) - )); - } - - return CommandResult.success(); - }) - .build(); - - return CommandSpec.builder() - .description(Text.of("Displays BlueMaps render status")) - .permission("bluemap.status") - .childArgumentParseExceptionFallback(false) - .child(createReloadCommand(), "reload") - .child(createPauseRenderCommand(), "pause") - .child(createResumeRenderCommand(), "resume") - .child(createRenderCommand(), "render") - .child(debugCommand, "debug") - .executor((source, args) -> { - source.sendMessages(createStatusMessage()); - return CommandResult.success(); - }) - .build(); - } - - public CommandSpec createStandaloneReloadCommand() { - return CommandSpec.builder() - .description(Text.of("BlueMaps root command")) - .childArgumentParseExceptionFallback(false) - .child(createReloadCommand(), "reload") - .build(); - } - - public CommandSpec createReloadCommand() { - return CommandSpec.builder() - .description(Text.of("Reloads all resources and configuration-files")) - .permission("bluemap.reload") - .executor((source, args) -> { - source.sendMessage(Text.of(TextColors.GOLD, "Reloading BlueMap...")); - - plugin.getAsyncExecutor().submit(() -> { - try { - bluemap.reload(); - - if (bluemap.isLoaded()) { - source.sendMessage(Text.of(TextColors.GREEN, "BlueMap reloaded!")); - } else { - source.sendMessage(Text.of(TextColors.RED, "Could not load BlueMap! See the console for details!")); - } - - } catch (Exception ex) { - Logger.global.logError("Failed to reload BlueMap!", ex); - - source.sendMessage(Text.of(TextColors.RED, "There was an error reloading BlueMap! See the console for details!")); - } - }); - - return CommandResult.success(); - }) - .build(); - } - - public CommandSpec createPauseRenderCommand() { - return CommandSpec.builder() - .description(Text.of("Pauses all rendering")) - .permission("bluemap.pause") - .executor((source, args) -> { - if (bluemap.getRenderManager().isRunning()) { - bluemap.getRenderManager().stop(); - source.sendMessage(Text.of(TextColors.GREEN, "BlueMap rendering paused!")); - return CommandResult.success(); - } else { - source.sendMessage(Text.of(TextColors.RED, "BlueMap rendering are already paused!")); - return CommandResult.empty(); - } - }) - .build(); - } - - public CommandSpec createResumeRenderCommand() { - return CommandSpec.builder() - .description(Text.of("Resumes all paused rendering")) - .permission("bluemap.resume") - .executor((source, args) -> { - if (!bluemap.getRenderManager().isRunning()) { - bluemap.getRenderManager().start(); - source.sendMessage(Text.of(TextColors.GREEN, "BlueMap renders resumed!")); - return CommandResult.success(); - } else { - source.sendMessage(Text.of(TextColors.RED, "BlueMap renders are already running!")); - return CommandResult.empty(); - } - }) - .build(); - } - - public CommandSpec createRenderCommand() { - return CommandSpec.builder() - .description(Text.of("Renders the whole world")) - .permission("bluemap.rendertask.create.world") - .childArgumentParseExceptionFallback(false) - .child(createPrioritizeTaskCommand(), "prioritize") - .child(createRemoveTaskCommand(), "remove") - .arguments(GenericArguments.optional(GenericArguments.world(Text.of("world")))) - .executor((source, args) -> { - WorldProperties spongeWorld = args.getOne("world").orElse(null); - - if (spongeWorld == null && source instanceof Locatable) { - Location loc = ((Locatable) source).getLocation(); - spongeWorld = loc.getExtent().getProperties(); - } - - if (spongeWorld == null){ - source.sendMessage(Text.of(TextColors.RED, "You have to define a world to render!")); - return CommandResult.empty(); - } - - World world = bluemap.getWorld(spongeWorld.getUniqueId()); - if (world == null) { - source.sendMessage(Text.of(TextColors.RED, "This world is not loaded with BlueMap! Maybe it is not configured?")); - } - - world.invalidateChunkCache(); - - Sponge.getScheduler().createTaskBuilder() - .async() - .execute(() -> createWorldRenderTask(source, world)) - .submit(plugin); - - return CommandResult.success(); - }) - .build(); - } - - public CommandSpec createPrioritizeTaskCommand() { - return CommandSpec.builder() - .description(Text.of("Prioritizes the render-task with the given uuid")) - .permission("bluemap.rendertask.prioritize") - .arguments(GenericArguments.uuid(Text.of("task-uuid"))) - .executor((source, args) -> { - Optional uuid = args.getOne("task-uuid"); - if (!uuid.isPresent()) { - source.sendMessage(Text.of("You need to specify a task-uuid")); - return CommandResult.empty(); - } - - for (RenderTask task : bluemap.getRenderManager().getRenderTasks()) { - if (task.getUuid().equals(uuid.get())) { - bluemap.getRenderManager().prioritizeRenderTask(task); - break; - } - } - - source.sendMessages(createStatusMessage()); - return CommandResult.success(); - }) - .build(); - } - - public CommandSpec createRemoveTaskCommand() { - return CommandSpec.builder() - .description(Text.of("Removes the render-task with the given uuid")) - .permission("bluemap.rendertask.remove") - .arguments(GenericArguments.uuid(Text.of("task-uuid"))) - .executor((source, args) -> { - Optional uuid = args.getOne("task-uuid"); - if (!uuid.isPresent()) { - source.sendMessage(Text.of("You need to specify a task-uuid")); - return CommandResult.empty(); - } - - for (RenderTask task : bluemap.getRenderManager().getRenderTasks()) { - if (task.getUuid().equals(uuid.get())) { - bluemap.getRenderManager().removeRenderTask(task); - break; - } - } - - source.sendMessages(createStatusMessage()); - return CommandResult.success(); - }) - .build(); - } - - private List createStatusMessage(){ - List lines = new ArrayList<>(); - - RenderManager renderer = bluemap.getRenderManager(); - - lines.add(Text.EMPTY); - lines.add(Text.of(TextColors.BLUE, "Tile-Updates:")); - - if (renderer.isRunning()) { - lines.add(Text.of(TextColors.WHITE, " Render-Threads are ", Text.of(TextActions.runCommand("/bluemap pause"), TextActions.showText(Text.of("click to pause rendering")), TextColors.GREEN, "running"), TextColors.GRAY, "!")); - } else { - lines.add(Text.of(TextColors.WHITE, " Render-Threads are ", Text.of(TextActions.runCommand("/bluemap resume"), TextActions.showText(Text.of("click to resume rendering")), TextColors.RED, "paused"), TextColors.GRAY, "!")); - } - - lines.add(Text.of(TextColors.WHITE, " Scheduled tile-updates: ", Text.of(TextActions.showText(Text.of("tiles waiting for a free render-thread")), TextColors.GOLD, renderer.getQueueSize()), Text.of(TextActions.showText(Text.of("tiles waiting for world-save")), TextColors.GRAY, " + " + bluemap.getUpdateHandler().getUpdateBufferCount()))); - - RenderTask[] tasks = renderer.getRenderTasks(); - if (tasks.length > 0) { - RenderTask task = tasks[0]; - - long time = task.getActiveTime(); - String durationString = DurationFormatUtils.formatDurationWords(time, true, true); - double pct = (double)task.getRenderedTileCount() / (double)(task.getRenderedTileCount() + task.getRemainingTileCount()); - - long ert = (long)((time / pct) * (1d - pct)); - String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true); - - double tps = task.getRenderedTileCount() / (time / 1000.0); - - lines.add(Text.of(TextColors.BLUE, "Current task:")); - lines.add(Text.of(" ", createCancelTaskText(task), TextColors.WHITE, " Task ", TextColors.GOLD, task.getName(), TextColors.WHITE, " for map ", TextActions.showText(Text.of(TextColors.WHITE, "World: ", TextColors.GOLD, task.getMapType().getWorld().getName())), TextColors.GOLD, task.getMapType().getName())); - lines.add(Text.of(TextColors.WHITE, " rendered ", TextColors.GOLD, task.getRenderedTileCount(), TextColors.WHITE, " tiles ", TextColors.GRAY, "(" + (Math.round(pct * 1000)/10.0) + "% | " + GenericMath.round(tps, 1) + "t/s)", TextColors.WHITE, " in ", TextColors.GOLD, durationString)); - lines.add(Text.of(TextColors.WHITE, " with ", TextColors.GOLD, task.getRemainingTileCount(), TextColors.WHITE, " tiles to go. ETA: ", TextColors.GOLD, ertDurationString)); - } - - if (tasks.length > 1) { - lines.add(Text.of(TextColors.BLUE, "Waiting tasks:")); - for (int i = 1; i < tasks.length; i++) { - RenderTask task = tasks[i]; - lines.add(Text.of(" ", createCancelTaskText(task), createPrioritizeTaskText(task), TextColors.WHITE, " Task ", TextColors.GOLD, task.getName(), TextColors.WHITE, " for map ", Text.of(TextActions.showText(Text.of(TextColors.WHITE, "World: ", TextColors.GOLD, task.getMapType().getWorld().getName())), TextColors.GOLD, task.getMapType().getName()), TextColors.GRAY, " (" + task.getRemainingTileCount() + " tiles)")); - } - } - - return lines; - } - - private Text createCancelTaskText(RenderTask task) { - return Text.of(TextActions.runCommand("/bluemap render remove " + task.getUuid()), TextActions.showText(Text.of("click to remove this render-task")), TextColors.RED, "[X]"); - } - - private Text createPrioritizeTaskText(RenderTask task) { - return Text.of(TextActions.runCommand("/bluemap render prioritize " + task.getUuid()), TextActions.showText(Text.of("click to prioritize this render-task")), TextColors.GREEN, "[^]"); - } - - private void createWorldRenderTask(CommandSource source, World world) { - source.sendMessage(Text.of(TextColors.GOLD, "Collecting chunks to render...")); - Collection chunks = world.getChunkList(); - source.sendMessage(Text.of(TextColors.GREEN, chunks.size() + " chunks found!")); - - for (MapType map : bluemap.getMapTypes()) { - if (!map.getWorld().getUUID().equals(world.getUUID())) continue; - - source.sendMessage(Text.of(TextColors.GOLD, "Collecting tiles for map '" + map.getId() + "'")); - - HiresModelManager hmm = map.getTileRenderer().getHiresModelManager(); - Collection tiles = hmm.getTilesForChunks(chunks); - - RenderTask task = new RenderTask("world-render", map); - task.addTiles(tiles); - task.optimizeQueue(); - bluemap.getRenderManager().addRenderTask(task); - - source.sendMessage(Text.of(TextColors.GREEN, tiles.size() + " tiles found! Task created.")); - } - - source.sendMessage(Text.of(TextColors.GREEN, "All render tasks created! Use /bluemap to view the progress!")); - } - -} diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java new file mode 100644 index 00000000..8f9b80f6 --- /dev/null +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java @@ -0,0 +1,46 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.sponge; + +import org.spongepowered.api.text.serializer.TextSerializers; + +import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.plugin.text.Text; + +public class SpongeCommandSource implements CommandSource { + + private org.spongepowered.api.command.CommandSource delegate; + + public SpongeCommandSource(org.spongepowered.api.command.CommandSource delegate) { + this.delegate = delegate; + } + + @Override + public void sendMessage(Text text) { + org.spongepowered.api.text.Text spongeText = TextSerializers.JSON.deserializeUnchecked(text.toJSONString()); + delegate.sendMessage(spongeText); + } + +} diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommands.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommands.java new file mode 100644 index 00000000..b58b17d3 --- /dev/null +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommands.java @@ -0,0 +1,170 @@ +package de.bluecolored.bluemap.sponge; + +import java.util.Optional; +import java.util.UUID; + +import org.spongepowered.api.command.CommandResult; +import org.spongepowered.api.command.args.GenericArguments; +import org.spongepowered.api.command.spec.CommandSpec; +import org.spongepowered.api.text.Text; +import org.spongepowered.api.text.format.TextColors; +import org.spongepowered.api.world.Locatable; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.storage.WorldProperties; + +import de.bluecolored.bluemap.common.plugin.Commands; + +public class SpongeCommands { + + private Commands commands; + + public SpongeCommands(Commands commands) { + this.commands = commands; + } + + public CommandSpec createRootCommand() { + return CommandSpec.builder() + .description(Text.of("Displays BlueMaps render status")) + .permission("bluemap.status") + .childArgumentParseExceptionFallback(false) + .child(createReloadCommand(), "reload") + .child(createPauseRenderCommand(), "pause") + .child(createResumeRenderCommand(), "resume") + .child(createRenderCommand(), "render") + .child(createDebugCommand(), "debug") + .executor((source, args) -> { + commands.executeRootCommand(new SpongeCommandSource(source)); + return CommandResult.success(); + }) + .build(); + } + + public CommandSpec createReloadCommand() { + return CommandSpec.builder() + .description(Text.of("Reloads all resources and configuration-files")) + .permission("bluemap.reload") + .executor((source, args) -> { + commands.executeReloadCommand(new SpongeCommandSource(source)); + return CommandResult.success(); + }) + .build(); + } + + public CommandSpec createPauseRenderCommand() { + return CommandSpec.builder() + .description(Text.of("Pauses all rendering")) + .permission("bluemap.pause") + .executor((source, args) -> { + if (commands.executePauseCommand(new SpongeCommandSource(source))) { + return CommandResult.success(); + } else { + return CommandResult.empty(); + } + }) + .build(); + } + + public CommandSpec createResumeRenderCommand() { + return CommandSpec.builder() + .description(Text.of("Resumes all paused rendering")) + .permission("bluemap.resume") + .executor((source, args) -> { + if (commands.executeResumeCommand(new SpongeCommandSource(source))) { + return CommandResult.success(); + } else { + return CommandResult.empty(); + } + }) + .build(); + } + + public CommandSpec createRenderCommand() { + return CommandSpec.builder() + .description(Text.of("Renders the whole world")) + .permission("bluemap.rendertask.create.world") + .childArgumentParseExceptionFallback(false) + .child(createPrioritizeTaskCommand(), "prioritize") + .child(createRemoveTaskCommand(), "remove") + .arguments(GenericArguments.optional(GenericArguments.world(Text.of("world")))) + .executor((source, args) -> { + WorldProperties spongeWorld = args.getOne("world").orElse(null); + + if (spongeWorld == null && source instanceof Locatable) { + Location loc = ((Locatable) source).getLocation(); + spongeWorld = loc.getExtent().getProperties(); + } + + if (spongeWorld == null){ + source.sendMessage(Text.of(TextColors.RED, "You have to define a world to render!")); + return CommandResult.empty(); + } + + if (commands.executeRenderWorldCommand(new SpongeCommandSource(source), spongeWorld.getUniqueId())) { + return CommandResult.success(); + } else { + return CommandResult.empty(); + } + }) + .build(); + } + + public CommandSpec createPrioritizeTaskCommand() { + return CommandSpec.builder() + .description(Text.of("Prioritizes the render-task with the given uuid")) + .permission("bluemap.rendertask.prioritize") + .arguments(GenericArguments.uuid(Text.of("task-uuid"))) + .executor((source, args) -> { + Optional uuid = args.getOne("task-uuid"); + if (!uuid.isPresent()) { + source.sendMessage(Text.of(TextColors.RED, "You need to specify a task-uuid")); + return CommandResult.empty(); + } + + commands.executePrioritizeRenderTaskCommand(new SpongeCommandSource(source), uuid.get()); + return CommandResult.success(); + }) + .build(); + } + + public CommandSpec createRemoveTaskCommand() { + return CommandSpec.builder() + .description(Text.of("Removes the render-task with the given uuid")) + .permission("bluemap.rendertask.remove") + .arguments(GenericArguments.uuid(Text.of("task-uuid"))) + .executor((source, args) -> { + Optional uuid = args.getOne("task-uuid"); + if (!uuid.isPresent()) { + source.sendMessage(Text.of(TextColors.RED, "You need to specify a task-uuid")); + return CommandResult.empty(); + } + + commands.executeRemoveRenderTaskCommand(new SpongeCommandSource(source), uuid.get()); + return CommandResult.success(); + }) + .build(); + } + + public CommandSpec createDebugCommand() { + return CommandSpec.builder() + .permission("bluemap.debug") + .description(Text.of("Prints some debug info")) + .extendedDescription(Text.of("Prints some information about how bluemap sees the blocks at and below your position")) + .executor((source, args) -> { + if (source instanceof Locatable) { + Location loc = ((Locatable) source).getLocation(); + UUID worldUuid = loc.getExtent().getUniqueId(); + + if (commands.executeDebugCommand(new SpongeCommandSource(source), worldUuid, loc.getBlockPosition())) { + return CommandResult.success(); + } else { + return CommandResult.empty(); + } + } + + source.sendMessage(Text.of(TextColors.RED, "You can only execute this command as a player!")); + return CommandResult.empty(); + }) + .build(); + } + +} diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java index c7e88a9e..8c070220 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -85,7 +85,7 @@ public void onServerStart(GameStartingServerEvent evt) { Sponge.getServer().saveWorldProperties(properties); } - Sponge.getCommandManager().register(this, new Commands(this).createRootCommand(), "bluemap"); + Sponge.getCommandManager().register(this, new SpongeCommands(bluemap.getCommands()).createRootCommand(), "bluemap"); asyncExecutor.execute(() -> { try { @@ -146,12 +146,4 @@ public File getConfigFolder() { return configurationDir.toFile(); } - public SpongeExecutorService getAsyncExecutor() { - return asyncExecutor; - } - - public Plugin getBlueMap() { - return bluemap; - } - } From 0bb50f8044e6d700ba7451f01b32c9376851e66f Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Sat, 18 Jan 2020 00:59:51 +0100 Subject: [PATCH 6/6] Complete bukkit plugin implementation --- .../bluemap/bukkit/BukkitCommandSource.java | 18 +- .../bluemap/bukkit/BukkitCommands.java | 197 +++++++++++++++++- .../bluemap/bukkit/BukkitPlugin.java | 37 +++- BlueMapBukkit/src/main/resources/plugin.yml | 42 +++- .../src}/main/resources/mcmod.info | 0 5 files changed, 276 insertions(+), 18 deletions(-) rename {src => BlueMapCommon/src}/main/resources/mcmod.info (100%) diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java index 6b68c6e3..44be5fec 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java @@ -17,15 +17,17 @@ public BukkitCommandSource(CommandSender delegate) { @Override public void sendMessage(Text text) { - if (delegate instanceof Player) { - Player player = (Player) delegate; + Bukkit.getScheduler().runTask(BukkitPlugin.getInstance(), () -> { + if (delegate instanceof Player) { + Player player = (Player) delegate; + + //kinda hacky but works + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + player.getName() + " " + text.toJSONString()); + return; + } - //kinda hacky but works - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + player.getName() + " " + text.toJSONString()); - return; - } - - delegate.sendMessage(text.toFormattingCodedString('§')); + delegate.sendMessage(text.toFormattingCodedString('§')); + }); } } diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java index ca94fe58..b95538d5 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java @@ -1,24 +1,209 @@ package de.bluecolored.bluemap.bukkit; -import org.bukkit.command.Command; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.flowpowered.math.vector.Vector3i; import de.bluecolored.bluemap.common.plugin.Commands; +import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.plugin.text.TextColor; public class BukkitCommands implements CommandExecutor { - private Commands commands; + private Commands bluemapCommands; + + private Collection commands; public BukkitCommands(Commands commands) { - this.commands = commands; + this.bluemapCommands = commands; + this.commands = new ArrayList<>(); + initCommands(); + } + + private void initCommands() { + + commands.add(new Command("bluemap.status") { + @Override + public boolean execute(CommandSender sender, CommandSource source, String[] args) { + if (args.length != 0) return false; + + bluemapCommands.executeRootCommand(source); + return true; + } + }); + + commands.add(new Command("bluemap.reload", "reload") { + @Override + public boolean execute(CommandSender sender, CommandSource source, String[] args) { + if (args.length != 0) return false; + + bluemapCommands.executeReloadCommand(source); + return true; + } + }); + + commands.add(new Command("bluemap.pause", "pause") { + @Override + public boolean execute(CommandSender sender, CommandSource source, String[] args) { + if (args.length != 0) return false; + + bluemapCommands.executePauseCommand(source); + return true; + } + }); + + commands.add(new Command("bluemap.resume", "resume") { + @Override + public boolean execute(CommandSender sender, CommandSource source, String[] args) { + if (args.length != 0) return false; + + bluemapCommands.executeResumeCommand(source); + return true; + } + }); + + commands.add(new Command("bluemap.rendertask.create.world", "render") { + @Override + public boolean execute(CommandSender sender, CommandSource source, String[] args) { + if (args.length > 1) return false; + + World world; + if (args.length == 1) { + world = Bukkit.getWorld(args[0]); + if (world == null) { + source.sendMessage(Text.of(TextColor.RED, "There is no world named '" + args[0] + "'!")); + return true; + } + } else { + if (sender instanceof Player) { + Player player = (Player) sender; + world = player.getWorld(); + } else { + source.sendMessage(Text.of(TextColor.RED, "Since you are not a player, you have to specify a world!")); + return true; + } + } + + bluemapCommands.executeRenderWorldCommand(source, world.getUID()); + return true; + } + }); + + commands.add(new Command("bluemap.rendertask.prioritize", "render", "prioritize") { + @Override + public boolean execute(CommandSender sender, CommandSource source, String[] args) { + if (args.length != 1) return false; + + try { + UUID uuid = UUID.fromString(args[0]); + bluemapCommands.executePrioritizeRenderTaskCommand(source, uuid); + return true; + } catch (IllegalArgumentException ex) { + source.sendMessage(Text.of(TextColor.RED, "'" + args[0] + "' is not a valid UUID!")); + return true; + } + } + }); + + commands.add(new Command("bluemap.rendertask.remove", "render", "remove") { + @Override + public boolean execute(CommandSender sender, CommandSource source, String[] args) { + if (args.length != 1) return false; + + try { + UUID uuid = UUID.fromString(args[0]); + bluemapCommands.executeRemoveRenderTaskCommand(source, uuid); + return true; + } catch (IllegalArgumentException ex) { + source.sendMessage(Text.of(TextColor.RED, "'" + args[0] + "' is not a valid UUID!")); + return true; + } + } + }); + + commands.add(new Command("bluemap.debug", "debug") { + @Override + public boolean execute(CommandSender sender, CommandSource source, String[] args) { + if (!(sender instanceof Player)) { + source.sendMessage(Text.of(TextColor.RED, "You have to be a player to use this command!")); + return true; + } + + Player player = (Player) sender; + UUID world = player.getWorld().getUID(); + Vector3i pos = new Vector3i( + player.getLocation().getBlockX(), + player.getLocation().getBlockY(), + player.getLocation().getBlockZ() + ); + + bluemapCommands.executeDebugCommand(source, world, pos); + return true; + } + }); + } @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - commands.executeRootCommand(new BukkitCommandSource(sender)); + public boolean onCommand(CommandSender sender, org.bukkit.command.Command bukkitCommand, String label, String[] args) { + int max = -1; + Command maxCommand = null; + for (Command command : commands) { + int matchSize = command.matches(args); + if (matchSize > max) { + maxCommand = command; + max = matchSize; + } + } - return true; + if (maxCommand == null) return false; + + BukkitCommandSource source = new BukkitCommandSource(sender); + + if (!maxCommand.checkPermission(sender)) { + source.sendMessage(Text.of(TextColor.RED, "You don't have permission to use this command!")); + return true; + } + + return maxCommand.execute(sender, source, Arrays.copyOfRange(args, max, args.length)); } + private abstract class Command { + + private String[] command; + private String permission; + + public Command(String permission, String... command) { + this.command = command; + } + + public abstract boolean execute(CommandSender sender, CommandSource source, String[] args); + + public int matches(String[] args) { + if (args.length < command.length) return -1; + + for (int i = 0; i < command.length; i++) { + if (!args[i].equalsIgnoreCase(command[i])) return -1; + } + + return command.length; + } + + public boolean checkPermission(CommandSender sender) { + if (sender.isOp()) return true; + return sender.hasPermission(permission); + } + + } + } diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index 41421fe8..b3d49919 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -3,8 +3,12 @@ import java.io.File; import java.io.IOException; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import org.bstats.bukkit.MetricsLite; +import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.plugin.java.JavaPlugin; @@ -15,6 +19,8 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface { + private static BukkitPlugin instance; + private Plugin bluemap; private EventForwarder eventForwarder; private BukkitCommands commands; @@ -25,6 +31,8 @@ public BukkitPlugin() { this.eventForwarder = new EventForwarder(); this.bluemap = new Plugin("bukkit", this); this.commands = new BukkitCommands(bluemap.getCommands()); + + BukkitPlugin.instance = this; } @Override @@ -64,9 +72,30 @@ public void unregisterAllListeners() { @Override public UUID getUUIDForWorld(File worldFolder) throws IOException { - worldFolder = worldFolder.getCanonicalFile(); + final File normalizedWorldFolder = worldFolder.getCanonicalFile(); + + Future futureUUID; + if (!Bukkit.isPrimaryThread()) { + futureUUID = Bukkit.getScheduler().callSyncMethod(BukkitPlugin.getInstance(), () -> getUUIDForWorldSync(normalizedWorldFolder)); + } else { + futureUUID = CompletableFuture.completedFuture(getUUIDForWorldSync(normalizedWorldFolder)); + } + + try { + return futureUUID.get(); + } catch (InterruptedException e) { + throw new IOException(e); + } catch (ExecutionException e) { + if (e.getCause() instanceof IOException) { + throw (IOException) e.getCause(); + } else { + throw new IOException(e); + } + } + } + + private UUID getUUIDForWorldSync (File worldFolder) throws IOException { for (World world : getServer().getWorlds()) { - Logger.global.logInfo("Found world-folder: " + world.getWorldFolder().getCanonicalPath()); if (worldFolder.equals(world.getWorldFolder().getCanonicalFile())) return world.getUID(); } @@ -82,4 +111,8 @@ public Plugin getBlueMap() { return bluemap; } + public static BukkitPlugin getInstance() { + return instance; + } + } diff --git a/BlueMapBukkit/src/main/resources/plugin.yml b/BlueMapBukkit/src/main/resources/plugin.yml index 71cfbf31..6c8fbe10 100644 --- a/BlueMapBukkit/src/main/resources/plugin.yml +++ b/BlueMapBukkit/src/main/resources/plugin.yml @@ -1,8 +1,46 @@ name: BlueMap +description: "A 3d-map of your Minecraft worlds view-able in your browser using three.js (WebGL)" main: de.bluecolored.bluemap.bukkit.BukkitPlugin version: 0.2.1 -author: Blue (TBlueF / Lukas Rieger) +author: "Blue (TBlueF / Lukas Rieger)" authors: [Blue (TBlueF / Lukas Rieger)] website: "https://github.com/BlueMap-Minecraft" commands: - bluemap: \ No newline at end of file + bluemap: + description: Root command for all bluemap commands + permission: bluemap + usage: | + / + / reload + / pause + / resume + / render [world] + / debug +permissions: + bluemap.*: + children: + bluemap.status: true + bluemap.reload: true + bluemap.pause: true + bluemap.resume: true + bluemap.rendertask.create.world: true + bluemap.rendertask.prioritize: true + bluemap.rendertask.remove: true + bluemap.debug: true + default: op + bluemap.status: + default: op + bluemap.reload: + default: op + bluemap.pause: + default: op + bluemap.resume: + default: op + bluemap.rendertask.create.world: + default: op + bluemap.rendertask.prioritize: + default: op + bluemap.rendertask.remove: + default: op + bluemap.debug: + default: op \ No newline at end of file diff --git a/src/main/resources/mcmod.info b/BlueMapCommon/src/main/resources/mcmod.info similarity index 100% rename from src/main/resources/mcmod.info rename to BlueMapCommon/src/main/resources/mcmod.info