Be less hacky. Dynamic aliases in a new and less intrusive way.

This commit is contained in:
Olof Larsson 2014-02-14 03:08:56 +01:00
parent 4384c5f396
commit ddcca231a6
6 changed files with 184 additions and 280 deletions

View File

@ -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<String, Command> knownCommands = getSimpleCommandMapDotKnownCommands(simpleCommandMap);
// For each known command ...
Iterator<Entry<String, Command>> iter = knownCommands.entrySet().iterator();
while (iter.hasNext())
{
Entry<String, Command> 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<String, Command> getSimpleCommandMapDotKnownCommands(SimpleCommandMap simpleCommandMap)
{
return (Map<String, Command>) 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;
}
}
}

View File

@ -20,7 +20,6 @@ import com.massivecraft.mcore.adapter.ModdedEnumTypeAdapter;
import com.massivecraft.mcore.adapter.ObjectIdAdapter; import com.massivecraft.mcore.adapter.ObjectIdAdapter;
import com.massivecraft.mcore.adapter.PlayerInventoryAdapter; import com.massivecraft.mcore.adapter.PlayerInventoryAdapter;
import com.massivecraft.mcore.adapter.UUIDAdapter; import com.massivecraft.mcore.adapter.UUIDAdapter;
import com.massivecraft.mcore.cmd.MCoreBukkitSimpleCommandMap;
import com.massivecraft.mcore.integration.protocollib.ProtocolLibFeatures; import com.massivecraft.mcore.integration.protocollib.ProtocolLibFeatures;
import com.massivecraft.mcore.integration.vault.VaultFeatures; import com.massivecraft.mcore.integration.vault.VaultFeatures;
import com.massivecraft.mcore.mcorecmd.CmdMCore; import com.massivecraft.mcore.mcorecmd.CmdMCore;
@ -150,7 +149,8 @@ public class MCore extends MPlugin
EngineScheduledTeleport.get().activate(); EngineScheduledTeleport.get().activate();
EngineTeleportMixinCause.get().activate(); EngineTeleportMixinCause.get().activate();
EngineWorldNameSet.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(); PlayerUtil.get().setup();
// Tasks // Tasks
@ -161,9 +161,6 @@ public class MCore extends MPlugin
AspectColl.get().init(); AspectColl.get().init();
MCoreConfColl.get().init(); MCoreConfColl.get().init();
// Inject our command map with dynamic tweaks
MCoreBukkitSimpleCommandMap.inject();
// Register commands // Register commands
this.outerCmdMCore = new CmdMCore() { public List<String> getAliases() { return MCoreConf.get().aliasesOuterMCore; } }; this.outerCmdMCore = new CmdMCore() { public List<String> getAliases() { return MCoreConf.get().aliasesOuterMCore; } };
this.outerCmdMCore.register(); this.outerCmdMCore.register();

View File

@ -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<String, Command> getSimpleCommandMapDotKnownCommands(SimpleCommandMap simpleCommandMap)
{
return (Map<String, Command>) get(SimpleCommandMap.class, "knownCommands", simpleCommandMap);
}
public static Set<String> getSimpleCommandMapDotAliases(SimpleCommandMap simpleCommandMap)
{
return (Set<String>) 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;
}
}
}

View File

@ -4,11 +4,9 @@ import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.massivecraft.mcore.Lang; import com.massivecraft.mcore.Lang;
import com.massivecraft.mcore.MCore;
import com.massivecraft.mcore.cmd.arg.ArgReader; import com.massivecraft.mcore.cmd.arg.ArgReader;
import com.massivecraft.mcore.cmd.arg.ArgResult; import com.massivecraft.mcore.cmd.arg.ArgResult;
import com.massivecraft.mcore.cmd.req.Req; import com.massivecraft.mcore.cmd.req.Req;
@ -18,7 +16,28 @@ import com.massivecraft.mcore.util.PermUtil;
import com.massivecraft.mcore.util.Txt; import com.massivecraft.mcore.util.Txt;
public class MCommand 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<MCommand> registeredCommands = new LinkedHashSet<MCommand>();
public static Set<MCommand> getRegisteredCommands() { return registeredCommands; }
public void register() { getRegisteredCommands().add(this); }
public void unregister() { getRegisteredCommands().remove(this); }
// -------------------------------------------- // // -------------------------------------------- //
// COMMAND BEHAVIOR // COMMAND BEHAVIOR
// -------------------------------------------- // // -------------------------------------------- //
@ -160,19 +179,6 @@ public class MCommand
public Player me; public Player me;
public boolean senderIsConsole; 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 // CONSTRUCTORS AND EXECUTOR
// -------------------------------------------- // // -------------------------------------------- //

View File

@ -25,45 +25,16 @@ public class MCoreBukkitCommand extends Command
// CONSTRUCT // CONSTRUCT
// -------------------------------------------- // // -------------------------------------------- //
public MCoreBukkitCommand(MCommand mcommand) public MCoreBukkitCommand(String name, MCommand mcommand)
{ {
super( super(
mcommand.getClass().getSimpleName(), // The name field is final. MCommand aliases/names are not final so we simply use the class name. name,
null, // Set description to null. Instead we override the getter. mcommand.getDesc(),
null, // Set usage to null. Instead we override the getter. mcommand.getUseageTemplate(),
new ArrayList<String>() // Set aliases to "null". Instead we override the getter. new ArrayList<String>() // We don't use aliases
); );
this.mcommand = mcommand; this.mcommand = mcommand;
} }
// -------------------------------------------- //
// OVERRIDE: GETTERS
// -------------------------------------------- //
@Override
public String getDescription()
{
return this.getMcommand().getDesc();
}
@Override
public String getUsage()
{
return this.getMcommand().getUseageTemplate();
}
@Override
public List<String> getAliases()
{
return this.getMcommand().getAliases();
}
@Override
public String getLabel()
{
return this.getMcommand().getAliases().get(0);
}
// -------------------------------------------- // // -------------------------------------------- //
// OVERRIDE: EXECUTE // OVERRIDE: EXECUTE

View File

@ -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<MCoreBukkitCommand> mcoreBukkitCommands = new LinkedHashSet<MCoreBukkitCommand>();
public LinkedHashSet<MCoreBukkitCommand> 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<String, Command> 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);
}
}