From a4059ae921089bfe22522bcecce00b8ba803f3aa Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Wed, 24 Dec 2025 04:53:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A4=9A=E9=AB=98=E6=96=B9?= =?UTF-8?q?=E5=9D=97=E5=8F=8A=E7=89=A9=E5=93=81=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/behavior/BukkitBlockBehaviors.java | 2 + .../block/behavior/DoorBlockBehavior.java | 6 +- .../behavior/MultiHighBlockBehavior.java | 251 ++++++++++++++++++ .../item/behavior/BukkitItemBehaviors.java | 2 + .../behavior/MultiHighBlockItemBehavior.java | 122 +++++++++ .../bukkit/util/LocationUtils.java | 8 + .../src/main/resources/translations/en.yml | 2 + .../src/main/resources/translations/zh_cn.yml | 2 + 8 files changed, 390 insertions(+), 5 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/MultiHighBlockBehavior.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/MultiHighBlockItemBehavior.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java index aae33ecc1..a6c163446 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java @@ -49,6 +49,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key HANGABLE_BLOCK = Key.from("craftengine:hangable_block"); public static final Key DROP_EXPERIENCE_BLOCK = Key.from("craftengine:drop_experience_block"); public static final Key DROP_EXP_BLOCK = Key.from("craftengine:drop_exp_block"); + public static final Key MULTI_HIGH_BLOCK = Key.from("craftengine:multi_high_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -96,5 +97,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(HANGABLE_BLOCK, HangableBlockBehavior.FACTORY); register(DROP_EXPERIENCE_BLOCK, DropExperienceBlockBehavior.FACTORY); register(DROP_EXP_BLOCK, DropExperienceBlockBehavior.FACTORY); + register(MULTI_HIGH_BLOCK, MultiHighBlockBehavior.FACTORY); } } 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 a93cdf0e3..b0dcadff4 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 @@ -114,11 +114,7 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior implement } else { if (half == DoubleBlockHalf.LOWER && direction == CoreReflections.instance$Direction$DOWN && !canSurvive(thisBlock, blockState, level, blockPos)) { - BlockPos pos = LocationUtils.fromBlockPos(blockPos); - 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$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); + MultiHighBlockBehavior.playBreakEffect(customState, blockPos, level); return MBlocks.AIR$defaultState; } return blockState; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/MultiHighBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/MultiHighBlockBehavior.java new file mode 100644 index 000000000..017fceb24 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/MultiHighBlockBehavior.java @@ -0,0 +1,251 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +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.plugin.reflection.minecraft.MFluids; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.world.BukkitWorld; +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.IntegerProperty; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.*; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class MultiHighBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + public final IntegerProperty highProperty; + + public MultiHighBlockBehavior(CustomBlock customBlock, IntegerProperty highProperty) { + super(customBlock); + this.highProperty = highProperty; + } + + @SuppressWarnings("DuplicatedCode") + @Override + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object blockState = args[0]; + ImmutableBlockState customState = BlockStateUtils.getOptionalCustomBlockState(blockState).orElse(null); + if (customState == null || customState.isEmpty()) { + return MBlocks.AIR$defaultState; + } + MultiHighBlockBehavior behavior = customState.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return MBlocks.AIR$defaultState; + } + IntegerProperty property = behavior.highProperty; + int high = customState.get(property); + Object direction = args[updateShape$direction]; + Object level = args[updateShape$level]; + Object blockPos = args[updateShape$blockPos]; + if (direction == CoreReflections.instance$Direction$UP && high != property.max) { + Object abovePos = LocationUtils.above(blockPos); + Object aboveState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, abovePos); + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(aboveState).orElse(null); + if (state == null) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + MultiHighBlockBehavior aboveBehavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (aboveBehavior == null || aboveBehavior.highProperty != property) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + Integer aboveHigh = state.get(property); + if (high + 1 != aboveHigh) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + } else if (direction == CoreReflections.instance$Direction$DOWN && high != property.min) { + Object belowPos = LocationUtils.below(blockPos); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, belowPos); + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(belowState).orElse(null); + if (state == null) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + MultiHighBlockBehavior belowBehavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (belowBehavior == null || belowBehavior.highProperty != property) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + Integer belowHigh = state.get(property); + if (high - 1 != belowHigh) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + } + return blockState; + } + + public static void playBreakEffect(ImmutableBlockState customState, Object blockPos, Object level) { + BlockPos pos = LocationUtils.fromBlockPos(blockPos); + 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$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); + } + + @Override + public Object playerWillDestroy(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object player = args[3]; + ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[2]).orElse(null); + if (blockState == null || blockState.isEmpty()) { + return superMethod.call(); + } + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(FastNMS.INSTANCE.method$ServerPlayer$getBukkitEntity(player)); + if (serverPlayer == null) { + return superMethod.call(); + } + Item item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); + if (serverPlayer.canInstabuild() || !BlockStateUtils.isCorrectTool(blockState, item)) { + preventDropFromBasePart(args[0], args[1], blockState, player); + } + return superMethod.call(); + } + + private void preventDropFromBasePart(Object level, Object pos, ImmutableBlockState state, Object player) { + MultiHighBlockBehavior behavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return; + } + IntegerProperty property = behavior.highProperty; + int high = state.get(property); + if (high == property.min) { + return; + } + Object basePos = LocationUtils.below(pos, high - property.min); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, basePos); + ImmutableBlockState baseState = BlockStateUtils.getOptionalCustomBlockState(blockState).orElse(null); + if (baseState == null || baseState.isEmpty()) { + return; + } + Optional baseBehavior = baseState.behavior().getAs(MultiHighBlockBehavior.class); + if (baseBehavior.isEmpty()) { + return; + } + IntegerProperty baseProperty = baseBehavior.get().highProperty; + if (baseState.get(baseProperty) != baseProperty.min) { + return; + } + Object emptyState = FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.field$BlockBehaviour$BlockStateBase$fluidState(blockState)) == MFluids.WATER + ? MBlocks.WATER$defaultState + : MBlocks.AIR$defaultState; + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, basePos, emptyState, UpdateOption.builder().updateSuppressDrops().updateClients().updateNeighbors().build().flags()); + FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, player, WorldEvents.BLOCK_BREAK_EFFECT, basePos, baseState.customBlockState().registryId()); + } + + @Override + public boolean canSurvive(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object state = args[0]; + Object world = args[1]; + Object blockPos = args[2]; + ImmutableBlockState customState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null); + if (customState == null || customState.isEmpty()) { + return false; + } + MultiHighBlockBehavior behavior = customState.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return false; + } + IntegerProperty property = behavior.highProperty; + int high = customState.get(property); + if (high != property.min && high != property.max) { + Object aboveState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, LocationUtils.above(blockPos)); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, LocationUtils.below(blockPos)); + CustomBlock aboveCustomBlock = BlockStateUtils.getOptionalCustomBlockState(aboveState).map(blockState -> blockState.owner().value()).orElse(null); + CustomBlock belowCustomBlock = BlockStateUtils.getOptionalCustomBlockState(belowState).map(blockState -> blockState.owner().value()).orElse(null); + return aboveCustomBlock == behavior.customBlock && belowCustomBlock == behavior.customBlock; + } else if (high == property.max) { + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, LocationUtils.below(blockPos)); + CustomBlock belowCustomBlock = BlockStateUtils.getOptionalCustomBlockState(belowState).map(blockState -> blockState.owner().value()).orElse(null); + return belowCustomBlock == behavior.customBlock; + } + return true; + } + + @Override + public void placeMultiState(Object thisBlock, Object[] args, Callable superMethod) { + Object blockState = args[2]; + Object pos = args[1]; + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(blockState).orElse(null); + if (state == null) { + return; + } + MultiHighBlockBehavior behavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return; + } + IntegerProperty property = behavior.highProperty; + for (int i = property.min + 1; i <= property.max; i++) { + FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], LocationUtils.above(pos, i), state.with(property, i).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); + } + } + + @Override + public boolean hasMultiState(ImmutableBlockState baseState) { + return this.highProperty.max - this.highProperty.min > 0; + } + + @Override + public boolean canPlaceMultiState(BlockAccessor accessor, BlockPos pos, ImmutableBlockState state) { + MultiHighBlockBehavior behavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return false; + } + IntegerProperty property = behavior.highProperty; + if (pos.y() >= accessor.worldHeight().getMaxBuildHeight() - property.max) { + return false; + } + for (int i = property.min + 1; i <= property.max; i++) { + if (!accessor.getBlockState(pos.relative(Direction.UP, i)).isAir()) { + return false; + } + } + return true; + } + + @Override + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { + World world = context.getLevel(); + BlockPos pos = context.getClickedPos(); + MultiHighBlockBehavior behavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return null; + } + IntegerProperty property = behavior.highProperty; + if (pos.y() >= context.getLevel().worldHeight().getMaxBuildHeight() - property.max) { + return null; + } + for (int i = property.min + 1; i <= property.max; i++) { + if (!world.getBlock(pos.relative(Direction.UP, i)).canBeReplaced(context)) { + return null; + } + } + return state.with(property, 0); + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + IntegerProperty high = (IntegerProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("high"), "warning.config.block.behavior.multi_high.missing_high"); + return new MultiHighBlockBehavior(block, high); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java index f236c8c29..a8a8a1ee6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java @@ -16,6 +16,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { public static final Key WALL_BLOCK_ITEM = Key.from("craftengine:wall_block_item"); public static final Key CEILING_BLOCK_ITEM = Key.from("craftengine:ceiling_block_item"); public static final Key GROUND_BLOCK_ITEM = Key.from("craftengine:ground_block_item"); + public static final Key MULTI_HIGH_BLOCK_ITEM = Key.from("craftengine:multi_high_block_item"); public static void init() { register(EMPTY, EmptyItemBehavior.FACTORY); @@ -30,5 +31,6 @@ public class BukkitItemBehaviors extends ItemBehaviors { register(WALL_BLOCK_ITEM, WallBlockItemBehavior.FACTORY); register(CEILING_BLOCK_ITEM, CeilingBlockItemBehavior.FACTORY); register(GROUND_BLOCK_ITEM, GroundBlockItemBehavior.FACTORY); + register(MULTI_HIGH_BLOCK_ITEM, MultiHighBlockItemBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/MultiHighBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/MultiHighBlockItemBehavior.java new file mode 100644 index 000000000..fad14c59c --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/MultiHighBlockItemBehavior.java @@ -0,0 +1,122 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.block.behavior.MultiHighBlockBehavior; +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.plugin.reflection.minecraft.MFluids; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.properties.IntegerProperty; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.event.block.BlockCanBuildEvent; +import org.bukkit.inventory.EquipmentSlot; + +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +public class MultiHighBlockItemBehavior extends BlockItemBehavior { + public static final Factory FACTORY = new Factory(); + + public MultiHighBlockItemBehavior(Key blockId) { + super(blockId); + } + + @SuppressWarnings({"UnstableApiUsage", "DuplicatedCode"}) + @Override + protected boolean canPlace(BlockPlaceContext context, ImmutableBlockState state) { + if (!super.canPlace(context, state)) { + return false; + } + MultiHighBlockBehavior behavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return false; + } + IntegerProperty property = behavior.highProperty; + Player cePlayer = context.getPlayer(); + Object player = cePlayer != null ? cePlayer.serverPlayer() : null; + Object blockState = state.customBlockState().literalObject(); + for (int i = property.min + 1; i <= property.max; i++) { + Object blockPos = LocationUtils.toBlockPos(context.getClickedPos().relative(Direction.UP, i)); + try { + 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 = (boolean) CoreReflections.method$ServerLevel$checkEntityCollision.invoke(world, blockState, player, voxelShape, blockPos, true); // paper only + Block block = FastNMS.INSTANCE.method$CraftBlock$at(world, blockPos); + BlockData blockData = FastNMS.INSTANCE.method$CraftBlockData$fromData(blockState); + 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); + if (!canBuildEvent.isBuildable()) { + return false; + } + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to check canPlace", e); + return false; + } + } + return true; + } + + @Override + protected boolean placeBlock(Location location, ImmutableBlockState blockState, List revertState) { + MultiHighBlockBehavior behavior = blockState.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return false; + } + IntegerProperty property = behavior.highProperty; + for (int i = property.min + 1; i <= property.max; i++) { + Object level = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()); + Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(location.getBlockX(), location.getBlockY() + i, location.getBlockZ()); + UpdateOption option = UpdateOption.builder().updateNeighbors().updateClients().updateImmediate().updateKnownShape().build(); + Object fluidData = FastNMS.INSTANCE.method$BlockGetter$getFluidState(level, blockPos); + Object stateToPlace = fluidData == MFluids.WATER$defaultState ? MBlocks.WATER$defaultState : MBlocks.AIR$defaultState; + revertState.add(location.getWorld().getBlockAt(location.getBlockX(), location.getBlockY() + i, location.getBlockZ()).getState()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, stateToPlace, option.flags()); + } + return super.placeBlock(location, blockState, revertState); + } + + public static class Factory implements ItemBehaviorFactory { + @Override + public ItemBehavior create(Pack pack, Path path, String node, Key key, Map arguments) { + Object id = arguments.get("block"); + if (id == null) { + throw new LocalizedResourceConfigException("warning.config.item.behavior.multi_high.missing_block"); + } + if (id instanceof Map map) { + addPendingSection(pack, path, node, key, map); + return new MultiHighBlockItemBehavior(key); + } else { + return new MultiHighBlockItemBehavior(Key.of(id.toString())); + } + } + } +} 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 f421ddedd..83b564ac9 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 @@ -45,10 +45,18 @@ public final class LocationUtils { return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + 1, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); } + public static Object above(Object blockPos, int y) { + return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + y, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); + } + public static Object below(Object blockPos) { return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) - 1, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); } + public static Object below(Object blockPos, int y) { + return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) - y, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); + } + public static Object toBlockPos(int x, int y, int z) { return FastNMS.INSTANCE.constructor$BlockPos(x, y, z); } diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index f8f325359..f543e69db 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -289,6 +289,7 @@ warning.config.item.behavior.ground_block.missing_block: "Issue found in warning.config.item.behavior.furniture.missing_furniture: "Issue found in file - The item '' is missing the required 'furniture' argument for 'furniture_item' behavior." warning.config.item.behavior.liquid_collision.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'liquid_collision_block_item' behavior." warning.config.item.behavior.double_high.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'double_high_block_item' behavior." +warning.config.item.behavior.multi_high.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'multi_high_block_item' behavior." warning.config.item.legacy_model.missing_path: "Issue found in file - The item '' is missing the require 'path' argument for legacy-model." warning.config.item.legacy_model.overrides.missing_path: "Issue found in file - The item '' is missing the require 'path' argument for legacy-model overrides." warning.config.item.legacy_model.overrides.missing_predicate: "Issue found in file - The item '' is missing the require 'predicate' argument for legacy-model overrides." @@ -433,6 +434,7 @@ warning.config.block.behavior.chime.missing_sounds_projectile_hit: "Issu warning.config.block.behavior.surface_spreading.missing_base_block: "Issue found in file - The block '' is missing the required 'base-block' argument for 'surface_spreading_block' behavior." warning.config.block.behavior.snowy.missing_snowy: "Issue found in file - The block '' is missing the required 'snowy' property for 'snowy_block' behavior." warning.config.block.behavior.hangable.missing_hanging: "Issue found in file - The block '' is missing the required 'hanging' property for 'hangable_block' behavior." +warning.config.block.behavior.multi_high.missing_high: "Issue found in file - The block '' is missing the required 'high' property for 'multi_high_block' behavior." warning.config.model.generation.missing_parent: "Issue found in file - The config '' is missing the required 'parent' argument in 'generation' section." warning.config.model.generation.conflict: "Issue found in file - Failed to generate model for '' as two or more configurations attempt to generate different json models with the same path: ''." warning.config.model.generation.invalid_display_position: "Issue found in file - The config '' is using an invalid display position '' in 'generation.display' section. Allowed display positions: []" diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index ee38b4de6..6367c9c05 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -289,6 +289,7 @@ warning.config.item.behavior.ground_block.missing_block: "在文件 在文件 发现问题 - 物品 '' 的 'furniture_item' 行为缺少必需的 'furniture' 参数" warning.config.item.behavior.liquid_collision.missing_block: "在文件 发现问题 - 物品 '' 的 'liquid_collision_block_item' 行为缺少必需的 'block' 参数" warning.config.item.behavior.double_high.missing_block: "在文件 发现问题 - 物品 '' 的 'double_high_block_item' 行为缺少必需的 'block' 参数" +warning.config.item.behavior.multi_high.missing_block: "在文件 发现问题 - 物品 '' 的 'multi_high_block_item' 行为缺少必需的 'block' 参数" warning.config.item.legacy_model.missing_path: "在文件 发现问题 - 物品 '' 的旧版模型(legacy-model)缺少必需的 'path' 参数" warning.config.item.legacy_model.overrides.missing_path: "在文件 发现问题 - 物品 '' 的旧版模型覆写规则(overrides)缺少必需的 'path' 参数" warning.config.item.legacy_model.overrides.missing_predicate: "在文件 发现问题 - 物品 '' 的旧版模型覆写规则(overrides)缺少必需的 'predicate' 参数" @@ -433,6 +434,7 @@ warning.config.block.behavior.chime.missing_sounds_projectile_hit: "在 warning.config.block.behavior.surface_spreading.missing_base_block: "在文件 发现问题 - 方块 '' 的 'surface_spreading_block' 行为缺少必需的 'base-block' 选项" warning.config.block.behavior.snowy.missing_snowy: "在文件 发现问题 - 方块 '' 的 'snowy_block' 行为缺少必需的 'snowy' 属性" warning.config.block.behavior.hangable.missing_hanging: "在文件 发现问题 - 方块 '' 的 'hangable_block' 行为缺少必需的 'hanging' 属性" +warning.config.block.behavior.multi_high.missing_high: "在文件 发现问题 - 方块 '' 的 'multi_high_block' 行为缺少必需的 'high' 属性" warning.config.model.generation.missing_parent: "在文件 发现问题 - 配置项 '' 的 'generation' 段落缺少必需的 'parent' 参数" warning.config.model.generation.conflict: "在文件 发现问题 - 无法为 '' 生成模型 存在多个配置尝试使用相同路径 '' 生成不同的 JSON 模型" warning.config.model.generation.invalid_display_position: "在文件 发现问题 - 配置项 '' 在 'generation.display' 区域使用了无效的 display 位置类型 ''. 可用展示类型: []"