From 2167c929d57f93eac809d49a0cc991fa21dff200 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sun, 6 Oct 2019 22:06:38 -0300 Subject: [PATCH] two more events, and nbt cleanups --- .../java/carpet/commands/ScriptCommand.java | 12 +- .../mixins/MinecraftServer_scarpetMixin.java | 8 +- ...PlayNetworkHandler_scarpetEventsMixin.java | 80 +++- .../ServerPlayerEntity_scarpetEventMixin.java | 46 ++ ...InteractionManager_scarpetEventsMixin.java | 5 +- .../java/carpet/script/CarpetEventServer.java | 426 +++++++++++------- .../java/carpet/script/CarpetExpression.java | 32 +- .../carpet/script/CarpetScriptServer.java | 2 +- .../script/value/NBTSerializableValue.java | 57 ++- src/main/resources/carpet.mixins.json | 1 + 10 files changed, 462 insertions(+), 207 deletions(-) create mode 100644 src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java diff --git a/src/main/java/carpet/commands/ScriptCommand.java b/src/main/java/carpet/commands/ScriptCommand.java index 347876f2..fb4b690f 100644 --- a/src/main/java/carpet/commands/ScriptCommand.java +++ b/src/main/java/carpet/commands/ScriptCommand.java @@ -233,7 +233,7 @@ public class ScriptCommand executes( (cc) -> listEvents(cc.getSource())). then(literal("add_to"). then(argument("event", StringArgumentType.word()). - suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.events.eventHandlers.keySet() ,bb)). + suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.keySet() ,bb)). then(argument("call", StringArgumentType.word()). suggests( (cc, bb) -> suggestMatching(suggestFunctionCalls(cc), bb)). executes( (cc) -> CarpetServer.scriptServer.events.addEvent( @@ -253,9 +253,9 @@ public class ScriptCommand )?1:0)))))). then(literal("remove_from"). then(argument("event", StringArgumentType.word()). - suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.events.eventHandlers.keySet() ,bb)). + suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.keySet() ,bb)). then(argument("call", StringArgumentType.greedyString()). - suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.events.eventHandlers.get(StringArgumentType.getString(cc, "event")).callList.stream().map(CarpetEventServer.Callback::toString), bb)). + 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( StringArgumentType.getString(cc, "event"), StringArgumentType.getString(cc, "call") @@ -293,14 +293,14 @@ public class ScriptCommand private static int listEvents(ServerCommandSource source) { Messenger.m(source, "w Lists ALL event handlers:"); - for (String event: CarpetServer.scriptServer.events.eventHandlers.keySet()) + for (CarpetEventServer.Event event: CarpetEventServer.Event.values()) { boolean shownEvent = false; - for (CarpetEventServer.Callback c: CarpetServer.scriptServer.events.eventHandlers.get(event).callList) + for (CarpetEventServer.Callback c: event.handler.callList) { if (!shownEvent) { - Messenger.m(source, "w Handlers for "+event+": "); + Messenger.m(source, "w Handlers for "+event.name+": "); shownEvent = true; } Messenger.m(source, "w - "+c.udf+(c.host==null?"":" (from "+c.host+")")); diff --git a/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java b/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java index 6e008402..a656b640 100644 --- a/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java +++ b/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java @@ -14,6 +14,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.function.BooleanSupplier; +import static carpet.script.CarpetEventServer.Event.ENDER_TICK; +import static carpet.script.CarpetEventServer.Event.NETHER_TICK; +import static carpet.script.CarpetEventServer.Event.TICK; + @Mixin(MinecraftServer.class) public abstract class MinecraftServer_scarpetMixin extends NonBlockingThreadExecutor implements MinecraftServerInterface { @@ -42,6 +46,8 @@ public abstract class MinecraftServer_scarpetMixin extends NonBlockingThreadExec )) public void tickTasks(BooleanSupplier booleanSupplier_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onTick(); + TICK.onTick(); + NETHER_TICK.onTick(); + ENDER_TICK.onTick(); } } diff --git a/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java index aacf95ac..82420afe 100644 --- a/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java @@ -1,6 +1,6 @@ package carpet.mixins; -import carpet.CarpetServer; +import net.minecraft.item.ItemStack; import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.packet.ClientCommandC2SPacket; @@ -16,8 +16,25 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import static carpet.script.CarpetEventServer.Event.PLAYER_ATTACKS_ENTITY; +import static carpet.script.CarpetEventServer.Event.PLAYER_CLICKS_BLOCK; +import static carpet.script.CarpetEventServer.Event.PLAYER_DEPLOYS_ELYTRA; +import static carpet.script.CarpetEventServer.Event.PLAYER_INERACTSW_WITH_ENTITY; +import static carpet.script.CarpetEventServer.Event.PLAYER_RELEASED_ITEM; +import static carpet.script.CarpetEventServer.Event.PLAYER_RIDES; +import static carpet.script.CarpetEventServer.Event.PLAYER_JUMPS; +import static carpet.script.CarpetEventServer.Event.PLAYER_RIGHT_CLICKS_BLOCK; +import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SNEAKING; +import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SPRINTING; +import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SNEAKING; +import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SPRINTING; +import static carpet.script.CarpetEventServer.Event.PLAYER_USES_ITEM; +import static carpet.script.CarpetEventServer.Event.PLAYER_WAKES_UP; + + @Mixin(ServerPlayNetworkHandler.class) public class ServerPlayNetworkHandler_scarpetEventsMixin { @@ -26,9 +43,9 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin @Inject(method = "onPlayerInput", at = @At(value = "RETURN")) private void checkMoves(PlayerInputC2SPacket p, CallbackInfo ci) { - if (p.getSideways() != 0.0F || p.getForward() != 0.0F || p.isJumping() || p.isSneaking()) + if (PLAYER_RIDES.isNeeded() && (p.getSideways() != 0.0F || p.getForward() != 0.0F || p.isJumping() || p.isSneaking())) { - CarpetServer.scriptServer.events.onMountControls(player, p.getSideways(), p.getForward(), p.isJumping(), p.isSneaking()); + PLAYER_RIDES.onMountControls(player, p.getSideways(), p.getForward(), p.isJumping(), p.isSneaking()); } } @@ -38,17 +55,37 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onJump(PlayerMoveC2SPacket playerMoveC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onPlayerJumped(player); + PLAYER_JUMPS.onPlayerEvent(player); } @Inject(method = "onPlayerAction", at = @At( value = "INVOKE", - target = "Lnet/minecraft/server/network/ServerPlayerInteractionManager;method_14263(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/server/network/packet/PlayerActionC2SPacket$Action;Lnet/minecraft/util/math/Direction;I)V" + target = "Lnet/minecraft/server/network/ServerPlayerInteractionManager;method_14263(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/server/network/packet/PlayerActionC2SPacket$Action;Lnet/minecraft/util/math/Direction;I)V", + shift = At.Shift.AFTER )) private void onClicked(PlayerActionC2SPacket packet, CallbackInfo ci) { if (packet.getAction() == PlayerActionC2SPacket.Action.START_DESTROY_BLOCK) - CarpetServer.scriptServer.events.onBlockClicked(player, packet.getPos(), packet.getDirection()); + PLAYER_CLICKS_BLOCK.onBlockAction(player, packet.getPos(), packet.getDirection()); + } + + @Redirect(method = "onPlayerAction", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;stopUsingItem()V" + )) + private void onStopUsing(ServerPlayerEntity serverPlayerEntity) + { + if (PLAYER_RELEASED_ITEM.isNeeded()) + { + Hand hand = serverPlayerEntity.getActiveHand(); + ItemStack stack = serverPlayerEntity.getActiveItem().copy(); + serverPlayerEntity.stopUsingItem(); + PLAYER_RELEASED_ITEM.onItemAction(player, hand, stack); + } + else + { + serverPlayerEntity.stopUsingItem(); + } } @Inject(method = "onPlayerInteractBlock", at = @At( @@ -57,9 +94,12 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onBlockInteracted(PlayerInteractBlockC2SPacket playerInteractBlockC2SPacket_1, CallbackInfo ci) { - Hand hand = playerInteractBlockC2SPacket_1.getHand(); - BlockHitResult hitRes = playerInteractBlockC2SPacket_1.getHitY(); - CarpetServer.scriptServer.events.onRightClickBlock(player, hand, hitRes ); + if (PLAYER_RIGHT_CLICKS_BLOCK.isNeeded()) + { + Hand hand = playerInteractBlockC2SPacket_1.getHand(); + BlockHitResult hitRes = playerInteractBlockC2SPacket_1.getHitY(); + PLAYER_RIGHT_CLICKS_BLOCK.onBlockHit(player, hand, hitRes); + } } @Inject(method = "onPlayerInteractItem", at = @At( @@ -68,7 +108,11 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onItemClicked(PlayerInteractItemC2SPacket playerInteractItemC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onRightClick(player, playerInteractItemC2SPacket_1.getHand()); + if (PLAYER_USES_ITEM.isNeeded()) + { + Hand hand = playerInteractItemC2SPacket_1.getHand(); + PLAYER_USES_ITEM.onItemAction(player, hand, player.getStackInHand(hand).copy()); + } } @Inject(method = "onClientCommand", at = @At( @@ -78,7 +122,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onStartSneaking(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onStartSneaking(player); + PLAYER_STARTS_SNEAKING.onPlayerEvent(player); } @Inject(method = "onClientCommand", at = @At( @@ -88,7 +132,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onStopSneaking(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onStopSneaking(player); + PLAYER_STOPS_SNEAKING.onPlayerEvent(player); } @Inject(method = "onClientCommand", at = @At( @@ -98,7 +142,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onStartSprinting(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onStartSprinting(player); + PLAYER_STARTS_SPRINTING.onPlayerEvent(player); } @Inject(method = "onClientCommand", at = @At( @@ -108,7 +152,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onStopSprinting(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onStopSprinting(player); + PLAYER_STOPS_SPRINTING.onPlayerEvent(player); } @Inject(method = "onClientCommand", at = @At( @@ -119,7 +163,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin private void onWakeUp(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { //weird one - doesn't seem to work, maybe MP - CarpetServer.scriptServer.events.onOutOfBed(player); + PLAYER_WAKES_UP.onPlayerEvent(player); } @Inject(method = "onClientCommand", at = @At( @@ -129,7 +173,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onElytraEngage(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onElytraDeploy(player); + PLAYER_DEPLOYS_ELYTRA.onPlayerEvent(player); } @Inject(method = "onPlayerInteractEntity", at = @At( @@ -138,7 +182,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onEntityInteract(PlayerInteractEntityC2SPacket playerInteractEntityC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onEntityInteracted(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), playerInteractEntityC2SPacket_1.getHand()); + PLAYER_INERACTSW_WITH_ENTITY.onEntityAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), playerInteractEntityC2SPacket_1.getHand()); } @Inject(method = "onPlayerInteractEntity", at = @At( @@ -148,6 +192,6 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin private void onEntityAttack(PlayerInteractEntityC2SPacket playerInteractEntityC2SPacket_1, CallbackInfo ci) { //todo add hit and hand in the future - CarpetServer.scriptServer.events.onEntityAttacked(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld())); + PLAYER_ATTACKS_ENTITY.onEntityAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), null); } } diff --git a/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java b/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java new file mode 100644 index 00000000..d291efb5 --- /dev/null +++ b/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java @@ -0,0 +1,46 @@ +package carpet.mixins; + +import com.mojang.authlib.GameProfile; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.Hand; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import static carpet.script.CarpetEventServer.Event.PLAYER_FINISHED_USING_ITEM; + +@Mixin(ServerPlayerEntity.class) +public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity +{ + public ServerPlayerEntity_scarpetEventMixin(World world_1, GameProfile gameProfile_1) + { + super(world_1, gameProfile_1); + } + + @Shadow protected abstract void method_6040(); + + @Redirect(method = "method_6040", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/entity/player/PlayerEntity;method_6040()V" + )) + private void finishedUsingItem(PlayerEntity playerEntity) + { + if (PLAYER_FINISHED_USING_ITEM.isNeeded()) + { + Hand hand = getActiveHand(); + ItemStack stack = getActiveItem().copy(); + // do vanilla + super.method_6040(); + PLAYER_FINISHED_USING_ITEM.onItemAction((ServerPlayerEntity) (Object)this, hand, stack); + } + else + { + // do vanilla + super.method_6040(); + } + } +} diff --git a/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java b/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java index add9efd1..79e83cec 100644 --- a/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java @@ -1,6 +1,5 @@ package carpet.mixins; -import carpet.CarpetServer; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; @@ -14,6 +13,8 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import static carpet.script.CarpetEventServer.Event.PLAYER_BREAK_BLOCK; + @Mixin(ServerPlayerInteractionManager.class) public class ServerPlayerInteractionManager_scarpetEventsMixin { @@ -26,6 +27,6 @@ public class ServerPlayerInteractionManager_scarpetEventsMixin )) private void onBlockBroken(BlockPos blockPos_1, CallbackInfoReturnable cir, BlockState blockState_1, BlockEntity be, Block b, boolean boolean_1) { - CarpetServer.scriptServer.events.onBlockBroken(player, blockPos_1, blockState_1); + PLAYER_BREAK_BLOCK.onBlockBroken(player, blockPos_1, blockState_1); } } diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index 72a52108..c68e9960 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -7,13 +7,10 @@ import carpet.script.value.ListValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; -import carpet.settings.CarpetSettings; import net.minecraft.block.BlockState; import net.minecraft.entity.damage.DamageSource; -import net.minecraft.nbt.CompoundTag; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.util.Hand; @@ -21,9 +18,6 @@ import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.World; -import net.minecraft.world.dimension.DimensionType; import java.util.ArrayList; import java.util.Arrays; @@ -36,10 +30,11 @@ import java.util.Map; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; public class CarpetEventServer { + public List scheduledCalls = new LinkedList<>(); + public static class Callback { public String host; @@ -146,9 +141,262 @@ public class CarpetEventServer } } - public Map eventHandlers = new HashMap<>(); + public enum Event + { + TICK("tick", new CallbackList(0)) + { + @Override + public void onTick() + { + handler.call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource); + } + }, + NETHER_TICK("tick_nether",new CallbackList(0)) + { + @Override + public void onTick() + { + handler.call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource); + } + }, + ENDER_TICK("tick_ender",new CallbackList(0)) + { + @Override + public void onTick() + { + handler.call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource); + } + }, + PLAYER_JUMPS("player_jumps", new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_DEPLOYS_ELYTRA("player_deploys_elytra",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_WAKES_UP("player_wakes_up",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_RIDES("player_rides",new CallbackList(5)) + { + @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)) + ), player::getCommandSource); + } + }, + PLAYER_USES_ITEM("player_uses_item",new CallbackList(3)) + { + 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)), + ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")) + ); + }, player::getCommandSource); + } + }, + PLAYER_CLICKS_BLOCK("player_clicks_block",new CallbackList(3)) + { + public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) + { + 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); + } + }, + PLAYER_RIGHT_CLICKS_BLOCK("player_right_clicks_block",new CallbackList(6)) + { + @Override + public void onBlockHit(ServerPlayerEntity player, Hand enumhand, BlockHitResult hitRes)//ItemStack itemstack, Hand enumhand, BlockPos blockpos, Direction enumfacing, Vec3d vec3d) + { + handler.call( () -> + { + ItemStack itemstack = player.getStackInHand(enumhand); + BlockPos blockpos = hitRes.getBlockPos(); + 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 NumericValue(vec3d.x), + new NumericValue(vec3d.y), + new NumericValue(vec3d.z) + )) + ); + }, player::getCommandSource); + } + }, + PLAYER_BREAK_BLOCK("player_breaks_block",new CallbackList(2)) + { + @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); + } + }, + PLAYER_INERACTSW_WITH_ENTITY("player_interacts_with_entity",new CallbackList(3)) + { + @Override + public void onEntityAction(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")) + ), player::getCommandSource); + } + }, + PLAYER_ATTACKS_ENTITY("player_attacks_entity",new CallbackList(2)) + { + @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); + } + }, + PLAYER_STARTS_SNEAKING("player_starts_sneaking",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_STOPS_SNEAKING("player_stops_sneaking",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_STARTS_SPRINTING("player_starts_sprinting",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_STOPS_SPRINTING("player_stops_sprinting",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, - public List scheduledCalls = new LinkedList<>(); + PLAYER_RELEASED_ITEM("player_released_item",new CallbackList(3)) + { + @Override + public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) + { + // 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); + } + }, + PLAYER_FINISHED_USING_ITEM("player_finished_using_item",new CallbackList(3)) + { + public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) + { + // 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); + } + }; + // on projectile thrown (arrow from bows, crossbows, tridents, snoballs, e-pearls + + + + + public String name; + public static Map byName = new HashMap(){{ + for (Event e: Event.values()) + { + put(e.name, e); + } + }}; + public CallbackList handler; + Event(String name, CallbackList eventHandler) + { + this.name = name; + this.handler = eventHandler; + } + public boolean isNeeded() + { + return handler.callList.size() > 0; + } + //stubs for calls just to ease calls in vanilla code so they don't need to deal with scarpet value types + public void onTick() { } + public void onPlayerEvent(ServerPlayerEntity player) { } + public void onMountControls(ServerPlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking) { } + public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) { } + public void onBlockAction(ServerPlayerEntity player, BlockPos blockpos, Direction facing) { } + public void onBlockHit(ServerPlayerEntity player, Hand enumhand, BlockHitResult hitRes) { } + public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS) { } + public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { } + } + + + public CarpetEventServer() + { + for (Event e: Event.values()) + e.handler.callList.clear(); + } public void tick() { @@ -175,50 +423,28 @@ public class CarpetEventServer scheduledCalls.add(new ScheduledCall(context, function, args, due)); } - - public CarpetEventServer() - { - eventHandlers.put("tick",new CallbackList(0)); - eventHandlers.put("tick_nether",new CallbackList(0)); - eventHandlers.put("tick_ender",new CallbackList(0)); - eventHandlers.put("player_jumps",new CallbackList(1)); - eventHandlers.put("player_deploys_elytra",new CallbackList(1)); - eventHandlers.put("player_wakes_up",new CallbackList(1)); - eventHandlers.put("player_rides",new CallbackList(5)); - eventHandlers.put("player_uses_item",new CallbackList(3)); - eventHandlers.put("player_clicks_block",new CallbackList(3)); - eventHandlers.put("player_right_clicks_block",new CallbackList(6)); - eventHandlers.put("player_breaks_block",new CallbackList(2)); - eventHandlers.put("player_interacts_with_entity",new CallbackList(3)); - eventHandlers.put("player_attacks_entity",new CallbackList(2)); - eventHandlers.put("player_starts_sneaking",new CallbackList(1)); - eventHandlers.put("player_stops_sneaking",new CallbackList(1)); - eventHandlers.put("player_starts_sprinting",new CallbackList(1)); - eventHandlers.put("player_stops_sprinting",new CallbackList(1)); - } - public boolean addEvent(String event, String host, String funName) { - if (!eventHandlers.containsKey(event)) + if (!Event.byName.containsKey(event)) { return false; } - return eventHandlers.get(event).addEventCall(host, funName); + return Event.byName.get(event).handler.addEventCall(host, funName); } public boolean removeEvent(String event, String funName) { - if (!eventHandlers.containsKey(event)) + if (!Event.byName.containsKey(event)) return false; Callback callback= decodeCallback(funName); - eventHandlers.get(event).removeEventCall(callback.host, callback.udf); + Event.byName.get(event).handler.removeEventCall(callback.host, callback.udf); return true; } public void removeAllHostEvents(String hostName) { - eventHandlers.forEach((e, a) -> a.removeAllCalls(hostName)); + Event.byName.values().forEach((e) -> e.handler.removeAllCalls(hostName)); } private Callback decodeCallback(String funName) @@ -250,131 +476,6 @@ public class CarpetEventServer return new ScheduledCall(cc, function, lazyArgs, 0); } - public void onTick() - { - eventHandlers.get("tick").call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource); - eventHandlers.get("tick_nether").call(Collections::emptyList, () -> - CarpetServer.minecraft_server.getCommandSource().withWorld(CarpetServer.minecraft_server.getWorld(DimensionType.THE_NETHER))); - eventHandlers.get("tick_ender").call(Collections::emptyList, () -> - CarpetServer.minecraft_server.getCommandSource().withWorld(CarpetServer.minecraft_server.getWorld(DimensionType.THE_END))); - } - - public void onPlayerJumped(PlayerEntity player) - { - eventHandlers.get("player_jumps").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onElytraDeploy(ServerPlayerEntity player) - { - eventHandlers.get("player_deploys_elytra").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onOutOfBed(ServerPlayerEntity player) - { - eventHandlers.get("player_wakes_up").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onMountControls(PlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking) - { - eventHandlers.get("player_rides").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)) - ), player::getCommandSource); - } - - public void onRightClick(ServerPlayerEntity player, Hand enumhand) - { - eventHandlers.get("player_uses_item").call( () -> - { - 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")) - ); - }, player::getCommandSource); - } - - public void onBlockClicked(ServerPlayerEntity player, BlockPos blockpos, Direction facing) - { - eventHandlers.get("player_clicks_block").call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new BlockValue(null, player.getServerWorld(), blockpos)), - ((c, t) -> new StringValue(facing.getName())) - ), player::getCommandSource); - } - - public void onRightClickBlock(ServerPlayerEntity player, Hand enumhand, BlockHitResult hitRes)//ItemStack itemstack, Hand enumhand, BlockPos blockpos, Direction enumfacing, Vec3d vec3d) - { - eventHandlers.get("player_right_clicks_block").call( () -> - { - ItemStack itemstack = player.getStackInHand(enumhand); - BlockPos blockpos = hitRes.getBlockPos(); - 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 NumericValue(vec3d.x), - new NumericValue(vec3d.y), - new NumericValue(vec3d.z) - )) - ); - }, player::getCommandSource); - } - - public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS) - { - eventHandlers.get("player_breaks_block").call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new BlockValue(previousBS, player.getServerWorld(), pos)) - ), player::getCommandSource); - } - - public void onEntityInteracted(ServerPlayerEntity player, Entity entity, Hand enumhand) - { - eventHandlers.get("player_interacts_with_entity").call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new EntityValue(entity)), - ((c, t) -> new StringValue(enumhand==Hand.MAIN_HAND?"mainhand":"offhand")) - ), player::getCommandSource); - } - - public void onEntityAttacked(ServerPlayerEntity player, Entity entity) - { - eventHandlers.get("player_attacks_entity").call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new EntityValue(entity)) - ), player::getCommandSource); - } - - public void onStartSneaking(ServerPlayerEntity player) - { - eventHandlers.get("player_starts_sneaking").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onStopSneaking(ServerPlayerEntity player) - { - eventHandlers.get("player_stops_sneaking").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onStartSprinting(ServerPlayerEntity player) - { - eventHandlers.get("player_starts_sprinting").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onStopSprinting(ServerPlayerEntity player) - { - eventHandlers.get("player_stops_sprinting").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - public ScheduledCall makeDeathCall(CarpetContext cc, String function, List extraArgs) { return makeEventCall(cc, function, extraArgs, 2); @@ -413,7 +514,4 @@ public class CarpetEventServer (c, t) -> source.getAttacker()==null?Value.NULL:new EntityValue(source.getAttacker()) )); } - - - } diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index d2b75ded..dacd3d24 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -347,7 +347,7 @@ public class CarpetExpression *

Blocks / World API

*
*

Specifying blocks

- *

block(x, y, z), block(state)

+ *

block(x, y, z), block(l(x,y,z)), block(state)

*

Returns either a block from specified location, or block with a specific state * (as used by /setblock command), so allowing for block properties, block entity data etc. * Blocks otherwise can be referenced everywhere by its simple string name, but its only used in its default state

@@ -1189,6 +1189,8 @@ public class CarpetExpression Value nbtValue = lv.get(inventoryLocator.offset+3).evalValue(c); if (nbtValue instanceof NBTSerializableValue) nbt = ((NBTSerializableValue)nbtValue).getCompoundTag(); + else if (nbtValue instanceof NullValue) + nbt = null; else nbt = new NBTSerializableValue(nbtValue.getString()).getCompoundTag(); } @@ -1251,7 +1253,7 @@ public class CarpetExpression if (inventoryLocator == null) return (_c, _t) -> Value.NULL; if (lv.size() <= inventoryLocator.offset) - throw new InternalExpressionException("inventory_set requires at least an item to be removed"); + throw new InternalExpressionException("inventory_remove requires at least an item to be removed"); ItemStackArgument searchItem = NBTSerializableValue.parseItem(lv.get(inventoryLocator.offset).evalValue(c).getString()); int amount = 1; if (lv.size() > inventoryLocator.offset+1) @@ -1540,10 +1542,9 @@ public class CarpetExpression * that corresponds to the path. For specification of path attribute, consult * vanilla /data get entity command.

*

Note that calls to nbt are considerably more expensive comparing to other - * calls in Minecraft API, and should only be used when there is no other option. Also returned - * NBT is just a string to any retrieval of information post can currently only be done with matching - * operator ~. With time we are hoping not to support the 'nbt' call better, - * but rather to fill the API, so that 'nbt' calls are not needed

+ * calls in Minecraft API, and should only be used when there is no other option.A returned value is of type nbt, + * which can be further manipulated with nbt type objects via get, put, has, delete, so try to use API calls + * first

*

Entity Modification

*

Like with entity querying, entity modifications happen through one function. Most position and movements * modifications don't work currently on players as their position is controlled by clients.

@@ -1724,8 +1725,12 @@ public class CarpetExpression boolean hasTag = false; if (lv.size() > position.offset) { - hasTag = true; - tag = new NBTSerializableValue(lv.get(position.offset).evalValue(c).getString()).getCompoundTag(); + NBTSerializableValue v = NBTSerializableValue.parseString(lv.get(position.offset).evalValue(c).getString()); + if (v != null) + { + hasTag = true; + tag = v.getCompoundTag(); + } } tag.putString("id", entityId.toString()); Vec3d vec3d = position.vec; @@ -2355,8 +2360,7 @@ public class CarpetExpression *

System function

*

nbt(expr)

*

Treats the argument as a nbt serializable string and returns its nbt value. - * In case nbt is not in a correct nbt compound tag format, the execution of a script will stop with an error, - * whenever that tag is used.

+ * In case nbt is not in a correct nbt compound tag format, it will return null value.

*

print(expr)

*

Displays the result of the expression to the chat. Overrides default scarpet behaviour of * sending everyting to stderr.

@@ -2376,11 +2380,11 @@ public class CarpetExpression * loop(1000,tick(100)) // runs the game twice as slow for 1000 ticks * *

current_dimension()

- *

Returns current dimension that scripts run in.

+ *

Returns current dimension that the script runs in.

*

in_dimension(smth, expr)

*

Evaluates the expression expr with different dimension execution context. smth can * be an entity, world-localized block, so not block('stone'), or a string representing a dimension like: - * 'nether', 'end' or 'overworld'.

+ * 'nether', 'the_nether', 'end' or 'overworld', etc.

*

schedule(delay, function, args...)

*

Schedules a user defined function to run with a specified delay ticks of delay. * Scheduled functions run at the end of the tick, and they will run in order they were scheduled.

@@ -2677,7 +2681,9 @@ public class CarpetExpression Value v = lv.get(0).evalValue(c); if (v instanceof NBTSerializableValue) return (cc, tt) -> v; - Value ret = new NBTSerializableValue(v.getString()); + Value ret = NBTSerializableValue.parseString(v.getString()); + if (ret == null) + return LazyValue.NULL; return (cc, tt) -> ret; }); diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index f9e5c602..d4a29872 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -200,7 +200,7 @@ public class CarpetScriptServer if (!fun.startsWith("__on_")) continue; String event = fun.replaceFirst("__on_",""); - if (!events.eventHandlers.containsKey(event)) + if (!CarpetEventServer.Event.byName.containsKey(event)) continue; events.addEvent(event, hostName, fun); } diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 79b250d9..5618c869 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -27,6 +27,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; public class NBTSerializableValue extends Value implements ContainerValueInterface @@ -55,6 +57,22 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa }; } + public static NBTSerializableValue parseString(String nbtString) + { + Tag tag; + try + { + tag = (new StringNbtReader(new StringReader(nbtString))).parseTag(); + } + catch (CommandSyntaxException e) + { + return null; + } + NBTSerializableValue value = new NBTSerializableValue(tag); + value.nbtString = nbtString; + return value; + } + public NBTSerializableValue(Tag tag) { nbtTag = tag; @@ -261,10 +279,15 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa } + private Tag modify_insert(int index, NbtPathArgumentType.NbtPath nbtPath, Tag newElement) + { + return modify_insert(index, nbtPath, newElement, this.getTag().copy()); + } + + private Tag modify_insert(int index, NbtPathArgumentType.NbtPath nbtPath, Tag newElement, Tag currentTag) { Collection targets; - Tag currentTag = getTag().copy(); try { targets = nbtPath.putIfAbsent(currentTag, ListTag::new); @@ -284,7 +307,8 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa try { AbstractListTag targetList = (AbstractListTag) target; - targetList.addTag(index < 0 ? targetList.size() + index + 1 : index, newElement.copy()); + if (!targetList.addTag(index < 0 ? targetList.size() + index + 1 : index, newElement.copy())) + return null; modified = true; } catch (IndexOutOfBoundsException ignored) @@ -320,6 +344,35 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa private Tag modify_replace(NbtPathArgumentType.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1) { Tag originalTag = getTag().copy(); + String pathText = nbtPath.toString(); + if (pathText.endsWith("]")) // workaround for array replacement or item in the array replacement + { + if (nbtPath.remove(originalTag)==0) + return null; + Pattern pattern = Pattern.compile("\\[[^\\[]*]$"); + Matcher matcher = pattern.matcher(pathText); + if (!matcher.find()) // malformed path + { + return null; + } + String arrAccess = matcher.group(); + int pos; + if (arrAccess.length()==2) // we just removed entire array + pos = 0; + else + { + try + { + pos = Integer.parseInt(arrAccess.substring(1, arrAccess.length() - 1)); + } + catch (NumberFormatException e) + { + return null; + } + } + NbtPathArgumentType.NbtPath newPath = cachePath(pathText.substring(0, pathText.length()-arrAccess.length())); + return modify_insert(pos,newPath,replacement, originalTag); + } try { nbtPath.put(originalTag, () -> replacement); diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index bbda0dea..2781ed19 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -123,6 +123,7 @@ "Entity_scarpetEventsMixin", "LivingEntity_scarpetEventsMixin", "DamageTracker_scarpetEventMixin", + "ServerPlayerEntity_scarpetEventMixin", "PistonBlock_movableTEMixin",