From bf9ca6c00fc289170f0ce810e68a6345bf87d999 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sun, 16 Feb 2025 01:57:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=B5=E4=B8=8D=E5=8E=8C=E8=AF=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/item/BukkitItemManager.java | 6 ++ .../bukkit/item/ItemEventListener.java | 11 +++ .../item/behavior/BoneMealBehavior.java | 25 +++++++ .../item/behavior/BucketItemBehavior.java | 69 +++++++++++++++++++ .../item/behavior/BukkitItemBehaviors.java | 4 ++ .../behavior/WaterBucketItemBehavior.java | 61 ++++++++++++++++ .../plugin/user/BukkitServerPlayer.java | 5 ++ .../bukkit/util/BlockStateUtils.java | 10 +++ .../bukkit/util/DirectionUtils.java | 11 +++ .../core/entity/player/InteractionResult.java | 3 +- .../core/entity/player/Player.java | 2 + .../craftengine/core/item/ItemKeys.java | 9 +++ 12 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealBehavior.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BucketItemBehavior.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WaterBucketItemBehavior.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java index 43e17b532..a99f2a1b3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java @@ -1,6 +1,9 @@ package net.momirealms.craftengine.bukkit.item; import net.momirealms.craftengine.bukkit.item.behavior.AxeItemBehavior; +import net.momirealms.craftengine.bukkit.item.behavior.BoneMealBehavior; +import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior; +import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior; import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.ItemUtils; @@ -41,6 +44,9 @@ public class BukkitItemManager extends AbstractItemManager { static { registerVanillaItemExtraBehavior(AxeItemBehavior.INSTANCE, ItemKeys.WOODEN_AXE, ItemKeys.STONE_AXE, ItemKeys.IRON_AXE, ItemKeys.GOLDEN_AXE, ItemKeys.DIAMOND_AXE, ItemKeys.NETHERITE_AXE); + registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKET, ItemKeys.COD_BUCKET, ItemKeys.SALMON_BUCKET, ItemKeys.TROPICAL_FISH_BUCKET, ItemKeys.TADPOLE_BUCKET, ItemKeys.PUFFERFISH_BUCKET, ItemKeys.AXOLOTL_BUCKET); + registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET); + registerVanillaItemExtraBehavior(BoneMealBehavior.INSTANCE, ItemKeys.BONE_MEAL); } private static void registerVanillaItemExtraBehavior(ItemBehavior behavior, Key... items) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ItemEventListener.java index 012b53055..0efe4427e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ItemEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ItemEventListener.java @@ -120,8 +120,18 @@ public class ItemEventListener implements Listener { return; } + // TODO We need to further investigate how to handle adventure mode + // no spectator interactions + if (player.isSpectatorMode() || player.isAdventureMode()) { + return; + } + for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) { InteractionResult result = itemBehavior.useOnBlock(new UseOnContext(player, hand, hitResult)); + if (result == InteractionResult.SUCCESS_AND_CANCEL) { + event.setCancelled(true); + return; + } int maxY = player.level().worldHeight().getMaxBuildHeight() - 1; if (direction == Direction.UP && result != InteractionResult.SUCCESS @@ -129,6 +139,7 @@ public class ItemEventListener implements Listener { && itemBehavior instanceof BlockItemBehavior ) { player.sendActionBar(Component.translatable("build.tooHigh").arguments(Component.text(maxY)).color(NamedTextColor.RED)); + return; } if (result != InteractionResult.PASS) { return; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealBehavior.java new file mode 100644 index 000000000..2411f63ae --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealBehavior.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.util.Key; + +import java.util.Map; + +public class BoneMealBehavior extends ItemBehavior { + public static final BoneMealBehavior INSTANCE = new BoneMealBehavior(); + + @Override + public InteractionResult useOnBlock(UseOnContext context) { + return super.useOnBlock(context); + } + + public static class Factory implements ItemBehaviorFactory { + @Override + public ItemBehavior create(Key id, Map arguments) { + return INSTANCE; + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BucketItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BucketItemBehavior.java new file mode 100644 index 000000000..a09c4c498 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BucketItemBehavior.java @@ -0,0 +1,69 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.world.BukkitWorldBlock; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.BlockPos; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; + +public class BucketItemBehavior extends ItemBehavior { + public static final BucketItemBehavior INSTANCE = new BucketItemBehavior(); + public static final Factory FACTORY = new Factory(); + private static final Key ITEM_BUCKET_FILL = Key.of("item.bucket.fill"); + + @SuppressWarnings("unchecked") + @Override + public InteractionResult useOnBlock(UseOnContext context) { + BukkitWorldBlock clicked = (BukkitWorldBlock) context.getLevel().getBlockAt(context.getClickedPos()); + Block block = clicked.block(); + ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData())); + if (state == null || state.isEmpty()) return InteractionResult.PASS; + CustomBlock customBlock = state.owner().value(); + Property waterlogged = (Property) customBlock.getProperty("waterlogged"); + if (waterlogged == null) return InteractionResult.PASS; + boolean waterloggedState = state.get(waterlogged); + if (!waterloggedState) return InteractionResult.PASS; + BlockPos pos = context.getClickedPos(); + Player player = (Player) context.getPlayer().platformPlayer(); + World world = player.getWorld(); + Location location = new Location(world, pos.x(), pos.y(), pos.z()); + EquipmentSlot slot = context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND; + + CraftEngineBlocks.place(location, state.with(waterlogged, false), UpdateOption.UPDATE_ALL); + if (player.getGameMode() == GameMode.SURVIVAL) { + // to prevent dupe in moment + player.getInventory().setItem(slot, new ItemStack(Material.AIR)); + BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> + player.getInventory().setItem(slot, new ItemStack(Material.WATER_BUCKET)), world, location.getBlockX() >> 4, location.getBlockZ() >> 4); + } + player.setStatistic(Statistic.USE_ITEM, Material.BUCKET, player.getStatistic(Statistic.USE_ITEM, Material.BUCKET) + 1); + // client will assume it has sounds + // context.getPlayer().level().playBlockSound(Vec3d.atCenterOf(context.getClickedPos()), ITEM_BUCKET_FILL, 1, 1); + return InteractionResult.SUCCESS_AND_CANCEL; + } + + public static class Factory implements ItemBehaviorFactory { + @Override + public ItemBehavior create(Key id, Map arguments) { + return INSTANCE; + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java index cb0e6a100..76ff82d53 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java @@ -9,11 +9,15 @@ public class BukkitItemBehaviors extends ItemBehaviors { public static final Key BLOCK_ITEM = Key.from("craftengine:block_item"); public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item"); public static final Key AXE_ITEM = Key.from("craftengine:axe_item"); + public static final Key WATER_BUCKET_ITEM = Key.from("craftengine:water_bucket_item"); + public static final Key BUCKET_ITEM = Key.from("craftengine:bucket_item"); public static void init() { register(EMPTY, (args, id) -> EmptyItemBehavior.INSTANCE); register(BLOCK_ITEM, BlockItemBehavior.FACTORY); register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY); register(AXE_ITEM, AxeItemBehavior.FACTORY); + register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY); + register(BUCKET_ITEM, BucketItemBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WaterBucketItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WaterBucketItemBehavior.java new file mode 100644 index 000000000..3336cd3e8 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WaterBucketItemBehavior.java @@ -0,0 +1,61 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.world.BukkitWorldBlock; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.BlockPos; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +import java.util.Map; + +public class WaterBucketItemBehavior extends ItemBehavior { + public static final WaterBucketItemBehavior INSTANCE = new WaterBucketItemBehavior(); + public static final Factory FACTORY = new Factory(); + + @SuppressWarnings("unchecked") + @Override + public InteractionResult useOnBlock(UseOnContext context) { + BlockPos pos = context.getClickedPos(); + BukkitWorldBlock clicked = (BukkitWorldBlock) context.getLevel().getBlockAt(pos); + Block block = clicked.block(); + ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData())); + if (state == null || state.isEmpty()) return InteractionResult.PASS; + CustomBlock customBlock = state.owner().value(); + Property waterlogged = (Property) customBlock.getProperty("waterlogged"); + if (waterlogged == null) return InteractionResult.PASS; + + Player player = (Player) context.getPlayer().platformPlayer(); + World world = player.getWorld(); + Location location = new Location(world, pos.x(), pos.y(), pos.z()); + + // TODO Refactor all of this because it's playing a trick with the server + block.setBlockData(BlockStateUtils.createBlockData(state.vanillaBlockState().handle()), false); + // actually we should broadcast this change + context.getPlayer().sendPacket(BlockStateUtils.createBlockUpdatePacket(pos, state), true); + BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> + CraftEngineBlocks.place(location, state.with(waterlogged, true), UpdateOption.UPDATE_ALL), world, location.getBlockX() >> 4, location.getBlockZ() >> 4); + + return InteractionResult.SUCCESS; + } + + public static class Factory implements ItemBehaviorFactory { + @Override + public ItemBehavior create(Key id, Map arguments) { + return INSTANCE; + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 2a9897ca5..f4be53c9f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -124,6 +124,11 @@ public class BukkitServerPlayer extends Player { return platformPlayer().getGameMode() == GameMode.SPECTATOR; } + @Override + public boolean isAdventureMode() { + return platformPlayer().getGameMode() == GameMode.ADVENTURE; + } + @Override public void sendActionBar(Component text) { try { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java index 01c24eb9d..50a1c69a8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java @@ -1,9 +1,11 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.PushReaction; import net.momirealms.craftengine.core.util.Instrument; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MapColor; +import net.momirealms.craftengine.core.world.BlockPos; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.event.block.BlockPhysicsEvent; @@ -24,6 +26,14 @@ public class BlockStateUtils { hasInit = true; } + public static Object createBlockUpdatePacket(BlockPos pos, ImmutableBlockState state) { + try { + return Reflections.constructor$ClientboundBlockUpdatePacket.newInstance(LocationUtils.toBlockPos(pos), state.customBlockState().handle()); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + public static BlockData createBlockData(Object blockState) { try { return (BlockData) Reflections.method$CraftBlockData$createData.invoke(null, blockState); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java index 8e9b08ab0..25d566d1e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java @@ -18,4 +18,15 @@ public class DirectionUtils { default -> throw new IllegalStateException("Unexpected value: " + face); }; } + + public static BlockFace toBlockFace(Direction direction) { + return switch (direction) { + case UP -> BlockFace.UP; + case DOWN -> BlockFace.DOWN; + case NORTH -> BlockFace.NORTH; + case SOUTH -> BlockFace.SOUTH; + case WEST -> BlockFace.WEST; + case EAST -> BlockFace.EAST; + }; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/InteractionResult.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/InteractionResult.java index 6de57dc02..bb586139d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/InteractionResult.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/InteractionResult.java @@ -3,5 +3,6 @@ package net.momirealms.craftengine.core.entity.player; public enum InteractionResult { FAIL, SUCCESS, - PASS + PASS, + SUCCESS_AND_CANCEL } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 9ca0e44d8..bd4595319 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -42,6 +42,8 @@ public abstract class Player extends Entity implements NetWorkUser { public abstract boolean isSpectatorMode(); + public abstract boolean isAdventureMode(); + public abstract void sendActionBar(Component text); public abstract boolean updateLastSuccessfulInteractionTick(int tick); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java index 7dbcfee8b..b1a81069b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java @@ -17,4 +17,13 @@ public class ItemKeys { public static final Key GOLDEN_AXE = Key.of("minecraft:golden_axe"); public static final Key DIAMOND_AXE = Key.of("minecraft:diamond_axe"); public static final Key NETHERITE_AXE = Key.of("minecraft:netherite_axe"); + public static final Key WATER_BUCKET = Key.of("minecraft:water_bucket"); + public static final Key COD_BUCKET = Key.of("minecraft:cod_bucket"); + public static final Key SALMON_BUCKET = Key.of("minecraft:salmon_bucket"); + public static final Key TADPOLE_BUCKET = Key.of("minecraft:tadpole_bucket"); + public static final Key TROPICAL_FISH_BUCKET = Key.of("minecraft:tropical_fish_bucket"); + public static final Key PUFFERFISH_BUCKET = Key.of("minecraft:pufferfish_bucket"); + public static final Key AXOLOTL_BUCKET = Key.of("minecraft:axolotl_bucket"); + public static final Key BUCKET = Key.of("minecraft:bucket"); + public static final Key BONE_MEAL = Key.of("minecraft:bone_meal"); }