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 4542f71d2..81eb1ad79 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 @@ -74,6 +74,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) { @@ -264,6 +276,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]; @@ -282,4 +318,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 83b94ad9f..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; @@ -34,7 +35,6 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; import java.util.concurrent.Callable; -import java.util.function.BiConsumer; import java.util.function.Function; public final class BlockGenerator { @@ -63,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")) @@ -90,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)) @@ -106,54 +116,21 @@ public final class BlockGenerator { .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$randomTick)) .intercept(MethodDelegation.to(RandomTickInterceptor.INSTANCE)) // onPlace - .method(ElementMatchers.takesArguments(5) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(3, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(4, boolean.class)) - .and(ElementMatchers.named("onPlace").or(ElementMatchers.named("a"))) - ) + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onPlace)) .intercept(MethodDelegation.to(OnPlaceInterceptor.INSTANCE)) // onBrokenAfterFall - .method(ElementMatchers.takesArguments(3) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$FallingBlockEntity)) - ) + .method(ElementMatchers.is(CoreReflections.method$Fallable$onBrokenAfterFall)) .intercept(MethodDelegation.to(OnBrokenAfterFallInterceptor.INSTANCE)) // onLand - .method(ElementMatchers.takesArguments(5) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(3, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(4, CoreReflections.clazz$FallingBlockEntity)) - ) + .method(ElementMatchers.is(CoreReflections.method$Fallable$onLand)) .intercept(MethodDelegation.to(OnLandInterceptor.INSTANCE)) // canSurvive - .method(ElementMatchers.takesArguments(3) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$LevelReader)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockPos)) + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$canSurvive) ) .intercept(MethodDelegation.to(CanSurviveInterceptor.INSTANCE)) // updateShape - .method(ElementMatchers.returns(CoreReflections.clazz$BlockState) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - // LevelReader 1.21.3+ // 1.20-1.12.2 - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$LevelReader).or(ElementMatchers.takesArgument(1, CoreReflections.clazz$Direction))) - .and(ElementMatchers.named("updateShape").or(ElementMatchers.named("a")))) + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$updateShape)) .intercept(MethodDelegation.to(UpdateShapeInterceptor.INSTANCE)) - // onExplosionHit 1.21+ - .method(ElementMatchers.returns(void.class) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(1, VersionHelper.isOrAbove1_21_2() ? CoreReflections.clazz$ServerLevel : CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(3, CoreReflections.clazz$Explosion)) - .and(ElementMatchers.takesArgument(4, BiConsumer.class)) - ) - .intercept(MethodDelegation.to(OnExplosionHitInterceptor.INSTANCE)) // neighborChanged .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$neighborChanged)) .intercept(MethodDelegation.to(NeighborChangedInterceptor.INSTANCE)) @@ -184,15 +161,21 @@ public final class BlockGenerator { // spawnAfterBreak .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$spawnAfterBreak)) .intercept(MethodDelegation.to(SpawnAfterBreakInterceptor.INSTANCE)); + // 1.21.5+ if (CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval != null) { - builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval)) + builder = builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval)) .intercept(MethodDelegation.to(AffectNeighborsAfterRemovalInterceptor.INSTANCE)); } + // 1.20-1.21.4 if (CoreReflections.method$BlockBehaviour$onRemove != null) { - builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onRemove)) + builder = builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onRemove)) .intercept(MethodDelegation.to(OnRemoveInterceptor.INSTANCE)); } - + // 1.21+ + if (CoreReflections.method$BlockBehaviour$onExplosionHit != null) { + builder = builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onExplosionHit)) + .intercept(MethodDelegation.to(OnExplosionHitInterceptor.INSTANCE)); + } Class clazz$CraftEngineBlock = builder.make().load(BlockGenerator.class.getClassLoader()).getLoaded(); constructor$CraftEngineBlock = MethodHandles.publicLookup().in(clazz$CraftEngineBlock) .findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$BlockBehaviour$Properties)) @@ -469,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(); @@ -672,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 bff6a601b..4126b0ac6 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 @@ -1181,6 +1181,17 @@ public final class CoreReflections { ) ); + public static final Class clazz$ServerLevel = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "server.level.WorldServer", + "server.level.ServerLevel" + ) + ); + + public static final Class clazz$Explosion = requireNonNull( + ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("world.level.Explosion")) + ); + public static final Constructor constructor$StateDefinition$Builder = requireNonNull( ReflectionUtils.getTheOnlyConstructor(clazz$StateDefinition$Builder) ); @@ -1451,6 +1462,18 @@ public final class CoreReflections { ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 0) ); + public static final Field field$BlockBehaviour$friction = requireNonNull( + ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 1) + ); + + public static final Field field$BlockBehaviour$speedFactor = requireNonNull( + ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 2) + ); + + public static final Field field$BlockBehaviour$jumpFactor = requireNonNull( + ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 3) + ); + public static final Field field$BlockBehaviour$soundType = requireNonNull( ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, clazz$SoundType, 0) ); @@ -1547,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) ); @@ -1651,6 +1682,15 @@ public final class CoreReflections { ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$BlockState, clazz$BlockState, clazz$Direction, clazz$BlockState, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockPos) ); + public static final Method method$BlockBehaviour$canSurvive = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, boolean.class, clazz$BlockState, clazz$LevelReader, clazz$BlockPos) + ); + + public static final Method method$BlockBehaviour$onExplosionHit = MiscUtils.requireNonNullIf( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, VersionHelper.isOrAbove1_21_2() ? clazz$ServerLevel : clazz$Level, clazz$BlockPos, clazz$Explosion, BiConsumer.class), + VersionHelper.isOrAbove1_21() + ); + public static final Class clazz$Fallable = requireNonNull( ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("world.level.block.Fallable")) ); @@ -1673,6 +1713,14 @@ public final class CoreReflections { ) ); + public static final Method method$Fallable$onLand = requireNonNull( + ReflectionUtils.getMethod(clazz$Fallable, void.class, clazz$Level, clazz$BlockPos, clazz$BlockState, clazz$BlockState, clazz$FallingBlockEntity) + ); + + public static final Method method$Fallable$onBrokenAfterFall = requireNonNull( + ReflectionUtils.getMethod(clazz$Fallable, void.class, clazz$Level, clazz$BlockPos, clazz$FallingBlockEntity) + ); + public static final Method method$FallingBlockEntity$fall = requireNonNull( ReflectionUtils.getStaticMethod(clazz$FallingBlockEntity, clazz$FallingBlockEntity, clazz$Level, clazz$BlockPos, clazz$BlockState) ); @@ -2274,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) ); @@ -2957,13 +3023,6 @@ public final class CoreReflections { ReflectionUtils.getInstanceDeclaredField(clazz$ServerPlayer, clazz$ServerGamePacketListenerImpl, 0) ); - public static final Class clazz$ServerLevel = requireNonNull( - BukkitReflectionUtils.findReobfOrMojmapClass( - "server.level.WorldServer", - "server.level.ServerLevel" - ) - ); - public static final Method method$ServerLevel$getNoiseBiome = requireNonNull( ReflectionUtils.getMethod(clazz$ServerLevel, clazz$Holder, int.class, int.class, int.class) ); @@ -3446,6 +3505,11 @@ public final class CoreReflections { ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"randomTick", "b"}, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, clazz$RandomSource) ); + public static final Method method$BlockBehaviour$onPlace = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"onPlace", VersionHelper.isOrAbove1_21_5() ? "a" : "b"}, + clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$BlockState, boolean.class) + ); + public static final Class clazz$InsideBlockEffectApplier = BukkitReflectionUtils.findReobfOrMojmapClass( "world.entity.InsideBlockEffectApplier", "world.entity.InsideBlockEffectApplier" @@ -3603,12 +3667,6 @@ public final class CoreReflections { ) ); - public static final Class clazz$Explosion = requireNonNull( - ReflectionUtils.getClazz( - BukkitReflectionUtils.assembleMCClass("world.level.Explosion") - ) - ); - // 1.20.5+ public static final Field field$ItemStack$CODEC = ReflectionUtils.getDeclaredField(clazz$ItemStack, "CODEC", "b"); @@ -3993,18 +4051,15 @@ public final class CoreReflections { ReflectionUtils.getStaticMethod(clazz$ArmorTrim, Optional.class, clazz$RegistryAccess, clazz$ItemStack); public static final Method method$BlockBehaviour$spawnAfterBreak = requireNonNull( - ReflectionUtils.getDeclaredMethod( - clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, clazz$ItemStack, boolean.class - ) + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, clazz$ItemStack, boolean.class) ); // 1.20~1.21.4 public static final Method method$BlockBehaviour$onRemove = MiscUtils.requireNonNullIf( - ReflectionUtils.getDeclaredMethod( - clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$BlockState, boolean.class - ), + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"a", "onRemove"}, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$BlockState, boolean.class), !VersionHelper.isOrAbove1_21_5() ); + public static final Object instance$CollisionContext$empty; static { @@ -4148,4 +4203,4 @@ public final class CoreReflections { "world.level.storage.loot.entries.LootPoolEntryType" ) ); -} +} \ No newline at end of file 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 4af326d66..e9bcb1550 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 @@ -85,6 +85,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; @@ -186,4 +201,4 @@ public abstract class BlockBehavior { } public abstract CustomBlock block(); -} +} \ No newline at end of file