Minecraft Modding: Custom Server Commands

Introduction


While most modding is about changing game play, sometimes you want to give the player more power -- give them commands that allow them to "cheat".

In this tutorial I explain how create a custom command.  

I use the example of a "conjure" command that acts like summon but for my custom entities.  

The built in /summon command can call custom entities by pre-pending the mod id before the name of the entity, such as /summon wildanimals.Elephant.

However, for convenience and fun I don't like putting the modid in front so created my own equivalent command called "conjure" that spawns your named entity at the player's location. So this way I can just use /conjure Elephant.

In any case, it is useful to know how to create your own server command so here is an example for based on this conjure idea.

    Create Custom Command Class Implementing ICommand


    Follow these steps:
    1. Create a class that implements ICommand.
    2. Eclipse should warn you about unimplemented methods; accept the suggestion to implement the unimplemented methods.
    3. Then put your custom code inside those methods.  For example:

      public class CommandConjure implements ICommand
      { 
          private final List aliases;
        
          protected String fullEntityName; 
          protected Entity conjuredEntity; 
        
          public CommandConjure() 
          { 
              aliases = new ArrayList(); 
              aliases.add("conjure"); 
              aliases.add("conj"); 
          } 
        
          @Override 
          public int compareTo(Object o)
          { 
              return 0; 
          } 
      
          @Override 
          public String getCommandName() 
          { 
              return "conjure"; 
          } 
      
          @Override         
          public String getCommandUsage(ICommandSender var1) 
          { 
              return "conjure <text>"; 
          } 
      
          @Override 
          public List getCommandAliases() 
          { 
              return this.aliases;
          } 
      
          @Override 
          public void processCommand(ICommandSender sender, String[] argString)
          { 
              World world = sender.getEntityWorld(); 
          
              if (world.isRemote) 
              { 
                  System.out.println("Not processing on Client side"); 
              } 
              else 
              { 
                  System.out.println("Processing on Server side"); 
                  if(argString.length == 0) 
                  { 
                      sender.addChatMessage(new ChatComponentText("Invalid argument")); 
                      return; 
                  } 
          
                  sender.addChatMessage(new ChatComponentText("Conjuring: [" + argString[0]  
                        + "]")); 
           
                  fullEntityName = WildAnimals.MODID+"."+argString[0]; 
                  if (EntityList.stringToClassMapping.containsKey(fullEntityName))
                  { 
                      conjuredEntity = EntityList.createEntityByName(fullEntityName, world);  
                      conjuredEntity.setPosition(sender.getPlayerCoordinates().posX,       
                      sender.getPlayerCoordinates().posY, 
                      sender.getPlayerCoordinates().posZ); 
                      world.spawnEntityInWorld(conjuredEntity); 
                  } 
                  else 
                  { 
                      sender.addChatMessage(new ChatComponentText("Entity not found")); 
                  } 
              } 
          } 
      
          @Override 
          public boolean canCommandSenderUseCommand(ICommandSender var1) 
          { 
              return true;
          } 
      
          @Override  
          public List addTabCompletionOptions(ICommandSender var1, String[] var2) 
          { 
              // TODO Auto-generated method stub 
              return null; 
          } 
      
          @Override 
          public boolean isUsernameIndex(String[] var1, int var2) 
          { 
              // TODO Auto-generated method stub 
              return false;
          } 
      }

      There are a few things to note:

      • The method names have changed a bit in recent versions of Minecraft The example above was for 1.8 but in 1.12 things have changed for example:
        • getCommandName() is now just getName()
        • processCommand() is now execute().
        • If you implement the ICommand interface Eclipse should tell you the current name of all the methods you need to implement.
      • The actual command name is specified by the getCommandName() method, but you can also add aliases (other words that will also act to invoke the command).  
      • These aliases should be added in the command class constructor.


      Tip: People often use aliases to allow for abbreviated command names.

      The canCommandSenderUseCommand() method can be used to filter out which users are allowed to use the command.  In my case I wanted all users to be allowed so I returned true.

      The getCommandUsage() is important as it defines the format of the command.  For example, in this case in addition to the command itself I wanted to pass an additional text argument (in my case the name of the entity).

      The "real work" of the command happens in the processCommand() method.  Here you can do anything you want.  In my case I am using the additional text argument of the command and using it to look up registered entities.  If I find that there is an entity registered with that name I will spawn it at the player location, otherwise I post an error message to the chat.

      Tip: It is usually a good idea to send chat messages to indicate the success or failure of the command.

      Register Command In FMLServerStartingEvent In Your Main Mod Class


      Similar to how the preInit() and init() methods are put into the main class (or common proxy) of your mod, there is another "FML life cycle event" called the FMLServerStartingEvent that makes a good place to register the server commands.  You just need to annotate the handling method with the @EventHandler annotation.

      For example:

        @EventHandler
        public void serverLoad(FMLServerStartingEvent event)
        {
        
            // register server commands
        
        event.registerServerCommand(new CommandConjure()); }

        Conclusion


        I hope this helps you have fun creating custom commands.  Please feel free to contact me with any suggestions for corrections or clarifications!

        11 comments:

        1. This helped me greatly. Although in 1.8 there are different methods, but if you select add missing methods it becomes really to see what names have changed into what.

          ReplyDelete
        2. This was a beautifully written tutorial - thank you.

          ReplyDelete
        3. This comment has been removed by the author.

          ReplyDelete
        4. Just have one question. If I follow these instructions, will I be able to use the commands in single player as well? This is assuming I have my mod installed/active.

          ReplyDelete
          Replies
          1. Yes. In single player there is still both a server and client running. So it will operate in single player.

            Delete
          2. You said to contact you about changes and suggestions, so here is one.

            Include stuff related to command block use, such as making it output a redstone signal through comparators when the command runs (like /testfor does)

            Delete
        5. I followed the instructions but I'm having a problem, whenever I use the command it tells me the entity I want to spawn can't be found (but still, I have 1 of the entities right in front of me, spawned naturally of course). May I get some help please?

          ReplyDelete
        6. This comment has been removed by the author.

          ReplyDelete
        7. This comment has been removed by the author.

          ReplyDelete
        8. How do you make command blocks have an analog output when the command runs, like /testfor in vanilla?

          ReplyDelete
          Replies
          1. Figured it out, you throw a CommandException for a fail.

            Delete