mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-22 02:26:00 +01:00
Reimplement render command and add update command
This commit is contained in:
parent
412e27682f
commit
d2d6071446
@ -38,7 +38,7 @@ public InterruptableReentrantLock(boolean fair) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Aquires the lock and interrupts the currently holding thread if there is any.
|
||||
* Acquires the lock and interrupts the currently holding thread if there is any.
|
||||
*/
|
||||
public void interruptAndLock() {
|
||||
while (!tryLock()) {
|
||||
|
@ -32,8 +32,7 @@
|
||||
import de.bluecolored.bluemap.common.live.LiveAPIRequestHandler;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||
import de.bluecolored.bluemap.common.plugin.skins.PlayerSkinUpdater;
|
||||
import de.bluecolored.bluemap.common.rendermanager.RenderManager;
|
||||
import de.bluecolored.bluemap.common.rendermanager.WorldRegionRenderTask;
|
||||
import de.bluecolored.bluemap.common.rendermanager.*;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||
import de.bluecolored.bluemap.core.config.CoreConfig;
|
||||
@ -79,7 +78,7 @@ public class Plugin {
|
||||
private RenderManager renderManager;
|
||||
private WebServer webServer;
|
||||
|
||||
private final Timer daemonTimer;
|
||||
private Timer daemonTimer;
|
||||
private TimerTask saveTask;
|
||||
private TimerTask metricsTask;
|
||||
|
||||
@ -93,8 +92,6 @@ public Plugin(MinecraftVersion minecraftVersion, String implementationType, Serv
|
||||
this.minecraftVersion = minecraftVersion;
|
||||
this.implementationType = implementationType.toLowerCase();
|
||||
this.serverInterface = serverInterface;
|
||||
|
||||
this.daemonTimer = new Timer("BlueMap-Plugin-Daemon-Timer", true);
|
||||
}
|
||||
|
||||
public void load() throws IOException, ParseResourceException {
|
||||
@ -120,7 +117,7 @@ public void load() throws IOException, ParseResourceException {
|
||||
true,
|
||||
true
|
||||
));
|
||||
|
||||
|
||||
//create and start webserver
|
||||
if (webServerConfig.isWebserverEnabled()) {
|
||||
FileUtils.mkDirs(webServerConfig.getWebRoot());
|
||||
@ -161,10 +158,28 @@ public void load() throws IOException, ParseResourceException {
|
||||
//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...");
|
||||
|
||||
unload();
|
||||
return;
|
||||
}
|
||||
|
||||
//initialize render manager
|
||||
renderManager = new RenderManager();
|
||||
|
||||
//update all maps
|
||||
for (BmMap map : maps.values()) {
|
||||
Collection<Vector2i> regions = map.getWorld().listRegions();
|
||||
List<WorldRegionRenderTask> mapTasks = new ArrayList<>(regions.size());
|
||||
|
||||
for (Vector2i region : regions)
|
||||
mapTasks.add(new WorldRegionRenderTask(map, region));
|
||||
mapTasks.sort(WorldRegionRenderTask::compare);
|
||||
|
||||
CombinedRenderTask<WorldRegionRenderTask> mapUpdateTask = new CombinedRenderTask<>("Update map '" + map.getId() + "'", mapTasks);
|
||||
renderManager.scheduleRenderTask(mapUpdateTask);
|
||||
}
|
||||
|
||||
//start render-manager
|
||||
renderManager.start(coreConfig.getRenderThreadCount());
|
||||
|
||||
//update webapp and settings
|
||||
@ -180,6 +195,9 @@ public void load() throws IOException, ParseResourceException {
|
||||
serverInterface.registerListener(skinUpdater);
|
||||
}
|
||||
|
||||
//init timer
|
||||
daemonTimer = new Timer("BlueMap-Plugin-Daemon-Timer", true);
|
||||
|
||||
//periodically save
|
||||
saveTask = new TimerTask() {
|
||||
@Override
|
||||
@ -193,7 +211,7 @@ public void run() {
|
||||
}
|
||||
};
|
||||
daemonTimer.schedule(saveTask, TimeUnit.MINUTES.toMillis(2), TimeUnit.MINUTES.toMillis(2));
|
||||
|
||||
|
||||
//metrics
|
||||
metricsTask = new TimerTask() {
|
||||
@Override
|
||||
@ -203,12 +221,6 @@ public void run() {
|
||||
}
|
||||
};
|
||||
daemonTimer.scheduleAtFixedRate(metricsTask, TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(30));
|
||||
|
||||
loaded = true;
|
||||
|
||||
//enable api
|
||||
this.api = new BlueMapAPIImpl(this);
|
||||
this.api.register();
|
||||
|
||||
//watch map-changes
|
||||
this.regionFileWatchServices = new ArrayList<>();
|
||||
@ -222,13 +234,12 @@ public void run() {
|
||||
}
|
||||
}
|
||||
|
||||
//update all maps
|
||||
for (BmMap map : maps.values()) {
|
||||
for (Vector2i region : map.getWorld().listRegions()){
|
||||
renderManager.scheduleRenderTask(new WorldRegionRenderTask(map, region));
|
||||
}
|
||||
}
|
||||
|
||||
//enable api
|
||||
this.api = new BlueMapAPIImpl(this);
|
||||
this.api.register();
|
||||
|
||||
//done
|
||||
loaded = true;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
@ -249,22 +260,31 @@ public void unload() {
|
||||
|
||||
//unregister listeners
|
||||
serverInterface.unregisterAllListeners();
|
||||
skinUpdater = null;
|
||||
|
||||
//stop scheduled threads
|
||||
if (metricsTask != null) metricsTask.cancel();
|
||||
metricsTask = null;
|
||||
if (saveTask != null) saveTask.cancel();
|
||||
saveTask = null;
|
||||
if (daemonTimer != null) daemonTimer.cancel();
|
||||
daemonTimer = null;
|
||||
|
||||
// stop file-watchers
|
||||
//stop file-watchers
|
||||
if (regionFileWatchServices != null) {
|
||||
for (RegionFileWatchService watcher : regionFileWatchServices) {
|
||||
watcher.close();
|
||||
}
|
||||
regionFileWatchServices.clear();
|
||||
}
|
||||
regionFileWatchServices = null;
|
||||
|
||||
//stop services
|
||||
if (renderManager != null) renderManager.stop();
|
||||
renderManager = null;
|
||||
|
||||
if (webServer != null) webServer.close();
|
||||
webServer = null;
|
||||
|
||||
//save renders
|
||||
if (maps != null) {
|
||||
@ -277,13 +297,14 @@ public void unload() {
|
||||
blueMap = null;
|
||||
worlds = null;
|
||||
maps = null;
|
||||
renderManager = null;
|
||||
webServer = null;
|
||||
|
||||
coreConfig = null;
|
||||
renderConfig = null;
|
||||
webServerConfig = null;
|
||||
pluginConfig = null;
|
||||
|
||||
|
||||
//done
|
||||
loaded = false;
|
||||
|
||||
}
|
||||
} finally {
|
||||
loadingLock.unlock();
|
||||
@ -334,7 +355,7 @@ public Collection<BmMap> getMapTypes(){
|
||||
public RenderManager getRenderManager() {
|
||||
return renderManager;
|
||||
}
|
||||
|
||||
|
||||
public WebServer getWebServer() {
|
||||
return webServer;
|
||||
}
|
||||
|
@ -24,9 +24,6 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common.plugin.commands;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
@ -34,6 +31,9 @@
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public abstract class AbstractSuggestionProvider<S> implements SuggestionProvider<S> {
|
||||
|
||||
@Override
|
||||
|
@ -24,13 +24,16 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common.plugin.commands;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||
import de.bluecolored.bluemap.common.plugin.text.TextColor;
|
||||
import de.bluecolored.bluemap.common.rendermanager.RenderManager;
|
||||
import de.bluecolored.bluemap.common.rendermanager.RenderTask;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
import de.bluecolored.bluemap.core.world.Grid;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
import org.apache.commons.lang3.time.DurationFormatUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -48,25 +51,61 @@ public List<Text> createStatusMessage(){
|
||||
List<Text> lines = new ArrayList<>();
|
||||
|
||||
RenderManager renderer = plugin.getRenderManager();
|
||||
List<RenderTask> tasks = renderer.getScheduledRenderTasks();
|
||||
|
||||
lines.add(Text.of(TextColor.BLUE, "BlueMap - Status:"));
|
||||
|
||||
if (renderer.isRunning()) {
|
||||
lines.add(Text.of(TextColor.WHITE, " Render-Threads are ",
|
||||
Text.of(TextColor.GREEN, "running")
|
||||
.setHoverText(Text.of("click to pause rendering"))
|
||||
.setClickAction(Text.ClickAction.RUN_COMMAND, "/bluemap pause"),
|
||||
TextColor.GRAY, "!"));
|
||||
Text status;
|
||||
if (tasks.isEmpty()) {
|
||||
status = Text.of(TextColor.GRAY, "idle");
|
||||
} else {
|
||||
status = Text.of(TextColor.GREEN, "running");
|
||||
}
|
||||
|
||||
status.setHoverText(Text.of("click to stop rendering"));
|
||||
status.setClickAction(Text.ClickAction.RUN_COMMAND, "/bluemap stop");
|
||||
|
||||
lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", status, TextColor.WHITE, "!"));
|
||||
|
||||
if (!tasks.isEmpty()) {
|
||||
lines.add(Text.of(TextColor.WHITE, " Queued Tasks (" + tasks.size() + "):"));
|
||||
for (int i = 0; i < tasks.size(); i++) {
|
||||
if (i >= 10){
|
||||
lines.add(Text.of(TextColor.GRAY, "..."));
|
||||
break;
|
||||
}
|
||||
|
||||
RenderTask task = tasks.get(i);
|
||||
lines.add(Text.of(TextColor.GRAY, " - ", TextColor.GOLD, task.getDescription()));
|
||||
|
||||
if (i == 0) {
|
||||
lines.add(Text.of(TextColor.GRAY, " Progress: ", TextColor.WHITE,
|
||||
(Math.round(task.estimateProgress() * 10000) / 100.0) + "%"));
|
||||
lines.add(Text.of(TextColor.GRAY, " ETA: ", TextColor.WHITE, DurationFormatUtils.formatDuration(renderer.estimateCurrentRenderTaskTimeRemaining(), "HH:mm:ss")));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lines.add(Text.of(TextColor.WHITE, " Render-Threads are ",
|
||||
Text.of(TextColor.RED, "paused")
|
||||
.setHoverText(Text.of("click to resume rendering"))
|
||||
.setClickAction(Text.ClickAction.RUN_COMMAND, "/bluemap resume"),
|
||||
Text.of(TextColor.RED, "stopped")
|
||||
.setHoverText(Text.of("click to start rendering"))
|
||||
.setClickAction(Text.ClickAction.RUN_COMMAND, "/bluemap start"),
|
||||
TextColor.GRAY, "!"));
|
||||
}
|
||||
|
||||
List<RenderTask> tasks = renderer.getScheduledRenderTasks();
|
||||
lines.add(Text.of(TextColor.WHITE, " Scheduled tasks: ", TextColor.GOLD, tasks.size()));
|
||||
if (!tasks.isEmpty()) {
|
||||
lines.add(Text.of(TextColor.WHITE, " Queued Tasks (" + tasks.size() + "):"));
|
||||
for (int i = 0; i < tasks.size(); i++) {
|
||||
if (i >= 10){
|
||||
lines.add(Text.of(TextColor.GRAY, "..."));
|
||||
break;
|
||||
}
|
||||
|
||||
RenderTask task = tasks.get(i);
|
||||
lines.add(Text.of(TextColor.GRAY, " - ", TextColor.WHITE, task.getDescription()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
@ -88,4 +127,25 @@ public Text mapHelperHover() {
|
||||
|
||||
return Text.of("map").setHoverText(Text.of(TextColor.WHITE, "Available maps: \n", TextColor.GRAY, joiner.toString()));
|
||||
}
|
||||
|
||||
public List<Vector2i> getRegions(World world, Vector2i center, int radius) {
|
||||
if (center == null || radius < 0) return new ArrayList<>(world.listRegions());
|
||||
|
||||
List<Vector2i> regions = new ArrayList<>();
|
||||
|
||||
Grid regionGrid = world.getRegionGrid();
|
||||
Vector2i halfCell = regionGrid.getGridSize().div(2);
|
||||
int increasedRadiusSquared = (int) Math.pow(radius + Math.ceil(halfCell.length()), 2);
|
||||
|
||||
for (Vector2i region : world.listRegions()) {
|
||||
Vector2i min = regionGrid.getCellMin(region);
|
||||
Vector2i regionCenter = min.add(halfCell);
|
||||
|
||||
if (regionCenter.distanceSquared(center) <= increasedRadiusSquared)
|
||||
regions.add(region);
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,11 +28,13 @@
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.arguments.DoubleArgumentType;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
@ -47,12 +49,15 @@
|
||||
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.CombinedRenderTask;
|
||||
import de.bluecolored.bluemap.common.rendermanager.WorldRegionRenderTask;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
import de.bluecolored.bluemap.core.mca.MCAChunk;
|
||||
import de.bluecolored.bluemap.core.map.MapRenderState;
|
||||
import de.bluecolored.bluemap.core.mca.ChunkAnvil112;
|
||||
import de.bluecolored.bluemap.core.mca.MCAChunk;
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
@ -61,6 +66,8 @@
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
@ -137,45 +144,37 @@ public void init() {
|
||||
.build();
|
||||
|
||||
LiteralCommandNode<S> pauseCommand =
|
||||
literal("pause")
|
||||
.requires(requirements("bluemap.pause"))
|
||||
.executes(this::pauseCommand)
|
||||
literal("stop")
|
||||
.requires(requirements("bluemap.stop"))
|
||||
.executes(this::stopCommand)
|
||||
.build();
|
||||
|
||||
LiteralCommandNode<S> resumeCommand =
|
||||
literal("resume")
|
||||
.requires(requirements("bluemap.resume"))
|
||||
.executes(this::resumeCommand)
|
||||
literal("start")
|
||||
.requires(requirements("bluemap.start"))
|
||||
.executes(this::startCommand)
|
||||
.build();
|
||||
|
||||
LiteralCommandNode<S> renderCommand =
|
||||
literal("render")
|
||||
.requires(requirements("bluemap.render"))
|
||||
.executes(this::renderCommand) // /bluemap render
|
||||
addRenderArguments(
|
||||
literal("render")
|
||||
.requires(requirements("bluemap.render")),
|
||||
this::renderCommand
|
||||
).build();
|
||||
|
||||
.then(argument("radius", IntegerArgumentType.integer())
|
||||
.executes(this::renderCommand)) // /bluemap render <radius>
|
||||
|
||||
.then(argument("x", DoubleArgumentType.doubleArg())
|
||||
.then(argument("z", DoubleArgumentType.doubleArg())
|
||||
.then(argument("radius", IntegerArgumentType.integer())
|
||||
.executes(this::renderCommand)))) // /bluemap render <x> <z> <radius>
|
||||
|
||||
.then(argument("world|map", StringArgumentType.string()).suggests(new WorldOrMapSuggestionProvider<>(plugin))
|
||||
.executes(this::renderCommand) // /bluemap render <world|map>
|
||||
|
||||
.then(argument("x", DoubleArgumentType.doubleArg())
|
||||
.then(argument("z", DoubleArgumentType.doubleArg())
|
||||
.then(argument("radius", IntegerArgumentType.integer())
|
||||
.executes(this::renderCommand))))) // /bluemap render <world|map> <x> <z> <radius>
|
||||
.build();
|
||||
LiteralCommandNode<S> updateCommand =
|
||||
addRenderArguments(
|
||||
literal("update")
|
||||
.requires(requirements("bluemap.update")),
|
||||
this::updateCommand
|
||||
).build();
|
||||
|
||||
LiteralCommandNode<S> purgeCommand =
|
||||
literal("purge")
|
||||
.requires(requirements("bluemap.render"))
|
||||
.then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin))
|
||||
.executes(this::purgeCommand))
|
||||
.build();
|
||||
.requires(requirements("bluemap.render"))
|
||||
.then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin))
|
||||
.executes(this::purgeCommand))
|
||||
.build();
|
||||
|
||||
LiteralCommandNode<S> worldsCommand =
|
||||
literal("worlds")
|
||||
@ -225,7 +224,8 @@ public void init() {
|
||||
baseCommand.addChild(debugCommand);
|
||||
baseCommand.addChild(pauseCommand);
|
||||
baseCommand.addChild(resumeCommand);
|
||||
//baseCommand.addChild(renderCommand);
|
||||
baseCommand.addChild(renderCommand);
|
||||
baseCommand.addChild(updateCommand);
|
||||
baseCommand.addChild(purgeCommand);
|
||||
baseCommand.addChild(worldsCommand);
|
||||
baseCommand.addChild(mapsCommand);
|
||||
@ -233,6 +233,27 @@ public void init() {
|
||||
markerCommand.addChild(createMarkerCommand);
|
||||
markerCommand.addChild(removeMarkerCommand);
|
||||
}
|
||||
|
||||
private <B extends ArgumentBuilder<S, B>> B addRenderArguments(B builder, Command<S> command) {
|
||||
return builder
|
||||
.executes(command) // /bluemap render
|
||||
|
||||
.then(argument("radius", IntegerArgumentType.integer())
|
||||
.executes(command)) // /bluemap render <radius>
|
||||
|
||||
.then(argument("x", DoubleArgumentType.doubleArg())
|
||||
.then(argument("z", DoubleArgumentType.doubleArg())
|
||||
.then(argument("radius", IntegerArgumentType.integer())
|
||||
.executes(command)))) // /bluemap render <x> <z> <radius>
|
||||
|
||||
.then(argument("world|map", StringArgumentType.string()).suggests(new WorldOrMapSuggestionProvider<>(plugin))
|
||||
.executes(command) // /bluemap render <world|map>
|
||||
|
||||
.then(argument("x", DoubleArgumentType.doubleArg())
|
||||
.then(argument("z", DoubleArgumentType.doubleArg())
|
||||
.then(argument("radius", IntegerArgumentType.integer())
|
||||
.executes(command))))); // /bluemap render <world|map> <x> <z> <radius>
|
||||
}
|
||||
|
||||
private Predicate<S> requirements(String permission){
|
||||
return s -> {
|
||||
@ -498,58 +519,66 @@ public int debugBlockCommand(CommandContext<S> context) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int pauseCommand(CommandContext<S> context) {
|
||||
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, "BlueMap rendering paused!"));
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Render-Threads stopped!"));
|
||||
return 1;
|
||||
} else {
|
||||
source.sendMessage(Text.of(TextColor.RED, "BlueMap rendering are already paused!"));
|
||||
source.sendMessage(Text.of(TextColor.RED, "Render-Threads are already stopped!"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int resumeCommand(CommandContext<S> context) {
|
||||
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, "BlueMap renders resumed!"));
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Render-Threads started!"));
|
||||
return 1;
|
||||
} else {
|
||||
source.sendMessage(Text.of(TextColor.RED, "BlueMap renders are already running!"));
|
||||
source.sendMessage(Text.of(TextColor.RED, "Render-Threads are already running!"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int renderCommand(CommandContext<S> context) {
|
||||
return updateCommand(context, true);
|
||||
}
|
||||
|
||||
public int updateCommand(CommandContext<S> context) {
|
||||
return updateCommand(context, false);
|
||||
}
|
||||
|
||||
public int updateCommand(CommandContext<S> context, boolean force) {
|
||||
final CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
|
||||
// parse world/map argument
|
||||
Optional<String> worldOrMap = getOptionalArgument(context, "world|map", String.class);
|
||||
|
||||
final World world;
|
||||
final BmMap map;
|
||||
final World worldToRender;
|
||||
final BmMap mapToRender;
|
||||
if (worldOrMap.isPresent()) {
|
||||
world = parseWorld(worldOrMap.get()).orElse(null);
|
||||
worldToRender = parseWorld(worldOrMap.get()).orElse(null);
|
||||
|
||||
if (world == null) {
|
||||
map = parseMap(worldOrMap.get()).orElse(null);
|
||||
if (worldToRender == null) {
|
||||
mapToRender = parseMap(worldOrMap.get()).orElse(null);
|
||||
|
||||
if (map == null) {
|
||||
if (mapToRender == null) {
|
||||
source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.worldHelperHover(), " or ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, worldOrMap.get()));
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
map = null;
|
||||
mapToRender = null;
|
||||
}
|
||||
} else {
|
||||
world = source.getWorld().orElse(null);
|
||||
map = null;
|
||||
worldToRender = source.getWorld().orElse(null);
|
||||
mapToRender = null;
|
||||
|
||||
if (world == null) {
|
||||
if (worldToRender == null) {
|
||||
source.sendMessage(Text.of(TextColor.RED, "Can't detect a world from this command-source, you'll have to define a world or a map to render!").setHoverText(Text.of(TextColor.GRAY, "/bluemap render <world|map>")));
|
||||
return 0;
|
||||
}
|
||||
@ -580,13 +609,45 @@ public int renderCommand(CommandContext<S> context) {
|
||||
// execute render
|
||||
new Thread(() -> {
|
||||
try {
|
||||
if (world != null) {
|
||||
plugin.getServerInterface().persistWorldChanges(world.getUUID());
|
||||
//TODO: helper.createWorldRenderTask(source, world, center, radius);
|
||||
List<BmMap> maps = new ArrayList<>();
|
||||
World world = worldToRender;
|
||||
if (worldToRender != null) {
|
||||
plugin.getServerInterface().persistWorldChanges(worldToRender.getUUID());
|
||||
for (BmMap map : plugin.getMapTypes()) {
|
||||
if (map.getWorld().equals(worldToRender)) maps.add(map);
|
||||
}
|
||||
} else {
|
||||
plugin.getServerInterface().persistWorldChanges(map.getWorld().getUUID());
|
||||
//TODO: helper.createMapRenderTask(source, map, center, radius);
|
||||
plugin.getServerInterface().persistWorldChanges(mapToRender.getWorld().getUUID());
|
||||
maps.add(mapToRender);
|
||||
world = mapToRender.getWorld();
|
||||
}
|
||||
|
||||
String taskType = "Update";
|
||||
List<Vector2i> regions = helper.getRegions(world, center, radius);
|
||||
|
||||
if (force) {
|
||||
taskType = "Render";
|
||||
for (BmMap map : maps) {
|
||||
MapRenderState state = map.getRenderState();
|
||||
regions.forEach(region -> state.setRenderTime(region, -1));
|
||||
}
|
||||
}
|
||||
|
||||
if (center != null) {
|
||||
taskType = "Radius-" + taskType;
|
||||
}
|
||||
|
||||
for (BmMap map : maps) {
|
||||
List<WorldRegionRenderTask> tasks = new ArrayList<>(regions.size());
|
||||
regions.forEach(region -> tasks.add(new WorldRegionRenderTask(map, region)));
|
||||
tasks.sort(WorldRegionRenderTask::compare);
|
||||
plugin.getRenderManager().scheduleRenderTask(new CombinedRenderTask<>(
|
||||
taskType + " map '" + map.getId() + "'",
|
||||
tasks
|
||||
));
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Created new " + taskType + "-Task for map '" + map.getId() + "' ", TextColor.GRAY, "(" + regions.size() + " regions, ~" + regions.size() * 1024L + " chunks)"));
|
||||
}
|
||||
source.sendMessage(Text.of(TextColor.GREEN, "Use ", TextColor.GRAY, "/bluemap", TextColor.GREEN, " to see the progress."));
|
||||
} catch (IOException ex) {
|
||||
source.sendMessage(Text.of(TextColor.RED, "There was an unexpected exception trying to save the world. Please check the console for more details..."));
|
||||
Logger.global.logError("Unexpected exception trying to save the world!", ex);
|
||||
|
@ -24,18 +24,20 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common.rendermanager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class CombinedRenderTask<T extends RenderTask> implements RenderTask {
|
||||
|
||||
private final String description;
|
||||
private final List<T> tasks;
|
||||
private final Set<T> taskSet;
|
||||
private int currentTaskIndex;
|
||||
|
||||
public CombinedRenderTask(Collection<T> tasks) {
|
||||
this.tasks = new ArrayList<>();
|
||||
this.tasks.addAll(tasks);
|
||||
public CombinedRenderTask(String description, Collection<T> tasks) {
|
||||
this.description = description;
|
||||
this.tasks = Collections.unmodifiableList(new ArrayList<>(tasks));
|
||||
this.taskSet = Collections.unmodifiableSet(new HashSet<>(tasks));
|
||||
|
||||
this.currentTaskIndex = 0;
|
||||
}
|
||||
|
||||
@ -43,7 +45,7 @@ public CombinedRenderTask(Collection<T> tasks) {
|
||||
public void doWork() throws Exception {
|
||||
T task;
|
||||
|
||||
synchronized (this.tasks) {
|
||||
synchronized (this) {
|
||||
if (!hasMoreWork()) return;
|
||||
task = this.tasks.get(this.currentTaskIndex);
|
||||
|
||||
@ -57,20 +59,18 @@ public void doWork() throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMoreWork() {
|
||||
public synchronized boolean hasMoreWork() {
|
||||
return this.currentTaskIndex < this.tasks.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double estimateProgress() {
|
||||
synchronized (this.tasks) {
|
||||
if (!hasMoreWork()) return 1;
|
||||
public synchronized double estimateProgress() {
|
||||
if (!hasMoreWork()) return 1;
|
||||
|
||||
double total = currentTaskIndex;
|
||||
total += this.tasks.get(this.currentTaskIndex).estimateProgress();
|
||||
double total = currentTaskIndex;
|
||||
total += this.tasks.get(this.currentTaskIndex).estimateProgress();
|
||||
|
||||
return total / tasks.size();
|
||||
}
|
||||
return total / tasks.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -78,4 +78,22 @@ public void cancel() {
|
||||
for (T task : tasks) task.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(RenderTask task) {
|
||||
if (this.equals(task)) return true;
|
||||
if (taskSet.contains(task)) return true;
|
||||
|
||||
for (RenderTask subTask : this.tasks) {
|
||||
if (subTask.contains(task)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
//return description + " (" + (this.currentTaskIndex + 1) + "/" + tasks.size() + ")";
|
||||
return description;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,6 +36,9 @@ public class RenderManager {
|
||||
private final int id;
|
||||
private volatile boolean running;
|
||||
|
||||
private volatile long currentTaskStartTime;
|
||||
private volatile double currentTaskStartProgress;
|
||||
|
||||
private final AtomicInteger nextWorkerThreadIndex;
|
||||
private final Collection<WorkerThread> workerThreads;
|
||||
private final AtomicInteger busyCount;
|
||||
@ -53,6 +56,9 @@ public RenderManager() {
|
||||
|
||||
this.renderTasks = new LinkedList<>();
|
||||
this.renderTaskSet = new HashSet<>();
|
||||
|
||||
this.currentTaskStartTime = System.currentTimeMillis();
|
||||
this.currentTaskStartProgress = -1;
|
||||
}
|
||||
|
||||
public void start(int threadCount) throws IllegalStateException {
|
||||
@ -65,6 +71,9 @@ public void start(int threadCount) throws IllegalStateException {
|
||||
|
||||
this.running = true;
|
||||
|
||||
this.currentTaskStartTime = System.currentTimeMillis();
|
||||
this.currentTaskStartProgress = -1;
|
||||
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
WorkerThread worker = new WorkerThread();
|
||||
this.workerThreads.add(worker);
|
||||
@ -106,27 +115,38 @@ public void awaitShutdown() throws InterruptedException {
|
||||
|
||||
public boolean scheduleRenderTask(RenderTask task) {
|
||||
synchronized (this.renderTasks) {
|
||||
if (renderTaskSet.add(task)) {
|
||||
renderTasks.addLast(task);
|
||||
renderTasks.notifyAll();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
if (containsRenderTask(task)) return false;
|
||||
|
||||
renderTaskSet.add(task);
|
||||
renderTasks.addLast(task);
|
||||
renderTasks.notifyAll();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int scheduleRenderTasks(RenderTask... tasks) {
|
||||
return scheduleRenderTasks(Arrays.asList(tasks));
|
||||
}
|
||||
|
||||
public int scheduleRenderTasks(Collection<RenderTask> tasks) {
|
||||
synchronized (this.renderTasks) {
|
||||
int count = 0;
|
||||
for (RenderTask task : tasks) {
|
||||
if (scheduleRenderTask(task)) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean scheduleRenderTaskNext(RenderTask task) {
|
||||
synchronized (this.renderTasks) {
|
||||
if (renderTasks.size() <= 1) return scheduleRenderTask(task);
|
||||
if (containsRenderTask(task)) return false;
|
||||
|
||||
if (renderTaskSet.add(task)) {
|
||||
renderTasks.add(1, task);
|
||||
renderTasks.notifyAll();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
renderTaskSet.add(task);
|
||||
renderTasks.add(1, task);
|
||||
renderTasks.notifyAll();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +160,7 @@ public void reorderRenderTasks(Comparator<RenderTask> taskComparator) {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeTask(RenderTask task) {
|
||||
public boolean removeRenderTask(RenderTask task) {
|
||||
synchronized (this.renderTasks) {
|
||||
if (this.renderTasks.isEmpty()) return false;
|
||||
|
||||
@ -156,7 +176,7 @@ public boolean removeTask(RenderTask task) {
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllTasks() {
|
||||
public void removeAllRenderTasks() {
|
||||
synchronized (this.renderTasks) {
|
||||
if (this.renderTasks.isEmpty()) return;
|
||||
|
||||
@ -167,8 +187,51 @@ public void removeAllTasks() {
|
||||
}
|
||||
}
|
||||
|
||||
public long estimateCurrentRenderTaskTimeRemaining() {
|
||||
synchronized (this.renderTasks) {
|
||||
long now = System.currentTimeMillis();
|
||||
double progress = getCurrentRenderTask().estimateProgress();
|
||||
|
||||
long deltaTime = now - currentTaskStartTime;
|
||||
double deltaProgress = progress - currentTaskStartProgress;
|
||||
|
||||
double estimatedTotalDuration = deltaTime / deltaProgress;
|
||||
double estimatedRemainingDuration = (1 - progress) * estimatedTotalDuration;
|
||||
|
||||
return (long) estimatedRemainingDuration;
|
||||
}
|
||||
}
|
||||
|
||||
public RenderTask getCurrentRenderTask() {
|
||||
synchronized (this.renderTasks) {
|
||||
return this.renderTasks.getFirst();
|
||||
}
|
||||
}
|
||||
|
||||
public List<RenderTask> getScheduledRenderTasks() {
|
||||
return Collections.unmodifiableList(renderTasks);
|
||||
synchronized (this.renderTasks) {
|
||||
return new ArrayList<>(this.renderTasks);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean containsRenderTask(RenderTask task) {
|
||||
synchronized (this.renderTasks) {
|
||||
// checking all scheduled renderTasks except the first one, since that is already being processed
|
||||
|
||||
// quick check
|
||||
if (renderTaskSet.contains(task) && !getCurrentRenderTask().equals(task)) return true;
|
||||
|
||||
// iterate over all (skipping the first) using the "contains" method
|
||||
Iterator<RenderTask> iterator = renderTasks.iterator();
|
||||
if (!iterator.hasNext()) return false;
|
||||
iterator.next(); // skip first
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
if (iterator.next().contains(task)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getWorkerThreadCount() {
|
||||
@ -184,12 +247,21 @@ private void doWork() throws Exception {
|
||||
|
||||
task = this.renderTasks.getFirst();
|
||||
|
||||
if (this.currentTaskStartProgress < 0) {
|
||||
this.currentTaskStartTime = System.currentTimeMillis();
|
||||
this.currentTaskStartProgress = task.estimateProgress();
|
||||
}
|
||||
|
||||
// the following is making sure every render-thread is done working on this task (no thread is "busy")
|
||||
// before continuing working on the next RenderTask
|
||||
if (!task.hasMoreWork()) {
|
||||
if (busyCount.get() <= 0) {
|
||||
this.renderTaskSet.remove(this.renderTasks.removeFirst());
|
||||
this.renderTasks.notifyAll();
|
||||
|
||||
this.currentTaskStartTime = System.currentTimeMillis();
|
||||
this.currentTaskStartProgress = -1;
|
||||
|
||||
busyCount.set(0);
|
||||
} else {
|
||||
this.renderTasks.wait(10000);
|
||||
|
@ -46,4 +46,13 @@ default double estimateProgress() {
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/**
|
||||
* Checks if the given task is somehow included with this task
|
||||
*/
|
||||
default boolean contains(RenderTask task) {
|
||||
return equals(task);
|
||||
}
|
||||
|
||||
String getDescription();
|
||||
|
||||
}
|
||||
|
@ -25,9 +25,11 @@
|
||||
package de.bluecolored.bluemap.common.rendermanager;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
import de.bluecolored.bluemap.core.world.Grid;
|
||||
import de.bluecolored.bluemap.core.world.Region;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.TreeSet;
|
||||
@ -66,6 +68,8 @@ private synchronized void init() {
|
||||
tiles = new TreeSet<>(WorldRegionRenderTask::tileComparator);
|
||||
startTime = System.currentTimeMillis();
|
||||
|
||||
//Logger.global.logInfo("Starting: " + worldRegion);
|
||||
|
||||
long changesSince = 0;
|
||||
if (!force) changesSince = map.getRenderState().getRenderTime(worldRegion);
|
||||
|
||||
@ -106,6 +110,7 @@ public void doWork() {
|
||||
this.atWork++;
|
||||
}
|
||||
|
||||
//Logger.global.logInfo("Working on " + worldRegion + " - Tile " + tile);
|
||||
map.renderTile(tile); // <- actual work
|
||||
|
||||
synchronized (this) {
|
||||
@ -119,10 +124,12 @@ public void doWork() {
|
||||
|
||||
private void complete() {
|
||||
map.getRenderState().setRenderTime(worldRegion, startTime);
|
||||
|
||||
//Logger.global.logInfo("Done with: " + worldRegion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMoreWork() {
|
||||
public synchronized boolean hasMoreWork() {
|
||||
return !cancelled && (tiles == null || !tiles.isEmpty());
|
||||
}
|
||||
|
||||
@ -144,6 +151,27 @@ public void cancel() {
|
||||
}
|
||||
}
|
||||
|
||||
public BmMap getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public Vector2i getWorldRegion() {
|
||||
return worldRegion;
|
||||
}
|
||||
|
||||
public boolean isForce() {
|
||||
return force;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
if (force) {
|
||||
return "Render region " + getWorldRegion() + " for map '" + map.getId() + "'";
|
||||
} else {
|
||||
return "Update region " + getWorldRegion() + " for map '" + map.getId() + "'";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@ -164,4 +192,27 @@ private static int tileComparator(Vector2i v1, Vector2i v2) {
|
||||
return v2.getY() - v1.getY();
|
||||
}
|
||||
|
||||
public static int compare(WorldRegionRenderTask task1, WorldRegionRenderTask task2) {
|
||||
if (task1.equals(task2)) return 0;
|
||||
|
||||
int comp = task1.getMap().getId().compareTo(task2.getMap().getId());
|
||||
if (comp != 0) return comp;
|
||||
|
||||
//sort based on the worlds spawn-point
|
||||
World world = task1.getMap().getWorld();
|
||||
Vector2i spawnPoint = world.getSpawnPoint().toVector2(true);
|
||||
Grid regionGrid = world.getRegionGrid();
|
||||
Vector2i spawnRegion = regionGrid.getCell(spawnPoint);
|
||||
|
||||
Vector2i task1Rel = task1.getWorldRegion().sub(spawnRegion);
|
||||
Vector2i task2Rel = task2.getWorldRegion().sub(spawnRegion);
|
||||
|
||||
comp = tileComparator(task1Rel, task2Rel);
|
||||
if (comp != 0) return comp;
|
||||
|
||||
if (task1.isForce() == task2.isForce()) return 0;
|
||||
if (task1.isForce()) return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -136,15 +136,7 @@ public boolean isClosed(){
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
in.close();
|
||||
} finally {
|
||||
try {
|
||||
out.close();
|
||||
} finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
connection.close();
|
||||
}
|
||||
|
||||
public static class ConnectionClosedException extends IOException {
|
||||
|
@ -86,11 +86,13 @@ public void renderMaps(BlueMapService blueMap, boolean watch, boolean forceRende
|
||||
}
|
||||
|
||||
//update all maps
|
||||
for (BmMap map : maps.values()) {
|
||||
for (Vector2i region : map.getWorld().listRegions()){
|
||||
renderManager.scheduleRenderTask(new WorldRegionRenderTask(map, region, forceRender));
|
||||
}
|
||||
}
|
||||
List<WorldRegionRenderTask> tasks = new ArrayList<>();
|
||||
for (BmMap map : maps.values())
|
||||
for (Vector2i region : map.getWorld().listRegions())
|
||||
tasks.add(new WorldRegionRenderTask(map, region, forceRender));
|
||||
tasks.sort(WorldRegionRenderTask::compare);
|
||||
tasks.forEach(renderManager::scheduleRenderTask);
|
||||
|
||||
int totalRegions = renderManager.getScheduledRenderTasks().size();
|
||||
|
||||
Logger.global.logInfo("Start " + (forceRender ? "rendering " : "updating ") + maps.size() + " maps (" + totalRegions + " regions, ~" + totalRegions * 1024L + " chunks)...");
|
||||
@ -115,7 +117,7 @@ public void run() {
|
||||
long etr = (long) ((elapsedTime / progress) * (1 - progress));
|
||||
String etrDurationString = DurationFormatUtils.formatDuration(etr, "HH:mm:ss");
|
||||
|
||||
Logger.global.logInfo("Rendering: " + (Math.round(progress * 100000) / 1000.0) + "% (ETR: " + etrDurationString + ")");
|
||||
Logger.global.logInfo("Rendering: " + (Math.round(progress * 100000) / 1000.0) + "% (ETA: " + etrDurationString + ")");
|
||||
}
|
||||
};
|
||||
timer.scheduleAtFixedRate(updateInfoTask, TimeUnit.SECONDS.toMillis(10), TimeUnit.SECONDS.toMillis(10));
|
||||
|
Loading…
Reference in New Issue
Block a user