From 730446b219f06674cfeb90c1221048e80a6d6a37 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sun, 2 Jun 2019 01:54:22 -0300 Subject: [PATCH] schedules and events from jarmod --- src/main/java/carpet/CarpetServer.java | 1 + .../java/carpet/commands/ScriptCommand.java | 54 ++- .../mixins/MinecraftServer_scarpetMixin.java | 13 + ...PlayNetworkHandler_scarpetEventsMixin.java | 153 ++++++++ ...InteractionManager_scarpetEventsMixin.java | 29 ++ .../java/carpet/mixins/SpawnHelperMixin.java | 16 +- .../java/carpet/script/CarpetEventServer.java | 340 ++++++++++++++++++ .../java/carpet/script/CarpetExpression.java | 158 ++++++-- .../carpet/script/CarpetScriptServer.java | 53 ++- src/main/java/carpet/script/ScriptHost.java | 29 +- .../exception/InvalidCallbackException.java | 5 + .../java/carpet/script/value/BlockValue.java | 32 +- .../java/carpet/script/value/EntityValue.java | 2 +- src/main/resources/carpet.mixins.json | 2 + 14 files changed, 825 insertions(+), 62 deletions(-) create mode 100644 src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java create mode 100644 src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java create mode 100644 src/main/java/carpet/script/CarpetEventServer.java create mode 100644 src/main/java/carpet/script/exception/InvalidCallbackException.java diff --git a/src/main/java/carpet/CarpetServer.java b/src/main/java/carpet/CarpetServer.java index 46d193f0..22f088c6 100644 --- a/src/main/java/carpet/CarpetServer.java +++ b/src/main/java/carpet/CarpetServer.java @@ -46,6 +46,7 @@ public class CarpetServer // static for now - easier to handle all around the co { TickSpeed.tick(server); HUDController.update_hud(server); + scriptServer.events.tick(); //in case something happens CarpetSettings.impendingFillSkipUpdates = false; } diff --git a/src/main/java/carpet/commands/ScriptCommand.java b/src/main/java/carpet/commands/ScriptCommand.java index e068d0be..394e8149 100644 --- a/src/main/java/carpet/commands/ScriptCommand.java +++ b/src/main/java/carpet/commands/ScriptCommand.java @@ -2,6 +2,7 @@ package carpet.commands; import carpet.CarpetServer; import carpet.CarpetSettings; +import carpet.script.CarpetEventServer; import carpet.script.CarpetExpression; import carpet.script.Expression; import carpet.script.ExpressionInspector; @@ -193,9 +194,42 @@ public class ScriptCommand return success?1:0; })); + LiteralArgumentBuilder q = literal("event").requires( (player) -> player.hasPermissionLevel(2) ). + executes( (cc) -> listEvents(cc.getSource())). + then(literal("add_to"). + then(argument("event", StringArgumentType.word()). + suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.events.eventHandlers.keySet() ,bb)). + then(argument("call", StringArgumentType.word()). + suggests( (cc, bb) -> suggestMatching(suggestFunctionCalls(cc), bb)). + executes( (cc) -> CarpetServer.scriptServer.events.addEvent( + StringArgumentType.getString(cc, "event"), + null, + StringArgumentType.getString(cc, "call") + )?1:0)). + then(literal("from"). + then(argument("package", StringArgumentType.word()). + suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.modules.keySet(), bb)). + then(argument("call", StringArgumentType.word()). + suggests( (cc, bb) -> suggestMatching(suggestFunctionCalls(cc), bb)). + executes( (cc) -> CarpetServer.scriptServer.events.addEvent( + StringArgumentType.getString(cc, "event"), + StringArgumentType.getString(cc, "package"), + StringArgumentType.getString(cc, "call") + )?1:0)))))). + then(literal("remove_from"). + then(argument("event", StringArgumentType.word()). + suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.events.eventHandlers.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)). + executes( (cc) -> CarpetServer.scriptServer.events.removeEvent( + StringArgumentType.getString(cc, "event"), + StringArgumentType.getString(cc, "call") + )?1:0)))); + + dispatcher.register(literal("script"). requires((player) -> CarpetSettings.getBool("commandScript")). - then(b).then(u).then(o).then(l).then(s).then(c).then(h).then(i).then(e).then(t).then(a).then(f)); + then(b).then(u).then(o).then(l).then(s).then(c).then(h).then(i).then(e).then(t).then(a).then(f).then(q)); dispatcher.register(literal("script"). requires((player) -> CarpetSettings.getBool("commandScript")). then(literal("in"). @@ -222,6 +256,24 @@ public class ScriptCommand ScriptHost host = getHost(c); return host.getPublicFunctions(); } + private static int listEvents(ServerCommandSource source) + { + Messenger.m(source, "w Lists ALL event handlers:"); + for (String event: CarpetServer.scriptServer.events.eventHandlers.keySet()) + { + boolean shownEvent = false; + for (CarpetEventServer.Callback c: CarpetServer.scriptServer.events.eventHandlers.get(event).callList) + { + if (!shownEvent) + { + Messenger.m(source, "w Handlers for "+event+": "); + shownEvent = true; + } + Messenger.m(source, "w - "+c.udf+(c.host==null?"":" (from "+c.host+")")); + } + } + return 1; + } private static int listGlobals(CommandContext context) { ScriptHost host = getHost(context); diff --git a/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java b/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java index 2540b068..6e008402 100644 --- a/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java +++ b/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java @@ -1,5 +1,6 @@ package carpet.mixins; +import carpet.CarpetServer; import carpet.fakes.MinecraftServerInterface; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerTask; @@ -7,6 +8,9 @@ import net.minecraft.util.NonBlockingThreadExecutor; import net.minecraft.util.SystemUtil; 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.callback.CallbackInfo; import java.util.function.BooleanSupplier; @@ -31,4 +35,13 @@ public abstract class MinecraftServer_scarpetMixin extends NonBlockingThreadExec tick(isAhead); executeTaskQueue(); } + + @Inject(method = "tick", at = @At( + value = "CONSTANT", + args = "stringValue=tallying" + )) + public void tickTasks(BooleanSupplier booleanSupplier_1, CallbackInfo ci) + { + CarpetServer.scriptServer.events.onTick(); + } } diff --git a/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java new file mode 100644 index 00000000..7fb9b64e --- /dev/null +++ b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java @@ -0,0 +1,153 @@ +package carpet.mixins; + +import carpet.CarpetServer; +import carpet.CarpetSettings; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.network.packet.ClientCommandC2SPacket; +import net.minecraft.server.network.packet.PlayerActionC2SPacket; +import net.minecraft.server.network.packet.PlayerInputC2SPacket; +import net.minecraft.server.network.packet.PlayerInteractBlockC2SPacket; +import net.minecraft.server.network.packet.PlayerInteractEntityC2SPacket; +import net.minecraft.server.network.packet.PlayerInteractItemC2SPacket; +import net.minecraft.server.network.packet.PlayerMoveC2SPacket; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +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.callback.CallbackInfo; + +@Mixin(ServerPlayNetworkHandler.class) +public class ServerPlayNetworkHandler_scarpetEventsMixin +{ + @Shadow public ServerPlayerEntity player; + + @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()) + { + CarpetServer.scriptServer.events.onMountControls(player, p.getSideways(), p.getForward(), p.isJumping(), p.isSneaking()); + } + } + + @Inject(method = "onPlayerMove", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;jump()V" + )) + private void onJump(PlayerMoveC2SPacket playerMoveC2SPacket_1, CallbackInfo ci) + { + CarpetServer.scriptServer.events.onPlayerJumped(player); + } + + @Inject(method = "onPlayerAction", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerInteractionManager;method_14263(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)V" + )) + private void onClicked(PlayerActionC2SPacket packet, CallbackInfo ci) + { + CarpetServer.scriptServer.events.onBlockClicked(player, packet.getPos(), packet.getDirection()); + } + + @Inject(method = "onPlayerInteractBlock", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerInteractionManager;interactBlock(Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/world/World;Lnet/minecraft/item/ItemStack;Lnet/minecraft/util/Hand;Lnet/minecraft/util/hit/BlockHitResult;)Lnet/minecraft/util/ActionResult;" + )) + 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 ); + } + + @Inject(method = "onPlayerInteractItem", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;updateLastActionTime()V" + )) + private void onItemClicked(PlayerInteractItemC2SPacket playerInteractItemC2SPacket_1, CallbackInfo ci) + { + CarpetServer.scriptServer.events.onRightClick(player, playerInteractItemC2SPacket_1.getHand()); + } + + @Inject(method = "onClientCommand", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;setSneaking(Z)V", + ordinal = 0 + )) + private void onStartSneaking(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) + { + CarpetServer.scriptServer.events.onStartSneaking(player); + } + + @Inject(method = "onClientCommand", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;setSneaking(Z)V", + ordinal = 1 + )) + private void onStopSneaking(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) + { + CarpetServer.scriptServer.events.onStopSneaking(player); + } + + @Inject(method = "onClientCommand", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;setSprinting(Z)V", + ordinal = 0 + )) + private void onStartSprinting(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) + { + CarpetServer.scriptServer.events.onStartSprinting(player); + } + + @Inject(method = "onClientCommand", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;setSprinting(Z)V", + ordinal = 1 + )) + private void onStopSprinting(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) + { + CarpetServer.scriptServer.events.onStopSprinting(player); + } + + @Inject(method = "onClientCommand", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;isSleeping()Z", + shift = At.Shift.BEFORE + )) + private void onWakeUp(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) + { + //weird one - doesn't seem to work, maybe MP + CarpetServer.scriptServer.events.onOutOfBed(player); + } + + @Inject(method = "onClientCommand", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;isFallFlying()Z", + shift = At.Shift.BEFORE + )) + private void onElytraEngage(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) + { + CarpetServer.scriptServer.events.onElytraDeploy(player); + } + + @Inject(method = "onPlayerInteractEntity", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;interact(Lnet/minecraft/entity/Entity;Lnet/minecraft/util/Hand;)Lnet/minecraft/util/ActionResult;" + )) + private void onEntityInteract(PlayerInteractEntityC2SPacket playerInteractEntityC2SPacket_1, CallbackInfo ci) + { + CarpetServer.scriptServer.events.onEntityInteracted(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), playerInteractEntityC2SPacket_1.getHand()); + } + + @Inject(method = "onPlayerInteractEntity", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;attack(Lnet/minecraft/entity/Entity;)V" + )) + 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())); + } +} diff --git a/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java b/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java new file mode 100644 index 00000000..1eda4fb9 --- /dev/null +++ b/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java @@ -0,0 +1,29 @@ +package carpet.mixins; + +import carpet.CarpetServer; +import net.minecraft.block.BlockState; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.network.ServerPlayerInteractionManager; +import net.minecraft.util.math.BlockPos; +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.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ServerPlayerInteractionManager.class) +public class ServerPlayerInteractionManager_scarpetEventsMixin +{ + @Shadow public ServerPlayerEntity player; + + @Inject(method = "destroyBlock", locals = LocalCapture.CAPTURE_FAILEXCEPTION, at = @At( + value = "INVOKE", + target = "Lnet/minecraft/block/Block;onBroken(Lnet/minecraft/world/IWorld;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V", + shift = At.Shift.BEFORE + )) + private void onBlockBroken(BlockPos blockPos_1, CallbackInfoReturnable cir, BlockState blockState_1, boolean boolean_1) + { + CarpetServer.scriptServer.events.onBlockBroken(player, blockPos_1, blockState_1); + } +} diff --git a/src/main/java/carpet/mixins/SpawnHelperMixin.java b/src/main/java/carpet/mixins/SpawnHelperMixin.java index d703866d..31d2dddf 100644 --- a/src/main/java/carpet/mixins/SpawnHelperMixin.java +++ b/src/main/java/carpet/mixins/SpawnHelperMixin.java @@ -3,11 +3,13 @@ package carpet.mixins; import carpet.CarpetSettings; import carpet.fakes.WorldInterface; import carpet.utils.SpawnReporter; +import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.mob.MobEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BoundingBox; +import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShapes; import net.minecraft.world.SpawnHelper; import net.minecraft.world.World; @@ -35,13 +37,17 @@ public class SpawnHelperMixin if (CarpetSettings.b_lagFreeSpawning) { BlockPos.Mutable blockpos = new BlockPos.Mutable(); - for (int x = (int)bb.minX, maxx = (int)Math.ceil(bb.maxX); x < maxx; x++) - for (int z = (int)bb.minZ, maxz = (int)Math.ceil(bb.maxZ); z < maxz; z++) - for (int y = (int)bb.minY, maxy = (int)Math.ceil(bb.maxY); y < maxy; y++) + for (int y = (int)bb.minY, maxy = (int)Math.ceil(bb.maxY); y < maxy; y++) + for (int x = (int)bb.minX, maxx = (int)Math.ceil(bb.maxX); x < maxx; x++) + for (int z = (int)bb.minZ, maxz = (int)Math.ceil(bb.maxZ); z < maxz; z++) { blockpos.set(x, y, z); - if (world.getBlockState(blockpos).getCollisionShape(world, blockpos) != VoxelShapes.empty()) - return world.doesNotCollide(bb); + VoxelShape box = world.getBlockState(blockpos).getCollisionShape(world, blockpos); + if ( box == VoxelShapes.empty()) + continue; + if (Block.isShapeFullCube(box)) + return false; + return world.doesNotCollide(bb); } return true; } diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java new file mode 100644 index 00000000..f08c5dfd --- /dev/null +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -0,0 +1,340 @@ +package carpet.script; + +import carpet.CarpetServer; +import carpet.script.value.BlockValue; +import carpet.script.value.EntityValue; +import carpet.script.value.ListValue; +import carpet.script.value.NumericValue; +import carpet.script.value.StringValue; +import net.minecraft.block.BlockState; +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; +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; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CarpetEventServer +{ + public static class Callback + { + public String host; + public String udf; + + public Callback(String host, String udf) + { + this.host = host; + this.udf = udf; + } + + @Override + public String toString() + { + return udf+((host==null)?"":"(from "+host+")"); + } + } + + public static class ScheduledCall extends Callback + { + public List args; + public ServerCommandSource context_source; + public BlockPos context_origin; + public long dueTime; + + public ScheduledCall(CarpetContext context, String udf, List args, long dueTime) + { + super(context.host.getName(), udf); + this.args = args; + this.context_source = context.s; + this.context_origin = context.origin; + this.dueTime = dueTime; + } + + public void execute() + { + CarpetServer.scriptServer.runas(context_origin, context_source, host, udf, args); + } + } + + public static class CallbackList + { + + public List callList; + public int reqArgs; + + public CallbackList(int reqArgs) + { + this.callList = new ArrayList<>(); + this.reqArgs = reqArgs; + } + + public void call(Supplier> argumentSupplier, Supplier cmdSourceSupplier) + { + if (callList.size() > 0) + { + List argv = argumentSupplier.get(); // empty for onTickDone + ServerCommandSource source = cmdSourceSupplier.get(); + assert argv.size() == reqArgs; + callList.removeIf(call -> !CarpetServer.scriptServer.runas(source, call.host, call.udf, argv)); // this actually does the calls + } + } + public boolean addEventCall(String hostName, String funName) + { + ScriptHost host = CarpetServer.scriptServer.getHostByName(hostName); + if (host == null) + { + // impossible call to add + return false; + } + UserDefinedFunction udf = host.globalFunctions.get(funName); + if (udf == null || udf.getArguments().size() != reqArgs) + { + // call won't match arguments + return false; + } + //all clear + //remove duplicates + removeEventCall(hostName, funName); + callList.add(new Callback(hostName, funName)); + return true; + } + public void removeEventCall(String hostName, String callName) + { + callList.removeIf((c)-> c.udf.equalsIgnoreCase(callName) && ( hostName == null || c.host.equalsIgnoreCase(hostName) ) ); + } + } + + public Map eventHandlers = new HashMap<>(); + + public List scheduledCalls = new LinkedList<>(); + + public void tick() + { + Iterator eventIterator = scheduledCalls.iterator(); + List currentCalls = new ArrayList<>(); + while(eventIterator.hasNext()) + { + ScheduledCall call = eventIterator.next(); + call.dueTime--; + if (call.dueTime <= 0) + { + currentCalls.add(call); + eventIterator.remove(); + } + } + for (ScheduledCall call: currentCalls) + { + call.execute(); + } + + } + public void scheduleCall(CarpetContext context, String function, List args, long due) + { + 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)) + { + return false; + } + return eventHandlers.get(event).addEventCall(host, funName); + } + + public boolean removeEvent(String event, String funName) + { + + if (!eventHandlers.containsKey(event)) + return false; + Callback callback= decodeCallback(funName); + eventHandlers.get(event).removeEventCall(callback.host, callback.udf); + return true; + } + + private Callback decodeCallback(String funName) + { + Pattern find = Pattern.compile("(\\w+)\\(from (\\w+)\\)"); + Matcher matcher = find.matcher(funName); + if(matcher.matches()) + { + return new Callback(matcher.group(2), matcher.group(1)); + } + return new Callback(null, funName); + } + + 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.of( + new StringValue(Registry.ITEM.getId(itemstack.getItem()).getPath()), + new NumericValue(itemstack.getAmount()), + new StringValue(itemstack.toTag(new CompoundTag()).toString()) + )), + ((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.world, 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.of( + new StringValue(Registry.ITEM.getId(itemstack.getItem()).getPath()), + new NumericValue(itemstack.getAmount()), + new StringValue(itemstack.toTag(new CompoundTag()).toString()) + )), + ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")), + ((c, t) -> new BlockValue(null, player.world, 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(PlayerEntity player, BlockPos pos, BlockState previousBS) + { + eventHandlers.get("player_breaks_block").call( () -> Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> new BlockValue(previousBS, player.getEntityWorld(), 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(PlayerEntity player) + { + eventHandlers.get("player_starts_sneaking").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + + public void onStopSneaking(PlayerEntity player) + { + eventHandlers.get("player_stops_sneaking").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + + public void onStartSprinting(PlayerEntity player) + { + eventHandlers.get("player_starts_sprinting").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + + public void onStopSprinting(PlayerEntity player) + { + eventHandlers.get("player_stops_sprinting").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } +} diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index c7f90dc9..5a0f39a4 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -21,11 +21,16 @@ import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.enchantment.Enchantments; import net.minecraft.entity.EntityType; +import net.minecraft.item.Items; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.command.arguments.ParticleArgumentType; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; import net.minecraft.client.network.packet.PlaySoundIdS2CPacket; import net.minecraft.particle.ParticleEffect; import net.minecraft.block.BlockPlacementEnvironment; @@ -44,6 +49,8 @@ import net.minecraft.world.LightType; import net.minecraft.world.World; import net.minecraft.server.world.ServerWorld; import net.minecraft.world.Heightmap; +import net.minecraft.world.loot.context.LootContext; +import net.minecraft.world.loot.context.LootContextParameters; import org.apache.commons.lang3.tuple.Pair; import java.util.ArrayList; @@ -302,6 +309,12 @@ public class CarpetExpression *

