mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-26 20:45:16 +01:00
Merge branch 'feature/plugins'
This commit is contained in:
commit
6b8dadd1dc
15
BlueMapBukkit/build.gradle
Normal file
15
BlueMapBukkit/build.gradle
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/'
|
||||||
|
|
||||||
|
content {
|
||||||
|
includeGroup 'org.bukkit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
shadow "org.bukkit:bukkit:1.14.4-R0.1-SNAPSHOT"
|
||||||
|
compile group: 'org.bstats', name: 'bstats-bukkit-lite', version: '1.5'
|
||||||
|
compile project(':BlueMapCommon')
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package de.bluecolored.bluemap.bukkit;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource;
|
||||||
|
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||||
|
|
||||||
|
public class BukkitCommandSource implements CommandSource {
|
||||||
|
|
||||||
|
private CommandSender delegate;
|
||||||
|
|
||||||
|
public BukkitCommandSource(CommandSender delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(Text text) {
|
||||||
|
Bukkit.getScheduler().runTask(BukkitPlugin.getInstance(), () -> {
|
||||||
|
if (delegate instanceof Player) {
|
||||||
|
Player player = (Player) delegate;
|
||||||
|
|
||||||
|
//kinda hacky but works
|
||||||
|
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + player.getName() + " " + text.toJSONString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate.sendMessage(text.toFormattingCodedString('§'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,209 @@
|
|||||||
|
package de.bluecolored.bluemap.bukkit;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.common.plugin.Commands;
|
||||||
|
import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource;
|
||||||
|
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||||
|
import de.bluecolored.bluemap.common.plugin.text.TextColor;
|
||||||
|
|
||||||
|
public class BukkitCommands implements CommandExecutor {
|
||||||
|
|
||||||
|
private Commands bluemapCommands;
|
||||||
|
|
||||||
|
private Collection<Command> commands;
|
||||||
|
|
||||||
|
public BukkitCommands(Commands commands) {
|
||||||
|
this.bluemapCommands = commands;
|
||||||
|
this.commands = new ArrayList<>();
|
||||||
|
initCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initCommands() {
|
||||||
|
|
||||||
|
commands.add(new Command("bluemap.status") {
|
||||||
|
@Override
|
||||||
|
public boolean execute(CommandSender sender, CommandSource source, String[] args) {
|
||||||
|
if (args.length != 0) return false;
|
||||||
|
|
||||||
|
bluemapCommands.executeRootCommand(source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(new Command("bluemap.reload", "reload") {
|
||||||
|
@Override
|
||||||
|
public boolean execute(CommandSender sender, CommandSource source, String[] args) {
|
||||||
|
if (args.length != 0) return false;
|
||||||
|
|
||||||
|
bluemapCommands.executeReloadCommand(source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(new Command("bluemap.pause", "pause") {
|
||||||
|
@Override
|
||||||
|
public boolean execute(CommandSender sender, CommandSource source, String[] args) {
|
||||||
|
if (args.length != 0) return false;
|
||||||
|
|
||||||
|
bluemapCommands.executePauseCommand(source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(new Command("bluemap.resume", "resume") {
|
||||||
|
@Override
|
||||||
|
public boolean execute(CommandSender sender, CommandSource source, String[] args) {
|
||||||
|
if (args.length != 0) return false;
|
||||||
|
|
||||||
|
bluemapCommands.executeResumeCommand(source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(new Command("bluemap.rendertask.create.world", "render") {
|
||||||
|
@Override
|
||||||
|
public boolean execute(CommandSender sender, CommandSource source, String[] args) {
|
||||||
|
if (args.length > 1) return false;
|
||||||
|
|
||||||
|
World world;
|
||||||
|
if (args.length == 1) {
|
||||||
|
world = Bukkit.getWorld(args[0]);
|
||||||
|
if (world == null) {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "There is no world named '" + args[0] + "'!"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sender instanceof Player) {
|
||||||
|
Player player = (Player) sender;
|
||||||
|
world = player.getWorld();
|
||||||
|
} else {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "Since you are not a player, you have to specify a world!"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bluemapCommands.executeRenderWorldCommand(source, world.getUID());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(new Command("bluemap.rendertask.prioritize", "render", "prioritize") {
|
||||||
|
@Override
|
||||||
|
public boolean execute(CommandSender sender, CommandSource source, String[] args) {
|
||||||
|
if (args.length != 1) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
UUID uuid = UUID.fromString(args[0]);
|
||||||
|
bluemapCommands.executePrioritizeRenderTaskCommand(source, uuid);
|
||||||
|
return true;
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "'" + args[0] + "' is not a valid UUID!"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(new Command("bluemap.rendertask.remove", "render", "remove") {
|
||||||
|
@Override
|
||||||
|
public boolean execute(CommandSender sender, CommandSource source, String[] args) {
|
||||||
|
if (args.length != 1) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
UUID uuid = UUID.fromString(args[0]);
|
||||||
|
bluemapCommands.executeRemoveRenderTaskCommand(source, uuid);
|
||||||
|
return true;
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "'" + args[0] + "' is not a valid UUID!"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add(new Command("bluemap.debug", "debug") {
|
||||||
|
@Override
|
||||||
|
public boolean execute(CommandSender sender, CommandSource source, String[] args) {
|
||||||
|
if (!(sender instanceof Player)) {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "You have to be a player to use this command!"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player player = (Player) sender;
|
||||||
|
UUID world = player.getWorld().getUID();
|
||||||
|
Vector3i pos = new Vector3i(
|
||||||
|
player.getLocation().getBlockX(),
|
||||||
|
player.getLocation().getBlockY(),
|
||||||
|
player.getLocation().getBlockZ()
|
||||||
|
);
|
||||||
|
|
||||||
|
bluemapCommands.executeDebugCommand(source, world, pos);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, org.bukkit.command.Command bukkitCommand, String label, String[] args) {
|
||||||
|
int max = -1;
|
||||||
|
Command maxCommand = null;
|
||||||
|
for (Command command : commands) {
|
||||||
|
int matchSize = command.matches(args);
|
||||||
|
if (matchSize > max) {
|
||||||
|
maxCommand = command;
|
||||||
|
max = matchSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxCommand == null) return false;
|
||||||
|
|
||||||
|
BukkitCommandSource source = new BukkitCommandSource(sender);
|
||||||
|
|
||||||
|
if (!maxCommand.checkPermission(sender)) {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "You don't have permission to use this command!"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxCommand.execute(sender, source, Arrays.copyOfRange(args, max, args.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract class Command {
|
||||||
|
|
||||||
|
private String[] command;
|
||||||
|
private String permission;
|
||||||
|
|
||||||
|
public Command(String permission, String... command) {
|
||||||
|
this.command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean execute(CommandSender sender, CommandSource source, String[] args);
|
||||||
|
|
||||||
|
public int matches(String[] args) {
|
||||||
|
if (args.length < command.length) return -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < command.length; i++) {
|
||||||
|
if (!args[i].equalsIgnoreCase(command[i])) return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkPermission(CommandSender sender) {
|
||||||
|
if (sender.isOp()) return true;
|
||||||
|
return sender.hasPermission(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
package de.bluecolored.bluemap.bukkit;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import org.bstats.bukkit.MetricsLite;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.plugin.java.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;
|
||||||
|
|
||||||
|
public class BukkitPlugin extends JavaPlugin implements ServerInterface {
|
||||||
|
|
||||||
|
private static BukkitPlugin instance;
|
||||||
|
|
||||||
|
private Plugin bluemap;
|
||||||
|
private EventForwarder eventForwarder;
|
||||||
|
private BukkitCommands commands;
|
||||||
|
|
||||||
|
public BukkitPlugin() {
|
||||||
|
Logger.global = new JavaLogger(getLogger());
|
||||||
|
|
||||||
|
this.eventForwarder = new EventForwarder();
|
||||||
|
this.bluemap = new Plugin("bukkit", this);
|
||||||
|
this.commands = new BukkitCommands(bluemap.getCommands());
|
||||||
|
|
||||||
|
BukkitPlugin.instance = 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 {
|
||||||
|
final File normalizedWorldFolder = worldFolder.getCanonicalFile();
|
||||||
|
|
||||||
|
Future<UUID> futureUUID;
|
||||||
|
if (!Bukkit.isPrimaryThread()) {
|
||||||
|
futureUUID = Bukkit.getScheduler().callSyncMethod(BukkitPlugin.getInstance(), () -> getUUIDForWorldSync(normalizedWorldFolder));
|
||||||
|
} else {
|
||||||
|
futureUUID = CompletableFuture.completedFuture(getUUIDForWorldSync(normalizedWorldFolder));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return futureUUID.get();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
if (e.getCause() instanceof IOException) {
|
||||||
|
throw (IOException) e.getCause();
|
||||||
|
} else {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UUID getUUIDForWorldSync (File worldFolder) throws IOException {
|
||||||
|
for (World world : getServer().getWorlds()) {
|
||||||
|
if (worldFolder.equals(world.getWorldFolder().getCanonicalFile())) return world.getUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IOException("There is no world with this folder loaded: " + worldFolder.getCanonicalPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File getConfigFolder() {
|
||||||
|
return getDataFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Plugin getBlueMap() {
|
||||||
|
return bluemap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BukkitPlugin getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of BlueMapSponge, 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.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.core.logger.AbstractLogger;
|
||||||
|
|
||||||
|
public class JavaLogger extends AbstractLogger {
|
||||||
|
|
||||||
|
private Logger out;
|
||||||
|
|
||||||
|
public JavaLogger(Logger out) {
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logError(String message, Throwable throwable) {
|
||||||
|
out.log(Level.SEVERE, message, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logWarning(String message) {
|
||||||
|
out.log(Level.WARNING, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logInfo(String message) {
|
||||||
|
out.log(Level.INFO, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logDebug(String message) {
|
||||||
|
if (out.isLoggable(Level.FINE)) out.log(Level.FINE, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void noFloodDebug(String message) {
|
||||||
|
if (out.isLoggable(Level.FINE)) super.noFloodDebug(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void noFloodDebug(String key, String message) {
|
||||||
|
if (out.isLoggable(Level.FINE)) super.noFloodDebug(key, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
accept-download: false
|
||||||
|
metrics: true
|
||||||
|
renderThreadCount: -2
|
||||||
|
data: "bluemap"
|
||||||
|
webroot: "bluemap/web"
|
||||||
|
webserver {
|
||||||
|
enabled: true
|
||||||
|
port: 8100
|
||||||
|
maxConnectionCount: 100
|
||||||
|
}
|
171
BlueMapBukkit/src/main/resources/bluemap-bukkit.conf
Normal file
171
BlueMapBukkit/src/main/resources/bluemap-bukkit.conf
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
## ##
|
||||||
|
## BlueMap ##
|
||||||
|
## ##
|
||||||
|
## by Blue (Lukas Rieger) ##
|
||||||
|
## http://bluecolored.de/ ##
|
||||||
|
## ##
|
||||||
|
|
||||||
|
# By changing the setting (accept-download) below to TRUE you are indicating that you have accepted mojang's EULA (https://account.mojang.com/documents/minecraft_eula),
|
||||||
|
# you confirm that you own a license to Minecraft (Java Edition)
|
||||||
|
# and you agree that BlueMap will download and use this file for you: %minecraft-client-url%
|
||||||
|
# (Alternatively you can download the file yourself and store it here: <data>/minecraft-client-%minecraft-client-version%.jar)
|
||||||
|
# This file contains resources that belong to mojang and you must not redistribute it or do anything else that is not compliant with mojang's EULA.
|
||||||
|
# BlueMap uses resources in this file to generate the 3D-Models used for the map and texture them. (BlueMap will not work without those resources.)
|
||||||
|
# %datetime-iso%
|
||||||
|
accept-download: false
|
||||||
|
|
||||||
|
# This changes the amount of threads that BlueMap will use to render the maps.
|
||||||
|
# A higher value can improve render-speed but could impact performance on the host machine.
|
||||||
|
# This should be always below or equal to the number of available processor-cores.
|
||||||
|
# Zero or a negative value means the amount of of available processor-cores subtracted by the value.
|
||||||
|
# (So a value of -2 with 6 cores results in 4 render-processes)
|
||||||
|
# Default is -2
|
||||||
|
renderThreadCount: -2
|
||||||
|
|
||||||
|
# If this is true, BlueMap might send really basic metrics reports containg only the implementation-type and the version that is being used to https://metrics.bluecolored.de/bluemap/
|
||||||
|
# This allows me to track the basic usage of BlueMap and helps me stay motivated to further develop this tool! Please leave it on :)
|
||||||
|
# An example report looks like this: {"implementation":"CLI","version":"%version%"}
|
||||||
|
metrics: true
|
||||||
|
|
||||||
|
# The folder where bluemap saves data-files it needs during runtime or to save e.g. the render-progress to resume it later.
|
||||||
|
data: "bluemap"
|
||||||
|
|
||||||
|
# The webroot of the website that displays the map.
|
||||||
|
webroot: "bluemap/web"
|
||||||
|
|
||||||
|
# Unncomment this to override the path where bluemap stores the data-files.
|
||||||
|
# Default is "<webroot>/data"
|
||||||
|
#webdata: "path/to/data/folder"
|
||||||
|
|
||||||
|
webserver {
|
||||||
|
# With this setting you can disable the integrated web-server.
|
||||||
|
# This is usefull if you want to only render the map-data for later use, or if you setup your own webserver.
|
||||||
|
# Default is enabled
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# The IP-Adress that the webserver binds to.
|
||||||
|
# If this setting is commented out, bluemap tries to find the default ip-adress of your system.
|
||||||
|
# If you only want to access it locally use "localhost".
|
||||||
|
#ip: "localhost"
|
||||||
|
#ip: "127.0.0.1"
|
||||||
|
|
||||||
|
# The port that the webserver listenes to.
|
||||||
|
# Default is 8100
|
||||||
|
port: 8100
|
||||||
|
|
||||||
|
# Max number of simultaneous connections that the webserver allows
|
||||||
|
# Default is 100
|
||||||
|
maxConnectionCount: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
# This is an array with multiple configured maps.
|
||||||
|
# You can define multiple maps, for different worlds with different render-settings here
|
||||||
|
maps: [
|
||||||
|
|
||||||
|
{
|
||||||
|
# The id of this map
|
||||||
|
# Should only contain word-charactes: [a-zA-Z0-9_]
|
||||||
|
id: "world"
|
||||||
|
|
||||||
|
# The name of this map
|
||||||
|
# This defines the display name of this map, you can change this at any time
|
||||||
|
# Default is the id of this map
|
||||||
|
name: "World"
|
||||||
|
|
||||||
|
# The path to the save-folder of the world to render
|
||||||
|
world: "world"
|
||||||
|
|
||||||
|
# If this is false, BlueMap tries to omit all blocks that are not visible from above-ground.
|
||||||
|
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
|
||||||
|
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
|
||||||
|
# Default is false
|
||||||
|
renderCaves: false
|
||||||
|
|
||||||
|
# AmbientOcclusion adds soft shadows into corners, which gives the map a much better look.
|
||||||
|
# This has only a small impact on render-time and has no impact on the web-performance of the map.
|
||||||
|
# The value defines the strength of the shading, a value of 0 disables ambientOcclusion.
|
||||||
|
# Default is 0.25
|
||||||
|
ambientOcclusion: 0.25
|
||||||
|
|
||||||
|
# Lighting uses the light-data in minecraft to shade each block-face.
|
||||||
|
# If this is enabled, caves and inside buildings without torches will be darker.
|
||||||
|
# The value defines the strength of the shading and a value of 0 disables lighting (every block will be fully lit).
|
||||||
|
# Default is 0.8
|
||||||
|
lighting: 0.8
|
||||||
|
|
||||||
|
# With the below values you can limit the map-render.
|
||||||
|
# This can be used to ignore the nethers ceiling or render only a certain part of a world.
|
||||||
|
# Default is no min or max value (= infinite bounds)
|
||||||
|
#minX: -4000
|
||||||
|
#maxX: 4000
|
||||||
|
#minZ: -4000
|
||||||
|
#maxZ: 4000
|
||||||
|
#minY: 50
|
||||||
|
#maxY: 126
|
||||||
|
|
||||||
|
# Using this, BlueMap pretends that every Block out of the defined render-bounds is AIR,
|
||||||
|
# this means you can see the blocks where the world is cut (instead of having a see-through/xray view).
|
||||||
|
# This has only an effect if you set some render-bounds above.
|
||||||
|
# Default is enabled
|
||||||
|
renderEdges: true
|
||||||
|
|
||||||
|
# HIRES is the high-resolution render of the map. Where you see every block.
|
||||||
|
hires {
|
||||||
|
# Defines the size of one map-tile in blocks.
|
||||||
|
# If you change this value, the lowres values might need adjustment as well!
|
||||||
|
# Default is 32
|
||||||
|
tileSize: 32
|
||||||
|
|
||||||
|
# The View-Distance for hires tiles on the web-map (the value is the radius in tiles)
|
||||||
|
# Default is 4.5
|
||||||
|
viewDistance: 4.5
|
||||||
|
}
|
||||||
|
|
||||||
|
# LOWRES is the low-resolution render of the map. Thats the model that you see if you zoom far out to get an overview.
|
||||||
|
lowres {
|
||||||
|
# Defines resolution of the lowres model. E.g. If the hires.tileSize is 32, a value of 4 means that every 8*8 blocks will be summarized by one point on the lowres map.
|
||||||
|
# Calculation: 32 / 4 = 8
|
||||||
|
# You can only use values that result in an integer if you use the above calculation!
|
||||||
|
# Default is 4
|
||||||
|
pointsPerHiresTile: 4
|
||||||
|
|
||||||
|
# Defines the size of one lowres-map-tile in points.
|
||||||
|
# Default is 50
|
||||||
|
pointsPerLowresTile: 50
|
||||||
|
|
||||||
|
# The View-Distance for lowres tiles on the web-map (the value is the radius in tiles)
|
||||||
|
# Default is 7
|
||||||
|
viewDistance: 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Here another example for the End-Map
|
||||||
|
# Things we don't want to change from default we can just omit
|
||||||
|
{
|
||||||
|
id: "end"
|
||||||
|
name: "End"
|
||||||
|
world: "world/DIM1"
|
||||||
|
|
||||||
|
# In the end is no sky-light, so we need to enable this or we won't see anything.
|
||||||
|
renderCaves: true
|
||||||
|
|
||||||
|
# Same here, we don't want a dark map. But not completely disabled, so we see the effect of e.g torches.
|
||||||
|
lighting: 0.4
|
||||||
|
}
|
||||||
|
|
||||||
|
# Here another example for the Nether-Map
|
||||||
|
{
|
||||||
|
id: "nether"
|
||||||
|
name: "Nether"
|
||||||
|
world: "world/DIM-1"
|
||||||
|
|
||||||
|
renderCaves: true
|
||||||
|
lighting: 0.6
|
||||||
|
|
||||||
|
# We slice the whole world at y:90 so every block above 90 will be air.
|
||||||
|
# This way we don't render the nethers ceiling.
|
||||||
|
maxY: 90
|
||||||
|
renderEdges: true
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
46
BlueMapBukkit/src/main/resources/plugin.yml
Normal file
46
BlueMapBukkit/src/main/resources/plugin.yml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
name: BlueMap
|
||||||
|
description: "A 3d-map of your Minecraft worlds view-able in your browser using three.js (WebGL)"
|
||||||
|
main: de.bluecolored.bluemap.bukkit.BukkitPlugin
|
||||||
|
version: 0.2.1
|
||||||
|
author: "Blue (TBlueF / Lukas Rieger)"
|
||||||
|
authors: [Blue (TBlueF / Lukas Rieger)]
|
||||||
|
website: "https://github.com/BlueMap-Minecraft"
|
||||||
|
commands:
|
||||||
|
bluemap:
|
||||||
|
description: Root command for all bluemap commands
|
||||||
|
permission: bluemap
|
||||||
|
usage: |
|
||||||
|
/<command>
|
||||||
|
/<command> reload
|
||||||
|
/<command> pause
|
||||||
|
/<command> resume
|
||||||
|
/<command> render [world]
|
||||||
|
/<command> debug
|
||||||
|
permissions:
|
||||||
|
bluemap.*:
|
||||||
|
children:
|
||||||
|
bluemap.status: true
|
||||||
|
bluemap.reload: true
|
||||||
|
bluemap.pause: true
|
||||||
|
bluemap.resume: true
|
||||||
|
bluemap.rendertask.create.world: true
|
||||||
|
bluemap.rendertask.prioritize: true
|
||||||
|
bluemap.rendertask.remove: true
|
||||||
|
bluemap.debug: true
|
||||||
|
default: op
|
||||||
|
bluemap.status:
|
||||||
|
default: op
|
||||||
|
bluemap.reload:
|
||||||
|
default: op
|
||||||
|
bluemap.pause:
|
||||||
|
default: op
|
||||||
|
bluemap.resume:
|
||||||
|
default: op
|
||||||
|
bluemap.rendertask.create.world:
|
||||||
|
default: op
|
||||||
|
bluemap.rendertask.prioritize:
|
||||||
|
default: op
|
||||||
|
bluemap.rendertask.remove:
|
||||||
|
default: op
|
||||||
|
bluemap.debug:
|
||||||
|
default: op
|
@ -31,10 +31,8 @@
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ForkJoinPool;
|
import java.util.concurrent.ForkJoinPool;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -49,7 +47,6 @@
|
|||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.config.ConfigManager;
|
import de.bluecolored.bluemap.core.config.ConfigManager;
|
||||||
@ -162,14 +159,7 @@ public void renderMaps() throws IOException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HiresModelManager hiresModelManager = map.getTileRenderer().getHiresModelManager();
|
HiresModelManager hiresModelManager = map.getTileRenderer().getHiresModelManager();
|
||||||
Set<Vector2i> tiles = new HashSet<>();
|
Collection<Vector2i> tiles = hiresModelManager.getTilesForChunks(chunks);
|
||||||
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)));
|
|
||||||
}
|
|
||||||
Logger.global.logInfo("Found " + tiles.size() + " tiles to render! (" + chunks.size() + " chunks)");
|
Logger.global.logInfo("Found " + tiles.size() + " tiles to render! (" + chunks.size() + " chunks)");
|
||||||
if (!forceRender && chunks.size() == 0) {
|
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)");
|
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)");
|
||||||
|
3
BlueMapCommon/build.gradle
Normal file
3
BlueMapCommon/build.gradle
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
dependencies {
|
||||||
|
compile project(':BlueMapCore')
|
||||||
|
}
|
@ -22,7 +22,7 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.sponge;
|
package de.bluecolored.bluemap.common;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package de.bluecolored.bluemap.sponge;
|
package de.bluecolored.bluemap.common;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
@ -194,14 +194,14 @@ public void writeState(DataOutputStream out) throws IOException {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readState(DataInputStream in) throws IOException {
|
public void readState(DataInputStream in, Collection<MapType> mapTypes) throws IOException {
|
||||||
//read renderTickets
|
//read renderTickets
|
||||||
int mapCount = in.readInt();
|
int mapCount = in.readInt();
|
||||||
for (int i = 0; i < mapCount; i++) {
|
for (int i = 0; i < mapCount; i++) {
|
||||||
String mapId = in.readUTF();
|
String mapId = in.readUTF();
|
||||||
|
|
||||||
MapType mapType = null;
|
MapType mapType = null;
|
||||||
for (MapType map : SpongePlugin.getInstance().getMapTypes()) {
|
for (MapType map : mapTypes) {
|
||||||
if (map.getId().equals(mapId)) {
|
if (map.getId().equals(mapId)) {
|
||||||
mapType = map;
|
mapType = map;
|
||||||
break;
|
break;
|
||||||
@ -227,7 +227,7 @@ public void readState(DataInputStream in) throws IOException {
|
|||||||
int taskCount = in.readInt();
|
int taskCount = in.readInt();
|
||||||
for (int i = 0; i < taskCount; i++) {
|
for (int i = 0; i < taskCount; i++) {
|
||||||
try {
|
try {
|
||||||
RenderTask task = RenderTask.read(in);
|
RenderTask task = RenderTask.read(in, mapTypes);
|
||||||
addRenderTask(task);
|
addRenderTask(task);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Logger.global.logWarning("A render-task can not be loaded. It will be discared. (Error message: " + ex.toString() + ")");
|
Logger.global.logWarning("A render-task can not be loaded. It will be discared. (Error message: " + ex.toString() + ")");
|
@ -1,4 +1,4 @@
|
|||||||
package de.bluecolored.bluemap.sponge;
|
package de.bluecolored.bluemap.common;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
@ -159,12 +159,12 @@ public void write(DataOutputStream out) throws IOException {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RenderTask read(DataInputStream in) throws IOException {
|
public static RenderTask read(DataInputStream in, Collection<MapType> mapTypes) throws IOException {
|
||||||
String name = in.readUTF();
|
String name = in.readUTF();
|
||||||
String mapId = in.readUTF();
|
String mapId = in.readUTF();
|
||||||
|
|
||||||
MapType mapType = null;
|
MapType mapType = null;
|
||||||
for (MapType map : SpongePlugin.getInstance().getMapTypes()) {
|
for (MapType map : mapTypes) {
|
||||||
if (map.getId().equals(mapId)) {
|
if (map.getId().equals(mapId)) {
|
||||||
mapType = map;
|
mapType = map;
|
||||||
break;
|
break;
|
@ -1,4 +1,4 @@
|
|||||||
package de.bluecolored.bluemap.sponge;
|
package de.bluecolored.bluemap.common;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
@ -0,0 +1,298 @@
|
|||||||
|
package de.bluecolored.bluemap.common.plugin;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.time.DurationFormatUtils;
|
||||||
|
|
||||||
|
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.serverinterface.CommandSource;
|
||||||
|
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||||
|
import de.bluecolored.bluemap.common.plugin.text.TextColor;
|
||||||
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
|
import de.bluecolored.bluemap.core.mca.Chunk;
|
||||||
|
import de.bluecolored.bluemap.core.mca.ChunkAnvil112;
|
||||||
|
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||||
|
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
||||||
|
import de.bluecolored.bluemap.core.world.Block;
|
||||||
|
import de.bluecolored.bluemap.core.world.World;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commands:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>/bluemap</li>
|
||||||
|
* <li>/bluemap reload</li>
|
||||||
|
* <li>/bluemap pause</li>
|
||||||
|
* <li>/bluemap resume</li>
|
||||||
|
* <li>/bluemap render [world]</li>
|
||||||
|
* <li>/bluemap render prioritize [task-uuid]</li>
|
||||||
|
* <li>/bluemap render remove [task-uuid]</li>
|
||||||
|
* <li>/bluemap debug</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public class Commands {
|
||||||
|
|
||||||
|
private Plugin bluemap;
|
||||||
|
|
||||||
|
public Commands(Plugin bluemap) {
|
||||||
|
this.bluemap = bluemap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command: /bluemap
|
||||||
|
*/
|
||||||
|
public void executeRootCommand(CommandSource source) {
|
||||||
|
if (!checkLoaded(source)) return;
|
||||||
|
|
||||||
|
source.sendMessages(createStatusMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command: /bluemap debug
|
||||||
|
*/
|
||||||
|
public boolean executeDebugCommand(CommandSource source, UUID worldUuid, Vector3i playerPosition) {
|
||||||
|
if (!checkLoaded(source)) return false;
|
||||||
|
|
||||||
|
World world = bluemap.getWorld(worldUuid);
|
||||||
|
|
||||||
|
if (world == null) {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "This world is not loaded with BlueMap! Maybe it is not configured?"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block block = world.getBlock(playerPosition);
|
||||||
|
Block blockBelow = world.getBlock(playerPosition.add(0, -1, 0));
|
||||||
|
|
||||||
|
String blockIdMeta = "";
|
||||||
|
String blockBelowIdMeta = "";
|
||||||
|
|
||||||
|
if (world instanceof MCAWorld) {
|
||||||
|
try {
|
||||||
|
Chunk chunk = ((MCAWorld) world).getChunk(MCAWorld.blockToChunk(playerPosition));
|
||||||
|
if (chunk instanceof ChunkAnvil112) {
|
||||||
|
blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(playerPosition) + ")";
|
||||||
|
blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(playerPosition.add(0, -1, 0)) + ")";
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.global.logError("Failed to read chunk for debug!", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
source.sendMessages(Lists.newArrayList(
|
||||||
|
Text.of(TextColor.GOLD, "Block at you: ", TextColor.WHITE, block, TextColor.GRAY, blockIdMeta),
|
||||||
|
Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow, TextColor.GRAY, blockBelowIdMeta)
|
||||||
|
));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command: /bluemap reload
|
||||||
|
*/
|
||||||
|
public void executeReloadCommand(CommandSource source) {
|
||||||
|
source.sendMessage(Text.of(TextColor.GOLD, "Reloading BlueMap..."));
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
bluemap.reload();
|
||||||
|
|
||||||
|
if (bluemap.isLoaded()) {
|
||||||
|
source.sendMessage(Text.of(TextColor.GREEN, "BlueMap reloaded!"));
|
||||||
|
} else {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "Could not load BlueMap! See the console for details!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.global.logError("Failed to reload BlueMap!", ex);
|
||||||
|
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "There was an error reloading BlueMap! See the console for details!"));
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command: /bluemap pause
|
||||||
|
*/
|
||||||
|
public boolean executePauseCommand(CommandSource source) {
|
||||||
|
if (!checkLoaded(source)) return false;
|
||||||
|
|
||||||
|
if (bluemap.getRenderManager().isRunning()) {
|
||||||
|
bluemap.getRenderManager().stop();
|
||||||
|
source.sendMessage(Text.of(TextColor.GREEN, "BlueMap rendering paused!"));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "BlueMap rendering are already paused!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command: /bluemap resume
|
||||||
|
*/
|
||||||
|
public boolean executeResumeCommand(CommandSource source) {
|
||||||
|
if (!checkLoaded(source)) return false;
|
||||||
|
|
||||||
|
if (!bluemap.getRenderManager().isRunning()) {
|
||||||
|
bluemap.getRenderManager().start();
|
||||||
|
source.sendMessage(Text.of(TextColor.GREEN, "BlueMap renders resumed!"));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "BlueMap renders are already running!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command: /bluemap render [world]
|
||||||
|
*/
|
||||||
|
public boolean executeRenderWorldCommand(CommandSource source, UUID worldUuid) {
|
||||||
|
if (!checkLoaded(source)) return false;
|
||||||
|
|
||||||
|
World world = bluemap.getWorld(worldUuid);
|
||||||
|
|
||||||
|
if (world == null) {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "This world is not loaded with BlueMap! Maybe it is not configured?"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
world.invalidateChunkCache();
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
createWorldRenderTask(source, world);
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command: /bluemap render prioritize [task-uuid]
|
||||||
|
*/
|
||||||
|
public void executePrioritizeRenderTaskCommand(CommandSource source, UUID taskUUID) {
|
||||||
|
if (!checkLoaded(source)) return;
|
||||||
|
|
||||||
|
for (RenderTask task : bluemap.getRenderManager().getRenderTasks()) {
|
||||||
|
if (task.getUuid().equals(taskUUID)) {
|
||||||
|
bluemap.getRenderManager().prioritizeRenderTask(task);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
source.sendMessages(createStatusMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command: /bluemap render remove [task-uuid]
|
||||||
|
*/
|
||||||
|
public void executeRemoveRenderTaskCommand(CommandSource source, UUID taskUUID) {
|
||||||
|
if (!checkLoaded(source)) return;
|
||||||
|
|
||||||
|
for (RenderTask task : bluemap.getRenderManager().getRenderTasks()) {
|
||||||
|
if (task.getUuid().equals(taskUUID)) {
|
||||||
|
bluemap.getRenderManager().removeRenderTask(task);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
source.sendMessages(createStatusMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Text> createStatusMessage(){
|
||||||
|
List<Text> lines = new ArrayList<>();
|
||||||
|
|
||||||
|
RenderManager renderer = bluemap.getRenderManager();
|
||||||
|
|
||||||
|
lines.add(Text.of());
|
||||||
|
lines.add(Text.of(TextColor.BLUE, "Tile-Updates:"));
|
||||||
|
|
||||||
|
if (renderer.isRunning()) {
|
||||||
|
lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.GREEN, "running").setHoverText(Text.of("click to pause rendering")).setClickCommand("/bluemap pause"), TextColor.GRAY, "!"));
|
||||||
|
} else {
|
||||||
|
lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.RED, "paused").setHoverText(Text.of("click to resume rendering")).setClickCommand("/bluemap resume"), TextColor.GRAY, "!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.add(Text.of(TextColor.WHITE, " Scheduled tile-updates: ", Text.of(TextColor.GOLD, renderer.getQueueSize()).setHoverText(Text.of("tiles waiting for a free render-thread")), TextColor.GRAY, " + " , Text.of(TextColor.GRAY, bluemap.getUpdateHandler().getUpdateBufferCount()).setHoverText(Text.of("tiles waiting for world-save"))));
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
double tps = task.getRenderedTileCount() / (time / 1000.0);
|
||||||
|
|
||||||
|
lines.add(Text.of(TextColor.BLUE, "Current task:"));
|
||||||
|
lines.add(Text.of(" ", createCancelTaskText(task), TextColor.WHITE, " Task ", TextColor.GOLD, task.getName(), TextColor.WHITE, " for map ", Text.of(TextColor.GOLD, task.getMapType().getName()).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GOLD, task.getMapType().getWorld().getName()))));
|
||||||
|
lines.add(Text.of(TextColor.WHITE, " rendered ", TextColor.GOLD, task.getRenderedTileCount(), TextColor.WHITE, " tiles ", TextColor.GRAY, "(" + (Math.round(pct * 1000)/10.0) + "% | " + GenericMath.round(tps, 1) + "t/s)", TextColor.WHITE, " in ", TextColor.GOLD, durationString));
|
||||||
|
lines.add(Text.of(TextColor.WHITE, " with ", TextColor.GOLD, task.getRemainingTileCount(), TextColor.WHITE, " tiles to go. ETA: ", TextColor.GOLD, ertDurationString));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tasks.length > 1) {
|
||||||
|
lines.add(Text.of(TextColor.BLUE, "Waiting tasks:"));
|
||||||
|
for (int i = 1; i < tasks.length; i++) {
|
||||||
|
RenderTask task = tasks[i];
|
||||||
|
lines.add(Text.of(" ", createCancelTaskText(task), createPrioritizeTaskText(task), TextColor.WHITE, " Task ", TextColor.GOLD, task.getName(), TextColor.WHITE, " for map ", Text.of(TextColor.GOLD, task.getMapType().getName()).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GOLD, task.getMapType().getWorld().getName())), TextColor.GRAY, " (" + task.getRemainingTileCount() + " tiles)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Text createCancelTaskText(RenderTask task) {
|
||||||
|
return Text.of(TextColor.RED, "[X]").setHoverText(Text.of(TextColor.GRAY, "click to remove this render-task")).setClickCommand("/bluemap render remove " + task.getUuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Text createPrioritizeTaskText(RenderTask task) {
|
||||||
|
return Text.of(TextColor.GREEN, "[^]").setHoverText(Text.of(TextColor.GRAY, "click to prioritize this render-task")).setClickCommand("/bluemap render prioritize " + task.getUuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createWorldRenderTask(CommandSource source, World world) {
|
||||||
|
source.sendMessage(Text.of(TextColor.GOLD, "Collecting chunks to render..."));
|
||||||
|
Collection<Vector2i> chunks = world.getChunkList();
|
||||||
|
source.sendMessage(Text.of(TextColor.GREEN, chunks.size() + " chunks found!"));
|
||||||
|
|
||||||
|
for (MapType map : bluemap.getMapTypes()) {
|
||||||
|
if (!map.getWorld().getUUID().equals(world.getUUID())) continue;
|
||||||
|
|
||||||
|
source.sendMessage(Text.of(TextColor.GOLD, "Collecting tiles for map '" + map.getId() + "'"));
|
||||||
|
|
||||||
|
HiresModelManager hmm = map.getTileRenderer().getHiresModelManager();
|
||||||
|
Collection<Vector2i> tiles = hmm.getTilesForChunks(chunks);
|
||||||
|
|
||||||
|
RenderTask task = new RenderTask("world-render", map);
|
||||||
|
task.addTiles(tiles);
|
||||||
|
task.optimizeQueue();
|
||||||
|
bluemap.getRenderManager().addRenderTask(task);
|
||||||
|
|
||||||
|
source.sendMessage(Text.of(TextColor.GREEN, tiles.size() + " tiles found! Task created."));
|
||||||
|
}
|
||||||
|
|
||||||
|
source.sendMessage(Text.of(TextColor.GREEN, "All render tasks created! Use /bluemap to view the progress!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkLoaded(CommandSource source) {
|
||||||
|
if (!bluemap.isLoaded()) {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "BlueMap is not loaded!", TextColor.GRAY, "(Try /bluemap reload)"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,45 +1,34 @@
|
|||||||
package de.bluecolored.bluemap.sponge;
|
package de.bluecolored.bluemap.common.plugin;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
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.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.MultimapBuilder;
|
import com.google.common.collect.MultimapBuilder;
|
||||||
|
|
||||||
public class MapUpdateHandler {
|
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 {
|
||||||
|
|
||||||
public Multimap<MapType, Vector2i> updateBuffer;
|
public Multimap<MapType, Vector2i> updateBuffer;
|
||||||
|
|
||||||
public MapUpdateHandler() {
|
public MapUpdateHandler() {
|
||||||
updateBuffer = MultimapBuilder.hashKeys().hashSetValues().build();
|
updateBuffer = MultimapBuilder.hashKeys().hashSetValues().build();
|
||||||
|
|
||||||
Sponge.getEventManager().registerListeners(SpongePlugin.getInstance(), this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listener(order = Order.POST)
|
@Override
|
||||||
public void onWorldSave(SaveWorldEvent.Post evt) {
|
public void onWorldSaveToDisk(UUID world) {
|
||||||
UUID worldUuid = evt.getTargetWorld().getUniqueId();
|
RenderManager renderManager = Plugin.getInstance().getRenderManager();
|
||||||
RenderManager renderManager = SpongePlugin.getInstance().getRenderManager();
|
|
||||||
|
|
||||||
synchronized (updateBuffer) {
|
synchronized (updateBuffer) {
|
||||||
Iterator<MapType> iterator = updateBuffer.keys().iterator();
|
Iterator<MapType> iterator = updateBuffer.keys().iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
MapType map = iterator.next();
|
MapType map = iterator.next();
|
||||||
if (map.getWorld().getUUID().equals(worldUuid)) {
|
if (map.getWorld().getUUID().equals(world)) {
|
||||||
renderManager.createTickets(map, updateBuffer.get(map));
|
renderManager.createTickets(map, updateBuffer.get(map));
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
@ -48,27 +37,17 @@ public void onWorldSave(SaveWorldEvent.Post evt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listener(order = Order.POST)
|
@Override
|
||||||
@Exclude({ChangeBlockEvent.Post.class, ChangeBlockEvent.Pre.class, ChangeBlockEvent.Modify.class})
|
public void onBlockChange(UUID world, Vector3i blockPos) {
|
||||||
public void onBlockChange(ChangeBlockEvent evt) {
|
|
||||||
synchronized (updateBuffer) {
|
synchronized (updateBuffer) {
|
||||||
for (Transaction<BlockSnapshot> tr : evt.getTransactions()) {
|
updateBlock(world, blockPos);
|
||||||
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)
|
@Override
|
||||||
public void onChunkPopulate(PopulateChunkEvent.Post evt) {
|
public void onChunkFinishedGeneration(UUID world, Vector2i chunkPos) {
|
||||||
UUID world = evt.getTargetChunk().getWorld().getUniqueId();
|
int x = chunkPos.getX();
|
||||||
|
int z = chunkPos.getY();
|
||||||
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
|
// 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 dx = -1; dx <= 1; dx++) {
|
||||||
@ -96,7 +75,7 @@ private void updateChunk(UUID world, Vector2i chunkPos) {
|
|||||||
|
|
||||||
private void updateBlock(UUID world, Vector3i pos){
|
private void updateBlock(UUID world, Vector3i pos){
|
||||||
synchronized (updateBuffer) {
|
synchronized (updateBuffer) {
|
||||||
for (MapType mapType : SpongePlugin.getInstance().getMapTypes()) {
|
for (MapType mapType : Plugin.getInstance().getMapTypes()) {
|
||||||
if (mapType.getWorld().getUUID().equals(world)) {
|
if (mapType.getWorld().getUUID().equals(world)) {
|
||||||
mapType.getWorld().invalidateChunkCache(mapType.getWorld().blockPosToChunkPos(pos));
|
mapType.getWorld().invalidateChunkCache(mapType.getWorld().blockPosToChunkPos(pos));
|
||||||
|
|
||||||
@ -112,7 +91,7 @@ public int getUpdateBufferCount() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void flushTileBuffer() {
|
public void flushTileBuffer() {
|
||||||
RenderManager renderManager = SpongePlugin.getInstance().getRenderManager();
|
RenderManager renderManager = Plugin.getInstance().getRenderManager();
|
||||||
|
|
||||||
synchronized (updateBuffer) {
|
synchronized (updateBuffer) {
|
||||||
for (MapType map : updateBuffer.keySet()) {
|
for (MapType map : updateBuffer.keySet()) {
|
@ -0,0 +1,367 @@
|
|||||||
|
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 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;
|
||||||
|
import de.bluecolored.bluemap.core.config.MainConfig.MapConfig;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class Plugin {
|
||||||
|
|
||||||
|
public static final String PLUGIN_ID = "bluemap";
|
||||||
|
public static final String PLUGIN_NAME = "BlueMap";
|
||||||
|
public static final String PLUGIN_VERSION = BlueMap.VERSION;
|
||||||
|
|
||||||
|
private static Plugin instance;
|
||||||
|
|
||||||
|
private String implementationType;
|
||||||
|
|
||||||
|
private ServerInterface serverInterface;
|
||||||
|
private Commands commands;
|
||||||
|
|
||||||
|
private MainConfig config;
|
||||||
|
private ResourcePack resourcePack;
|
||||||
|
|
||||||
|
private Map<UUID, World> worlds;
|
||||||
|
private Map<String, MapType> maps;
|
||||||
|
|
||||||
|
private MapUpdateHandler updateHandler;
|
||||||
|
|
||||||
|
private RenderManager renderManager;
|
||||||
|
private BlueMapWebServer webServer;
|
||||||
|
|
||||||
|
private Thread metricsThread;
|
||||||
|
|
||||||
|
private boolean loaded = false;
|
||||||
|
|
||||||
|
public Plugin(String implementationType, ServerInterface serverInterface) {
|
||||||
|
this.implementationType = implementationType.toLowerCase();
|
||||||
|
|
||||||
|
this.serverInterface = serverInterface;
|
||||||
|
this.commands = new Commands(this);
|
||||||
|
|
||||||
|
this.maps = new HashMap<>();
|
||||||
|
this.worlds = new HashMap<>();
|
||||||
|
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void load() throws IOException, ParseResourceException {
|
||||||
|
if (loaded) return;
|
||||||
|
unload(); //ensure nothing is left running (from a failed load or something)
|
||||||
|
|
||||||
|
//load configs
|
||||||
|
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();
|
||||||
|
|
||||||
|
//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()) {
|
||||||
|
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(Plugin.class.getResource("/resourceExtensions.zip"), resourceExtensionsFile, 10000, 10000);
|
||||||
|
|
||||||
|
//find more resource packs
|
||||||
|
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
|
||||||
|
|
||||||
|
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 {
|
||||||
|
worldUUID = serverInterface.getUUIDForWorld(worldFolder);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.global.logError("Failed to load map '" + id + "': Failed to get UUID for the world!", 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()),
|
||||||
|
ForkJoinPool.commonPool()
|
||||||
|
);
|
||||||
|
|
||||||
|
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();
|
||||||
|
serverInterface.registerListener(updateHandler);
|
||||||
|
|
||||||
|
//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();
|
||||||
|
}
|
||||||
|
|
||||||
|
//metrics
|
||||||
|
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() throws IOException, ParseResourceException {
|
||||||
|
unload();
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerInterface getServerInterface() {
|
||||||
|
return serverInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Commands getCommands() {
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MainConfig getMainConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourcePack getResourcePack() {
|
||||||
|
return resourcePack;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 BlueMapWebServer getWebServer() {
|
||||||
|
return webServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLoaded() {
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Plugin getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package de.bluecolored.bluemap.common.plugin.serverinterface;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||||
|
|
||||||
|
public interface CommandSource {
|
||||||
|
|
||||||
|
void sendMessage(Text text);
|
||||||
|
|
||||||
|
default void sendMessages(Iterable<Text> textLines) {
|
||||||
|
for (Text text : textLines) {
|
||||||
|
sendMessage(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package de.bluecolored.bluemap.common.plugin.serverinterface;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
|
public interface ServerEventListener {
|
||||||
|
|
||||||
|
void onWorldSaveToDisk(UUID world);
|
||||||
|
|
||||||
|
void onBlockChange(UUID world, Vector3i blockPos);
|
||||||
|
|
||||||
|
void onChunkFinishedGeneration(UUID world, Vector2i chunkPos);
|
||||||
|
|
||||||
|
}
|
@ -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,207 @@
|
|||||||
|
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 Text setHoverText(Text hoverText) {
|
||||||
|
this.hoverText = hoverText;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Text setClickCommand(String clickCommand) {
|
||||||
|
this.clickCommand = clickCommand;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Text addChild(Text child) {
|
||||||
|
children.add(child);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toJSONString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("{");
|
||||||
|
|
||||||
|
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("extra")).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) {
|
||||||
|
if (sb.length() > 0) sb.append(escapeChar).append('r');
|
||||||
|
sb.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("§", "\\u00a7");
|
||||||
|
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(0);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
"name": "BlueMap",
|
"name": "BlueMap",
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"description": "A 3d-map of your Minecraft worlds view-able in your browser using three.js (WebGL)",
|
"description": "A 3d-map of your Minecraft worlds view-able in your browser using three.js (WebGL)",
|
||||||
"url": "https://ore.spongepowered.org/Blue/BlueMap",
|
"url": "https://github.com/BlueMap-Minecraft",
|
||||||
"authorList": [
|
"authorList": [
|
||||||
"Blue (TBlueF, Lukas Rieger)"
|
"Blue (TBlueF, Lukas Rieger)"
|
||||||
],
|
],
|
@ -385,7 +385,10 @@ public static MCAWorld load(Path worldFolder, UUID uuid, BlockIdMapper blockIdMa
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
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) {
|
for (Tag<?> tag : blockIdReg) {
|
||||||
if (tag instanceof CompoundTag) {
|
if (tag instanceof CompoundTag) {
|
||||||
CompoundTag entry = (CompoundTag) tag;
|
CompoundTag entry = (CompoundTag) tag;
|
||||||
|
@ -31,6 +31,9 @@
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
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.concurrent.ExecutorService;
|
||||||
import java.util.zip.GZIPOutputStream;
|
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
|
* Returns the region of blocks that a tile includes
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
dependencies {
|
dependencies {
|
||||||
shadow "org.spongepowered:spongeapi:7.1.0-SNAPSHOT"
|
shadow "org.spongepowered:spongeapi:7.1.0-SNAPSHOT"
|
||||||
compile group: 'org.bstats', name: 'bstats-sponge-lite', version: '1.5'
|
compile group: 'org.bstats', name: 'bstats-sponge-lite', version: '1.5'
|
||||||
compile project(':BlueMapCore')
|
compile project(':BlueMapCommon')
|
||||||
}
|
}
|
||||||
|
@ -1,345 +0,0 @@
|
|||||||
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.GenericMath;
|
|
||||||
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.mca.Chunk;
|
|
||||||
import de.bluecolored.bluemap.core.mca.ChunkAnvil112;
|
|
||||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
|
||||||
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
|
||||||
import de.bluecolored.bluemap.core.world.Block;
|
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
|
||||||
|
|
||||||
public class Commands {
|
|
||||||
|
|
||||||
private SpongePlugin plugin;
|
|
||||||
|
|
||||||
public Commands(SpongePlugin plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandSpec createRootCommand() {
|
|
||||||
|
|
||||||
CommandSpec debugCommand = CommandSpec.builder()
|
|
||||||
.permission("bluemap.debug")
|
|
||||||
.description(Text.of("Prints some debug info"))
|
|
||||||
.extendedDescription(Text.of("Prints some information about how bluemap sees the blocks at and below your position"))
|
|
||||||
.executor((source, args) -> {
|
|
||||||
if (source instanceof Locatable) {
|
|
||||||
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));
|
|
||||||
|
|
||||||
String blockIdMeta = "";
|
|
||||||
String blockBelowIdMeta = "";
|
|
||||||
|
|
||||||
if (world instanceof MCAWorld) {
|
|
||||||
try {
|
|
||||||
Chunk chunk = ((MCAWorld) world).getChunk(MCAWorld.blockToChunk(loc.getBlockPosition()));
|
|
||||||
if (chunk instanceof ChunkAnvil112) {
|
|
||||||
blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(loc.getBlockPosition()) + ")";
|
|
||||||
blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(loc.getBlockPosition().add(0, -1, 0)) + ")";
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
Logger.global.logError("Failed to read chunk for debug!", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
source.sendMessages(Lists.newArrayList(
|
|
||||||
Text.of(TextColors.GOLD, "Block at you: ", TextColors.RESET, block, TextColors.GRAY, blockIdMeta),
|
|
||||||
Text.of(TextColors.GOLD, "Block below you: ", TextColors.RESET, blockBelow, TextColors.GRAY, blockBelowIdMeta)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 createStandaloneReloadCommand() {
|
|
||||||
return CommandSpec.builder()
|
|
||||||
.description(Text.of("BlueMaps root command"))
|
|
||||||
.childArgumentParseExceptionFallback(false)
|
|
||||||
.child(createReloadCommand(), "reload")
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandSpec createReloadCommand() {
|
|
||||||
return CommandSpec.builder()
|
|
||||||
.description(Text.of("Reloads all resources and configuration-files"))
|
|
||||||
.permission("bluemap.reload")
|
|
||||||
.executor((source, args) -> {
|
|
||||||
source.sendMessage(Text.of(TextColors.GOLD, "Reloading BlueMap..."));
|
|
||||||
|
|
||||||
plugin.getAsyncExecutor().submit(() -> {
|
|
||||||
try {
|
|
||||||
plugin.reload();
|
|
||||||
|
|
||||||
if (plugin.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!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception 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.success();
|
|
||||||
})
|
|
||||||
.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);
|
|
||||||
|
|
||||||
double tps = task.getRenderedTileCount() / (time / 1000.0);
|
|
||||||
|
|
||||||
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) + "% | " + GenericMath.round(tps, 1) + "t/s)", 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!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.spongepowered.api.text.serializer.TextSerializers;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource;
|
||||||
|
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||||
|
|
||||||
|
public class SpongeCommandSource implements CommandSource {
|
||||||
|
|
||||||
|
private org.spongepowered.api.command.CommandSource delegate;
|
||||||
|
|
||||||
|
public SpongeCommandSource(org.spongepowered.api.command.CommandSource delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(Text text) {
|
||||||
|
org.spongepowered.api.text.Text spongeText = TextSerializers.JSON.deserializeUnchecked(text.toJSONString());
|
||||||
|
delegate.sendMessage(spongeText);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,170 @@
|
|||||||
|
package de.bluecolored.bluemap.sponge;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.spongepowered.api.command.CommandResult;
|
||||||
|
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.format.TextColors;
|
||||||
|
import org.spongepowered.api.world.Locatable;
|
||||||
|
import org.spongepowered.api.world.Location;
|
||||||
|
import org.spongepowered.api.world.storage.WorldProperties;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.common.plugin.Commands;
|
||||||
|
|
||||||
|
public class SpongeCommands {
|
||||||
|
|
||||||
|
private Commands commands;
|
||||||
|
|
||||||
|
public SpongeCommands(Commands commands) {
|
||||||
|
this.commands = commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandSpec createRootCommand() {
|
||||||
|
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(createDebugCommand(), "debug")
|
||||||
|
.executor((source, args) -> {
|
||||||
|
commands.executeRootCommand(new SpongeCommandSource(source));
|
||||||
|
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) -> {
|
||||||
|
commands.executeReloadCommand(new SpongeCommandSource(source));
|
||||||
|
return CommandResult.success();
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandSpec createPauseRenderCommand() {
|
||||||
|
return CommandSpec.builder()
|
||||||
|
.description(Text.of("Pauses all rendering"))
|
||||||
|
.permission("bluemap.pause")
|
||||||
|
.executor((source, args) -> {
|
||||||
|
if (commands.executePauseCommand(new SpongeCommandSource(source))) {
|
||||||
|
return CommandResult.success();
|
||||||
|
} else {
|
||||||
|
return CommandResult.empty();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandSpec createResumeRenderCommand() {
|
||||||
|
return CommandSpec.builder()
|
||||||
|
.description(Text.of("Resumes all paused rendering"))
|
||||||
|
.permission("bluemap.resume")
|
||||||
|
.executor((source, args) -> {
|
||||||
|
if (commands.executeResumeCommand(new SpongeCommandSource(source))) {
|
||||||
|
return CommandResult.success();
|
||||||
|
} else {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commands.executeRenderWorldCommand(new SpongeCommandSource(source), spongeWorld.getUniqueId())) {
|
||||||
|
return CommandResult.success();
|
||||||
|
} else {
|
||||||
|
return CommandResult.empty();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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(TextColors.RED, "You need to specify a task-uuid"));
|
||||||
|
return CommandResult.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.executePrioritizeRenderTaskCommand(new SpongeCommandSource(source), uuid.get());
|
||||||
|
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(TextColors.RED, "You need to specify a task-uuid"));
|
||||||
|
return CommandResult.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.executeRemoveRenderTaskCommand(new SpongeCommandSource(source), uuid.get());
|
||||||
|
return CommandResult.success();
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandSpec createDebugCommand() {
|
||||||
|
return CommandSpec.builder()
|
||||||
|
.permission("bluemap.debug")
|
||||||
|
.description(Text.of("Prints some debug info"))
|
||||||
|
.extendedDescription(Text.of("Prints some information about how bluemap sees the blocks at and below your position"))
|
||||||
|
.executor((source, args) -> {
|
||||||
|
if (source instanceof Locatable) {
|
||||||
|
Location<org.spongepowered.api.world.World> loc = ((Locatable) source).getLocation();
|
||||||
|
UUID worldUuid = loc.getExtent().getUniqueId();
|
||||||
|
|
||||||
|
if (commands.executeDebugCommand(new SpongeCommandSource(source), worldUuid, loc.getBlockPosition())) {
|
||||||
|
return CommandResult.success();
|
||||||
|
} else {
|
||||||
|
return CommandResult.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
source.sendMessage(Text.of(TextColors.RED, "You can only execute this command as a player!"));
|
||||||
|
return CommandResult.empty();
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -24,29 +24,13 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.sponge;
|
package de.bluecolored.bluemap.sponge;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.file.Path;
|
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.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 javax.inject.Inject;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
|
||||||
import org.bstats.sponge.MetricsLite2;
|
import org.bstats.sponge.MetricsLite2;
|
||||||
import org.spongepowered.api.Sponge;
|
import org.spongepowered.api.Sponge;
|
||||||
import org.spongepowered.api.config.ConfigDir;
|
import org.spongepowered.api.config.ConfigDir;
|
||||||
@ -54,47 +38,24 @@
|
|||||||
import org.spongepowered.api.event.game.GameReloadEvent;
|
import org.spongepowered.api.event.game.GameReloadEvent;
|
||||||
import org.spongepowered.api.event.game.state.GameStartingServerEvent;
|
import org.spongepowered.api.event.game.state.GameStartingServerEvent;
|
||||||
import org.spongepowered.api.event.game.state.GameStoppingEvent;
|
import org.spongepowered.api.event.game.state.GameStoppingEvent;
|
||||||
import org.spongepowered.api.plugin.Plugin;
|
|
||||||
import org.spongepowered.api.scheduler.SpongeExecutorService;
|
import org.spongepowered.api.scheduler.SpongeExecutorService;
|
||||||
import org.spongepowered.api.world.storage.WorldProperties;
|
import org.spongepowered.api.world.storage.WorldProperties;
|
||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||||
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||||
import de.bluecolored.bluemap.core.BlueMap;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||||
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.core.logger.Logger;
|
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 net.querz.nbt.CompoundTag;
|
import net.querz.nbt.CompoundTag;
|
||||||
import net.querz.nbt.NBTUtil;
|
import net.querz.nbt.NBTUtil;
|
||||||
|
|
||||||
@Plugin(
|
@org.spongepowered.api.plugin.Plugin(
|
||||||
id = SpongePlugin.PLUGIN_ID,
|
id = Plugin.PLUGIN_ID,
|
||||||
name = SpongePlugin.PLUGIN_NAME,
|
name = Plugin.PLUGIN_NAME,
|
||||||
authors = { "Blue (Lukas Rieger)" },
|
authors = { "Blue (Lukas Rieger)" },
|
||||||
description = "This plugin provides a fully 3D map of your world for your browser!",
|
description = "This plugin provides a fully 3D map of your world for your browser!",
|
||||||
version = SpongePlugin.PLUGIN_VERSION
|
version = Plugin.PLUGIN_VERSION
|
||||||
)
|
)
|
||||||
public class SpongePlugin {
|
public class SpongePlugin implements ServerInterface {
|
||||||
|
|
||||||
public static final String PLUGIN_ID = "bluemap";
|
|
||||||
public static final String PLUGIN_NAME = "BlueMap";
|
|
||||||
public static final String PLUGIN_VERSION = BlueMap.VERSION;
|
|
||||||
|
|
||||||
private static SpongePlugin instance;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ConfigDir(sharedRoot = false)
|
@ConfigDir(sharedRoot = false)
|
||||||
@ -104,261 +65,19 @@ public class SpongePlugin {
|
|||||||
@Inject
|
@Inject
|
||||||
private MetricsLite2 metrics;
|
private MetricsLite2 metrics;
|
||||||
|
|
||||||
private MainConfig config;
|
private Plugin bluemap;
|
||||||
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 SpongeExecutorService asyncExecutor;
|
||||||
|
|
||||||
private boolean loaded = false;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SpongePlugin(org.slf4j.Logger logger) {
|
public SpongePlugin(org.slf4j.Logger logger) {
|
||||||
Logger.global = new Slf4jLogger(logger);
|
Logger.global = new Slf4jLogger(logger);
|
||||||
|
|
||||||
this.maps = new HashMap<>();
|
this.bluemap = new Plugin("sponge", this);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listener
|
@Listener
|
||||||
public void onServerStart(GameStartingServerEvent evt) {
|
public void onServerStart(GameStartingServerEvent evt) {
|
||||||
syncExecutor = Sponge.getScheduler().createSyncExecutor(this);
|
|
||||||
asyncExecutor = Sponge.getScheduler().createAsyncExecutor(this);
|
asyncExecutor = Sponge.getScheduler().createAsyncExecutor(this);
|
||||||
|
|
||||||
//save all world properties to generate level_sponge.dat files
|
//save all world properties to generate level_sponge.dat files
|
||||||
@ -366,19 +85,23 @@ public void onServerStart(GameStartingServerEvent evt) {
|
|||||||
Sponge.getServer().saveWorldProperties(properties);
|
Sponge.getServer().saveWorldProperties(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sponge.getCommandManager().register(this, new SpongeCommands(bluemap.getCommands()).createRootCommand(), "bluemap");
|
||||||
|
|
||||||
asyncExecutor.execute(() -> {
|
asyncExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
load();
|
Logger.global.logInfo("Loading...");
|
||||||
if (isLoaded()) Logger.global.logInfo("Loaded!");
|
bluemap.load();
|
||||||
} catch (Exception e) {
|
if (bluemap.isLoaded()) Logger.global.logInfo("Loaded!");
|
||||||
Logger.global.logError("Failed to load!", e);
|
} catch (Throwable t) {
|
||||||
|
Logger.global.logError("Failed to load!", t);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listener
|
@Listener
|
||||||
public void onServerStop(GameStoppingEvent evt) {
|
public void onServerStop(GameStoppingEvent evt) {
|
||||||
unload();
|
Logger.global.logInfo("Stopping...");
|
||||||
|
bluemap.unload();
|
||||||
Logger.global.logInfo("Saved and stopped!");
|
Logger.global.logInfo("Saved and stopped!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,7 +110,7 @@ public void onServerReload(GameReloadEvent evt) {
|
|||||||
asyncExecutor.execute(() -> {
|
asyncExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
Logger.global.logInfo("Reloading...");
|
Logger.global.logInfo("Reloading...");
|
||||||
reload();
|
bluemap.reload();
|
||||||
Logger.global.logInfo("Reloaded!");
|
Logger.global.logInfo("Reloaded!");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.global.logError("Failed to load!", e);
|
Logger.global.logError("Failed to load!", e);
|
||||||
@ -395,72 +118,32 @@ public void onServerReload(GameReloadEvent evt) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMissingResources(File resourceFile, File mainConfigFile) {
|
@Override
|
||||||
if (config.isDownloadAccepted()) {
|
public void registerListener(ServerEventListener listener) {
|
||||||
|
Sponge.getEventManager().registerListeners(this, new EventForwarder(listener));
|
||||||
|
}
|
||||||
|
|
||||||
//download file async
|
@Override
|
||||||
asyncExecutor.execute(() -> {
|
public void unregisterAllListeners() {
|
||||||
|
Sponge.getEventManager().unregisterPluginListeners(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getUUIDForWorld(File worldFolder) throws IOException {
|
||||||
try {
|
try {
|
||||||
Logger.global.logInfo("Downloading " + ResourcePack.MINECRAFT_CLIENT_URL + " to " + resourceFile + " ...");
|
CompoundTag levelSponge = (CompoundTag) NBTUtil.readTag(new File(worldFolder, "level_sponge.dat"));
|
||||||
ResourcePack.downloadDefaultResource(resourceFile);
|
CompoundTag spongeData = levelSponge.getCompoundTag("SpongeData");
|
||||||
} catch (IOException e) {
|
long most = spongeData.getLong("UUIDMost");
|
||||||
Logger.global.logError("Failed to download resources!", e);
|
long least = spongeData.getLong("UUIDLeast");
|
||||||
return;
|
return new UUID(most, least);
|
||||||
}
|
} catch (Throwable t) {
|
||||||
|
throw new IOException("Failed to read level_sponge.dat", t);
|
||||||
// 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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpongeExecutorService getSyncExecutor(){
|
@Override
|
||||||
return syncExecutor;
|
public File getConfigFolder() {
|
||||||
}
|
return configurationDir.toFile();
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,13 @@ allprojects {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':BlueMapCLI')
|
compile project(':BlueMapCLI')
|
||||||
//compile project(':BlueMapSponge')
|
compile project(':BlueMapBukkit')
|
||||||
|
compile project(':BlueMapSponge')
|
||||||
}
|
}
|
||||||
|
|
||||||
assemble.dependsOn shadowJar {
|
assemble.dependsOn shadowJar {
|
||||||
|
relocate 'org.bstats.bukkit', 'de.bluecolored.bluemap.bstats.bukkit'
|
||||||
|
|
||||||
baseName = 'BlueMap'
|
baseName = 'BlueMap'
|
||||||
version = null
|
version = null
|
||||||
classifier = null
|
classifier = null
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
rootProject.name = 'BlueMap'
|
rootProject.name = 'BlueMap'
|
||||||
include ':BlueMapCore'
|
include ':BlueMapCore'
|
||||||
include ':BlueMapCLI'
|
include ':BlueMapCLI'
|
||||||
|
include ':BlueMapCommon'
|
||||||
include ':BlueMapSponge'
|
include ':BlueMapSponge'
|
||||||
|
include ':BlueMapBukkit'
|
||||||
|
|
||||||
project(':BlueMapCore').projectDir = "$rootDir/BlueMapCore" as File
|
project(':BlueMapCore').projectDir = "$rootDir/BlueMapCore" as File
|
||||||
project(':BlueMapCLI').projectDir = "$rootDir/BlueMapCLI" as File
|
project(':BlueMapCLI').projectDir = "$rootDir/BlueMapCLI" as File
|
||||||
|
project(':BlueMapCommon').projectDir = "$rootDir/BlueMapCommon" as File
|
||||||
project(':BlueMapSponge').projectDir = "$rootDir/BlueMapSponge" as File
|
project(':BlueMapSponge').projectDir = "$rootDir/BlueMapSponge" as File
|
||||||
|
project(':BlueMapBukkit').projectDir = "$rootDir/BlueMapBukkit" as File
|
Loading…
Reference in New Issue
Block a user