mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2025-02-22 23:41:31 +01:00
More common/plugin refactoring 2
This commit is contained in:
parent
e156c818b2
commit
4914814744
@ -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')
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.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<ServerEventListener> 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));
|
||||
}
|
||||
|
||||
}
|
@ -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<Vector2i> 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<Vector2i> 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)");
|
||||
|
@ -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;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package de.bluecolored.bluemap.plugin;
|
||||
package de.bluecolored.bluemap.common;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
@ -1,4 +1,4 @@
|
||||
package de.bluecolored.bluemap.plugin;
|
||||
package de.bluecolored.bluemap.common;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
@ -1,4 +1,4 @@
|
||||
package de.bluecolored.bluemap.plugin;
|
||||
package de.bluecolored.bluemap.common;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package de.bluecolored.bluemap.plugin.serverinterface;
|
||||
package de.bluecolored.bluemap.common.plugin.serverinterface;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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<TextFormat> formats = new HashSet<>();
|
||||
private Text hoverText;
|
||||
private String clickCommand;
|
||||
private List<Text> 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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -385,7 +385,10 @@ public static MCAWorld load(Path worldFolder, UUID uuid, BlockIdMapper blockIdMa
|
||||
);
|
||||
|
||||
try {
|
||||
ListTag<? extends Tag<?>> blockIdReg = level.getCompoundTag("FML").getCompoundTag("Registries").getCompoundTag("minecraft:blocks").getListTag("ids");
|
||||
CompoundTag fmlTag = level.getCompoundTag("FML");
|
||||
if (fmlTag == null) fmlTag = level.getCompoundTag("fml");
|
||||
|
||||
ListTag<? extends Tag<?>> blockIdReg = fmlTag.getCompoundTag("Registries").getCompoundTag("minecraft:blocks").getListTag("ids");
|
||||
for (Tag<?> tag : blockIdReg) {
|
||||
if (tag instanceof CompoundTag) {
|
||||
CompoundTag entry = (CompoundTag) tag;
|
||||
|
@ -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<Vector2i> getTilesForChunks(Iterable<Vector2i> chunks){
|
||||
Set<Vector2i> 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
|
||||
*/
|
||||
|
@ -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<Tag<?>> textTags) {
|
||||
ListTag<Tag<?>> textListTag = new ListTag<>(Tag.class);
|
||||
for (Tag<?> textTag : textTags) {
|
||||
textListTag.add(textTag);
|
||||
}
|
||||
sendSerializedMessage(textListTag);
|
||||
}
|
||||
|
||||
default void sendMessage(Tag<?>... textTags) {
|
||||
ListTag<Tag<?>> 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<Tag<?>> 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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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!");
|
||||
}
|
||||
|
||||
}
|
@ -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')
|
||||
}
|
||||
|
@ -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<org.spongepowered.api.world.World> 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<Text> createStatusMessage(){
|
||||
List<Text> 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<Text> 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<Vector2i> 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<Vector2i> 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<Vector2i> 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."));
|
||||
}
|
||||
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.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<BlockSnapshot> tr : evt.getTransactions()) {
|
||||
if(!tr.isValid()) continue;
|
||||
|
||||
Optional<Location<org.spongepowered.api.world.World>> 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()));
|
||||
}
|
||||
|
||||
}
|
@ -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<MapType, Vector2i> 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<MapType> 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<BlockSnapshot> tr : evt.getTransactions()) {
|
||||
if (!tr.isValid()) continue;
|
||||
|
||||
Optional<Location<org.spongepowered.api.world.World>> 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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<UUID, World> worlds;
|
||||
private Map<String, MapType> 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<File> 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<MapType> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user