diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java index bab5db3ec..f13128275 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java @@ -184,7 +184,7 @@ public final class CraftEngineBlocks { world.playBlockSound(position, state.settings().sounds().breakSound()); } if (sendParticles) { - FastNMS.INSTANCE.method$Level$levelEvent(world.serverWorld(), WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId()); + FastNMS.INSTANCE.method$LevelAccessor$levelEvent(world.serverWorld(), WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId()); } FastNMS.INSTANCE.method$Level$removeBlock(world.serverWorld(), LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), isMoving); return true; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java index 7e6bc299b..2f83e55e0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java @@ -75,7 +75,7 @@ public abstract class AbstractCanSurviveBlockBehavior extends BukkitBlockBehavio net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos)); world.playBlockSound(position, customState.settings().sounds().breakSound()); - FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); + FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); return MBlocks.AIR$defaultState; } return state; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java index 189bf0fca..f053ff84a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java @@ -1,8 +1,12 @@ 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.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MItems; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.DirectionUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; @@ -15,10 +19,13 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.block.state.properties.DoorHinge; import net.momirealms.craftengine.core.block.state.properties.DoubleBlockHalf; +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.context.BlockPlaceContext; import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.HorizontalDirection; @@ -32,6 +39,7 @@ import org.bukkit.block.data.Bisected; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.Door; import org.bukkit.event.block.BlockRedstoneEvent; +import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; import javax.annotation.Nullable; @@ -111,13 +119,55 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior { net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos)); world.playBlockSound(position, customState.settings().sounds().breakSound()); - FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); + FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); return MBlocks.AIR$defaultState; } return blockState; } } + @Override + public Object playerWillDestroy(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object level = args[0]; + Object pos = args[1]; + Object state = args[2]; + Object player = args[3]; + ImmutableBlockState blockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state)); + if (blockState == null || blockState.isEmpty()) return superMethod.call(); + org.bukkit.entity.Player bukkitPlayer = FastNMS.INSTANCE.method$ServerPlayer$getBukkitEntity(player); + BukkitServerPlayer cePlayer = BukkitCraftEngine.instance().adapt(bukkitPlayer); + Item item = cePlayer.getItemInHand(InteractionHand.MAIN_HAND); + if (preventsBlockDrops(cePlayer) || !hasCorrectToolForDrops(blockState, item)) { + preventDropFromBottomPart(level, pos, blockState, player); + } + return superMethod.call(); + } + + private void preventDropFromBottomPart(Object level, Object pos, ImmutableBlockState state, Object player) { + DoubleBlockHalf half = state.get(this.halfProperty); + if (half == DoubleBlockHalf.UPPER) { + Object blockPos = FastNMS.INSTANCE.method$BlockPos$relative(pos, CoreReflections.instance$Direction$DOWN); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos); + ImmutableBlockState belowState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)); + if (belowState == null || belowState.isEmpty()) return; + Optional belowDoorBehavior = belowState.behavior().getAs(DoorBlockBehavior.class); + if (belowDoorBehavior.isEmpty() || belowState.get(this.halfProperty) != DoubleBlockHalf.LOWER) return; + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, MBlocks.AIR$defaultState, UpdateOption.builder().updateSuppressDrops().updateClients().updateNeighbors().build().flags()); + FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, player, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, belowState.customBlockState().registryId()); + } + } + + private boolean hasCorrectToolForDrops(ImmutableBlockState state, @Nullable Item item) { + if (item == null) return !state.settings().requireCorrectTool(); + return !state.settings().requireCorrectTool() + || state.settings().isCorrectTool(item.id()) + || (state.settings().respectToolComponent() && FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), state)); + } + + private boolean preventsBlockDrops(BukkitServerPlayer cePlayer) { + return VersionHelper.isOrAbove1_21_5() ? cePlayer.canInstabuild() : cePlayer.isCreativeMode(); + } + @Override public void onExplosionHit(Object thisBlock, Object[] args, Callable superMethod) { if (this.canOpenByWindCharge && FastNMS.INSTANCE.method$Explosion$canTriggerBlocks(args[3])) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java index e2713c35b..51ad4edb3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java @@ -70,7 +70,7 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior { net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos)); world.playBlockSound(position, customState.settings().sounds().breakSound()); - FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); + FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); return MBlocks.AIR$defaultState; } return state; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java index 5508e189e..e70881894 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java @@ -256,4 +256,16 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { } return false; } + + @Override + public Object playerWillDestroy(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object previous = args[0]; + for (AbstractBlockBehavior behavior : this.behaviors) { + Object processed = behavior.playerWillDestroy(thisBlock, args, superMethod); + if (processed != previous) { + return processed; + } + } + return previous; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java index c5f7fa313..653c5258c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java @@ -177,7 +177,9 @@ public final class BlockGenerator { .intercept(MethodDelegation.to(GetDirectSignalInterceptor.INSTANCE)) // isSignalSource .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$isSignalSource)) - .intercept(MethodDelegation.to(IsSignalSourceInterceptor.INSTANCE)); + .intercept(MethodDelegation.to(IsSignalSourceInterceptor.INSTANCE)) + .method(ElementMatchers.is(CoreReflections.method$Block$playerWillDestroy)) + .intercept(MethodDelegation.to(VersionHelper.isOrAbove1_21_2() ? PlayerWillDestroyInterceptor1_20_3.INSTANCE : PlayerWillDestroyInterceptor1_20.INSTANCE)); if (CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval != null) { builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval)) .intercept(MethodDelegation.to(AffectNeighborsAfterRemovalInterceptor.INSTANCE)); @@ -625,4 +627,33 @@ public final class BlockGenerator { } } } + + public static class PlayerWillDestroyInterceptor1_20_3 { + public static final PlayerWillDestroyInterceptor1_20_3 INSTANCE = new PlayerWillDestroyInterceptor1_20_3(); + + @RuntimeType + public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable superMethod) throws Exception { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + return holder.value().playerWillDestroy(thisObj, args, superMethod); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run entityInside", e); + return superMethod.call(); + } + } + } + + public static class PlayerWillDestroyInterceptor1_20 { + public static final PlayerWillDestroyInterceptor1_20 INSTANCE = new PlayerWillDestroyInterceptor1_20(); + + @RuntimeType + public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable superMethod) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + holder.value().playerWillDestroy(thisObj, args, superMethod); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run entityInside", e); + } + } + } } 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 41844a1d2..e82d3b130 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 @@ -1824,6 +1824,7 @@ public final class CoreReflections { ReflectionUtils.getInstanceDeclaredField(clazz$Abilities, boolean.class, 2) ); + @Deprecated public static final Field field$Abilities$instabuild = requireNonNull( ReflectionUtils.getInstanceDeclaredField(clazz$Abilities, boolean.class, 3) ); @@ -1832,6 +1833,7 @@ public final class CoreReflections { ReflectionUtils.getInstanceDeclaredField(clazz$Abilities, boolean.class, 4) ); + @Deprecated public static final Field field$Player$abilities = requireNonNull( ReflectionUtils.getInstanceDeclaredField(clazz$Player, clazz$Abilities, 0) ); @@ -3764,4 +3766,12 @@ public final class CoreReflections { throw new ReflectionInitException("Failed to initialize reflection", e); } } + + public static final Method method$Block$playerWillDestroy = requireNonNull( + ReflectionUtils.getDeclaredMethod( + clazz$Block, + VersionHelper.isOrAbove1_20_3() ? clazz$BlockState : void.class, + clazz$Level, clazz$BlockPos, clazz$BlockState, clazz$Player + ) + ); } 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 3216a3bd1..5145f721c 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 @@ -250,13 +250,8 @@ public class BukkitServerPlayer extends Player { @Override public boolean canInstabuild() { - try { - Object abilities = CoreReflections.field$Player$abilities.get(serverPlayer()); - return (boolean) CoreReflections.field$Abilities$instabuild.get(abilities); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to get canInstabuild for " + name(), e); - return false; - } + Object abilities = FastNMS.INSTANCE.field$Player$abilities(serverPlayer()); + return FastNMS.INSTANCE.field$Abilities$instabuild(abilities); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java index ff28dbf29..9e0751a36 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java @@ -121,6 +121,6 @@ public class BukkitWorld implements World { @Override public void levelEvent(int id, BlockPos pos, int data) { - FastNMS.INSTANCE.method$Level$levelEvent(serverWorld(), id, LocationUtils.toBlockPos(pos), data); + FastNMS.INSTANCE.method$LevelAccessor$levelEvent(serverWorld(), id, LocationUtils.toBlockPos(pos), data); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java index 94fb7bb2e..0cca29faf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java @@ -138,6 +138,11 @@ public abstract class BlockBehavior { return false; } + // Level level, BlockPos pos, BlockState state, Player player + public Object playerWillDestroy(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + return superMethod.call(); + } + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { return state; } diff --git a/gradle.properties b/gradle.properties index 69673cad8..d71f01899 100644 --- a/gradle.properties +++ b/gradle.properties @@ -50,7 +50,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.18 -nms_helper_version=1.0.28 +nms_helper_version=1.0.29 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23