diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java index 4ae6829e4..e585501ec 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java @@ -1,23 +1,136 @@ 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.MFluids; +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.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.block.state.properties.SingleBlockHalf; +import net.momirealms.craftengine.core.block.state.properties.StairsShape; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.HorizontalDirection; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.BlockPos; +import org.jetbrains.annotations.Nullable; import java.util.Map; +import java.util.concurrent.Callable; public class StairsBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); + private final Property facingProperty; + private final Property halfProperty; + private final Property shapeProperty; - public StairsBlockBehavior(CustomBlock block) { + public StairsBlockBehavior(CustomBlock block, Property facing, Property half, Property shape) { super(block); + this.facingProperty = facing; + this.halfProperty = half; + this.shapeProperty = shape; + } + + @Override + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { + Direction clickedFace = context.getClickedFace(); + BlockPos clickedPos = context.getClickedPos(); + Object fluidState = FastNMS.INSTANCE.method$Level$getFluidState(context.getLevel().serverWorld(), LocationUtils.toBlockPos(clickedPos)); + return state.owner().value().defaultState() + .with(this.facingProperty, context.getHorizontalDirection().toHorizontalDirection()) + .with(this.halfProperty, clickedFace != Direction.DOWN && (clickedFace == Direction.UP || !(context.getClickLocation().y - clickedPos.y() > 0.5)) ? SingleBlockHalf.BOTTOM : SingleBlockHalf.TOP) + .with(this.waterloggedProperty, FastNMS.INSTANCE.method$FluidState$getType(fluidState) == MFluids.WATER); + } + + @Override + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object level; + Object blockPos; + Object blockState = args[0]; + if (VersionHelper.isOrAbove1_21_2()) { + level = args[1]; + blockPos = args[3]; + } else { + level = args[3]; + blockPos = args[4]; + } + int stateId = BlockStateUtils.blockStateToId(blockState); + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); + if (immutableBlockState == null || immutableBlockState.isEmpty()) return blockState; + Direction direction = DirectionUtils.fromNMSDirection(VersionHelper.isOrAbove1_21_2() ? args[4] : args[0]); + return direction.axis().isHorizontal() + ? immutableBlockState.with(this.shapeProperty, getStairsShape(immutableBlockState, level, LocationUtils.fromBlockPos(blockPos))).customBlockState().handle() + : superMethod.call(); + } + + private StairsShape getStairsShape(ImmutableBlockState state, Object level, BlockPos blockPos) { + if (!state.customBlockState().isVanillaBlock()) { + Direction direction = state.get(this.facingProperty).toDirection(); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.toBlockPos(blockPos.relative(direction))); + StairsShape straight1 = getStairsShape(state, level, blockPos, blockState, direction); + if (straight1 != null) return straight1; + Object blockState1 = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.toBlockPos(blockPos.relative(direction.opposite()))); + StairsShape straight = getStairsShape(state, level, blockPos, blockState1, direction); + if (straight != null) return straight; + } + return StairsShape.STRAIGHT; + } + + @Nullable + private StairsShape getStairsShape(ImmutableBlockState state, Object level, BlockPos blockPos, Object blockState1, Direction direction) { + if (isStairs(blockState1)) { + int stateId = BlockStateUtils.blockStateToId(blockState1); + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); + if (immutableBlockState == null || immutableBlockState.isEmpty()) return StairsShape.STRAIGHT; + if (state.get(this.facingProperty) == immutableBlockState.get(this.facingProperty)) { + Direction direction1 = immutableBlockState.get(this.facingProperty).toDirection(); + if (direction1.axis() != state.get(this.facingProperty).toDirection().axis() && canTakeShape(state, level, blockPos, direction1.opposite())) { + if (direction1 == direction.counterClockWise()) { + return StairsShape.OUTER_LEFT; + } + return StairsShape.OUTER_RIGHT; + } + } + } + return null; + } + + private boolean isStairs(Object state) { + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state)); + if (immutableBlockState == null || immutableBlockState.isEmpty()) { + return FastNMS.INSTANCE.method$BlockState$getBlock(state).equals(CoreReflections.clazz$StairBlock); + } + return immutableBlockState.behavior().equals(this); + } + + private boolean canTakeShape(ImmutableBlockState state, Object level, BlockPos blockPos, Direction direction) { + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.toBlockPos(blockPos.relative(direction))); + if (!isStairs(blockState)) { + return false; + } + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)); + if (immutableBlockState == null || immutableBlockState.isEmpty()) { + return false; + } + return immutableBlockState.get(this.facingProperty) != state.get(this.facingProperty) || immutableBlockState.get(this.halfProperty) != state.get(this.halfProperty); } public static class Factory implements BlockBehaviorFactory { @Override + @SuppressWarnings("unchecked") public BlockBehavior create(CustomBlock block, Map arguments) { - return new StairsBlockBehavior(block); + Property facing = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.stairs.missing_facing"); + Property half = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("half"), "warning.config.block.behavior.stairs.missing_half"); + Property shape = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("shape"), "warning.config.block.behavior.stairs.missing_shape"); + return new StairsBlockBehavior(block, facing, half, shape); } } } 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 ed5a93376..0f07d09fa 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 @@ -3347,4 +3347,11 @@ public final class CoreReflections { throw new ReflectionInitException("Failed to init ItemStack$CODEC", e); } } + + public static final Class clazz$StairBlock = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.level.block.BlockStairs", + "world.level.block.StairBlock" + ) + ); } diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 486500104..f00a6c2ed 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -263,6 +263,9 @@ warning.config.block.behavior.fence_gate.missing_in_wall: "Issue found i warning.config.block.behavior.fence_gate.missing_open: "Issue found in file - The block '' is missing the required 'powered' argument for 'fence_gate_block' behavior." warning.config.block.behavior.fence_gate.missing_powered: "Issue found in file - The block '' is missing the required 'open' argument for 'fence_gate_block' behavior." warning.config.block.behavior.trapdoor.missing_type: "Issue found in file - The block '' is missing the required 'type' property for 'slab_block' behavior." +warning.config.block.behavior.stairs.missing_facing: "Issue found in file - The block '' is missing the required 'facing' property for 'stairs' behavior." +warning.config.block.behavior.stairs.missing_half: "Issue found in file - The block '' is missing the required 'half' property for 'stairs' behavior." +warning.config.block.behavior.stairs.missing_shape: "Issue found in file - The block '' is missing the required 'shape' property for 'stairs' 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.invalid_display_position: "Issue found in file - The config '' is using an invalid display position '' in 'generation.display' section. Allowed display positions: []" warning.config.model.generation.invalid_gui_light: "Issue found in file - The config '' is using an invalid gui-light option '' in 'generation' section. Allowed gui light options: []" diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index f4a0bb371..89491c83e 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -263,6 +263,9 @@ warning.config.block.behavior.fence_gate.missing_in_wall: "在文件 在文件 发现问题 - 方块 '' 的 'fence_gate_block' 行为缺少必需的 'open' 属性" warning.config.block.behavior.fence_gate.missing_powered: "在文件 发现问题 - 方块 '' 的 'fence_gate_block' 行为缺少必需的 'powered' 属性" warning.config.block.behavior.slab.missing_type: "在文件 发现问题 - 方块 '' 的 'slab_block' 行为缺少必需的 'type' 属性" +warning.config.block.behavior.stairs.missing_facing: "在文件 发现问题 - 方块 '' 的 'stairs_block' 行为缺少必需的 'facing' 属性" +warning.config.block.behavior.stairs.missing_half: "在文件 发现问题 - 方块 '' 的 'stairs_block' 行为缺少必需的 'half' 属性" +warning.config.block.behavior.stairs.missing_shape: "在文件 发现问题 - 方块 '' 的 'stairs_block' 行为缺少必需的 'shape' 属性" warning.config.model.generation.missing_parent: "在文件 发现问题 - 配置项 '' 的 'generation' 段落缺少必需的 'parent' 参数" warning.config.model.generation.conflict: "在文件 发现问题 - 无法为 '' 生成模型 存在多个配置尝试使用相同路径 '' 生成不同的 JSON 模型" warning.config.model.generation.invalid_display_position: "在文件 发现问题 - 配置项 '' 在 'generation.display' 区域使用了无效的 display 位置类型 ''. 可用展示类型: []" diff --git a/gradle.properties b/gradle.properties index 159a9f1f2..1c385d86e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.jvmargs=-Xmx1G # Rule: [major update].[feature update].[bug fix] project_version=0.0.57.6 config_version=37 -lang_version=18 +lang_version=19 project_group=net.momirealms latest_supported_version=1.21.6