diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index e7946c18..2dd019ae 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -2540,20 +2540,34 @@ run, or by UUID, obtained via `entity ~ 'uuid'`. It returns null if no such enti entities between calls, as missing entities will be returning `null`. Both calls using UUID or numerical ID are `O(1)`, but obviously using UUIDs takes more memory and compute. -### `entity_list(type)` +### `entity_list(descriptor)` -Returns global lists of entities in the current dimension of a specified type. Currently the following -selectors are available: +Returns global lists of entities in the current dimension matching specified descriptor. +Calls to `entity_list` always fetch entities from the current world that the script executes. + +### `entity_types(descriptor)` -* `*`: all entities, even `!valid` +Resolves a given descriptor returning list of entity types that match it. The returned list of types is also a valid list +of descriptors that can be use elsewhere where entity types are required. + +Currently, the following descriptors are available: + +* `*`: all entities, even `!valid`, matches all entity types. * `valid` - all entities that are not dead (health > 0). All main categories below also return only -entities in the `valid` category. -* `living` - all entities that resemble a creature of any sort -* `projectile` - all entities that are not living that can be throw or projected -* `undead`, `arthropod`, `aquatic`, `regular`, `illager` - all entities that belong to any of these groups. Every -living entity belongs to one and only one of these. +entities in the `valid` category. matches all entity types. `!valid` matches all entites that are already dead of all types. +* `living` - all entities that resemble a creature of some sort +* `projectile` - all entities or types that are not living that can be throw or projected, `!projectile` matches all types that + are not living, but cannot the thrown or projected. +* `minecarts` matches all minecart types. `!minecarts` matches all types that are not live, but also not minecarts. Using plural +since `minecart` is a proper entity type on its own. +* `undead`, `arthropod`, `aquatic`, `regular`, `illager` - all entities / types that belong to any of these groups. All +living entities belong to one and only one of these. Corresponding negative (e.g. `!undead`) corresponds to all mobs that are +living but don't belong to that group. Entity groups are used in interaction / battle mechanics like smite for undead, or impaling +for aquatic. Also certain mechanics interact with groups, like ringing a bell with illagers. All other mobs that don't have any of these traits belong +to the `regular` group. * `monster`, `creature`, `ambient`, `water_creature`, `water_ambient`, `misc` - another categorization of -living entities based on their spawn group. +living entities based on their spawn group. Negative descriptor resolves to all living types that don't belong to that +category. * Any of the following standard entity types (equivalent to selection from `/summon` vanilla command: `area_effect_cloud`, `armor_stand`, `arrow`, `bat`, `bee`, `blaze`, `boat`, `cat`, `cave_spider`, `chest_minecart`, `chicken`, `cod`, `command_block_minecart`, `cow`, `creeper`, `dolphin`, `donkey`, `dragon_fireball`, `drowned`, @@ -2569,8 +2583,8 @@ living entities based on their spawn group. `wither`, `wither_skeleton`, `wither_skull`, `wolf`, `zoglin`, `zombie`, `zombie_horse`, `zombie_villager`, `zombified_piglin` -All categories can be preceded with `'!'` which will fetch all entities that are valid (health > 0) but not -belonging to that group. Calls to `entity_list` always fetch entities from the current world that the script executes. +All categories can be preceded with `'!'` which will fetch all entities (unless otherwise noted) that are valid (health > 0) but not +belonging to that group. ### `entity_area(type, center, distance)` @@ -3243,7 +3257,26 @@ Required arguments: `entity, amount, source, attacking_entity` It doesn't mean that all entity types will have a chance to execute a given event, but entities will not error when you attach an inapplicable event to it. -### `entity_event(e, event, call_name, args...)` +### `entity_load_handler(descriptor / descriptors, function)`, `entity_load_handler(descriptor / descriptors, call_name, ... args?)` + +Attaches a callback to when any entity matching the following type / types is loaded in the game, allowing to grab a handle +to an entity right when it is loaded to the world without querying them every tick. Callback expects one parameter - the entity. +If callback is `null`, then the current entity handler, if present, is removed. Consecutive calls to `entity_load_handler` will add / subtract +of the currently targeted entity types pool. + +Like other global events, calls to `entity_load_handler` can only be attached in apps with global scope. Player scope makes so +that it is not clear which player to use run the load call. + +``` +// veryfast method of getting rid of all the zombies. Callback is so early, its packets haven't reached yet the clients +// so to save on log errors, removal of mobs needs to be scheduled for later. +entity_load_handler('zombie', _(e) -> schedule(0, _(outer(e)) -> modify(e, 'remove'))) + +// making all zombies immediately faster and less susceptible to friction of any sort +entity_load_handler('zombie', _(e) -> entity_event(e, 'on_tick', _(e) -> modify(e, 'motion', 1.2*e~'motion'))) +``` + +### `entity_event(e, event, function)`, `entity_event(e, event, call_name, ... args?)` Attaches specific function from the current package to be called upon the `event`, with extra `args` carried to the original required arguments for the event handler. diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index 4919ba47..2cdc4829 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -24,20 +24,34 @@ run, or by UUID, obtained via `entity ~ 'uuid'`. It returns null if no such enti entities between calls, as missing entities will be returning `null`. Both calls using UUID or numerical ID are `O(1)`, but obviously using UUIDs takes more memory and compute. -### `entity_list(type)` +### `entity_list(descriptor)` -Returns global lists of entities in the current dimension of a specified type. Currently the following -selectors are available: +Returns global lists of entities in the current dimension matching specified descriptor. +Calls to `entity_list` always fetch entities from the current world that the script executes. + +### `entity_types(descriptor)` -* `*`: all entities, even `!valid` +Resolves a given descriptor returning list of entity types that match it. The returned list of types is also a valid list +of descriptors that can be use elsewhere where entity types are required. + +Currently, the following descriptors are available: + +* `*`: all entities, even `!valid`, matches all entity types. * `valid` - all entities that are not dead (health > 0). All main categories below also return only -entities in the `valid` category. -* `living` - all entities that resemble a creature of any sort -* `projectile` - all entities that are not living that can be throw or projected -* `undead`, `arthropod`, `aquatic`, `regular`, `illager` - all entities that belong to any of these groups. Every -living entity belongs to one and only one of these. +entities in the `valid` category. matches all entity types. `!valid` matches all entites that are already dead of all types. +* `living` - all entities that resemble a creature of some sort +* `projectile` - all entities or types that are not living that can be throw or projected, `!projectile` matches all types that + are not living, but cannot the thrown or projected. +* `minecarts` matches all minecart types. `!minecarts` matches all types that are not live, but also not minecarts. Using plural +since `minecart` is a proper entity type on its own. +* `undead`, `arthropod`, `aquatic`, `regular`, `illager` - all entities / types that belong to any of these groups. All +living entities belong to one and only one of these. Corresponding negative (e.g. `!undead`) corresponds to all mobs that are +living but don't belong to that group. Entity groups are used in interaction / battle mechanics like smite for undead, or impaling +for aquatic. Also certain mechanics interact with groups, like ringing a bell with illagers. All other mobs that don't have any of these traits belong +to the `regular` group. * `monster`, `creature`, `ambient`, `water_creature`, `water_ambient`, `misc` - another categorization of -living entities based on their spawn group. +living entities based on their spawn group. Negative descriptor resolves to all living types that don't belong to that +category. * Any of the following standard entity types (equivalent to selection from `/summon` vanilla command: `area_effect_cloud`, `armor_stand`, `arrow`, `bat`, `bee`, `blaze`, `boat`, `cat`, `cave_spider`, `chest_minecart`, `chicken`, `cod`, `command_block_minecart`, `cow`, `creeper`, `dolphin`, `donkey`, `dragon_fireball`, `drowned`, @@ -53,8 +67,8 @@ living entities based on their spawn group. `wither`, `wither_skeleton`, `wither_skull`, `wolf`, `zoglin`, `zombie`, `zombie_horse`, `zombie_villager`, `zombified_piglin` -All categories can be preceded with `'!'` which will fetch all entities that are valid (health > 0) but not -belonging to that group. Calls to `entity_list` always fetch entities from the current world that the script executes. +All categories can be preceded with `'!'` which will fetch all entities (unless otherwise noted) that are valid (health > 0) but not +belonging to that group. ### `entity_area(type, center, distance)` @@ -727,7 +741,26 @@ Required arguments: `entity, amount, source, attacking_entity` It doesn't mean that all entity types will have a chance to execute a given event, but entities will not error when you attach an inapplicable event to it. -### `entity_event(e, event, call_name, args...)` +### `entity_load_handler(descriptor / descriptors, function)`, `entity_load_handler(descriptor / descriptors, call_name, ... args?)` + +Attaches a callback to when any entity matching the following type / types is loaded in the game, allowing to grab a handle +to an entity right when it is loaded to the world without querying them every tick. Callback expects one parameter - the entity. +If callback is `null`, then the current entity handler, if present, is removed. Consecutive calls to `entity_load_handler` will add / subtract +of the currently targeted entity types pool. + +Like other global events, calls to `entity_load_handler` can only be attached in apps with global scope. Player scope makes so +that it is not clear which player to use run the load call. + +``` +// veryfast method of getting rid of all the zombies. Callback is so early, its packets haven't reached yet the clients +// so to save on log errors, removal of mobs needs to be scheduled for later. +entity_load_handler('zombie', _(e) -> schedule(0, _(outer(e)) -> modify(e, 'remove'))) + +// making all zombies immediately faster and less susceptible to friction of any sort +entity_load_handler('zombie', _(e) -> entity_event(e, 'on_tick', _(e) -> modify(e, 'motion', 1.2*e~'motion'))) +``` + +### `entity_event(e, event, function)`, `entity_event(e, event, call_name, ... args?)` Attaches specific function from the current package to be called upon the `event`, with extra `args` carried to the original required arguments for the event handler. diff --git a/docs/scarpet/resources/editors/idea/2.txt b/docs/scarpet/resources/editors/idea/2.txt index 4deb4e35..71f6e287 100644 --- a/docs/scarpet/resources/editors/idea/2.txt +++ b/docs/scarpet/resources/editors/idea/2.txt @@ -26,7 +26,9 @@ entity_area entity_event entity_id entity_list +entity_load_handler entity_selector +entity_types escape_nbt flammable format diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml index 21a37c1d..8bd5d2b5 100644 --- a/docs/scarpet/resources/editors/npp/scarpet.xml +++ b/docs/scarpet/resources/editors/npp/scarpet.xml @@ -60,7 +60,7 @@ scoreboard scoreboard_add scoreboard_remove scoreboard_display - player spawn entity_id entity_list entity_area entity_selector query modify entity_event + player spawn entity_id entity_list entity_area entity_selector query modify entity_event entity_load_handler entity_types stack_limit item_category recipe_data crafting_remaining_item diff --git a/src/main/java/carpet/commands/ScriptCommand.java b/src/main/java/carpet/commands/ScriptCommand.java index ffc5efc5..e19ef806 100644 --- a/src/main/java/carpet/commands/ScriptCommand.java +++ b/src/main/java/carpet/commands/ScriptCommand.java @@ -74,7 +74,7 @@ public class ScriptCommand scarpetMatches.addAll(APIFunctions.stream(). filter(s -> s.startsWith(prefix) && s.length() <= maxLen).map(s -> s+"(").collect(Collectors.toList())); // not that useful in commandline, more so in external scripts, so skipping here - if (eventPrefix != null) scarpetMatches.addAll(CarpetEventServer.Event.byName.keySet().stream(). + if (eventPrefix != null) scarpetMatches.addAll(CarpetEventServer.Event.publicEvents().stream(). filter(s -> s.startsWith(eventPrefix)).map(s -> "__on_"+s+"(").collect(Collectors.toList())); scarpetMatches.addAll(host.globaFunctionNames(host.main, s -> s.startsWith(prefix)).map(s -> s+"(").collect(Collectors.toList())); scarpetMatches.addAll(host.globaVariableNames(host.main, s -> s.startsWith(prefix)).collect(Collectors.toList())); @@ -269,7 +269,7 @@ public class ScriptCommand executes( (cc) -> listEvents(cc.getSource())). then(literal("add_to"). then(argument("event", StringArgumentType.word()). - suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.keySet() ,bb)). + suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.publicEvents() ,bb)). then(argument("call", StringArgumentType.word()). suggests( (cc, bb) -> suggestMatching(suggestFunctionCalls(cc), bb)). executes( (cc) -> CarpetServer.scriptServer.events.addEvent( @@ -291,7 +291,7 @@ public class ScriptCommand )?1:0)))))). then(literal("remove_from"). then(argument("event", StringArgumentType.word()). - suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.keySet() ,bb)). + suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.publicEvents() ,bb)). then(argument("call", StringArgumentType.greedyString()). suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.get(StringArgumentType.getString(cc, "event")).handler.callList.stream().map(CarpetEventServer.Callback::toString), bb)). executes( (cc) -> CarpetServer.scriptServer.events.removeEvent( 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..c21b8e16 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; @@ -33,7 +34,6 @@ import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.RegistryKey; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.dimension.DimensionType; import org.apache.commons.lang3.tuple.Pair; import java.util.ArrayList; @@ -56,16 +56,41 @@ public class CarpetEventServer { public final List scheduledCalls = new LinkedList<>(); public final MinecraftServer server; + private static final List NOARGS = Collections.emptyList(); public static class Callback { public final String host; public final FunctionValue function; + public final List parametrizedArgs; - public Callback(String host, FunctionValue function) + + public Callback(String host, FunctionValue function, List parametrizedArgs) { this.host = host; this.function = function; + this.parametrizedArgs = parametrizedArgs==null?NOARGS:parametrizedArgs; + } + + /** + * Used also in entity events + * @param asSource - entity command source + * @param runtimeArgs = options + */ + public boolean execute(ServerCommandSource asSource, List runtimeArgs) + { + if (this.parametrizedArgs.isEmpty()) + return CarpetServer.scriptServer.runas( + asSource.withLevel(CarpetSettings.runPermissionLevel), + host, function, runtimeArgs); + else + { + List combinedArgs = new ArrayList<>(runtimeArgs); + combinedArgs.addAll(this.parametrizedArgs); + return CarpetServer.scriptServer.runas( + asSource.withLevel(CarpetSettings.runPermissionLevel), + host, function, combinedArgs); + } } @Override @@ -77,14 +102,13 @@ public class CarpetEventServer public static class ScheduledCall extends Callback { - public final List args; + private final CarpetContext ctx; public long dueTime; public ScheduledCall(CarpetContext context, FunctionValue function, List args, long dueTime) { - super(context.host.getName(), function); - this.args = args; + super(context.host.getName(), function, args); this.ctx = context; this.dueTime = dueTime; } @@ -94,34 +118,10 @@ public class CarpetEventServer */ public void execute() { - CarpetServer.scriptServer.runas(ctx.origin, ctx.s, host, function, lazify(args)); + CarpetServer.scriptServer.runas(ctx.origin, ctx.s, host, function, parametrizedArgs); } - /** - * Used in entity events - * @param asSource - entity command source - * @param args = options - */ - public void execute(ServerCommandSource asSource, List args) - { - if (this.args == null || this.args.isEmpty()) - CarpetServer.scriptServer.runas( - ctx.origin, asSource.withLevel(CarpetSettings.runPermissionLevel), - host, function, lazify(args)); - else - { - List combinedArgs = new ArrayList<>(); - combinedArgs.addAll(args); - combinedArgs.addAll(this.args); - CarpetServer.scriptServer.runas( - ctx.origin, asSource.withLevel(CarpetSettings.runPermissionLevel), - host, function, lazify(combinedArgs)); - } - } - private List lazify(List args) - { - return args.stream().map(v -> (LazyValue) (c, t) -> v ).collect(Collectors.toList()); - } + } public static class CallbackList @@ -136,18 +136,17 @@ public class CarpetEventServer this.reqArgs = reqArgs; } - public void call(Supplier> argumentSupplier, Supplier cmdSourceSupplier) + public void call(Supplier> argumentSupplier, Supplier cmdSourceSupplier) { if (callList.size() > 0) { - List argv = argumentSupplier.get(); // empty for onTickDone + List argv = argumentSupplier.get(); // empty for onTickDone ServerCommandSource source = cmdSourceSupplier.get(); assert argv.size() == reqArgs; List fails = new ArrayList<>(); for (Callback call: callList) { - if (!CarpetServer.scriptServer.runas(source, call.host, call.function, argv)) - fails.add(call); + if (!call.execute(source, argv)) fails.add(call); } for (Callback call : fails) callList.remove(call); } @@ -177,19 +176,19 @@ public class CarpetEventServer //remove duplicates removeEventCall(hostName, udf.getString()); - callList.add(new Callback(hostName, udf)); + callList.add(new Callback(hostName, udf, null)); return true; } - public boolean addEventCallDirect(ScriptHost host, FunctionValue function) + public boolean addEventCallInternal(ScriptHost host, FunctionValue function, List args) { - if (function == null || function.getArguments().size() != reqArgs) + if (function == null || (function.getArguments().size() - args.size()) != reqArgs) { return false; } //all clear //remove duplicates removeEventCall(host.getName(), function.getString()); - callList.add(new Callback(host.getName(), function)); + callList.add(new Callback(host.getName(), function, args)); return true; } @@ -211,6 +210,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 @@ -252,10 +256,7 @@ public class CarpetEventServer handler.call( () -> { ChunkPos pos = chunk.getPos(); - return Arrays.asList( - ((c, t) -> new NumericValue(pos.x << 4)), - ((c, t) -> new NumericValue(pos.z << 4)) - ); + return Arrays.asList(new NumericValue(pos.x << 4), new NumericValue(pos.z << 4)); }, () -> CarpetServer.minecraft_server.getCommandSource().withWorld(world) ); } @@ -266,7 +267,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_DEPLOYS_ELYTRA = new Event("player_deploys_elytra", 1, false) @@ -274,7 +275,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_WAKES_UP = new Event("player_wakes_up", 1, false) @@ -282,7 +283,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_RIDES = new Event("player_rides", 5, false) @@ -290,12 +291,8 @@ public class CarpetEventServer @Override public void onMountControls(ServerPlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking) { - handler.call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new NumericValue(forwardSpeed)), - ((c, t) -> new NumericValue(strafeSpeed)), - ((c, t) -> new NumericValue(jumping)), - ((c, t) -> new NumericValue(sneaking)) + handler.call( () -> Arrays.asList(new EntityValue(player), + new NumericValue(forwardSpeed), new NumericValue(strafeSpeed), new NumericValue(jumping), new NumericValue(sneaking) ), player::getCommandSource); } }; @@ -308,9 +305,9 @@ public class CarpetEventServer { //ItemStack itemstack = player.getStackInHand(enumhand); return Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> ListValue.fromItemStack(itemstack)), - ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")) + new EntityValue(player), + ListValue.fromItemStack(itemstack), + StringValue.of(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand") ); }, player::getCommandSource); } @@ -323,9 +320,9 @@ public class CarpetEventServer handler.call( () -> { return Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new BlockValue(null, player.getServerWorld(), blockpos)), - ((c, t) -> new StringValue(facing.getName())) + new EntityValue(player), + new BlockValue(null, player.getServerWorld(), blockpos), + StringValue.of(facing.getName()) ); }, player::getCommandSource); } @@ -342,16 +339,16 @@ public class CarpetEventServer Direction enumfacing = hitRes.getSide(); Vec3d vec3d = hitRes.getPos().subtract(blockpos.getX(), blockpos.getY(), blockpos.getZ()); return Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> ListValue.fromItemStack(itemstack)), - ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")), - ((c, t) -> new BlockValue(null, player.getServerWorld(), blockpos)), - ((c, t) -> new StringValue(enumfacing.getName())), - ((c, t) -> ListValue.of( + new EntityValue(player), + ListValue.fromItemStack(itemstack), + StringValue.of(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand"), + new BlockValue(null, player.getServerWorld(), blockpos), + StringValue.of(enumfacing.getName()), + ListValue.of( new NumericValue(vec3d.x), new NumericValue(vec3d.y), new NumericValue(vec3d.z) - )) + ) ); }, player::getCommandSource); } @@ -367,15 +364,15 @@ public class CarpetEventServer Direction enumfacing = hitRes.getSide(); Vec3d vec3d = hitRes.getPos().subtract(blockpos.getX(), blockpos.getY(), blockpos.getZ()); return Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")), - ((c, t) -> new BlockValue(null, player.getServerWorld(), blockpos)), - ((c, t) -> new StringValue(enumfacing.getName())), - ((c, t) -> ListValue.of( + new EntityValue(player), + StringValue.of(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand"), + new BlockValue(null, player.getServerWorld(), blockpos), + StringValue.of(enumfacing.getName()), + ListValue.of( new NumericValue(vec3d.x), new NumericValue(vec3d.y), new NumericValue(vec3d.z) - )) + ) ); }, player::getCommandSource); } @@ -386,10 +383,10 @@ public class CarpetEventServer public void onBlockPlaced(ServerPlayerEntity player, BlockPos pos, Hand enumhand, ItemStack itemstack) { handler.call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> ListValue.fromItemStack(itemstack)), - ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")), - ((c, t) -> new BlockValue(null, player.getServerWorld(), pos)) + new EntityValue(player), + ListValue.fromItemStack(itemstack), + StringValue.of(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand"), + new BlockValue(null, player.getServerWorld(), pos) ), player::getCommandSource); } }; @@ -398,21 +395,19 @@ public class CarpetEventServer @Override public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS) { - handler.call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new BlockValue(previousBS, player.getServerWorld(), pos)) - ), player::getCommandSource); + handler.call( + () -> Arrays.asList(new EntityValue(player), new BlockValue(previousBS, player.getServerWorld(), pos)), + player::getCommandSource + ); } }; 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)), - ((c, t) -> new EntityValue(entity)), - ((c, t) -> new StringValue(enumhand==Hand.MAIN_HAND?"mainhand":"offhand")) + new EntityValue(player), new EntityValue(entity), StringValue.of(enumhand==Hand.MAIN_HAND?"mainhand":"offhand") ), player::getCommandSource); } }; @@ -420,26 +415,16 @@ public class CarpetEventServer { @Override public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) { - handler.call( () -> - { - //ItemStack itemstack = player.getStackInHand(enumhand); - return Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> ListValue.fromItemStack(itemstack)) - ); - }, player::getCommandSource); + handler.call( () -> Arrays.asList(new EntityValue(player), ListValue.fromItemStack(itemstack)), player::getCommandSource); } }; 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)), - ((c, t) -> new EntityValue(entity)) - ), player::getCommandSource); + handler.call( () -> Arrays.asList(new EntityValue(player), new EntityValue(entity)), player::getCommandSource); } }; public static final Event PLAYER_STARTS_SNEAKING = new Event("player_starts_sneaking", 1, false) @@ -447,7 +432,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_STOPS_SNEAKING = new Event("player_stops_sneaking", 1, false) @@ -455,7 +440,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_STARTS_SPRINTING = new Event("player_starts_sprinting", 1, false) @@ -463,7 +448,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_STOPS_SPRINTING = new Event("player_stops_sprinting", 1, false) @@ -471,7 +456,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; @@ -482,13 +467,11 @@ public class CarpetEventServer { // this.getStackInHand(this.getActiveHand()), this.activeItemStack) handler.call( () -> - { - return Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> ListValue.fromItemStack(itemstack)), - ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")) - ); - }, player::getCommandSource); + Arrays.asList( + new EntityValue(player), + ListValue.fromItemStack(itemstack), + StringValue.of(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand") + ), player::getCommandSource); } }; public static final Event PLAYER_FINISHED_USING_ITEM = new Event("player_finishes_using_item", 3, false) @@ -498,13 +481,11 @@ public class CarpetEventServer { // this.getStackInHand(this.getActiveHand()), this.activeItemStack) handler.call( () -> - { - return Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> ListValue.fromItemStack(itemstack)), - ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")) - ); - }, player::getCommandSource); + Arrays.asList( + new EntityValue(player), + ListValue.fromItemStack(itemstack), + new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand") + ), player::getCommandSource); } }; public static final Event PLAYER_DROPS_ITEM = new Event("player_drops_item", 1, false) @@ -512,7 +493,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_DROPS_STACK = new Event("player_drops_stack", 1, false) @@ -520,7 +501,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_CHOOSES_RECIPE = new Event("player_chooses_recipe", 3, false) @@ -529,13 +510,11 @@ public class CarpetEventServer public void onRecipeSelected(ServerPlayerEntity player, Identifier recipe, boolean fullStack) { handler.call( () -> - { - return Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new StringValue(NBTSerializableValue.nameFromRegistryId(recipe))), - ((c, t) -> new NumericValue(fullStack)) - ); - }, player::getCommandSource); + Arrays.asList( + new EntityValue(player), + StringValue.of(NBTSerializableValue.nameFromRegistryId(recipe)), + new NumericValue(fullStack) + ), player::getCommandSource); } }; public static final Event PLAYER_SWITCHES_SLOT = new Event("player_switches_slot", 3, false) @@ -545,13 +524,11 @@ public class CarpetEventServer { if (from == to) return; // initial slot update handler.call( () -> - { - return Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new NumericValue(from)), - ((c, t) -> new NumericValue(to)) - ); - }, player::getCommandSource); + Arrays.asList( + new EntityValue(player), + new NumericValue(from), + new NumericValue(to) + ), player::getCommandSource); } }; public static final Event PLAYER_SWAPS_HANDS = new Event("player_swaps_hands", 1, false) @@ -559,7 +536,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_TAKES_DAMAGE = new Event("player_takes_damage", 4, false) @@ -568,14 +545,12 @@ public class CarpetEventServer public void onDamage(Entity target, float amount, DamageSource source) { handler.call( () -> - { - return Arrays.asList( - ((c, t) -> new EntityValue(target)), - ((c, t) -> new NumericValue(amount)), - ((c, t) ->new StringValue(source.getName())), - ((c, t) -> source.getAttacker()==null?Value.NULL:new EntityValue(source.getAttacker())) - ); - }, target::getCommandSource); + Arrays.asList( + new EntityValue(target), + new NumericValue(amount), + StringValue.of(source.getName()), + source.getAttacker()==null?Value.NULL:new EntityValue(source.getAttacker()) + ), target::getCommandSource); } }; public static final Event PLAYER_DEALS_DAMAGE = new Event("player_deals_damage", 3, false) @@ -584,23 +559,16 @@ public class CarpetEventServer public void onDamage(Entity target, float amount, DamageSource source) { handler.call( () -> - { - return Arrays.asList( - ((c, t) -> new EntityValue(source.getAttacker())), - ((c, t) -> new NumericValue(amount)), - ((c, t) -> new EntityValue(target)) - ); - }, () -> source.getAttacker().getCommandSource()); + Arrays.asList(new EntityValue(source.getAttacker()), new NumericValue(amount), new EntityValue(target)), + () -> source.getAttacker().getCommandSource() + ); } }; 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) { - handler.call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new EntityValue(entity)) - ), player::getCommandSource); + public void onEntityHandAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { + handler.call( () -> Arrays.asList(new EntityValue(player), new EntityValue(entity)), player::getCommandSource); } }; @@ -609,7 +577,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_RESPAWNS = new Event("player_respawns", 1, false) @@ -617,7 +585,7 @@ public class CarpetEventServer @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_CHANGES_DIMENSION = new Event("player_changes_dimension", 5, false) @@ -631,30 +599,21 @@ public class CarpetEventServer Value fromDimStr = new StringValue(NBTSerializableValue.nameFromRegistryId(fromDim.getValue())); Value toDimStr = new StringValue(NBTSerializableValue.nameFromRegistryId(dimTo.getValue())); - handler.call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> fromValue), - ((c, t) -> fromDimStr), - ((c, t) -> toValue), - ((c, t) -> toDimStr) - ), player::getCommandSource); + handler.call( () -> Arrays.asList(new EntityValue(player), fromValue, fromDimStr, toValue, toDimStr), player::getCommandSource); } }; public static final Event PLAYER_CONNECTS = new Event("player_connects", 1, false) { @Override public void onPlayerEvent(ServerPlayerEntity player) { - handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + handler.call( () -> Collections.singletonList(new EntityValue(player)), player::getCommandSource); } }; public static final Event PLAYER_DISCONNECTS = new Event("player_disconnects", 2, false) { @Override public void onPlayerMessage(ServerPlayerEntity player, String message) { - handler.call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new StringValue(message)) - ), player::getCommandSource); + handler.call( () -> Arrays.asList(new EntityValue(player), new StringValue(message)), player::getCommandSource); } }; public static final Event STATISTICS = new Event("statistic", 4, false) @@ -674,10 +633,10 @@ public class CarpetEventServer Identifier id = getStatId(stat); if (skippedStats.contains(id)) return; handler.call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.STAT_TYPE.getId(stat.getType())))), - ((c, t) -> new StringValue(NBTSerializableValue.nameFromRegistryId(id))), - ((c, t) -> new NumericValue(amount)) + new EntityValue(player), + StringValue.of(NBTSerializableValue.nameFromRegistryId(Registry.STAT_TYPE.getId(stat.getType()))), + StringValue.of(NBTSerializableValue.nameFromRegistryId(id)), + new NumericValue(amount) ), player::getCommandSource); } }; @@ -688,24 +647,52 @@ public class CarpetEventServer { handler.call( () -> Arrays.asList( - ((c, t) -> new BlockValue(null, world, pos)), - ((c, t) -> flag>0?Value.TRUE:Value.FALSE) + new BlockValue(null, world, pos), + flag>0?Value.TRUE:Value.FALSE ), () -> CarpetServer.minecraft_server.getCommandSource().withWorld(world) ); } }; + public static String getLoadEvent(EntityType et) + { + return "entity_loaded_" + Registry.ENTITY_TYPE.getId(et); + } + + public static final Map, Event> ENTITY_LOAD= new HashMap, Event>() {{ + EntityType.get("zombie"); + Registry.ENTITY_TYPE.forEach(et -> { + put(et, new Event(getLoadEvent(et), 1, true, false) + { + @Override + public void onEntityAction(Entity entity) + { + handler.call( + () -> Collections.singletonList(new EntityValue(entity)), + () -> CarpetServer.minecraft_server.getCommandSource().withWorld((ServerWorld) entity.world).withLevel(CarpetSettings.runPermissionLevel) + ); + } + }); + }); + }}; + // 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 +711,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) {} @@ -780,12 +768,13 @@ public class CarpetEventServer return added; } - public boolean addEventDirectly(String event, ScriptHost host, FunctionValue function) + public void addEventDirectly(String event, ScriptHost host, FunctionValue function, List args) { Event ev = Event.byName.get(event); if (!canAddEvent(ev, host)) throw new InternalExpressionException("Global event "+event+" can only be added to apps with global scope"); - return ev.handler.addEventCallDirect(host, function); + boolean success = ev.handler.addEventCallInternal(host, function, args==null?NOARGS:args); + if (!success) throw new InternalExpressionException("Global event "+event+" requires "+ev.handler.reqArgs+", not "+(function.getNumParams()-((args==null)?0:args.size()))); } private boolean canAddEvent(Event event, ScriptHost host) @@ -808,6 +797,16 @@ public class CarpetEventServer Messenger.m(source, "gi Removed event: " + funName + " from "+event); return true; } + public boolean removeEventDirectly(String event, ScriptHost host) + { + + if (!Event.byName.containsKey(event)) + { + return false; + } + Event.byName.get(event).handler.removeAllCalls(host.getName()); + return true; + } public void removeEventDirectly(String event, ScriptHost host, String funName) { diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index 504de1ac..11d546d0 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -109,7 +109,7 @@ public class CarpetScriptHost extends ScriptHost // this is nasty, we have the host and function, yet we add it via names, but hey - works for now String event = funName.replaceFirst("__on_", ""); if (CarpetEventServer.Event.byName.containsKey(event)) - scriptServer.events.addEventDirectly(event, this, function); + scriptServer.events.addEventDirectly(event, this, function, null); } else if (funName.equals("__config")) { @@ -305,12 +305,12 @@ public class CarpetScriptHost extends ScriptHost } } - public Value callUDF(BlockPos pos, ServerCommandSource source, FunctionValue fun, List argv) throws InvalidCallbackException + public Value callUDF(BlockPos pos, ServerCommandSource source, FunctionValue fun, List argv) throws InvalidCallbackException { if (CarpetServer.scriptServer.stopAll) return Value.NULL; try { // cause we can't throw checked exceptions in lambda. Left if be until need to handle these more gracefully - fun.assertArgsOk(argv.size(), (b) -> { + fun.assertArgsOk(argv, (b) -> { throw new InternalExpressionException(""); }); } @@ -323,7 +323,7 @@ public class CarpetScriptHost extends ScriptHost // TODO: this is just for now - invoke would be able to invoke other hosts scripts Context context = new CarpetContext(this, source, pos); return fun.getExpression().evalValue( - () -> fun.lazyEval(context, Context.VOID, fun.getExpression(), fun.getToken(), argv), + () -> fun.lazyEval(context, Context.VOID, fun.getExpression(), fun.getToken(), FunctionValue.lazify(argv)), context, Context.VOID); } @@ -334,6 +334,7 @@ public class CarpetScriptHost extends ScriptHost return Value.NULL; } + @Override public void onClose() { diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index 73bd60c4..95ca7f02 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -268,12 +268,12 @@ public class CarpetScriptServer return true; } - public boolean runas(ServerCommandSource source, String hostname, FunctionValue udf, List argv) + public boolean runas(ServerCommandSource source, String hostname, FunctionValue udf, List argv) { return runas(BlockPos.ORIGIN, source, hostname, udf, argv); } - public boolean runas(BlockPos origin, ServerCommandSource source, String hostname, FunctionValue udf, List argv) + public boolean runas(BlockPos origin, ServerCommandSource source, String hostname, FunctionValue udf, List argv) { CarpetScriptHost host = globalHost; try diff --git a/src/main/java/carpet/script/EntityEventsGroup.java b/src/main/java/carpet/script/EntityEventsGroup.java index b1214abd..76d09f33 100644 --- a/src/main/java/carpet/script/EntityEventsGroup.java +++ b/src/main/java/carpet/script/EntityEventsGroup.java @@ -19,7 +19,7 @@ import java.util.Map; public class EntityEventsGroup { - private final Map> actions; + private final Map> actions; private final Entity entity; public EntityEventsGroup(Entity e) { @@ -30,35 +30,37 @@ public class EntityEventsGroup public void onEvent(Event type, Object... args) { if (actions.isEmpty()) return; // most of the cases, trying to be nice - Map actionSet = actions.get(type); + Map actionSet = actions.get(type); if (actionSet == null) return; - for (Iterator> iterator = actionSet.entrySet().iterator(); iterator.hasNext(); ) + + for (Iterator> iterator = actionSet.entrySet().iterator(); iterator.hasNext(); ) { - Map.Entry action = iterator.next(); + Map.Entry action = iterator.next(); ScriptHost host = CarpetServer.scriptServer.getHostByName(action.getKey()); if (host == null) { iterator.remove(); continue; } - type.call(action.getValue(), entity, args); // optionally remove when call failing + if (!type.call(action.getValue(), entity, args)) + iterator.remove(); } if (actionSet.isEmpty()) actions.remove(type); } - public void addEvent(Event type, CarpetContext cc, FunctionValue fun, List extraargs) + public void addEvent(Event type, String hostName, FunctionValue fun, List extraargs) { if (fun != null) { - CarpetEventServer.ScheduledCall call = type.create(cc, fun, extraargs); + CarpetEventServer.Callback call = type.create(hostName, fun, extraargs); if (call == null) throw new InternalExpressionException("wrong number of arguments for callback, required "+type.argcount); - actions.computeIfAbsent(type, k -> new HashMap<>()).put(cc.host.getName(), call); + actions.computeIfAbsent(type, k -> new HashMap<>()).put(hostName, call); } else { - actions.computeIfAbsent(type, k -> new HashMap<>()).remove(cc.host.getName()); + actions.computeIfAbsent(type, k -> new HashMap<>()).remove(hostName); if (actions.get(type).isEmpty()) actions.remove(type); } @@ -105,18 +107,18 @@ public class EntityEventsGroup argcount = args+1; // entity is not extra byName.put(identifier, this); } - public CarpetEventServer.ScheduledCall create(CarpetContext cc, FunctionValue function, List extraArgs) + public CarpetEventServer.Callback create(String hostName, FunctionValue function, List extraArgs) { if ((function.getArguments().size()-(extraArgs == null ? 0 : extraArgs.size())) != argcount) { return null; } - return new CarpetEventServer.ScheduledCall(cc, function, extraArgs, 0); + return new CarpetEventServer.Callback(hostName, function, extraArgs); } - public void call(CarpetEventServer.ScheduledCall tickCall, Entity entity, Object ... args) + public boolean call(CarpetEventServer.Callback tickCall, Entity entity, Object ... args) { assert args.length == argcount-1; - tickCall.execute(entity.getCommandSource(), makeArgs(entity, args)); + return tickCall.execute(entity.getCommandSource(), makeArgs(entity, args)); } protected List makeArgs(Entity entity, Object ... args) { diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java index e0b67829..f354ae2c 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, true); 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..e666f950 100644 --- a/src/main/java/carpet/script/api/Entities.java +++ b/src/main/java/carpet/script/api/Entities.java @@ -1,15 +1,17 @@ package carpet.script.api; import carpet.script.CarpetContext; +import carpet.script.CarpetEventServer; +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; import carpet.script.value.FunctionValue; import carpet.script.value.ListValue; import carpet.script.value.NBTSerializableValue; -import carpet.script.value.NullValue; import carpet.script.value.NumericValue; import carpet.script.value.Value; import com.mojang.brigadier.StringReader; @@ -27,13 +29,13 @@ import net.minecraft.util.Identifier; import net.minecraft.util.math.Box; import net.minecraft.util.math.Vec3d; import net.minecraft.util.registry.Registry; -import org.apache.commons.lang3.tuple.Pair; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; -import java.util.function.Predicate; import java.util.stream.Collectors; public class Entities { @@ -117,7 +119,7 @@ public class Entities { try { entityId = Identifier.fromCommandInput(new StringReader(entityString)); - EntityType type = Registry.ENTITY_TYPE.getOrEmpty(entityId).orElse(null); + EntityType type = Registry.ENTITY_TYPE.getOrEmpty(entityId).orElse(null); if (type == null || !type.isSummonable()) return LazyValue.NULL; } @@ -181,12 +183,8 @@ public class Entities { expression.addLazyFunction("entity_list", 1, (c, t, lv) -> { String who = lv.get(0).evalValue(c).getString(); - Pair, Predicate> pair = EntityValue.getPredicate(who); - if (pair == null) - { - throw new InternalExpressionException("Unknown entity selection criterion: "+who); - } - List entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(pair.getKey(), pair.getValue()); + EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who); + List entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(eDesc.directType, eDesc.filteringPredicate); Value retval = ListValue.wrap(entityList.stream().map(EntityValue::new).collect(Collectors.toList())); return (_c, _t ) -> retval; }); @@ -204,13 +202,8 @@ public class Entities { throw new InternalExpressionException("Range of 'entity_area' cannot come from a block argument"); Vec3d range = rangeLocator.vec; Box area = new Box(center, center).expand(range.x, range.y, range.z); - - Pair, Predicate> pair = EntityValue.getPredicate(who); - if (pair == null) - { - throw new InternalExpressionException("Unknown entity selection criterion: "+who); - } - List entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType((EntityType) pair.getKey(), area, pair.getValue()); + EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who); + List entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(eDesc.directType, area,eDesc.filteringPredicate); Value retval = ListValue.wrap(entityList.stream().map(EntityValue::new).collect(Collectors.toList())); return (_c, _t ) -> retval; }); @@ -266,6 +259,37 @@ public class Entities { return (cc, tt) -> v; }); + expression.addFunction("entity_types", (lv) -> + { + if (lv.size() > 1) throw new InternalExpressionException("'entity_types' requires one or no arguments"); + String desc = (lv.size() == 1)?lv.get(0).getString():"*"; + return EntityValue.getEntityDescriptor(desc).listValue; + }); + + expression.addLazyFunction("entity_load_handler", -1, (c, t, lv) -> + { + if (c.host.isPerUser()) throw new InternalExpressionException("'entity_load_handler' can only be called in apps with global scope"); + if (lv.size() < 2) throw new InternalExpressionException("'entity_load_handler' required the entity type, and a function to call"); + Value entityValue = lv.get(0).evalValue(c); + List descriptors = (entityValue instanceof ListValue) + ? ((ListValue) entityValue).getItems().stream().map(Value::getString).collect(Collectors.toList()) + : Collections.singletonList(entityValue.getString()); + Set> types = new HashSet<>(); + descriptors.forEach(s -> types.addAll(EntityValue.getEntityDescriptor(s).typeList)); + FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 1, false, true, false); + CarpetEventServer events = ((CarpetScriptHost)c.host).getScriptServer().events; + if (funArg.function == null) + { + types.forEach(et -> events.removeEventDirectly(CarpetEventServer.Event.getLoadEvent(et), c.host)); + } + else + { + types.forEach(et -> events.addEventDirectly(CarpetEventServer.Event.getLoadEvent(et), c.host, funArg.function, FunctionValue.resolveArgs(funArg.args, c, t))); + } + Value ret = new NumericValue(types.size()); + return (cc, tt) -> ret; + }); + // or update expression.addLazyFunction("entity_event", -1, (c, t, lv) -> { @@ -275,24 +299,10 @@ 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; - else if (!(functionValue instanceof FunctionValue)) - { - String name = functionValue.getString(); - functionValue = c.host.getAssertFunction(expression.module, name); - } - FunctionValue function = (FunctionValue)functionValue; - List args = null; - if (lv.size()==4) - args = Collections.singletonList(lv.get(3).evalValue(c)); - else if (lv.size()>4) - { - args = lv.subList(3, lv.size()).stream().map((vv) -> vv.evalValue(c)).collect(Collectors.toList()); - } - ((EntityValue) v).setEvent((CarpetContext)c, what, function, args); + FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 2, false, true, false); + + ((EntityValue) v).setEvent((CarpetContext)c, what, funArg.function, FunctionValue.resolveArgs(funArg.args, c, t)); return LazyValue.NULL; }); diff --git a/src/main/java/carpet/script/argument/FunctionArgument.java b/src/main/java/carpet/script/argument/FunctionArgument.java index 568d8411..451b7d72 100644 --- a/src/main/java/carpet/script/argument/FunctionArgument.java +++ b/src/main/java/carpet/script/argument/FunctionArgument.java @@ -21,9 +21,34 @@ 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) + + /** + * @param c context + * @param module module + * @param params list of lazy params + * @param offset offset where to start looking for functional argument + * @param prematureEvaluation evaluates eagerly arguments in current context before returning + * @param allowNone none indicates no function present, otherwise it will croak + * @param checkArgs whether the caller expects trailing parameters to fully resolve function argument list + * if not - argument count check will not be performed and its up to the caller to verify + * if the number of supplied arguments is right + * @return argument data + */ + public static FunctionArgument findIn( + Context c, + Module module, + List params, + int offset, + boolean prematureEvaluation, + boolean allowNone, + boolean checkArgs) { Value functionValue = params.get(offset).evalValue(c); + if (functionValue.isNull()) + { + if (allowNone) return new FunctionArgument(null, offset+1, null); + throw new InternalExpressionException("function argument cannot be null"); + } if (!(functionValue instanceof FunctionValue)) { String name = functionValue.getString(); @@ -31,11 +56,14 @@ public class FunctionArgument extends Argument } FunctionValue fun = (FunctionValue)functionValue; int argsize = fun.getArguments().size(); - int extraargs = params.size() - argsize - offset - 1; - if (extraargs < 0) - throw new InternalExpressionException("Function "+fun.getPrettyString()+" requires at least "+fun.getArguments().size()+" arguments"); - if (extraargs > 0 && fun.getVarArgs()==null) - throw new InternalExpressionException("Function "+fun.getPrettyString()+" requires "+fun.getArguments().size()+" arguments"); + if (checkArgs) + { + int extraargs = params.size() - argsize - offset - 1; + if (extraargs < 0) + throw new InternalExpressionException("Function " + fun.getPrettyString() + " requires at least " + fun.getArguments().size() + " arguments"); + if (extraargs > 0 && fun.getVarArgs() == null) + throw new InternalExpressionException("Function " + fun.getPrettyString() + " requires " + fun.getArguments().size() + " arguments"); + } List lvargs = new ArrayList<>(); if (prematureEvaluation) { diff --git a/src/main/java/carpet/script/language/Functions.java b/src/main/java/carpet/script/language/Functions.java index 36566305..24eaf124 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, true); 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..b317a3d8 100644 --- a/src/main/java/carpet/script/language/Threading.java +++ b/src/main/java/carpet/script/language/Threading.java @@ -4,7 +4,6 @@ import carpet.script.Expression; import carpet.script.argument.FunctionArgument; import carpet.script.exception.ExitStatement; import carpet.script.exception.InternalExpressionException; -import carpet.script.value.FunctionValue; import carpet.script.value.NumericValue; import carpet.script.value.ThreadValue; import carpet.script.value.Value; @@ -17,7 +16,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, true); ThreadValue thread = new ThreadValue(Value.NULL, functionArgument.function, expr, tok, c, functionArgument.args); Thread.yield(); return (cc, tt) -> thread; @@ -28,7 +27,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, true); 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..b09cb5b5 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; @@ -24,6 +25,7 @@ import net.minecraft.entity.EntityGroup; import net.minecraft.entity.ai.brain.MemoryModuleType; import net.minecraft.entity.ai.pathing.Path; import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.entity.vehicle.AbstractMinecartEntity; import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket; @@ -70,15 +72,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; @@ -187,62 +192,139 @@ public class EntityValue extends Value return "entity"; } - @Override public int hashCode() { return entity.hashCode(); } - public static Pair, Predicate> getPredicate(String who) + public static EntityClassDescriptor getEntityDescriptor(String who) { - Pair, Predicate> res = entityPredicates.get(who); - if (res != null) return res; - return res; //TODO add more here like search by tags, or type + EntityClassDescriptor eDesc = EntityClassDescriptor.byName.get(who); + if (eDesc == null) throw new InternalExpressionException(who+" is not a valid entity descriptor"); + return eDesc; + //TODO add more here like search by tags, or type //if (who.startsWith('tag:')) } - private static Map entityGroupMap = ImmutableMap.of( - "undead", EntityGroup.UNDEAD, - "arthropod", EntityGroup.ARTHROPOD, - "aquatic", EntityGroup.AQUATIC, - "regular", EntityGroup.DEFAULT, - "illager", EntityGroup.ILLAGER - ); - - private static final Map, Predicate>> entityPredicates = - new HashMap, Predicate>>() - {{ - put("*", Pair.of(null, e -> true)); - put("valid", Pair.of(null, EntityPredicates.VALID_ENTITY)); - put("!valid", Pair.of(null, e -> !e.isAlive())); - - put("living", Pair.of(null, (e) -> e instanceof LivingEntity && e.isAlive())); - 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()) + public static class EntityClassDescriptor + { + public EntityType directType; + public Predicate filteringPredicate; + public List> typeList; + public Value listValue; + EntityClassDescriptor(EntityType type, Predicate predicate, List> types) { - 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()))); + directType = type; + filteringPredicate = predicate; + typeList = types; + listValue = (types==null)?Value.NULL:ListValue.wrap(types.stream().map(et -> StringValue.of(nameFromRegistryId(Registry.ENTITY_TYPE.getId(et)))).collect(Collectors.toList())); } - for (Identifier typeId : Registry.ENTITY_TYPE.getIds()) + EntityClassDescriptor(EntityType type, Predicate predicate, Stream> types) { - 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())); + this(type, predicate, types.collect(Collectors.toList())); } - 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())); - } - }}; + + public final static Map byName = new HashMap() {{ + List> allTypes = Registry.ENTITY_TYPE.stream().collect(Collectors.toList()); + // nonliving types + Set> projectiles = Sets.newHashSet( + EntityType.ARROW, EntityType.DRAGON_FIREBALL, EntityType.FIREWORK_ROCKET, + EntityType.FIREBALL, EntityType.LLAMA_SPIT, EntityType.SMALL_FIREBALL, + EntityType.SNOWBALL, EntityType.SPECTRAL_ARROW, EntityType.EGG, + EntityType.ENDER_PEARL, EntityType.EXPERIENCE_BOTTLE, EntityType.POTION, + EntityType.TRIDENT, EntityType.WITHER_SKULL, EntityType.FISHING_BOBBER + ); + Set> deads = Sets.newHashSet( + EntityType.AREA_EFFECT_CLOUD, EntityType.BOAT, EntityType.END_CRYSTAL, + EntityType.EVOKER_FANGS, EntityType.EXPERIENCE_ORB, EntityType.EYE_OF_ENDER, + EntityType.FALLING_BLOCK, EntityType.ITEM, EntityType.ITEM_FRAME, + EntityType.LEASH_KNOT, EntityType.LIGHTNING_BOLT, EntityType.PAINTING, + EntityType.TNT + + ); + Set> minecarts = Sets.newHashSet( + EntityType.MINECART, EntityType.CHEST_MINECART, EntityType.COMMAND_BLOCK_MINECART, + EntityType.FURNACE_MINECART, EntityType.HOPPER_MINECART, + EntityType.SPAWNER_MINECART, EntityType.TNT_MINECART + ); + // living mob groups - non-defeault + Set> undeads = Sets.newHashSet( + EntityType.STRAY, EntityType.SKELETON, EntityType.WITHER_SKELETON, + EntityType.ZOMBIE, EntityType.DROWNED, EntityType.ZOMBIE_VILLAGER, + EntityType.ZOMBIE_HORSE, EntityType.SKELETON_HORSE, EntityType.PHANTOM, + EntityType.WITHER, EntityType.ZOGLIN, EntityType.HUSK, EntityType.ZOMBIFIED_PIGLIN + + ); + Set> arthropods = Sets.newHashSet( + EntityType.BEE, EntityType.ENDERMITE, EntityType.SILVERFISH, EntityType.SPIDER, + EntityType.CAVE_SPIDER + ); + Set> aquatique = Sets.newHashSet( + EntityType.GUARDIAN, EntityType.TURTLE, EntityType.COD, EntityType.DOLPHIN, EntityType.PUFFERFISH, + EntityType.SALMON, EntityType.SQUID, EntityType.TROPICAL_FISH + ); + Set> illagers = Sets.newHashSet( + EntityType.PILLAGER, EntityType.ILLUSIONER, EntityType.VINDICATOR, EntityType.EVOKER, + EntityType.RAVAGER, EntityType.WITCH + ); + + Set> living = allTypes.stream().filter(et -> + !deads.contains(et) && !projectiles.contains(et) && !minecarts.contains(et) + ).collect(Collectors.toSet()); + + Set> regular = allTypes.stream().filter(et -> + living.contains(et) && !undeads.contains(et) && !arthropods.contains(et) && !aquatique.contains(et) && !illagers.contains(et) + ).collect(Collectors.toSet()); + + + 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", new EntityClassDescriptor(null, (e) -> (e instanceof LivingEntity && e.isAlive()), allTypes.stream().filter(living::contains))); + put("!living", new EntityClassDescriptor(null, (e) -> (!(e instanceof LivingEntity) && e.isAlive()), allTypes.stream().filter(et -> !living.contains(et)))); + + put("projectile", new EntityClassDescriptor(null, (e) -> (e instanceof ProjectileEntity && e.isAlive()), allTypes.stream().filter(projectiles::contains))); + put("!projectile", new EntityClassDescriptor(null, (e) -> (!(e instanceof ProjectileEntity) && e.isAlive()), allTypes.stream().filter(et -> !projectiles.contains(et) && !living.contains(et)))); + + put("minecarts", new EntityClassDescriptor(null, (e) -> (e instanceof AbstractMinecartEntity && e.isAlive()), allTypes.stream().filter(minecarts::contains))); + put("!minecarts", new EntityClassDescriptor(null, (e) -> (!(e instanceof AbstractMinecartEntity) && e.isAlive()), allTypes.stream().filter(et -> !minecarts.contains(et) && !living.contains(et)))); + + + // combat groups + + put("arthropod", new EntityClassDescriptor(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() == EntityGroup.ARTHROPOD && e.isAlive()), allTypes.stream().filter(arthropods::contains))); + put("!arthropod", new EntityClassDescriptor(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() != EntityGroup.ARTHROPOD && e.isAlive()), allTypes.stream().filter(et -> !arthropods.contains(et) && living.contains(et)))); + + put("undead", new EntityClassDescriptor(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() == EntityGroup.UNDEAD && e.isAlive()), allTypes.stream().filter(undeads::contains))); + put("!undead", new EntityClassDescriptor(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() != EntityGroup.UNDEAD && e.isAlive()), allTypes.stream().filter(et -> !undeads.contains(et) && living.contains(et)))); + + put("aquatic", new EntityClassDescriptor(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() == EntityGroup.AQUATIC && e.isAlive()), allTypes.stream().filter(aquatique::contains))); + put("!aquatic", new EntityClassDescriptor(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() != EntityGroup.AQUATIC && e.isAlive()), allTypes.stream().filter(et -> !aquatique.contains(et) && living.contains(et)))); + + put("illager", new EntityClassDescriptor(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() == EntityGroup.ILLAGER && e.isAlive()), allTypes.stream().filter(illagers::contains))); + put("!illager", new EntityClassDescriptor(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() != EntityGroup.ILLAGER && e.isAlive()), allTypes.stream().filter(et -> !illagers.contains(et) && living.contains(et)))); + + put("regular", new EntityClassDescriptor(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() == EntityGroup.DEFAULT && e.isAlive()), allTypes.stream().filter(regular::contains))); + put("!regular", new EntityClassDescriptor(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() != EntityGroup.DEFAULT && e.isAlive()), allTypes.stream().filter(et -> !regular.contains(et) && living.contains(et)))); + + for (Identifier typeId : Registry.ENTITY_TYPE.getIds()) + { + EntityType type = Registry.ENTITY_TYPE.get(typeId); + String mobType = ValueConversions.simplify(typeId); + put( mobType, new EntityClassDescriptor(type, EntityPredicates.VALID_ENTITY, Stream.of(type))); + put("!"+mobType, new EntityClassDescriptor(null, (e) -> e.getType() != type && e.isAlive(), allTypes.stream().filter(et -> et != type))); + } + for (SpawnGroup catId : SpawnGroup.values()) + { + String catStr = catId.getName(); + put( catStr, new EntityClassDescriptor(null, e -> ((e.getType().getSpawnGroup() == catId) && e.isAlive()), allTypes.stream().filter(et -> et.getSpawnGroup() == catId))); + put("!"+catStr, new EntityClassDescriptor(null, e -> ((e.getType().getSpawnGroup() != catId) && e.isAlive()), allTypes.stream().filter(et -> et.getSpawnGroup() != catId))); + } + }}; + } + public Value get(String what, Value arg) { if (!(featureAccessors.containsKey(what))) @@ -1264,7 +1346,7 @@ public class EntityValue extends Value EntityEventsGroup.Event event = EntityEventsGroup.Event.byName.get(eventName); if (event == null) throw new InternalExpressionException("Unknown entity event: " + eventName); - ((EntityInterface)entity).getEventContainer().addEvent(event, cc, fun, args); + ((EntityInterface)entity).getEventContainer().addEvent(event, cc.host.getName(), fun, args); } @Override diff --git a/src/main/java/carpet/script/value/FunctionValue.java b/src/main/java/carpet/script/value/FunctionValue.java index 88c2440a..d81276cc 100644 --- a/src/main/java/carpet/script/value/FunctionValue.java +++ b/src/main/java/carpet/script/value/FunctionValue.java @@ -24,13 +24,13 @@ import java.util.stream.Collectors; public class FunctionValue extends Value implements Fluff.ILazyFunction { - private Expression expression; - private Tokenizer.Token token; - private String name; - private LazyValue body; + private final Expression expression; + private final Tokenizer.Token token; + private final String name; + private final LazyValue body; private Map outerState; - private List args; - private String varArgs; + private final List args; + private final String varArgs; private static long variantCounter = 1; private long variant; @@ -99,8 +99,6 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction return name.hashCode()+(int)variant; } - - @Override public boolean equals(Object o) { @@ -178,7 +176,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction @Override public LazyValue lazyEval(Context c, Integer type, Expression e, Tokenizer.Token t, List lazyParams) { - assertArgsOk(lazyParams.size(), (fixedArgs) ->{ + assertArgsOk(lazyParams, (fixedArgs) ->{ if (fixedArgs) // wrong number of args for fixed args { throw new ExpressionException(c, e, t, @@ -264,16 +262,30 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction return StringTag.of(getString()); } - public void assertArgsOk(int size, Consumer feedback) + public void assertArgsOk(List list, Consumer feedback) { - if (varArgs == null && args.size() != size) // wrong number of args for fixed args + int size = list.size(); + if (varArgs == null && args.size() != size) // wrong number of args for fixed args { feedback.accept(true); } - else if (varArgs != null && args.size() > size) // too few args for varargs + else if (varArgs != null && args.size() > size) // too few args for varargs { feedback.accept(false); } } + public static List resolveArgs(List lzargs, Context c, Integer t) + { + List args = new ArrayList<>(lzargs.size()); + lzargs.forEach( v -> args.add( v.evalValue(c, t))); + return args; + } + + public static List lazify(List args) + { + List lzargs = new ArrayList<>(args.size()); + args.forEach( v -> lzargs.add( (c, t) -> v)); + return lzargs; + } } diff --git a/src/main/java/carpet/script/value/NumericValue.java b/src/main/java/carpet/script/value/NumericValue.java index a93dfa3d..4049df78 100644 --- a/src/main/java/carpet/script/value/NumericValue.java +++ b/src/main/java/carpet/script/value/NumericValue.java @@ -10,15 +10,18 @@ import net.minecraft.nbt.Tag; import org.apache.commons.lang3.StringUtils; import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; import java.util.Locale; import static java.lang.Math.abs; public class NumericValue extends Value { - private Double value; + private final Double value; private Long longValue; - final static double epsilon = 1024*Double.MIN_VALUE; + private final static double epsilon = 32*((7*0.1)*10-7); + private final static MathContext displayRounding = new MathContext(12, RoundingMode.HALF_EVEN); public static NumericValue asNumber(Value v1, String id) { @@ -46,7 +49,8 @@ public class NumericValue extends Value { if (value.isInfinite()) return "INFINITY"; if (value.isNaN()) return "NaN"; - return BigDecimal.valueOf(value).stripTrailingZeros().toPlainString(); + // dobules have 16 point precision, 12 is plenty to display + return BigDecimal.valueOf(value).round(displayRounding).stripTrailingZeros().toPlainString(); } catch (NumberFormatException exc) {