9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-26 02:19:23 +00:00

Added container and analog

This commit is contained in:
Arubik
2025-08-26 11:32:40 -05:00
parent cd2f13a01a
commit bb79b621c3
4 changed files with 198 additions and 65 deletions

View File

@@ -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<Object> 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<Object> superMethod) throws Exception {
Object previous = args[0];
@@ -282,4 +318,4 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior {
behavior.spawnAfterBreak(thisBlock, args, superMethod);
}
}
}
}

View File

@@ -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<BlockBehavior> 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<BlockBehavior> 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<BlockBehavior> 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 {
}
}
}
}
}

View File

@@ -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"
)
);
}
}

View File

@@ -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();
}
}