From ddcca231a6ac35f870f46979b0db537d99625421 Mon Sep 17 00:00:00 2001 From: Olof Larsson Date: Fri, 14 Feb 2014 03:08:56 +0100 Subject: [PATCH] Be less hacky. Dynamic aliases in a new and less intrusive way. --- .../mcore/EngineCommandRegistration.java | 155 ++++++++++++++++++ src/com/massivecraft/mcore/MCore.java | 7 +- .../mcore/cmd/BukkitCommandDoor.java | 115 ------------- src/com/massivecraft/mcore/cmd/MCommand.java | 38 +++-- .../mcore/cmd/MCoreBukkitCommand.java | 39 +---- .../cmd/MCoreBukkitSimpleCommandMap.java | 110 ------------- 6 files changed, 184 insertions(+), 280 deletions(-) create mode 100644 src/com/massivecraft/mcore/EngineCommandRegistration.java delete mode 100644 src/com/massivecraft/mcore/cmd/BukkitCommandDoor.java delete mode 100644 src/com/massivecraft/mcore/cmd/MCoreBukkitSimpleCommandMap.java diff --git a/src/com/massivecraft/mcore/EngineCommandRegistration.java b/src/com/massivecraft/mcore/EngineCommandRegistration.java new file mode 100644 index 00000000..8f46f747 --- /dev/null +++ b/src/com/massivecraft/mcore/EngineCommandRegistration.java @@ -0,0 +1,155 @@ +package com.massivecraft.mcore; + +import java.lang.reflect.Field; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.craftbukkit.v1_7_R1.CraftServer; +import org.bukkit.plugin.Plugin; + +import com.massivecraft.mcore.cmd.MCommand; +import com.massivecraft.mcore.cmd.MCoreBukkitCommand; + +public class EngineCommandRegistration extends EngineAbstract +{ + // -------------------------------------------- // + // INSTANCE & CONSTRUCT + // -------------------------------------------- // + + private static EngineCommandRegistration i = new EngineCommandRegistration(); + public static EngineCommandRegistration get() { return i; } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public Plugin getPlugin() + { + return MCore.get(); + } + + @Override + public Long getPeriod() + { + return 20L; + } + + // -------------------------------------------- // + // TASK + // -------------------------------------------- // + + @Override + public void run() + { + updateRegistrations(); + } + + // -------------------------------------------- // + // UPDATE REGISTRATIONS + // -------------------------------------------- // + + public static void updateRegistrations() + { + // Get the SimpleCommandMap and it's knownCommands. + SimpleCommandMap simpleCommandMap = getSimpleCommandMap(); + Map knownCommands = getSimpleCommandMapDotKnownCommands(simpleCommandMap); + + // For each known command ... + Iterator> iter = knownCommands.entrySet().iterator(); + while (iter.hasNext()) + { + Entry entry = iter.next(); + Command command = entry.getValue(); + + // ... if this command is a MCoreBukkitCommand ... + if (!(command instanceof MCoreBukkitCommand)) continue; + + // ... unregister it. + command.unregister(simpleCommandMap); + iter.remove(); + } + + // For each MCommand that is supposed to be registered ... + for (MCommand mcommand : MCommand.getRegisteredCommands()) + { + // ... and for each of it's aliases ... + for (String alias : mcommand.getAliases()) + { + // ... clean the alias ... + alias = alias.trim().toLowerCase(); + + // ... unregister current occupant of that alias ... + Command previousOccupant = knownCommands.remove(alias); + if (previousOccupant != null) + { + previousOccupant.unregister(simpleCommandMap); + } + + // ... create a new MCoreBukkitCommand ... + MCoreBukkitCommand command = new MCoreBukkitCommand(alias, mcommand); + + // ... and finally register it. + simpleCommandMap.register("MCore", command); + } + } + } + + // -------------------------------------------- // + // GETTERS + // -------------------------------------------- // + + public static CraftServer getCraftServer() + { + return (CraftServer)Bukkit.getServer(); + } + + public static SimpleCommandMap getSimpleCommandMap() + { + return getCraftServer().getCommandMap(); + } + + @SuppressWarnings("unchecked") + public static Map getSimpleCommandMapDotKnownCommands(SimpleCommandMap simpleCommandMap) + { + return (Map) get(SimpleCommandMap.class, "knownCommands", simpleCommandMap); + } + + // -------------------------------------------- // + // UTIL + // -------------------------------------------- // + + public static Object get(Class clazz, String fieldName, Object object) + { + try + { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field.get(object); + } + catch (Exception e) + { + return null; + } + } + + public static void set(Class clazz, String fieldName, Object object, Object value) + { + try + { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(object, value); + } + catch (Exception e) + { + return; + } + } + + +} diff --git a/src/com/massivecraft/mcore/MCore.java b/src/com/massivecraft/mcore/MCore.java index e910abae..8539653c 100644 --- a/src/com/massivecraft/mcore/MCore.java +++ b/src/com/massivecraft/mcore/MCore.java @@ -20,7 +20,6 @@ import com.massivecraft.mcore.adapter.ModdedEnumTypeAdapter; import com.massivecraft.mcore.adapter.ObjectIdAdapter; import com.massivecraft.mcore.adapter.PlayerInventoryAdapter; import com.massivecraft.mcore.adapter.UUIDAdapter; -import com.massivecraft.mcore.cmd.MCoreBukkitSimpleCommandMap; import com.massivecraft.mcore.integration.protocollib.ProtocolLibFeatures; import com.massivecraft.mcore.integration.vault.VaultFeatures; import com.massivecraft.mcore.mcorecmd.CmdMCore; @@ -150,7 +149,8 @@ public class MCore extends MPlugin EngineScheduledTeleport.get().activate(); EngineTeleportMixinCause.get().activate(); EngineWorldNameSet.get().activate(); - EngineOfflineCase.get().activate(); // TODO: Make all engines + EngineOfflineCase.get().activate(); + EngineCommandRegistration.get().activate(); // TODO: Make all engines PlayerUtil.get().setup(); // Tasks @@ -161,9 +161,6 @@ public class MCore extends MPlugin AspectColl.get().init(); MCoreConfColl.get().init(); - // Inject our command map with dynamic tweaks - MCoreBukkitSimpleCommandMap.inject(); - // Register commands this.outerCmdMCore = new CmdMCore() { public List getAliases() { return MCoreConf.get().aliasesOuterMCore; } }; this.outerCmdMCore.register(); diff --git a/src/com/massivecraft/mcore/cmd/BukkitCommandDoor.java b/src/com/massivecraft/mcore/cmd/BukkitCommandDoor.java deleted file mode 100644 index a639ff37..00000000 --- a/src/com/massivecraft/mcore/cmd/BukkitCommandDoor.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.massivecraft.mcore.cmd; - -import java.lang.reflect.Field; -import java.util.Map; -import java.util.Set; - -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandMap; -import org.bukkit.command.SimpleCommandMap; -import org.bukkit.craftbukkit.v1_7_R1.CraftServer; -import org.bukkit.plugin.SimplePluginManager; - -@SuppressWarnings("unchecked") -public class BukkitCommandDoor -{ - // -------------------------------------------- // - // SINGLETON - // -------------------------------------------- // - - public static CraftServer getCraftServer() - { - return (CraftServer)Bukkit.getServer(); - } - - public static SimpleCommandMap getSimpleCommandMap() - { - return getCraftServer().getCommandMap(); - } - - public static void setSimpleCommandMap(SimpleCommandMap simpleCommandMap) - { - set(CraftServer.class, "commandMap", getCraftServer(), simpleCommandMap); - } - - public static SimplePluginManager getSimplePluginManager() - { - return (SimplePluginManager)Bukkit.getPluginManager(); - } - - // -------------------------------------------- // - // SIMPLE COMMAND MAP - // -------------------------------------------- // - - public static Map getSimpleCommandMapDotKnownCommands(SimpleCommandMap simpleCommandMap) - { - return (Map) get(SimpleCommandMap.class, "knownCommands", simpleCommandMap); - } - - public static Set getSimpleCommandMapDotAliases(SimpleCommandMap simpleCommandMap) - { - return (Set) get(SimpleCommandMap.class, "aliases", simpleCommandMap); - } - - // -------------------------------------------- // - // SIMPLE PLUGIN MANAGER - // -------------------------------------------- // - - public static CommandMap getSimplePluginManagerCommandMap(SimplePluginManager simplePluginManager) - { - return (CommandMap) get(SimplePluginManager.class, "commandMap", simplePluginManager); - } - - public static void setSimplePluginManagerCommandMap(SimplePluginManager simplePluginManager, CommandMap commandMap) - { - set(SimplePluginManager.class, "commandMap", simplePluginManager, commandMap); - } - - // -------------------------------------------- // - // COMMAND - // -------------------------------------------- // - - public static CommandMap getCommandDotCommandMap(Command command) - { - return (CommandMap) get(Command.class, "commandMap", command); - } - - public static void setCommandDotCommandMap(Command command, CommandMap commandMap) - { - set(Command.class, "commandMap", command, commandMap); - } - - // -------------------------------------------- // - // UTIL - // -------------------------------------------- // - - public static Object get(Class clazz, String fieldName, Object object) - { - try - { - Field field = clazz.getDeclaredField(fieldName); - field.setAccessible(true); - return field.get(object); - } - catch (Exception e) - { - return null; - } - } - - public static void set(Class clazz, String fieldName, Object object, Object value) - { - try - { - Field field = clazz.getDeclaredField(fieldName); - field.setAccessible(true); - field.set(object, value); - } - catch (Exception e) - { - return; - } - } - -} diff --git a/src/com/massivecraft/mcore/cmd/MCommand.java b/src/com/massivecraft/mcore/cmd/MCommand.java index 737f4636..3b9fd16c 100644 --- a/src/com/massivecraft/mcore/cmd/MCommand.java +++ b/src/com/massivecraft/mcore/cmd/MCommand.java @@ -4,11 +4,9 @@ import java.util.*; import java.util.Map.Entry; import org.bukkit.command.CommandSender; -import org.bukkit.command.SimpleCommandMap; import org.bukkit.entity.Player; import com.massivecraft.mcore.Lang; -import com.massivecraft.mcore.MCore; import com.massivecraft.mcore.cmd.arg.ArgReader; import com.massivecraft.mcore.cmd.arg.ArgResult; import com.massivecraft.mcore.cmd.req.Req; @@ -18,7 +16,28 @@ import com.massivecraft.mcore.util.PermUtil; import com.massivecraft.mcore.util.Txt; public class MCommand -{ +{ + // -------------------------------------------- // + // REGISTER + // -------------------------------------------- // + // MCore commands are a bit special when it comes to registration. + // + // I want my users to be able to edit the command aliases and I want + // them to be able to do so during server runtime without having to use the /reload command. + // + // To provide a truly neat experience I place the command aliases in a mstore database configuration file. + // As such these config files are polled for changes and loaded into the server automatically. + // If someone changed the command aliases we must update all Bukkit command registrations. + // + // In order to achieve this we run a task once a second (see com.massivecraft.mcore.EngineCommandRegistration). + // This task unregisters /all/ registered MCommands and then register them all again. + // When registering again we use the fresh and current aliases. + + private static transient Set registeredCommands = new LinkedHashSet(); + public static Set getRegisteredCommands() { return registeredCommands; } + public void register() { getRegisteredCommands().add(this); } + public void unregister() { getRegisteredCommands().remove(this); } + // -------------------------------------------- // // COMMAND BEHAVIOR // -------------------------------------------- // @@ -160,19 +179,6 @@ public class MCommand public Player me; public boolean senderIsConsole; - // -------------------------------------------- // - // BUKKIT INTEGRATION - // -------------------------------------------- // - - protected final MCoreBukkitCommand bukkitCommand = new MCoreBukkitCommand(this); - public MCoreBukkitCommand getBukkitCommand() { return this.bukkitCommand; } - - public void register() - { - SimpleCommandMap scm = BukkitCommandDoor.getSimpleCommandMap(); - scm.register(MCore.get().getDescription().getName(), this.getBukkitCommand()); - } - // -------------------------------------------- // // CONSTRUCTORS AND EXECUTOR // -------------------------------------------- // diff --git a/src/com/massivecraft/mcore/cmd/MCoreBukkitCommand.java b/src/com/massivecraft/mcore/cmd/MCoreBukkitCommand.java index fcb7f6bf..1cdd07ae 100644 --- a/src/com/massivecraft/mcore/cmd/MCoreBukkitCommand.java +++ b/src/com/massivecraft/mcore/cmd/MCoreBukkitCommand.java @@ -25,45 +25,16 @@ public class MCoreBukkitCommand extends Command // CONSTRUCT // -------------------------------------------- // - public MCoreBukkitCommand(MCommand mcommand) + public MCoreBukkitCommand(String name, MCommand mcommand) { super( - mcommand.getClass().getSimpleName(), // The name field is final. MCommand aliases/names are not final so we simply use the class name. - null, // Set description to null. Instead we override the getter. - null, // Set usage to null. Instead we override the getter. - new ArrayList() // Set aliases to "null". Instead we override the getter. + name, + mcommand.getDesc(), + mcommand.getUseageTemplate(), + new ArrayList() // We don't use aliases ); - this.mcommand = mcommand; } - - // -------------------------------------------- // - // OVERRIDE: GETTERS - // -------------------------------------------- // - - @Override - public String getDescription() - { - return this.getMcommand().getDesc(); - } - - @Override - public String getUsage() - { - return this.getMcommand().getUseageTemplate(); - } - - @Override - public List getAliases() - { - return this.getMcommand().getAliases(); - } - - @Override - public String getLabel() - { - return this.getMcommand().getAliases().get(0); - } // -------------------------------------------- // // OVERRIDE: EXECUTE diff --git a/src/com/massivecraft/mcore/cmd/MCoreBukkitSimpleCommandMap.java b/src/com/massivecraft/mcore/cmd/MCoreBukkitSimpleCommandMap.java deleted file mode 100644 index 204d6403..00000000 --- a/src/com/massivecraft/mcore/cmd/MCoreBukkitSimpleCommandMap.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.massivecraft.mcore.cmd; - -import java.util.LinkedHashSet; -import java.util.Map.Entry; - -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.SimpleCommandMap; - - -public class MCoreBukkitSimpleCommandMap extends SimpleCommandMap -{ - // -------------------------------------------- // - // INJECT - // -------------------------------------------- // - - public static MCoreBukkitSimpleCommandMap get() - { - SimpleCommandMap ret = BukkitCommandDoor.getSimpleCommandMap(); - if (!(ret instanceof MCoreBukkitSimpleCommandMap)) return null; - return (MCoreBukkitSimpleCommandMap) ret; - } - - public static boolean isInjected() - { - return get() != null; - } - - public static void inject() - { - if (isInjected()) return; - MCoreBukkitSimpleCommandMap instance = new MCoreBukkitSimpleCommandMap(); - BukkitCommandDoor.setSimpleCommandMap(instance); - BukkitCommandDoor.setSimplePluginManagerCommandMap(BukkitCommandDoor.getSimplePluginManager(), instance); - } - - // -------------------------------------------- // - // FIELDS - // -------------------------------------------- // - - private final LinkedHashSet mcoreBukkitCommands = new LinkedHashSet(); - public LinkedHashSet getMCoreBukkitCommands() { return this.mcoreBukkitCommands; } - - // -------------------------------------------- // - // CONSTRUCT - // -------------------------------------------- // - - public MCoreBukkitSimpleCommandMap(SimpleCommandMap simpleCommandMap) - { - // Trigger the super constructor - super(Bukkit.getServer()); - - // Fetch non static collection content - this.knownCommands.putAll(BukkitCommandDoor.getSimpleCommandMapDotKnownCommands(simpleCommandMap)); - this.aliases.addAll(BukkitCommandDoor.getSimpleCommandMapDotAliases(simpleCommandMap)); - - // Convert registrations - for (Entry entry : this.knownCommands.entrySet()) - { - Command command = entry.getValue(); - if (BukkitCommandDoor.getCommandDotCommandMap(command) == null) continue; - BukkitCommandDoor.setCommandDotCommandMap(command, this); - } - } - - public MCoreBukkitSimpleCommandMap() - { - this(BukkitCommandDoor.getSimpleCommandMap()); - } - - // -------------------------------------------- // - // OVERRIDE - // -------------------------------------------- // - - @Override - public boolean register(String label, String fallbackPrefix, Command command) - { - // Bukkit - if (!(command instanceof MCoreBukkitCommand)) - { - return super.register(label, fallbackPrefix, command); - } - - // MCore - command.register(this); - this.getMCoreBukkitCommands().add((MCoreBukkitCommand)command); - - return true; - } - - @Override - public Command getCommand(String name) - { - // MCore - for (MCoreBukkitCommand mbc : this.getMCoreBukkitCommands()) - { - for (String alias : mbc.getAliases()) - { - if (alias.equalsIgnoreCase(name)) - { - return mbc; - } - } - } - - // Bukkit - return super.getCommand(name); - } - -}