mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-22 02:26:00 +01:00
Add abillity to freeze maps (they wont update) and persist stopped render-threads
This commit is contained in:
parent
113d7f3feb
commit
e90f329ad8
@ -47,6 +47,8 @@
|
||||
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.core.webserver.WebServer;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -72,6 +74,8 @@ public class Plugin {
|
||||
private WebServerConfig webServerConfig;
|
||||
private PluginConfig pluginConfig;
|
||||
|
||||
private PluginStatus pluginStatus;
|
||||
|
||||
private Map<UUID, World> worlds;
|
||||
private Map<String, BmMap> maps;
|
||||
|
||||
@ -82,7 +86,7 @@ public class Plugin {
|
||||
private TimerTask saveTask;
|
||||
private TimerTask metricsTask;
|
||||
|
||||
private Collection<RegionFileWatchService> regionFileWatchServices;
|
||||
private Map<String, RegionFileWatchService> regionFileWatchServices;
|
||||
|
||||
private PlayerSkinUpdater skinUpdater;
|
||||
|
||||
@ -118,6 +122,17 @@ public void load() throws IOException, ParseResourceException {
|
||||
true
|
||||
));
|
||||
|
||||
//load plugin status
|
||||
try {
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
|
||||
.file(new File(getCoreConfig().getDataFolder(), "pluginStatus.json"))
|
||||
.build();
|
||||
pluginStatus = loader.load().get(PluginStatus.class);
|
||||
} catch (SerializationException ex) {
|
||||
Logger.global.logWarning("Failed to load pluginStatus.json (invalid format), creating a new one...");
|
||||
pluginStatus = new PluginStatus();
|
||||
}
|
||||
|
||||
//create and start webserver
|
||||
if (webServerConfig.isWebserverEnabled()) {
|
||||
FileUtils.mkDirs(webServerConfig.getWebRoot());
|
||||
@ -168,11 +183,17 @@ public void load() throws IOException, ParseResourceException {
|
||||
|
||||
//update all maps
|
||||
for (BmMap map : maps.values()) {
|
||||
renderManager.scheduleRenderTask(new MapUpdateTask(map));
|
||||
if (pluginStatus.getMapStatus(map).isUpdateEnabled()) {
|
||||
renderManager.scheduleRenderTask(new MapUpdateTask(map));
|
||||
}
|
||||
}
|
||||
|
||||
//start render-manager
|
||||
renderManager.start(coreConfig.getRenderThreadCount());
|
||||
if (pluginStatus.isRenderThreadsEnabled()) {
|
||||
renderManager.start(coreConfig.getRenderThreadCount());
|
||||
} else {
|
||||
Logger.global.logInfo("Render-Threads are STOPPED! Use the command 'bluemap start' to start them.");
|
||||
}
|
||||
|
||||
//update webapp and settings
|
||||
blueMap.createOrUpdateWebApp(false);
|
||||
@ -194,12 +215,7 @@ public void load() throws IOException, ParseResourceException {
|
||||
saveTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (Plugin.this) {
|
||||
if (maps == null) return;
|
||||
for (BmMap map : maps.values()) {
|
||||
map.save();
|
||||
}
|
||||
}
|
||||
save();
|
||||
}
|
||||
};
|
||||
daemonTimer.schedule(saveTask, TimeUnit.MINUTES.toMillis(2), TimeUnit.MINUTES.toMillis(2));
|
||||
@ -215,14 +231,10 @@ public void run() {
|
||||
daemonTimer.scheduleAtFixedRate(metricsTask, TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(30));
|
||||
|
||||
//watch map-changes
|
||||
this.regionFileWatchServices = new ArrayList<>();
|
||||
this.regionFileWatchServices = new HashMap<>();
|
||||
for (BmMap map : maps.values()) {
|
||||
try {
|
||||
RegionFileWatchService watcher = new RegionFileWatchService(renderManager, map, false);
|
||||
watcher.start();
|
||||
regionFileWatchServices.add(watcher);
|
||||
} catch (IOException ex) {
|
||||
Logger.global.logError("Failed to create file-watcher for map: " + map.getId() + " (This map might not automatically update)", ex);
|
||||
if (pluginStatus.getMapStatus(map).isUpdateEnabled()) {
|
||||
startWatchingMap(map);
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,6 +257,8 @@ public void unload() {
|
||||
try {
|
||||
loadingLock.interruptAndLock();
|
||||
synchronized (this) {
|
||||
//save
|
||||
save();
|
||||
|
||||
//disable api
|
||||
if (api != null) api.unregister();
|
||||
@ -264,7 +278,7 @@ public void unload() {
|
||||
|
||||
//stop file-watchers
|
||||
if (regionFileWatchServices != null) {
|
||||
for (RegionFileWatchService watcher : regionFileWatchServices) {
|
||||
for (RegionFileWatchService watcher : regionFileWatchServices.values()) {
|
||||
watcher.close();
|
||||
}
|
||||
regionFileWatchServices.clear();
|
||||
@ -278,13 +292,6 @@ public void unload() {
|
||||
if (webServer != null) webServer.close();
|
||||
webServer = null;
|
||||
|
||||
//save renders
|
||||
if (maps != null) {
|
||||
for (BmMap map : maps.values()) {
|
||||
map.save();
|
||||
}
|
||||
}
|
||||
|
||||
//clear resources and configs
|
||||
blueMap = null;
|
||||
worlds = null;
|
||||
@ -295,6 +302,8 @@ public void unload() {
|
||||
webServerConfig = null;
|
||||
pluginConfig = null;
|
||||
|
||||
pluginStatus = null;
|
||||
|
||||
//done
|
||||
loaded = false;
|
||||
}
|
||||
@ -308,6 +317,44 @@ public void reload() throws IOException, ParseResourceException {
|
||||
load();
|
||||
}
|
||||
|
||||
public synchronized void save() {
|
||||
if (pluginStatus != null) {
|
||||
try {
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
|
||||
.file(new File(getCoreConfig().getDataFolder(), "pluginStatus.json"))
|
||||
.build();
|
||||
loader.save(loader.createNode().set(PluginStatus.class, pluginStatus));
|
||||
} catch (IOException ex) {
|
||||
Logger.global.logError("Failed to save pluginStatus.json!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (maps != null) {
|
||||
for (BmMap map : maps.values()) {
|
||||
map.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void startWatchingMap(BmMap map) {
|
||||
stopWatchingMap(map);
|
||||
|
||||
try {
|
||||
RegionFileWatchService watcher = new RegionFileWatchService(renderManager, map, false);
|
||||
watcher.start();
|
||||
regionFileWatchServices.put(map.getId(), watcher);
|
||||
} catch (IOException ex) {
|
||||
Logger.global.logError("Failed to create file-watcher for map: " + map.getId() + " (This means the map might not automatically update)", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void stopWatchingMap(BmMap map) {
|
||||
RegionFileWatchService watcher = regionFileWatchServices.remove(map.getId());
|
||||
if (watcher != null) {
|
||||
watcher.close();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean flushWorldUpdates(UUID worldUUID) throws IOException {
|
||||
return serverInterface.persistWorldChanges(worldUUID);
|
||||
}
|
||||
@ -331,6 +378,10 @@ public WebServerConfig getWebServerConfig() {
|
||||
public PluginConfig getPluginConfig() {
|
||||
return pluginConfig;
|
||||
}
|
||||
|
||||
public PluginStatus getPluginStatus() {
|
||||
return pluginStatus;
|
||||
}
|
||||
|
||||
public World getWorld(UUID uuid){
|
||||
return worlds.get(uuid);
|
||||
|
@ -24,11 +24,44 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common.plugin;
|
||||
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
@ConfigSerializable
|
||||
public class PluginStatus {
|
||||
|
||||
private boolean renderThreadsEnabled = true;
|
||||
private Map<String, MapStatus> maps = new HashMap<>();
|
||||
|
||||
public boolean isRenderThreadsEnabled() {
|
||||
return renderThreadsEnabled;
|
||||
}
|
||||
|
||||
public void setRenderThreadsEnabled(boolean renderThreadsEnabled) {
|
||||
this.renderThreadsEnabled = renderThreadsEnabled;
|
||||
}
|
||||
|
||||
public MapStatus getMapStatus(BmMap map) {
|
||||
return maps.computeIfAbsent(map.getId(), k -> new MapStatus());
|
||||
}
|
||||
|
||||
@ConfigSerializable
|
||||
public static class MapStatus {
|
||||
|
||||
private boolean updateEnabled = true;
|
||||
|
||||
public boolean isUpdateEnabled() {
|
||||
return updateEnabled;
|
||||
}
|
||||
|
||||
public void setUpdateEnabled(boolean update) {
|
||||
this.updateEnabled = update;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,13 +44,12 @@
|
||||
import de.bluecolored.bluemap.api.marker.MarkerSet;
|
||||
import de.bluecolored.bluemap.api.marker.POIMarker;
|
||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||
import de.bluecolored.bluemap.common.plugin.PluginStatus;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource;
|
||||
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||
import de.bluecolored.bluemap.common.plugin.text.TextColor;
|
||||
import de.bluecolored.bluemap.common.plugin.text.TextFormat;
|
||||
import de.bluecolored.bluemap.common.rendermanager.MapPurgeTask;
|
||||
import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask;
|
||||
import de.bluecolored.bluemap.common.rendermanager.RenderTask;
|
||||
import de.bluecolored.bluemap.common.rendermanager.*;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
@ -140,18 +139,32 @@ public void init() {
|
||||
|
||||
.build();
|
||||
|
||||
LiteralCommandNode<S> pauseCommand =
|
||||
LiteralCommandNode<S> stopCommand =
|
||||
literal("stop")
|
||||
.requires(requirements("bluemap.stop"))
|
||||
.executes(this::stopCommand)
|
||||
.build();
|
||||
|
||||
LiteralCommandNode<S> resumeCommand =
|
||||
LiteralCommandNode<S> startCommand =
|
||||
literal("start")
|
||||
.requires(requirements("bluemap.start"))
|
||||
.executes(this::startCommand)
|
||||
.build();
|
||||
|
||||
LiteralCommandNode<S> freezeCommand =
|
||||
literal("freeze")
|
||||
.requires(requirements("bluemap.freeze"))
|
||||
.then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin))
|
||||
.executes(this::freezeCommand))
|
||||
.build();
|
||||
|
||||
LiteralCommandNode<S> unfreezeCommand =
|
||||
literal("unfreeze")
|
||||
.requires(requirements("bluemap.freeze"))
|
||||
.then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin))
|
||||
.executes(this::unfreezeCommand))
|
||||
.build();
|
||||
|
||||
LiteralCommandNode<S> forceUpdateCommand =
|
||||
addRenderArguments(
|
||||
literal("force-update")
|
||||
@ -227,8 +240,10 @@ public void init() {
|
||||
baseCommand.addChild(helpCommand);
|
||||
baseCommand.addChild(reloadCommand);
|
||||
baseCommand.addChild(debugCommand);
|
||||
baseCommand.addChild(pauseCommand);
|
||||
baseCommand.addChild(resumeCommand);
|
||||
baseCommand.addChild(stopCommand);
|
||||
baseCommand.addChild(startCommand);
|
||||
baseCommand.addChild(freezeCommand);
|
||||
baseCommand.addChild(unfreezeCommand);
|
||||
baseCommand.addChild(forceUpdateCommand);
|
||||
baseCommand.addChild(updateCommand);
|
||||
baseCommand.addChild(cancelCommand);
|
||||
@ -521,26 +536,113 @@ public int stopCommand(CommandContext<S> context) {
|
||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
|
||||
if (plugin.getRenderManager().isRunning()) {
|
||||
plugin.getRenderManager().stop();
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Render-Threads stopped!"));
|
||||
return 1;
|
||||
new Thread(() -> {
|
||||
plugin.getPluginStatus().setRenderThreadsEnabled(false);
|
||||
|
||||
plugin.getRenderManager().stop();
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Render-Threads stopped!"));
|
||||
|
||||
plugin.save();
|
||||
}).start();
|
||||
} else {
|
||||
source.sendMessage(Text.of(TextColor.RED, "Render-Threads are already stopped!"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int startCommand(CommandContext<S> context) {
|
||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
|
||||
if (!plugin.getRenderManager().isRunning()) {
|
||||
plugin.getRenderManager().start(plugin.getCoreConfig().getRenderThreadCount());
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Render-Threads started!"));
|
||||
return 1;
|
||||
new Thread(() -> {
|
||||
plugin.getPluginStatus().setRenderThreadsEnabled(true);
|
||||
|
||||
plugin.getRenderManager().start(plugin.getCoreConfig().getRenderThreadCount());
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Render-Threads started!"));
|
||||
|
||||
plugin.save();
|
||||
}).start();
|
||||
} else {
|
||||
source.sendMessage(Text.of(TextColor.RED, "Render-Threads are already running!"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int freezeCommand(CommandContext<S> context) {
|
||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
|
||||
// parse map argument
|
||||
String mapString = context.getArgument("map", String.class);
|
||||
BmMap map = parseMap(mapString).orElse(null);
|
||||
|
||||
if (map == null) {
|
||||
source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, mapString));
|
||||
return 0;
|
||||
}
|
||||
|
||||
PluginStatus.MapStatus mapStatus = plugin.getPluginStatus().getMapStatus(map);
|
||||
if (mapStatus.isUpdateEnabled()) {
|
||||
new Thread(() -> {
|
||||
mapStatus.setUpdateEnabled(false);
|
||||
|
||||
plugin.stopWatchingMap(map);
|
||||
plugin.getRenderManager().removeRenderTasksIf(task -> {
|
||||
if (task instanceof MapUpdateTask)
|
||||
return ((MapUpdateTask) task).getMap().equals(map);
|
||||
|
||||
if (task instanceof WorldRegionRenderTask)
|
||||
return ((WorldRegionRenderTask) task).getMap().equals(map);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Map ", TextColor.WHITE, mapString, TextColor.GREEN, " is now frozen and will no longer be automatically updated!"));
|
||||
source.sendMessage(Text.of(TextColor.GRAY, "Any currently scheduled updates for this map have been cancelled."));
|
||||
|
||||
plugin.save();
|
||||
}).start();
|
||||
} else {
|
||||
source.sendMessage(Text.of(TextColor.RED, "This map is already frozen!"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int unfreezeCommand(CommandContext<S> context) {
|
||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
|
||||
// parse map argument
|
||||
String mapString = context.getArgument("map", String.class);
|
||||
BmMap map = parseMap(mapString).orElse(null);
|
||||
|
||||
if (map == null) {
|
||||
source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, mapString));
|
||||
return 0;
|
||||
}
|
||||
|
||||
PluginStatus.MapStatus mapStatus = plugin.getPluginStatus().getMapStatus(map);
|
||||
if (!mapStatus.isUpdateEnabled()) {
|
||||
new Thread(() -> {
|
||||
mapStatus.setUpdateEnabled(true);
|
||||
|
||||
plugin.startWatchingMap(map);
|
||||
plugin.getRenderManager().scheduleRenderTask(new MapUpdateTask(map));
|
||||
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Map ", TextColor.WHITE, mapString, TextColor.GREEN, " is no longer frozen and will be automatically updated!"));
|
||||
|
||||
plugin.save();
|
||||
}).start();
|
||||
} else {
|
||||
source.sendMessage(Text.of(TextColor.RED, "This map is not frozen!"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int forceUpdateCommand(CommandContext<S> context) {
|
||||
@ -727,7 +829,21 @@ public int mapsCommand(CommandContext<S> context) {
|
||||
|
||||
source.sendMessage(Text.of(TextColor.BLUE, "Maps loaded by BlueMap:"));
|
||||
for (BmMap map : plugin.getMapTypes()) {
|
||||
source.sendMessage(Text.of(TextColor.GRAY, " - ", TextColor.WHITE, map.getId(), TextColor.GRAY, " (" + map.getName() + ")").setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GRAY, map.getWorld().getName())));
|
||||
boolean unfrozen = plugin.getPluginStatus().getMapStatus(map).isUpdateEnabled();
|
||||
if (unfrozen) {
|
||||
source.sendMessage(Text.of(
|
||||
TextColor.GRAY, " - ",
|
||||
TextColor.WHITE, map.getId(),
|
||||
TextColor.GRAY, " (" + map.getName() + ")"
|
||||
).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GRAY, map.getWorld().getName())));
|
||||
} else {
|
||||
source.sendMessage(Text.of(
|
||||
TextColor.GRAY, " - ",
|
||||
TextColor.WHITE, map.getId(),
|
||||
TextColor.GRAY, " (" + map.getName() + ") - ",
|
||||
TextColor.AQUA, TextFormat.ITALIC, "frozen!"
|
||||
).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GRAY, map.getWorld().getName())));
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -741,7 +857,7 @@ public int createMarkerCommand(CommandContext<S> context) {
|
||||
.replace("<", "<")
|
||||
.replace(">", ">"); //no html via commands
|
||||
|
||||
// parse world/map argument
|
||||
// parse map argument
|
||||
String mapString = context.getArgument("map", String.class);
|
||||
BmMap map = parseMap(mapString).orElse(null);
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class RenderManager {
|
||||
private static final AtomicInteger nextRenderManagerIndex = new AtomicInteger(0);
|
||||
@ -176,6 +177,17 @@ public boolean removeRenderTask(RenderTask task) {
|
||||
}
|
||||
}
|
||||
|
||||
public void removeRenderTasksIf(Predicate<RenderTask> removeCondition) {
|
||||
synchronized (this.renderTasks) {
|
||||
if (this.renderTasks.isEmpty()) return;
|
||||
|
||||
RenderTask first = renderTasks.removeFirst();
|
||||
if (removeCondition.test(first)) first.cancel();
|
||||
renderTasks.removeIf(removeCondition);
|
||||
renderTasks.addFirst(first);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllRenderTasks() {
|
||||
synchronized (this.renderTasks) {
|
||||
if (this.renderTasks.isEmpty()) return;
|
||||
|
Loading…
Reference in New Issue
Block a user