From e52ca6661fe582005b891290aebbba72bd92327f Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 01:11:35 -0700 Subject: [PATCH] Added support for platforms to declare capabilities. Platforms can declare certain capabilities and a suggested preference for the platform for each capability. WorldEdit can then choose the best platform for a given capability. Examples of capabilities include providing configuration, registering game hooks/events, performing changes to the world, or checking permissions/authorization. --- .../bukkit/BukkitServerInterface.java | 28 ++- .../worldedit/bukkit/WorldEditListener.java | 12 ++ .../worldedit/bukkit/WorldEditPlugin.java | 38 ++-- .../sk89q/worldedit/forge/ForgePlatform.java | 38 +++- .../sk89q/worldedit/forge/ForgeWorldEdit.java | 16 +- .../worldedit/command/WorldEditCommands.java | 10 +- .../event/platform/PlatformReadyEvent.java | 29 +++ .../extension/platform/Capability.java | 81 ++++++++ ...n.java => NoCapablePlatformException.java} | 29 ++- .../extension/platform/Platform.java | 15 ++ .../extension/platform/PlatformManager.java | 175 +++++++++++------- .../extension/platform/Preference.java | 59 ++++++ .../internal/ServerInterfaceAdapter.java | 17 +- 13 files changed, 416 insertions(+), 131 deletions(-) create mode 100644 src/main/java/com/sk89q/worldedit/event/platform/PlatformReadyEvent.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/platform/Capability.java rename src/main/java/com/sk89q/worldedit/extension/platform/{PlatformRejectionException.java => NoCapablePlatformException.java} (62%) create mode 100644 src/main/java/com/sk89q/worldedit/extension/platform/Preference.java diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index 6a34748a9..fdfecd950 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -25,6 +25,8 @@ 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.*; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Preference; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Server; @@ -32,16 +34,14 @@ import org.bukkit.World; import org.bukkit.entity.EntityType; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.util.*; public class BukkitServerInterface extends ServerInterface { public Server server; public WorldEditPlugin plugin; private CommandRegistration dynamicCommands; private BukkitBiomeTypes biomes; + private boolean hookingEvents; public BukkitServerInterface(WorldEditPlugin plugin, Server server) { this.plugin = plugin; @@ -50,6 +50,10 @@ public class BukkitServerInterface extends ServerInterface { dynamicCommands = new CommandRegistration(plugin); } + boolean isHookingEvents() { + return hookingEvents; + } + @Override public int resolveItem(String name) { Material mat = Material.matchMaterial(name); @@ -114,6 +118,11 @@ public class BukkitServerInterface extends ServerInterface { dynamicCommands.register(toRegister); } + @Override + public void registerGameHooks() { + hookingEvents = true; + } + @Override public LocalConfiguration getConfiguration() { return plugin.getLocalConfiguration(); @@ -134,6 +143,17 @@ public class BukkitServerInterface extends ServerInterface { return plugin.getDescription().getVersion(); } + @Override + public Map getCapabilities() { + Map capabilities = new EnumMap(Capability.class); + capabilities.put(Capability.CONFIGURATION, Preference.NORMAL); + capabilities.put(Capability.GAME_HOOKS, Preference.PREFERRED); + capabilities.put(Capability.PERMISSIONS, Preference.PREFERRED); + capabilities.put(Capability.USER_COMMANDS, Preference.PREFERRED); + capabilities.put(Capability.WORLD_EDITING, Preference.PREFER_OTHERS); + return capabilities; + } + public void unregisterCommands() { dynamicCommands.unregisterCommands(); } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java index 50e7f9e37..4a60658a3 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java @@ -69,11 +69,19 @@ public class WorldEditListener implements Listener { */ @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { + if (!plugin.getInternalPlatform().isHookingEvents()) { + return; + } + plugin.getWorldEdit().markExpire(plugin.wrapPlayer(event.getPlayer())); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onGamemode(PlayerGameModeChangeEvent event) { + if (!plugin.getInternalPlatform().isHookingEvents()) { + return; + } + // this will automatically refresh their sesssion, we don't have to do anything WorldEdit.getInstance().getSession(plugin.wrapPlayer(event.getPlayer())); } @@ -114,6 +122,10 @@ public class WorldEditListener implements Listener { */ @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { + if (!plugin.getInternalPlatform().isHookingEvents()) { + return; + } + if (event.useItemInHand() == Result.DENY) { return; } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 7efb2b240..03d4383dc 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -26,13 +26,9 @@ import com.sk89q.worldedit.bukkit.selections.CuboidSelection; import com.sk89q.worldedit.bukkit.selections.CylinderSelection; 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.event.platform.PlatformReadyEvent; import com.sk89q.worldedit.extent.inventory.BlockBag; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.regions.CylinderRegion; -import com.sk89q.worldedit.regions.Polygonal2DRegion; -import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.regions.*; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -106,21 +102,19 @@ public class WorldEditPlugin extends JavaPlugin { // Setup interfaces server = new BukkitServerInterface(this, getServer()); 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); + 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); - } 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); - } + getServer().getScheduler().runTaskTimerAsynchronously(this, new SessionTimer(controller, getServer()), 120, 120); + + // If we are on MCPC+/Cauldron, then Forge will have already loaded + // Forge WorldEdit and there's (probably) not going to be any other + // platforms to be worried about... at the current time of writing + WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); } private void copyNmsBlockClasses(File target) { @@ -357,6 +351,10 @@ public class WorldEditPlugin extends JavaPlugin { return server; } + BukkitServerInterface getInternalPlatform() { + return server; + } + /** * Get WorldEdit. * diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java index f24396222..0e15f1ca8 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -23,6 +23,8 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.worldedit.BiomeTypes; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.ServerInterface; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Preference; import cpw.mods.fml.common.FMLCommonHandler; import net.minecraft.command.CommandBase; import net.minecraft.command.ICommand; @@ -34,21 +36,26 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.world.WorldServer; import net.minecraftforge.common.DimensionManager; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; class ForgePlatform extends ServerInterface { + private final ForgeWorldEdit mod; private final MinecraftServer server; private final ForgeBiomeTypes biomes; + private boolean hookingEvents = false; - public ForgePlatform(ForgeWorldEdit mod) { + ForgePlatform(ForgeWorldEdit mod) { this.mod = mod; this.server = FMLCommonHandler.instance().getMinecraftServerInstance(); this.biomes = new ForgeBiomeTypes(); } + boolean isHookingEvents() { + return hookingEvents; + } + + @Override public int resolveItem(String name) { if (name == null) return 0; for (Item item : Item.itemsList) { @@ -65,21 +72,26 @@ class ForgePlatform extends ServerInterface { return 0; } + @Override public boolean isValidMobType(String type) { return EntityList.stringToClassMapping.containsKey(type); } + @Override public void reload() { } + @Override public BiomeTypes getBiomes() { return this.biomes; } + @Override public int schedule(long delay, long period, Runnable task) { return -1; } + @Override public List getWorlds() { List worlds = Arrays.asList(DimensionManager.getWorlds()); List ret = new ArrayList(worlds.size()); @@ -125,6 +137,12 @@ class ForgePlatform extends ServerInterface { } } + @Override + public void registerGameHooks() { + // We registered the events already anyway, so we just 'turn them on' + hookingEvents = true; + } + @Override public LocalConfiguration getConfiguration() { return mod.getConfig(); @@ -144,4 +162,16 @@ class ForgePlatform extends ServerInterface { public String getPlatformVersion() { return mod.getInternalVersion(); } + + @Override + public Map getCapabilities() { + Map capabilities = new EnumMap(Capability.class); + capabilities.put(Capability.CONFIGURATION, Preference.PREFER_OTHERS); + capabilities.put(Capability.GAME_HOOKS, Preference.NORMAL); + capabilities.put(Capability.PERMISSIONS, Preference.PREFER_OTHERS); + capabilities.put(Capability.USER_COMMANDS, Preference.NORMAL); + capabilities.put(Capability.WORLD_EDITING, Preference.PREFERRED); + return capabilities; + } + } diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 7e21f7134..72647b26a 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -24,8 +24,8 @@ import com.google.common.io.Closer; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.event.platform.PlatformReadyEvent; import com.sk89q.worldedit.extension.platform.Platform; -import com.sk89q.worldedit.extension.platform.PlatformRejectionException; import com.sk89q.worldedit.internal.LocalWorldAdapter; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.Mod; @@ -103,11 +103,8 @@ public class ForgeWorldEdit { } this.platform = new ForgePlatform(this); - try { - WorldEdit.getInstance().getPlatformManager().register(platform); - } catch (PlatformRejectionException e) { - throw new RuntimeException("Failed to register with WorldEdit", e); - } + + WorldEdit.getInstance().getPlatformManager().register(platform); } @EventHandler @@ -115,6 +112,11 @@ public class ForgeWorldEdit { WorldEdit.getInstance().getPlatformManager().unregister(platform); } + @EventHandler + public void serverStarted(FMLServerStartedEvent event) { + WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); + } + @ForgeSubscribe public void onCommandEvent(CommandEvent event) { if ((event.sender instanceof EntityPlayerMP)) { @@ -128,6 +130,8 @@ public class ForgeWorldEdit { @ForgeSubscribe public void onPlayerInteract(PlayerInteractEvent event) { + if (!platform.isHookingEvents()) return; // We have to be told to catch these events + if (event.useItem == Result.DENY || event.entity.worldObj.isRemote) return; WorldEdit we = WorldEdit.getInstance(); diff --git a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index a4ce0f806..6da2a3ecf 100644 --- a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -56,19 +56,11 @@ public class WorldEditCommands { player.print("https://github.com/sk89q/worldedit/"); PlatformManager pm = we.getPlatformManager(); - Platform primary = pm.getPrimaryPlatform(); player.printDebug(""); player.printDebug("Platforms:"); for (Platform platform : pm.getPlatforms()) { - String prefix = ""; - - if (primary != null && primary.equals(platform)) { - prefix = "[PRIMARY] "; - } - - player.printDebug(String.format("- %s%s v%s (WE v%s)", - prefix, platform.getPlatformName(), platform.getPlatformVersion(), platform.getVersion())); + player.printDebug(String.format("- %s v%s (WE v%s)", platform.getPlatformName(), platform.getPlatformVersion(), platform.getVersion())); } } diff --git a/src/main/java/com/sk89q/worldedit/event/platform/PlatformReadyEvent.java b/src/main/java/com/sk89q/worldedit/event/platform/PlatformReadyEvent.java new file mode 100644 index 000000000..e80e12557 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/platform/PlatformReadyEvent.java @@ -0,0 +1,29 @@ +/* + * 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.event.Event; + +/** + * Raised when a platform thinks that all the platforms have had a chance to + * register themselves. + */ +public class PlatformReadyEvent extends Event { +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java b/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java new file mode 100644 index 000000000..61ee2bfb8 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java @@ -0,0 +1,81 @@ +/* + * 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; + +/** + * A collection of capabilities that a {@link Platform} may support. + */ +public enum Capability { + + /** + * The capability of registering game hooks to catch events such as + * a player clicking a block. + */ + GAME_HOOKS { + @Override + void initialize(PlatformManager platformManager, Platform platform) { + platform.registerGameHooks(); + } + + @Override + void unload(PlatformManager platformManager, Platform platform) { + } + }, + + /** + * The capability of providing configuration. + */ + CONFIGURATION, + + /** + * The capability of handling user commands entered in chat or console. + */ + USER_COMMANDS { + @Override + void initialize(PlatformManager platformManager, Platform platform) { + platformManager.getCommandManager().register(platform); + } + + @Override + void unload(PlatformManager platformManager, Platform platform) { + platformManager.getCommandManager().unregister(); + } + }, + + /** + * The capability of a platform to assess whether a given + * {@link Actor} has sufficient authorization to perform a task. + */ + PERMISSIONS, + + /** + * The capability of a platform to perform modifications to a world. + */ + WORLD_EDITING; + + void initialize(PlatformManager platformManager, Platform platform) { + + } + + void unload(PlatformManager platformManager, Platform platform) { + + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformRejectionException.java b/src/main/java/com/sk89q/worldedit/extension/platform/NoCapablePlatformException.java similarity index 62% rename from src/main/java/com/sk89q/worldedit/extension/platform/PlatformRejectionException.java rename to src/main/java/com/sk89q/worldedit/extension/platform/NoCapablePlatformException.java index 1810950eb..c3e1019a7 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformRejectionException.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/NoCapablePlatformException.java @@ -19,31 +19,24 @@ 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. + * Thrown when no capable platform is found. */ -public class PlatformRejectionException extends WorldEditException { +public class NoCapablePlatformException extends RuntimeException { - /** - * Create with a message. - * - * @param message the message - */ - public PlatformRejectionException(String message) { + public NoCapablePlatformException() { + } + + public NoCapablePlatformException(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) { + public NoCapablePlatformException(String message, Throwable cause) { super(message, cause); } + public NoCapablePlatformException(Throwable cause) { + super(cause); + } + } diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java b/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java index 2d506a07c..5fcfa4941 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -27,6 +27,7 @@ import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.world.World; import java.util.List; +import java.util.Map; /** * Represents a platform that WorldEdit has been implemented for. @@ -82,6 +83,11 @@ public interface Platform { void onCommandRegistration(List commands, CommandsManager manager); + /** + * Register game hooks. + */ + void registerGameHooks(); + /** * Get the configuration from this platform. * @@ -116,4 +122,13 @@ public interface Platform { */ String getPlatformVersion(); + /** + * Get a map of advertised capabilities of this platform, where each key + * in the given map is a supported capability and the respective value + * indicates the preference for this platform for that given capability. + * + * @return a map of capabilities + */ + Map getCapabilities(); + } diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 4a93df9e4..7c04b8168 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -20,10 +20,12 @@ package com.sk89q.worldedit.extension.platform; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.command.tool.*; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.BlockInteractEvent; import com.sk89q.worldedit.event.platform.Interaction; +import com.sk89q.worldedit.event.platform.PlatformReadyEvent; import com.sk89q.worldedit.event.platform.PlayerInputEvent; import com.sk89q.worldedit.internal.ServerInterfaceAdapter; import com.sk89q.worldedit.regions.RegionSelector; @@ -31,8 +33,8 @@ import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.eventbus.Subscribe; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; @@ -48,11 +50,11 @@ 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 WorldEdit worldEdit; private final CommandManager commandManager; - private @Nullable Platform primary = null; + private final List platforms = new ArrayList(); + private final Map preferences = new EnumMap(Capability.class); + private @Nullable String firstSeenVersion; /** * Create a new platform manager. @@ -72,56 +74,128 @@ public class PlatformManager { * 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 { + public synchronized void register(Platform platform) { checkNotNull(platform); + logger.log(Level.FINE, "Got request to register " + platform.getClass() + " with WorldEdit [" + super.toString() + "]"); + + // Just add the platform to the list of platforms: we'll pick favorites + // once all the platforms have been loaded platforms.add(platform); - // Register primary platform - if (this.primary == null) { - commandManager.register(platform); - this.primary = platform; - } else { - // Make sure that versions are in sync - if (!primary.getVersion().equals(platform.getVersion())) { + // Make sure that versions are in sync + if (firstSeenVersion != null) { + if (!firstSeenVersion.equals(platform.getVersion())) { logger.log(Level.WARNING, "\n**********************************************\n" + - "** There is a mismatch in available WorldEdit platforms!\n" + + "** You have WorldEdit installed for multiple platforms in the same \n" + + "** game/program. This is OK except that you have different WorldEdit versions\n" + + "** installed (i.e. {0} and {1}).\n" + "**\n" + - "** {0} v{1} is trying to register WE version v{2}\n" + - "** but the primary platform, {3} v{4}, uses WE version v{5}\n" + + "** WorldEdit has seen both versions {0} and {1}.\n" + "**\n" + "** Things may break! Please make sure that your WE versions are in sync.\n" + "**********************************************\n", new Object[]{ - platform.getClass(), platform.getPlatformVersion(), platform.getVersion(), - primary.getClass(), primary.getPlatformVersion(), primary.getVersion() + firstSeenVersion, platform.getVersion() }); } + } else { + firstSeenVersion = platform.getVersion(); } } /** * Unregister a platform from WorldEdit. + *

+ * If the platform has been chosen for any capabilities, then a new + * platform will be found. * * @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(); + boolean choosePreferred = false; + + // Check whether this platform was chosen to be the preferred one + // for any capability and be sure to remove it + Iterator> it = preferences.entrySet().iterator(); + while (it.hasNext()) { + Entry entry = it.next(); + if (entry.getValue().equals(platform)) { + entry.getKey().unload(this, entry.getValue()); + it.remove(); + choosePreferred = true; // Have to choose new favorites + } + } + + if (choosePreferred) { + choosePreferred(); } } + return removed; } + /** + * Get the preferred platform for handling a certain capability. Returns + * null if none is available. + * + * @param capability the capability + * @return the platform + * @throws NoCapablePlatformException thrown if no platform is capable + */ + public synchronized Platform queryCapability(Capability capability) throws NoCapablePlatformException { + Platform platform = preferences.get(checkNotNull(capability)); + if (platform != null) { + return platform; + } else { + throw new NoCapablePlatformException("No platform was found supporting " + capability.name()); + } + } + + /** + * Choose preferred platforms and perform necessary initialization. + */ + private synchronized void choosePreferred() { + for (Capability capability : Capability.values()) { + Platform preferred = findMostPreferred(capability); + if (preferred != null) { + preferences.put(capability, preferred); + capability.initialize(this, preferred); + } + } + } + + /** + * Find the most preferred platform for a given capability from the list of + * platforms. This does not use the map of preferred platforms. + * + * @param capability the capability + * @return the most preferred platform, or null if no platform was found + */ + private synchronized @Nullable Platform findMostPreferred(Capability capability) { + Platform preferred = null; + Preference highest = null; + + for (Platform platform : platforms) { + Preference preference = platform.getCapabilities().get(capability); + if (preference != null && (highest == null || preference.isPreferredOver(highest))) { + preferred = platform; + highest = preference; + } + } + + return preferred; + } + /** * Get a list of loaded platforms. *

@@ -133,15 +207,6 @@ public class PlatformManager { return new ArrayList(platforms); } - /** - * Get the primary platform. - * - * @return the primary platform (may be null) - */ - public @Nullable Platform getPrimaryPlatform() { - return primary; - } - /** * Get the command manager. * @@ -160,26 +225,7 @@ public class PlatformManager { * @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 queryCapability(Capability.CONFIGURATION).getConfiguration(); } /** @@ -188,19 +234,17 @@ public class PlatformManager { * @return a {@link ServerInterface} * @throws IllegalStateException if no platform has been registered */ + @SuppressWarnings("deprecation") public ServerInterface getServerInterface() throws IllegalStateException { - Platform platform = primary; - if (platform != null) { - if (platform instanceof ServerInterface) { - return (ServerInterface) platform; - } else { - return ServerInterfaceAdapter.adapt(platform); - } - } else { - throw new IllegalStateException("No platform has been registered"); - } + return ServerInterfaceAdapter.adapt(queryCapability(Capability.USER_COMMANDS)); } + @Subscribe + public void handlePlatformReady(PlatformReadyEvent event) { + choosePreferred(); + } + + @SuppressWarnings("deprecation") @Subscribe public void handleBlockInteract(BlockInteractEvent event) { Actor actor = event.getCause(); @@ -288,6 +332,7 @@ public class PlatformManager { } } + @SuppressWarnings("deprecation") @Subscribe public void handlePlayerInput(PlayerInputEvent event) { Player player = event.getPlayer(); @@ -370,13 +415,5 @@ public class PlatformManager { } } - /** - * 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/Preference.java b/src/main/java/com/sk89q/worldedit/extension/platform/Preference.java new file mode 100644 index 000000000..f047fa26d --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Preference.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.extension.platform; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Indicates the preference of a platform for a particular + * {@link Capability}. + */ +public enum Preference { + + /** + * Indicates that the platform should be preferred for a given capability. + */ + PREFERRED, + + /** + * Indicates that preference for a platform is neutral for a given + * capability. + */ + NORMAL, + + /** + * Indicates that there should not be a preference for the platform for + * a given capability. + */ + PREFER_OTHERS; + + /** + * Returns whether this given preference is preferred over the given + * other preference. + * + * @param other the other preference + * @return true if this preference is greater + */ + public boolean isPreferredOver(Preference other) { + checkNotNull(other); + return ordinal() < other.ordinal(); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java b/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java index fa71a403a..96cac54b3 100644 --- a/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java +++ b/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java @@ -21,11 +21,17 @@ package com.sk89q.worldedit.internal; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandsManager; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.BiomeTypes; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.ServerInterface; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.extension.platform.Preference; import com.sk89q.worldedit.world.World; import java.util.List; +import java.util.Map; import static com.google.common.base.Preconditions.checkNotNull; @@ -87,6 +93,10 @@ public class ServerInterfaceAdapter extends ServerInterface { platform.onCommandRegistration(commands, manager); } + @Override + public void registerGameHooks() { + } + @Override public LocalConfiguration getConfiguration() { return platform.getConfiguration(); @@ -107,6 +117,11 @@ public class ServerInterfaceAdapter extends ServerInterface { return platform.getPlatformVersion(); } + @Override + public Map getCapabilities() { + return platform.getCapabilities(); + } + /** * Adapt an {@link Platform} instance into a {@link ServerInterface}. *