Causes a block to tick at position.

*

random_tick(pos)

*

Causes a random tick at position.

+ *

destroy(pos), destroy(pos, -1), destroy(pos, <N>)

+ *

Destroys the block like it was harvest by a player. Add -1 for silk touch, and positive number for fortune level.

+ *

harvest(player, pos)

+ *

Causes a block to be harvested by a specified player entity. Honors player item enchantments, as well as damages the + * tool if applicable. If the entity is not a valid player, no block gets destroyed.

+ * * *

Block and World querying

* @@ -518,13 +531,7 @@ public class CarpetExpression { throw new InternalExpressionException("Block requires at least one parameter"); } - if (lv.size() == 1) - { - Value retval = BlockValue.fromCommandExpression(lv.get(0).evalValue(cc).getString()); - return (c_, t_) -> retval; - //return new BlockValue(Registry.field_212618_g.get(new Identifier(lv.get(0).getString())).getDefaultState(), origin); - } - Value retval = BlockValue.fromParams(cc, lv, 0).block; + Value retval = BlockValue.fromParams(cc, lv, 0, true).block; return (c_, t_) -> retval; }); @@ -681,36 +688,95 @@ public class CarpetExpression World world = cc.s.getWorld(); if (lv.size() < 2 || lv.size() % 2 == 1) throw new InternalExpressionException("set block should have at least 2 params and odd attributes"); - BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 0); - Value blockArg = lv.get(locator.offset).evalValue(cc); - BlockValue bv = ((blockArg instanceof BlockValue)) ? (BlockValue) blockArg : BlockValue.fromString(blockArg.getString()); - if (bv == BlockValue.NULL) - throw new InternalExpressionException("block to set to should be a valid block"); - BlockState bs = bv.getBlockState(); - - BlockState targetBlockState = world.getBlockState(locator.block.getPos()); - if (lv.size()==1+locator.offset && !(blockArg instanceof BlockValue)) // no reqs for properties - if (targetBlockState.getBlock() == bs.getBlock()) - return (c_, t_) -> Value.FALSE; - - StateFactory states = bs.getBlock().getStateFactory(); - - for (int i = 1+locator.offset; i < lv.size(); i += 2) + BlockValue.LocatorResult targetLocator = BlockValue.fromParams(cc, lv, 0); + BlockValue.LocatorResult sourceLocator = BlockValue.fromParams(cc, lv, targetLocator.offset, true); + BlockState sourceBlockState = sourceLocator.block.getBlockState(); + BlockState targetBlockState = world.getBlockState(targetLocator.block.getPos()); + if (sourceLocator.offset < lv.size()) { - String paramString = lv.get(i).evalValue(c).getString(); - Property property = states.getProperty(paramString); - if (property == null) - throw new InternalExpressionException("property " + paramString + " doesn't apply to " + blockArg.getString()); - - String paramValue = lv.get(i + 1).evalValue(c).getString(); - - bs = setProperty(property, paramString, paramValue, bs); + StateFactory states = sourceBlockState.getBlock().getStateFactory(); + for (int i = sourceLocator.offset; i < lv.size(); i += 2) + { + String paramString = lv.get(i).evalValue(c).getString(); + Property property = states.getProperty(paramString); + if (property == null) + throw new InternalExpressionException("property " + paramString + " doesn't apply to " + sourceLocator.block.getString()); + String paramValue = lv.get(i + 1).evalValue(c).getString(); + sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState); + } } - cc.s.getWorld().setBlockState(locator.block.getPos(), bs, 2 | (CarpetSettings.getBool("fillUpdates") ? 0 : 1024)); - Value retval = new BlockValue(bs, world, locator.block.getPos()); + if (sourceBlockState == targetBlockState) + return (c_, t_) -> Value.FALSE; + if (!CarpetSettings.b_fillUpdates) CarpetSettings.impendingFillSkipUpdates = true; + cc.s.getWorld().setBlockState(targetLocator.block.getPos(), sourceBlockState, 2); + CarpetSettings.impendingFillSkipUpdates = false; + Value retval = new BlockValue(sourceBlockState, world, targetLocator.block.getPos()); return (c_, t_) -> retval; }); + this.expr.addLazyFunction("destroy", -1, (c, t, lv) -> { + + CarpetContext cc = (CarpetContext)c; + ServerWorld world = cc.s.getWorld(); + BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 0); + BlockState state = locator.block.getBlockState(); + if (state.isAir()) + return (c_, t_) -> Value.FALSE; + BlockPos where = locator.block.getPos(); + BlockEntity be = world.getBlockEntity(where); + long how = 0; + if (lv.size() > locator.offset) + how = NumericValue.asNumber(lv.get(locator.offset).evalValue(cc)).getLong(); + world.clearBlockState(where, false); + world.playLevelEvent(null, 2001, where, Block.getRawIdFromState(state)); + if (how < 0) + { + Block.dropStack(world, where, new ItemStack(state.getBlock())); + } + else + { + ItemStack tool = new ItemStack(Items.DIAMOND_PICKAXE, 1); + tool.addEnchantment(Enchantments.FORTUNE,(int)how); + + LootContext.Builder lootContext$Builder_1 = (new LootContext.Builder(world)). + setRandom(world.random). + put(LootContextParameters.POSITION, where). + put(LootContextParameters.BLOCK_STATE, state). + putNullable(LootContextParameters.BLOCK_ENTITY, be). + putNullable(LootContextParameters.THIS_ENTITY, cc.s.getEntity()). + put(LootContextParameters.TOOL, tool); + state.getDroppedStacks(lootContext$Builder_1).forEach((stack) -> Block.dropStack(world, where, stack)); + + //Block.dropStacks(state, world, where, be, cc.s.getEntity(), tool ); + } + return (c_, t_) -> Value.TRUE; + }); + + this.expr.addLazyFunction("harvest", -1, (c, t, lv) -> { + + CarpetContext cc = (CarpetContext)c; + World world = cc.s.getWorld(); + Value entityValue = lv.get(0).evalValue(cc); + if (!(entityValue instanceof EntityValue)) + return (c_, t_) -> Value.FALSE; + Entity e = ((EntityValue) entityValue).getEntity(); + if (!(e instanceof PlayerEntity)) + return (c_, t_) -> Value.FALSE; + PlayerEntity player = (PlayerEntity)e; + + BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 1); + BlockState state = locator.block.getBlockState(); + BlockPos where = locator.block.getPos(); + BlockEntity be = world.getBlockEntity(where); + world.clearBlockState(where, false); + ItemStack itemstack2 = player.getMainHandStack(); + itemstack2.onBlockBroken(player.world, state, where, player); + ItemStack itemstack1 = itemstack2.isEmpty() ? ItemStack.EMPTY : itemstack2.copy(); + state.getBlock().afterBreak(world, player, where,state, be, itemstack1); + world.playLevelEvent(null, 2001, where, Block.getRawIdFromState(state)); + return (c_, t_) -> Value.TRUE; + }); + this.expr.addLazyFunction("blocks_movement", -1, (c, t, lv) -> booleanStateTest(c, "blocks_movement", lv, (s, p) -> !s.canPlaceAtSide(((CarpetContext) c).s.getWorld(), p, BlockPlacementEnvironment.LAND))); @@ -891,7 +957,7 @@ public class CarpetExpression *

Returns triple of short name, stack count, and NBT of item held in slot. * Available options for slot are:

*
    - *
  • main
  • + *
  • mainhand
  • *
  • offhand
  • *
  • head
  • *
  • chest
  • @@ -1573,6 +1639,9 @@ public class CarpetExpression * *

    current_dimension()

    *

    Returns current dimension that scripts run in.

    + *

    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.

    *

    plop(pos, what)

    *

    Plops a structure or a feature at a given pos, so block, triple position coordinates * or a list of coordinates. To what gets plopped and exactly where it often depends on the @@ -1816,7 +1885,7 @@ public class CarpetExpression } else { - Messenger.m(s, "w "); + Messenger.m(s, "w "+ lv.get(0).evalValue(c).getString()); } return lv.get(0); // pass through for variables }); @@ -1885,6 +1954,27 @@ public class CarpetExpression return (c_, t_) -> Value.NULL; return (c_, t_) -> new NumericValue(res); }); + + this.expr.addLazyFunction("schedule", -1, (c, t, lv) -> { + if (lv.size()<2) + throw new InternalExpressionException("schedule should have at least 2 arguments, delay and call name"); + Long delay = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong(); + String funname = lv.get(1).evalValue(c).getString(); + CarpetContext cc = (CarpetContext)c; + if (!cc.host.globalFunctions.containsKey(funname)) + throw new InternalExpressionException("function "+funname+" is not defined"); + List args = new ArrayList<>(); + for (int i=2; i < lv.size(); i++) + { + Value arg = lv.get(i).evalValue(cc); + args.add( (_c, _t) -> arg); + } + if (cc.host.globalFunctions.get(funname).getArguments().size() != args.size()) + throw new InternalExpressionException("function "+funname+" takes "+ + cc.host.globalFunctions.get(funname).getArguments().size()+" arguments, "+args.size()+" provided."); + CarpetServer.scriptServer.events.scheduleCall(cc, funname, args, delay); + return (c_, t_) -> Value.TRUE; + }); } /** diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index 25ad80f0..c312f013 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -7,6 +7,7 @@ import carpet.script.bundled.FileModule; import carpet.script.bundled.ModuleInterface; import carpet.script.exception.CarpetExpressionException; import carpet.script.exception.ExpressionException; +import carpet.script.exception.InvalidCallbackException; import carpet.script.value.Value; import carpet.utils.Messenger; import com.mojang.brigadier.arguments.StringArgumentType; @@ -36,6 +37,7 @@ public class CarpetScriptServer long tickStart; public boolean stopAll; Set holyMoly; + public CarpetEventServer events; public static List bundledModuleData = new ArrayList(){{ add(new CameraPathModule()); @@ -43,7 +45,8 @@ public class CarpetScriptServer public CarpetScriptServer() { - globalHost = createMinecraftScriptHost(""); + globalHost = createMinecraftScriptHost(null); + events = new CarpetEventServer(); modules = new HashMap<>(); tickStart = 0L; stopAll = false; @@ -102,6 +105,13 @@ public class CarpetScriptServer return moduleNames; } + public ScriptHost getHostByName(String name) + { + if (name == null) + return globalHost; + return modules.get(name); + } + private static ScriptHost createMinecraftScriptHost(String name) { @@ -146,11 +156,31 @@ public class CarpetScriptServer } modules.put(name, newHost); + addEvents(source, name); + addCommand(source, name); return true; } + public void addEvents(ServerCommandSource source, String hostName) + { + ScriptHost host = modules.get(hostName); + if (host == null) + { + return; + } + for (String fun : host.globalFunctions.keySet()) + { + if (!fun.startsWith("__on_")) + continue; + String event = fun.replaceFirst("__on_",""); + if (!events.eventHandlers.containsKey(event)) + continue; + events.addEvent(event, hostName, fun); + } + } + public void addCommand(ServerCommandSource source, String hostName) { @@ -278,4 +308,25 @@ public class CarpetScriptServer Messenger.m(source, "w Removed host "+name); return true; } + + public boolean runas(ServerCommandSource source, String hostname, String udf_name, List argv) + { + return runas(BlockPos.ORIGIN, source, hostname, udf_name, argv); + } + + public boolean runas(BlockPos origin, ServerCommandSource source, String hostname, String udf_name, List argv) + { + ScriptHost host = globalHost; + if (hostname != null) + host = modules.get(hostname); + try + { + host.callUDF(origin, source, host.globalFunctions.get(udf_name), argv); + } + catch (NullPointerException | InvalidCallbackException npe) + { + return false; + } + return true; + } } diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java index 14882d95..c72327b7 100644 --- a/src/main/java/carpet/script/ScriptHost.java +++ b/src/main/java/carpet/script/ScriptHost.java @@ -1,7 +1,9 @@ package carpet.script; import carpet.CarpetServer; +import carpet.CarpetSettings; import carpet.script.exception.ExpressionException; +import carpet.script.exception.InvalidCallbackException; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; @@ -28,7 +30,7 @@ public class ScriptHost private String name; public String getName() {return name;} - public ScriptHost(String name) + ScriptHost(String name) { this.name = name; globalVariables.put("euler", (c, t) -> Expression.euler); @@ -155,4 +157,29 @@ public class ScriptHost return e.getMessage(); } } + + public void callUDF(BlockPos pos, ServerCommandSource source, UserDefinedFunction acf, List argv) throws InvalidCallbackException + { + if (CarpetServer.scriptServer.stopAll) + return; + + List args = acf.getArguments(); + if (argv.size() != args.size()) + { + throw new InvalidCallbackException(); + } + try + { + // TODO: this is just for now - invoke would be able to invoke other hosts scripts + Context context = new CarpetContext(this, source, pos); + Expression.evalValue( + () -> acf.lazyEval(context, Context.VOID, acf.expression, acf.token, argv), + context, + Context.VOID); + } + catch (ExpressionException e) + { + CarpetSettings.LOG.error("Callback failed: "+e.getMessage()); + } + } } diff --git a/src/main/java/carpet/script/exception/InvalidCallbackException.java b/src/main/java/carpet/script/exception/InvalidCallbackException.java new file mode 100644 index 00000000..3a8ca684 --- /dev/null +++ b/src/main/java/carpet/script/exception/InvalidCallbackException.java @@ -0,0 +1,5 @@ +package carpet.script.exception; + +public class InvalidCallbackException extends Exception +{ +} diff --git a/src/main/java/carpet/script/value/BlockValue.java b/src/main/java/carpet/script/value/BlockValue.java index 31c4e853..85c0dda4 100644 --- a/src/main/java/carpet/script/value/BlockValue.java +++ b/src/main/java/carpet/script/value/BlockValue.java @@ -33,26 +33,8 @@ public class BlockValue extends Value return new BlockValue(null, c.s.getWorld(), pos); } - public static BlockValue fromString(String str) - { - try - { - Identifier blockId = Identifier.fromCommandInput(new StringReader(str)); - if (Registry.BLOCK.containsId(blockId)) - { - - Block block = Registry.BLOCK.get(blockId); - return new BlockValue(block.getDefaultState(), null, null); - } - } - catch (CommandSyntaxException ignored) - { - } - throw new InternalExpressionException("Unknown block: "+str); - } - private static Map bvCache= new HashMap<>(); - public static BlockValue fromCommandExpression(String str) + public static BlockValue fromString(String str) { try { @@ -62,6 +44,8 @@ public class BlockValue extends Value if (blockstateparser.getBlockState() != null) { bv = new BlockValue(blockstateparser.getBlockState(), null, null); + if (bvCache.size()>10000) + bvCache.clear(); bvCache.put(str, bv); return bv; } @@ -110,10 +94,20 @@ public class BlockValue extends Value } public static LocatorResult fromParams(CarpetContext c, List params, int offset) + { + return fromParams(c, params,offset, false); + } + + public static LocatorResult fromParams(CarpetContext c, List params, int offset, boolean acceptString) { try { Value v1 = params.get(0 + offset).evalValue(c); + //add conditional from string name + if (acceptString && v1 instanceof StringValue) + { + return new LocatorResult(fromString(v1.getString()), 1+offset); + } if (v1 instanceof BlockValue) { return new LocatorResult(((BlockValue) v1), 1+offset); diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index de752ca2..5a779d71 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -131,7 +131,7 @@ public class EntityValue extends Value return featureAccessors.get(what).apply(entity, arg); } private static Map inventorySlots = new HashMap(){{ - put("main", EquipmentSlot.MAINHAND); + put("mainhand", EquipmentSlot.MAINHAND); put("offhand", EquipmentSlot.OFFHAND); put("head", EquipmentSlot.HEAD); put("chest", EquipmentSlot.CHEST); diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 12cba0c2..9b5b0e6c 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -48,6 +48,8 @@ "FossilFeatureMixin", "BuriedTreasureGeneratorPieceMixin", "GravityStructureProcessorMixin", + "ServerPlayNetworkHandler_scarpetEventsMixin", + "ServerPlayerInteractionManager_scarpetEventsMixin", "ServerChunkManagerMixin",