diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java index 7ffbfcd02..81f40cbd8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java @@ -92,6 +92,18 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { return previous; } + + @Override + public Object getContainer(Object thisBlock, Object[] args) throws Exception { + for (AbstractBlockBehavior behavior : this.behaviors) { + Object container = behavior.getContainer(thisBlock, args); + if (container != null) { + return container; + } + } + return null; + } + @Override public void tick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { for (AbstractBlockBehavior behavior : this.behaviors) { @@ -282,6 +294,30 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { return false; } + @Override + public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + for (AbstractBlockBehavior behavior : this.behaviors) { + if (behavior.hasAnalogOutputSignal(thisBlock, args)) { + return true; + } + } + return false; + } + + @Override + public int getAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + int signal = 0; + int count = 0; + for (AbstractBlockBehavior behavior : this.behaviors) { + int s = behavior.getAnalogOutputSignal(thisBlock, args); + if (s != 0) { + signal += s; + count++; + } + } + return count == 0 ? 0 : signal / count; + } + @Override public Object playerWillDestroy(Object thisBlock, Object[] args, Callable superMethod) throws Exception { Object previous = args[0]; @@ -300,4 +336,4 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { behavior.spawnAfterBreak(thisBlock, args, superMethod); } } -} +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java index e05bada17..5b6b21096 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java @@ -15,6 +15,7 @@ import net.bytebuddy.implementation.bind.annotation.This; import net.bytebuddy.matcher.ElementMatchers; import net.momirealms.craftengine.bukkit.block.BukkitBlockShape; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.injector.BlockGenerator.*; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.util.NoteBlockChainUpdateUtils; @@ -62,6 +63,7 @@ public final class BlockGenerator { .implement(CoreReflections.clazz$Fallable) .implement(CoreReflections.clazz$BonemealableBlock) .implement(CoreReflections.clazz$SimpleWaterloggedBlock) + .implement(CoreReflections.clazz$WorldlyContainerHolder) // internal interfaces .method(ElementMatchers.named("behaviorDelegate")) .intercept(FieldAccessor.ofField("behaviorHolder")) @@ -89,12 +91,21 @@ public final class BlockGenerator { // rotate .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$rotate)) .intercept(MethodDelegation.to(RotateInterceptor.INSTANCE)) + // hasAnalogOutputSignal + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$hasAnalogOutputSignal)) + .intercept(MethodDelegation.to(HasAnalogOutputSignalInterceptor.INSTANCE)) + // getAnalogOutputSignal + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$getAnalogOutputSignal)) + .intercept(MethodDelegation.to(GetAnalogOutputSignalInterceptor.INSTANCE)) // tick .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$tick)) .intercept(MethodDelegation.to(TickInterceptor.INSTANCE)) // isValidBoneMealTarget .method(ElementMatchers.is(CoreReflections.method$BonemealableBlock$isValidBonemealTarget)) .intercept(MethodDelegation.to(IsValidBoneMealTargetInterceptor.INSTANCE)) + // getContainer + .method(ElementMatchers.is(CoreReflections.method$WorldlyContainerHolder$getContainer)) + .intercept(MethodDelegation.to(GetContainerInterceptor.INSTANCE)) // isBoneMealSuccess .method(ElementMatchers.is(CoreReflections.method$BonemealableBlock$isBonemealSuccess)) .intercept(MethodDelegation.to(IsBoneMealSuccessInterceptor.INSTANCE)) @@ -441,6 +452,50 @@ public final class BlockGenerator { } } + public static class GetContainerInterceptor { + public static final GetContainerInterceptor INSTANCE = new GetContainerInterceptor(); + + @RuntimeType + public Object intercept(@This Object thisObj, @AllArguments Object[] args) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + return holder.value().getContainer(thisObj, args); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run getContainer", e); + return null; + } + } + } + + public static class HasAnalogOutputSignalInterceptor { + public static final HasAnalogOutputSignalInterceptor INSTANCE = new HasAnalogOutputSignalInterceptor(); + + @RuntimeType + public boolean intercept(@This Object thisObj, @AllArguments Object[] args) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + return holder.value().hasAnalogOutputSignal(thisObj, args); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run hasAnalogOutputSignal", e); + return false; + } + } + } + + public static class GetAnalogOutputSignalInterceptor { + public static final GetAnalogOutputSignalInterceptor INSTANCE = new GetAnalogOutputSignalInterceptor(); + + @RuntimeType + public int intercept(@This Object thisObj, @AllArguments Object[] args) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + return holder.value().getAnalogOutputSignal(thisObj, args); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run getAnalogOutputSignal", e); + return 0; + } + } + } public static class PerformBoneMealInterceptor { public static final PerformBoneMealInterceptor INSTANCE = new PerformBoneMealInterceptor(); @@ -644,4 +699,4 @@ public final class BlockGenerator { } } } -} +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index 085c81690..247226581 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -1570,6 +1570,14 @@ public final class CoreReflections { ) ); + public static final Method method$BlockBehaviour$hasAnalogOutputSignal = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, boolean.class, new String[]{"hasAnalogOutputSignal", "c"}, clazz$BlockState) + ); + + public static final Method method$BlockBehaviour$getAnalogOutputSignal = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, int.class, new String[]{"getAnalogOutputSignal", "a"}, clazz$BlockState, clazz$Level, clazz$BlockPos) + ); + public static final Method method$Entity$level = requireNonNull( ReflectionUtils.getMethod(clazz$Entity, clazz$Level) ); @@ -2314,12 +2322,30 @@ public final class CoreReflections { ) ); + public static final Class clazz$WorldlyContainerHolder = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.IInventoryHolder", + "world.WorldlyContainerHolder" + ) + ); + public static final Method method$BonemealableBlock$isValidBonemealTarget = requireNonNull( VersionHelper.isOrAbove1_20_2() ? ReflectionUtils.getInstanceMethod(clazz$BonemealableBlock, boolean.class, clazz$LevelReader, clazz$BlockPos, clazz$BlockState) : ReflectionUtils.getInstanceMethod(clazz$BonemealableBlock, boolean.class, clazz$LevelReader, clazz$BlockPos, clazz$BlockState, boolean.class) ); + public static final Class clazz$WorldlyContainer = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.IWorldInventory", + "world.WorldlyContainer" + ) + ); + + + public static final Method method$WorldlyContainerHolder$getContainer = requireNonNull( + ReflectionUtils.getMethod(clazz$WorldlyContainerHolder, clazz$WorldlyContainer, clazz$BlockState, clazz$LevelAccessor, clazz$BlockPos) + ); public static final Method method$BonemealableBlock$isBonemealSuccess = requireNonNull( ReflectionUtils.getMethod(clazz$BonemealableBlock, boolean.class, clazz$Level, clazz$RandomSource, clazz$BlockPos, clazz$BlockState) ); 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 af354ade7..6ef4b0cf6 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 @@ -95,6 +95,21 @@ public abstract class BlockBehavior { return false; } + //BlockState state + public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + return false; + } + + //BlockState state Level level BlockPos pos + public int getAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + return 0; + } + + // BlockState state, LevelReader world, BlockPos pos + public Object getContainer(Object thisBlock, Object[] args) throws Exception { + return null; + } + // Level level, RandomSource random, BlockPos pos, BlockState state public boolean isBoneMealSuccess(Object thisBlock, Object[] args) throws Exception { return false; @@ -196,4 +211,4 @@ public abstract class BlockBehavior { } public abstract CustomBlock block(); -} +} \ No newline at end of file