diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index c650044bd..955f7d84d 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -19,28 +19,23 @@ package com.sk89q.worldedit.bukkit; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - import com.sk89q.bukkit.util.CommandInfo; import com.sk89q.bukkit.util.CommandRegistration; import com.sk89q.minecraft.util.commands.Command; - import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.CommandsManager; -import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.*; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Server; import org.bukkit.World; import org.bukkit.entity.EntityType; -import com.sk89q.worldedit.BiomeTypes; -import com.sk89q.worldedit.LocalWorld; -import com.sk89q.worldedit.ServerInterface; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; public class BukkitServerInterface extends ServerInterface { public Server server; @@ -119,6 +114,11 @@ public class BukkitServerInterface extends ServerInterface { dynamicCommands.register(toRegister); } + @Override + public LocalConfiguration getConfiguration() { + return plugin.getLocalConfiguration(); + } + public void unregisterCommands() { dynamicCommands.unregisterCommands(); } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 9c64464e7..2a84f875c 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -25,12 +25,12 @@ import com.sk89q.worldedit.*; import com.sk89q.worldedit.bukkit.selections.CuboidSelection; import com.sk89q.worldedit.bukkit.selections.Polygonal2DSelection; import com.sk89q.worldedit.bukkit.selections.Selection; +import com.sk89q.worldedit.extension.platform.PlatformRejectionException; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Polygonal2DRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionSelector; -import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -40,7 +40,6 @@ import java.io.*; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.logging.Handler; import java.util.zip.ZipEntry; /** @@ -104,16 +103,22 @@ public class WorldEditPlugin extends JavaPlugin { // Setup interfaces server = new BukkitServerInterface(this, getServer()); - controller = new WorldEdit(server, config); - WorldEdit.logger.setParent(Bukkit.getLogger()); - api = new WorldEditAPI(this); - getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this)); - getServer().getMessenger().registerOutgoingPluginChannel(this, CUI_PLUGIN_CHANNEL); - // Now we can register events! - getServer().getPluginManager().registerEvents(new WorldEditListener(this), this); + controller = WorldEdit.getInstance(); + try { + controller.getPlatformManager().register(server); + api = new WorldEditAPI(this); + getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this)); + getServer().getMessenger().registerOutgoingPluginChannel(this, CUI_PLUGIN_CHANNEL); + // Now we can register events! + getServer().getPluginManager().registerEvents(new WorldEditListener(this), this); - getServer().getScheduler().runTaskTimerAsynchronously(this, - new SessionTimer(controller, getServer()), 120, 120); + getServer().getScheduler().runTaskTimerAsynchronously(this, + new SessionTimer(controller, getServer()), 120, 120); + } catch (PlatformRejectionException e) { + throw new RuntimeException( + "WorldEdit rejected the Bukkit implementation of WorldEdit! This is strange and should " + + "not have happened. Please report this error.", e); + } } private void copyNmsBlockClasses(File target) { @@ -149,9 +154,7 @@ public class WorldEditPlugin extends JavaPlugin { @Override public void onDisable() { controller.clearSessions(); - for (Handler h : controller.commandLogger.getHandlers()) { - h.close(); - } + controller.getPlatformManager().unregister(server); config.unload(); server.unregisterCommands(); this.getServer().getScheduler().cancelTasks(this); diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeServerInterface.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeServerInterface.java index 0bee6be0d..b5a62f1af 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeServerInterface.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeServerInterface.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import com.sk89q.worldedit.LocalConfiguration; import net.minecraft.command.CommandBase; import net.minecraft.command.ICommandSender; import net.minecraft.command.ServerCommandManager; @@ -40,10 +41,12 @@ import com.sk89q.worldedit.ServerInterface; import cpw.mods.fml.common.FMLCommonHandler; public class ForgeServerInterface extends ServerInterface { - private MinecraftServer server; - private ForgeBiomeTypes biomes; + private final WorldEditMod mod; + private final MinecraftServer server; + private final ForgeBiomeTypes biomes; - public ForgeServerInterface() { + public ForgeServerInterface(WorldEditMod mod) { + this.mod = mod; this.server = FMLCommonHandler.instance().getMinecraftServerInstance(); this.biomes = new ForgeBiomeTypes(); } @@ -114,4 +117,9 @@ public class ForgeServerInterface extends ServerInterface { }); } } + + @Override + public LocalConfiguration getConfiguration() { + return mod.getConfig(); + } } diff --git a/src/forge/java/com/sk89q/worldedit/forge/WorldEditMod.java b/src/forge/java/com/sk89q/worldedit/forge/WorldEditMod.java index 753ee1584..37b446050 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/WorldEditMod.java +++ b/src/forge/java/com/sk89q/worldedit/forge/WorldEditMod.java @@ -28,6 +28,7 @@ import java.util.jar.JarFile; import java.util.logging.Logger; import java.util.zip.ZipEntry; +import com.sk89q.worldedit.extension.platform.PlatformRejectionException; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; @@ -92,8 +93,13 @@ public class WorldEditMod { @EventHandler public void serverStarting(FMLServerStartingEvent event) { - this.server = new ForgeServerInterface(); - this.controller = new WorldEdit(this.server, this.config); + this.server = new ForgeServerInterface(this); + this.controller = WorldEdit.getInstance(); + try { + controller.getPlatformManager().register(server); + } catch (PlatformRejectionException e) { + throw new RuntimeException("Failed to register with WorldEdit", e); + } } public ForgeConfiguration getConfig() { diff --git a/src/main/java/com/sk89q/worldedit/ServerInterface.java b/src/main/java/com/sk89q/worldedit/ServerInterface.java index 3d40e8f40..bc6d4610b 100644 --- a/src/main/java/com/sk89q/worldedit/ServerInterface.java +++ b/src/main/java/com/sk89q/worldedit/ServerInterface.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandsManager; +import com.sk89q.worldedit.extension.platform.Platform; import java.util.Collections; import java.util.List; @@ -29,58 +30,27 @@ import java.util.List; * * @author sk89q */ -public abstract class ServerInterface { - /** - * Resolves an item name to its ID. - * - * @param name The name to look up - * @return The id that corresponds to the name, or -1 if no such ID exists - */ - public abstract int resolveItem(String name); +public abstract class ServerInterface implements Platform { - /** - * Checks if a mob type is valid. - * - * @param type The mob type name to check - * @return Whether the name is a valid mod bype - */ - public abstract boolean isValidMobType(String type); - - /** - * Reload WorldEdit configuration. - */ - public abstract void reload(); - - /** - * Returns all available biomes. - * - * @return - */ - public abstract BiomeTypes getBiomes(); - - /** - * Schedules the given task to be invoked once every period ticks - * after an initial delay of delay ticks. - * - * @param delay Delay in server ticks before executing first repeat - * @param period Period in server ticks of the task - * @param task Task to be executed - * @return Task id number (-1 if scheduling failed) - */ + @Override public int schedule(long delay, long period, Runnable task) { return -1; } + @Override public List getWorlds() { return Collections.emptyList(); } + @Override @Deprecated public void onCommandRegistration(List commands) { // Do nothing :) } + @Override public void onCommandRegistration(List commands, CommandsManager manager) { onCommandRegistration(commands); } + } diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index 0240ad6a5..db7fe81ac 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -19,17 +19,16 @@ package com.sk89q.worldedit; -import com.sk89q.minecraft.util.commands.*; -import com.sk89q.minecraft.util.commands.Console; -import com.sk89q.util.StringUtil; +import com.sk89q.minecraft.util.commands.CommandsManager; import com.sk89q.worldedit.CuboidClipboard.FlipDirection; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockType; -import com.sk89q.worldedit.blocks.ItemType; -import com.sk89q.worldedit.command.*; import com.sk89q.worldedit.command.tool.*; import com.sk89q.worldedit.event.extent.EditSessionEvent; +import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.extension.platform.PlatformManager; import com.sk89q.worldedit.extension.registry.BlockRegistry; import com.sk89q.worldedit.extension.registry.MaskRegistry; import com.sk89q.worldedit.extension.registry.PatternRegistry; @@ -44,35 +43,41 @@ import com.sk89q.worldedit.scripting.CraftScriptEngine; import com.sk89q.worldedit.scripting.RhinoCraftScriptEngine; import com.sk89q.worldedit.session.SessionManager; import com.sk89q.worldedit.session.request.Request; -import com.sk89q.worldedit.util.LogFormat; import com.sk89q.worldedit.util.eventbus.EventBus; +import com.sk89q.worldedit.util.logging.WorldEditPrefixHandler; import javax.script.ScriptException; import java.io.*; -import java.lang.reflect.Method; -import java.util.*; -import java.util.logging.FileHandler; -import java.util.logging.Level; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.logging.Logger; -import java.util.regex.Matcher; import static com.google.common.base.Preconditions.checkNotNull; /** - * The current instance of WorldEdit. + * The entry point and container for a working implementation of WorldEdit. + *

+ * An instance handles event handling; block, mask, pattern, etc. registration; + * the management of sessions; the creation of {@link EditSession}s; and more. + * In order to use WorldEdit, at least one {@link Platform} must be registered + * with WorldEdit using {@link PlatformManager#register(Platform)} on the + * manager retrieved using {@link WorldEdit#getPlatformManager()}. + *

+ * An instance of WorldEdit can be retrieved using the static + * method {@link WorldEdit#getInstance()}, which is shared among all + * platforms within the same classloader hierarchy. */ public class WorldEdit { - public static final Logger logger = Logger.getLogger("Minecraft.WorldEdit"); - public final Logger commandLogger = Logger.getLogger("Minecraft.WorldEdit.CommandLogger"); + public static final Logger logger = Logger.getLogger(WorldEdit.class.getCanonicalName()); - private static WorldEdit instance; + private final static WorldEdit instance = new WorldEdit(); private static String version; - private final ServerInterface server; - private final LocalConfiguration config; - private final CommandsManager commands; private final EventBus eventBus = new EventBus(); + private final PlatformManager platformManager = new PlatformManager(this); private final EditSessionFactory editSessionFactory = new EditSessionFactory.EditSessionFactoryImpl(eventBus); private final SessionManager sessions = new SessionManager(this); @@ -81,139 +86,41 @@ public class WorldEdit { private final PatternRegistry patternRegistry = new PatternRegistry(this); static { + WorldEditPrefixHandler.register("com.sk89q.worldedit"); getVersion(); } + private WorldEdit() { + } + /** - * Construct an instance of WorldEdit. + * Gets the current instance of this class. + *

+ * An instance will always be available, but no platform may yet be + * registered with WorldEdit, meaning that a number of operations + * may fail. However, event handlers can be registered. * - * @param server - * @param config - */ - public WorldEdit(ServerInterface server, final LocalConfiguration config) { - instance = this; - this.server = server; - this.config = config; - - if (!config.logFile.equals("")) { - try { - FileHandler logFileHandler; - logFileHandler = new FileHandler(new File(config.getWorkingDirectory(), - config.logFile).getAbsolutePath(), true); - logFileHandler.setFormatter(new LogFormat()); - commandLogger.addHandler(logFileHandler); - } catch (IOException e) { - logger.log(Level.WARNING, "Could not use command log file " + config.logFile + ": " - + e.getMessage()); - } - } - - commands = new CommandsManager() { - @Override - protected void checkPermission(LocalPlayer player, Method method) throws CommandException { - if (!player.isPlayer() && !method.isAnnotationPresent(Console.class)) { - throw new UnhandledCommandException(); - } - - super.checkPermission(player, method); - } - - @Override - public boolean hasPermission(LocalPlayer player, String perm) { - return player.hasPermission(perm); - } - - @Override - public void invokeMethod(Method parent, String[] args, - LocalPlayer player, Method method, Object instance, - Object[] methodArgs, int level) throws CommandException { - if (config.logCommands) { - final Logging loggingAnnotation = method.getAnnotation(Logging.class); - - final Logging.LogMode logMode; - if (loggingAnnotation == null) { - logMode = null; - } else { - logMode = loggingAnnotation.value(); - } - - String msg = "WorldEdit: " + player.getName(); - if (player.isPlayer()) { - msg += " (in \"" + player.getWorld().getName() + "\")"; - } - msg += ": " + StringUtil.joinString(args, " "); - if (logMode != null && player.isPlayer()) { - Vector position = player.getPosition(); - final LocalSession session = getSession(player); - switch (logMode) { - case PLACEMENT: - try { - position = session.getPlacementPosition(player); - } catch (IncompleteRegionException e) { - break; - } - /* FALL-THROUGH */ - - case POSITION: - msg += " - Position: " + position; - break; - - case ALL: - msg += " - Position: " + position; - /* FALL-THROUGH */ - - case ORIENTATION_REGION: - msg += " - Orientation: " + player.getCardinalDirection().name(); - /* FALL-THROUGH */ - - case REGION: - try { - msg += " - Region: " + session.getSelection(player.getWorld()); - } catch (IncompleteRegionException e) { - break; - } - break; - } - } - commandLogger.info(msg); - } - super.invokeMethod(parent, args, player, method, instance, methodArgs, level); - } - }; - - commands.setInjector(new SimpleInjector(this)); - - reg(BiomeCommands.class); - reg(ChunkCommands.class); - reg(ClipboardCommands.class); - reg(GeneralCommands.class); - reg(GenerationCommands.class); - reg(HistoryCommands.class); - reg(NavigationCommands.class); - reg(RegionCommands.class); - reg(ScriptingCommands.class); - reg(SelectionCommands.class); - reg(SnapshotUtilCommands.class); - reg(ToolUtilCommands.class); - reg(ToolCommands.class); - reg(UtilityCommands.class); - } - - private void reg(Class clazz) { - server.onCommandRegistration(commands.registerAndReturn(clazz), commands); - } - - /** - * Gets the current instance of this class - * - * @return + * @return an instance of WorldEdit. */ public static WorldEdit getInstance() { return instance; } + /** + * Get the platform manager, where platforms (that implement WorldEdit) + * can be registered and information about registered platforms can + * be queried. + * + * @return the platform manager + */ + public PlatformManager getPlatformManager() { + return platformManager; + } + /** * Get the event bus for WorldEdit. + *

+ * Event handlers can be registered on the event bus. * * @return the event bus */ @@ -487,7 +394,7 @@ public class WorldEdit { String filePath = f.getCanonicalPath(); String dirPath = dir.getCanonicalPath(); - if (!filePath.substring(0, dirPath.length()).equals(dirPath) && !config.allowSymlinks) { + if (!filePath.substring(0, dirPath.length()).equals(dirPath) && !getConfiguration().allowSymlinks) { throw new FilenameResolutionException(filename, "Path is outside allowable root"); } @@ -500,27 +407,27 @@ public class WorldEdit { } public int getMaximumPolygonalPoints(LocalPlayer player) { - if (player.hasPermission("worldedit.limit.unrestricted") || config.maxPolygonalPoints < 0) { - return config.defaultMaxPolygonalPoints; + if (player.hasPermission("worldedit.limit.unrestricted") || getConfiguration().maxPolygonalPoints < 0) { + return getConfiguration().defaultMaxPolygonalPoints; } - if (config.defaultMaxPolygonalPoints < 0) { - return config.maxPolygonalPoints; + if (getConfiguration().defaultMaxPolygonalPoints < 0) { + return getConfiguration().maxPolygonalPoints; } - return Math.min(config.defaultMaxPolygonalPoints, config.maxPolygonalPoints); + return Math.min(getConfiguration().defaultMaxPolygonalPoints, getConfiguration().maxPolygonalPoints); } public int getMaximumPolyhedronPoints(LocalPlayer player) { - if (player.hasPermission("worldedit.limit.unrestricted") || config.maxPolyhedronPoints < 0) { - return config.defaultMaxPolyhedronPoints; + if (player.hasPermission("worldedit.limit.unrestricted") || getConfiguration().maxPolyhedronPoints < 0) { + return getConfiguration().defaultMaxPolyhedronPoints; } - if (config.defaultMaxPolyhedronPoints < 0) { - return config.maxPolyhedronPoints; + if (getConfiguration().defaultMaxPolyhedronPoints < 0) { + return getConfiguration().maxPolyhedronPoints; } - return Math.min(config.defaultMaxPolyhedronPoints, config.maxPolyhedronPoints); + return Math.min(getConfiguration().defaultMaxPolyhedronPoints, getConfiguration().maxPolyhedronPoints); } /** @@ -530,7 +437,7 @@ public class WorldEdit { * @throws MaxRadiusException */ public void checkMaxRadius(double radius) throws MaxRadiusException { - if (config.maxRadius > 0 && radius > config.maxRadius) { + if (getConfiguration().maxRadius > 0 && radius > getConfiguration().maxRadius) { throw new MaxRadiusException(); } } @@ -542,7 +449,7 @@ public class WorldEdit { * @throws MaxBrushRadiusException */ public void checkMaxBrushRadius(double radius) throws MaxBrushRadiusException { - if (config.maxBrushRadius > 0 && radius > config.maxBrushRadius) { + if (getConfiguration().maxBrushRadius > 0 && radius > getConfiguration().maxBrushRadius) { throw new MaxBrushRadiusException(); } } @@ -560,7 +467,7 @@ public class WorldEdit { return f; } - return new File(config.getWorkingDirectory(), path); + return new File(getConfiguration().getWorkingDirectory(), path); } /** @@ -761,14 +668,14 @@ public class WorldEdit { * @return the commands */ public Map getCommands() { - return commands.getCommands(); + return getCommandsManager().getCommands(); } /** * @return the commands */ public CommandsManager getCommandsManager() { - return commands; + return getPlatformManager().getCommandManager().getCommands(); } /** @@ -813,8 +720,8 @@ public class WorldEdit { * @return */ public boolean handleArmSwing(LocalPlayer player) { - if (player.getItemInHand() == config.navigationWand) { - if (config.navigationWandMaxDistance <= 0) { + if (player.getItemInHand() == getConfiguration().navigationWand) { + if (getConfiguration().navigationWandMaxDistance <= 0) { return false; } @@ -822,7 +729,7 @@ public class WorldEdit { return false; } - WorldVector pos = player.getSolidBlockTrace(config.navigationWandMaxDistance); + WorldVector pos = player.getSolidBlockTrace(getConfiguration().navigationWandMaxDistance); if (pos != null) { player.findFreePosition(pos); } else { @@ -836,7 +743,7 @@ public class WorldEdit { Tool tool = session.getTool(player.getItemInHand()); if (tool != null && tool instanceof DoubleActionTraceTool) { if (tool.canUse(player)) { - ((DoubleActionTraceTool) tool).actSecondary(server, config, player, session); + ((DoubleActionTraceTool) tool).actSecondary(getServer(), getConfiguration(), player, session); return true; } } @@ -851,8 +758,8 @@ public class WorldEdit { * @return */ public boolean handleRightClick(LocalPlayer player) { - if (player.getItemInHand() == config.navigationWand) { - if (config.navigationWandMaxDistance <= 0) { + if (player.getItemInHand() == getConfiguration().navigationWand) { + if (getConfiguration().navigationWandMaxDistance <= 0) { return false; } @@ -872,7 +779,7 @@ public class WorldEdit { Tool tool = session.getTool(player.getItemInHand()); if (tool != null && tool instanceof TraceTool) { if (tool.canUse(player)) { - ((TraceTool) tool).actPrimary(server, config, player, session); + ((TraceTool) tool).actPrimary(getServer(), getConfiguration(), player, session); return true; } } @@ -890,7 +797,7 @@ public class WorldEdit { public boolean handleBlockRightClick(LocalPlayer player, WorldVector clicked) { LocalSession session = getSession(player); - if (player.getItemInHand() == config.wandItem) { + if (player.getItemInHand() == getConfiguration().wandItem) { if (!session.isToolControlEnabled()) { return false; } @@ -910,7 +817,7 @@ public class WorldEdit { Tool tool = session.getTool(player.getItemInHand()); if (tool != null && tool instanceof BlockTool) { if (tool.canUse(player)) { - ((BlockTool) tool).actPrimary(server, config, player, session, clicked); + ((BlockTool) tool).actPrimary(getServer(), getConfiguration(), player, session, clicked); return true; } } @@ -928,7 +835,7 @@ public class WorldEdit { public boolean handleBlockLeftClick(LocalPlayer player, WorldVector clicked) { LocalSession session = getSession(player); - if (player.getItemInHand() == config.wandItem) { + if (player.getItemInHand() == getConfiguration().wandItem) { if (!session.isToolControlEnabled()) { return false; } @@ -948,14 +855,14 @@ public class WorldEdit { if (player.isHoldingPickAxe() && session.hasSuperPickAxe()) { final BlockTool superPickaxe = session.getSuperPickaxe(); if (superPickaxe != null && superPickaxe.canUse(player)) { - return superPickaxe.actPrimary(server, config, player, session, clicked); + return superPickaxe.actPrimary(getServer(), getConfiguration(), player, session, clicked); } } Tool tool = session.getTool(player.getItemInHand()); if (tool != null && tool instanceof DoubleActionBlockTool) { if (tool.canUse(player)) { - ((DoubleActionBlockTool) tool).actSecondary(server, config, player, session, clicked); + ((DoubleActionBlockTool) tool).actSecondary(getServer(), getConfiguration(), player, session, clicked); return true; } } @@ -963,8 +870,6 @@ public class WorldEdit { return false; } - private static final java.util.regex.Pattern numberFormatExceptionPattern = java.util.regex.Pattern.compile("^For input string: \"(.*)\"$"); - /** * * @param player @@ -972,135 +877,13 @@ public class WorldEdit { * @return whether the command was processed */ public boolean handleCommand(LocalPlayer player, String[] split) { - Request.reset(); - - try { - split = commandDetection(split); - - // No command found! - if (!commands.hasCommand(split[0])) { - return false; - } - - LocalSession session = getSession(player); - EditSession editSession = session.createEditSession(player); - editSession.enableQueue(); - - session.tellVersion(player); - - long start = System.currentTimeMillis(); - - try { - commands.execute(split, player, session, player, editSession); - } catch (CommandPermissionsException e) { - player.printError("You don't have permission to do this."); - } catch (MissingNestedCommandException e) { - player.printError(e.getUsage()); - } catch (CommandUsageException e) { - player.printError(e.getMessage()); - player.printError(e.getUsage()); - } catch (PlayerNeededException e) { - player.printError(e.getMessage()); - } catch (WrappedCommandException e) { - throw e.getCause(); - } catch (UnhandledCommandException e) { - player.printError("Command could not be handled; invalid sender!"); - return false; - } finally { - session.remember(editSession); - editSession.flushQueue(); - - if (config.profile) { - long time = System.currentTimeMillis() - start; - int changed = editSession.getBlockChangeCount(); - if (time > 0) { - double throughput = changed / (time / 1000.0); - player.printDebug((time / 1000.0) + "s elapsed (history: " - + changed + " changed; " - + Math.round(throughput) + " blocks/sec)."); - } else { - player.printDebug((time / 1000.0) + "s elapsed."); - } - } - - flushBlockBag(player, editSession); - } - } catch (NumberFormatException e) { - final Matcher matcher = numberFormatExceptionPattern.matcher(e.getMessage()); - - if (matcher.matches()) { - player.printError("Number expected; string \"" + matcher.group(1) + "\" given."); - } else { - player.printError("Number expected; string given."); - } - } catch (IncompleteRegionException e) { - player.printError("Make a region selection first."); - } catch (UnknownItemException e) { - player.printError("Block name '" + e.getID() + "' was not recognized."); - } catch (InvalidItemException e) { - player.printError(e.getMessage()); - } catch (DisallowedItemException e) { - player.printError("Block '" + e.getID() + "' not allowed (see WorldEdit configuration)."); - } catch (MaxChangedBlocksException e) { - player.printError("Max blocks changed in an operation reached (" - + e.getBlockLimit() + ")."); - } catch (MaxBrushRadiusException e) { - player.printError("Maximum allowed brush size: " + config.maxBrushRadius); - } catch (MaxRadiusException e) { - player.printError("Maximum allowed size: " + config.maxRadius); - } catch (UnknownDirectionException e) { - player.printError("Unknown direction: " + e.getDirection()); - } catch (InsufficientArgumentsException e) { - player.printError(e.getMessage()); - } catch (EmptyClipboardException e) { - player.printError("Your clipboard is empty. Use //copy first."); - } catch (InvalidFilenameException e) { - player.printError("Filename '" + e.getFilename() + "' invalid: " - + e.getMessage()); - } catch (FilenameResolutionException e) { - player.printError("File '" + e.getFilename() + "' resolution error: " - + e.getMessage()); - } catch (InvalidToolBindException e) { - player.printError("Can't bind tool to " - + ItemType.toHeldName(e.getItemId()) + ": " + e.getMessage()); - } catch (FileSelectionAbortedException e) { - player.printError("File selection aborted."); - } catch (WorldEditException e) { - player.printError(e.getMessage()); - } catch (Throwable excp) { - player.printError("Please report this error: [See console]"); - player.printRaw(excp.getClass().getName() + ": " + excp.getMessage()); - excp.printStackTrace(); - } - - return true; + CommandEvent event = new CommandEvent(player, split); + getEventBus().post(event); + return event.isCancelled(); } public String[] commandDetection(String[] split) { - Request.reset(); - - split[0] = split[0].substring(1); - - // Quick script shortcut - if (split[0].matches("^[^/].*\\.js$")) { - String[] newSplit = new String[split.length + 1]; - System.arraycopy(split, 0, newSplit, 1, split.length); - newSplit[0] = "cs"; - newSplit[1] = newSplit[1]; - split = newSplit; - } - - String searchCmd = split[0].toLowerCase(); - - // Try to detect the command - if (commands.hasCommand(searchCmd)) { - } else if (config.noDoubleSlash && commands.hasCommand("/" + searchCmd)) { - split[0] = "/" + split[0]; - } else if (split[0].length() >= 2 && split[0].charAt(0) == '/' - && commands.hasCommand(searchCmd.substring(1))) { - split[0] = split[0].substring(1); - } - return split; + return getPlatformManager().getCommandManager().commandDetection(split); } /** @@ -1152,7 +935,7 @@ public class WorldEdit { LocalSession session = getSession(player); CraftScriptContext scriptContext = - new CraftScriptContext(this, server, config, session, player, args); + new CraftScriptContext(this, getServer(), getConfiguration(), session, player, args); CraftScriptEngine engine = null; @@ -1164,7 +947,7 @@ public class WorldEdit { return; } - engine.setTimeLimit(config.scriptTimeout); + engine.setTimeLimit(getConfiguration().scriptTimeout); Map vars = new HashMap(); vars.put("argv", args); @@ -1196,19 +979,19 @@ public class WorldEdit { /** * Get Worldedit's configuration. * - * @return + * @return a configuration */ public LocalConfiguration getConfiguration() { - return config; + return getPlatformManager().getConfiguration(); } /** * Get the server interface. * - * @return + * @return the server interface */ public ServerInterface getServer() { - return server; + return getPlatformManager().getServerInterface(); } /** diff --git a/src/main/java/com/sk89q/worldedit/event/AbstractCancellable.java b/src/main/java/com/sk89q/worldedit/event/AbstractCancellable.java new file mode 100644 index 000000000..673b251c5 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/AbstractCancellable.java @@ -0,0 +1,39 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.event; + +/** + * An abstract implementation of {@link Cancellable} that has all + * of {@link Cancellable}'s methods implemented. + */ +public abstract class AbstractCancellable implements Cancellable { + + private boolean cancelled; + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } +} diff --git a/src/main/java/com/sk89q/worldedit/event/Cancellable.java b/src/main/java/com/sk89q/worldedit/event/Cancellable.java new file mode 100644 index 000000000..35f142e7e --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/Cancellable.java @@ -0,0 +1,42 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.event; + +/** + * Marks an event that has a cancellable state. The meaning of cancellation + * depends on the event. + */ +public interface Cancellable { + + /** + * Returns whether the event has been cancelled. + * + * @return true if cancelled + */ + boolean isCancelled(); + + /** + * Set whether the event has been cancelled. + * + * @param cancelled true if cancelled + */ + void setCancelled(boolean cancelled); + +} diff --git a/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java b/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java new file mode 100644 index 000000000..0cba50b7e --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java @@ -0,0 +1,67 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.event.platform; + +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.event.AbstractCancellable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * This class is currently only for internal use. Do not post or catch this event. + */ +public class CommandEvent extends AbstractCancellable { + + private final LocalPlayer player; + private final String[] args; + + /** + * Create a new instance. + * + * @param player the player + * @param args the arguments + */ + public CommandEvent(LocalPlayer player, String[] args) { + checkNotNull(player); + checkNotNull(args); + + this.player = player; + this.args = args; + } + + /** + * Get the player. + * + * @return the player + */ + public LocalPlayer getPlayer() { + return player; + } + + /** + * Get the arguments. + * + * @return the arguments + */ + public String[] getArguments() { + return args; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java new file mode 100644 index 000000000..6eb9f8994 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -0,0 +1,343 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.platform; + +import com.sk89q.minecraft.util.commands.*; +import com.sk89q.util.StringUtil; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.blocks.ItemType; +import com.sk89q.worldedit.command.*; +import com.sk89q.worldedit.event.platform.CommandEvent; +import com.sk89q.worldedit.session.request.Request; +import com.sk89q.worldedit.util.logging.LogFormat; +import com.sk89q.worldedit.util.eventbus.Subscribe; +import com.sk89q.worldedit.util.logging.DynamicStreamHandler; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.logging.*; +import java.util.regex.Matcher; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Handles the registration and invocation of commands. + *

+ * This class is primarily for internal usage. + */ +public final class CommandManager { + + private static final Logger logger = Logger.getLogger(CommandManager.class.getCanonicalName()); + private static final java.util.regex.Pattern numberFormatExceptionPattern = java.util.regex.Pattern.compile("^For input string: \"(.*)\"$"); + + private final WorldEdit worldEdit; + private final CommandsManager commands; + private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler(); + + /** + * Create a new instance. + * + * @param worldEdit the WorldEdit instance + */ + CommandManager(final WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; + + // Register this instance for command events + worldEdit.getEventBus().register(this); + + // Setup the logger + logger.addHandler(dynamicHandler); + dynamicHandler.setFormatter(new LogFormat()); + + // Set up the commands manager + commands = new CommandsManagerImpl(); + commands.setInjector(new SimpleInjector(worldEdit)); + } + + void register(Platform platform) { + logger.log(Level.FINE, "Registering commands with " + platform.getClass().getCanonicalName()); + + LocalConfiguration config = platform.getConfiguration(); + boolean logging = config.logCommands; + String path = config.logFile; + + // Register log + if (!logging || path.isEmpty()) { + dynamicHandler.setHandler(null); + } else { + File file = new File(config.getWorkingDirectory(), path); + + logger.log(Level.INFO, "Logging WorldEdit commands to " + file.getAbsolutePath()); + + try { + dynamicHandler.setHandler(new FileHandler(file.getAbsolutePath(), true)); + } catch (IOException e) { + logger.log(Level.WARNING, "Could not use command log file " + path + ": " + e.getMessage()); + } + } + + register(platform, BiomeCommands.class); + register(platform, ChunkCommands.class); + register(platform, ClipboardCommands.class); + register(platform, GeneralCommands.class); + register(platform, GenerationCommands.class); + register(platform, HistoryCommands.class); + register(platform, NavigationCommands.class); + register(platform, RegionCommands.class); + register(platform, ScriptingCommands.class); + register(platform, SelectionCommands.class); + register(platform, SnapshotUtilCommands.class); + register(platform, ToolUtilCommands.class); + register(platform, ToolCommands.class); + register(platform, UtilityCommands.class); + } + + void unregister() { + dynamicHandler.setHandler(null); + } + + private void register(Platform platform, Class clazz) { + platform.onCommandRegistration(commands.registerAndReturn(clazz), commands); + } + + public CommandsManager getCommands() { + return commands; + } + + public String[] commandDetection(String[] split) { + Request.reset(); + + split[0] = split[0].substring(1); + + // Quick script shortcut + if (split[0].matches("^[^/].*\\.js$")) { + String[] newSplit = new String[split.length + 1]; + System.arraycopy(split, 0, newSplit, 1, split.length); + newSplit[0] = "cs"; + newSplit[1] = newSplit[1]; + split = newSplit; + } + + String searchCmd = split[0].toLowerCase(); + + // Try to detect the command + if (commands.hasCommand(searchCmd)) { + } else if (worldEdit.getConfiguration().noDoubleSlash && commands.hasCommand("/" + searchCmd)) { + split[0] = "/" + split[0]; + } else if (split[0].length() >= 2 && split[0].charAt(0) == '/' + && commands.hasCommand(searchCmd.substring(1))) { + split[0] = split[0].substring(1); + } + + return split; + } + + @Subscribe + public void handleCommand(CommandEvent event) { + Request.reset(); + + LocalPlayer player = event.getPlayer(); + String[] split = event.getArguments(); + + try { + split = commandDetection(split); + + // No command found! + if (!commands.hasCommand(split[0])) { + return; + } + + LocalSession session = worldEdit.getSession(player); + EditSession editSession = session.createEditSession(player); + editSession.enableQueue(); + + session.tellVersion(player); + + long start = System.currentTimeMillis(); + + try { + commands.execute(split, player, session, player, editSession); + } catch (CommandPermissionsException e) { + player.printError("You don't have permission to do this."); + } catch (MissingNestedCommandException e) { + player.printError(e.getUsage()); + } catch (CommandUsageException e) { + player.printError(e.getMessage()); + player.printError(e.getUsage()); + } catch (PlayerNeededException e) { + player.printError(e.getMessage()); + } catch (WrappedCommandException e) { + throw e.getCause(); + } catch (UnhandledCommandException e) { + player.printError("Command could not be handled; invalid sender!"); + event.setCancelled(true); + return; + } finally { + session.remember(editSession); + editSession.flushQueue(); + + if (worldEdit.getConfiguration().profile) { + long time = System.currentTimeMillis() - start; + int changed = editSession.getBlockChangeCount(); + if (time > 0) { + double throughput = changed / (time / 1000.0); + player.printDebug((time / 1000.0) + "s elapsed (history: " + + changed + " changed; " + + Math.round(throughput) + " blocks/sec)."); + } else { + player.printDebug((time / 1000.0) + "s elapsed."); + } + } + + worldEdit.flushBlockBag(player, editSession); + } + } catch (NumberFormatException e) { + final Matcher matcher = numberFormatExceptionPattern.matcher(e.getMessage()); + + if (matcher.matches()) { + player.printError("Number expected; string \"" + matcher.group(1) + "\" given."); + } else { + player.printError("Number expected; string given."); + } + } catch (IncompleteRegionException e) { + player.printError("Make a region selection first."); + } catch (UnknownItemException e) { + player.printError("Block name '" + e.getID() + "' was not recognized."); + } catch (InvalidItemException e) { + player.printError(e.getMessage()); + } catch (DisallowedItemException e) { + player.printError("Block '" + e.getID() + "' not allowed (see WorldEdit configuration)."); + } catch (MaxChangedBlocksException e) { + player.printError("Max blocks changed in an operation reached (" + + e.getBlockLimit() + ")."); + } catch (MaxBrushRadiusException e) { + player.printError("Maximum allowed brush size: " + worldEdit.getConfiguration().maxBrushRadius); + } catch (MaxRadiusException e) { + player.printError("Maximum allowed size: " + worldEdit.getConfiguration().maxRadius); + } catch (UnknownDirectionException e) { + player.printError("Unknown direction: " + e.getDirection()); + } catch (InsufficientArgumentsException e) { + player.printError(e.getMessage()); + } catch (EmptyClipboardException e) { + player.printError("Your clipboard is empty. Use //copy first."); + } catch (InvalidFilenameException e) { + player.printError("Filename '" + e.getFilename() + "' invalid: " + + e.getMessage()); + } catch (FilenameResolutionException e) { + player.printError("File '" + e.getFilename() + "' resolution error: " + + e.getMessage()); + } catch (InvalidToolBindException e) { + player.printError("Can't bind tool to " + + ItemType.toHeldName(e.getItemId()) + ": " + e.getMessage()); + } catch (FileSelectionAbortedException e) { + player.printError("File selection aborted."); + } catch (WorldEditException e) { + player.printError(e.getMessage()); + } catch (Throwable excp) { + player.printError("Please report this error: [See console]"); + player.printRaw(excp.getClass().getName() + ": " + excp.getMessage()); + excp.printStackTrace(); + } + + event.setCancelled(true); + } + + private class CommandsManagerImpl extends CommandsManager { + @Override + protected void checkPermission(LocalPlayer player, Method method) throws CommandException { + if (!player.isPlayer() && !method.isAnnotationPresent(Console.class)) { + throw new UnhandledCommandException(); + } + + super.checkPermission(player, method); + } + + @Override + public boolean hasPermission(LocalPlayer player, String perm) { + return player.hasPermission(perm); + } + + @Override + public void invokeMethod(Method parent, String[] args, + LocalPlayer player, Method method, Object instance, + Object[] methodArgs, int level) throws CommandException { + if (worldEdit.getConfiguration().logCommands) { + final Logging loggingAnnotation = method.getAnnotation(Logging.class); + + final Logging.LogMode logMode; + if (loggingAnnotation == null) { + logMode = null; + } else { + logMode = loggingAnnotation.value(); + } + + String msg = "WorldEdit: " + player.getName(); + if (player.isPlayer()) { + msg += " (in \"" + player.getWorld().getName() + "\")"; + } + msg += ": " + StringUtil.joinString(args, " "); + if (logMode != null && player.isPlayer()) { + Vector position = player.getPosition(); + final LocalSession session = worldEdit.getSessionManager().get(player); + + switch (logMode) { + case PLACEMENT: + try { + position = session.getPlacementPosition(player); + } catch (IncompleteRegionException e) { + break; + } + /* FALL-THROUGH */ + + case POSITION: + msg += " - Position: " + position; + break; + + case ALL: + msg += " - Position: " + position; + /* FALL-THROUGH */ + + case ORIENTATION_REGION: + msg += " - Orientation: " + player.getCardinalDirection().name(); + /* FALL-THROUGH */ + + case REGION: + try { + msg += " - Region: " + session.getSelection(player.getWorld()); + } catch (IncompleteRegionException e) { + break; + } + break; + } + } + + getLogger().info(msg); + } + super.invokeMethod(parent, args, player, method, instance, methodArgs, level); + } + } + + public static Logger getLogger() { + return logger; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java b/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java new file mode 100644 index 000000000..6e4f65945 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -0,0 +1,89 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.platform; + +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandsManager; +import com.sk89q.worldedit.BiomeTypes; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.LocalWorld; + +import java.util.List; + +/** + * Represents a platform that WorldEdit has been implemented for. + */ +public interface Platform { + + /** + * Resolves an item name to its ID. + * + * @param name The name to look up + * @return The id that corresponds to the name, or -1 if no such ID exists + */ + int resolveItem(String name); + + /** + * Checks if a mob type is valid. + * + * @param type The mob type name to check + * @return Whether the name is a valid mod bype + */ + boolean isValidMobType(String type); + + /** + * Reload WorldEdit configuration. + */ + void reload(); + + /** + * Returns all available biomes. + * + * @return an object containing all the biomes + */ + BiomeTypes getBiomes(); + + /** + * Schedules the given task to be invoked once every period ticks + * after an initial delay of delay ticks. + * + * @param delay Delay in server ticks before executing first repeat + * @param period Period in server ticks of the task + * @param task Task to be executed + * @return Task id number (-1 if scheduling failed) + */ + int schedule(long delay, long period, Runnable task); + + List getWorlds(); + + @Deprecated + void onCommandRegistration(List commands); + + void onCommandRegistration(List commands, CommandsManager manager); + + /** + * Get the configuration from this platform. + * + * @return the configuration + */ + LocalConfiguration getConfiguration(); + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java new file mode 100644 index 000000000..eb246fd05 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -0,0 +1,162 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.platform; + +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.ServerInterface; +import com.sk89q.worldedit.WorldEdit; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Manages registered {@link Platform}s for WorldEdit. Platforms are + * implementations of WorldEdit. + *

+ * This class is thread-safe. + */ +public class PlatformManager { + + private static final Logger logger = Logger.getLogger(PlatformManager.class.getCanonicalName()); + + private final LocalConfiguration defaultConfig = new DefaultConfiguration(); + private final List platforms = new ArrayList(); + private final CommandManager commandManager; + private @Nullable Platform primary = null; + + /** + * Create a new platform manager. + * + * @param worldEdit the WorldEdit instance + */ + public PlatformManager(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.commandManager = new CommandManager(worldEdit); + } + + /** + * Register a platform with WorldEdit. + * + * @param platform the platform + * @throws PlatformRejectionException thrown if the registration is rejected + */ + public synchronized void register(Platform platform) throws PlatformRejectionException { + checkNotNull(platform); + logger.log(Level.FINE, "Got request to register " + platform.getClass().getCanonicalName() + " with WorldEdit"); + platforms.add(platform); + if (this.primary == null) { + commandManager.register(platform); + this.primary = platform; + } + } + + /** + * Unregister a platform from WorldEdit. + * + * @param platform the platform + */ + public synchronized boolean unregister(Platform platform) { + checkNotNull(platform); + boolean removed = platforms.remove(platform); + if (removed) { + logger.log(Level.FINE, "Unregistering " + platform.getClass().getCanonicalName() + " from WorldEdit"); + + if (platform == primary) { + primary = null; + commandManager.unregister(); + } + } + return removed; + } + + /** + * Get the command manager. + * + * @return the command manager + */ + public CommandManager getCommandManager() { + return commandManager; + } + + /** + * Get the current configuration. + *

+ * If no platform has been registered yet, then a default configuration + * will be returned. + * + * @return the configuration + */ + public LocalConfiguration getConfiguration() { + Platform platform = primary; + if (platform != null) { + return platform.getConfiguration(); + } else { + return defaultConfig; + } + } + /** + * Return a {@link Platform}. + * + * @return a {@link Platform} + * @throws IllegalStateException if no platform has been registered + */ + public Platform getPlatform() throws IllegalStateException { + Platform platform = primary; + if (platform != null) { + return platform; + } else { + throw new IllegalStateException("No platform has been registered"); + } + } + + /** + * Return a legacy {@link ServerInterface}. + * + * @return a {@link ServerInterface} + * @throws IllegalStateException if no platform has been registered + */ + public ServerInterface getServerInterface() throws IllegalStateException { + Platform platform = primary; + if (platform != null) { + if (platform instanceof ServerInterface) { + return (ServerInterface) platform; + } else { + return new ServerInterfaceAdapter(platform); + } + } else { + throw new IllegalStateException("No platform has been registered"); + } + } + + /** + * A default configuration for when none is set. + */ + private static class DefaultConfiguration extends LocalConfiguration { + @Override + public void load() { + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformRejectionException.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformRejectionException.java new file mode 100644 index 000000000..1810950eb --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformRejectionException.java @@ -0,0 +1,49 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.platform; + +import com.sk89q.worldedit.WorldEditException; + +/** + * Thrown when a platform registration request is rejected, which may + * be because another platform is already registered. + */ +public class PlatformRejectionException extends WorldEditException { + + /** + * Create with a message. + * + * @param message the message + */ + public PlatformRejectionException(String message) { + super(message); + } + + /** + * Create with a message and a cause. + * + * @param message the message + * @param cause the cause + */ + public PlatformRejectionException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/ServerInterfaceAdapter.java b/src/main/java/com/sk89q/worldedit/extension/platform/ServerInterfaceAdapter.java new file mode 100644 index 000000000..d07ea2809 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/ServerInterfaceAdapter.java @@ -0,0 +1,93 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.platform; + +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandsManager; +import com.sk89q.worldedit.*; + +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Adapts {@link Platform}s into the legacy {@link ServerInterface}. + */ +class ServerInterfaceAdapter extends ServerInterface { + + private final Platform platform; + + /** + * Create a new adapter. + * + * @param platform the platform + */ + ServerInterfaceAdapter(Platform platform) { + checkNotNull(platform); + this.platform = platform; + } + + @Override + public int resolveItem(String name) { + return platform.resolveItem(name); + } + + @Override + public boolean isValidMobType(String type) { + return platform.isValidMobType(type); + } + + @Override + public void reload() { + platform.reload(); + } + + @Override + public BiomeTypes getBiomes() { + return platform.getBiomes(); + } + + @Override + public int schedule(long delay, long period, Runnable task) { + return platform.schedule(delay, period, task); + } + + @Override + public List getWorlds() { + return platform.getWorlds(); + } + + @Override + @Deprecated + public void onCommandRegistration(List commands) { + platform.onCommandRegistration(commands); + } + + @Override + public void onCommandRegistration(List commands, CommandsManager manager) { + platform.onCommandRegistration(commands, manager); + } + + @Override + public LocalConfiguration getConfiguration() { + return platform.getConfiguration(); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/logging/DynamicStreamHandler.java b/src/main/java/com/sk89q/worldedit/util/logging/DynamicStreamHandler.java new file mode 100644 index 000000000..500b77aef --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/logging/DynamicStreamHandler.java @@ -0,0 +1,179 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.logging; + +import javax.annotation.Nullable; +import java.io.UnsupportedEncodingException; +import java.util.logging.*; + +/** + * A {@link StreamHandler} delegate that allows for the swap and disable of + * another handler. When {@link #setHandler(StreamHandler)} is called with + * null, then records passed onto this handler will be dropped. Otherwise, + * the delegate handler will receive those records. + */ +public class DynamicStreamHandler extends StreamHandler { + + private @Nullable StreamHandler handler; + private @Nullable Formatter formatter; + private @Nullable Filter filter; + private @Nullable String encoding; + private Level level = Level.ALL; + + /** + * Get the delegate handler. + * + * @return the delegate handler (Which may be null) + */ + public @Nullable synchronized StreamHandler getHandler() { + return handler; + } + + /** + * Set the handler. + * + * @param handler the delegate handler (which can be null) + */ + public synchronized void setHandler(@Nullable StreamHandler handler) { + if (this.handler != null) { + this.handler.close(); + } + + this.handler = handler; + + if (handler != null) { + handler.setFormatter(formatter); + handler.setFilter(filter); + try { + handler.setEncoding(encoding); + } catch (UnsupportedEncodingException ignore) { + } + handler.setLevel(level); + } + } + + @Override + public synchronized void publish(LogRecord record) { + if (handler != null) { + handler.publish(record); + } + } + + @Override + public synchronized void close() throws SecurityException { + if (handler != null) { + handler.close(); + } + } + + @Override + public void setEncoding(@Nullable String encoding) throws SecurityException, UnsupportedEncodingException { + StreamHandler handler = this.handler; + this.encoding = encoding; + if (handler != null) { + handler.setEncoding(encoding); + } + } + + @Override + public boolean isLoggable(LogRecord record) { + StreamHandler handler = this.handler; + return handler != null && handler.isLoggable(record); + } + + @Override + public synchronized void flush() { + StreamHandler handler = this.handler; + if (handler != null) { + handler.flush(); + } + } + + @Override + public void setFormatter(@Nullable Formatter newFormatter) throws SecurityException { + StreamHandler handler = this.handler; + this.formatter = newFormatter; + if (handler != null) { + handler.setFormatter(newFormatter); + } + } + + @Override + public Formatter getFormatter() { + StreamHandler handler = this.handler; + Formatter formatter = this.formatter; + if (handler != null) { + return handler.getFormatter(); + } else if (formatter != null) { + return formatter; + } else { + return new SimpleFormatter(); + } + } + + @Override + public String getEncoding() { + StreamHandler handler = this.handler; + String encoding = this.encoding; + if (handler != null) { + return handler.getEncoding(); + } else { + return encoding; + } + } + + @Override + public void setFilter(@Nullable Filter newFilter) throws SecurityException { + StreamHandler handler = this.handler; + this.filter = newFilter; + if (handler != null) { + handler.setFilter(newFilter); + } + } + + @Override + public Filter getFilter() { + StreamHandler handler = this.handler; + Filter filter = this.filter; + if (handler != null) { + return handler.getFilter(); + } else { + return filter; + } + } + + @Override + public synchronized void setLevel(Level newLevel) throws SecurityException { + if (handler != null) { + handler.setLevel(newLevel); + } + this.level = newLevel; + } + + @Override + public synchronized Level getLevel() { + if (handler != null) { + return handler.getLevel(); + } else { + return level; + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/LogFormat.java b/src/main/java/com/sk89q/worldedit/util/logging/LogFormat.java similarity index 95% rename from src/main/java/com/sk89q/worldedit/util/LogFormat.java rename to src/main/java/com/sk89q/worldedit/util/logging/LogFormat.java index a7be80c05..c605b6a3a 100644 --- a/src/main/java/com/sk89q/worldedit/util/LogFormat.java +++ b/src/main/java/com/sk89q/worldedit/util/logging/LogFormat.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.util; +package com.sk89q.worldedit.util.logging; import java.util.logging.Formatter; import java.util.logging.LogRecord; @@ -26,9 +26,7 @@ import java.io.PrintWriter; import java.io.StringWriter; /** - * Used for formatting. - * - * @author sk89q + * A standard logging format for WorldEdit. */ public class LogFormat extends Formatter { @Override diff --git a/src/main/java/com/sk89q/worldedit/util/logging/WorldEditPrefixHandler.java b/src/main/java/com/sk89q/worldedit/util/logging/WorldEditPrefixHandler.java new file mode 100644 index 000000000..f2c8a0754 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/logging/WorldEditPrefixHandler.java @@ -0,0 +1,59 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.logging; + +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +/** + * Adds a WorldEdit prefix to WorldEdit's logger messages using a handler. + */ +public final class WorldEditPrefixHandler extends Handler { + + private WorldEditPrefixHandler() { + } + + @Override + public void publish(LogRecord record) { + String message = record.getMessage(); + if (!message.startsWith("WorldEdit: ") && !message.startsWith("[WorldEdit] ")) { + record.setMessage("[WorldEdit] " + message); + } + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + } + + /** + * Add the handler to the following logger name. + * + * @param name the logger name + */ + public static void register(String name) { + Logger.getLogger(name).addHandler(new WorldEditPrefixHandler()); + } + +} diff --git a/src/spout/java/com/sk89q/worldedit/spout/SpoutServerInterface.java b/src/spout/java/com/sk89q/worldedit/spout/SpoutServerInterface.java index e2de37895..5bcf68cae 100644 --- a/src/spout/java/com/sk89q/worldedit/spout/SpoutServerInterface.java +++ b/src/spout/java/com/sk89q/worldedit/spout/SpoutServerInterface.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.spout; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.CommandsManager; +import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.LocalWorld; import com.sk89q.worldedit.ServerInterface; @@ -102,4 +103,9 @@ public class SpoutServerInterface extends ServerInterface { spoutCommand.closeSubCommand(); } } + + @Override + public LocalConfiguration getConfiguration() { + return plugin.getLocalConfiguration(); + } } diff --git a/src/spout/java/com/sk89q/worldedit/spout/WorldEditPlugin.java b/src/spout/java/com/sk89q/worldedit/spout/WorldEditPlugin.java index 5f7454ded..0e9a7cae7 100644 --- a/src/spout/java/com/sk89q/worldedit/spout/WorldEditPlugin.java +++ b/src/spout/java/com/sk89q/worldedit/spout/WorldEditPlugin.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.spout; import com.sk89q.util.yaml.YAMLProcessor; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.extension.platform.PlatformRejectionException; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Polygonal2DRegion; @@ -99,7 +100,12 @@ public class WorldEditPlugin extends CommonPlugin { // Setup interfaces server = new SpoutServerInterface(this, getEngine()); - controller = new WorldEdit(server, config); + controller = WorldEdit.getInstance(); + try { + controller.getPlatformManager().register(server); + } catch (PlatformRejectionException e) { + throw new RuntimeException("Failed to register with WorldEdit", e); + } // Now we can register events! registerEvents();