From 5c827ddf629e51976c01a9656db246e1d2857487 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sun, 25 May 2025 21:15:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=B7=E5=90=88=E6=96=B9=E5=9D=97=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/BukkitBlockManager.java | 2 +- .../bukkit/block/BukkitCustomBlock.java | 6 +- .../AbstractCanSurviveBlockBehavior.java | 77 ++++++++++++ .../block/behavior/BukkitBlockBehaviors.java | 6 +- .../block/behavior/BushBlockBehavior.java | 83 +++---------- .../behavior/ConcretePowderBlockBehavior.java | 17 ++- .../block/behavior/CropBlockBehavior.java | 19 ++- .../block/behavior/FallingBlockBehavior.java | 6 +- .../block/behavior/HangingBlockBehavior.java | 7 +- .../block/behavior/LampBlockBehavior.java | 22 +++- .../behavior/NearLiquidBlockBehavior.java | 102 ++++++++++++++++ .../block/behavior/OnLiquidBlockBehavior.java | 34 +++++- .../block/behavior/SaplingBlockBehavior.java | 14 +-- ...or.java => VerticalCropBlockBehavior.java} | 114 ++++-------------- .../bukkit/util/LocationUtils.java | 8 +- .../core/block/AbstractCustomBlock.java | 16 ++- .../craftengine/core/block/CustomBlock.java | 2 +- .../craftengine/core/block/EmptyBlock.java | 3 +- .../core/block/InactiveCustomBlock.java | 3 +- .../UnsafeCompositeBlockBehavior.java | 24 +++- .../core/pack/AbstractPackManager.java | 4 + .../craftengine/core/util/MiscUtils.java | 11 ++ gradle.properties | 2 +- .../shared/block/BlockBehavior.java | 2 +- 24 files changed, 361 insertions(+), 223 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java rename bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/{SugarCaneBlockBehavior.java => VerticalCropBlockBehavior.java} (54%) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java index 4f3b93ee7..4bbd48d83 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java @@ -428,7 +428,7 @@ public class BukkitBlockManager extends AbstractBlockManager { .properties(properties) .settings(settings) .lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true))) - .behavior(MiscUtils.castToMap(section.get("behavior"), true)) + .behavior(MiscUtils.getAsMapList(ResourceConfigUtils.get(section, "behavior", "behaviors"))) .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) .build(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java index 443c63c44..592862fde 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java @@ -41,7 +41,7 @@ public class BukkitCustomBlock extends AbstractCustomBlock { @NotNull Map variantMapper, @NotNull BlockSettings settings, @NotNull Map>> events, - @Nullable Map behavior, + @Nullable List> behavior, @Nullable LootTable lootTable ) { super(id, holder, properties, appearances, variantMapper, settings, events, behavior, lootTable); @@ -159,7 +159,7 @@ public class BukkitCustomBlock extends AbstractCustomBlock { protected Map appearances; protected Map variantMapper; protected BlockSettings settings; - protected Map behavior; + protected List> behavior; protected LootTable lootTable; protected Map>> events; @@ -180,7 +180,7 @@ public class BukkitCustomBlock extends AbstractCustomBlock { } @Override - public Builder behavior(Map behavior) { + public Builder behavior(List> behavior) { this.behavior = behavior; return this; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java new file mode 100644 index 000000000..16e915a1b --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java @@ -0,0 +1,77 @@ +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.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.bukkit.world.BukkitWorld; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldEvents; +import net.momirealms.craftengine.core.world.WorldPosition; + +import java.util.concurrent.Callable; + +public abstract class AbstractCanSurviveBlockBehavior extends BukkitBlockBehavior { + + protected AbstractCanSurviveBlockBehavior(CustomBlock customBlock) { + super(customBlock); + } + + @Override + public boolean canSurvive(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object state = args[0]; + Object world = args[1]; + Object pos = args[2]; + return canSurvive(thisBlock, state, world, pos); + } + + @Override + public void onPlace(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object world = args[1]; + Object blockPos = args[2]; + Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2); + } + + @Override + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object level; + Object blockPos; + Object state = args[0]; + if (VersionHelper.isOrAbove1_21_2()) { + level = args[1]; + blockPos = args[3]; + } else { + level = args[3]; + blockPos = args[4]; + } + int stateId = BlockStateUtils.blockStateToId(state); + ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(stateId); + if (previousState == null || previousState.isEmpty()) { + return state; + } + if (!canSurvive(thisBlock, new Object[] {state, level, blockPos}, () -> true)) { + 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)); + ContextHolder.Builder builder = ContextHolder.builder() + .withParameter(DirectContextParameters.POSITION, position); + for (Item item : previousState.getDrops(builder, world, null)) { + world.dropItemNaturally(position, item); + } + world.playBlockSound(position, previousState.sounds().breakSound()); + FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId); + return Reflections.method$Block$defaultBlockState.invoke(Reflections.instance$Blocks$AIR); + } + return state; + } + + protected abstract boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws Exception; +} 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 2b36c888f..b0942ed95 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 @@ -12,9 +12,10 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key STRIPPABLE_BLOCK = Key.from("craftengine:strippable_block"); public static final Key SAPLING_BLOCK = Key.from("craftengine:sapling_block"); public static final Key ON_LIQUID_BLOCK = Key.from("craftengine:on_liquid_block"); + public static final Key NEAR_LIQUID_BLOCK = Key.from("craftengine:near_liquid_block"); public static final Key WATERLOGGED_BLOCK = Key.from("craftengine:waterlogged_block"); public static final Key CONCRETE_POWDER_BLOCK = Key.from("craftengine:concrete_powder_block"); - public static final Key SUGARCANE_BLOCK = Key.from("craftengine:sugar_cane_block"); + public static final Key VERTICAL_CROP_BLOCK = Key.from("craftengine:vertical_crop_block"); public static final Key CROP_BLOCK = Key.from("craftengine:crop_block"); public static final Key GRASS_BLOCK = Key.from("craftengine:grass_block"); public static final Key LAMP_BLOCK = Key.from("craftengine:lamp_block"); @@ -28,9 +29,10 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(STRIPPABLE_BLOCK, StrippableBlockBehavior.FACTORY); register(SAPLING_BLOCK, SaplingBlockBehavior.FACTORY); register(ON_LIQUID_BLOCK, OnLiquidBlockBehavior.FACTORY); + register(NEAR_LIQUID_BLOCK, NearLiquidBlockBehavior.FACTORY); register(WATERLOGGED_BLOCK, WaterLoggedBlockBehavior.FACTORY); register(CONCRETE_POWDER_BLOCK, ConcretePowderBlockBehavior.FACTORY); - register(SUGARCANE_BLOCK, SugarCaneBlockBehavior.FACTORY); + register(VERTICAL_CROP_BLOCK, VerticalCropBlockBehavior.FACTORY); register(CROP_BLOCK, CropBlockBehavior.FACTORY); register(GRASS_BLOCK, GrassBlockBehavior.FACTORY); register(LAMP_BLOCK, LampBlockBehavior.FACTORY); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java index 6c1d87c11..11cf0c44b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java @@ -4,23 +4,13 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.BlockTags; -import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.Reflections; -import net.momirealms.craftengine.bukkit.world.BukkitWorld; 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.item.Item; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.Tuple; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.BlockPos; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.WorldEvents; -import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -28,76 +18,31 @@ import org.bukkit.NamespacedKey; import org.bukkit.Registry; import java.util.*; -import java.util.concurrent.Callable; -public class BushBlockBehavior extends BukkitBlockBehavior { +public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior { public static final Factory FACTORY = new Factory(); protected final List tagsCanSurviveOn; protected final Set blocksCansSurviveOn; protected final Set customBlocksCansSurviveOn; protected final boolean any; + protected final boolean stackable; - public BushBlockBehavior(CustomBlock block, List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn) { + public BushBlockBehavior(CustomBlock block, boolean stackable, List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn) { super(block); + this.stackable = stackable; this.tagsCanSurviveOn = tagsCanSurviveOn; this.blocksCansSurviveOn = blocksCansSurviveOn; this.customBlocksCansSurviveOn = customBlocksCansSurviveOn; this.any = this.tagsCanSurviveOn.isEmpty() && this.blocksCansSurviveOn.isEmpty() && this.customBlocksCansSurviveOn.isEmpty(); } - @Override - public void onPlace(Object thisBlock, Object[] args, Callable superMethod) throws Exception { - Object world = args[1]; - Object blockPos = args[2]; - Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2); - } - - @Override - public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { - Object level; - Object blockPos; - Object state = args[0]; - if (VersionHelper.isOrAbove1_21_2()) { - level = args[1]; - blockPos = args[3]; - } else { - level = args[3]; - blockPos = args[4]; - } - if (!canSurvive(thisBlock, state, level, blockPos)) { - int stateId = BlockStateUtils.blockStateToId(state); - ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(stateId); - if (previousState != null && !previousState.isEmpty()) { - 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)); - ContextHolder.Builder builder = ContextHolder.builder() - .withParameter(DirectContextParameters.POSITION, position); - for (Item item : previousState.getDrops(builder, world, null)) { - world.dropItemNaturally(position, item); - } - world.playBlockSound(position, previousState.sounds().breakSound()); - FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId); - } - return Reflections.method$Block$defaultBlockState.invoke(Reflections.instance$Blocks$AIR); - } - return super.updateShape(thisBlock, args, superMethod); - } - - @Override - public boolean canSurvive(Object thisBlock, Object[] args, Callable superMethod) throws Exception { - Object state = args[0]; - Object world = args[1]; - Object pos = args[2]; - return canSurvive(thisBlock, state, world, pos); - } - public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { Tuple, Set, Set> tuple = readTagsAndState(arguments, false); - return new BushBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right()); + boolean stackable = (boolean) arguments.getOrDefault("stackable", false); + return new BushBlockBehavior(block, stackable, tuple.left(), tuple.mid(), tuple.right()); } } @@ -127,7 +72,8 @@ public class BushBlockBehavior extends BukkitBlockBehavior { return new Tuple<>(mcTags, mcBlocks, customBlocks); } - protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { + @Override + protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws Exception { int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); @@ -151,12 +97,17 @@ public class BushBlockBehavior extends BukkitBlockBehavior { return true; } } else { - ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(id); - if (previousState != null && !previousState.isEmpty()) { - if (this.customBlocksCansSurviveOn.contains(previousState.owner().value().id().toString())) { + ImmutableBlockState belowCustomState = BukkitBlockManager.instance().getImmutableBlockState(id); + if (belowCustomState != null && !belowCustomState.isEmpty()) { + if (stackable) { + if (belowCustomState.owner().value() == super.customBlock) { + return true; + } + } + if (this.customBlocksCansSurviveOn.contains(belowCustomState.owner().value().id().toString())) { return true; } - if (this.customBlocksCansSurviveOn.contains(previousState.toString())) { + if (this.customBlocksCansSurviveOn.contains(belowCustomState.toString())) { return true; } } 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 e15ae9e6f..5fb2dff7a 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 @@ -25,15 +25,14 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.Callable; -// TODO Inject FallingBlockEntity? -public class ConcretePowderBlockBehavior extends FallingBlockBehavior { +public class ConcretePowderBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); - private final Key targetBlock; + private final Key targetBlock; // TODO 更宽泛的,使用state,似乎也不是很好的方案? private Object defaultBlockState; private ImmutableBlockState defaultImmutableBlockState; - public ConcretePowderBlockBehavior(CustomBlock block, float hurtAmount, int maxHurt, Key targetBlock) { - super(block, hurtAmount, maxHurt); + public ConcretePowderBlockBehavior(CustomBlock block, Key targetBlock) { + super(block); this.targetBlock = targetBlock; } @@ -79,7 +78,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior { return super.updateStateForPlacement(context, state); } } - } catch (ReflectiveOperationException e) { + } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to update state for placement " + context.getClickedPos(), e); } return super.updateStateForPlacement(context, state); @@ -118,7 +117,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior { } } } - return super.updateShape(thisBlock, args, superMethod); + return args[0]; } private static boolean shouldSolidify(Object level, Object blockPos, Object blockState) throws ReflectiveOperationException { @@ -155,10 +154,8 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - float hurtAmount = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("hurt-amount", -1f), "hurt-amount"); - int hurtMax = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-hurt", -1), "max-hurt"); String solidBlock = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("solid-block"), "warning.config.block.behavior.concrete.missing_solid"); - return new ConcretePowderBlockBehavior(block, hurtAmount, hurtMax, Key.of(solidBlock)); + return new ConcretePowderBlockBehavior(block, Key.of(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 5581362a0..df47ace39 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 @@ -25,7 +25,6 @@ import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.RandomUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.Tuple; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.Vec3i; import net.momirealms.craftengine.core.world.WorldPosition; @@ -33,12 +32,10 @@ import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.World; import java.lang.reflect.InvocationTargetException; -import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.Callable; -public class CropBlockBehavior extends BushBlockBehavior { +public class CropBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); private final IntegerProperty ageProperty; private final float growSpeed; @@ -46,9 +43,8 @@ public class CropBlockBehavior extends BushBlockBehavior { private final boolean isBoneMealTarget; private final NumberProvider boneMealBonus; - public CropBlockBehavior(CustomBlock block, List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn, - Property ageProperty, float growSpeed, int minGrowLight, boolean isBoneMealTarget, NumberProvider boneMealBonus) { - super(block, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); + public CropBlockBehavior(CustomBlock block, Property ageProperty, float growSpeed, int minGrowLight, boolean isBoneMealTarget, NumberProvider boneMealBonus) { + super(block); this.ageProperty = (IntegerProperty) ageProperty; this.growSpeed = growSpeed; this.minGrowLight = minGrowLight; @@ -105,8 +101,10 @@ public class CropBlockBehavior extends BushBlockBehavior { } @Override - protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { - return hasSufficientLight(world, blockPos) && super.canSurvive(thisBlock, state, world, blockPos); + public boolean canSurvive(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object world = args[1]; + Object pos = args[2]; + return hasSufficientLight(world, pos); } @Override @@ -204,13 +202,12 @@ public class CropBlockBehavior extends BushBlockBehavior { @SuppressWarnings("unchecked") @Override public BlockBehavior create(CustomBlock block, Map arguments) { - Tuple, Set, Set> tuple = readTagsAndState(arguments, false); Property ageProperty = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.crop.missing_age"); int minGrowLight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("light-requirement", 9), "light-requirement"); float growSpeed = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("grow-speed", 0.125f), "grow-speed"); boolean isBoneMealTarget = (boolean) arguments.getOrDefault("is-bone-meal-target", true); NumberProvider boneMealAgeBonus = NumberProviders.fromObject(arguments.getOrDefault("bone-meal-age-bonus", 1)); - return new CropBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right(), ageProperty, growSpeed, minGrowLight, isBoneMealTarget, boneMealAgeBonus); + return new CropBlockBehavior(block, ageProperty, growSpeed, minGrowLight, isBoneMealTarget, boneMealAgeBonus); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java index fb98a3e5f..48cd63cb8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java @@ -51,7 +51,7 @@ public class FallingBlockBehavior extends BukkitBlockBehavior { blockPos = args[4]; } Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2); - return super.updateShape(thisBlock, args, superMethod); + return args[0]; } @Override @@ -109,6 +109,8 @@ public class FallingBlockBehavior extends BukkitBlockBehavior { @Override public void onLand(Object thisBlock, Object[] args) throws Exception { Object fallingBlock = args[4]; + Object level = args[0]; + Object pos = args[1]; Object entityData = Reflections.field$Entity$entityData.get(fallingBlock); boolean isSilent = (boolean) Reflections.method$SynchedEntityData$get.invoke(entityData, Reflections.instance$Entity$DATA_SILENT); if (!isSilent) { @@ -116,8 +118,6 @@ public class FallingBlockBehavior extends BukkitBlockBehavior { int stateId = BlockStateUtils.blockStateToId(blockState); ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); if (immutableBlockState == null || immutableBlockState.isEmpty()) return; - Object level = args[0]; - Object pos = args[1]; net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); world.playBlockSound(Vec3d.atCenterOf(LocationUtils.fromBlockPos(pos)), immutableBlockState.sounds().landSound()); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java index 7366aad9d..a11b68890 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java @@ -13,8 +13,8 @@ import java.util.Set; public class HangingBlockBehavior extends BushBlockBehavior { public static final Factory FACTORY = new Factory(); - public HangingBlockBehavior(CustomBlock block, List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn) { - super(block, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); + public HangingBlockBehavior(CustomBlock block, boolean stackable, List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn) { + super(block, stackable, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); } @Override @@ -32,7 +32,8 @@ public class HangingBlockBehavior extends BushBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { Tuple, Set, Set> tuple = readTagsAndState(arguments, true); - return new HangingBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right()); + boolean stackable = (boolean) arguments.getOrDefault("stackable", false); + return new HangingBlockBehavior(block, stackable, tuple.left(), tuple.mid(), tuple.right()); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java index 56ff8079c..fd6f67e78 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java @@ -3,11 +3,13 @@ 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.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.Reflections; 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.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.shared.block.BlockBehavior; @@ -23,6 +25,13 @@ public class LampBlockBehavior extends BukkitBlockBehavior { this.litProperty = litProperty; } + @Override + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { + Object level = context.getLevel().serverWorld(); + state = state.with(this.litProperty, FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(level, LocationUtils.toBlockPos(context.getClickedPos()))); + return state; + } + @Override public void tick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { Object blockState = args[0]; @@ -30,9 +39,16 @@ public class LampBlockBehavior extends BukkitBlockBehavior { if (state == null || state.isEmpty()) return; Object world = args[1]; Object blockPos = args[2]; - if (state.get(this.litProperty) && !FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) { - // TODO Call Event - FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, state.cycle(this.litProperty).customBlockState().handle(), 2); + if (state.get(this.litProperty)) { + if (!FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) { + // TODO Call Event + FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, state.cycle(this.litProperty).customBlockState().handle(), 2); + } + } else { + if (FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) { + // TODO Call Event + FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, state.cycle(this.litProperty).customBlockState().handle(), 2); + } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java new file mode 100644 index 000000000..fd40a6b95 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java @@ -0,0 +1,102 @@ +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.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +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.util.MiscUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.shared.block.BlockBehavior; + +import java.util.List; +import java.util.Map; + +public class NearLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior { + private static final List WATER = List.of(Reflections.instance$Fluids$WATER, Reflections.instance$Fluids$FLOWING_WATER); + private static final List LAVA = List.of(Reflections.instance$Fluids$LAVA, Reflections.instance$Fluids$FLOWING_LAVA); + public static final Factory FACTORY = new Factory(); + private final boolean onWater; + private final boolean onLava; + private final boolean stackable; + private final BlockPos[] positions; + + public NearLiquidBlockBehavior(CustomBlock block, BlockPos[] positions, boolean stackable, boolean onWater, boolean onLava) { + super(block); + this.onWater = onWater; + this.onLava = onLava; + this.stackable = stackable; + this.positions = positions; + } + + public boolean onWater() { + return this.onWater; + } + + public boolean onLava() { + return this.onLava; + } + + public static class Factory implements BlockBehaviorFactory { + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); + boolean stackable = (boolean) arguments.getOrDefault("stackable", false); + List positionsToCheck = MiscUtils.getAsStringList(arguments.getOrDefault("positions", List.of())); + if (positionsToCheck.isEmpty()) { + return new NearLiquidBlockBehavior(block, new BlockPos[]{new BlockPos(0,-1,0)}, stackable, liquidTypes.contains("water"), liquidTypes.contains("lava")); + } else { + BlockPos[] pos = new BlockPos[positionsToCheck.size()]; + for (int i = 0; i < pos.length; i++) { + String[] split = positionsToCheck.get(i).split(","); + pos[i] = new BlockPos(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2])); + } + return new NearLiquidBlockBehavior(block, pos, stackable, liquidTypes.contains("water"), liquidTypes.contains("lava")); + } + } + } + + @Override + protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { + int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); + int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); + int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); + if (this.stackable) { + Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); + int id = BlockStateUtils.blockStateToId(belowState); + if (!BlockStateUtils.isVanillaBlock(id)) { + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(id); + if (immutableBlockState.owner().value() == super.customBlock) { + return true; + } + } + } + for (BlockPos pos : positions) { + Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x + pos.x(), y + pos.y(), z + pos.z()); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); + if (mayPlaceOn(belowState, world, belowPos)) { + return true; + } + } + return false; + } + + protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException { + Object fluidState = Reflections.method$Level$getFluidState.invoke(world, belowPos); + Object fluidStateAbove = Reflections.method$Level$getFluidState.invoke(world, LocationUtils.above(belowPos)); + if (Reflections.method$FluidState$getType.invoke(fluidStateAbove) != Reflections.instance$Fluids$EMPTY) { + return false; + } + if (this.onWater && (WATER.contains(Reflections.method$FluidState$getType.invoke(fluidState)) || Reflections.field$StateHolder$owner.get(belowState) == Reflections.instance$Blocks$ICE)) { + return true; + } + if (this.onLava && LAVA.contains(Reflections.method$FluidState$getType.invoke(fluidState))) { + return true; + } + return false; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java index dda596013..cb73eeaab 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java @@ -1,25 +1,30 @@ 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.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.Reflections; 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.util.MiscUtils; import net.momirealms.craftengine.shared.block.BlockBehavior; import java.util.List; import java.util.Map; -import java.util.Set; -public class OnLiquidBlockBehavior extends BushBlockBehavior { +public class OnLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior { public static final Factory FACTORY = new Factory(); private final boolean onWater; private final boolean onLava; + private final boolean stackable; - public OnLiquidBlockBehavior(CustomBlock block, boolean onWater, boolean onLava) { - super(block, List.of(), Set.of(), Set.of()); + public OnLiquidBlockBehavior(CustomBlock block, boolean stackable, boolean onWater, boolean onLava) { + super(block); this.onWater = onWater; this.onLava = onLava; + this.stackable = stackable; } public boolean onWater() { @@ -34,12 +39,31 @@ public class OnLiquidBlockBehavior extends BushBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); - return new OnLiquidBlockBehavior(block, liquidTypes.contains("water"), liquidTypes.contains("lava")); + boolean stackable = (boolean) arguments.getOrDefault("stackable", false); + return new OnLiquidBlockBehavior(block, stackable, liquidTypes.contains("water"), liquidTypes.contains("lava")); } } @Override + protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { + int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); + int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); + int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); + Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); + return mayPlaceOn(belowState, world, belowPos); + } + protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException { + if (this.stackable) { + int id = BlockStateUtils.blockStateToId(belowState); + if (!BlockStateUtils.isVanillaBlock(id)) { + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(id); + if (immutableBlockState.owner().value() == super.customBlock) { + return true; + } + } + } Object fluidState = Reflections.method$Level$getFluidState.invoke(world, belowPos); Object fluidStateAbove = Reflections.method$Level$getFluidState.invoke(world, LocationUtils.above(belowPos)); if (Reflections.method$FluidState$getType.invoke(fluidStateAbove) != Reflections.instance$Fluids$EMPTY) { 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 7511e265a..c83b5ff1a 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 @@ -17,26 +17,23 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.RandomUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.Tuple; import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.Location; import org.bukkit.World; -import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.concurrent.Callable; -public class SaplingBlockBehavior extends BushBlockBehavior { +public class SaplingBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); private final Key feature; private final Property stageProperty; private final double boneMealSuccessChance; private final float growSpeed; - public SaplingBlockBehavior(CustomBlock block, Key feature, Property stageProperty, List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn, double boneMealSuccessChance, float growSpeed) { - super(block, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); + public SaplingBlockBehavior(CustomBlock block, Key feature, Property stageProperty, double boneMealSuccessChance, float growSpeed) { + super(block); this.feature = feature; this.stageProperty = stageProperty; this.boneMealSuccessChance = boneMealSuccessChance; @@ -53,7 +50,7 @@ public class SaplingBlockBehavior extends BushBlockBehavior { Object blockPos = args[2]; Object blockState = args[0]; Object aboveBlockPos = LocationUtils.above(blockPos); - if ((int) Reflections.method$LevelReader$getMaxLocalRawBrightness.invoke(world, aboveBlockPos) >= 9 && (float) RandomUtils.generateRandomFloat(0, 1) < growSpeed) { + if ((int) Reflections.method$LevelReader$getMaxLocalRawBrightness.invoke(world, aboveBlockPos) >= 9 && RandomUtils.generateRandomFloat(0, 1) < growSpeed) { increaseStage(world, blockPos, blockState, args[3]); } } @@ -175,8 +172,7 @@ public class SaplingBlockBehavior extends BushBlockBehavior { String feature = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("feature"), "warning.config.block.behavior.sapling.missing_feature"); Property stageProperty = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("stage"), "warning.config.block.behavior.sapling.missing_stage"); double boneMealSuccessChance = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("bone-meal-success-chance", 0.45), "bone-meal-success-chance"); - Tuple, Set, Set> tuple = readTagsAndState(arguments, false); - return new SaplingBlockBehavior(block, Key.of(feature), stageProperty, tuple.left(), tuple.mid(), tuple.right(), boneMealSuccessChance, + return new SaplingBlockBehavior(block, Key.of(feature), stageProperty, boneMealSuccessChance, ResourceConfigUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1.0 / 7.0), "grow-speed")); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/VerticalCropBlockBehavior.java similarity index 54% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/VerticalCropBlockBehavior.java index 7606c343c..039010453 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/VerticalCropBlockBehavior.java @@ -15,37 +15,32 @@ import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; -import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldEvents; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.shared.block.BlockBehavior; -import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.Callable; -public class SugarCaneBlockBehavior extends BushBlockBehavior { +public class VerticalCropBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); - private static final List WATER = List.of(Reflections.instance$Fluids$WATER, Reflections.instance$Fluids$FLOWING_WATER); - private static final List LAVA = List.of(Reflections.instance$Fluids$LAVA, Reflections.instance$Fluids$FLOWING_LAVA); - private static final List HORIZON_DIRECTIONS = List.of(Reflections.instance$Direction$NORTH, Reflections.instance$Direction$EAST, Reflections.instance$Direction$SOUTH, Reflections.instance$Direction$WEST); private final int maxHeight; - private final boolean nearWater; - private final boolean nearLava; private final IntegerProperty ageProperty; private final float growSpeed; + private final boolean direction; - public SugarCaneBlockBehavior(CustomBlock customBlock, List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn, Property ageProperty, - int maxHeight, boolean nearWater, boolean nearLava, float growSpeed) { - super(customBlock, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); - this.nearWater = nearWater; - this.nearLava = nearLava; + public VerticalCropBlockBehavior(CustomBlock customBlock, Property ageProperty, + int maxHeight, float growSpeed, boolean direction) { + super(customBlock); this.maxHeight = maxHeight; this.ageProperty = (IntegerProperty) ageProperty; this.growSpeed = growSpeed; + this.direction = direction; } @Override @@ -53,11 +48,11 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior { Object blockState = args[0]; Object level = args[1]; Object blockPos = args[2]; - if (!canSurvive(thisBlock, blockState, level, blockPos)) { + if (!canSurvive(thisBlock, args, () -> true)) { int stateId = BlockStateUtils.blockStateToId(blockState); ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(stateId); if (currentState != null && !currentState.isEmpty()) { - // break the sugar cane + // break the crop FastNMS.INSTANCE.method$Level$removeBlock(level, blockPos, false); net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(LocationUtils.fromBlockPos(blockPos))); @@ -85,7 +80,7 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior { } Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 1); // return state, do not call super. - return superMethod.call(); + return args[0]; } @Override @@ -94,15 +89,15 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior { Object level = args[1]; Object blockPos = args[2]; // above block is empty - if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.above(blockPos)) == Reflections.instance$Blocks$AIR$defaultState) { + if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, (this.direction ? LocationUtils.above(blockPos) : LocationUtils.below(blockPos))) == Reflections.instance$Blocks$AIR$defaultState) { int currentHeight = 1; BlockPos currentPos = LocationUtils.fromBlockPos(blockPos); ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)); if (currentState != null && !currentState.isEmpty()) { while (true) { - Object belowPos = LocationUtils.toBlockPos(currentPos.x(), currentPos.y() - currentHeight, currentPos.z()); - Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, belowPos); - ImmutableBlockState belowImmutableState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(belowState)); + Object nextPos = LocationUtils.toBlockPos(currentPos.x(), this.direction ? currentPos.y() - currentHeight : currentPos.y() + currentHeight, currentPos.z()); + Object nextState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, nextPos); + ImmutableBlockState belowImmutableState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(nextState)); if (belowImmutableState != null && !belowImmutableState.isEmpty() && belowImmutableState.owner() == currentState.owner()) { currentHeight++; } else { @@ -116,11 +111,11 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior { if (currentHeight < this.maxHeight) { int age = currentState.get(ageProperty); if (age >= this.ageProperty.max || RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) { - Object abovePos = LocationUtils.above(blockPos); + Object nextPos = this.direction ? LocationUtils.above(blockPos) : LocationUtils.below(blockPos); if (VersionHelper.isOrAbove1_21_5()) { - Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, super.customBlock.defaultState().customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); } else { - Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, super.customBlock.defaultState().customBlockState().handle()); + Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().handle()); } FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags()); } else if (RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) { @@ -130,81 +125,16 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior { } } - @Override - protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { - int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); - int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); - int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); - Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z); - Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); - int id = BlockStateUtils.blockStateToId(belowState); - // 如果下方是同种方块 - if (!BlockStateUtils.isVanillaBlock(id)) { - ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(id); - if (immutableBlockState.owner().value() == super.customBlock) { - return true; - } - } - if (!super.mayPlaceOn(belowState, world, belowPos)) { - return false; - } - // 如果不需要依靠流体 - if (!this.nearWater && !this.nearLava) { - return true; - } - // 需要流体 - if (this.nearWater) { - if (hasNearbyLiquid(world, belowPos, true)) { - return true; - } - } - if (this.nearLava) { - if (hasNearbyLiquid(world, belowPos, false)) { - return true; - } - } - return false; - } - - private boolean hasNearbyLiquid(Object world, Object blockPos, boolean waterOrLava) throws ReflectiveOperationException { - for (Object direction : HORIZON_DIRECTIONS) { - Object relativePos = Reflections.method$BlockPos$relative.invoke(blockPos, direction); - if (waterOrLava) { - // water - Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, relativePos); - if (Reflections.method$BlockStateBase$getBlock.invoke(blockState) == Reflections.instance$Blocks$ICE) { - return true; - } - Object fluidState = Reflections.method$Level$getFluidState.invoke(world, relativePos); - Object fluidType = Reflections.method$FluidState$getType.invoke(fluidState); - if (WATER.contains(fluidType)) { - return true; - } - } else { - // lava - Object fluidState = Reflections.method$Level$getFluidState.invoke(world, relativePos); - Object fluidType = Reflections.method$FluidState$getType.invoke(fluidState); - if (LAVA.contains(fluidType)) { - return true; - } - } - } - return false; - } - public static class Factory implements BlockBehaviorFactory { @SuppressWarnings("unchecked") @Override public BlockBehavior create(CustomBlock block, Map arguments) { - Tuple, Set, Set> tuple = readTagsAndState(arguments, false); Property ageProperty = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.sugar_cane.missing_age"); int maxHeight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-height", 3), "max-height"); - List nearbyLiquids = MiscUtils.getAsStringList(arguments.getOrDefault("required-adjacent-liquids", List.of())); - boolean nearWater = nearbyLiquids.contains("water"); - boolean nearLava = nearbyLiquids.contains("lava"); - return new SugarCaneBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right(), ageProperty, maxHeight, nearWater, nearLava, - ResourceConfigUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1), "grow-speed")); + boolean direction = arguments.getOrDefault("direction", "up").toString().equalsIgnoreCase("up"); + return new VerticalCropBlockBehavior(block, ageProperty, maxHeight, + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1), "grow-speed"), direction); } } } 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 bb3d7feec..c7fa13c15 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 @@ -37,10 +37,14 @@ public class LocationUtils { return toBlockPos(pos.x(), pos.y(), pos.z()); } - public static Object above(Object blockPos) throws ReflectiveOperationException { + public static Object above(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) { + return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) - 1, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); + } + public static Object toBlockPos(int x, int y, int z) { return FastNMS.INSTANCE.constructor$BlockPos(x, y, z); } @@ -49,7 +53,7 @@ public class LocationUtils { return new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); } - public static BlockPos fromBlockPos(Object pos) throws ReflectiveOperationException { + public static BlockPos fromBlockPos(Object pos) { return new BlockPos( FastNMS.INSTANCE.field$Vec3i$x(pos), FastNMS.INSTANCE.field$Vec3i$y(pos), diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java index aa39e69c3..c91a7bacf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.block; import com.google.common.collect.ImmutableMap; import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior; import net.momirealms.craftengine.core.block.behavior.BlockBehaviors; +import net.momirealms.craftengine.core.block.behavior.UnsafeCompositeBlockBehavior; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.loot.LootTable; @@ -14,6 +15,7 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigExce import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.shared.block.BlockBehavior; +import net.momirealms.craftengine.shared.block.EmptyBlockBehavior; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; import org.jetbrains.annotations.NotNull; @@ -42,7 +44,7 @@ public abstract class AbstractCustomBlock implements CustomBlock { @NotNull Map variantMapper, @NotNull BlockSettings settings, @NotNull Map>> events, - @Nullable Map behavior, + @NotNull List> behaviorConfig, @Nullable LootTable lootTable ) { holder.bindValue(this); @@ -53,7 +55,17 @@ public abstract class AbstractCustomBlock implements CustomBlock { this.events = events; this.variantProvider = new BlockStateVariantProvider(holder, ImmutableBlockState::new, properties); this.defaultState = this.variantProvider.getDefaultState(); - this.behavior = BlockBehaviors.fromMap(this, behavior); + if (behaviorConfig.isEmpty()) { + this.behavior = new EmptyBlockBehavior(); + } else if (behaviorConfig.size() == 1) { + this.behavior = BlockBehaviors.fromMap(this, behaviorConfig.get(0)); + } else { + List behaviors = new ArrayList<>(); + for (Map config : behaviorConfig) { + behaviors.add((AbstractBlockBehavior) BlockBehaviors.fromMap(this, config)); + } + this.behavior = new UnsafeCompositeBlockBehavior(this, behaviors); + } List> placements = new ArrayList<>(4); for (Map.Entry> propertyEntry : this.properties.entrySet()) { placements.add(Property.createStateForPlacement(propertyEntry.getKey(), propertyEntry.getValue())); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java index 5b0585f86..0ff330d4b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java @@ -43,7 +43,7 @@ public interface CustomBlock { Builder appearances(Map appearances); - Builder behavior(Map behavior); + Builder behavior(List> behavior); Builder lootTable(LootTable lootTable); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java index 1e24acdc0..d6e65cf34 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.block; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; +import java.util.List; import java.util.Map; public class EmptyBlock extends AbstractCustomBlock { @@ -10,7 +11,7 @@ public class EmptyBlock extends AbstractCustomBlock { public static ImmutableBlockState STATE; public EmptyBlock(Key id, Holder.Reference holder) { - super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), Map.of(), null, null); + super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), Map.of(), List.of(), null); INSTANCE = this; STATE = defaultState(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java index 29ee6e7a0..70bfa59b9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java @@ -6,13 +6,14 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.sparrow.nbt.CompoundTag; import java.util.HashMap; +import java.util.List; import java.util.Map; public class InactiveCustomBlock extends AbstractCustomBlock { private final Map cachedData = new HashMap<>(); public InactiveCustomBlock(Key id, Holder.Reference holder) { - super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), Map.of(), null, null); + super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), Map.of(), List.of(), null); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/UnsafeCompositeBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/UnsafeCompositeBlockBehavior.java index 21bb9d76d..58f844744 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/UnsafeCompositeBlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/UnsafeCompositeBlockBehavior.java @@ -51,10 +51,14 @@ public class UnsafeCompositeBlockBehavior extends AbstractBlockBehavior { @Override public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object previous = args[0]; for (AbstractBlockBehavior behavior : this.behaviors) { - args[0] = behavior.updateShape(thisBlock, args, superMethod); + Object processed = behavior.updateShape(thisBlock, args, superMethod); + if (processed != previous) { + return processed; + } } - return args[0]; + return previous; } @Override @@ -73,18 +77,26 @@ public class UnsafeCompositeBlockBehavior extends AbstractBlockBehavior { @Override public Object rotate(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object previous = args[0]; for (AbstractBlockBehavior behavior : this.behaviors) { - args[0] = behavior.rotate(thisBlock, args, superMethod); + Object processed = behavior.rotate(thisBlock, args, superMethod); + if (processed != previous) { + return processed; + } } - return args[0]; + return previous; } @Override public Object mirror(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object previous = args[0]; for (AbstractBlockBehavior behavior : this.behaviors) { - args[0] = behavior.mirror(thisBlock, args, superMethod); + Object processed = behavior.mirror(thisBlock, args, superMethod); + if (processed != previous) { + return processed; + } } - return args[0]; + return previous; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index d0ded6b10..22ed3e10b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -346,6 +346,10 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/netherite_anvil_top.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/solid_gunpowder_block.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/gunpowder_block.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/copper_coil.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/copper_coil_side.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/copper_coil_on.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/copper_coil_on_side.png"); // items plugin.saveResource("resources/default/configuration/items.yml"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_rod.png"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java index 3e9c4ba60..d3c9b178b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java @@ -23,6 +23,17 @@ public class MiscUtils { throw new IllegalArgumentException("Expected Map, got: " + (obj == null ? null : obj.getClass().getSimpleName())); } + @SuppressWarnings("unchecked") + public static List> getAsMapList(Object obj) { + if (obj == null) return List.of(); + if (obj instanceof List list) { + return (List>) list; + } else if (obj instanceof Map) { + return List.of((Map) obj); + } + throw new IllegalArgumentException("Expected MapList/Map, got: " + obj.getClass().getSimpleName()); + } + public static List getAsStringList(Object o) { List list = new ArrayList<>(); if (o instanceof List) { diff --git a/gradle.properties b/gradle.properties index ef3d4750e..b9f802a04 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.54.9 +project_version=0.0.54.10 config_version=34 lang_version=14 project_group=net.momirealms diff --git a/shared/src/main/java/net/momirealms/craftengine/shared/block/BlockBehavior.java b/shared/src/main/java/net/momirealms/craftengine/shared/block/BlockBehavior.java index 5c4987cab..a39948fb9 100644 --- a/shared/src/main/java/net/momirealms/craftengine/shared/block/BlockBehavior.java +++ b/shared/src/main/java/net/momirealms/craftengine/shared/block/BlockBehavior.java @@ -22,7 +22,7 @@ public abstract class BlockBehavior { } public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { - return superMethod.call(); + return args[0]; } public void neighborChanged(Object thisBlock, Object[] args, Callable superMethod) throws Exception {