From 5b5145199abdd375e9815253fea6bd5bf05a4850 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 28 Mar 2025 10:25:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(bukkit):=20=E6=B7=BB=E5=8A=A0=E4=BD=9C?= =?UTF-8?q?=E7=89=A9=E6=96=B9=E5=9D=97=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/behavior/BukkitBlockBehaviors.java | 2 + .../block/behavior/CropBlockBehavior.java | 120 ++++++++++++++++++ .../craftengine/bukkit/util/Reflections.java | 32 +++++ .../craftengine/core/util/RandomUtils.java | 4 + 4 files changed, 158 insertions(+) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java index 046911410..6d8de9243 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 @@ -15,6 +15,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { 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 CROP_BLOCK = Key.from("craftengine:crop_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -28,5 +29,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(WATERLOGGED_BLOCK, WaterLoggedBlockBehavior.FACTORY); register(CONCRETE_POWDER_BLOCK, ConcretePowderBlockBehavior.FACTORY); register(SUGARCANE_BLOCK, SugarCaneBlockBehavior.FACTORY); + register(CROP_BLOCK, CropBlockBehavior.FACTORY); } } 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 new file mode 100644 index 000000000..4cc1a8fea --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java @@ -0,0 +1,120 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +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.IntegerProperty; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.util.Tuple; +import net.momirealms.craftengine.shared.block.BlockBehavior; + +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 static final Factory FACTORY = new Factory(); + private final IntegerProperty ageProperty; + private final float growSpeed; + private final int minGrowLight; + + public CropBlockBehavior(List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn, + Property ageProperty, float growSpeed, int minGrowLight) { + super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); + this.ageProperty = (IntegerProperty) ageProperty; + this.growSpeed = growSpeed; + this.minGrowLight = minGrowLight; + } + + public final boolean isMaxAge(Object state) { + return this.getAge(state) >= this.ageProperty.max; + } + + public static ImmutableBlockState getCEBlockState(Object nmsState) { + return BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(nmsState)); + } + + public final int getAge(Object state) { + return getCEBlockState(state).get(ageProperty); + } + + public Object getStateForAge(Object state, int age) { + ImmutableBlockState afterState = getCEBlockState(state).owner().value().defaultState().with(ageProperty, age); + return afterState.customBlockState().handle(); + } + + public void growCrops(Object level, Object pos, Object state) throws InvocationTargetException, IllegalAccessException { + int i = this.getAge(state) + RandomUtils.generateRandomInt(2, 5); + int maxAge = this.ageProperty.max; + if (i > maxAge) { + i = maxAge; + } + Reflections.method$ServerLevel$sendBlockUpdated.invoke(level, pos, state, getStateForAge(state, i), 3); + } + + private static int getRawBrightness(Object level, Object pos) throws InvocationTargetException, IllegalAccessException { + return (int) Reflections.method$BlockAndTintGetter$getRawBrightness.invoke(level, pos, 0); + } + + private boolean hasSufficientLight(Object level, Object pos) throws InvocationTargetException, IllegalAccessException { + return getRawBrightness(level, pos) >= minGrowLight - 1; + } + + @Override + public void randomTick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object state = args[0]; + Object level = args[1]; + Object pos = args[2]; + if (getRawBrightness(level, pos) >= minGrowLight) { + int age = this.getAge(state); + if (age < this.ageProperty.max && RandomUtils.generateRandomFloat(0, 1) >= this.growSpeed) { + Reflections.method$ServerLevel$sendBlockUpdated.invoke(level, pos, state, getStateForAge(state, age + 1), 3); + } + } + } + + @Override + protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { + return hasSufficientLight(world, blockPos) && super.canSurvive(thisBlock, state, world, blockPos); + } + + @Override + public boolean isBoneMealSuccess(Object thisBlock, Object[] args) { + return true; + } + + @Override + public boolean isValidBoneMealTarget(Object thisBlock, Object[] args) { + Object state = args[2]; + return !this.isMaxAge(state); + } + + @Override + public void performBoneMeal(Object thisBlock, Object[] args) throws Exception { + this.growCrops(args[0], args[2], args[3]); + } + + public static class Factory implements BlockBehaviorFactory { + + @SuppressWarnings("unchecked") + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + Tuple, Set, Set> tuple = readTagsAndState(arguments); + Property ageProperty = (Property) block.getProperty("age"); + if (ageProperty == null) { + throw new IllegalArgumentException("age property not set for crop"); + } + // 存活条件是最小生长亮度-1 + int minGrowLight = MiscUtils.getAsInt(arguments.getOrDefault("min-grow-light", 9)); + float growSpeed = MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1)); + return new CropBlockBehavior(tuple.left(), tuple.mid(), tuple.right(), ageProperty, growSpeed, minGrowLight); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index bcd33e054..d859d3bf8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -5739,4 +5739,36 @@ public class Reflections { clazz$CraftEventFactory, boolean.class, clazz$Level, clazz$BlockPos, clazz$BlockState ) ); + + public static final Class clazz$BlockAndTintGetter = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.level.BlockAndTintGetter"), + BukkitReflectionUtils.assembleMCClass("world.level.IBlockLightAccess") + ) + ); + + public static final Method method$BlockAndTintGetter$getRawBrightness = requireNonNull( + ReflectionUtils.getMethod( + clazz$BlockAndTintGetter, int.class, clazz$BlockPos, int.class + ) + ); + + public static final Field field$Level$random = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$Level, clazz$RandomSource, 0 + ) + ); + + public static final Class clazz$Mth = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("util.Mth"), + BukkitReflectionUtils.assembleMCClass("util.MathHelper") + ) + ); + + public static final Method method$nextInt = requireNonNull( + ReflectionUtils.getMethod( + clazz$Mth, int.class, clazz$RandomSource, int.class, int.class + ) + ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java index 38d5f78dd..34c0b03e7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java @@ -26,6 +26,10 @@ public class RandomUtils { return min + (max - min) * getInstance().random.nextFloat(); } + public static int generateRandomInt(int min, int max) { + return min >= max ? min : getInstance().random.nextInt(max - min + 1) + min; + } + public static boolean generateRandomBoolean() { return getInstance().random.nextBoolean(); }