9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-26 02:19:23 +00:00

refactor(bukkit): 重构方块放置逻辑以便支持无玩家对象的上下文

This commit is contained in:
jhqwqmc
2025-06-24 05:27:16 +08:00
parent cac7c57341
commit bf41c572bf
10 changed files with 127 additions and 67 deletions

View File

@@ -6,7 +6,6 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
@@ -67,15 +66,10 @@ public abstract class FacingTriggerableBlockBehavior extends BukkitBlockBehavior
@Override
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
Player player = context.getPlayer();
Direction direction = player.getDirection().opposite();
float yRot = player.yRot();
if (yRot > 45 && yRot < 90) direction = Direction.UP;
if (yRot < -45 && yRot > -90) direction = Direction.DOWN;
return state.owner().value().defaultState().with(this.facingProperty, direction);
return state.owner().value().defaultState().with(this.facingProperty, context.getNearestLookingDirection().opposite());
}
protected boolean blockCheck(Object blockState) {
protected boolean blockCheckByBlockState(Object blockState) {
if (blockState == null || FastNMS.INSTANCE.method$BlockStateBase$isAir(blockState)) {
return false;
}
@@ -83,6 +77,10 @@ public abstract class FacingTriggerableBlockBehavior extends BukkitBlockBehavior
.filter(state -> !state.isEmpty())
.map(state -> state.owner().value().id())
.orElseGet(() -> BlockStateUtils.getBlockOwnerIdFromState(blockState));
return blockCheckByKey(blockId);
}
protected boolean blockCheckByKey(Key blockId) {
return this.blocks.contains(blockId) == this.whitelistMode;
}

View File

@@ -44,7 +44,7 @@ public class PickaxeBlockBehavior extends FacingTriggerableBlockBehavior {
ImmutableBlockState blockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (blockState == null || blockState.isEmpty()) return;
Object blockPos = FastNMS.INSTANCE.method$BlockPos$relative(pos, DirectionUtils.toNMSDirection(blockState.get(this.facingProperty)));
if (blockCheck(FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos))) {
if (blockCheckByBlockState(FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos))) {
FastNMS.INSTANCE.method$LevelWriter$destroyBlock(level, blockPos, true, null, 512);
}
}

View File

