Bonjour à tous et à toutes, me voici de retour pour un 2ème tutoriel. De plus, ce tutoriel est inédit car vous ne le trouverez qu’ici.
- Introduction
- La base de la commande
- Appliquer la/les commande(s)
- Allez plus loin
- Ressources + rendu final
- Questions/Réponses
La première étape consiste à déterminer le fonctionnement de la ou des commandes que vous allez utilisé.
Dans notre cas, nous allons utilisé une commande pour activer ou désactiver le spawn d’une entitée (le creeper plus précisément)
Classe Principale
Dans votre classe Principale, après :
| @EventHandler |
| public void PostInit(FMLPostInitializationEvent event) |
| { |
| |
| } |
Rajoutez :
| @EventHandler |
| public void serverStarting(FMLServerStartingEvent event) |
| { |
| |
| event.registerServerCommand(new CommandTutoriel()); |
| } |
N’oubliez pas d’importez FMLServerStartingEvent
Vous aurez une erreur à CommandTutoriel pour la résoudre, il vous suffit de créer la classe.
La classe CommandTutoriel
Voilà vous avez créé la classe CommandTutoriel, elle devrais ressembler à ceci :
| package tutoriel.common; |
| |
| import java.util.List; |
| |
| import net.minecraft.command.ICommand; |
| import net.minecraft.command.ICommandSender; |
| |
| public class CommandTutoriel implements ICommand { |
| |
| @Override |
| public int compareTo(Object arg0) { |
| |
| return 0; |
| } |
| |
| @Override |
| public String getCommandName() { |
| |
| return null; |
| } |
| |
| @Override |
| public String getCommandUsage(ICommandSender icommandsender) { |
| |
| return null; |
| } |
| |
| @Override |
| public List getCommandAliases() { |
| |
| return null; |
| } |
| |
| @Override |
| public void processCommand(ICommandSender icommandsender, String[] astring) { |
| |
| |
| } |
| |
| @Override |
| public boolean canCommandSenderUseCommand(ICommandSender icommandsender) { |
| |
| return false; |
| } |
| |
| @Override |
| public List addTabCompletionOptions(ICommandSender icommandsender, String[] astring) { |
| |
| return null; |
| } |
| |
| @Override |
| public boolean isUsernameIndex(String[] astring, int i) { |
| |
| return false; |
| } |
| |
| } |
Modifiez-la de manière à ce qu’elle ressemble à celle-ci :
| package tutoriel.common; |
| |
| import net.minecraft.command.CommandBase; |
| import net.minecraft.command.ICommandSender; |
| |
| public class CommandTutoriel extends CommandBase |
| { |
| @Override |
| public String getCommandName() |
| { |
| return null; |
| } |
| |
| @Override |
| public String getCommandUsage(ICommandSender sender) |
| { |
| return null; |
| } |
| |
| @Override |
| public void processCommand(ICommandSender sender, String[] arguments) |
| { |
| |
| } |
| } |
Dans la méthode getCommandName, à la place de:
return null;
Mettez
return "tutoriel";
Ceci va être le nom de la commande en jeu, donc /tutoriel dans notre cas.
Dans la méthode getCommandUsage, à la place de :
return null;
Mettez
return "commands.tutoriel.usage";
Si la commande est mal utilisé, ce texte sera envoyé au joueur. Nous allons l’ajouter en ressource externe dans les fichiers de langages.
Dans la méthode processCommand, rajoutez :
| if(arguments.length <= 0) |
| throw new WrongUsageException(this.getCommandUsage(sender)); |
| if(arguments[0].matches("creeper")) |
| { |
| if(arguments.length == 1) |
| { |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationKey("commands.tutoriel.usage").setColor(EnumChatFormatting.RED)); |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationKey("commands.tutoriel.creeper.usage").setColor(EnumChatFormatting.RED)); |
| } |
| else if(arguments[1].matches("enable")) |
| { |
| sender.getEntityWorld().getWorldInfo().getGameRulesInstance().setOrCreateGameRule("enableCreeper", "true"); |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationKey("commands.tutoriel.creeper.enable")); |
| } |
| else if(arguments[1].matches("disable")) |
| { |
| sender.getEntityWorld().getWorldInfo().getGameRulesInstance().setOrCreateGameRule("enableCreeper", "false"); |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationKey("commands.tutoriel.creeper.disable")); |
| } |
| else if(arguments[1].matches("help")) |
| { |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationKey("commands.tutoriel.usage")); |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationKey("commands.tutoriel.creeper.usage")); |
| } |
| else |
| { |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationKey("commands.tutoriel.creeper.invalid").setColor(EnumChatFormatting.RED)); |
| } |
| } |
| else if(arguments[0].matches("help")) |
| { |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationKey("commands.tutoriel.help")); |
| } |
| else |
| { |
| throw new WrongUsageException(this.getCommandUsage(sender)); |
| } |
arguments.length est le nombre de mot tapez après la commande. En fonction de sa taille, on fait une action. S’il y a aucun argument, alors on envoie une erreur pour dire que la commande est mal utilisé. Si la longueur est de 1 et que l’argument correspond à creeper alors on vérifie que la longueur des arguments est 1. Si oui, le joueur à juste entré /tutoriel creeper, donc on lui dit comment utiliser la commande. Sinon, la longueur est plus longue, donc si l’argument 2 correspond à enable, on enregistre un tag nbt dans le world info. Si c’est disable, même chose, mais on met le tag sur false. Si c’est help, on envoie l’aide et sinon l’argument 2 est invalide, on envoie une erreur.
Si l’argument 0 de la commande correspond à help, on envoie l’aide de la commande, sinon, c’est que l’argument 0 est invalide, on envoie donc une erreur.
Pour les effets de votre commande, grâce à sender.getEntityWorld() vous pouvez obtenir l’instance du monde du joueur qui a envoyé la commande, et avec ça on peut faire de nombreuses choses. Dans cette exemple, j’ai enregistré une clé dans le NBT des règles de jeu, pour l’instant il n’a pas d’effet, nous allons voir ça juste après.
Les commandes étant exécuter sur le side serveur, vous pouvez aussi utiliser tout ce qui vient du serveur.
Tapez juste MinecraftServer.getServer(). et faite ctrl + espace, vous allez voir de nombreuses fonctions et variables, vous pouvez toutes les utilisez pour faire votre effet (exemple d’utilisation plus bas).
Vous avez aussi pu remarquer que j’ai utilisé des sous-commandes (creeper et help), vous pouvez en ajouter d’autres avec des simples conditions.
Comme dit plus haut, pour l’instant ma commande enregistre juste un tag dans le NBT des gameRules, elle n’a pas d’autre effet.
Les events de forge vont donc nous être très utile. Dans la classe principale, dans ma fonction init, j’enregistre mon event :
MinecraftForge.EVENT_BUS.register(new EntityEvent());
Dans cette classe, j’ajoute :
| @ForgeSubscribe |
| public void onEntityJoinWorld(EntityJoinWorldEvent event) |
| { |
| if(event.entity instanceof EntityCreeper) |
| { |
| if(event.world.getWorldInfo().getGameRulesInstance().hasRule("enableCreeper")) |
| { |
| if(!event.world.getWorldInfo().getGameRulesInstance().getGameRuleBooleanValue("enableCreeper")) |
| { |
| event.setCanceled(true); |
| } |
| } |
| } |
| } |
| |
| @ForgeSubscribe |
| public void onLivingUpdate(LivingUpdateEvent event) |
| { |
| if(event.entity instanceof EntityCreeper) |
| { |
| if(event.entity.worldObj.getWorldInfo().getGameRulesInstance().hasRule("enableCreeper")) |
| { |
| if(!event.entity.worldObj.getWorldInfo().getGameRulesInstance().getGameRuleBooleanValue("enableCreeper")) |
| { |
| event.entity.setDead(); |
| } |
| } |
| } |
| } |
Le premier event est déclenché à chaque fois qu’une entité rejoint le monde, l’autre à chaque fois qu’une entité vivante est tické.
Dans les deux cas, je vérifie si l’entité est d’instance EntityCreeper (donc EntityCreeper lui même, et toutes les classes filles).
Je vérifie que le NBT des règles de jeu ont un tag nommé enableCreeper pour éviter tout risque de NullPointerException, si la commande n’a jamais été exécuté dans ce monde.
Si elle existe et qu’elle est sur false, alors j’annule l’event de spawn dans le cas du EntityJoinWorldEvent et je tue l’entité dans le cas du LivingUpdateEvent. Ma commande a donc maintenant un effet, si je mets sur false, les creepers ne pourront plus spawné, et ceux présent sur la map vont être tués (sans drop, le setDead(); supprime l’entitée)
L’auto-complétion
Vous avez créé vos premières commandes, mais l’utilisateur est obligé de connaître les commandes de tête, nous allons donc lui faciliter le travail en rajoutant la possibilité de taper les commandes via la touche TAB :
| public List addTabCompletionOptions(ICommandSender sender, String[] arguments) |
| { |
| return arguments.length == 1 ? getListOfStringsMatchingLastWord(arguments, new String[] {"creeper", "fire"}) : (arguments.length == 2 && arguments[0].matches("creeper") ? getListOfStringsMatchingLastWord(arguments, new String[] {"enable", "disable"}) : (arguments.length == 2 && arguments[0].matches("fire") ? getListOfStringsMatchingLastWord(arguments, this.getPlayers()) : null)); |
| } |
Le principe est exactement le même que la fonction processCommand. Ce sont à chaque fois des conditions (enfin une condition ternaire ici). Lorsqu’il y a plusieurs possibilités, il faut utilisé un tableau de string (new String[] {“exemple1”, “exemple2”, “exemple3”})
La commande fire
Un autre exemple de commande, celle-ci permet d’enflammer soit le joueur qui utilise la commande, soit un joueur défini. Ajoutez ce code avant le else if(arguments[0].matches(“help”))
| else if(arguments[0].matches("fire")) |
| { |
| if(arguments.length == 2) |
| { |
| EntityPlayerMP playermp = this.getCommandSenderAsPlayer(sender); |
| if(playermp != null) |
| { |
| playermp.setFire(this.parseInt(sender, arguments[1])); |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions("commands.tutoriel.fire.success", playermp.getEntityName(), arguments[1])); |
| } |
| else |
| { |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions("commands.tutoriel.fire.failure")); |
| } |
| } |
| else if(arguments.length == 3) |
| { |
| if(arguments[2].matches("help")) |
| { |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationKey("commands.tutoriel.usage")); |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions("commands.tutoriel.fire.usage.1")); |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions("commands.tutoriel.fire.usage.2")); |
| } |
| else |
| { |
| EntityPlayerMP playermp = MinecraftServer.getServer().getConfigurationManager().getPlayerForUsername(arguments[1]); |
| if(playermp != null) |
| { |
| playermp.setFire(this.parseInt(sender, arguments[2])); |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions("commands.tutoriel.fire.success", playermp.getEntityName(), arguments[2])); |
| } |
| else |
| { |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions("commands.tutoriel.fire.failure")); |
| } |
| } |
| } |
| else |
| { |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions("commands.tutoriel.usage").setColor(EnumChatFormatting.RED)); |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions("commands.tutoriel.fire.usage.1").setColor(EnumChatFormatting.RED)); |
| sender.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions("commands.tutoriel.fire.usage.2").setColor(EnumChatFormatting.RED)); |
| } |
| } |
Pensez à rajouter ceci :
| protected String[] getPlayers() |
| { |
| return MinecraftServer.getServer().getAllUsernames(); |
| } |
Cette commande est donc un exemple d’utilisation de MinecraftServer.getServer().quelque_chose
Dans vos fichiez de lang, voici ce que vous devez rajouter :
| |
| commands.tutoriel.usage=Usage : /tutoriel <subcommand><value> |
| commands.tutoriel.help=Available commands is creeper (Enable or disable creeper) and fire (burn a player) |
| commands.tutoriel.creeper.enable=Creeper enabled |
| commands.tutoriel.creeper.disable=Creeper disabled |
| commands.tutoriel.creeper.usage=- creeper <value>: Enable or disable creeper. <value>is "enable" or "disable" |
| commands.tutoriel.creeper.invalid=Invalid argument. |
| commands.tutoriel.fire.success=%1$s burn for %2$s secondes |
| commands.tutoriel.fire.failure=Player no found |
| commands.tutoriel.fire.usage.1=- fire <seconds>: burn you for <seconds> |
| commands.tutoriel.fire.usage.2=- fire <player><seconds>: burn <player>for <seconds> |
Voir le commit sur github
Rien pour l’instant.