From 8084ca43b92b5d2feeea56e9caa8a7a86f985755 Mon Sep 17 00:00:00 2001 From: m00nl1ght-dev <36201707+m00nl1ght-dev@users.noreply.github.com> Date: Sat, 9 Feb 2019 02:41:50 +0100 Subject: [PATCH] Port CommandEvent and custom entity selectors to 1.13 (#5411) --- .../net/minecraft/command/Commands.java.patch | 19 ++++ .../arguments/EntitySelectorParser.java.patch | 21 ++++ .../command/CommandHandler.java.patch | 20 ---- .../command/EntitySelector.java.patch | 46 --------- .../common/command/EntitySelectorManager.java | 97 ++++++++++++++++++ .../common/command/IEntitySelectorType.java | 44 +++++++++ .../minecraftforge/event/CommandEvent.java | 58 +++++++++++ .../resources/META-INF/accesstransformer.cfg | 8 +- .../debug/chat/CommandEventTest.java | 78 +++++++++++++++ .../debug/chat/EntitySelectorFactoryTest.java | 99 ------------------- .../debug/chat/EntitySelectorTest.java | 97 ++++++++++++++++++ src/test/resources/META-INF/mods.toml | 9 ++ 12 files changed, 429 insertions(+), 167 deletions(-) create mode 100644 patches/minecraft/net/minecraft/command/Commands.java.patch create mode 100644 patches/minecraft/net/minecraft/command/arguments/EntitySelectorParser.java.patch delete mode 100644 patches_old/minecraft/net/minecraft/command/CommandHandler.java.patch delete mode 100644 patches_old/minecraft/net/minecraft/command/EntitySelector.java.patch create mode 100644 src/main/java/net/minecraftforge/common/command/EntitySelectorManager.java create mode 100644 src/main/java/net/minecraftforge/common/command/IEntitySelectorType.java create mode 100644 src/main/java/net/minecraftforge/event/CommandEvent.java create mode 100644 src/test/java/net/minecraftforge/debug/chat/CommandEventTest.java delete mode 100644 src/test/java/net/minecraftforge/debug/chat/EntitySelectorFactoryTest.java create mode 100644 src/test/java/net/minecraftforge/debug/chat/EntitySelectorTest.java diff --git a/patches/minecraft/net/minecraft/command/Commands.java.patch b/patches/minecraft/net/minecraft/command/Commands.java.patch new file mode 100644 index 0000000000..e4f3fd2a33 --- /dev/null +++ b/patches/minecraft/net/minecraft/command/Commands.java.patch @@ -0,0 +1,19 @@ +--- a/net/minecraft/command/Commands.java ++++ b/net/minecraft/command/Commands.java +@@ -189,7 +189,15 @@ + + try { + try { +- int lvt_4_3_ = this.field_197062_b.execute(p_197059_2_, p_197059_1_); ++ com.mojang.brigadier.ParseResults parse = this.field_197062_b.parse(p_197059_2_, p_197059_1_); ++ net.minecraftforge.event.CommandEvent event = new net.minecraftforge.event.CommandEvent(parse); ++ if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(event)) { ++ if (event.getException() != null) { ++ com.google.common.base.Throwables.throwIfUnchecked(event.getException()); ++ } ++ return 1; ++ } ++ int lvt_4_3_ = this.field_197062_b.execute(event.getParseResults()); + return lvt_4_3_; + } catch (CommandException commandexception) { + p_197059_1_.func_197021_a(commandexception.func_197003_a()); diff --git a/patches/minecraft/net/minecraft/command/arguments/EntitySelectorParser.java.patch b/patches/minecraft/net/minecraft/command/arguments/EntitySelectorParser.java.patch new file mode 100644 index 0000000000..2c761fd895 --- /dev/null +++ b/patches/minecraft/net/minecraft/command/arguments/EntitySelectorParser.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/command/arguments/EntitySelectorParser.java ++++ b/net/minecraft/command/arguments/EntitySelectorParser.java +@@ -441,6 +441,10 @@ + } + + this.field_197417_j.skip(); ++ EntitySelector forgeSelector = net.minecraftforge.common.command.EntitySelectorManager.parseSelector(this); ++ if (forgeSelector != null) { ++ return forgeSelector; ++ } + this.func_197403_b(); + } else { + this.func_197382_c(); +@@ -456,6 +460,7 @@ + p_210326_0_.suggest("@r", new TextComponentTranslation("argument.entity.selector.randomPlayer", new Object[0])); + p_210326_0_.suggest("@s", new TextComponentTranslation("argument.entity.selector.self", new Object[0])); + p_210326_0_.suggest("@e", new TextComponentTranslation("argument.entity.selector.allEntities", new Object[0])); ++ net.minecraftforge.common.command.EntitySelectorManager.fillSelectorSuggestions(p_210326_0_); + } + + private CompletableFuture func_201981_b(SuggestionsBuilder p_201981_1_, Consumer p_201981_2_) { diff --git a/patches_old/minecraft/net/minecraft/command/CommandHandler.java.patch b/patches_old/minecraft/net/minecraft/command/CommandHandler.java.patch deleted file mode 100644 index de6db71b37..0000000000 --- a/patches_old/minecraft/net/minecraft/command/CommandHandler.java.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- ../src-base/minecraft/net/minecraft/command/CommandHandler.java -+++ ../src-work/minecraft/net/minecraft/command/CommandHandler.java -@@ -50,6 +50,17 @@ - } - else if (icommand.func_184882_a(this.func_184879_a(), p_71556_1_)) - { -+ net.minecraftforge.event.CommandEvent event = new net.minecraftforge.event.CommandEvent(icommand, p_71556_1_, astring); -+ if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(event)) -+ { -+ if (event.getException() != null) -+ { -+ com.google.common.base.Throwables.throwIfUnchecked(event.getException()); -+ } -+ return 1; -+ } -+ if (event.getParameters() != null) astring = event.getParameters(); -+ - if (j > -1) - { - List list = EntitySelector.func_179656_b(p_71556_1_, astring[j], Entity.class); diff --git a/patches_old/minecraft/net/minecraft/command/EntitySelector.java.patch b/patches_old/minecraft/net/minecraft/command/EntitySelector.java.patch deleted file mode 100644 index dd80d47d1e..0000000000 --- a/patches_old/minecraft/net/minecraft/command/EntitySelector.java.patch +++ /dev/null @@ -1,46 +0,0 @@ ---- ../src-base/minecraft/net/minecraft/command/EntitySelector.java -+++ ../src-work/minecraft/net/minecraft/command/EntitySelector.java -@@ -121,6 +121,11 @@ - - public static List func_179656_b(ICommandSender p_179656_0_, String p_179656_1_, Class p_179656_2_) throws CommandException - { -+ return net.minecraftforge.common.command.SelectorHandlerManager.matchEntities(p_179656_0_, p_179656_1_, p_179656_2_); -+ } -+ -+ public static List matchEntitiesDefault(ICommandSender p_179656_0_, String p_179656_1_, Class p_179656_2_) throws CommandException -+ { - Matcher matcher = field_82389_a.matcher(p_179656_1_); - - if (matcher.matches() && p_179656_0_.func_70003_b(1, "@")) -@@ -153,6 +158,7 @@ - list2.addAll(func_184951_f(map)); - list2.addAll(func_180698_a(map, vec3d)); - list2.addAll(func_179662_g(map)); -+ list2.addAll(net.minecraftforge.fml.common.registry.GameRegistry.createEntitySelectors(map, s, p_179656_0_, vec3d)); - - if ("s".equalsIgnoreCase(s)) - { -@@ -786,6 +792,11 @@ - - public static boolean func_82377_a(String p_82377_0_) throws CommandException - { -+ return net.minecraftforge.common.command.SelectorHandlerManager.matchesMultiplePlayers(p_82377_0_); -+ } -+ -+ public static boolean matchesMultiplePlayersDefault(String p_82377_0_) throws CommandException -+ { - Matcher matcher = field_82389_a.matcher(p_82377_0_); - - if (!matcher.matches()) -@@ -803,6 +814,11 @@ - - public static boolean func_82378_b(String p_82378_0_) - { -+ return net.minecraftforge.common.command.SelectorHandlerManager.isSelector(p_82378_0_); -+ } -+ -+ public static boolean isSelectorDefault(String p_82378_0_) -+ { - return field_82389_a.matcher(p_82378_0_).matches(); - } - diff --git a/src/main/java/net/minecraftforge/common/command/EntitySelectorManager.java b/src/main/java/net/minecraftforge/common/command/EntitySelectorManager.java new file mode 100644 index 0000000000..e9c0890594 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/command/EntitySelectorManager.java @@ -0,0 +1,97 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.command; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.command.arguments.EntitySelector; +import net.minecraft.command.arguments.EntitySelectorParser; + +import java.util.Arrays; +import java.util.HashMap; + +/** + * Allows modders to register custom entity selectors by assigning an {@link IEntitySelectorType} to a String token.
+ * The token "test", for example, corresponds to @test[...] in a command. + */ +public class EntitySelectorManager +{ + private static final HashMap REGISTRY = new HashMap<>(); + + /** + * Registers a new {@link IEntitySelectorType} for the given {@code token}.
+ * + * @param token Defines the name of the selector + */ + public static void register(String token, IEntitySelectorType type) + { + if (token.isEmpty()) + { + throw new IllegalArgumentException("Token must not be empty"); + } + + if (Arrays.asList("p", "a", "r", "s", "e").contains(token)) + { + throw new IllegalArgumentException("Token clashes with vanilla @" + token); + } + + for (char c : token.toCharArray()) + { + if (!StringReader.isAllowedInUnquotedString(c)) { + throw new IllegalArgumentException("Token must only contain allowed characters"); + } + } + + REGISTRY.put(token, type); + } + + /** + * This method is called in {@link EntitySelectorParser#parse}
+ * + * If the REGISTRY does not contain a custom selector for the command being parsed, + * this method returns {@code null} and the vanilla logic in {@link EntitySelectorParser#parseSelector} is used. + */ + public static EntitySelector parseSelector(EntitySelectorParser parser) throws CommandSyntaxException + { + if (parser.getReader().canRead()) + { + int i = parser.getReader().getCursor(); + String token = parser.getReader().readUnquotedString(); + IEntitySelectorType type = REGISTRY.get(token); + if (type != null) + { + return type.build(parser); + } + + parser.getReader().setCursor(i); + } + + return null; + } + + /** + * This method is called in {@link EntitySelectorParser#fillSelectorSuggestions} + */ + public static void fillSelectorSuggestions(SuggestionsBuilder suggestionBuilder) + { + REGISTRY.forEach((token, type) -> suggestionBuilder.suggest("@" + token, type.getSuggestionTooltip())); + } +} diff --git a/src/main/java/net/minecraftforge/common/command/IEntitySelectorType.java b/src/main/java/net/minecraftforge/common/command/IEntitySelectorType.java new file mode 100644 index 0000000000..18c61eaed2 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/command/IEntitySelectorType.java @@ -0,0 +1,44 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.command; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.command.arguments.EntitySelector; +import net.minecraft.command.arguments.EntitySelectorParser; +import net.minecraft.util.text.ITextComponent; + +/** + * Implementations of this interface can be registered using {@link EntitySelectorManager#register} + */ +public interface IEntitySelectorType +{ + /** + * Returns an {@link EntitySelector} based on the given {@link EntitySelectorParser}.
+ * + * Use {@link EntitySelectorParser#getReader} to read extra arguments and {@link EntitySelectorParser#addFilter} to add the corresponding filters.
+ * If the token being parsed does not match the syntax of this selector, this method should throw an appropriate {@link CommandSyntaxException}. + */ + EntitySelector build(EntitySelectorParser parser) throws CommandSyntaxException; + + /** + * Returns an {@link ITextComponent} containing a short description for this selector type. + */ + ITextComponent getSuggestionTooltip(); +} diff --git a/src/main/java/net/minecraftforge/event/CommandEvent.java b/src/main/java/net/minecraftforge/event/CommandEvent.java new file mode 100644 index 0000000000..4a1ff611a7 --- /dev/null +++ b/src/main/java/net/minecraftforge/event/CommandEvent.java @@ -0,0 +1,58 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.event; + +import com.mojang.brigadier.ParseResults; +import net.minecraft.command.CommandSource; +import net.minecraft.command.Commands; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.Cancelable; +import net.minecraftforge.eventbus.api.Event; + +/** + * CommandEvent is fired after a command is parsed, but before it is executed. + * This event is fired during the invocation of {@link Commands#handleCommand(CommandSource, String)}.
+ *
+ * {@link #parse} contains the instance of {@link ParseResults} for the parsed command.
+ * {@link #exception} begins null, but can be populated with an exception to be thrown within the command.
+ *
+ * This event is {@link Cancelable}.
+ * If the event is canceled, the execution of the command does not occur.
+ *
+ * This event does not have a result. {@link HasResult}
+ *
+ * This event is fired on the {@link MinecraftForge#EVENT_BUS}.
+ **/ +@Cancelable +public class CommandEvent extends Event +{ + private ParseResults parse; + private Throwable exception; + + public CommandEvent(ParseResults parse) + { + this.parse = parse; + } + + public ParseResults getParseResults() { return parse; } + public void setParseResults(ParseResults parse) { this.parse = parse; } + public Throwable getException() { return exception; } + public void setException(Throwable exception) { this.exception = exception; } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index ac1e57dfb8..50323554e5 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -344,8 +344,12 @@ protected net.minecraft.entity.monster.EntityStray func_190727_o()Lnet/minecraft # EntitySkeleton protected net.minecraft.entity.monster.EntitySkeleton func_190727_o()Lnet/minecraft/util/SoundEvent; # getStepSound - make AbstractSkeleton implementable -# EntitySelector -public net.minecraft.command.EntitySelector func_190826_c(Ljava/lang/String;)Ljava/lang/String; # addArgument +# EntitySelectorParser +public net.minecraft.command.arguments.EntitySelectorParser func_197396_n()V # updateFilter +public net.minecraft.command.arguments.EntitySelectorParser func_197404_d()V # parseArguments + +# EntityOptions +public net.minecraft.command.arguments.EntityOptions func_202024_a(Ljava/lang/String;Lnet/minecraft/command/arguments/EntityOptions$Filter;Ljava/util/function/Predicate;Lnet/minecraft/util/text/ITextComponent;)V # register # Teleporter protected net.minecraft.world.Teleporter field_85192_a # world diff --git a/src/test/java/net/minecraftforge/debug/chat/CommandEventTest.java b/src/test/java/net/minecraftforge/debug/chat/CommandEventTest.java new file mode 100644 index 0000000000..42eedec6af --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/chat/CommandEventTest.java @@ -0,0 +1,78 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.debug.chat; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.tree.CommandNode; +import net.minecraft.command.CommandException; +import net.minecraft.command.CommandSource; +import net.minecraft.util.text.TextComponentString; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.CommandEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +@Mod("command_event_test") +public class CommandEventTest +{ + public final Logger LOGGER = LogManager.getLogger(); + + public CommandEventTest() + { + MinecraftForge.EVENT_BUS.register(this); + } + + @SubscribeEvent + public void onCommand(CommandEvent event) + { + CommandDispatcher dispatcher = event.getParseResults().getContext().getDispatcher(); + List> nodes = new ArrayList<>(event.getParseResults().getContext().getNodes().keySet()); + CommandSource source = event.getParseResults().getContext().getSource(); + + // test: when the /time command is used with no arguments, automatically add default arguments (/time set day) + if (nodes.size() == 1 && nodes.get(0) == dispatcher.getRoot().getChild("time")) + { + event.setParseResults(dispatcher.parse("time set day", source)); + return; + } + + // test: whenever a player uses the /give command, let everyone on the server know + if (nodes.size() > 0 && nodes.get(0) == dispatcher.getRoot().getChild("give")) + { + String msg = source.getName() + " used the give command: " + event.getParseResults().getReader().getString(); + source.getServer().getPlayerList().getPlayers().forEach(entityPlayerMP -> entityPlayerMP.sendMessage(new TextComponentString(msg))); + return; + } + + // test: when the /kill command is used with no arguments, throw a custom exception + if (nodes.size() == 1 && nodes.get(0) == dispatcher.getRoot().getChild("kill")) + { + event.setException(new CommandException(new TextComponentString("You tried to use the /kill command with no arguments"))); + event.setCanceled(true); + return; + } + } + +} diff --git a/src/test/java/net/minecraftforge/debug/chat/EntitySelectorFactoryTest.java b/src/test/java/net/minecraftforge/debug/chat/EntitySelectorFactoryTest.java deleted file mode 100644 index 7b556bd313..0000000000 --- a/src/test/java/net/minecraftforge/debug/chat/EntitySelectorFactoryTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2018. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation version 2.1 - * of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.debug.chat; - -import com.google.common.base.Predicate; -import net.minecraft.command.ICommandSender; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.text.TextComponentString; -import net.minecraftforge.fml.common.IEntitySelectorFactory; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.FMLInitializationEvent; -import net.minecraftforge.fml.common.registry.GameRegistry; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * You can verify that this test is working fine by following these steps. - * Spawn a few cows and a few sheep and then use "/kill @e[forge:min_health=!10]" - * All entities with less than 10 health points (current, not max) should die. - * This should include the sheep, but not the (healthy) cows. - */ -@Mod(modid = "customentityselectortest", name = "Custom Entity Selector Test", version = "1.0", acceptableRemoteVersions = "*") -public class EntitySelectorFactoryTest -{ - - @Mod.EventHandler - public void init(FMLInitializationEvent event) - { - GameRegistry.registerEntitySelector(new EntitySelectorFactory(), "forge:min_health"); - } - - private class EntitySelectorFactory implements IEntitySelectorFactory - { - - @Nonnull - @Override - public List> createPredicates(Map arguments, String mainSelector, ICommandSender sender, Vec3d position) - { - String health = arguments.get("forge:min_health"); - - //If our selector is used in this command create a Predicate otherwise return an empty list. - if (health != null) - { - final boolean invert = health.startsWith("!"); - - if (invert) - { - health = health.substring(1); - } - try - { - final int value = Integer.parseInt(health); - - //Return a list of predicates. All these predicates have to apply for any entity to be selected. - return Collections.>singletonList(new Predicate() - { - @Override - public boolean apply(@Nullable Entity input) - { - if (!(input instanceof EntityLivingBase)) - { - return false; - } - return (((EntityLivingBase) input).getHealth() >= value) != invert; - } - }); - } - catch (NumberFormatException e) - { - sender.sendMessage(new TextComponentString("Entity selector 'forge:min_health' has to be an integer")); - } - } - return Collections.emptyList(); - } - } -} diff --git a/src/test/java/net/minecraftforge/debug/chat/EntitySelectorTest.java b/src/test/java/net/minecraftforge/debug/chat/EntitySelectorTest.java new file mode 100644 index 0000000000..5ca40d41d2 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/chat/EntitySelectorTest.java @@ -0,0 +1,97 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.debug.chat; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.advancements.criterion.MinMaxBounds; +import net.minecraft.command.arguments.EntityOptions; +import net.minecraft.command.arguments.EntitySelector; +import net.minecraft.command.arguments.EntitySelectorParser; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import net.minecraftforge.common.command.EntitySelectorManager; +import net.minecraftforge.common.command.IEntitySelectorType; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLModLoadingContext; + +@Mod("entity_selector_test") +public class EntitySelectorTest +{ + public EntitySelectorTest() + { + FMLModLoadingContext.get().getModEventBus().addListener(this::setup); + } + + public void setup(FMLCommonSetupEvent event) + { + EntityOptions.register("health", this::healthArgument, parser -> true, + new TextComponentString("Selects entities based on their current health.")); + EntitySelectorManager.register("er", new ExampleCustomSelector()); + } + + /** + * Example for a custom selector argument, checks for the health of the entity + */ + private void healthArgument(EntitySelectorParser parser) throws CommandSyntaxException + { + MinMaxBounds.FloatBound bound = MinMaxBounds.FloatBound.fromReader(parser.getReader()); + parser.addFilter(entity -> entity instanceof EntityLivingBase && bound.test(((EntityLivingBase) entity).getHealth())); + } + + /** + * Example for a custom selector type, works like @r but for entities. + * Basically does exactly what @e[sorter=random, limit=1, ...] does. + */ + private class ExampleCustomSelector implements IEntitySelectorType + { + @Override + public EntitySelector build(EntitySelectorParser parser) throws CommandSyntaxException + { + parser.setSorter(EntitySelectorParser.RANDOM); + parser.setLimit(1); + parser.setIncludeNonPlayers(true); + parser.addFilter(Entity::isAlive); + parser.setSuggestionHandler((builder, consumer) -> builder.suggest(String.valueOf('[')).buildFuture()); + if (parser.getReader().canRead() && parser.getReader().peek() == '[') + { + parser.getReader().skip(); + parser.setSuggestionHandler((builder, consumer) -> { + builder.suggest(String.valueOf(']')); + EntityOptions.suggestOptions(parser, builder); + return builder.buildFuture(); + }); + + parser.parseArguments(); + } + + parser.updateFilter(); + return parser.build(); + } + + @Override + public ITextComponent getSuggestionTooltip() + { + return new TextComponentString("Example: Selects a random entity"); + } + } +} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index 8c48762439..843de44dba 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -74,3 +74,12 @@ key="value" modId="enumplanttypetest" version="1.0" displayName="EnumPlantTypeTest" +# debug/chat +[[mods]] + modId="command_event_test" + version="1.0" + displayName="CommandEventTest" +[[mods]] + modId="entity_selector_test" + version="1.0" + displayName="EntitySelectorTest"