@@ -1,6 +1,5 @@
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.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.item.behavior.BlockItemBehavior;
@@ -10,23 +9,24 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityType
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.UpdateOption;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.item.context.PlaceBlockBlockPlaceContext;
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.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import javax.annotation.Nullable;
@@ -74,7 +74,7 @@ public class PlaceBlockBehavior extends FacingTriggerableBlockBehavior {
boolean flag = false;
if (CoreReflections.clazz$BlockItem.isInstance(itemStack1)) {
Object block = FastNMS.INSTANCE.method$BlockItem$getBlock(itemStack1);
if (blockCheck(FastNMS.INSTANCE.method$Block$defaultState(block))) {
if (blockCheckByBlockState(FastNMS.INSTANCE.method$Block$defaultState(block))) {
Object blockHitResult = FastNMS.INSTANCE.constructor$BlockHitResult(
FastNMS.INSTANCE.method$BlockPos$getCenter(LocationUtils.toBlockPos(blockPos1)),
DirectionUtils.toNMSDirection(opposite),
@@ -88,7 +88,6 @@ public class PlaceBlockBehavior extends FacingTriggerableBlockBehavior {
flag = FastNMS.INSTANCE.method$InteractionResult$consumesAction(interactionResult);
}
}
if (!flag) {
Item<ItemStack> item = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack));
Optional<CustomItem<ItemStack>> optionalCustomItem = item.getCustomItem();
@@ -96,21 +95,21 @@ public class PlaceBlockBehavior extends FacingTriggerableBlockBehavior {
CustomItem<ItemStack> customItem = optionalCustomItem.get();
for (ItemBehavior itemBehavior : customItem.behaviors()) {
if (itemBehavior instanceof BlockItemBehavior blockItemBehavior) {
Optional<CustomBlock> optionalBlock = BukkitBlockManager.instance().blockById(blockItemBehavior.block());
if (optionalBlock.isEmpty()) {
CraftEngine.instance().logger().warn("Failed to place unknown block " + blockItemBehavior.block());
continue;
}
ImmutableBlockState placeBlockState = optionalBlock.get().defaultState();
if (!blockCheck(placeBlockState.customBlockState().handle())) {
continue;
}
Location placeLocation = new Location(FastNMS.INSTANCE.method$Level$getCraftWorld(level), blockPos1.x(), blockPos1.y(), blockPos1.z());
if (placeLocation.getBlock().getType() != Material.AIR) {
break;
}
// TODO: 修复放置多方块自定义方块问题
if (CraftEngineBlocks.place(placeLocation, placeBlockState, UpdateOption.UPDATE_ALL_IMMEDIATE, true)) {
if (!blockCheckByKey(blockItemBehavior.block())) continue;
BlockHitResult hitResult = new BlockHitResult(
LocationUtils.toVec3d(blockPos1),
opposite,
blockPos1,
false
);
PlaceBlockBlockPlaceContext context = new PlaceBlockBlockPlaceContext(
BukkitWorldManager.instance().wrap(level),
InteractionHand.MAIN_HAND,
BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack)),
hitResult
);
InteractionResult result = blockItemBehavior.place(context);
if (result.success()) {
return true;
}
}
@@ -130,6 +129,7 @@ public class PlaceBlockBehavior extends FacingTriggerableBlockBehavior {
}
@SuppressWarnings("UnusedReturnValue")
private static boolean getItemAndDoThings(Object level, BlockPos blockPos, Direction direction, Function<Object, Boolean> function) {
for (Object container : getContainersAt(level, blockPos)) {
boolean flag = FastNMS.INSTANCE.method$HopperBlockEntity$getSlots(container, DirectionUtils.toNMSDirection(direction)).anyMatch(i -> {

View File

@@ -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());
int stateId = BlockStateUtils.blockStateToId(againstBlockState);
if (BlockStateUtils.isVanillaBlock(stateId)) {
@@ -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,7 +155,7 @@ 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);
item.load();
@@ -162,7 +163,9 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
block.setPlacedBy(context, blockStateToPlace);
player.swingHand(context.getHand());
if (player != null) {
player.swingHand(context.getHand());
}
context.getLevel().playBlockSound(position, blockStateToPlace.sounds().placeSound());
world.sendGameEvent(bukkitPlayer, GameEvent.BLOCK_PLACE, new Vector(pos.x(), pos.y(), pos.z()));
return InteractionResult.SUCCESS;
@@ -179,16 +182,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) {

View File

@@ -3578,4 +3578,19 @@ public final class CoreReflections {
"world.item.BlockItem"
)
);
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
);
}

View File

@@ -4,6 +4,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.Vec3i;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.Location;
import org.bukkit.World;
@@ -98,4 +99,11 @@ public class LocationUtils {
centerLoc.setY(location.getBlockY());
return centerLoc;
}
public static Vec3d atCenterOf(Vec3d vec) {
return atLowerCornerWithOffset(vec, 0.5F, 0.5F, 0.5F);
}
public static Vec3d atLowerCornerWithOffset(Vec3d vec, double deltaX, double deltaY, double deltaZ) {
return new Vec3d(vec.x() + deltaX, vec.y() + deltaY, vec.z() + deltaZ);
}
}

View File

@@ -235,6 +235,8 @@ public class BukkitWorldManager implements WorldManager, Listener {
public <T> net.momirealms.craftengine.core.world.World wrap(T world) {
if (world instanceof World w) {
return new BukkitWorld(w);
} else if (CoreReflections.clazz$Level.isInstance(world)) {
return new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(world));
} else {
throw new IllegalArgumentException(world.getClass() + " is not a Bukkit World");
}

View File

@@ -8,6 +8,8 @@ import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import javax.annotation.Nullable;
public class BlockPlaceContext extends UseOnContext {
private final BlockPos relativePos;
protected boolean replaceClicked;
@@ -16,7 +18,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;

View File

@@ -0,0 +1,18 @@
package net.momirealms.craftengine.core.item.context;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.World;
public class PlaceBlockBlockPlaceContext extends BlockPlaceContext {
public PlaceBlockBlockPlaceContext(World world, InteractionHand hand, Item<?> stack, BlockHitResult hit) {
super(world, null, hand, stack, hit);
}
@Override
public Direction getNearestLookingDirection() {
return this.getHitResult().getDirection();
}
}

View File

@@ -8,8 +8,10 @@ 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;
public class UseOnContext {
@Nullable
private final Player player;
private final InteractionHand hand;
private final BlockHitResult hitResult;
@@ -24,7 +26,7 @@ public class UseOnContext {
this(player.world(), player, hand, stack, hit);
}
public UseOnContext(World world, Player player, InteractionHand hand, Item<?> stack, BlockHitResult hit) {
public UseOnContext(World world, @Nullable Player player, InteractionHand hand, Item<?> stack, BlockHitResult hit) {
this.player = player;
this.hand = hand;
this.hitResult = hit;
@@ -56,6 +58,7 @@ public class UseOnContext {
return this.itemStack;
}
@Nullable
public Player getPlayer() {
return this.player;
}
@@ -69,11 +72,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() {