9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2026-01-04 15:41:38 +00:00

解决吃食物等行为与原版方块放置行为的冲突

This commit is contained in:
XiaoMoMi
2025-10-11 01:40:40 +08:00
parent bcd3648f2d
commit 725f0dadd2
11 changed files with 82 additions and 10 deletions

View File

@@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.item.setting.FoodData;
import net.momirealms.craftengine.core.item.updater.ItemUpdateResult;
@@ -141,7 +142,7 @@ public class ItemEventListener implements Listener {
Direction direction = DirectionUtils.toDirection(event.getBlockFace());
BlockPos pos = LocationUtils.toBlockPos(block.getLocation());
Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ());
hitResult = new BlockHitResult(vec3d, direction, pos, false);
hitResult = new BlockHitResult(vec3d, direction, pos, false); // todo 需要检测玩家是否在方块内
}
// 处理自定义方块
@@ -165,9 +166,8 @@ public class ItemEventListener implements Listener {
// fix client side issues
if (action.isRightClick() && hitResult != null &&
InteractUtils.canPlace(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()), hitResult, itemInHand)) {
InteractUtils.canPlaceVisualBlock(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()), hitResult, itemInHand)) {
player.updateInventory();
//PlayerUtils.resendItemInHand(player);
}
Cancellable dummy = Cancellable.dummy();
@@ -288,8 +288,9 @@ public class ItemEventListener implements Listener {
if (optionalCustomItem.get().settings().disableVanillaBehavior()) {
// 允许尝试放置方块
if (serverPlayer.isSecondaryUseActive() || !InteractUtils.isInteractable(player, blockData, hitResult, itemInHand)) {
// todo 检测不可以放置方块
event.setCancelled(true);
if (InteractUtils.canPlaceBlock(new BlockPlaceContext(new UseOnContext(serverPlayer, hand, itemInHand, hitResult)))) {
event.setCancelled(true);
}
}
}
}

View File

