public class Constants {

   /**
    * Contains the version string of the current Minecraft version
    */
   public static final String VERSION = "4.9";
   
   /**
    * The name of the mod
    */
   public static final String NAME = "Single Player Commands";
   
   /**
    * The current version of the mod
    */
   public static final ModVersion SPC_VERSION = new ModVersion(NAME, VERSION, new Date(1374315917859L)); // 2013-07-20 20:25:17

   /**
    * The directory that the mod saves/loads global settings from
    */
   public static final File MOD_DIR = new File(Minecraft.getMinecraftDirectory(), "mods/spc");
   
   // Creates the mod directory if it doesn't already exist
   static {
      if (!MOD_DIR.exists()) {
         MOD_DIR.mkdirs();
      }
   }
   
   /**
    * The default settings file to use
    */
   public static final Settings DEFAULT_SETTINGS = new Settings(new File(MOD_DIR, "default.properties"));
   
   /**
    * Directory where the Minecraft level saves are located
    */
   public static final File SAVES_DIR = new File(Minecraft.getMinecraftDirectory(), "saves");
   
   /**
    * @return the version
    */
   public static String getVersion() {
      return VERSION;
   }
   
   /**
    * @return the name
    */
   public static String getName() {
      return NAME;
   }
   
   /**
    * @return the spcVersion
    */
   public static ModVersion getSpcVersion() {
      return SPC_VERSION;
   }
   
