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 extends com.sk89q.worldedit.world.World> 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}.
*