@@ -19,6 +19,14 @@ import java.util.Optional;
public final class BlockStateUtils {
private BlockStateUtils() {}
public static boolean isTag(BlockData blockData, Key tag) {
return isTag(blockDataToBlockState(blockData), tag);
}
public static boolean isTag(Object blockState, Key tag) {
return FastNMS.INSTANCE.method$BlockStateBase$is(blockState, BlockTags.getOrCreate(tag));
}
public static BlockStateWrapper toBlockStateWrapper(BlockData blockData) {
Object state = blockDataToBlockState(blockData);
return toBlockStateWrapper(state);

View File

@@ -5,13 +5,17 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.item.behavior.BlockItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.FlintAndSteelItemBehavior;
import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.BlockKeys;
import net.momirealms.craftengine.core.entity.EntityTypeKeys;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemKeys;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.item.modifier.AttributeModifiersModifier;
import net.momirealms.craftengine.core.item.recipe.RecipeType;
import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
@@ -712,7 +716,6 @@ public final class InteractUtils {
registerInteraction(BlockKeys.WARPED_WALL_HANGING_SIGN, (player, item, blockState, result) -> true);
}
// 消耗
static {
registerCanPlace(BlockKeys.CACTUS, (player, item, blockState, result) -> {
Key id = item.vanillaId();
@@ -936,7 +939,7 @@ public final class InteractUtils {
private static void registerCanPlace(Key key, QuadFunction<org.bukkit.entity.Player, Item<ItemStack>, BlockData, BlockHitResult, Boolean> function) {
var previous = CAN_PLACE.put(key, function);
if (previous != null) {
CraftEngine.instance().logger().warn("Duplicated interaction check: " + key);
CraftEngine.instance().logger().warn("Duplicated can place check: " + key);
}
}
@@ -955,8 +958,9 @@ public final class InteractUtils {
return false;
}
// 这个方法用于解决玩家使用仙人掌放在基于仙人掌的方块上,物品暂时消失的类似问题
public static boolean canPlace(Player player, BlockData state, BlockHitResult hit, @Nullable Item<ItemStack> item) {
// 这个方法用于解决玩家使用仙人掌放在基于仙人掌的方块上,物品暂时消失的类似问题,但是无法彻底解决
// todo 需要通过创建代理Level来实现getBlockState的方法拦截从而实现模拟客户端测的方块状态更新这个过程可能也需要创建代理Chunk和代理Section
public static boolean canPlaceVisualBlock(Player player, BlockData state, BlockHitResult hit, @Nullable Item<ItemStack> item) {
if (item == null) return false;
Key blockType = BlockStateUtils.getBlockOwnerIdFromData(state);
if (CAN_PLACE.containsKey(blockType)) {
@@ -1046,4 +1050,30 @@ public final class InteractUtils {
Key id = item.vanillaId();
return entity instanceof Shearable shearable && shearable.readyToBeSheared() && ItemKeys.SHEARS.equals(id);
}
public static boolean canPlaceBlock(BlockPlaceContext context) {
Object item = FastNMS.INSTANCE.method$ItemStack$getItem(context.getItem().getLiteralObject());
Object block = FastNMS.INSTANCE.method$BlockItem$getBlock(item);
Object stateToPlace = FastNMS.INSTANCE.method$Block$getStateForPlacement(block, toNMSBlockPlaceContext(context));
return FastNMS.INSTANCE.method$BlockStateBase$canSurvive(stateToPlace, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()));
}
private static Object toNMSHitResult(BlockHitResult result) {
return FastNMS.INSTANCE.constructor$BlockHitResult(
LocationUtils.toVec(result.getLocation()),
DirectionUtils.toNMSDirection(result.getDirection()),
LocationUtils.toBlockPos(result.getBlockPos()),
result.isInside()
);
}
private static Object toNMSBlockPlaceContext(BlockPlaceContext context) {
return FastNMS.INSTANCE.constructor$BlockPlaceContext(
context.getLevel().serverWorld(),
Optional.ofNullable(context.getPlayer()).map(net.momirealms.craftengine.core.entity.player.Player::serverPlayer).orElse(null),
context.getHand() == InteractionHand.MAIN_HAND ? CoreReflections.instance$InteractionHand$MAIN_HAND : CoreReflections.instance$InteractionHand$OFF_HAND,
context.getItem().getLiteralObject(),
toNMSHitResult(context.getHitResult())
);
}
}

View File

@@ -21,6 +21,10 @@ public final class LocationUtils {
return new WorldPosition(new BukkitWorld(location.getWorld()), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
}
public static Object toVec(Vec3d vec) {
return FastNMS.INSTANCE.constructor$Vec3(vec.x, vec.y, vec.z);
}
public static Vec3d toVec3d(Location loc) {
return new Vec3d(loc.getX(), loc.getY(), loc.getZ());
}

View File

@@ -107,6 +107,12 @@ public class BukkitExistingBlock implements ExistingBlock {
return null;
}
@Override
public boolean is(Key tag) {
Object state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld()), LocationUtils.toBlockPos(block.getX(), block.getY(), block.getZ()));
return BlockStateUtils.isTag(state, tag);
}
public Block block() {
return this.block;
}

View File

@@ -44,6 +44,7 @@ public final class BlockKeys {
public static final Key DECORATED_POT = Key.of("minecraft:decorated_pot");
public static final Key CHISELED_BOOKSHELF = Key.of("minecraft:chiseled_bookshelf");
public static final Key LECTERN = Key.of("minecraft:lectern");
public static final Key FARMLAND = Key.of("minecraft:farmland");
public static final Key CHEST = Key.of("minecraft:chest");
public static final Key BARREL = Key.of("minecraft:barrel");

View File

@@ -0,0 +1,9 @@
package net.momirealms.craftengine.core.block;
import net.momirealms.craftengine.core.util.Key;
public final class BlockTagKeys {
private BlockTagKeys() {}
public static final Key DIRT = Key.of("minecraft:dirt");
}

View File

@@ -66,6 +66,13 @@ public final class ItemKeys {
public static final Key MAGENTA_DYE = Key.of("minecraft:magenta_dye");
public static final Key PINK_DYE = Key.of("minecraft:pink_dye");
public static final Key CARROT = Key.of("minecraft:carrot");
public static final Key POTATO = Key.of("minecraft:potato");
public static final Key BEETROOT_SEEDS = Key.of("minecraft:beetroot_seeds");
public static final Key WHEAT_SEEDS = Key.of("minecraft:wheat_seeds");
public static final Key SWEET_BERRIES = Key.of("minecraft:sweet_berries");
public static final Key GLOW_BERRIES = Key.of("minecraft:glow_berries");
public static final Key[] AXES = new Key[] {WOODEN_AXE, STONE_AXE, IRON_AXE, GOLDEN_AXE, DIAMOND_AXE, NETHERITE_AXE};
public static final Key[] WATER_BUCKETS = new Key[] {WATER_BUCKET, COD_BUCKET, SALMON_BUCKET, TROPICAL_FISH_BUCKET, TADPOLE_BUCKET, PUFFERFISH_BUCKET, AXOLOTL_BUCKET};

View File

@@ -40,6 +40,10 @@ public class BlockPos extends Vec3i {
return new BlockPos(this.x(), this.y() + 1, this.z());
}
public BlockPos below() {
return new BlockPos(this.x(), this.y() - 1, this.z());
}
public int toSectionBlockIndex() {
return (y & 15) << 8 | (z & 15) << 4 | x & 15;
}

View File

@@ -37,6 +37,8 @@ public interface ExistingBlock {
return new WorldPosition(world(), x(), y(), z());
}
boolean is(Key tag);
World world();
Key id();

View File

@@ -50,7 +50,7 @@ byte_buddy_version=1.17.5
ahocorasick_version=0.6.3
snake_yaml_version=2.5
anti_grief_version=1.0.2
nms_helper_version=1.0.109
nms_helper_version=1.0.112
evalex_version=3.5.0
reactive_streams_version=1.0.4
amazon_awssdk_version=2.34.5