Changed custom entity selectors from event based to factory based. (#3579)

This commit is contained in:
Max Becker 2017-05-02 03:36:24 +02:00 committed by LexManos
parent 6ffd94a08e
commit 34463690c5
7 changed files with 191 additions and 11 deletions

View file

@ -4,7 +4,7 @@
list2.addAll(func_184951_f(map));
list2.addAll(func_180698_a(map, vec3d));
list2.addAll(func_179662_g(map));
+ list2.addAll(net.minecraftforge.event.ForgeEventFactory.gatherEntitySelectors(map, s, p_179656_0_, vec3d));
+ list2.addAll(net.minecraftforge.fml.common.registry.GameRegistry.createEntitySelectors(map, s, p_179656_0_, vec3d));
list1.addAll(func_179660_a(map, p_179656_2_, list2, s, world, blockpos));
}
}

View file

@ -25,22 +25,18 @@ import net.minecraft.command.EntitySelector;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.IEntitySelectorFactory;
import net.minecraftforge.fml.common.eventhandler.Event;
import java.util.List;
import java.util.Map;
/**
* EntitySelectorEvent is fired whenever Minecraft collects entity selectors.
* This happens (one or multiple times) when you use something like @a[gamemode=1] in a command.<br>
* This event is fired via {@link ForgeEventFactory#gatherEntitySelectors(Map, String, ICommandSender, Vec3d)},
* which is executed in {@link net.minecraft.command.EntitySelector#matchEntities(ICommandSender, String, Class)}<br>
* <br>
* This event is not cancelable and does not have a result.<br>
* <br>
* This event is fired on the {@link MinecraftForge#EVENT_BUS}
* Is not fired anymore.
* Replaced by a factory, which has to be registered with {@link net.minecraftforge.fml.common.registry.GameRegistry#registerEntitySelector(IEntitySelectorFactory, String...)}
* TODO remove in 1.12
*/
@Deprecated
public class EntitySelectorEvent extends Event
{

View file

@ -253,6 +253,10 @@ public class ForgeEventFactory
return event.getDisplayname();
}
/**
* TODO remove in 1.12
*/
@Deprecated
public static List<Predicate<Entity>> gatherEntitySelectors(Map<String, String> map, String mainSelector, ICommandSender sender, Vec3d position)
{
EntitySelectorEvent event=new EntitySelectorEvent(map, mainSelector, sender, position);

View file

@ -0,0 +1,50 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* 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.fml.common;
import com.google.common.base.Predicate;
import net.minecraft.command.EntitySelector;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.Vec3d;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Allows mods to create custom selectors in commands.
* Registered in {@link net.minecraftforge.fml.common.registry.GameRegistry#registerEntitySelector(IEntitySelectorFactory, String...)}
* For an example implementation, see CustomEntitySelectorTest
*/
public interface IEntitySelectorFactory
{
/**
* Called every time a command that contains entity selectors is executed
*
* @param arguments A map with all arguments and their values
* @param mainSelector The main selector string (e.g. 'a' for all players or 'e' for all entities)
* @param sender The sender of the command
* @param position A position either specified in the selector arguments or by the players position. See {@link EntitySelector#getPosFromArguments(Map, Vec3d)}
* @return A list of new predicates, can be empty ({@link Collections#emptyList()} but not null.
*/
@Nonnull List<Predicate<Entity>> createPredicates(Map<String, String> arguments, String mainSelector, ICommandSender sender, Vec3d position);
}

View file

@ -32,7 +32,11 @@ import java.util.Map;
import java.util.Random;
import java.util.Set;
import com.google.common.base.Predicate;
import net.minecraft.block.Block;
import net.minecraft.command.EntitySelector;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.Entity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
@ -45,6 +49,7 @@ import net.minecraft.nbt.NBTException;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.chunk.IChunkGenerator;
import net.minecraft.world.chunk.IChunkProvider;
@ -55,6 +60,7 @@ import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.LoaderException;
import net.minecraftforge.fml.common.LoaderState;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.IEntitySelectorFactory;
import org.apache.logging.log4j.Level;
@ -76,6 +82,7 @@ public class GameRegistry
private static Map<IWorldGenerator, Integer> worldGeneratorIndex = Maps.newHashMap();
private static List<IFuelHandler> fuelHandlers = Lists.newArrayList();
private static List<IWorldGenerator> sortedGeneratorList;
private static List<IEntitySelectorFactory> entitySelectorFactories = Lists.newArrayList();
/**
* Register a world generator - something that inserts new block types into the world
@ -94,6 +101,49 @@ public class GameRegistry
}
}
/**
* Registers a entity selector factory which is used to create predicates whenever a command containing selectors is executed
* Any non vanilla arguments that you expect has to be registered. Otherwise Minecraft will throw an CommandException on usage.
*
* If you want to react to a command like "/kill @e[xyz=5]", you would have to register the argument "xyz" here and check for that argument in the factory.
* One factory can listen to any number of arguments as long as they are registered here.
*
* For inter mod compatibility you might want to use "modid:xyz" (e.g. "forge:min_health") as argument.
*
* For an example usage, see CustomEntitySelectorTest
* @param arguments Expected string arguments in commands
*/
public static void registerEntitySelector(IEntitySelectorFactory factory, String... arguments)
{
entitySelectorFactories.add(factory);
for (String s : arguments)
{
EntitySelector.addArgument(s);
}
}
/**
* Creates a list of entity selectors using the registered factories.
* Should probably only be called by Forge
*/
public static List<Predicate<Entity>> createEntitySelectors(Map<String, String> arguments, String mainSelector, ICommandSender sender, Vec3d position)
{
List<Predicate<Entity>> selectors = Lists.newArrayList();
for (IEntitySelectorFactory factory : entitySelectorFactories)
{
try
{
selectors.addAll(factory.createPredicates(arguments, mainSelector, sender, position));
}
catch (Exception e)
{
FMLLog.log(Level.ERROR, e, "Exception caught during entity selector creation with %s for argument map %s of %s for %s at %s", factory,
arguments, mainSelector, sender, position);
}
}
return selectors;
}
/**
* Callback hook for world gen - if your mod wishes to add extra mod related generation to the world
* call this

View file

@ -303,4 +303,7 @@ protected net.minecraft.entity.monster.EntityWitherSkeleton func_190727_o()Lnet/
protected net.minecraft.entity.monster.EntityStray func_190727_o()Lnet/minecraft/util/SoundEvent; # getStepSound - make AbstractSkeleton implementable
# EntitySkeleton
protected net.minecraft.entity.monster.EntitySkeleton func_190727_o()Lnet/minecraft/util/SoundEvent; # getStepSound - make AbstractSkeleton implementable
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

View file

@ -0,0 +1,77 @@
package net.minecraftforge.test;
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.common.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 CustomEntitySelectorTest
{
@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();
}
}
}