9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-25 18:09:27 +00:00

Merge pull request #267 from jhqwqmc/dev

fix(block): 修复门掉落问题
This commit is contained in:
XiaoMoMi
2025-07-07 00:56:35 +08:00
committed by GitHub
11 changed files with 89 additions and 22 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,8 +1,11 @@
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.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
@@ -15,8 +18,10 @@ 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.sound.SoundData;
@@ -32,6 +37,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 +117,44 @@ 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<Object> 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<ItemStack> item = cePlayer.getItemInHand(InteractionHand.MAIN_HAND);
if (cePlayer.canInstabuild() || !BlockStateUtils.isCorrectTool(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<DoorBlockBehavior> 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());
}
}
@Override
public void onExplosionHit(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.canOpenByWindCharge && FastNMS.INSTANCE.method$Explosion$canTriggerBlocks(args[3])) {

View File

@@ -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;

View File

@@ -256,4 +256,16 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior {
}
return false;
}
@Override
public Object playerWillDestroy(Object thisBlock, Object[] args, Callable<Object> 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;
}
}

View File

@@ -177,7 +177,10 @@ 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))
// playerWillDestroy
.method(ElementMatchers.is(CoreReflections.method$Block$playerWillDestroy))
.intercept(MethodDelegation.to(PlayerWillDestroyInterceptor.INSTANCE));
if (CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval != null) {
builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval))
.intercept(MethodDelegation.to(AffectNeighborsAfterRemovalInterceptor.INSTANCE));
@@ -625,4 +628,19 @@ public final class BlockGenerator {
}
}
}
public static class PlayerWillDestroyInterceptor {
public static final PlayerWillDestroyInterceptor INSTANCE = new PlayerWillDestroyInterceptor();
@RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) throws Exception {
ObjectHolder<BlockBehavior> holder = ((DelegatingBlock) thisObj).behaviorDelegate();
try {
return holder.value().playerWillDestroy(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run playerWillDestroy", e);
return superMethod.call();
}
}
}
}

View File

@@ -1824,18 +1824,10 @@ public final class CoreReflections {
ReflectionUtils.getInstanceDeclaredField(clazz$Abilities, boolean.class, 2)
);
public static final Field field$Abilities$instabuild = requireNonNull(
ReflectionUtils.getInstanceDeclaredField(clazz$Abilities, boolean.class, 3)
);
public static final Field field$Abilities$mayBuild = requireNonNull(
ReflectionUtils.getInstanceDeclaredField(clazz$Abilities, boolean.class, 4)
);
public static final Field field$Player$abilities = requireNonNull(
ReflectionUtils.getInstanceDeclaredField(clazz$Player, clazz$Abilities, 0)
);
public static final Class<?> clazz$FlowingFluid = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.material.FluidTypeFlowing",
@@ -3764,4 +3756,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
)
);
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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<Object> superMethod) throws Exception {
return superMethod.call();
}
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
return state;
}

View File

@@ -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