Port CommandEvent and custom entity selectors to 1.13 (#5411)

This commit is contained in:
m00nl1ght-dev 2019-02-09 02:41:50 +01:00 committed by LexManos
parent 2e5f25d8ae
commit 8084ca43b9
12 changed files with 429 additions and 167 deletions

View File

@ -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<CommandSource> 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());

View File

@ -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<Suggestions> func_201981_b(SuggestionsBuilder p_201981_1_, Consumer<SuggestionsBuilder> p_201981_2_) {

View File

@ -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<Entity> list = EntitySelector.<Entity>func_179656_b(p_71556_1_, astring[j], Entity.class);

View File

@ -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 <T extends Entity> List<T> func_179656_b(ICommandSender p_179656_0_, String p_179656_1_, Class <? extends T > p_179656_2_) throws CommandException
{
+ return net.minecraftforge.common.command.SelectorHandlerManager.matchEntities(p_179656_0_, p_179656_1_, p_179656_2_);
+ }
+
+ public static <T extends Entity> List<T> matchEntitiesDefault(ICommandSender p_179656_0_, String p_179656_1_, Class <? extends T > 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();
}

View File

@ -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. <br>
* The token "test", for example, corresponds to @test[...] in a command.
*/
public class EntitySelectorManager
{
private static final HashMap<String, IEntitySelectorType> REGISTRY = new HashMap<>();
/**
* Registers a new {@link IEntitySelectorType} for the given {@code token}.<br>
*
* @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} <br>
*
* 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()));
}
}

View File

@ -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}. <br>
*
* Use {@link EntitySelectorParser#getReader} to read extra arguments and {@link EntitySelectorParser#addFilter} to add the corresponding filters. <br>
* 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();
}

View File

@ -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)}. <br>
* <br>
* {@link #parse} contains the instance of {@link ParseResults} for the parsed command.<br>
* {@link #exception} begins null, but can be populated with an exception to be thrown within the command.<br>
* <br>
* This event is {@link Cancelable}. <br>
* If the event is canceled, the execution of the command does not occur.<br>
* <br>
* This event does not have a result. {@link HasResult}<br>
* <br>
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.<br>
**/
@Cancelable
public class CommandEvent extends Event
{
private ParseResults<CommandSource> parse;
private Throwable exception;
public CommandEvent(ParseResults<CommandSource> parse)
{
this.parse = parse;
}
public ParseResults<CommandSource> getParseResults() { return parse; }
public void setParseResults(ParseResults<CommandSource> parse) { this.parse = parse; }
public Throwable getException() { return exception; }
public void setException(Throwable exception) { this.exception = exception; }
}

View File

@ -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

View File

@ -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<CommandSource> dispatcher = event.getParseResults().getContext().getDispatcher();
List<CommandNode<CommandSource>> 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;
}
}
}

View File

@ -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<Predicate<Entity>> createPredicates(Map<String, String> 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.<Predicate<Entity>>singletonList(new Predicate<Entity>()
{
@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();
}
}
}

View File

@ -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");
}
}
}

View File

@ -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"