From 15ac7a4621fdbb645190eb97833629f80f10d174 Mon Sep 17 00:00:00 2001 From: gnembon Date: Thu, 29 Oct 2020 11:26:59 -0300 Subject: [PATCH] entity actions --- .../PlayerEntity_scarpetEventsMixin.java | 2 +- ...PlayNetworkHandler_scarpetEventsMixin.java | 4 +- .../mixins/ServerWorld_scarpetEventMixin.java | 24 ++++++++ .../java/carpet/script/CarpetEventServer.java | 36 +++++++++-- .../java/carpet/script/api/Auxiliary.java | 2 +- src/main/java/carpet/script/api/Entities.java | 13 ++++ .../script/argument/FunctionArgument.java | 2 +- .../carpet/script/language/Functions.java | 2 +- .../carpet/script/language/Threading.java | 4 +- .../java/carpet/script/value/EntityValue.java | 59 +++++++++++++++++++ 10 files changed, 136 insertions(+), 12 deletions(-) diff --git a/src/main/java/carpet/mixins/PlayerEntity_scarpetEventsMixin.java b/src/main/java/carpet/mixins/PlayerEntity_scarpetEventsMixin.java index a69f9b4b..e997f533 100644 --- a/src/main/java/carpet/mixins/PlayerEntity_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/PlayerEntity_scarpetEventsMixin.java @@ -50,7 +50,7 @@ public abstract class PlayerEntity_scarpetEventsMixin extends LivingEntity { if (PLAYER_COLLIDES_WITH_ENTITY.isNeeded() && !world.isClient) { - PLAYER_COLLIDES_WITH_ENTITY.onEntityAction((ServerPlayerEntity)(Object)this, entity, null); + PLAYER_COLLIDES_WITH_ENTITY.onEntityHandAction((ServerPlayerEntity)(Object)this, entity, null); } } } diff --git a/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java index 8ab6fd25..1f8a099b 100644 --- a/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java @@ -224,7 +224,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onEntityInteract(PlayerInteractEntityC2SPacket playerInteractEntityC2SPacket_1, CallbackInfo ci) { - PLAYER_INTERACTS_WITH_ENTITY.onEntityAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), playerInteractEntityC2SPacket_1.getHand()); + PLAYER_INTERACTS_WITH_ENTITY.onEntityHandAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), playerInteractEntityC2SPacket_1.getHand()); } @Inject(method = "onPlayerInteractEntity", at = @At( @@ -234,7 +234,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin private void onEntityAttack(PlayerInteractEntityC2SPacket playerInteractEntityC2SPacket_1, CallbackInfo ci) { //todo add hit and hand in the future - PLAYER_ATTACKS_ENTITY.onEntityAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), null); + PLAYER_ATTACKS_ENTITY.onEntityHandAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), null); } @Inject(method = "onButtonClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;updateLastActionTime()V")) diff --git a/src/main/java/carpet/mixins/ServerWorld_scarpetEventMixin.java b/src/main/java/carpet/mixins/ServerWorld_scarpetEventMixin.java index 03bae204..5c11338a 100644 --- a/src/main/java/carpet/mixins/ServerWorld_scarpetEventMixin.java +++ b/src/main/java/carpet/mixins/ServerWorld_scarpetEventMixin.java @@ -1,5 +1,8 @@ package carpet.mixins; +import carpet.CarpetSettings; +import carpet.script.CarpetEventServer; +import net.minecraft.entity.Entity; import net.minecraft.entity.LightningEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; @@ -12,6 +15,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import static carpet.script.CarpetEventServer.Event.ENTITY_LOAD; import static carpet.script.CarpetEventServer.Event.LIGHTNING; @Mixin(ServerWorld.class) @@ -30,4 +34,24 @@ public class ServerWorld_scarpetEventMixin if (LIGHTNING.isNeeded()) LIGHTNING.onWorldEventFlag((ServerWorld) (Object)this, blockPos, bl2?1:0); } + @Inject(method = "loadEntityUnchecked", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/world/ServerChunkManager;loadEntity(Lnet/minecraft/entity/Entity;)V" + )) + private void onEntityAddedToWorld(Entity entity, CallbackInfo ci) + { + CarpetEventServer.Event event = ENTITY_LOAD.get(entity.getType()); + if (event != null) + { + if (event.isNeeded()) + { + event.onEntityAction(entity); + } + } + else + { + CarpetSettings.LOG.error("Failed to handle entity "+entity.getType().getTranslationKey()); + } + } + } diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index c4b8ff25..256620bb 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -13,6 +13,7 @@ import carpet.script.value.StringValue; import carpet.script.value.Value; import carpet.utils.Messenger; import net.minecraft.block.BlockState; +import net.minecraft.entity.EntityType; import net.minecraft.entity.damage.DamageSource; import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.ServerCommandSource; @@ -211,6 +212,11 @@ public class CarpetEventServer public static class Event { public static final Map byName = new HashMap<>(); + public static List publicEvents() + { + return byName.entrySet().stream().filter(e -> e.getValue().isPublic).map(Map.Entry::getKey).collect(Collectors.toList()); + } + public static final Event TICK = new Event("tick", 0, true) { @Override @@ -407,7 +413,7 @@ public class CarpetEventServer public static final Event PLAYER_INTERACTS_WITH_ENTITY = new Event("player_interacts_with_entity", 3, false) { @Override - public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) + public void onEntityHandAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { handler.call( () -> Arrays.asList( ((c, t) -> new EntityValue(player)), @@ -434,7 +440,7 @@ public class CarpetEventServer public static final Event PLAYER_ATTACKS_ENTITY = new Event("player_attacks_entity", 2, false) { @Override - public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) + public void onEntityHandAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { handler.call( () -> Arrays.asList( ((c, t) -> new EntityValue(player)), @@ -596,7 +602,7 @@ public class CarpetEventServer public static final Event PLAYER_COLLIDES_WITH_ENTITY = new Event("player_collides_with_entity", 2, false) { @Override - public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { + public void onEntityHandAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { handler.call( () -> Arrays.asList( ((c, t) -> new EntityValue(player)), ((c, t) -> new EntityValue(entity)) @@ -695,17 +701,38 @@ public class CarpetEventServer } }; + + public static final Map, Event> ENTITY_LOAD= new HashMap, Event>() {{ + EntityType.get("zombie"); + Registry.ENTITY_TYPE.forEach(et -> { + put(et, new Event("entity_loaded_" + Registry.ENTITY_TYPE.getId(et), 1, true, false) + { + @Override + public void onEntityAction(Entity entity) + { + super.onEntityAction(entity); + } + }); + }); + }}; + // on projectile thrown (arrow from bows, crossbows, tridents, snoballs, e-pearls public final String name; public final CallbackList handler; public final boolean globalOnly; + public final boolean isPublic; // public events can be targetted with __on_ defs public Event(String name, int reqArgs, boolean isGlobalOnly) + { + this(name, reqArgs, isGlobalOnly, true); + } + public Event(String name, int reqArgs, boolean isGlobalOnly, boolean isPublic) { this.name = name; this.handler = new CallbackList(reqArgs); this.globalOnly = isGlobalOnly; + this.isPublic = isPublic; byName.put(name, this); } public boolean isNeeded() @@ -724,7 +751,8 @@ public class CarpetEventServer public void onBlockHit(ServerPlayerEntity player, Hand enumhand, BlockHitResult hitRes) { } public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS) { } public void onBlockPlaced(ServerPlayerEntity player, BlockPos pos, Hand enumhand, ItemStack itemstack) { } - public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { } + public void onEntityHandAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { } + public void onEntityAction(Entity entity) { } public void onDimensionChange(ServerPlayerEntity player, Vec3d from, Vec3d to, RegistryKey fromDim, RegistryKey dimTo) {} public void onDamage(Entity target, float amount, DamageSource source) { } public void onRecipeSelected(ServerPlayerEntity player, Identifier recipe, boolean fullStack) {} diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java index e0b67829..bbdcbfdc 100644 --- a/src/main/java/carpet/script/api/Auxiliary.java +++ b/src/main/java/carpet/script/api/Auxiliary.java @@ -640,7 +640,7 @@ public class Auxiliary { throw new InternalExpressionException("'schedule' should have at least 2 arguments, delay and call name"); long delay = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong(); - FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, true); + FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, true, false); CarpetServer.scriptServer.events.scheduleCall( (CarpetContext) c, diff --git a/src/main/java/carpet/script/api/Entities.java b/src/main/java/carpet/script/api/Entities.java index e8f92b00..d504f657 100644 --- a/src/main/java/carpet/script/api/Entities.java +++ b/src/main/java/carpet/script/api/Entities.java @@ -1,8 +1,10 @@ package carpet.script.api; import carpet.script.CarpetContext; +import carpet.script.CarpetScriptHost; import carpet.script.Expression; import carpet.script.LazyValue; +import carpet.script.argument.FunctionArgument; import carpet.script.argument.Vector3Argument; import carpet.script.exception.InternalExpressionException; import carpet.script.value.EntityValue; @@ -266,6 +268,16 @@ public class Entities { return (cc, tt) -> v; }); + + expression.addLazyFunction("entity_load_handler", -1, (c, t, lv) -> + { + if (lv.size() < 3) throw new InternalExpressionException("'entity_load_handler' required the entity type, and a function to call"); + String entityString = lv.get(0).evalValue(c).getString(); + + FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 2, true, true); + ((CarpetScriptHost)c.host).getScriptServer().events.addEventDirectly(entityString, c.host, funArg.function); + }; + // or update expression.addLazyFunction("entity_event", -1, (c, t, lv) -> { @@ -275,6 +287,7 @@ public class Entities { if (!(v instanceof EntityValue)) throw new InternalExpressionException("First argument to entity_event should be an entity"); String what = lv.get(1).evalValue(c).getString(); + Value functionValue = lv.get(2).evalValue(c); if (functionValue instanceof NullValue) functionValue = null; diff --git a/src/main/java/carpet/script/argument/FunctionArgument.java b/src/main/java/carpet/script/argument/FunctionArgument.java index 568d8411..ce2fe5a1 100644 --- a/src/main/java/carpet/script/argument/FunctionArgument.java +++ b/src/main/java/carpet/script/argument/FunctionArgument.java @@ -21,7 +21,7 @@ public class FunctionArgument extends Argument this.function = function; this.args = args; } - public static FunctionArgument findIn(Context c, Module module, List params, int offset, boolean prematureEvaluation) + public static FunctionArgument findIn(Context c, Module module, List params, int offset, boolean prematureEvaluation, boolean allowNone) { Value functionValue = params.get(offset).evalValue(c); if (!(functionValue instanceof FunctionValue)) diff --git a/src/main/java/carpet/script/language/Functions.java b/src/main/java/carpet/script/language/Functions.java index 36566305..e4bfb9ac 100644 --- a/src/main/java/carpet/script/language/Functions.java +++ b/src/main/java/carpet/script/language/Functions.java @@ -43,7 +43,7 @@ public class Functions { //lv.remove(lv.size()-1); // aint gonna cut it // maybe it will because of the eager eval changes if (t != Context.SIGNATURE) // just call the function { - FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, false); + FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, false, false); FunctionValue fun = functionArgument.function; Value retval = fun.callInContext(expr, c, t, fun.getExpression(), fun.getToken(), functionArgument.args).evalValue(c); return (cc, tt) -> retval; ///!!!! dono might need to store expr and token in statics? (e? t?) diff --git a/src/main/java/carpet/script/language/Threading.java b/src/main/java/carpet/script/language/Threading.java index dcaa62ef..283a9a72 100644 --- a/src/main/java/carpet/script/language/Threading.java +++ b/src/main/java/carpet/script/language/Threading.java @@ -17,7 +17,7 @@ public class Threading { if (lv.size() == 0) throw new InternalExpressionException("'task' requires at least function to call as a parameter"); - FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, true); + FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, true, false); ThreadValue thread = new ThreadValue(Value.NULL, functionArgument.function, expr, tok, c, functionArgument.args); Thread.yield(); return (cc, tt) -> thread; @@ -28,7 +28,7 @@ public class Threading if (lv.size() < 2) throw new InternalExpressionException("'task' requires at least function to call as a parameter"); Value queue = lv.get(0).evalValue(c); - FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, true); + FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, true, false); ThreadValue thread = new ThreadValue(queue, functionArgument.function, expr, tok, c, functionArgument.args); Thread.yield(); return (cc, tt) -> thread; diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 334ab2f4..02b6b617 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -15,6 +15,7 @@ import carpet.script.EntityEventsGroup; import carpet.script.argument.Vector3Argument; import carpet.script.exception.InternalExpressionException; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.entity.SpawnGroup; @@ -70,15 +71,18 @@ import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; import static carpet.script.value.NBTSerializableValue.nameFromRegistryId; import static carpet.utils.MobAI.genericJump; @@ -210,6 +214,61 @@ public class EntityValue extends Value "illager", EntityGroup.ILLAGER ); + public static class EntityClassDescriptor + { + public EntityType directType; + public Predicate filteringPredicate; + public List> typeList; + EntityClassDescriptor(EntityType type, Predicate predicate, Stream> types) + { + directType = type; + filteringPredicate = predicate; + typeList = types.collect(Collectors.toList()); + } + + public Map byName = new HashMap() {{ + Stream> allTypes = Registry.ENTITY_TYPE.stream(); + Set> projectiles = Sets.newHashSet( + EntityType.ARROW, EntityType.DRAGON_FIREBALL + ); + Set> deads = Sets.newHashSet( + EntityType.AREA_EFFECT_CLOUD, EntityType.BOAT, + ) + + put("*", new EntityClassDescriptor(null, e -> true, allTypes) ); + put("valid", new EntityClassDescriptor(null, EntityPredicates.VALID_ENTITY, allTypes)); + put("!valid", new EntityClassDescriptor(null, e -> !e.isAlive(), allTypes)); + + put("living", Pair.of(null, (e) -> e instanceof LivingEntity && e.isAlive(), allTypes.filter(et -> et.))); + put("!living", Pair.of(null, (e) -> !(e instanceof LivingEntity) && e.isAlive())); + + put("projectile", Pair.of(null, (e) -> e instanceof ProjectileEntity && e.isAlive())); + put("!projectile", Pair.of(null, (e) -> !(e instanceof ProjectileEntity) && e.isAlive())); + + for (String groupStr : entityGroupMap.keySet()) + { + EntityGroup group = entityGroupMap.get(groupStr); + put( groupStr, Pair.of(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() == group && e.isAlive()))); + put("!"+groupStr, Pair.of(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() != group && e.isAlive()))); + } + for (Identifier typeId : Registry.ENTITY_TYPE.getIds()) + { + EntityType type = Registry.ENTITY_TYPE.get(typeId); + String mobType = ValueConversions.simplify(typeId); + put( mobType, Pair.of(type, EntityPredicates.VALID_ENTITY)); + put("!"+mobType, Pair.of(null, (e) -> e.getType() != type && e.isAlive())); + } + for (SpawnGroup catId : SpawnGroup.values()) + { + String catStr = catId.getName(); + put( catStr, Pair.of(null, e -> (e.getType().getSpawnGroup() == catId) && e.isAlive())); + put("!"+catStr, Pair.of(null, e -> (e.getType().getSpawnGroup() != catId) && e.isAlive())); + } + }} + + } + + private static final Map, Predicate>> entityPredicates = new HashMap, Predicate>>() {{