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 1cc35512d..53b1d831e 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 @@ -320,7 +320,7 @@ public class BukkitBlockManager extends AbstractBlockManager { for (Object block : (Iterable) MBuiltInRegistries.BLOCK) { Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(block); if (affectedBlockSounds.contains(soundType)) { - Object state = getOnlyBlockState(block); + Object state = FastNMS.INSTANCE.method$Block$defaultState(block); if (BlockStateUtils.isVanillaBlock(state)) { affectedBlocks.add(block); } @@ -792,7 +792,7 @@ public class BukkitBlockManager extends AbstractBlockManager { CoreReflections.method$Holder$Reference$bindValue.invoke(blockHolder, newRealBlock); CoreReflections.field$Holder$Reference$tags.set(blockHolder, Set.of()); - newBlockState = getOnlyBlockState(newRealBlock); + newBlockState = FastNMS.INSTANCE.method$Block$defaultState(newRealBlock); CoreReflections.method$IdMapper$add.invoke(CoreReflections.instance$Block$BLOCK_STATE_REGISTRY, newBlockState); if (isNoteBlock) { @@ -838,13 +838,6 @@ public class BukkitBlockManager extends AbstractBlockManager { return blockProperties; } - private Object getOnlyBlockState(Object newBlock) throws IllegalAccessException { - Object stateDefinition = CoreReflections.field$Block$StateDefinition.get(newBlock); - @SuppressWarnings("unchecked") - ImmutableList states = (ImmutableList) CoreReflections.field$StateDefinition$states.get(stateDefinition); - return states.get(0); - } - @SuppressWarnings("unchecked") private void deceiveBukkit() { try { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java index d00647089..51450d06f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java @@ -108,6 +108,7 @@ public class BukkitCraftEngine extends CraftEngine { if (super.blockManager != null) return; try { BlockGenerator.init(); + BlockStateGenerator.init(); super.blockManager = new BukkitBlockManager(this); } catch (Exception e) { throw new InjectionException("Error injecting blocks", e); 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 8dcc4beee..466e24c13 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 @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.plugin.injector; +import com.google.common.collect.ImmutableList; import net.bytebuddy.ByteBuddy; import net.bytebuddy.ClassFileVersion; import net.bytebuddy.description.modifier.Visibility; @@ -30,6 +31,7 @@ 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 { private static final BukkitBlockShape STONE_SHAPE = @@ -201,6 +203,12 @@ public final class BlockGenerator { field$CraftEngineBlock$shape.set(newBlockInstance, shapeHolder); field$CraftEngineBlock$isNoteBlock.set(newBlockInstance, replacedBlock.equals(BlockKeys.NOTE_BLOCK)); field$CraftEngineBlock$isTripwire.set(newBlockInstance, replacedBlock.equals(BlockKeys.TRIPWIRE)); + + Object stateDefinitionBuilder = CoreReflections.constructor$StateDefinition$Builder.newInstance(newBlockInstance); + Object stateDefinition = CoreReflections.method$StateDefinition$Builder$create.invoke(stateDefinitionBuilder, + (Function) FastNMS.INSTANCE::method$Block$defaultState, BlockStateGenerator.instance$StateDefinition$Factory); + CoreReflections.field$Block$StateDefinition.set(newBlockInstance, stateDefinition); + CoreReflections.field$Block$defaultBlockState.set(newBlockInstance, ((ImmutableList) CoreReflections.field$StateDefinition$states.get(stateDefinition)).getFirst()); return newBlockInstance; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java new file mode 100644 index 000000000..c51f84cde --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java @@ -0,0 +1,58 @@ +package net.momirealms.craftengine.bukkit.plugin.injector; + +import com.mojang.serialization.MapCodec; +import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.ClassFileVersion; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.matcher.ElementMatchers; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.core.block.CustomBlockState; +import net.momirealms.craftengine.core.util.ReflectionUtils; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public final class BlockStateGenerator { + private static MethodHandle constructor$CraftEngineBlockState; + public static Object instance$StateDefinition$Factory; + + public static void init() throws ReflectiveOperationException { + ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17); + String packageWithName = BlockStateGenerator.class.getName(); + String generatedStateClassName = packageWithName.substring(0, packageWithName.lastIndexOf('.')) + ".CraftEngineBlockState"; + DynamicType.Builder stateBuilder = byteBuddy + .subclass(CoreReflections.clazz$BlockState, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) + .name(generatedStateClassName) + .implement(CustomBlockState.class); + Class clazz$CraftEngineBlock = stateBuilder.make().load(BlockStateGenerator.class.getClassLoader()).getLoaded(); + constructor$CraftEngineBlockState = MethodHandles.publicLookup().in(clazz$CraftEngineBlock) + .findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$Block, Reference2ObjectArrayMap.class, MapCodec.class)) + .asType(MethodType.methodType(CoreReflections.clazz$BlockState, CoreReflections.clazz$Block, Reference2ObjectArrayMap.class, MapCodec.class)); + + String generatedFactoryClassName = packageWithName.substring(0, packageWithName.lastIndexOf('.')) + ".CraftEngineStateFactory"; + DynamicType.Builder factoryBuilder = byteBuddy + .subclass(Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) + .name(generatedFactoryClassName) + .implement(CoreReflections.clazz$StateDefinition$Factory) + .method(ElementMatchers.named("create")) + .intercept(MethodDelegation.to(CreateStateInterceptor.INSTANCE)); + + Class clazz$Factory = factoryBuilder.make().load(BlockStateGenerator.class.getClassLoader()).getLoaded(); + instance$StateDefinition$Factory = ReflectionUtils.getTheOnlyConstructor(clazz$Factory).newInstance(); + } + + public static class CreateStateInterceptor { + public static final CreateStateInterceptor INSTANCE = new CreateStateInterceptor(); + + @RuntimeType + public Object intercept(@AllArguments Object[] args) throws Throwable { + return constructor$CraftEngineBlockState.invoke(args[0], args[1], args[2]); + } + } +} 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 e4e1f3685..647e2152d 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 @@ -22,6 +22,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Function; import static java.util.Objects.requireNonNull; @@ -1152,6 +1153,28 @@ public final class CoreReflections { ) ); + public static final Class clazz$StateDefinition$Builder = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.level.block.state.BlockStateList$a", + "world.level.block.state.StateDefinition$Builder" + ) + ); + + public static final Class clazz$StateDefinition$Factory = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.level.block.state.BlockStateList$b", + "world.level.block.state.StateDefinition$Factory" + ) + ); + + public static final Constructor constructor$StateDefinition$Builder = requireNonNull( + ReflectionUtils.getTheOnlyConstructor(clazz$StateDefinition$Builder) + ); + + public static final Method method$StateDefinition$Builder$create = requireNonNull( + ReflectionUtils.getMethod(clazz$StateDefinition$Builder, clazz$StateDefinition, Function.class, clazz$StateDefinition$Factory) + ); + public static final Field field$Block$StateDefinition = requireNonNull( ReflectionUtils.getDeclaredField(clazz$Block, clazz$StateDefinition, 0) ); @@ -1572,8 +1595,8 @@ public final class CoreReflections { } } - public static final Method method$Block$defaultBlockState = requireNonNull( - ReflectionUtils.getMethod(clazz$Block, clazz$BlockState) + public static final Field field$Block$defaultBlockState = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$Block, clazz$BlockState, 0) ); public static final Method method$Entity$getOnPos = requireNonNull( @@ -3503,4 +3526,5 @@ public final class CoreReflections { throw new RuntimeException(e); } } + } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java index 77a631de0..2c9355fdf 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java @@ -27,14 +27,14 @@ public final class MBlocks { static { try { AIR = getById("air"); - AIR$defaultState = CoreReflections.method$Block$defaultBlockState.invoke(AIR); + AIR$defaultState = FastNMS.INSTANCE.method$Block$defaultState(AIR); FIRE = getById("fire"); SOUL_FIRE = getById("soul_fire"); STONE = getById("stone"); - STONE$defaultState = CoreReflections.method$Block$defaultBlockState.invoke(STONE); + STONE$defaultState = FastNMS.INSTANCE.method$Block$defaultState(STONE); ICE = getById("ice"); SHORT_GRASS = getById(VersionHelper.isOrAbove1_20_3() ? "short_grass" : "grass"); - SHORT_GRASS$defaultState = CoreReflections.method$Block$defaultBlockState.invoke(SHORT_GRASS); + SHORT_GRASS$defaultState = FastNMS.INSTANCE.method$Block$defaultState(SHORT_GRASS); SHULKER_BOX = getById("shulker_box"); COMPOSTER = getById("composter"); } catch (ReflectiveOperationException e) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlockState.java b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlockState.java new file mode 100644 index 000000000..8f46bb74e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlockState.java @@ -0,0 +1,4 @@ +package net.momirealms.craftengine.core.block; + +public interface CustomBlockState { +} diff --git a/gradle.properties b/gradle.properties index 383055ad4..607e7092d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,13 +6,13 @@ project_version=0.0.58.3 config_version=39 lang_version=20 project_group=net.momirealms -latest_supported_version=1.21.7 +latest_supported_version=1.21.6 # Supported languages supported_languages=en,zh_cn,zh_tw,es,tr,de # Dependency settings -paper_version=1.21.5 +paper_version=1.21.6 jetbrains_annotations_version=26.0.2 slf4j_version=2.0.17 log4j_version=2.24.3