2020-04-20 12:50:28 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2020-01-15 20:20:22 +01:00
|
|
|
package de.bluecolored.bluemap.common.plugin;
|
2020-01-13 17:13:20 +01:00
|
|
|
|
2021-03-28 13:25:39 +02:00
|
|
|
import de.bluecolored.bluemap.common.*;
|
2020-04-10 17:16:47 +02:00
|
|
|
import de.bluecolored.bluemap.common.api.BlueMapAPIImpl;
|
2020-08-23 23:29:14 +02:00
|
|
|
import de.bluecolored.bluemap.common.live.LiveAPIRequestHandler;
|
2020-01-15 20:20:22 +01:00
|
|
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
2020-08-07 14:17:48 +02:00
|
|
|
import de.bluecolored.bluemap.common.plugin.skins.PlayerSkinUpdater;
|
2020-08-23 23:29:14 +02:00
|
|
|
import de.bluecolored.bluemap.core.BlueMap;
|
2020-08-25 15:07:42 +02:00
|
|
|
import de.bluecolored.bluemap.core.MinecraftVersion;
|
2020-08-23 23:29:14 +02:00
|
|
|
import de.bluecolored.bluemap.core.config.CoreConfig;
|
|
|
|
import de.bluecolored.bluemap.core.config.RenderConfig;
|
|
|
|
import de.bluecolored.bluemap.core.config.WebServerConfig;
|
2020-01-13 17:13:20 +01:00
|
|
|
import de.bluecolored.bluemap.core.logger.Logger;
|
|
|
|
import de.bluecolored.bluemap.core.metrics.Metrics;
|
2020-01-15 20:20:22 +01:00
|
|
|
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
2021-03-28 18:56:26 +02:00
|
|
|
import de.bluecolored.bluemap.core.util.FileUtils;
|
2020-08-23 23:29:14 +02:00
|
|
|
import de.bluecolored.bluemap.core.web.FileRequestHandler;
|
|
|
|
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
|
|
|
import de.bluecolored.bluemap.core.webserver.WebServer;
|
2020-01-13 17:13:20 +01:00
|
|
|
import de.bluecolored.bluemap.core.world.World;
|
|
|
|
|
2021-03-28 13:25:39 +02:00
|
|
|
import java.io.*;
|
2021-04-02 12:20:48 +02:00
|
|
|
import java.util.*;
|
2021-03-28 13:25:39 +02:00
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import java.util.zip.GZIPInputStream;
|
|
|
|
import java.util.zip.GZIPOutputStream;
|
|
|
|
|
2020-01-13 17:13:20 +01:00
|
|
|
public class Plugin {
|
|
|
|
|
|
|
|
public static final String PLUGIN_ID = "bluemap";
|
|
|
|
public static final String PLUGIN_NAME = "BlueMap";
|
2020-08-24 23:58:11 +02:00
|
|
|
|
2020-08-30 02:30:55 +02:00
|
|
|
private final InterruptableReentrantLock loadingLock = new InterruptableReentrantLock();
|
|
|
|
|
2021-04-02 12:20:48 +02:00
|
|
|
private final MinecraftVersion minecraftVersion;
|
|
|
|
private final String implementationType;
|
2020-01-13 17:13:20 +01:00
|
|
|
private ServerInterface serverInterface;
|
2020-08-24 23:58:11 +02:00
|
|
|
|
|
|
|
private BlueMapService blueMap;
|
|
|
|
private BlueMapAPIImpl api;
|
2020-01-13 17:13:20 +01:00
|
|
|
|
|
|
|
private Map<UUID, World> worlds;
|
|
|
|
private Map<String, MapType> maps;
|
|
|
|
|
|
|
|
private RenderManager renderManager;
|
2020-08-23 23:29:14 +02:00
|
|
|
private WebServer webServer;
|
2021-04-02 12:20:48 +02:00
|
|
|
|
|
|
|
private final Timer daemonTimer;
|
|
|
|
private TimerTask periodicalSaveTask;
|
|
|
|
private TimerTask metricsTask;
|
2020-08-24 23:58:11 +02:00
|
|
|
|
|
|
|
private PluginConfig pluginConfig;
|
|
|
|
private MapUpdateHandler updateHandler;
|
|
|
|
private PlayerSkinUpdater skinUpdater;
|
|
|
|
|
2020-01-13 17:13:20 +01:00
|
|
|
private boolean loaded = false;
|
|
|
|
|
2020-08-25 15:07:42 +02:00
|
|
|
public Plugin(MinecraftVersion minecraftVersion, String implementationType, ServerInterface serverInterface) {
|
|
|
|
this.minecraftVersion = minecraftVersion;
|
2020-01-15 20:20:22 +01:00
|
|
|
this.implementationType = implementationType.toLowerCase();
|
|
|
|
this.serverInterface = serverInterface;
|
2021-04-02 12:20:48 +02:00
|
|
|
|
|
|
|
this.daemonTimer = new Timer("BlueMap-Daemon-Timer", true);
|
2020-01-13 17:13:20 +01:00
|
|
|
}
|
|
|
|
|
2020-08-30 02:30:55 +02:00
|
|
|
public void load() throws IOException, ParseResourceException {
|
2020-08-24 23:58:11 +02:00
|
|
|
try {
|
2020-08-30 02:30:55 +02:00
|
|
|
loadingLock.lock();
|
|
|
|
synchronized (this) {
|
|
|
|
|
|
|
|
if (loaded) return;
|
|
|
|
unload(); //ensure nothing is left running (from a failed load or something)
|
|
|
|
|
|
|
|
blueMap = new BlueMapService(minecraftVersion, serverInterface);
|
2020-01-13 17:13:20 +01:00
|
|
|
|
2020-08-30 02:30:55 +02:00
|
|
|
//load configs
|
|
|
|
CoreConfig coreConfig = blueMap.getCoreConfig();
|
|
|
|
RenderConfig renderConfig = blueMap.getRenderConfig();
|
|
|
|
WebServerConfig webServerConfig = blueMap.getWebServerConfig();
|
|
|
|
|
|
|
|
//load plugin config
|
|
|
|
pluginConfig = new PluginConfig(blueMap.getConfigManager().loadOrCreate(
|
|
|
|
new File(serverInterface.getConfigFolder(), "plugin.conf"),
|
|
|
|
Plugin.class.getResource("/de/bluecolored/bluemap/plugin.conf"),
|
|
|
|
Plugin.class.getResource("/de/bluecolored/bluemap/plugin-defaults.conf"),
|
|
|
|
true,
|
|
|
|
true
|
|
|
|
));
|
2020-08-30 02:51:39 +02:00
|
|
|
|
|
|
|
//create and start webserver
|
|
|
|
if (webServerConfig.isWebserverEnabled()) {
|
2021-03-28 18:56:26 +02:00
|
|
|
FileUtils.mkDirs(webServerConfig.getWebRoot());
|
2020-08-30 02:51:39 +02:00
|
|
|
HttpRequestHandler requestHandler = new FileRequestHandler(webServerConfig.getWebRoot().toPath(), "BlueMap v" + BlueMap.VERSION);
|
|
|
|
|
|
|
|
//inject live api if enabled
|
|
|
|
if (pluginConfig.isLiveUpdatesEnabled()) {
|
|
|
|
requestHandler = new LiveAPIRequestHandler(serverInterface, pluginConfig, requestHandler);
|
|
|
|
}
|
|
|
|
|
|
|
|
webServer = new WebServer(
|
2021-04-02 10:42:10 +02:00
|
|
|
webServerConfig.getWebserverBindAddress(),
|
2020-10-04 12:38:09 +02:00
|
|
|
webServerConfig.getWebserverPort(),
|
|
|
|
webServerConfig.getWebserverMaxConnections(),
|
|
|
|
requestHandler,
|
|
|
|
false
|
2020-08-30 02:51:39 +02:00
|
|
|
);
|
|
|
|
webServer.start();
|
|
|
|
}
|
2020-01-13 17:13:20 +01:00
|
|
|
|
2020-08-30 02:30:55 +02:00
|
|
|
//try load resources
|
|
|
|
try {
|
|
|
|
blueMap.getResourcePack();
|
|
|
|
} catch (MissingResourcesException ex) {
|
|
|
|
Logger.global.logWarning("BlueMap is missing important resources!");
|
2020-12-26 23:53:00 +01:00
|
|
|
Logger.global.logWarning("You must accept the required file download in order for BlueMap to work!");
|
2020-08-30 02:30:55 +02:00
|
|
|
try { Logger.global.logWarning("Please check: " + blueMap.getCoreConfigFile().getCanonicalPath()); } catch (IOException ignored) {}
|
|
|
|
Logger.global.logInfo("If you have changed the config you can simply reload the plugin using: /bluemap reload");
|
|
|
|
|
|
|
|
unload();
|
|
|
|
return;
|
2020-01-13 17:13:20 +01:00
|
|
|
}
|
2020-08-30 02:30:55 +02:00
|
|
|
|
|
|
|
//load worlds and maps
|
|
|
|
worlds = blueMap.getWorlds();
|
|
|
|
maps = blueMap.getMaps();
|
|
|
|
|
|
|
|
//warn if no maps are configured
|
|
|
|
if (maps.isEmpty()) {
|
|
|
|
Logger.global.logWarning("There are no valid maps configured, please check your render-config! Disabling BlueMap...");
|
|
|
|
}
|
|
|
|
|
|
|
|
//initialize render manager
|
|
|
|
renderManager = new RenderManager(coreConfig.getRenderThreadCount());
|
|
|
|
renderManager.start();
|
|
|
|
|
|
|
|
//load render-manager state
|
|
|
|
try {
|
|
|
|
File saveFile = getRenderManagerSaveFile();
|
|
|
|
if (saveFile.exists()) {
|
|
|
|
try (DataInputStream in = new DataInputStream(new GZIPInputStream(new FileInputStream(saveFile)))) {
|
|
|
|
renderManager.readState(in, getMapTypes());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (IOException ex) {
|
|
|
|
Logger.global.logError("Failed to load render-manager state!", ex);
|
|
|
|
}
|
|
|
|
|
2021-04-02 12:20:48 +02:00
|
|
|
//do periodical saves
|
|
|
|
periodicalSaveTask = new TimerTask() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
try {
|
|
|
|
saveRenderManagerState();
|
|
|
|
|
|
|
|
//clean up caches
|
|
|
|
for (World world : blueMap.getWorlds().values()) {
|
|
|
|
world.cleanUpChunkCache();
|
2020-08-30 02:30:55 +02:00
|
|
|
}
|
2021-04-02 12:20:48 +02:00
|
|
|
} catch (IOException ex) {
|
|
|
|
Logger.global.logError("Failed to save render-manager state!", ex);
|
|
|
|
} catch (InterruptedException ex) {
|
|
|
|
this.cancel();
|
|
|
|
Thread.currentThread().interrupt();
|
2020-08-26 17:25:45 +02:00
|
|
|
}
|
2020-01-21 21:15:37 +01:00
|
|
|
}
|
2021-04-02 12:20:48 +02:00
|
|
|
};
|
|
|
|
daemonTimer.schedule(periodicalSaveTask, TimeUnit.MINUTES.toMillis(5), TimeUnit.MINUTES.toMillis(5));
|
2020-08-30 02:30:55 +02:00
|
|
|
|
|
|
|
//start map updater
|
|
|
|
this.updateHandler = new MapUpdateHandler(this);
|
|
|
|
serverInterface.registerListener(updateHandler);
|
|
|
|
|
|
|
|
//update webapp and settings
|
|
|
|
blueMap.createOrUpdateWebApp(false);
|
|
|
|
blueMap.updateWebAppSettings();
|
|
|
|
|
|
|
|
//start skin updater
|
|
|
|
if (pluginConfig.isLiveUpdatesEnabled()) {
|
2021-03-28 13:25:39 +02:00
|
|
|
this.skinUpdater = new PlayerSkinUpdater(
|
|
|
|
new File(renderConfig.getWebRoot(), "assets" + File.separator + "playerheads"),
|
|
|
|
new File(renderConfig.getWebRoot(), "assets" + File.separator + "steve.png")
|
|
|
|
);
|
2020-08-30 02:30:55 +02:00
|
|
|
serverInterface.registerListener(skinUpdater);
|
2020-01-21 21:15:37 +01:00
|
|
|
}
|
2020-01-15 20:20:22 +01:00
|
|
|
|
2020-08-30 02:30:55 +02:00
|
|
|
//metrics
|
2021-04-02 12:20:48 +02:00
|
|
|
metricsTask = new TimerTask() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (Plugin.this.serverInterface.isMetricsEnabled(coreConfig.isMetricsEnabled()))
|
|
|
|
Metrics.sendReport(Plugin.this.implementationType);
|
2020-08-30 02:30:55 +02:00
|
|
|
}
|
2021-04-02 12:20:48 +02:00
|
|
|
};
|
|
|
|
daemonTimer.scheduleAtFixedRate(metricsTask, TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(30));
|
2020-01-15 20:20:22 +01:00
|
|
|
|
2020-08-30 02:30:55 +02:00
|
|
|
loaded = true;
|
|
|
|
|
|
|
|
//enable api
|
|
|
|
this.api = new BlueMapAPIImpl(this);
|
|
|
|
this.api.register();
|
|
|
|
|
2020-01-15 20:20:22 +01:00
|
|
|
}
|
2020-08-30 02:30:55 +02:00
|
|
|
} catch (InterruptedException e) {
|
|
|
|
Thread.currentThread().interrupt();
|
|
|
|
Logger.global.logWarning("Loading has been interrupted!");
|
|
|
|
} finally {
|
|
|
|
loadingLock.unlock();
|
2020-01-15 20:20:22 +01:00
|
|
|
}
|
2020-08-30 02:30:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void unload() {
|
|
|
|
try {
|
|
|
|
loadingLock.interruptAndLock();
|
|
|
|
synchronized (this) {
|
|
|
|
|
|
|
|
//disable api
|
|
|
|
if (api != null) api.unregister();
|
|
|
|
api = null;
|
|
|
|
|
|
|
|
//unregister listeners
|
|
|
|
serverInterface.unregisterAllListeners();
|
2020-01-15 20:20:22 +01:00
|
|
|
|
2020-08-30 02:30:55 +02:00
|
|
|
//stop scheduled threads
|
2021-04-02 12:20:48 +02:00
|
|
|
metricsTask.cancel();
|
|
|
|
periodicalSaveTask.cancel();
|
2020-08-30 02:30:55 +02:00
|
|
|
|
|
|
|
//stop services
|
|
|
|
if (renderManager != null) renderManager.stop();
|
|
|
|
if (webServer != null) webServer.close();
|
|
|
|
|
|
|
|
//save render-manager state
|
2020-09-20 21:12:15 +02:00
|
|
|
if (updateHandler != null) updateHandler.flushUpdateBuffer(); //first write all buffered changes to the render manager to save them too
|
2020-08-30 02:30:55 +02:00
|
|
|
if (renderManager != null) {
|
|
|
|
try {
|
|
|
|
saveRenderManagerState();
|
|
|
|
} catch (IOException ex) {
|
|
|
|
Logger.global.logError("Failed to save render-manager state!", ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//save renders
|
|
|
|
if (maps != null) {
|
|
|
|
for (MapType map : maps.values()) {
|
|
|
|
map.getTileRenderer().save();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//clear resources and configs
|
|
|
|
blueMap = null;
|
|
|
|
worlds = null;
|
|
|
|
maps = null;
|
|
|
|
renderManager = null;
|
|
|
|
webServer = null;
|
|
|
|
updateHandler = null;
|
|
|
|
pluginConfig = null;
|
|
|
|
|
|
|
|
loaded = false;
|
|
|
|
|
2020-08-24 23:58:11 +02:00
|
|
|
}
|
2020-08-30 02:30:55 +02:00
|
|
|
} finally {
|
|
|
|
loadingLock.unlock();
|
2020-01-15 20:20:22 +01:00
|
|
|
}
|
2020-01-13 17:13:20 +01:00
|
|
|
}
|
2020-01-21 21:15:37 +01:00
|
|
|
|
|
|
|
public void saveRenderManagerState() throws IOException {
|
2020-08-23 23:29:14 +02:00
|
|
|
File saveFile = getRenderManagerSaveFile();
|
|
|
|
|
2021-03-28 18:56:26 +02:00
|
|
|
if (saveFile.exists()) FileUtils.delete(saveFile);
|
|
|
|
FileUtils.createFile(saveFile);
|
2020-01-21 21:15:37 +01:00
|
|
|
|
|
|
|
try (DataOutputStream out = new DataOutputStream(new GZIPOutputStream(new FileOutputStream(saveFile)))) {
|
|
|
|
renderManager.writeState(out);
|
|
|
|
}
|
|
|
|
}
|
2020-01-13 17:13:20 +01:00
|
|
|
|
2020-08-30 02:51:39 +02:00
|
|
|
public void reload() throws IOException, ParseResourceException {
|
2020-01-13 17:13:20 +01:00
|
|
|
unload();
|
|
|
|
load();
|
|
|
|
}
|
|
|
|
|
|
|
|
public ServerInterface getServerInterface() {
|
|
|
|
return serverInterface;
|
|
|
|
}
|
|
|
|
|
2020-08-24 23:58:11 +02:00
|
|
|
public CoreConfig getCoreConfig() throws IOException {
|
|
|
|
return blueMap.getCoreConfig();
|
2020-08-23 23:29:14 +02:00
|
|
|
}
|
|
|
|
|
2020-08-24 23:58:11 +02:00
|
|
|
public RenderConfig getRenderConfig() throws IOException {
|
|
|
|
return blueMap.getRenderConfig();
|
2020-08-23 23:29:14 +02:00
|
|
|
}
|
|
|
|
|
2020-08-24 23:58:11 +02:00
|
|
|
public WebServerConfig getWebServerConfig() throws IOException {
|
|
|
|
return blueMap.getWebServerConfig();
|
2020-08-23 23:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public PluginConfig getPluginConfig() {
|
|
|
|
return pluginConfig;
|
2020-01-13 17:13:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public World getWorld(UUID uuid){
|
|
|
|
return worlds.get(uuid);
|
|
|
|
}
|
|
|
|
|
2020-04-07 21:04:46 +02:00
|
|
|
public Collection<World> getWorlds(){
|
|
|
|
return worlds.values();
|
|
|
|
}
|
|
|
|
|
2020-01-13 17:13:20 +01:00
|
|
|
public Collection<MapType> getMapTypes(){
|
|
|
|
return maps.values();
|
|
|
|
}
|
|
|
|
|
|
|
|
public RenderManager getRenderManager() {
|
|
|
|
return renderManager;
|
|
|
|
}
|
|
|
|
|
2020-08-24 23:58:11 +02:00
|
|
|
public File getRenderManagerSaveFile() throws IOException {
|
|
|
|
if (blueMap == null) return null;
|
2021-03-28 18:56:26 +02:00
|
|
|
return new File(blueMap.getCoreConfig().getDataFolder(), "rmstate");
|
2020-08-23 23:29:14 +02:00
|
|
|
}
|
|
|
|
|
2020-01-13 17:13:20 +01:00
|
|
|
public MapUpdateHandler getUpdateHandler() {
|
|
|
|
return updateHandler;
|
|
|
|
}
|
|
|
|
|
2020-09-25 18:24:07 +02:00
|
|
|
public boolean flushWorldUpdates(UUID worldUUID) throws IOException {
|
|
|
|
if (serverInterface.persistWorldChanges(worldUUID)) {
|
|
|
|
updateHandler.onWorldSaveToDisk(worldUUID);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-23 23:29:14 +02:00
|
|
|
public WebServer getWebServer() {
|
2020-01-13 17:13:20 +01:00
|
|
|
return webServer;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isLoaded() {
|
|
|
|
return loaded;
|
|
|
|
}
|
|
|
|
|
2020-09-25 18:24:07 +02:00
|
|
|
public String getImplementationType() {
|
|
|
|
return implementationType;
|
|
|
|
}
|
2020-10-04 18:01:33 +02:00
|
|
|
|
|
|
|
public MinecraftVersion getMinecraftVersion() {
|
|
|
|
return minecraftVersion;
|
|
|
|
}
|
2020-09-25 18:24:07 +02:00
|
|
|
|
2020-01-13 17:13:20 +01:00
|
|
|
}
|