   /**
    * @return the modDir
    */
   public static File getModDir() {
      return MOD_DIR;
   }
} public enum FontColour {
   
   BLACK("\2470"),
   DARK_BLUE("\2471"),
   DARK_GREEN("\2472"),
   DARK_AQUA("\2473"),
   DARK_RED("\2474"),
   PURPLE("\2475"),
   ORANGE("\2476"),
   GREY("\2477"),
   DARK_GREY("\2478"),
   BLUE("\2479"),
   GREEN("\247a"),
   AQUA("\247b"),
   RED("\247c"),
   PINK("\247d"),
   YELLOW("\247e"),
   WHITE("\247f"),
   // Special case - random
   RANDOM("\247k");
   
   /**
    * Holds the random variables
    */
   private Random random;
   
   /**
    * The value of the enum
    */
   private final String value;
   
   /**
    * Initialises the enum using the specified value
    * 
    * @param value - The value of the FontColour
    */
   private FontColour(String value) {
      this.value = value;
      random = new Random();
   }
   
   /**
    * Overrides the default toString method to return the value of the Enum
    * 
    * @see java.lang.Enum#toString()
    */
   @Override
   public String toString() {
      if (value.equalsIgnoreCase("\247k")) {
         return values()[random.nextInt(values().length - 1)].toString();
      }
      return value;
   }
} public interface KeyListener {

   /**
    * Gets called on a registered key press event. See org.lwjgl.input.Keyboard 
    * key fields for more information. 
    * 
    * @param key - The key that was pressed
    * @see KeyboardHandler#addKeyPressedListener
    * @see org.lwjgl.input.Keyboard
    */
   public void keyPressed(int key);
   
   /**
    * Gets called on a registered key release event. See 
    * org.lwjgl.input.Keyboard key fields for more information. 
    * 
    * @param key - The key that was released
    * @see KeyboardHandler#addKeyReleasedListener
    * @see org.lwjgl.input.Keyboard
    */
   public void keyReleased(int key);
} public class KeyboardHandler extends Thread {

   /**
    * The delay in milliseconds that the keyboard events are checked
    */
   public static final long DELAY = 10;
   /**
    * The singleton instance of the class
    */
   private static KeyboardHandler KEYBOARD = new KeyboardHandler();
   /**
    * The singleton method that returns the instance
    * 
    * @return The instance of the class to use
    */
   public static KeyboardHandler getInstance() {
      return KEYBOARD;
   }

   /**
    * Holds the value of whether the instance is currently listening or not
    */
   private boolean listening;

   /**
    * A List of keys that are currently registered with this listener
    */
   private List<Integer> keys;
   /**
    * A List of keys that have currently been pressed
    */
   private List<Integer> pressed;

   /**
    * A Map containing the registered pressed event listeners
    */
   private Map<Integer, List<KeyListener>> registeredPressed;
   /**
    * A Map containing the registered released event listeners
    */
   private Map<Integer, List<KeyListener>> registeredReleased;

   /**
    * Default private constructor sets up the instance
    */
   private KeyboardHandler() {
      registeredPressed = new ConcurrentHashMap<Integer, List<KeyListener>>();
      registeredReleased = new ConcurrentHashMap<Integer, List<KeyListener>>();
      pressed = new CopyOnWriteArrayList<Integer>();
      keys = new CopyOnWriteArrayList<Integer>();
      listening = false;
   }

   /**
    * Checks for key press and key release events
    * 
    * @see java.lang.Thread#run()
    */
   @Override
   public void run() {
      try {
         while (listening) {
            if (!Keyboard.isCreated()) {
               listening = false;
               break;
            }
            // Check if pressed have been released
            for (Integer press : pressed) {
               if (!Keyboard.isKeyDown(press)) {
                  keyReleased(press);
               }
            }
            // Check if key is pressed
            for (Integer key : keys) {
               if (Keyboard.isKeyDown(key) && !pressed.contains(key)) {
                  keyPressed(key);
               }
            }
            try {
               Thread.sleep(DELAY);
            } catch (Exception e) {
            }
         }
      } catch (Throwable e) {
         e.printStackTrace();
         listening = false;
      }
   }

   /**
    * Calls all of the necessary listeners based off the key pressed
    * 
    * @param key - The key that was pressed
    */
   private void keyPressed(int key) {
      pressed.add(key);
      if (registeredPressed.get(key) == null) {
         return;
      }
      for (KeyListener listener : registeredPressed.get(key)) {
         listener.keyPressed(key);
      }
   }

   /**
    * Calls all of the necessary listeners based off the key released
    * 
    * @param key - The key that was released
    */
   private void keyReleased(int key) {
      pressed.remove((Object)key);
      if (registeredReleased.get(key) == null) {
         return;
      }
      for (KeyListener listener : registeredReleased.get(key)) {
         listener.keyReleased(key);
      }
   }

   /**
    * Stops the instance from listening
    */
   public void stopListening() {
      listening = false;
   }

   /**
    * Adds a key press listener to the specified key. See the Keyboard class 
    * for details on the key codes.
    * 
    * @param key - The key to add the listener to
    * @param listener - The listener to call on event
    * @return True if the listener was successfully added
    * @see org.lwjgl.input.Keyboard
    */
   public boolean addKeyPressedListener(int key, KeyListener listener) {
      return addListener(key, listener, registeredPressed);
   }

   /**
    * Adds a key release listener to the specified key. See the Keyboard class 
    * for details on the key codes.
    * 
    * @param key - The key to add the listener to
    * @param listener - The listener to call on event
    * @return True if the listener was successfully added
    * @see org.lwjgl.input.Keyboard
    */
   public boolean addKeyReleasedListener(int key, KeyListener listener) {
      return addListener(key, listener, registeredReleased);
   }

   /**
    * Generic implementation that adds the specified key and listener to the 
    * provided internal listener map.
    * 
    * @param key - The key to add
    * @param listener - The listener to call
    * @param internal - The map to add to
    * @return True if the listener was added successfully
    */
   private boolean addListener(int key, KeyListener listener, Map<Integer, List<KeyListener>> internal) {
      if (Keyboard.getKeyName(key) == null) {
         return false;
      }
      List<KeyListener> keylist = internal.get(key);
      if (keylist == null) {
         keylist = new CopyOnWriteArrayList<KeyListener>();
         internal.put(key, keylist);
      }
      keylist.add(listener);
      if (keys.indexOf(key) == -1) {
         keys.add(key);
      }
      if (!listening) {
         listening = true;
         start();
      }
      return true;
   }
   
   /**
    * Removes the specified pressed listener and key from being listened to 
    * by this class.
    * 
    * @param key - The key to remove
    * @param listener - The listener to remove
    */
   public void removeKeyPressedListener(int key, KeyListener listener) {
      removeListener(key, listener, registeredPressed);
   }
   
   /**
    * Removes the specified released listener and key from being listened to 
    * by this class.
    * 
    * @param key - The key to remove
    * @param listener - The listener to remove
    */
   public void removeKeyReleasedListener(int key, KeyListener listener) {
      removeListener(key, listener, registeredReleased);
   }   

   /**
    * Removes the specified key and listener instance from the specified Map 
    * listener. 
    * 
    * @param key - The key to remove based on the listener
    * @param listener - The listener to remove 
    * @param internal - The listener Map to remove the key from
    */
   private void removeListener(int key, KeyListener listener, Map<Integer, List<KeyListener>> internal) {
      // Key hasn't been added, ignore
      if (!keys.contains(key)) {
         return;
      }
      // Remove from List
      List<KeyListener> list = internal.get(key);
      if (list != null) {
         list.remove(listener);
      }
      // Check if the key is referenced anywhere
      checkKeyUsage(key);
   }

   /**
    * Checks the specified key whether it is still used by a listener or not. 
    * If the key is not used it is removed from the instance so that it isn't 
    * listened to anymore. 
    * 
    * @param key - The key to check
    */
   private void checkKeyUsage(int key) {
      List<KeyListener> pressed = registeredPressed.get(key);
      boolean pressedEmpty = false;
      if (pressed != null && pressed.size() == 0) {
         registeredPressed.remove(key);
         pressedEmpty = true;
      }
      List<KeyListener> released = registeredReleased.get(key);
      boolean releasedEmpty = false;
      if (released != null && released.size() == 0) {
         registeredReleased.remove(key);
         releasedEmpty = true;
      }
      if (pressedEmpty && releasedEmpty) {
         keys.remove((Object)key);
         pressed.remove((Object)key);
      }
   }

   /**
    * Gets the ID of the specified key string, this ID can be used to register
    * key listeners. If the specified String is an invalid key type then -1 is
    * returned.
    * 
    * @param key - The key to retrieve the ID for
    * @return The ID of the specified key, or -1 if not found
    */
   public static int getKeyCode(String key) {
      if (key == null) {
         return -1;
      }
      return Keyboard.getKeyIndex(key.toUpperCase()) == Keyboard.KEY_NONE ? -1 : Keyboard.getKeyIndex(key.toUpperCase());
   }
} public class Settings extends Properties {

   /**
    * serialVersionUID used by the serialised interface 
    */
   private static final long serialVersionUID = -7463327430611005961L;
   /**
    * The file that the settings are contained within
    */
   private File settings;

   /**
    * Initialises a default instance of the class with no disk location 
    * specified to load/save to settings from.
    */
   public Settings() {
      super();
   }

   /**
    * Loads the specified file into the instance
    * 
    * @param f - The file to load
    */
   public Settings(File f) {
      this(f, true);
   }

   /**
    * Associates the file with the instance allowing the file to be loaded or 
    * not using the load parameter.
    * 
    * @param f - The file to load
    * @param load - True to load the file, false otherwise
    */
   public Settings(File f, boolean load) {
      super();
      this.settings = f;
      if (load) {
         load(f);
      }
   }

   /**
    * Sets the specified setting to the boolean value
    * 
    * @param key - The key to set
    * @param value - The value to assign
    */
   public void set(String key, boolean value) {
      setProperty(key, new Boolean(value).toString());
   }

   /**
    * Gets the specified setting as a boolean value, if the setting doesn't 
    * exist then the base parameter value is used
    * 
    * @param key - The setting to retrieve 
    * @param base - The default setting to use if the key doesn't exist
    * @return The value of the setting
    */
   public boolean getBoolean(String key, boolean base) {
      String value = getProperty(key);
      try {
         return (value == null || value.trim().equalsIgnoreCase("")) ? base : new Boolean(value);
      } catch (Exception e) {
         return base;
      }
   }

   /**
    * Sets the specified setting to the integer value
    * 
    * @param key - The key to set
    * @param value - The value to assign
    */
   public void set(String key, int value) {
      setProperty(key,new Integer(value).toString());
   }

   /**
    * Gets the specified setting as a integer value, if the setting doesn't 
    * exist then the base parameter value is used
    * 
    * @param key - The setting to retrieve 
    * @param base - The default setting to use if the key doesn't exist
    * @return The value of the setting
    */
   public int getInteger(String key, int base) {
      String value = getProperty(key);
      try {
         return isEmpty(value) ? base : new Integer(value);
      } catch (NumberFormatException e) {
         return base;
      }
   }

   /**
    * Sets the specified setting to the char value
    * 
    * @param key - The key to set
    * @param value - The value to assign
    */
   public void set(String key, char value) {
      setProperty(key,new Character(value).toString());
   }

   /**
    * Gets the specified setting as a char value, if the setting doesn't 
    * exist then the base parameter value is used
    * 
    * @param key - The setting to retrieve 
    * @param base - The default setting to use if the key doesn't exist
    * @return The value of the setting
    */
   public char getCharacter(String key, char base) {
      String value = getProperty(key);
      try {
         return isEmpty(value) ? base : value.charAt(0);
      } catch (NumberFormatException e) {
         return base;
      }
   }

   /**
    * Sets the specified setting to the double value
    * 
    * @param key - The key to set
    * @param value - The value to assign
    */
   public void set(String key, double value) {
      setProperty(key,new Double(value).toString());
   }

   /**
    * Gets the specified setting as a double value, if the setting doesn't 
    * exist then the base parameter value is used
    * 
    * @param key - The setting to retrieve 
    * @param base - The default setting to use if the key doesn't exist
    * @return The value of the setting
    */
   public double getDouble(String key, double base) {
      String value = getProperty(key);
      try {
         return isEmpty(value) ? base : new Double(value);
      } catch (NumberFormatException e) {
         return base;
      }
   }

   /**
    * Sets the specified setting to the float value
    * 
    * @param key - The key to set
    * @param value - The value to assign
    */
   public void set(String key, float value) {
      setProperty(key,new Float(value).toString());
   }

   /**
    * Gets the specified setting as a float value, if the setting doesn't 
    * exist then the base parameter value is used
    * 
    * @param key - The setting to retrieve 
    * @param base - The default setting to use if the key doesn't exist
    * @return The value of the setting
    */
   public float getFloat(String key, float base) {
      String value = getProperty(key);
      try {
         return isEmpty(value) ? base : new Float(value);
      } catch (NumberFormatException e) {
         return base;
      }
   }

   /**
    * Sets the specified setting to the String value
    * 
    * @param key - The key to set
    * @param value - The value to assign
    */
   public void set(String key, String value) {
      setProperty(key,value);
   }

   /**
    * Gets the specified setting as a String value, if the setting doesn't 
    * exist then the base parameter value is used
    * 
    * @param key - The setting to retrieve 
    * @param base - The default setting to use if the key doesn't exist
    * @return The value of the setting
    */
   public String getString(String key, String base) {
      String value = getProperty(key);
      return isEmpty(value) ? base : value;
   }

   /**
    * Saves the settings instance back to file
    * 
    * @return True if the settings were able to be saved
    */
   public boolean save() {
      return this.save("");
   }

   /**
    * Saves the settings instance back to file with the specified header
    * 
    * @param header - The header to set to the properties
    * @return True if the settings were able to be saved
    */
   public boolean save(String header) {
      return this.save(settings,header);
   }

   /**
    * Saves the settings instance back to the specified file, not the one 
    * associated with the instance. The header is also set in the file 
    * 
    * @param file - The file to save the settings to
    * @param header - The header to set to the properties
    * @return True if the settings were able to be saved
    */
   public boolean save(File file, String header) {
      if (file == null || file.isDirectory()) {
         return false;
      }
      FileOutputStream fos = null;
      try {
         if (!file.exists()) {
            file.createNewFile();
         }
         fos = new FileOutputStream(file);
         super.store(fos, header);
         return true;
      } catch (Exception e) {
         return false;
      } finally {
         if (fos != null) {
            try {
               fos.close();
            } catch (IOException e) {}
         }
      }
   }

   /**
    * Loads the settings from disk.
    * 
    * @return True if the settings were able to be loaded
    */
   public boolean load() {
      return this.load(settings);
   }

   /**
    * Loads the settings from disk from the specified file.
    * 
    * @param file - The file to load the settings from
    * @return True if the settings were able to be loaded
    */
   public boolean load(File file) {
      if (file == null || file.isDirectory()) {
         return false;
      }
      try {
         if (!file.exists()) {
            file.createNewFile();
            return true;
         }
         super.load(new FileInputStream(file));
         return true;
      } catch (Exception e) {
         return false;
      }
   }

   /**
    * Gets the file that the settings are saving/loading from
    * 
    * @return The file that the instance is using
    */
   public File getFile() {
      return settings;
   }

   /**
    * Sets the file that the settings are saving/loading from 
    * 
    * @param settings - The path to the file to load/save from
    */
   public void setFile(File settings) {
      this.settings = settings;
   }

   /**
    * Checks if the specified value is empty
    * 
    * @param value - The value to check
    * @return True if the String is empty
    */
   private boolean isEmpty(String value) {
      return (value == null || value.trim().equalsIgnoreCase(""));
   }

   /**
    * @see java.util.Hashtable#clone()
    */
   @Override
   public Object clone() {
      return new Settings(this.settings);
   }
} public abstract class Parameter {

   private String label;
   private boolean optional;
   private boolean variableLength;
   
   public Parameter(String label, boolean optional) {
      this.label = label;
      this.optional = optional;
   }
   
   public Parameter(String label, boolean optional, boolean variableLength) {
      this.label = label;
      this.optional = optional;
      this.variableLength = variableLength;
   }
   
   /**
    * Returns the parameter label. The label is used to display to the user
    * 
    * @return The parameter label
    */
   public String getLabel() {
      return label;
   }
   
   /**
    * Returns whether the parameter is optional
    * 
    * @return Returns the optional value
    */
   public boolean isOptional() {
      return optional;
   }
   
   /**
    * Returns whether the parameter is variable length or not. This parameter 
    * is used while doing parameter validation but is ignored unless it is 
    * the last specified parameter
    * 
    * @return Returns the variable length flag
    */
   public boolean isVariableLength() {
      return variableLength;
   }
   
   /**
    * Validates the specified user input whether it is valid input according to
    * the implementing class. For example ParameterInteger will validate that
    * the provided input is a valid integer between the specified values
    * 
    * @param parameter - The parameter provided by the user to verify
    * @return The validated value is returned when the specified parameter is 
    * valid. The returned value is of the type it was validated as
    */
   public abstract Object validate(String parameter) throws ValidationException;
} public class ParameterInteger extends Parameter {
   private int min;
   private int max;
   
   public ParameterInteger(String label, boolean optional) {
      this(label,optional,Integer.MIN_VALUE,Integer.MAX_VALUE);
   }
   
   public ParameterInteger(String label, boolean optional, int min, int max) {
      super(label, optional);
      this.min = min;
      this.max = max;
   }

   /**
    * @see com.sijobe.spc.validation.Parameter#validate(java.lang.String)
    */
   @Override
   public Object validate(String parameter) throws ValidationException {
      try {
         int value = Integer.parseInt(parameter);
         if (value > min && value < max) {
            return value;
         } else {
            throw new ValidationException("Parameter " + parameter + 
                     " is out of the expected range (" + min + "-" + max + ")");
         }
      } catch (NumberFormatException n) {
         throw new ValidationException("Invalid text (" + parameter + 
                  ") where integer value was expected.");
      } catch (Exception e) {
         throw new ValidationException();
      }
   }
} public class ParameterString extends Parameter {
   private List<String> validValues;
   private String expected;

   public ParameterString(String label, boolean optional) {
      this(label,optional,false,null);
   }
   
   public ParameterString(String label, boolean optional, boolean variableLength) {
      this(label,optional,variableLength,null);
   }
   
   public ParameterString(String label, boolean optional, String validValues[]) {
      super(label, optional, false);
      List<String> values = new ArrayList<String>();
      for (String value : validValues) {
         values.add(value);
      }
      initialise(label, optional, false, values);
   }
   
   public ParameterString(String label, boolean optional, List<String> validValues) {
      this(label,optional,false,validValues);
   }
   
   public ParameterString(String label, boolean optional, boolean variableLength, List<String> validValues) {
      super(label, optional, variableLength);
      initialise(label, optional, variableLength, validValues);
   }
   
   private void initialise(String label, boolean optional, boolean variableLength, List<String> validValues) {
      this.validValues = validValues;
      if (validValues == null) {
         validValues = new ArrayList<String>();
      }
      for (int i = 0; i < validValues.size(); i++) {
         validValues.set(i, validValues.get(i).toLowerCase());
         if (i == 0) {
            expected = validValues.get(i);
         } else {
            expected = expected + ", " + validValues.get(i);
         }
      }
   }

   /**
    * @see com.sijobe.spc.validation.Parameter#validate(java.lang.String)
    */
   @Override
   public Object validate(String parameter) throws ValidationException {
      if (validValues == null) {
         return parameter;
      }
      if (validValues.contains(parameter.toLowerCase())) {
         return parameter;
      }
      throw new ValidationException("Parameter " + parameter + 
               " is an unexpected value (" + expected + ")");
   }

} public class Parameters {
   
   /**
    * Optional String of variable length arguments - ie: any input
    */
   public static final Parameters DEFAULT = new Parameters(
      new Parameter[] {
         new ParameterString("",true,true)
      }
   );
   /**
    * Parameters of the command
    */
   public static final Parameters DEFAULT_BOOLEAN = new Parameters (
      new Parameter[] {
         new ParameterBoolean("[enable|disable]", true, "enable", "disable")
      }
   );
   
   /**
    * Each of the parameter validators to use to validate
    */
   private Parameter params[];

   /**
    * Initialises the class using the specified validation parameters
    * 
    * @param params - The validation parameters to use
    */
   public Parameters(Parameter params[]) {
      this.params = params;
   }

   /**
    * Validates the input against the parameters for this command. The input
    * is validated by each of the validators and which results in a List of
    * type correct Object returned. 
    * 
    * If more parameters are specified than validators and the last validator
    * is not variable length then any extra parameters are ignored.
    * 
    * @param parameters - The parameters to validate
    * @return A list of validated objects that match their validation type
    * @throws ValidationException - when a validation error occurs an expection
    * is thrown detailing the problem
    */
   public List<?> validate(String parameters[]) throws ValidationException {
      List<Object> validated = new ArrayList<Object>();
      for (int i = 0; i < params.length; i++) {
         // Checks for the correct number of arguments
         if (i + 1 > parameters.length) { 
            if (params[i].isOptional()) {
               continue;
            } else {
               throw new ValidationException("Not enough arguments");
            }
         }

         try {
            if (i == params.length - 1 && parameters.length > params.length && params[i].isVariableLength()) { 
               // Last Parameter and variable length - combine parameters and validate
               String variableLength = "";
               for (int j = i; j < parameters.length; j++) {
                  if (j == i) {
                     variableLength = parameters[j];
                  } else {
                     variableLength = variableLength + " " + parameters[j];
                  }
               }
               validated.add(params[i].validate(variableLength));
            } else {
               // Validate the parameter
               validated.add(params[i].validate(parameters[i]));
            }
         } catch (ValidationException v) {
            throw v;
         } catch (Exception e) {
            throw new ValidationException("Problem with the command: " + e);
         }
      }
      return validated;
   }

   /**
    * Gets how to use the command
    * 
    * @return The String representing how to use the command
    */
   public String getUsage() {
      String usage = "";
      for (Parameter param : params) {
         usage += param.getLabel() + " ";
      }
      return usage.trim();
   }
} public class CommandException extends Exception {
   
   /**
    * GUID
    */
   private static final long serialVersionUID = -2082390336460702125L;

   public CommandException() {
      super();
   }
   
   public CommandException(String message) {
      super(message);
   }
   
   public CommandException(Throwable t) {
      super(t);
   }
} public class CommandManager {
   
   /**
    * The command manager to run the methods off
    * 
    * @return The command manager reference the server is using
    */   
   public static ServerCommandManager getCommandManager() {
      return (ServerCommandManager)net.minecraft.server.MinecraftServer.getServer().getCommandManager();
   }
   
   /**
    * Runs the specified command (including parameters) as the specified 
    * sender. The sender will get passed through to the commands execute method
    * and will have the action of the command executed on it. 
    * 
    * @param sender - The sender of the command
    * @param command - The command to run
    */
   public static void runCommand(CommandSender sender, String command) {
      getCommandManager().executeCommand(sender.getMinecraftISender(), command);
   }
   
   /**
    * Registers the specified command into the Minecraft command list. If a 
    * command with the same name (or alias) already exists it will be 
    * overwritten.
    * 
    * @param command - The command to register
    */
   public static void registerCommand(CommandBase command) {
      getCommandManager().registerCommand(command);
   }
   
   /**
    * Checks if the specified command name already exists in Minecraft
    * 
    * @param name - The name of the command to check
    * @return True if the command exists 
    */
   public static boolean doesCommandExist(String name) {
      return getCommandManager().getCommands().containsKey(name);
   }
   
   /**
    * Gets the names of the commands in Minecraft
    * 
    * @return A List of command names in Minecraft
    */
   public static List<String> getCommandNames() {
      return new ArrayList<String>(getCommandManager().getCommands().keySet());
   }
   
   /**
    * Returns true if the command is enabled, false otherwise. If the command 
    * isn't a SPC command then true is automatically returned as there is no
    * way to disable a standard Minecraft command
    * 
    * @param name - The name of the command to get the usage for
    * @return True if the command is enabled, false otherwise
    */
   public static boolean isCommandEnabled(String name) {
      try {
         return ((CommandBase)getCommandMap().get(name)).isEnabled();
      } catch (Exception e) {
         return true;
      }
   }
   
   /**
    * Gets the command usage
    * 
    * @param name - The name of the command to get the usage for
    * @param sender - The sender who requested the usage
    * @return A String specifying the command usage
    */
   public static String getCommandUsage(String name, CommandSender sender) {
      try {
         return getCommandMap().get(name).getCommandUsage(sender.getMinecraftISender());
      } catch (Exception e) {
         return null;
      }
   }
   
   /**
    * Gets the command description
    * 
    * @param name - The name of the command to get the description for
    * @return A String specifying the command description
    */
   public static String getCommandDescription(String name) {
      try {
         return ((CommandBase)getCommandMap().get(name)).getDescription();
      } catch (Exception e) {
         return null;
      }
   }
   
   /**
    * Gets the command example
    * 
    * @param name - The name of the command to get the example for
    * @return A String specifying the command example
    */
   public static String getCommandExample(String name) {
      try {
         return ((CommandBase)getCommandMap().get(name)).getExample();
      } catch (Exception e) {
         return null;
      }
   }
   
   /**
    * Gets the command video
    * 
    * @param name - The name of the command to get the video for
    * @return A String specifying the command video
    */
   public static String getCommandVideo(String name) {
      try {
         return ((CommandBase)getCommandMap().get(name)).getVideoURL();
      } catch (Exception e) {
         return null;
      }
   }
   
   /**
    * Checks if the specified player has the permission for the specified 
    * command to run it. True is returned if the player can run the command, 
    * false otherwise. If the command doesn't exist then false is returned.
    * 
    * @param name - The name of the command
    * @param player - The player to check
    * @return True if the player can run it, false otherwise
    */
   public static boolean hasPermission(String name, Player player) {
      try {
         return getCommandMap().get(name).canCommandSenderUseCommand(player.getMinecraftPlayer());
      } catch (Exception e) {
         return false;
      }
   }
   
   /**
    * Gets the command map in the correct format
    * 
    * @return The command map
    */
   private static Map<String,ICommand> getCommandMap() {
      return getCommandManager().getCommands();
   }
} public class CommandSender {
   private final ICommandSender sender;
   
   /**
    * Standard constructor that accepts the sender to wrap
    * 
    * @param sender - The sender to wrap
    */
   public CommandSender(ICommandSender sender) {
      this.sender = sender;
   }
   
   /**
    * Creates an instance of a command sender using the player instance
    * 
    * @param player - The player to create a command sender
    */
   public CommandSender(Player player) {
      this (player.getMinecraftPlayer());
   }
   
   /**
    * Gets the senders name, commonly this is the player's username
    * 
    * @return The senders name
    */
   public String getSenderName() {
      return sender.getCommandSenderName();
   }
   
   /**
    * Returns true if the sender can use the specified command
    * 
    * @param command - The command name
    * @return True is returned if the sender can use the specified command
    */
   public boolean canUseCommand(String command) {
      return sender.canCommandSenderUseCommand(4, command);
   }
   
   /**
    * Sends the specified message to the sender. The sender should display this
    * message.
    * 
    * @param message - The message to display
    */
   public void sendMessageToPlayer(String message) {
      sender.sendChatToPlayer(ChatMessageComponent.func_111066_d(message));
   }
   
   /**
    * Translates the provided key translate with the provided parameters
    * 
    * @param translate - The string to translate
    * @param params - The parameters
    * @return The translated String
    */
   public String translateString(String translate, Object ... params) {
      return StatCollector.translateToLocalFormatted(translate, params);
   }
   
   /**
    * Gets the raw data object that this class is wrapping around
    * 
    * @return The ICommandSender class
    */
   public ICommandSender getMinecraftISender() {
      return sender;
   }
   
   /**
    * Returns true if this command sender object is a player
    * 
    * @return True if the command sender is a player, false otherwise
    */
   public boolean isPlayer() {
      return sender instanceof EntityPlayer;
   }
} public class Minecraft {

   /**
    * Gets the directory that Minecraft is installed in
    * 
    * @return The Minecraft installation directory
    */
   public static File getMinecraftDirectory() {
      return getMinecraft().mcDataDir;
   }

   /**
    * Gets the internal Minecraft reference
    * 
    * @return The Minecraft instance
    */
   public static net.minecraft.src.Minecraft getMinecraft() {
      return net.minecraft.src.Minecraft.getMinecraft();
   }

   /**
    * Returns true if the game is being played in Single player, false if the
    * game is in LAN or server mode.
    * 
    * @return True when in single player, false otherwise.
    */
   public static boolean isSinglePlayer() {
      return getMinecraft().isSingleplayer() && !getMinecraft().getIntegratedServer().getPublic();
   }

   /**
    * Returns true if the a GUI screen is currently open
    * 
    * @return True if there is a GUI screen open
    */
   public static boolean isGuiScreenOpen() {
      return getMinecraft().currentScreen != null;
   }

   /**
    * If the game is being player in single player this will return the client 
    * instance, otherwise null
    * 
    * @return The player instance, otherwise null
    */
   public static Player getPlayer() {
      if (isSinglePlayer()) {
         return new Player(getMinecraft().thePlayer);
      } 
      return null;
   }
} public class MinecraftServer {

   /**
    * Gets the instance of the Minecraft server that is running
    * 
    * @return The Minecraft server instance
    */
   public static net.minecraft.server.MinecraftServer getMinecraftServer() {
      return net.minecraft.server.MinecraftServer.getServer();
   }
   
   /**
    * Gets the version of Minecraft that the server currently is
    * 
    * @return The server version
    */
   public static String getVersion() {
      return getMinecraftServer().getMinecraftVersion();
   }
   
   /**
    * Returns a List of all currently logged in users
    * 
    * @return A List of all the usernames of logged in users
    */
   public static List<String> getPlayers() {
      List<String> players = new ArrayList<String>();
      for (String username : getMinecraftServer().getAllUsernames()) {
         players.add(username.toLowerCase());
      }
      return players;
   }
   
   /**
    * Checks if the specified user is currently logged in, if so true is 
    * returned, otherwise false
    * 
    * @param username - The username to check
    * @return True if the user is logged in
    */
   public static boolean isLoggedIn(String username) {
      return getPlayers().indexOf(username.toLowerCase()) != -1;
   }
   
   /**
    * Gets the player object that matches the provided username or null if the 
    * username cannot be found
    * 
    * @param username - The username to search for
    * @return The Player object that wraps the Minecraft instance
    */
   public static Player getPlayerByUsername(String username) {
      if (isLoggedIn(username)) {
         return new Player(getMinecraftServer().getConfigurationManager().getPlayerForUsername(username));
      }
      return null;
   }
   
   /**
    * Runs the specified command (with parameters) on the server. Note that 
    * this sends through the command as a server instance rather than with a 
    * player. If you need to send through a command using a sender please see
    * CommandManager.runCommand
    * 
    * @param command - The command to run on the server
    * @return A String of the output received 
    * @see CommandManager#runCommand(CommandSender, String)
    */
   public static String runCommand(String command) {
      return getMinecraftServer().executeCommand(command);
   }
   
   /**
    * Gets the directory name of the loaded world
    * 
    * @return The directory name of the world
    */
   public static String getDirectoryName() {
      return getMinecraftServer().getFolderName();
   }
   
   /**
    * Gets the directory that the currently loaded world is located at
    * 
    * @return The location where the world is located
    */
   public static File getWorldDirectory() {
      return new File(Constants.SAVES_DIR, getDirectoryName());
   }
} public class Player {

   /**
    * The object that the instance is wrapped around
    */
   private final EntityPlayer player;

   public Player(EntityPlayer player) {
      this.player = player;
   }

   /**
    * Sets the player position and if on server sends the update to the client
    * 
    * @param c - The coordinate that the player is being moved to
    */
   public void setPosition(Coordinate c) {
      if (player instanceof EntityPlayerMP) {
         ((EntityPlayerMP) player).setPositionAndUpdate(c.getX(), c.getY(), c.getZ());
      } else {
         player.setPosition(c.getX(), c.getY(), c.getZ());
      }
   }

   /**
    * Gets the current position that the player is currently standing
    * 
    * @return The position that the player is standing
    */
   public Coordinate getPosition() {
      return new Coordinate(player.posX, player.posY, player.posZ);
   }

   /**
    * Gets the player rotation yaw
    * 
    * @return Rotation Yaw
    */
   public float getYaw() {
      return player.rotationYaw;
   }

   /**
    * Sets the rotation yaw of the player
    * 
    * @param yaw - The yaw value to set to the player
    */
   public void setYaw(float yaw) {
      player.rotationYaw = yaw;
   }

   /**
    * Gets the player rotation pitch
    * 
    * @return Rotation pitch
    */
   public float getPitch() {
      return player.rotationPitch;
   }

   /**
    * Sets the rotation pitch of the player
    * 
    * @param pitch - The pitch value to set to the player
    */
   public void setPitch(float pitch) {
      player.rotationPitch = pitch;
   }

   /**
    * Gets the world that the player is currently in
    * 
    * @return The world that the player is currently in
    */
   public World getWorld() {
      if (player instanceof EntityPlayerMP) {
         return new World(((EntityPlayerMP) player).theItemInWorldManager.theWorld); // why?
      } else {
         return new World(player.worldObj);
      }
   }

   /**
    * Sends a chat message to the user
    * 
    * @param message - The message to send to the user
    */
   public void sendChatMessage(String message) {
      player.addChatMessage(message);
   }

   /**
    * Gives the player a maximum stack of the specified item
    * 
    * @param id - The item id
    */
   public void givePlayerItem(int id) {
      givePlayerItem(id, Item.getMaxStack(id));
   }

   /**
    * Gives the player a maximum stack of the specified item
    * 
    * @param id - The item id
    * @param quantity - The quantity of the item
    */
   public void givePlayerItem(int id, int quantity) {
      givePlayerItem(id, quantity, 0);
   }

   /**
    * Gives the player a maximum stack of the specified item
    * 
    * @param id - The item id
    * @param quantity - The quantity of the item
    * @param damage - The "damage" (metadata) value of the item
    */
   public void givePlayerItem(int id, int quantity, int damage) {
      ItemStack itemStack = new ItemStack(id, quantity, damage);
      if (!player.inventory.addItemStackToInventory(itemStack)) {
         player.dropPlayerItem(itemStack);
      }
   }

   /**
    * Gets the players current health
    * 
    * @return The value of the players health
    */
   public float getHealth() {
      return player.func_110143_aJ();
   }

   /**
    * Sets the players health to the specified value
    * 
    * @param health - The health amount
    */
   public void setHealth(float health) {
      player.setEntityHealth(health);
   }

   /**
    * Heals the player the specified quantity. Use a negative number to remove
    * the specified amount from the player.
    * 
    * @param quantity - The ammount to heal the player
    */
   public void heal(float quantity) {
      setHealth(getHealth() + quantity);
   }

   /**
    * Gets the players current hunger level
    * 
    * @return The hunger level
    */
   public int getHunger() {
      return player.getFoodStats().getFoodLevel();
   }

   /**
    * Sets the players hunger level
    * 
    * @param food - The hunger level to set to
    */
   public void setHunger(int food) {
      player.getFoodStats().setFoodLevel(food);
   }

   /**
    * Gets the boolean value representing whether player damage is on or off.
    * 
    * @return True when damage is on, false when damage is off
    */
   public boolean getDamage() {
      return !player.capabilities.disableDamage;
   }

   /**
    * Sets whether player damage is on or off
    * 
    * @param damage - when true it turns player damage on, false turns it off
    */
   public void setDamage(boolean damage) {
      player.capabilities.disableDamage = !damage;
   }

   /**
    * Sets the item contained within the specified inventory slot
    * 
    * @param slot - The slot to set
    * @param id - The item id
    * @param quantity - The item quantity
    * @param damage - The item "damage" value
    * @return True if the slot was correctly set, false otherwise
    */
   public boolean setInventorySlot(int slot, int id, int quantity, int damage) {
      if (slot < 0 || slot >= player.inventory.mainInventory.length) {
         return false;
      } else if (!Item.isValidItem(id)) {
         if (id == 0) {
            player.inventory.mainInventory[slot] = null;
            return true;
         }
         return false;
      }
      player.inventory.mainInventory[slot] = new ItemStack(id, quantity, damage);
      return true;
   }

   /**
    * Performs a trace on the players view to the specified maximum distance.
    * The trace returns the coordinate where it hits a block or null if nothing
    * was found.
    * 
    * @param distance - The maximum distance to check
    * @return A coordinate object containing the block that was inline with
    *         the players view
    */
   public Coordinate trace(double distance) {
      MovingObjectPosition m = rayTrace(distance, 1.0F);
      if (m == null) {
         return null;
      }
      return new Coordinate(m.blockX, m.blockY, m.blockZ);
   }

   public MovingObjectPosition rayTrace(double distance, float partialTickTime) {
      Vec3 positionVec = getPositionVec(partialTickTime);
      Vec3 lookVec = player.getLook(partialTickTime);
      Vec3 hitVec = positionVec.addVector(lookVec.xCoord * distance, lookVec.yCoord * distance, lookVec.zCoord * distance);
      return player.worldObj.rayTraceBlocks_do_do(positionVec, hitVec, false, true); // TODO: Validate correct params
   }

   /**
    * interpolated position vector
    */
   public Vec3 getPositionVec(float partialTickTime) {
      double offsetY = player.posY + player.getEyeHeight();
      if (partialTickTime == 1.0F) {
         return getWorld().getMinecraftWorld().getWorldVec3Pool().getVecFromPool(player.posX, offsetY, player.posZ);
      } else {
         double var2 = player.prevPosX + (player.posX - player.prevPosX) * partialTickTime;
         double var4 = player.prevPosY + (offsetY - (player.prevPosY + player.getEyeHeight())) * partialTickTime;
         double var6 = player.prevPosZ + (player.posZ - player.prevPosZ) * partialTickTime;
         return getWorld().getMinecraftWorld().getWorldVec3Pool().getVecFromPool(var2, var4, var6);
      }
   }

   /*public String getGameType() { // TODO
   }*/

   /**
    * Sets the gametype that the player uses
    * 
    * @param gametype - The player game type
    * @return True if the specified gametype was found
    */
   public boolean setGameType(String gametype) {
      EnumGameType chosen = null;
      if ((chosen = EnumGameType.getByName(gametype)) == null) {
         return false;
      }
      player.setGameType(chosen);
      return true;
   }

   /**
    * Gets the players name
    * 
    * @return The players name
    */
   public String getPlayerName() {
      return player.getEntityName();
   }

   /**
    * Gets the item ID of the current held item
    * 
    * @return The ID of the currently held item
    */
   public int getCurrentItem() {
      try {
         return player.inventory.mainInventory[getCurrentSlot()].itemID;
      } catch (NullPointerException e) {
         return 0;
      }
   }

   /**
    * Gets the slot number of the current item
    * 
    * @return A value 0-8 based on the slot currently selected
    */
   public int getCurrentSlot() {
      return player.inventory.currentItem;
   }

   /**
    * Sets the URL that the skin is retrieved from
    * 
    * @param URL - The URL the skin is taken from
    */
   public void setSkin(String URL) {
      //TODO: Set username? AbstractClientPlayer - player.skinUrl = URL;
   }

   /**
    * Sets the player's current motion
    * 
    * @param motion - The motion to set to the player
    */
   public void setMotion(Coordinate motion) {
      player.motionX = motion.getX();
      player.motionY = motion.getY();
      player.motionZ = motion.getZ();
   }

   /**
    * Gets the player's current motion
    * 
    * @return The motion of the player
    */
   public Coordinate getMotion() {
      return new Coordinate(player.motionX, player.motionY, player.motionZ);
   }

   /**
    * Checks if the specified position is able to contain a player. This
    * involves two squares at the player's height that are empty and one block
    * beneath the player's feet.
    * 
    * @param player - The player object
    * @param x - The X coordinate
    * @param y - The Y coordinate
    * @param z - The Z coordinate
    * @return True if the player can stand in the specified location
    */
   public boolean isClear(Coordinate location) {
      return getWorld().getBlockId(location.getBlockX(), location.getBlockY(), location.getBlockZ()) == 0
      && getWorld().getBlockId(location.getBlockX(), location.getBlockY() + 1, location.getBlockZ()) == 0
      && !(getWorld().getBlockId(location.getBlockX(), location.getBlockY() - 1, location.getBlockZ()) == 0);
   }

   /**
    * Checks if the specified position is able to contain a player. This
    * involves two squares at the player's height that are empty and one block
    * below the player being empty.
    * 
    * @param player - The player object
    * @param x - The X coordinate
    * @param y - The Y coordinate
    * @param z - The Z coordinate
    * @return True if the player can fall in the specified location
    */
   public boolean isClearBelow(Coordinate location) {
      return getWorld().getBlockId(location.getBlockX(), location.getBlockY(), location.getBlockZ()) == 0
      && getWorld().getBlockId(location.getBlockX(), location.getBlockY() + 1, location.getBlockZ()) == 0
      && getWorld().getBlockId(location.getBlockX(), location.getBlockY() - 1, location.getBlockZ()) == 0;
   }
   
   /**
    * Gets the movement forward. This is a value between -1 and 1. -1 is when
    * the player is moving backward, 1 is that player moving forward. The value
    * of the float is the percentage of speed the player is moving.
    * 
    * @return The movement forward percentage
    */
   public float getMovementForward() {
      if (player instanceof EntityPlayerMP) {
         return ((EntityPlayerMP) player).getMoveForwardField();
      } else if (player instanceof EntityClientPlayerMP) {
         return ((EntityClientPlayerMP) player).getMovementForward();
      } else {
         return 0F;
      }
   }

   /**
    * Gets the movement strafe. This is a value between -1 and 1. -1 is when
    * the player is moving right, 1 is that player moving left. The value
    * of the float is the percentage of speed the player is moving.
    * 
    * @return The movement strafe percentage
    */
   public float getMovementStrafe() {
      if (player instanceof EntityPlayerMP) {
         return ((EntityPlayerMP) player).getMoveStrafingField();
      } else if (player instanceof EntityClientPlayerMP) {
         return ((EntityClientPlayerMP) player).getMovementStrafe();
      } else {
         return 0F;
      }
   }

   /**
    * Sets the player's step height.
    * 
    * @param height - The height of the player's step
    */
   public void setStepHeight(float height) {
      player.stepHeight = height;
   }

   /**
    * Gets the step height of the player
    * 
    * @return Height (in blocks) of the player step height
    */
   public float getStepHeight() {
      return player.stepHeight;
   }

   /**
    * Gets the internal Minecraft player object
    * 
    * @return The Minecraft player object
    */
   public EntityPlayer getMinecraftPlayer() {
      return player;
   }

   /**
    * Removes the specified potion from the player
    * 
    * @param potion - The potion ID to remove
    */
   public void removePotionEffect(int potion) {
      player.removePotionEffect(potion);
   }

   /**
    * Removes all potion effects
    */
   public void removeAllPotionEffects() {
      player.clearActivePotions();
   }

   /**
    * Adds a potion effect to the player
    * 
    * @param id - The ID of the potion
    * @param duration - The duration the potion should run for
    * @param strength - The strength of effect to add
    */
   public void addPotionEffect(int id, int duration, int strength) {
      player.addPotionEffect(new PotionEffect(id, duration, strength));
   }

   /**
    * Changes the dimension that the player is in
    * 
    * @param dimension - The dimension to move the player to
    */
   public void changeDimension(int dimension) {
      player.travelToDimension(dimension);
   }

   /**
    * Sets the quantity of air that the player has
    * 
    * @param air - The quantity of air that the player has
    */
   public void setAir(int air) {
      player.setAir(air);
   }

   /**
    * Gives the specified achievement to the player. Note that this will only
    * work in client mode, server cannot give achievements to the player.
    * 
    * @param name - The name of the achievement
    * @return If the achievement was able to be added to the player
    */
   public boolean addAchievement(String name) {
      if (player instanceof EntityClientPlayerMP) {
         if (Stats.doesAchievementExist(name)) {
            player.triggerAchievement(Stats.getAchievementByName(name));
            return true;
         }
      }
      return false;
   }

   /**
    * Returns true if flying is permitted for the user, false otherwise
    * 
    * @return True if flying is permitted
    */
   public boolean getAllowFlying() {
      return player.capabilities.allowFlying;
   }

   /**
    * Allows and disables Minecraft flying mode
    * 
    * @param allow - True to allow flying, false to disable it
    */
   public void setAllowFlying(boolean allow) {
      player.capabilities.allowFlying = allow;
      player.capabilities.isFlying = allow;
      player.sendPlayerAbilities();
   }

   /**
    * Returns true if the player is currently in creative mode, false if in
    * survival or adventure mode.
    * 
    * @return True is in creative mode, false otherwise.
    */
   public boolean isCreativeMode() {
      return player.capabilities.isCreativeMode;
   }
   
   /**
    * Gets the players username
    * 
    * @return The players username
    */
   public String getUsername() {
      if (player instanceof EntityClientPlayerMP) {
         return ((EntityClientPlayerMP) player).getUsername();
      } else if (player instanceof EntityPlayerMP) {
         ((EntityPlayerMP) player).getUsername();
      }
      return "";
   }
}
import com.sijobe.spc.core.Constants;
import com.sijobe.spc.util.FontColour;
import com.sijobe.spc.util.KeyListener;
import com.sijobe.spc.util.KeyboardHandler;
import com.sijobe.spc.util.Settings;
import com.sijobe.spc.validation.Parameter;
import com.sijobe.spc.validation.ParameterInteger;
import com.sijobe.spc.validation.ParameterString;
import com.sijobe.spc.validation.Parameters;
import com.sijobe.spc.wrapper.CommandException;
import com.sijobe.spc.wrapper.CommandManager;
import com.sijobe.spc.wrapper.CommandSender;
import com.sijobe.spc.wrapper.Minecraft;
import com.sijobe.spc.wrapper.MinecraftServer;
import com.sijobe.spc.wrapper.Player;
import java.io.File;
import java.util.List;
import org.lwjgl.input.Keyboard;
    * to the server. 
    * 
    * @see com.sijobe.spc.wrapper.CommandBase#isEnabled()
    */
   @Override
   public boolean isEnabled() {
      return Minecraft.isSinglePlayer();
   }

   /**
    * @see com.sijobe.spc.command.MultipleCommands#getCommands()
    */
   @Override
   public String[] getCommands() {
      return new String[] {"bind", "unbind", "bindid", "unbindid"};
   }

   /**
    * @see com.sijobe.spc.wrapper.CommandBase#execute(com.sijobe.spc.wrapper.CommandSender, java.util.List)
    */
   @Override
   public void execute(CommandSender sender, List<?> params) throws CommandException {
      if (getName().equalsIgnoreCase("bind") || getName().equalsIgnoreCase("bindid")) {
         int keycode = Keyboard.KEY_NONE;
         if (getName().equalsIgnoreCase("bind")) {
            String key = (String)params.get(0);
            keycode = Keyboard.getKeyIndex(key.toUpperCase());
         } else {
            keycode = (Integer)params.get(0);
         }
         if (keycode == Keyboard.KEY_NONE) {
            throw new CommandException("Unknown keycode " + params.get(0));
         }
         String command = (String)params.get(1);
         String param = "";
         if (params.size() > 2) {
            param = (String)params.get(2);
         }
         if (BINDINGS.get(keycode + "") == null) {
            bind(keycode);
         }
         BINDINGS.set(keycode + "", command + " " + param);
         BINDINGS.save();
         sender.sendMessageToPlayer("Key " + FontColour.AQUA + params.get(0) + 
                  FontColour.WHITE + " was successfully bound.");
      } else if (getName().equalsIgnoreCase("unbind") || getName().equalsIgnoreCase("unbindid")) {
         int keycode = Keyboard.KEY_NONE;
         if (getName().equalsIgnoreCase("unbind")) {
            String key = (String)params.get(0);
            if (key.equalsIgnoreCase("all")) {
               BINDINGS.clear();
               BINDINGS.save();
               sender.sendMessageToPlayer("All bindings removed.");
               return;
            }
            keycode = Keyboard.getKeyIndex(key.toUpperCase());
         } else {
            keycode = (Integer)params.get(0);
         }
         if (keycode == Keyboard.KEY_NONE) {
            throw new CommandException("Unknown keycode " + params.get(0));
         }
         unbind(keycode);
         if (BINDINGS.remove(keycode + "") == null) {
            throw new CommandException("No binding was found for key " + FontColour.AQUA + (String)params.get(0));
         } else {
            BINDINGS.save();
            sender.sendMessageToPlayer("Binding " + FontColour.AQUA + params.get(0) + 
                     FontColour.WHITE + " was successfully removed.");
         }
      } else {
         assert false : "Invalid command " + getName();
      }
   }

   /**
    * Binds a key to a listener
    * 
    * @param key - The key to bind
    */
   private void bind(int key) {
      if (!KeyboardHandler.getInstance().addKeyPressedListener(key, INSTANCE)) {
         System.err.println("Invalid key specified in bindings: " + key);
         BINDINGS.remove(key);
      }
   }
   
   /**
    * Unbinds the key from the listener
    * 
    * @param key - The key to unbind
    */
   private void unbind(int key) {
      KeyboardHandler.getInstance().removeKeyPressedListener(key, INSTANCE);
   }

   @Override
   public Parameters getParameters() {
      if (getName().equalsIgnoreCase("bind")) {
         return BIND_PARAMS;
      } else if (getName().equalsIgnoreCase("unbind")) {
         return UNBIND_PARAMS;
      } else if (getName().equalsIgnoreCase("bindid")) {
         return BINDID_PARAMS;
      } else {
         assert false : "Invalid command name initialsied " + getName();
      return Parameters.DEFAULT;
      }
   }

   /**
    * Handles binding events
    * 
    * @see com.sijobe.spc.util.KeyListener#keyPressed(int)
    */
   @Override
   public void keyPressed(int key) {
      if (Minecraft.isGuiScreenOpen()) {
         return;
      }
      Player player = Minecraft.getPlayer();