From 2ad04895be284b6823faa387ecd148b62ec6f8c9 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Sun, 22 Jun 2025 03:56:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(block):=20=E5=AE=9E=E7=8E=B0=E5=8E=8B?= =?UTF-8?q?=E5=8A=9B=E6=9D=BF=E6=96=B9=E5=9D=97=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../behavior/PressurePlateBlockBehavior.java | 161 +++++++++++++++++- .../reflection/minecraft/CoreReflections.java | 23 +++ .../bukkit/util/LocationUtils.java | 4 + .../default/configuration/palm_tree.yml | 4 + .../core/util/PressurePlateSensitivity.java | 34 ++++ gradle.properties | 2 +- 6 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/PressurePlateSensitivity.java 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 90871cb22..0489edceb 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 @@ -1,20 +1,169 @@ 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.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; import net.momirealms.craftengine.core.block.BlockBehavior; 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.block.properties.Property; +import net.momirealms.craftengine.core.sound.SoundData; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.PressurePlateSensitivity; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.World; +import org.bukkit.GameEvent; +import org.bukkit.util.Vector; +import javax.annotation.Nullable; import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; public class PressurePlateBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); private final Property poweredProperty; + private final SoundData onSound; + private final SoundData offSound; + private final PressurePlateSensitivity pressurePlateSensitivity; - public PressurePlateBlockBehavior(CustomBlock block, Property poweredProperty) { + public PressurePlateBlockBehavior( + CustomBlock block, + Property poweredProperty, + SoundData onSound, + SoundData offSound, + PressurePlateSensitivity pressurePlateSensitivity + ) { super(block); this.poweredProperty = poweredProperty; + this.onSound = onSound; + this.offSound = offSound; + this.pressurePlateSensitivity = pressurePlateSensitivity; + } + + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object state = args[0]; + Object level; + Object blockPos; + if (VersionHelper.isOrAbove1_21_2()) { + level = args[1]; + blockPos = args[3]; + } else { + level = args[3]; + blockPos = args[4]; + } + Direction direction = DirectionUtils.fromNMSDirection(VersionHelper.isOrAbove1_21_2() ? args[4] : args[0]); + return direction == Direction.DOWN && !FastNMS.INSTANCE.method$BlockStateBase$canSurvive(state, level, blockPos) + ? MBlocks.AIR$defaultState + : superMethod.call(); + } + + public boolean canSurvive(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object blockPos = LocationUtils.fromVec(args[2]); + Object level = args[1]; + return FastNMS.INSTANCE.method$Block$canSupportRigidBlock(level, blockPos) + || FastNMS.INSTANCE.method$Block$canSupportCenter(level, blockPos, CoreReflections.instance$Direction$UP); + } + + public void tick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object state = args[0]; + int signalForState = this.getSignalForState(state); + if (signalForState > 0) { + this.checkPressed(null, args[1], args[2], state, signalForState, thisBlock); + } + } + + public void entityInside(Object thisBlock, Object[] args, Callable superMethod) { + Object state = args[0]; + int signalForState = this.getSignalForState(state); + if (signalForState == 0) { + this.checkPressed(args[3], args[1], args[2], state, signalForState, thisBlock); + } + } + + protected int getSignalStrength(Object level, Object pos) { + Class clazz = switch (this.pressurePlateSensitivity) { + case EVERYTHING -> CoreReflections.clazz$Entity; + case MOBS -> CoreReflections.clazz$LivingEntity; + }; + Object box = FastNMS.INSTANCE.method$AABB$move(CoreReflections.instance$BasePressurePlateBlock$TOUCH_AABB, pos); + return FastNMS.INSTANCE.method$BasePressurePlateBlock$getEntityCount(level, box, clazz) > 0 ? 15 : 0; + } + + private Object setSignalForState(Object state, int strength) { + ImmutableBlockState blockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state)); + if (blockState == null || blockState.isEmpty()) return state; + return blockState.with(this.poweredProperty, strength > 0).customBlockState().handle(); + } + + private void checkPressed(@Nullable Object entity, Object level, Object pos, Object state, int currentSignal, Object thisBlock) { + int signalStrength = this.getSignalStrength(level, pos); + boolean flag = currentSignal > 0; + boolean flag1 = signalStrength > 0; + if (currentSignal != signalStrength) { + Object blockState = this.setSignalForState(state, signalStrength); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, blockState, 2); + this.updateNeighbours(level, pos, thisBlock); + FastNMS.INSTANCE.method$Level$setBlocksDirty(level, pos, state, blockState); + } + + if (!flag1 && flag) { + World world = BukkitWorldManager.instance().getWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)).world(); + world.playBlockSound(LocationUtils.toVec3d(LocationUtils.fromBlockPos(pos)), this.offSound); + FastNMS.INSTANCE.method$Level$getCraftWorld(level).sendGameEvent(FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity), + GameEvent.BLOCK_DEACTIVATE, + new Vector(FastNMS.INSTANCE.field$Vec3i$x(pos), FastNMS.INSTANCE.field$Vec3i$y(pos), FastNMS.INSTANCE.field$Vec3i$z(pos)) + ); + } else if (flag1 && !flag) { + World world = BukkitWorldManager.instance().getWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)).world(); + world.playBlockSound(LocationUtils.toVec3d(LocationUtils.fromBlockPos(pos)), this.onSound); + FastNMS.INSTANCE.method$Level$getCraftWorld(level).sendGameEvent(FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity), + GameEvent.BLOCK_ACTIVATE, + new Vector(FastNMS.INSTANCE.field$Vec3i$x(pos), FastNMS.INSTANCE.field$Vec3i$y(pos), FastNMS.INSTANCE.field$Vec3i$z(pos)) + ); + } + + if (flag1) { + FastNMS.INSTANCE.method$LevelAccessor$scheduleBlockTick(level, pos, thisBlock, 20); + } + } + + public void affectNeighborsAfterRemoval(Object thisBlock, Object[] args, Callable superMethod) { + boolean movedByPiston = (boolean) args[3]; + if (!movedByPiston && this.getSignalForState(args[0]) > 0) { + this.updateNeighbours(args[1], args[2], thisBlock); + } + } + + private void updateNeighbours(Object level, Object pos, Object thisBlock) { + FastNMS.INSTANCE.method$Level$updateNeighborsAt(level, pos, thisBlock); + FastNMS.INSTANCE.method$Level$updateNeighborsAt(level, LocationUtils.below(pos), thisBlock); + } + + public int getSignal(Object thisBlock, Object[] args, Callable superMethod) { + return this.getSignalForState(args[0]); + } + + private int getSignalForState(Object state) { + ImmutableBlockState blockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state)); + if (blockState == null || blockState.isEmpty()) return 0; + return blockState.get(this.poweredProperty) ? 15 : 0; + } + + public int getDirectSignal(Object thisBlock, Object[] args, Callable superMethod) { + Direction direction = DirectionUtils.fromNMSDirection(args[3]); + return direction == Direction.UP ? this.getSignalForState(args[0]) : 0; + } + + public boolean isSignalSource(Object thisBlock, Object[] args, Callable superMethod) { + return true; } public static class Factory implements BlockBehaviorFactory { @@ -23,7 +172,15 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { Property powered = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("powered"), "warning.config.block.behavior.pressure_plate.missing_powered"); - return new PressurePlateBlockBehavior(block, powered); + PressurePlateSensitivity pressurePlateSensitivity = PressurePlateSensitivity.byName(arguments.getOrDefault("pressure-plate-sensitivity", "everything").toString()); + Map sounds = (Map) arguments.get("sounds"); + SoundData onSound = null; + SoundData offSound = null; + if (sounds != null) { + onSound = Optional.ofNullable(sounds.get("on")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null); + offSound = Optional.ofNullable(sounds.get("off")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null); + } + return new PressurePlateBlockBehavior(block, powered, onSound, offSound, pressurePlateSensitivity); } } } 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 1b9026400..e4e1f3685 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 @@ -3480,4 +3480,27 @@ public final class CoreReflections { throw new RuntimeException(e); } } + + public static final Class clazz$BasePressurePlateBlock = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.level.block.BlockPressurePlateAbstract", + "world.level.block.BasePressurePlateBlock" + ) + ); + + public static final Field field$BasePressurePlateBlock$TOUCH_AABB = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$BasePressurePlateBlock, clazz$AABB, 0 + ) + ); + + public static final Object instance$BasePressurePlateBlock$TOUCH_AABB; + + static { + try { + instance$BasePressurePlateBlock$TOUCH_AABB = requireNonNull(field$BasePressurePlateBlock$TOUCH_AABB.get(null)); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java index c7fa13c15..5a103ac24 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java @@ -61,6 +61,10 @@ public class LocationUtils { ); } + public static Vec3d toVec3d(BlockPos pos) { + return new Vec3d(pos.x(), pos.y(), pos.z()); + } + public static double getDistance(Location location1, Location location2) { return Math.sqrt(Math.pow(location2.getX() - location1.getX(), 2) + Math.pow(location2.getY() - location1.getY(), 2) + diff --git a/common-files/src/main/resources/resources/default/configuration/palm_tree.yml b/common-files/src/main/resources/resources/default/configuration/palm_tree.yml index 618c6f09f..353a263a0 100644 --- a/common-files/src/main/resources/resources/default/configuration/palm_tree.yml +++ b/common-files/src/main/resources/resources/default/configuration/palm_tree.yml @@ -598,6 +598,10 @@ items: - minecraft:pressure_plates behaviors: type: pressure_plate_block + pressure-plate-sensitivity: all + sounds: + on: minecraft:block.wooden_pressure_plate.click_on + off: minecraft:block.wooden_pressure_plate.click_off states: template: default:block_state/pressure_plate arguments: diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/PressurePlateSensitivity.java b/core/src/main/java/net/momirealms/craftengine/core/util/PressurePlateSensitivity.java new file mode 100644 index 000000000..824edd7bb --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/PressurePlateSensitivity.java @@ -0,0 +1,34 @@ +package net.momirealms.craftengine.core.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public enum PressurePlateSensitivity { + EVERYTHING("everything", "all"), + MOBS("mobs", "mob"); + + private final String[] names; + + PressurePlateSensitivity(String... names) { + this.names = names; + } + + public String[] names() { + return names; + } + + private static final Map BY_NAME = new HashMap<>(); + + static { + for (PressurePlateSensitivity trigger : PressurePlateSensitivity.values()) { + for (String name : trigger.names()) { + BY_NAME.put(name, trigger); + } + } + } + + public static PressurePlateSensitivity byName(String name) { + return Optional.ofNullable(BY_NAME.get(name)).orElseThrow(() -> new IllegalArgumentException("PressurePlateSensitivity not found: " + name)); + } +} diff --git a/gradle.properties b/gradle.properties index 3b133a061..d2d1e9d20 100644 --- a/gradle.properties +++ b/gradle.properties @@ -51,7 +51,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.17 -nms_helper_version=0.67.27 +nms_helper_version=0.67.31 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23