317 lines
12 KiB
Java
317 lines
12 KiB
Java
package de.bluecolored.bluemap.sponge;
|
|
|
|
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;
|
|
import org.spongepowered.api.Sponge;
|
|
import org.spongepowered.api.command.CommandResult;
|
|
import org.spongepowered.api.command.CommandSource;
|
|
import org.spongepowered.api.command.args.GenericArguments;
|
|
import org.spongepowered.api.command.spec.CommandSpec;
|
|
import org.spongepowered.api.text.Text;
|
|
import org.spongepowered.api.text.action.TextActions;
|
|
import org.spongepowered.api.text.format.TextColors;
|
|
import org.spongepowered.api.world.Locatable;
|
|
import org.spongepowered.api.world.Location;
|
|
import org.spongepowered.api.world.storage.WorldProperties;
|
|
|
|
import com.flowpowered.math.vector.Vector2i;
|
|
import com.flowpowered.math.vector.Vector3i;
|
|
import com.google.common.collect.Lists;
|
|
|
|
import de.bluecolored.bluemap.core.logger.Logger;
|
|
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
|
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
|
import de.bluecolored.bluemap.core.world.Block;
|
|
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
|
import de.bluecolored.bluemap.core.world.World;
|
|
|
|
public class Commands {
|
|
|
|
private SpongePlugin plugin;
|
|
|
|
public Commands(SpongePlugin plugin) {
|
|
this.plugin = plugin;
|
|
}
|
|
|
|
public CommandSpec createRootCommand() {
|
|
|
|
@SuppressWarnings("unused")
|
|
CommandSpec debugCommand = CommandSpec.builder()
|
|
.executor((source, args) -> {
|
|
if (source instanceof Locatable) {
|
|
try {
|
|
Location<org.spongepowered.api.world.World> loc = ((Locatable) source).getLocation();
|
|
UUID worldUuid = loc.getExtent().getUniqueId();
|
|
World world = plugin.getWorld(worldUuid);
|
|
Block block = world.getBlock(loc.getBlockPosition());
|
|
Block blockBelow = world.getBlock(loc.getBlockPosition().add(0, -1, 0));
|
|
|
|
source.sendMessages(Lists.newArrayList(
|
|
Text.of("Block: " + block),
|
|
Text.of("Block below: " + blockBelow)
|
|
));
|
|
} catch (ChunkNotGeneratedException e) {
|
|
Logger.global.logError("Failed to debug!", e);
|
|
}
|
|
}
|
|
|
|
return CommandResult.success();
|
|
})
|
|
.build();
|
|
|
|
return CommandSpec.builder()
|
|
.description(Text.of("Displays BlueMaps render status"))
|
|
.permission("bluemap.status")
|
|
.childArgumentParseExceptionFallback(false)
|
|
.child(createReloadCommand(), "reload")
|
|
.child(createPauseRenderCommand(), "pause")
|
|
.child(createResumeRenderCommand(), "resume")
|
|
.child(createRenderCommand(), "render")
|
|
//.child(debugCommand, "debug")
|
|
.executor((source, args) -> {
|
|
source.sendMessages(createStatusMessage());
|
|
return CommandResult.success();
|
|
})
|
|
.build();
|
|
}
|
|
|
|
public CommandSpec createReloadCommand() {
|
|
return CommandSpec.builder()
|
|
.description(Text.of("Reloads all resources and configuration-files"))
|
|
.permission("bluemap.reload")
|
|
.executor((source, args) -> {
|
|
try {
|
|
plugin.reload();
|
|
|
|
if (plugin.isLoaded()) {
|
|
source.sendMessage(Text.of(TextColors.GREEN, "BlueMap reloaded!"));
|
|
return CommandResult.success();
|
|
} else {
|
|
source.sendMessage(Text.of(TextColors.RED, "Could not load BlueMap! See the console for details!"));
|
|
return CommandResult.empty();
|
|
}
|
|
} catch (IOException | NoSuchResourceException ex) {
|
|
Logger.global.logError("Failed to reload BlueMap!", ex);
|
|
|
|
source.sendMessage(Text.of(TextColors.RED, "There was an error reloading BlueMap! See the console for details!"));
|
|
return CommandResult.empty();
|
|
}
|
|
})
|
|
.build();
|
|
}
|
|
|
|
public CommandSpec createPauseRenderCommand() {
|
|
return CommandSpec.builder()
|
|
.description(Text.of("Pauses all rendering"))
|
|
.permission("bluemap.pause")
|
|
.executor((source, args) -> {
|
|
if (plugin.getRenderManager().isRunning()) {
|
|
plugin.getRenderManager().stop();
|
|
source.sendMessage(Text.of(TextColors.GREEN, "BlueMap rendering paused!"));
|
|
return CommandResult.success();
|
|
} else {
|
|
source.sendMessage(Text.of(TextColors.RED, "BlueMap rendering are already paused!"));
|
|
return CommandResult.empty();
|
|
}
|
|
})
|
|
.build();
|
|
}
|
|
|
|
public CommandSpec createResumeRenderCommand() {
|
|
return CommandSpec.builder()
|
|
.description(Text.of("Resumes all paused rendering"))
|
|
.permission("bluemap.resume")
|
|
.executor((source, args) -> {
|
|
if (!plugin.getRenderManager().isRunning()) {
|
|
plugin.getRenderManager().start();
|
|
source.sendMessage(Text.of(TextColors.GREEN, "BlueMap renders resumed!"));
|
|
return CommandResult.success();
|
|
} else {
|
|
source.sendMessage(Text.of(TextColors.RED, "BlueMap renders are already running!"));
|
|
return CommandResult.empty();
|
|
}
|
|
})
|
|
.build();
|
|
}
|
|
|
|
public CommandSpec createRenderCommand() {
|
|
return CommandSpec.builder()
|
|
.description(Text.of("Renders the whole world"))
|
|
.permission("bluemap.rendertask.create.world")
|
|
.childArgumentParseExceptionFallback(false)
|
|
.child(createPrioritizeTaskCommand(), "prioritize")
|
|
.child(createRemoveTaskCommand(), "remove")
|
|
.arguments(GenericArguments.optional(GenericArguments.world(Text.of("world"))))
|
|
.executor((source, args) -> {
|
|
WorldProperties spongeWorld = args.<WorldProperties>getOne("world").orElse(null);
|
|
|
|
if (spongeWorld == null && source instanceof Locatable) {
|
|
Location<org.spongepowered.api.world.World> loc = ((Locatable) source).getLocation();
|
|
spongeWorld = loc.getExtent().getProperties();
|
|
}
|
|
|
|
if (spongeWorld == null){
|
|
source.sendMessage(Text.of(TextColors.RED, "You have to define a world to render!"));
|
|
return CommandResult.empty();
|
|
}
|
|
|
|
World world = plugin.getWorld(spongeWorld.getUniqueId());
|
|
if (world == null) {
|
|
source.sendMessage(Text.of(TextColors.RED, "This world is not loaded with BlueMap! Maybe it is not configured?"));
|
|
}
|
|
|
|
world.invalidateChunkCache();
|
|
|
|
Sponge.getScheduler().createTaskBuilder()
|
|
.async()
|
|
.execute(() -> createWorldRenderTask(source, world))
|
|
.submit(plugin);
|
|
|
|
return CommandResult.success();
|
|
})
|
|
.build();
|
|
}
|
|
|
|
public CommandSpec createPrioritizeTaskCommand() {
|
|
return CommandSpec.builder()
|
|
.description(Text.of("Prioritizes the render-task with the given uuid"))
|
|
.permission("bluemap.rendertask.prioritize")
|
|
.arguments(GenericArguments.uuid(Text.of("task-uuid")))
|
|
.executor((source, args) -> {
|
|
Optional<UUID> uuid = args.<UUID>getOne("task-uuid");
|
|
if (!uuid.isPresent()) {
|
|
source.sendMessage(Text.of("You need to specify a task-uuid"));
|
|
return CommandResult.empty();
|
|
}
|
|
|
|
for (RenderTask task : plugin.getRenderManager().getRenderTasks()) {
|
|
if (task.getUuid().equals(uuid.get())) {
|
|
plugin.getRenderManager().prioritizeRenderTask(task);
|
|
break;
|
|
}
|
|
}
|
|
|
|
source.sendMessages(createStatusMessage());
|
|
return CommandResult.success();
|
|
})
|
|
.build();
|
|
}
|
|
|
|
public CommandSpec createRemoveTaskCommand() {
|
|
return CommandSpec.builder()
|
|
.description(Text.of("Removes the render-task with the given uuid"))
|
|
.permission("bluemap.rendertask.remove")
|
|
.arguments(GenericArguments.uuid(Text.of("task-uuid")))
|
|
.executor((source, args) -> {
|
|
Optional<UUID> uuid = args.<UUID>getOne("task-uuid");
|
|
if (!uuid.isPresent()) {
|
|
source.sendMessage(Text.of("You need to specify a task-uuid"));
|
|
return CommandResult.empty();
|
|
}
|
|
|
|
for (RenderTask task : plugin.getRenderManager().getRenderTasks()) {
|
|
if (task.getUuid().equals(uuid.get())) {
|
|
plugin.getRenderManager().removeRenderTask(task);
|
|
break;
|
|
}
|
|
}
|
|
|
|
source.sendMessages(createStatusMessage());
|
|
return CommandResult.success();
|
|
})
|
|
.build();
|
|
}
|
|
|
|
private List<Text> createStatusMessage(){
|
|
List<Text> lines = new ArrayList<>();
|
|
|
|
RenderManager renderer = plugin.getRenderManager();
|
|
|
|
lines.add(Text.EMPTY);
|
|
lines.add(Text.of(TextColors.BLUE, "Tile-Updates:"));
|
|
|
|
if (renderer.isRunning()) {
|
|
lines.add(Text.of(TextColors.WHITE, " Render-Threads are ", Text.of(TextActions.runCommand("/bluemap pause"), TextActions.showText(Text.of("click to pause rendering")), TextColors.GREEN, "running"), TextColors.GRAY, "!"));
|
|
} else {
|
|
lines.add(Text.of(TextColors.WHITE, " Render-Threads are ", Text.of(TextActions.runCommand("/bluemap resume"), TextActions.showText(Text.of("click to resume rendering")), TextColors.RED, "paused"), TextColors.GRAY, "!"));
|
|
}
|
|
|
|
lines.add(Text.of(TextColors.WHITE, " Scheduled tile-updates: ", Text.of(TextActions.showText(Text.of("tiles waiting for a free render-thread")), TextColors.GOLD, renderer.getQueueSize()), Text.of(TextActions.showText(Text.of("tiles waiting for world-save")), TextColors.GRAY, " + " + plugin.getUpdateHandler().getUpdateBufferCount())));
|
|
|
|
RenderTask[] tasks = renderer.getRenderTasks();
|
|
if (tasks.length > 0) {
|
|
RenderTask task = tasks[0];
|
|
|
|
long time = task.getActiveTime();
|
|
String durationString = DurationFormatUtils.formatDurationWords(time, true, true);
|
|
double pct = (double)task.getRenderedTileCount() / (double)(task.getRenderedTileCount() + task.getRemainingTileCount());
|
|
|
|
long ert = (long)((time / pct) * (1d - pct));
|
|
String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true);
|
|
|
|
lines.add(Text.of(TextColors.BLUE, "Current task:"));
|
|
lines.add(Text.of(" ", createCancelTaskText(task), TextColors.WHITE, " Task ", TextColors.GOLD, task.getName(), TextColors.WHITE, " for map ", TextActions.showText(Text.of(TextColors.WHITE, "World: ", TextColors.GOLD, task.getMapType().getWorld().getName())), TextColors.GOLD, task.getMapType().getName()));
|
|
lines.add(Text.of(TextColors.WHITE, " rendered ", TextColors.GOLD, task.getRenderedTileCount(), TextColors.WHITE, " tiles ", TextColors.GRAY, "(" + (Math.round(pct * 1000)/10.0) + "%)", TextColors.WHITE, " in ", TextColors.GOLD, durationString));
|
|
lines.add(Text.of(TextColors.WHITE, " with ", TextColors.GOLD, task.getRemainingTileCount(), TextColors.WHITE, " tiles to go. ETA: ", TextColors.GOLD, ertDurationString));
|
|
}
|
|
|
|
if (tasks.length > 1) {
|
|
lines.add(Text.of(TextColors.BLUE, "Waiting tasks:"));
|
|
for (int i = 1; i < tasks.length; i++) {
|
|
RenderTask task = tasks[i];
|
|
lines.add(Text.of(" ", createCancelTaskText(task), createPrioritizeTaskText(task), TextColors.WHITE, " Task ", TextColors.GOLD, task.getName(), TextColors.WHITE, " for map ", Text.of(TextActions.showText(Text.of(TextColors.WHITE, "World: ", TextColors.GOLD, task.getMapType().getWorld().getName())), TextColors.GOLD, task.getMapType().getName()), TextColors.GRAY, " (" + task.getRemainingTileCount() + " tiles)"));
|
|
}
|
|
}
|
|
|
|
return lines;
|
|
}
|
|
|
|
private Text createCancelTaskText(RenderTask task) {
|
|
return Text.of(TextActions.runCommand("/bluemap render remove " + task.getUuid()), TextActions.showText(Text.of("click to remove this render-task")), TextColors.RED, "[X]");
|
|
}
|
|
|
|
private Text createPrioritizeTaskText(RenderTask task) {
|
|
return Text.of(TextActions.runCommand("/bluemap render prioritize " + task.getUuid()), TextActions.showText(Text.of("click to prioritize this render-task")), TextColors.GREEN, "[^]");
|
|
}
|
|
|
|
private void createWorldRenderTask(CommandSource source, World world) {
|
|
source.sendMessage(Text.of(TextColors.GOLD, "Collecting chunks to render..."));
|
|
Collection<Vector2i> chunks = world.getChunkList();
|
|
source.sendMessage(Text.of(TextColors.GREEN, chunks.size() + " chunks found!"));
|
|
|
|
for (MapType map : SpongePlugin.getInstance().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)));
|
|
}
|
|
|
|
RenderTask task = new RenderTask("world-render", map);
|
|
task.addTiles(tiles);
|
|
task.optimizeQueue();
|
|
plugin.getRenderManager().addRenderTask(task);
|
|
|
|
source.sendMessage(Text.of(TextColors.GREEN, tiles.size() + " tiles found! Task created."));
|
|
}
|
|
|
|
source.sendMessage(Text.of(TextColors.GREEN, "All render tasks created! Use /bluemap to view the progress!"));
|
|
}
|
|
|
|
}
|