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 e819e7515..11133b67d 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 @@ -46,6 +46,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key SEAT_BLOCK = Key.from("craftengine:seat_block"); public static final Key SURFACE_SPREADING_BLOCK = Key.from("craftengine:surface_spreading_block"); public static final Key SNOWY_BLOCK = Key.from("craftengine:snowy_block"); + public static final Key HANGABLE_BLOCK = Key.from("craftengine:hangable_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -90,5 +91,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(SEAT_BLOCK, SeatBlockBehavior.FACTORY); register(SURFACE_SPREADING_BLOCK, SurfaceSpreadingBlockBehavior.FACTORY); register(SNOWY_BLOCK, SnowyBlockBehavior.FACTORY); + register(HANGABLE_BLOCK, HangableBlockBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java index d932532cf..91168a0fc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java @@ -1,6 +1,5 @@ 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.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; @@ -11,51 +10,31 @@ import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.parser.BlockStateParser; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Direction; -import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.LazyReference; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.bukkit.block.BlockState; import org.bukkit.event.block.BlockFormEvent; +import org.jetbrains.annotations.Nullable; import java.util.Map; -import java.util.Optional; import java.util.concurrent.Callable; public class ConcretePowderBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); - private final Key targetBlock; // TODO 更宽泛的,使用state,似乎也不是很好的方案? - private Object defaultBlockState; - private ImmutableBlockState defaultImmutableBlockState; + private final LazyReference<@Nullable ImmutableBlockState> targetBlock; - public ConcretePowderBlockBehavior(CustomBlock block, Key targetBlock) { + public ConcretePowderBlockBehavior(CustomBlock block, String targetBlock) { super(block); - this.targetBlock = targetBlock; - } - - public ImmutableBlockState defaultImmutableBlockState() { - if (this.defaultImmutableBlockState == null) { - this.getDefaultBlockState(); - } - return this.defaultImmutableBlockState; + this.targetBlock = LazyReference.lazyReference(() -> BlockStateParser.deserialize(targetBlock)); } public Object getDefaultBlockState() { - if (this.defaultBlockState != null) { - return this.defaultBlockState; - } - Optional optionalCustomBlock = BukkitBlockManager.instance().blockById(this.targetBlock); - if (optionalCustomBlock.isPresent()) { - CustomBlock customBlock = optionalCustomBlock.get(); - this.defaultBlockState = customBlock.defaultState().customBlockState().literalObject(); - this.defaultImmutableBlockState = customBlock.defaultState(); - } else { - CraftEngine.instance().logger().warn("Failed to create solid block " + this.targetBlock + " in ConcretePowderBlockBehavior"); - this.defaultBlockState = MBlocks.STONE$defaultState; - this.defaultImmutableBlockState = EmptyBlock.STATE; - } - return this.defaultBlockState; + ImmutableBlockState state = this.targetBlock.get(); + return state != null ? state.customBlockState().literalObject() : MBlocks.STONE$defaultState; } @SuppressWarnings("UnstableApiUsage") @@ -72,7 +51,7 @@ public class ConcretePowderBlockBehavior extends BukkitBlockBehavior { craftBlockState.setBlockData(BlockStateUtils.fromBlockData(getDefaultBlockState())); BlockFormEvent event = new BlockFormEvent(craftBlockState.getBlock(), craftBlockState); if (!EventUtils.fireAndCheckCancel(event)) { - return defaultImmutableBlockState(); + return this.targetBlock.get(); } else { return super.updateStateForPlacement(context, state); } @@ -148,7 +127,7 @@ public class ConcretePowderBlockBehavior extends BukkitBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { String solidBlock = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("solid-block"), "warning.config.block.behavior.concrete.missing_solid"); - return new ConcretePowderBlockBehavior(block, Key.of(solidBlock)); + return new ConcretePowderBlockBehavior(block, solidBlock); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java index 2e6793e35..7ae23af7f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java @@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.IntegerProperty; import net.momirealms.craftengine.core.block.properties.Property; 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.ItemKeys; import net.momirealms.craftengine.core.item.context.UseOnContext; @@ -121,14 +122,15 @@ public class CropBlockBehavior extends BukkitBlockBehavior { } @Override - public void performBoneMeal(Object thisBlock, Object[] args) throws Exception { + public void performBoneMeal(Object thisBlock, Object[] args) { this.performBoneMeal(args[0], args[2], args[3]); } @Override public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { Item item = context.getItem(); - if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode()) + Player player = context.getPlayer(); + if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || player == null || player.isAdventureMode()) return InteractionResult.PASS; if (isMaxAge(state)) return InteractionResult.PASS; @@ -144,7 +146,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { sendSwing = true; } if (sendSwing) { - context.getPlayer().swingHand(context.getHand()); + player.swingHand(context.getHand()); } return InteractionResult.SUCCESS; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java index 64c86a1ca..27ffdea31 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java @@ -145,6 +145,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior implements IsPat @SuppressWarnings("unchecked") private void playerToggle(UseOnContext context, ImmutableBlockState state) { Player player = context.getPlayer(); + if (player == null) return; this.toggle(state, context.getLevel(), context.getClickedPos(), player); if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item) context.getItem())) { player.swingHand(context.getHand()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java index d04773e38..a53af5459 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java @@ -14,6 +14,7 @@ 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.entity.player.InteractionResult; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.context.UseOnContext; @@ -83,7 +84,8 @@ public class GrassBlockBehavior extends BukkitBlockBehavior { @Override public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { Item item = context.getItem(); - if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode()) + Player player = context.getPlayer(); + if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || player == null || player.isAdventureMode()) return InteractionResult.PASS; BlockPos pos = context.getClickedPos(); BukkitExistingBlock upper = (BukkitExistingBlock) context.getLevel().getBlock(pos.x(), pos.y() + 1, pos.z()); @@ -102,7 +104,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior { sendSwing = true; } if (sendSwing) { - context.getPlayer().swingHand(context.getHand()); + player.swingHand(context.getHand()); } return InteractionResult.SUCCESS; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangableBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangableBlockBehavior.java new file mode 100644 index 000000000..a02fdd2f9 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangableBlockBehavior.java @@ -0,0 +1,93 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +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.BlockStateUtils; +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.behavior.IsPathFindableBlockBehavior; +import net.momirealms.craftengine.core.block.properties.BooleanProperty; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.concurrent.Callable; + +public class HangableBlockBehavior extends BukkitBlockBehavior implements IsPathFindableBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final BooleanProperty hanging; + + public HangableBlockBehavior(CustomBlock customBlock, BooleanProperty hanging) { + super(customBlock); + this.hanging = hanging; + } + + @Override + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { + BooleanProperty hanging = (BooleanProperty) state.owner().value().getProperty("hanging"); + if (hanging == null) return state; + @Nullable BooleanProperty waterlogged = (BooleanProperty) state.owner().value().getProperty("waterlogged"); + Object world = context.getLevel().serverWorld(); + Object blockPos = LocationUtils.toBlockPos(context.getClickedPos()); + Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.method$BlockGetter$getFluidState(world, blockPos)); + for (Direction direction : context.getNearestLookingDirections()) { + if (direction.axis() != Direction.Axis.Y) continue; + ImmutableBlockState blockState = state.with(hanging, direction == Direction.UP); + if (!FastNMS.INSTANCE.method$BlockStateBase$canSurvive(blockState.customBlockState().literalObject(), world, blockPos)) continue; + return waterlogged != null ? blockState.with(waterlogged, fluidType == MFluids.WATER) : blockState; + } + return state; + } + + @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 blockState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null); + if (blockState == null) return false; + BooleanProperty hangingProperty = (BooleanProperty) blockState.owner().value().getProperty("hanging"); + if (hangingProperty == null) return false; + Boolean hanging = blockState.get(hangingProperty); + Object relativePos = FastNMS.INSTANCE.method$BlockPos$relative(blockPos, hanging ? CoreReflections.instance$Direction$UP : CoreReflections.instance$Direction$DOWN); + return FastNMS.INSTANCE.method$Block$canSupportCenter(world, relativePos, hanging ? CoreReflections.instance$Direction$DOWN : CoreReflections.instance$Direction$UP); + } + + @Override + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null); + if (state == null) return MBlocks.AIR$defaultState; + @Nullable BooleanProperty waterlogged = (BooleanProperty) state.owner().value().getProperty("waterlogged"); + if (waterlogged != null && state.get(waterlogged)) { + FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleFluidTick(args[updateShape$level], args[updateShape$blockPos], MFluids.WATER, 5); + } + BooleanProperty hanging = (BooleanProperty) state.owner().value().getProperty("hanging"); + if (hanging == null) return MBlocks.AIR$defaultState; + if ((state.get(hanging) ? CoreReflections.instance$Direction$UP : CoreReflections.instance$Direction$DOWN) == args[updateShape$direction] + && !FastNMS.INSTANCE.method$BlockStateBase$canSurvive(args[0], args[updateShape$level], args[updateShape$blockPos])) { + return MBlocks.AIR$defaultState; + } + return superMethod.call(); + } + + @Override + public boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) { + return false; + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + BooleanProperty hanging = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("hanging"), "warning.config.block.behavior.hangable.missing_hanging"); + return new HangableBlockBehavior(block, hanging); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java index 85924d053..3d921fb77 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java @@ -165,7 +165,7 @@ public class LeavesBlockBehavior extends BukkitBlockBehavior { public BlockBehavior create(CustomBlock block, Map arguments) { Property persistent = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("persistent"), "warning.config.block.behavior.leaves.missing_persistent"); Property distance = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("distance"), "warning.config.block.behavior.leaves.missing_distance"); - int actual = distance.possibleValues().get(distance.possibleValues().size() - 1); + int actual = distance.possibleValues().getLast(); return new LeavesBlockBehavior(block, actual, distance, persistent); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java index fa3535e88..9f13df44c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java @@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.IntegerProperty; import net.momirealms.craftengine.core.block.properties.Property; 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.ItemKeys; import net.momirealms.craftengine.core.item.context.UseOnContext; @@ -148,7 +149,8 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior { @Override public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { Item item = context.getItem(); - if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode()) + Player player = context.getPlayer(); + if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || player == null || player.isAdventureMode()) return InteractionResult.PASS; boolean sendSwing = false; Object visualState = state.vanillaBlockState().literalObject(); @@ -162,7 +164,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior { sendSwing = true; } if (sendSwing) { - context.getPlayer().swingHand(context.getHand()); + player.swingHand(context.getHand()); } return InteractionResult.SUCCESS; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java index 6556345dc..933e2b3de 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java @@ -46,7 +46,7 @@ public class StackableBlockBehavior extends BukkitBlockBehavior { @SuppressWarnings("unchecked") public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { Player player = context.getPlayer(); - if (player.isSecondaryUseActive()) { + if (player == null || player.isSecondaryUseActive()) { return InteractionResult.PASS; } Item item = (Item) context.getItem(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java index 7d2793e02..c297d3289 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java @@ -118,6 +118,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior implements IsPath @SuppressWarnings("unchecked") private void playerToggle(UseOnContext context, ImmutableBlockState state) { Player player = context.getPlayer(); + if (player == null) return; this.toggle(state, context.getLevel(), context.getClickedPos(), player); if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item) context.getItem())) { player.swingHand(context.getHand()); 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 271930a23..23fde9447 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 @@ -13,6 +13,8 @@ public class BukkitItemBehaviors extends ItemBehaviors { public static final Key AXE_ITEM = Key.from("craftengine:axe_item"); public static final Key DOUBLE_HIGH_BLOCK_ITEM = Key.from("craftengine:double_high_block_item"); 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 void init() { register(EMPTY, EmptyItemBehavior.FACTORY); @@ -24,5 +26,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { register(AXE_ITEM, AxeItemBehavior.FACTORY); register(DOUBLE_HIGH_BLOCK_ITEM, DoubleHighBlockItemBehavior.FACTORY); register(WALL_BLOCK_ITEM, WallBlockItemBehavior.FACTORY); + register(CEILING_BLOCK_ITEM, CeilingBlockItemBehavior.FACTORY); + register(GROUND_BLOCK_ITEM, GroundBlockItemBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CeilingBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CeilingBlockItemBehavior.java new file mode 100644 index 000000000..2a38e53a6 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CeilingBlockItemBehavior.java @@ -0,0 +1,51 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.core.entity.player.InteractionResult; +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.item.context.UseOnContext; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.Key; + +import java.nio.file.Path; +import java.util.Map; + +public class CeilingBlockItemBehavior extends BlockItemBehavior { + public static final Factory FACTORY = new Factory(); + + public CeilingBlockItemBehavior(Key ceilingBlockId) { + super(ceilingBlockId); + } + + @Override + public InteractionResult useOnBlock(UseOnContext context) { + return this.place(new BlockPlaceContext(context)); + } + + @Override + public InteractionResult place(BlockPlaceContext context) { + if (context.getClickedFace() != Direction.DOWN) { + return InteractionResult.PASS; + } + return super.place(context); + } + + 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.ceiling_block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for ceiling_block_item behavior")); + } + if (id instanceof Map map) { + addPendingSection(pack, path, node, key, map); + return new CeilingBlockItemBehavior(key); + } else { + return new CeilingBlockItemBehavior(Key.of(id.toString())); + } + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/GroundBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/GroundBlockItemBehavior.java new file mode 100644 index 000000000..05956b049 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/GroundBlockItemBehavior.java @@ -0,0 +1,51 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.core.entity.player.InteractionResult; +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.item.context.UseOnContext; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.Key; + +import java.nio.file.Path; +import java.util.Map; + +public class GroundBlockItemBehavior extends BlockItemBehavior { + public static final Factory FACTORY = new Factory(); + + public GroundBlockItemBehavior(Key ceilingBlockId) { + super(ceilingBlockId); + } + + @Override + public InteractionResult useOnBlock(UseOnContext context) { + return this.place(new BlockPlaceContext(context)); + } + + @Override + public InteractionResult place(BlockPlaceContext context) { + if (context.getClickedFace() != Direction.UP) { + return InteractionResult.PASS; + } + return super.place(context); + } + + 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.ground_block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for ground_block_item behavior")); + } + if (id instanceof Map map) { + addPendingSection(pack, path, node, key, map); + return new GroundBlockItemBehavior(key); + } else { + return new GroundBlockItemBehavior(Key.of(id.toString())); + } + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WallBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WallBlockItemBehavior.java index 94b5254f5..58d597fa8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WallBlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WallBlockItemBehavior.java @@ -24,6 +24,7 @@ public class WallBlockItemBehavior extends BlockItemBehavior { return this.place(new BlockPlaceContext(context)); } + @Override public InteractionResult place(BlockPlaceContext context) { if (context.getClickedFace().stepY() != 0) { return InteractionResult.PASS; diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 781d07a46..899854b70 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -219,6 +219,8 @@ warning.config.item.behavior.missing_type: "Issue found in file warning.config.item.behavior.invalid_type: "Issue found in file - The item '' is using an invalid item behavior type ''." warning.config.item.behavior.block.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'block_item' behavior." warning.config.item.behavior.wall_block.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'wall_block_item' behavior." +warning.config.item.behavior.ceiling_block.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'ceiling_block_item' behavior." +warning.config.item.behavior.ground_block.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'ground_block_item' behavior." 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." @@ -363,6 +365,7 @@ warning.config.block.behavior.attached_stem.missing_stem: "Issue found i warning.config.block.behavior.chime.missing_sounds_projectile_hit: "Issue found in file - The block '' is missing the required 'sounds.projectile-hit' argument for 'chime_block' behavior." 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.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 d40ebd642..038b7d04d 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -219,6 +219,8 @@ warning.config.item.behavior.missing_type: "在文件 发现问 warning.config.item.behavior.invalid_type: "在文件 发现问题 - 物品 '' 使用了无效的行为类型 ''" warning.config.item.behavior.block.missing_block: "在文件 发现问题 - 物品 '' 的 'block_item' 行为缺少必需的 'block' 参数" warning.config.item.behavior.wall_block.missing_block: "在文件 发现问题 - 物品 '' 缺少 'wall_block_item' 行为所需的 'block' 参数" +warning.config.item.behavior.ceiling_block.missing_block: "在文件 发现问题 - 物品 '' 缺少 'ceiling_block_item' 行为所需的 'block' 参数" +warning.config.item.behavior.ground_block.missing_block: "在文件 发现问题 - 物品 '' 缺少 'ground_block_item' 行为所需的 'block' 参数" warning.config.item.behavior.furniture.missing_furniture: "在文件 发现问题 - 物品 '' 的 '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' 参数" @@ -363,6 +365,7 @@ warning.config.block.behavior.attached_stem.missing_stem: "在文件 在文件 发现问题 - 方块 '' 的 'chime_block' 行为缺少必需的 '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.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/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java index 310131a79..6d794219f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java @@ -73,7 +73,7 @@ public abstract class BlockBehavior { superMethod.call(); } - // 1.20+ BlockState state, LevelReader world, BlockPos pos + // BlockState state, LevelReader world, BlockPos pos public boolean canSurvive(Object thisBlock, Object[] args, Callable superMethod) throws Exception { return (boolean) superMethod.call(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java index 358116670..6e1e58bf4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.plugin.context.function; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.UpdateOption; import net.momirealms.craftengine.core.plugin.context.Condition; @@ -10,9 +11,9 @@ import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextPar 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.ExistingBlock; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; @@ -20,15 +21,28 @@ import java.util.Optional; public class CycleBlockPropertyFunction extends AbstractConditionalFunction { private final String property; + @Nullable + private final Map rules; + @Nullable private final NumberProvider inverse; private final NumberProvider x; private final NumberProvider y; private final NumberProvider z; private final NumberProvider updateFlags; - public CycleBlockPropertyFunction(List> predicates, String property, NumberProvider inverse, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider updateFlags) { + public CycleBlockPropertyFunction( + List> predicates, + String property, + @Nullable Map rules, + @Nullable NumberProvider inverse, + NumberProvider x, + NumberProvider y, + NumberProvider z, + NumberProvider updateFlags + ) { super(predicates); this.property = property; + this.rules = rules; this.inverse = inverse; this.x = x; this.y = y; @@ -44,11 +58,26 @@ public class CycleBlockPropertyFunction extends AbstractCon int x = MiscUtils.fastFloor(this.x.getDouble(ctx)); int y = MiscUtils.fastFloor(this.y.getDouble(ctx)); int z = MiscUtils.fastFloor(this.z.getDouble(ctx)); - ExistingBlock blockAt = world.getBlock(x, y, z); - BlockStateWrapper wrapper = blockAt.blockState().cycleProperty(this.property, this.inverse.getInt(ctx) == 0); + BlockStateWrapper wrapper = updateBlockState(world.getBlock(x, y, z).blockState(), ctx); world.setBlockState(x, y, z, wrapper, this.updateFlags.getInt(ctx)); } + private BlockStateWrapper updateBlockState(BlockStateWrapper wrapper, CTX ctx) { + boolean inverse = this.inverse != null && this.inverse.getInt(ctx) == 0; + if (this.rules == null) { + return wrapper.cycleProperty(this.property, inverse); + } + Object value = wrapper.getProperty(this.property); + if (value == null) { + return wrapper.cycleProperty(this.property, inverse); + } + String mapValue = this.rules.get(value.toString()); + if (mapValue == null) { + return wrapper.cycleProperty(this.property, inverse); + } + return wrapper.withProperty(this.property, mapValue); + } + @Override public Key type() { return CommonFunctions.CYCLE_BLOCK_PROPERTY; @@ -62,8 +91,15 @@ public class CycleBlockPropertyFunction extends AbstractCon @Override public Function create(Map arguments) { + String property = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("property"), "warning.config.function.cycle_block_property.missing_property"); + Map rules; + if (arguments.containsKey("rules")) { + rules = new Object2ObjectOpenHashMap<>(); + MiscUtils.castToMap(arguments.get("rules"), false).forEach((k, v) -> rules.put(k, v.toString())); + } else rules = null; return new CycleBlockPropertyFunction<>(getPredicates(arguments), - ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("property"), "warning.config.function.cycle_block_property.missing_property"), + property, + rules, NumberProviders.fromObject(arguments.getOrDefault("inverse", "")), NumberProviders.fromObject(arguments.getOrDefault("x", "")), NumberProviders.fromObject(arguments.getOrDefault("y", "")),