From 97001a76b3b7c4c97125af45fd981e8a76c7f9fe Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Mon, 14 Jul 2025 17:06:28 +0800 Subject: [PATCH] =?UTF-8?q?refactor(item):=20=E9=87=8D=E6=9E=84=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=89=A9=E5=93=81=E4=B8=8A=E4=B8=8B=E6=96=87=E4=BB=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=8E=A9=E5=AE=B6=E4=B8=BAnull=E7=9A=84?= =?UTF-8?q?=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/item/behavior/AxeItemBehavior.java | 63 ++++++++------- .../item/behavior/BlockItemBehavior.java | 78 +++++++++++-------- .../behavior/CompostableItemBehavior.java | 10 ++- .../behavior/FlintAndSteelItemBehavior.java | 12 +-- .../item/behavior/FurnitureItemBehavior.java | 32 ++++---- .../LiquidCollisionBlockItemBehavior.java | 21 ++++- .../item/listener/ItemEventListener.java | 2 +- .../reflection/minecraft/CoreReflections.java | 20 +++++ .../core/item/behavior/ItemBehavior.java | 4 +- .../core/item/context/BlockPlaceContext.java | 3 +- .../core/item/context/UseOnContext.java | 9 ++- 11 files changed, 162 insertions(+), 92 deletions(-) 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 index 5a57be97f..c2ae2b93b 100644 --- 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 @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.item.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.block.behavior.StrippableBlockBehavior; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.*; @@ -54,7 +55,7 @@ public class AxeItemBehavior extends ItemBehavior { public InteractionResult useOnBlock(UseOnContext context) { Player player = context.getPlayer(); // no adventure mode for the moment - if (player.isAdventureMode()) { + if (player != null && player.isAdventureMode()) { return InteractionResult.PASS; } @@ -66,9 +67,9 @@ public class AxeItemBehavior extends ItemBehavior { Optional behaviorOptional = customState.behavior().getAs(StrippableBlockBehavior.class); if (behaviorOptional.isEmpty()) return InteractionResult.PASS; Key stripped = behaviorOptional.get().stripped(); - Item offHandItem = (Item) player.getItemInHand(InteractionHand.OFF_HAND); + Item offHandItem = player != null ? (Item) player.getItemInHand(InteractionHand.OFF_HAND) : BukkitItemManager.instance().wrap(new ItemStack(Material.AIR)); // is using a shield - if (context.getHand() == InteractionHand.MAIN_HAND && !ItemUtils.isEmpty(offHandItem) && canBlockAttack(offHandItem) && !player.isSecondaryUseActive()) { + if (context.getHand() == InteractionHand.MAIN_HAND && !ItemUtils.isEmpty(offHandItem) && canBlockAttack(offHandItem) && player != null && !player.isSecondaryUseActive()) { return InteractionResult.PASS; } @@ -81,13 +82,15 @@ public class AxeItemBehavior extends ItemBehavior { CompoundTag compoundTag = customState.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()); - // Call bukkit event - EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().handle())); - if (EventUtils.fireAndCheckCancel(event)) { - return InteractionResult.FAIL; + org.bukkit.entity.Player bukkitPlayer = null; + if (player != null) { + bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer()); + // Call bukkit event + EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().handle())); + if (EventUtils.fireAndCheckCancel(event)) { + return InteractionResult.FAIL; + } } Item item = (Item) context.getItem(); @@ -98,28 +101,30 @@ public class AxeItemBehavior extends ItemBehavior { FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags()); clicked.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); + if (bukkitPlayer != null) { + 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(customState.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 ? CoreReflections.instance$EquipmentSlot$MAINHAND : CoreReflections.instance$EquipmentSlot$OFFHAND; - try { - CoreReflections.method$ItemStack$hurtAndBreak.invoke(itemStack, 1, serverPlayer, equipmentSlot); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to hurt itemStack", e); + // resend swing if it's not interactable on client side + if (!InteractUtils.isInteractable( + bukkitPlayer, BlockStateUtils.fromBlockData(customState.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 ? CoreReflections.instance$EquipmentSlot$MAINHAND : CoreReflections.instance$EquipmentSlot$OFFHAND; + try { + CoreReflections.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); } - } else { - ItemStack itemStack = item.getItem(); - itemStack.damage(1, bukkitPlayer); } return InteractionResult.SUCCESS; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java index 8c4fc7b99..92c240e9b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java @@ -30,10 +30,7 @@ import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.Cancellable; -import net.momirealms.craftengine.core.util.Direction; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.Bukkit; @@ -76,11 +73,12 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { return InteractionResult.FAIL; } + Player player = context.getPlayer(); CustomBlock block = optionalBlock.get(); BlockPos pos = context.getClickedPos(); int maxY = context.getLevel().worldHeight().getMaxBuildHeight() - 1; - if (context.getClickedFace() == Direction.UP && pos.y() >= maxY) { - context.getPlayer().sendActionBar(Component.translatable("build.tooHigh").arguments(Component.text(maxY)).color(NamedTextColor.RED)); + if (player != null && context.getClickedFace() == Direction.UP && pos.y() >= maxY) { + player.sendActionBar(Component.translatable("build.tooHigh").arguments(Component.text(maxY)).color(NamedTextColor.RED)); return InteractionResult.FAIL; } @@ -89,15 +87,14 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { return InteractionResult.FAIL; } - Player player = context.getPlayer(); BlockPos againstPos = context.getAgainstPos(); World world = (World) context.getLevel().platformWorld(); Location placeLocation = new Location(world, pos.x(), pos.y(), pos.z()); Block bukkitBlock = world.getBlockAt(placeLocation); Block againstBlock = world.getBlockAt(againstPos.x(), againstPos.y(), againstPos.z()); - org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer(); + org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; - if (player.isAdventureMode()) { + if (player != null && player.isAdventureMode()) { Object againstBlockState = BlockStateUtils.blockDataToBlockState(againstBlock.getBlockData()); Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(againstBlockState); if (optionalCustomState.isEmpty()) { @@ -113,31 +110,35 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { } } - // trigger event - CustomBlockAttemptPlaceEvent attemptPlaceEvent = new CustomBlockAttemptPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace, - DirectionUtils.toBlockFace(context.getClickedFace()), bukkitBlock, context.getHand()); - if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { - return InteractionResult.FAIL; + if (player != null) { + // trigger event + CustomBlockAttemptPlaceEvent attemptPlaceEvent = new CustomBlockAttemptPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace, + DirectionUtils.toBlockFace(context.getClickedFace()), bukkitBlock, context.getHand()); + if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { + return InteractionResult.FAIL; + } } // it's just world + pos BlockState previousState = bukkitBlock.getState(); // place custom block CraftEngineBlocks.place(placeLocation, blockStateToPlace, UpdateOption.UPDATE_ALL_IMMEDIATE, false); - // call bukkit event - BlockPlaceEvent bukkitPlaceEvent = new BlockPlaceEvent(bukkitBlock, previousState, againstBlock, (ItemStack) context.getItem().getItem(), bukkitPlayer, true, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND); - if (EventUtils.fireAndCheckCancel(bukkitPlaceEvent)) { - // revert changes - previousState.update(true, false); - return InteractionResult.FAIL; - } + if (player != null) { + // call bukkit event + BlockPlaceEvent bukkitPlaceEvent = new BlockPlaceEvent(bukkitBlock, previousState, againstBlock, (ItemStack) context.getItem().getItem(), bukkitPlayer, true, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND); + if (EventUtils.fireAndCheckCancel(bukkitPlaceEvent)) { + // revert changes + previousState.update(true, false); + return InteractionResult.FAIL; + } - // call custom event - CustomBlockPlaceEvent customPlaceEvent = new CustomBlockPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace, world.getBlockAt(placeLocation), context.getHand()); - if (EventUtils.fireAndCheckCancel(customPlaceEvent)) { - // revert changes - previousState.update(true, false); - return InteractionResult.FAIL; + // call custom event + CustomBlockPlaceEvent customPlaceEvent = new CustomBlockPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace, world.getBlockAt(placeLocation), context.getHand()); + if (EventUtils.fireAndCheckCancel(customPlaceEvent)) { + // revert changes + previousState.update(true, false); + return InteractionResult.FAIL; + } } WorldPosition position = new WorldPosition(context.getLevel(), pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5); @@ -154,14 +155,16 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { return InteractionResult.SUCCESS_AND_CANCEL; } - if (!player.isCreativeMode()) { + if (player != null && !player.isCreativeMode()) { Item item = context.getItem(); item.count(item.count() - 1); } block.setPlacedBy(context, blockStateToPlace); - player.swingHand(context.getHand()); + if (player != null) { + player.swingHand(context.getHand()); + } context.getLevel().playBlockSound(position, blockStateToPlace.settings().sounds().placeSound()); world.sendGameEvent(bukkitPlayer, GameEvent.BLOCK_PLACE, new Vector(pos.x(), pos.y(), pos.z())); return InteractionResult.SUCCESS; @@ -178,16 +181,27 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { protected boolean canPlace(BlockPlaceContext context, ImmutableBlockState state) { try { - Object player = context.getPlayer().serverPlayer(); + Player cePlayer = context.getPlayer(); + Object player = cePlayer != null ? cePlayer.serverPlayer() : null; Object blockState = state.customBlockState().handle(); Object blockPos = LocationUtils.toBlockPos(context.getClickedPos()); - Object voxelShape = CoreReflections.method$CollisionContext$of.invoke(null, player); + Object voxelShape; + if (VersionHelper.isOrAbove1_21_6()) { + voxelShape = CoreReflections.method$CollisionContext$placementContext.invoke(null, player); + } else if (player != null) { + voxelShape = CoreReflections.method$CollisionContext$of.invoke(null, player); + } else { + voxelShape = CoreReflections.instance$CollisionContext$empty; + } Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel((World) context.getLevel().platformWorld()); boolean defaultReturn = ((!this.checkStatePlacement() || (boolean) CoreReflections.method$BlockStateBase$canSurvive.invoke(blockState, world, blockPos)) && (boolean) CoreReflections.method$ServerLevel$checkEntityCollision.invoke(world, blockState, player, voxelShape, blockPos, true)); Block block = FastNMS.INSTANCE.method$CraftBlock$at(world, blockPos); BlockData blockData = FastNMS.INSTANCE.method$CraftBlockData$fromData(blockState); - BlockCanBuildEvent canBuildEvent = new BlockCanBuildEvent(block, (org.bukkit.entity.Player) context.getPlayer().platformPlayer(), blockData, defaultReturn, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND); + BlockCanBuildEvent canBuildEvent = new BlockCanBuildEvent( + block, cePlayer != null ? (org.bukkit.entity.Player) cePlayer.platformPlayer() : null, blockData, defaultReturn, + context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND + ); Bukkit.getPluginManager().callEvent(canBuildEvent); return canBuildEvent.isBuildable(); } catch (ReflectiveOperationException e) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java index 9085e54bb..d778f46ee 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java @@ -51,15 +51,17 @@ public class CompostableItemBehavior extends ItemBehavior { if (willRaise) { levelled.setLevel(currentLevel + 1); - EntityChangeBlockEvent event = new EntityChangeBlockEvent((Entity) context.getPlayer().platformPlayer(), block.block(), levelled); - if (EventUtils.fireAndCheckCancel(event)) { - return InteractionResult.FAIL; + if (context.getPlayer() != null) { + EntityChangeBlockEvent event = new EntityChangeBlockEvent((Entity) context.getPlayer().platformPlayer(), block.block(), levelled); + if (EventUtils.fireAndCheckCancel(event)) { + return InteractionResult.FAIL; + } } block.block().setBlockData(levelled); } context.getLevel().levelEvent(WorldEvents.COMPOSTER_COMPOSTS, context.getClickedPos(), willRaise ? 1 : 0); - ((World) context.getLevel().platformWorld()).sendGameEvent((Entity) context.getPlayer().platformPlayer(), GameEvent.BLOCK_CHANGE, new Vector(block.x() + 0.5, block.y() + 0.5, block.z() + 0.5)); + ((World) context.getLevel().platformWorld()).sendGameEvent(context.getPlayer() != null ? (Entity) context.getPlayer().platformPlayer() : null, GameEvent.BLOCK_CHANGE, new Vector(block.x() + 0.5, block.y() + 0.5, block.z() + 0.5)); if (currentLevel + 1 == 7) { FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), blockOwner, 20); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java index 397477d31..50add0a10 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java @@ -80,7 +80,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { } BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle()); // 点击的是方块上面,则只需要判断shift和可交互 - if (direction == Direction.UP) { + if (player != null && direction == Direction.UP) { // 客户端层面必须可交互 if (!InteractUtils.isInteractable((Player) player.platformPlayer(), vanillaBlockState, context.getHitResult(), (Item) context.getItem())) { @@ -106,7 +106,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { } // 客户端觉得这玩意可交互,就会忽略声音 - if (InteractUtils.isInteractable((Player) player.platformPlayer(), vanillaBlockState, context.getHitResult(), (Item) context.getItem())) { + if (player != null && InteractUtils.isInteractable((Player) player.platformPlayer(), vanillaBlockState, context.getHitResult(), (Item) context.getItem())) { // 如果按住了shift,则代表尝试对侧面方块点火 if (player.isSecondaryUseActive()) { // 如果底部不能燃烧,则燃烧点位为侧面,需要补发 @@ -119,7 +119,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { } } else { // 如果底部方块不可燃烧才补发 - if (!belowCanBurn) { + if (player != null && !belowCanBurn) { player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); player.swingHand(context.getHand()); } @@ -151,8 +151,10 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { } } } - player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); - player.swingHand(context.getHand()); + if (player != null) { + player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); + player.swingHand(context.getHand()); + } } return InteractionResult.PASS; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index d974d8924..ef85e8fa6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -77,14 +77,14 @@ public class FurnitureItemBehavior extends ItemBehavior { Player player = context.getPlayer(); // todo adventure check - if (player.isAdventureMode()) { + if (player != null && player.isAdventureMode()) { return InteractionResult.FAIL; } Vec3d clickedPosition = context.getClickLocation(); // trigger event - org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer(); + org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; World world = (World) context.getLevel().platformWorld(); // get position and rotation for placement @@ -100,7 +100,7 @@ public class FurnitureItemBehavior extends ItemBehavior { finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z()); } } else { - furnitureYaw = placement.rotationRule().apply(180 + player.xRot()); + furnitureYaw = placement.rotationRule().apply(180 + (player != null ? player.xRot() : 0)); Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z())); finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right()); } @@ -121,10 +121,12 @@ public class FurnitureItemBehavior extends ItemBehavior { return InteractionResult.FAIL; } - FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(), - DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z())); - if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { - return InteractionResult.FAIL; + if (player != null) { + FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(), + DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z())); + if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { + return InteractionResult.FAIL; + } } Item item = context.getItem(); @@ -140,10 +142,12 @@ public class FurnitureItemBehavior extends ItemBehavior { .fireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null)) .build(), false); - FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand()); - if (EventUtils.fireAndCheckCancel(placeEvent)) { - bukkitFurniture.destroy(); - return InteractionResult.FAIL; + if (player != null) { + FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand()); + if (EventUtils.fireAndCheckCancel(placeEvent)) { + bukkitFurniture.destroy(); + return InteractionResult.FAIL; + } } Cancellable dummy = Cancellable.dummy(); @@ -159,12 +163,14 @@ public class FurnitureItemBehavior extends ItemBehavior { return InteractionResult.SUCCESS_AND_CANCEL; } - if (!player.isCreativeMode()) { + if (player != null && !player.isCreativeMode()) { item.count(item.count() - 1); } context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound()); - player.swingHand(context.getHand()); + if (player != null) { + player.swingHand(context.getHand()); + } return InteractionResult.SUCCESS; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java index 38f668cef..90eb95066 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.item.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.InteractionResult; @@ -21,6 +22,7 @@ import net.momirealms.craftengine.core.world.BlockHitResult; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; +import org.jetbrains.annotations.Nullable; import java.nio.file.Path; import java.util.Map; @@ -36,13 +38,26 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior { @Override public InteractionResult useOnBlock(UseOnContext context) { - return use(context.getLevel(), context.getPlayer(), context.getHand()); + return use(context.getLevel(), context.getPlayer(), context.getHand(), context); } @Override - public InteractionResult use(World world, Player player, InteractionHand hand) { + public InteractionResult use(World world, @Nullable Player player, InteractionHand hand, @Nullable UseOnContext context) { try { - Object blockHitResult = CoreReflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), CoreReflections.instance$ClipContext$Fluid$SOURCE_ONLY); + Object blockHitResult; + if (player != null) { + blockHitResult = CoreReflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), CoreReflections.instance$ClipContext$Fluid$SOURCE_ONLY); + } else if (context != null) { + BlockPos clickedPos = context.getClickedPos(); + blockHitResult = CoreReflections.constructor$BlockHitResult.newInstance( + CoreReflections.constructor$Vec3.newInstance(clickedPos.x() + 0.5, clickedPos.y() + 0.5, clickedPos.z() + 0.5), + DirectionUtils.toNMSDirection(context.getHorizontalDirection()), + LocationUtils.toBlockPos(clickedPos), + false + ); + } else { + return InteractionResult.FAIL; + } Object blockPos = CoreReflections.field$BlockHitResul$blockPos.get(blockHitResult); BlockPos above = new BlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + offsetY, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); Direction direction = Direction.values()[(int) CoreReflections.method$Direction$ordinal.invoke(CoreReflections.field$BlockHitResul$direction.get(blockHitResult))]; 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 b10dd7ad7..e9833e005 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 @@ -365,7 +365,7 @@ public class ItemEventListener implements Listener { Optional> optionalItemBehaviors = itemInHand.getItemBehavior(); if (optionalItemBehaviors.isPresent()) { for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) { - InteractionResult result = itemBehavior.use(serverPlayer.world(), serverPlayer, hand); + InteractionResult result = itemBehavior.use(serverPlayer.world(), serverPlayer, hand, null); if (result.success()) { serverPlayer.updateLastSuccessfulInteractionTick(serverPlayer.gameTicks()); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index 0bd7981a8..a704f93b6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -3830,4 +3830,24 @@ public final class CoreReflections { ), !VersionHelper.isOrAbove1_21_5() ); + public static final Object instance$CollisionContext$empty; + + static { + try { + instance$CollisionContext$empty = requireNonNull(method$CollisionContext$empty.invoke(null)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + // 1.21.6+ + public static final Method method$CollisionContext$placementContext = ReflectionUtils.getStaticMethod( + clazz$CollisionContext, clazz$CollisionContext, clazz$Player + ); + + public static final Constructor constructor$BlockHitResult = requireNonNull( + ReflectionUtils.getConstructor( + CoreReflections.clazz$BlockHitResult, CoreReflections.clazz$Vec3, CoreReflections.clazz$Direction, CoreReflections.clazz$BlockPos, boolean.class + ) + ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehavior.java index 3c73c14e7..91407fbaf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehavior.java @@ -4,7 +4,9 @@ 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.context.UseOnContext; +import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.World; +import org.jetbrains.annotations.Nullable; public abstract class ItemBehavior { @@ -12,7 +14,7 @@ public abstract class ItemBehavior { return InteractionResult.PASS; } - public InteractionResult use(World world, Player player, InteractionHand hand) { + public InteractionResult use(World world, @Nullable Player player, InteractionHand hand, @Nullable UseOnContext context) { return InteractionResult.PASS; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/context/BlockPlaceContext.java b/core/src/main/java/net/momirealms/craftengine/core/item/context/BlockPlaceContext.java index b3cd761f5..cdc2ddd82 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/context/BlockPlaceContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/context/BlockPlaceContext.java @@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.world.BlockHitResult; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.World; +import org.jetbrains.annotations.Nullable; public class BlockPlaceContext extends UseOnContext { private final BlockPos relativePos; @@ -16,7 +17,7 @@ public class BlockPlaceContext extends UseOnContext { this(context.getLevel(), context.getPlayer(), context.getHand(), context.getItem(), context.getHitResult()); } - public BlockPlaceContext(World world, Player player, InteractionHand hand, Item stack, BlockHitResult hit) { + public BlockPlaceContext(World world, @Nullable Player player, InteractionHand hand, Item stack, BlockHitResult hit) { super(world, player, hand, stack, hit); this.relativePos = hit.getBlockPos().relative(hit.getDirection()); this.replaceClicked = true; 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 41ead315b..dc59e38aa 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 @@ -9,8 +9,10 @@ import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class UseOnContext { + @Nullable private final Player player; private final InteractionHand hand; private final BlockHitResult hitResult; @@ -25,7 +27,7 @@ public class UseOnContext { this(player.world(), player, hand, stack, hit); } - public UseOnContext(@NotNull World world, Player player, InteractionHand hand, @NotNull Item stack, BlockHitResult hit) { + public UseOnContext(@NotNull World world, @Nullable Player player, InteractionHand hand, @NotNull Item stack, BlockHitResult hit) { this.player = player; this.hand = hand; this.hitResult = hit; @@ -58,6 +60,7 @@ public class UseOnContext { return this.itemStack; } + @Nullable public Player getPlayer() { return this.player; } @@ -71,11 +74,11 @@ public class UseOnContext { } public Direction getHorizontalDirection() { - return this.player.getDirection(); + return this.player == null ? Direction.NORTH : this.player.getDirection(); } public boolean isSecondaryUseActive() { - return this.player.isSecondaryUseActive(); + return this.player != null && this.player.isSecondaryUseActive(); } public float getRotation() {