From c34ec302120efc277de8aac942d8e541fe1fb2b6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 23 May 2025 20:32:04 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BA=A4=E4=BA=92=E5=85=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/behavior/BushBlockBehavior.java | 4 +- .../block/behavior/CropBlockBehavior.java | 35 +++++ .../block/behavior/GrassBlockBehavior.java | 42 ++++++ .../block/behavior/SaplingBlockBehavior.java | 32 +++++ .../behavior/StrippableBlockBehavior.java | 107 ++++++++++++++- .../bukkit/item/BukkitItemManager.java | 4 - .../bukkit/item/behavior/AxeItemBehavior.java | 122 ------------------ .../item/behavior/BoneMealItemBehavior.java | 83 ------------ .../item/behavior/BukkitItemBehaviors.java | 4 - .../item/listener/ItemEventListener.java | 36 +++++- .../bukkit/plugin/BukkitCraftEngine.java | 2 +- .../bukkit/plugin/gui/BukkitGuiManager.java | 1 - .../block/behavior/AbstractBlockBehavior.java | 6 + .../core/item/context/UseOnContext.java | 4 + .../compatibility/CompatibilityManager.java | 1 - .../context/function/LevelerExpFunction.java | 3 - 16 files changed, 258 insertions(+), 228 deletions(-) delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealItemBehavior.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java index 381c9152f..6c1d87c11 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java @@ -137,7 +137,9 @@ public class BushBlockBehavior extends BukkitBlockBehavior { } protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException { - if (this.any) return true; + if (this.any) { + return belowState != Reflections.instance$Blocks$AIR$defaultState; + } for (Object tag : this.tagsCanSurviveOn) { if ((boolean) Reflections.method$BlockStateBase$hasTag.invoke(belowState, tag)) { return true; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java index a8d14c48f..5581362a0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.ParticleUtils; import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.bukkit.world.BukkitWorld; @@ -12,6 +13,11 @@ import net.momirealms.craftengine.core.block.UpdateOption; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.IntegerProperty; import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.SimpleContext; import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; @@ -125,6 +131,35 @@ public class CropBlockBehavior extends BushBlockBehavior { this.performBoneMeal(args[0], args[2], args[3]); } + @Override + public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { + Item item = context.getItem(); + if (item == null || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode()) + return InteractionResult.PASS; + if (isMaxAge(state)) + return InteractionResult.PASS; + boolean sendSwing = false; + try { + Object visualState = state.vanillaBlockState().handle(); + Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState); + if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { + boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState); + if (!is) { + sendSwing = true; + } + } else { + sendSwing = true; + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to check visual state bone meal state", e); + return InteractionResult.FAIL; + } + if (sendSwing) { + context.getPlayer().swingHand(context.getHand()); + } + return InteractionResult.SUCCESS; + } + private void performBoneMeal(Object level, Object pos, Object state) throws InvocationTargetException, IllegalAccessException { ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state)); if (immutableBlockState == null || immutableBlockState.isEmpty()) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java index 14b4efb72..a6763fedb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java @@ -3,14 +3,23 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.ParticleUtils; import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.World; +import org.bukkit.block.Block; import java.util.Map; @@ -57,6 +66,39 @@ public class GrassBlockBehavior extends BukkitBlockBehavior { return true; } + @SuppressWarnings("DuplicatedCode") + @Override + public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { + Item item = context.getItem(); + if (item == null || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode()) + return InteractionResult.PASS; + BlockPos pos = context.getClickedPos(); + BukkitBlockInWorld upper = (BukkitBlockInWorld) context.getLevel().getBlockAt(pos.x(), pos.y() + 1, pos.z()); + Block block = upper.block(); + if (!block.isEmpty()) + return InteractionResult.PASS; + boolean sendSwing = false; + try { + Object visualState = state.vanillaBlockState().handle(); + Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState); + if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { + boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState); + if (!is) { + sendSwing = true; + } + } else { + sendSwing = true; + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to check visual state bone meal state", e); + return InteractionResult.FAIL; + } + if (sendSwing) { + context.getPlayer().swingHand(context.getHand()); + } + return InteractionResult.SUCCESS; + } + @Override public void performBoneMeal(Object thisBlock, Object[] args) { FastNMS.INSTANCE.method$GrassBlock$performBoneMeal(args[0], args[1], args[2], args[3], thisBlock); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java index 6242f1103..7511e265a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java @@ -9,6 +9,10 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.RandomUtils; @@ -135,6 +139,34 @@ public class SaplingBlockBehavior extends BushBlockBehavior { this.increaseStage(args[0], args[2], args[3], args[1]); } + @SuppressWarnings("DuplicatedCode") + @Override + public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { + Item item = context.getItem(); + if (item == null || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode()) + return InteractionResult.PASS; + boolean sendSwing = false; + try { + Object visualState = state.vanillaBlockState().handle(); + Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState); + if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { + boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState); + if (!is) { + sendSwing = true; + } + } else { + sendSwing = true; + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to check visual state bone meal state", e); + return InteractionResult.FAIL; + } + if (sendSwing) { + context.getPlayer().swingHand(context.getHand()); + } + return InteractionResult.SUCCESS; + } + public static class Factory implements BlockBehaviorFactory { @SuppressWarnings("unchecked") diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java index f1a2478fe..5e9b41d6f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java @@ -1,15 +1,42 @@ package net.momirealms.craftengine.bukkit.block.behavior; +import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.util.*; +import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; 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.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.shared.block.BlockBehavior; +import net.momirealms.sparrow.nbt.CompoundTag; +import org.bukkit.GameEvent; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.block.Block; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; import java.util.Map; +import java.util.Optional; public class StrippableBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); + private static final Key AXE_STRIP_SOUND = Key.of("minecraft:item.axe.strip"); private final Key stripped; public StrippableBlockBehavior(CustomBlock block, Key stripped) { @@ -18,7 +45,85 @@ public class StrippableBlockBehavior extends BukkitBlockBehavior { } public Key stripped() { - return stripped; + return this.stripped; + } + + @SuppressWarnings("unchecked") + @Override + public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { + Item item = (Item) context.getItem(); + if (item == null) { + return InteractionResult.PASS; + } + Optional> optionalCustomItem = item.getCustomItem(); + if (optionalCustomItem.isEmpty()) { + if (!item.is(ItemTags.AXES)) + return InteractionResult.PASS; + } else { + CustomItem customItem = optionalCustomItem.get(); + if (!customItem.settings().tags().contains(ItemTags.AXES) && !item.is(ItemTags.AXES)) + return InteractionResult.PASS; + } + + Player player = context.getPlayer(); + // no adventure mode + if (player.isAdventureMode()) { + return InteractionResult.PASS; + } + Item offHandItem = player.getItemInHand(InteractionHand.OFF_HAND); + // is using a shield + if (context.getHand() == InteractionHand.MAIN_HAND && offHandItem != null && offHandItem.vanillaId().equals(ItemKeys.SHIELD) && !player.isSecondaryUseActive()) { + return InteractionResult.PASS; + } + + Optional optionalNewCustomBlock = BukkitBlockManager.instance().blockById(stripped()); + if (optionalNewCustomBlock.isEmpty()) { + CraftEngine.instance().logger().warn("stripped block " + stripped() + " does not exist"); + return InteractionResult.FAIL; + } + CustomBlock newCustomBlock = optionalNewCustomBlock.get(); + CompoundTag compoundTag = state.propertiesNbt(); + ImmutableBlockState newState = newCustomBlock.getBlockState(compoundTag); + + org.bukkit.entity.Player bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer()); + + BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos()); + Block block = clicked.block(); + // Call bukkit event + EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, block, BlockStateUtils.fromBlockData(newState.customBlockState().handle())); + if (EventUtils.fireAndCheckCancel(event)) { + return InteractionResult.FAIL; + } + + BlockPos pos = context.getClickedPos(); + context.getLevel().playBlockSound(Vec3d.atCenterOf(pos), AXE_STRIP_SOUND, 1, 1); + CraftEngineBlocks.place(block.getLocation(), newState, UpdateOption.UPDATE_ALL_IMMEDIATE, false); + block.getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z())); + Material material = MaterialUtils.getMaterial(item.vanillaId()); + bukkitPlayer.setStatistic(Statistic.USE_ITEM, material, bukkitPlayer.getStatistic(Statistic.USE_ITEM, material) + 1); + + // resend swing if it's not interactable on client side + if (!InteractUtils.isInteractable( + bukkitPlayer, BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), + context.getHitResult(), item + ) || player.isSecondaryUseActive()) { + player.swingHand(context.getHand()); + } + // shrink item amount + if (VersionHelper.isOrAbove1_20_5()) { + Object itemStack = item.getLiteralObject(); + Object serverPlayer = player.serverPlayer(); + Object equipmentSlot = context.getHand() == InteractionHand.MAIN_HAND ? Reflections.instance$EquipmentSlot$MAINHAND : Reflections.instance$EquipmentSlot$OFFHAND; + try { + Reflections.method$ItemStack$hurtAndBreak.invoke(itemStack, 1, serverPlayer, equipmentSlot); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to hurt itemStack", e); + } + } else { + ItemStack itemStack = item.getItem(); + itemStack.damage(1, bukkitPlayer); + } + return InteractionResult.SUCCESS; } public static class Factory implements BlockBehaviorFactory { 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 f51b4ee82..f72ecec0e 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,8 +1,6 @@ package net.momirealms.craftengine.bukkit.item; import com.saicone.rtag.item.ItemTagStream; -import net.momirealms.craftengine.bukkit.item.behavior.AxeItemBehavior; -import net.momirealms.craftengine.bukkit.item.behavior.BoneMealItemBehavior; import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior; import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior; import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory; @@ -42,10 +40,8 @@ import java.util.function.Function; public class BukkitItemManager extends AbstractItemManager { static { - registerVanillaItemExtraBehavior(AxeItemBehavior.INSTANCE, ItemKeys.AXES); registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS); registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET); - registerVanillaItemExtraBehavior(BoneMealItemBehavior.INSTANCE, ItemKeys.BONE_MEAL); } private static BukkitItemManager instance; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java deleted file mode 100644 index d1e6b038e..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java +++ /dev/null @@ -1,122 +0,0 @@ -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.block.behavior.StrippableBlockBehavior; -import net.momirealms.craftengine.bukkit.util.*; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; -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.entity.player.InteractionHand; -import net.momirealms.craftengine.core.entity.player.InteractionResult; -import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemKeys; -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.pack.Pack; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.BlockPos; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.sparrow.nbt.CompoundTag; -import org.bukkit.GameEvent; -import org.bukkit.Material; -import org.bukkit.Statistic; -import org.bukkit.block.Block; -import org.bukkit.event.entity.EntityChangeBlockEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.util.Vector; - -import java.nio.file.Path; -import java.util.Map; -import java.util.Optional; - -public class AxeItemBehavior extends ItemBehavior { - public static final Factory FACTORY = new Factory(); - public static final AxeItemBehavior INSTANCE = new AxeItemBehavior(); - private static final Key AXE_STRIP_SOUND = Key.of("minecraft:item.axe.strip"); - - @SuppressWarnings("unchecked") - @Override - public InteractionResult useOnBlock(UseOnContext context) { - BukkitBlockInWorld clicked = (BukkitBlockInWorld) 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; - - if (!(state.behavior() instanceof StrippableBlockBehavior blockBehavior)) { - return InteractionResult.PASS; - } - - Player player = context.getPlayer(); - // no adventure mode - if (player.isAdventureMode()) { - return InteractionResult.PASS; - } - - Item offHandItem = (Item) player.getItemInHand(InteractionHand.OFF_HAND); - // is using a shield - if (context.getHand() == InteractionHand.MAIN_HAND && offHandItem != null && offHandItem.vanillaId().equals(ItemKeys.SHIELD) && !player.isSecondaryUseActive()) { - return InteractionResult.PASS; - } - - Optional optionalNewCustomBlock = BukkitBlockManager.instance().blockById(blockBehavior.stripped()); - if (optionalNewCustomBlock.isEmpty()) { - CraftEngine.instance().logger().warn("stripped block " + blockBehavior.stripped() + " does not exist"); - return InteractionResult.FAIL; - } - CustomBlock newCustomBlock = optionalNewCustomBlock.get(); - CompoundTag compoundTag = state.propertiesNbt(); - ImmutableBlockState newState = newCustomBlock.getBlockState(compoundTag); - - org.bukkit.entity.Player bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer()); - // Call bukkit event - EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, block, BlockStateUtils.fromBlockData(newState.customBlockState().handle())); - if (EventUtils.fireAndCheckCancel(event)) { - return InteractionResult.PASS; - } - - BlockPos pos = context.getClickedPos(); - context.getLevel().playBlockSound(Vec3d.atCenterOf(pos), AXE_STRIP_SOUND, 1, 1); - CraftEngineBlocks.place(block.getLocation(), newState, UpdateOption.UPDATE_ALL_IMMEDIATE, false); - block.getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z())); - Item item = (Item) context.getItem(); - Material material = MaterialUtils.getMaterial(item.vanillaId()); - bukkitPlayer.setStatistic(Statistic.USE_ITEM, material, bukkitPlayer.getStatistic(Statistic.USE_ITEM, material) + 1); - - // resend swing if it's not interactable on client side - if (!InteractUtils.isInteractable( - bukkitPlayer, BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), - context.getHitResult(), item - ) || player.isSecondaryUseActive()) { - player.swingHand(context.getHand()); - } - // shrink item amount - if (VersionHelper.isOrAbove1_20_5()) { - Object itemStack = item.getLiteralObject(); - Object serverPlayer = player.serverPlayer(); - Object equipmentSlot = context.getHand() == InteractionHand.MAIN_HAND ? Reflections.instance$EquipmentSlot$MAINHAND : Reflections.instance$EquipmentSlot$OFFHAND; - try { - Reflections.method$ItemStack$hurtAndBreak.invoke(itemStack, 1, serverPlayer, equipmentSlot); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to hurt itemStack", e); - } - } else { - ItemStack itemStack = item.getItem(); - itemStack.damage(1, bukkitPlayer); - } - return InteractionResult.SUCCESS; - } - - public static class Factory implements ItemBehaviorFactory { - - @Override - public ItemBehavior create(Pack pack, Path path, Key id, Map arguments) { - return INSTANCE; - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealItemBehavior.java deleted file mode 100644 index 2996694ed..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BoneMealItemBehavior.java +++ /dev/null @@ -1,83 +0,0 @@ -package net.momirealms.craftengine.bukkit.item.behavior; - -import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; -import net.momirealms.craftengine.bukkit.block.behavior.CropBlockBehavior; -import net.momirealms.craftengine.bukkit.block.behavior.GrassBlockBehavior; -import net.momirealms.craftengine.bukkit.block.behavior.SaplingBlockBehavior; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.util.BlockStateUtils; -import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.bukkit.util.Reflections; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; -import net.momirealms.craftengine.core.block.ImmutableBlockState; -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.pack.Pack; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.Key; -import org.bukkit.block.Block; - -import java.nio.file.Path; -import java.util.Map; - -public class BoneMealItemBehavior extends ItemBehavior { - public static final Factory FACTORY = new Factory(); - public static final BoneMealItemBehavior INSTANCE = new BoneMealItemBehavior(); - - @Override - public InteractionResult useOnBlock(UseOnContext context) { - if (context.getPlayer().isAdventureMode()) { - return InteractionResult.PASS; - } - - BukkitBlockInWorld clicked = (BukkitBlockInWorld) 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; - - boolean shouldHandle =false; - if (state.behavior() instanceof CropBlockBehavior blockBehavior) { - if (!blockBehavior.isMaxAge(state)) { - shouldHandle = true; - } - } else if (state.behavior() instanceof SaplingBlockBehavior) { - shouldHandle = true; - } else if (state.behavior() instanceof GrassBlockBehavior) { - if (block.getLocation().add(0, 1, 0).getBlock().isEmpty()) { - shouldHandle = true; - } - } - - if (!shouldHandle) return InteractionResult.PASS; - - boolean sendSwing = false; - try { - Object visualState = state.vanillaBlockState().handle(); - Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState); - if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { - boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState); - if (!is) { - sendSwing = true; - } - } else { - sendSwing = true; - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to check visual state bone meal state", e); - } - if (sendSwing) { - context.getPlayer().swingHand(context.getHand()); - } - return InteractionResult.SUCCESS; - } - - public static class Factory implements ItemBehaviorFactory { - - @Override - public ItemBehavior create(Pack pack, Path path, 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 ed3e35737..f8081572b 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 @@ -8,19 +8,15 @@ public class BukkitItemBehaviors extends ItemBehaviors { public static final Key BLOCK_ITEM = Key.from("craftengine:block_item"); public static final Key ON_LIQUID_BLOCK_ITEM = Key.from("craftengine:liquid_collision_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 final Key BONE_MEAL_ITEM = Key.from("craftengine:bone_meal_item"); public static void init() { register(EMPTY, EmptyItemBehavior.FACTORY); register(BLOCK_ITEM, BlockItemBehavior.FACTORY); register(ON_LIQUID_BLOCK_ITEM, LiquidCollisionBlockItemBehavior.FACTORY); register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY); - register(AXE_ITEM, AxeItemBehavior.FACTORY); register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY); register(BUCKET_ITEM, BucketItemBehavior.FACTORY); - register(BONE_MEAL_ITEM, BoneMealItemBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java index 50fd9bd38..19cc4e5ce 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.item.CustomItem; @@ -75,6 +76,17 @@ public class ItemEventListener implements Listener { ImmutableBlockState immutableBlockState = null; int stateId = BlockStateUtils.blockStateToId(blockState); Item itemInHand = serverPlayer.getItemInHand(hand); + Location interactionPoint = event.getInteractionPoint(); + + + + BlockHitResult hitResult = null; + if (action == Action.RIGHT_CLICK_BLOCK && interactionPoint != null) { + Direction direction = DirectionUtils.toDirection(event.getBlockFace()); + BlockPos pos = LocationUtils.toBlockPos(block.getLocation()); + Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ()); + hitResult = new BlockHitResult(vec3d, direction, pos, false); + } // 处理自定义方块 if (!BlockStateUtils.isVanillaBlock(stateId)) { @@ -83,7 +95,7 @@ public class ItemEventListener implements Listener { CustomBlockInteractEvent interactEvent = new CustomBlockInteractEvent( player, block.getLocation(), - event.getInteractionPoint(), + interactionPoint, immutableBlockState, block, event.getBlockFace(), @@ -113,6 +125,20 @@ public class ItemEventListener implements Listener { event.setCancelled(true); return; } + + if (hitResult != null) { + UseOnContext useOnContext = new UseOnContext(serverPlayer, hand, itemInHand, hitResult); + if (immutableBlockState.behavior() instanceof AbstractBlockBehavior behavior) { + InteractionResult result = behavior.useOnBlock(useOnContext, immutableBlockState); + if (result == InteractionResult.SUCCESS_AND_CANCEL) { + event.setCancelled(true); + return; + } + if (result != InteractionResult.PASS) { + return; + } + } + } } Optional> optionalCustomItem = itemInHand == null ? Optional.empty() : itemInHand.getCustomItem(); @@ -121,7 +147,6 @@ public class ItemEventListener implements Listener { // interact block with items if (hasItem && action == Action.RIGHT_CLICK_BLOCK) { - Location interactionPoint = event.getInteractionPoint(); // some plugins would trigger this event without interaction point if (interactionPoint == null) { if (hasCustomItem) { @@ -129,10 +154,6 @@ public class ItemEventListener implements Listener { } return; } - Direction direction = DirectionUtils.toDirection(event.getBlockFace()); - BlockPos pos = LocationUtils.toBlockPos(block.getLocation()); - Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ()); - BlockHitResult hitResult = new BlockHitResult(vec3d, direction, pos, false); // handle block item if (itemInHand.isBlockItem()) { @@ -200,9 +221,10 @@ public class ItemEventListener implements Listener { if (!serverPlayer.isSecondaryUseActive() && interactable) { return; } + UseOnContext useOnContext = new UseOnContext(serverPlayer, hand, itemInHand, hitResult); // 依次执行物品行为 for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) { - InteractionResult result = itemBehavior.useOnBlock(new UseOnContext(serverPlayer, hand, hitResult)); + InteractionResult result = itemBehavior.useOnBlock(useOnContext); if (result == InteractionResult.SUCCESS_AND_CANCEL) { event.setCancelled(true); return; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java index 72f05d54c..ab28f1adf 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java @@ -26,10 +26,10 @@ import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; import net.momirealms.craftengine.core.item.ItemManager; -import net.momirealms.craftengine.core.plugin.compatibility.CompatibilityManager; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.classpath.ReflectionClassPathAppender; import net.momirealms.craftengine.core.plugin.command.sender.SenderFactory; +import net.momirealms.craftengine.core.plugin.compatibility.CompatibilityManager; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.dependency.Dependencies; import net.momirealms.craftengine.core.plugin.dependency.Dependency; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java index f08eca613..30201b3d2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java @@ -8,7 +8,6 @@ import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.gui.*; import net.momirealms.craftengine.core.util.ReflectionUtils; -import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/AbstractBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/AbstractBlockBehavior.java index ae8d38e1e..1a6dbbc56 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/AbstractBlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/AbstractBlockBehavior.java @@ -2,7 +2,9 @@ package net.momirealms.craftengine.core.block.behavior; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.shared.block.BlockBehavior; public abstract class AbstractBlockBehavior extends BlockBehavior { @@ -15,4 +17,8 @@ public abstract class AbstractBlockBehavior extends BlockBehavior { public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { return state; } + + public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { + return InteractionResult.PASS; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/context/UseOnContext.java b/core/src/main/java/net/momirealms/craftengine/core/item/context/UseOnContext.java index e07191478..cb65aff39 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/context/UseOnContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/context/UseOnContext.java @@ -20,6 +20,10 @@ public class UseOnContext { this(player.world(), player, hand, player.getItemInHand(hand), hit); } + public UseOnContext(Player player, InteractionHand hand, Item stack, BlockHitResult hit) { + this(player.world(), player, hand, stack, hit); + } + public UseOnContext(World world, Player player, InteractionHand hand, Item stack, BlockHitResult hit) { this.player = player; this.hand = hand; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java index 5afa56e2b..83b15f39b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.plugin.compatibility; -import net.momirealms.craftengine.core.entity.furniture.AbstractExternalModel; import net.momirealms.craftengine.core.entity.furniture.ExternalModel; import net.momirealms.craftengine.core.entity.player.Player; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/LevelerExpFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/LevelerExpFunction.java index 4c546ad3b..9d9edff9e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/LevelerExpFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/LevelerExpFunction.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.plugin.context.function; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.*; import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; @@ -9,13 +8,11 @@ import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; -import net.momirealms.craftengine.core.plugin.context.text.TextProvider; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.List; import java.util.Map; -import java.util.Optional; public class LevelerExpFunction extends AbstractConditionalFunction { private final PlayerSelector selector;