From afa01980c38925da7af0c04eca95ea3d2073be6a Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sun, 1 Jun 2025 23:05:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=86=E5=88=86=E6=B3=A8=E5=85=A5=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../worldedit/FastAsyncWorldEditDelegate.java | 4 +- .../bukkit/block/BlockEventListener.java | 2 +- .../bukkit/block/BukkitBlockManager.java | 2 +- .../bukkit/block/BukkitCustomBlock.java | 19 +- .../furniture/BukkitFurnitureManager.java | 4 +- .../item/recipe/RecipeEventListener.java | 12 +- .../bukkit/plugin/BukkitCraftEngine.java | 20 +- .../DebugIsSectionInjectedCommand.java | 4 +- .../plugin/injector/BlockGenerator.java | 4 +- .../plugin/injector/BukkitInjector.java | 714 ------------------ ...lFieldAccessor.java => FieldAccessor.java} | 2 +- .../injector/ProtectedFieldVisitor.java | 56 ++ .../plugin/injector/RecipeInjector.java | 341 +++++++++ .../plugin/injector/WorldStorageInjector.java | 263 +++++++ .../plugin/network/BukkitNetworkManager.java | 41 +- .../plugin/network/PacketConsumers.java | 6 +- .../handler/ProjectilePacketHandler.java | 4 +- .../plugin/user/BukkitServerPlayer.java | 2 +- .../craftengine/bukkit/util/PlayerUtils.java | 19 +- .../bukkit/world/BukkitWorldManager.java | 9 +- .../core/block/StatePredicate.java | 19 - .../craftengine/core/util/VersionHelper.java | 1 + gradle.properties | 2 +- 23 files changed, 731 insertions(+), 819 deletions(-) delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java rename bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/{InternalFieldAccessor.java => FieldAccessor.java} (76%) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/ProtectedFieldVisitor.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/block/StatePredicate.java diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index fc51538cf..8f761ad03 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -19,7 +19,7 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; +import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; @@ -87,7 +87,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { CESection ceSection = ceSections[i]; Object section = sections[i]; int finalI = i; - BukkitInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), + WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), (injected) -> sections[finalI] = injected); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java index 18952ff19..f20cdcca1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java @@ -62,7 +62,7 @@ public class BlockEventListener implements Listener { public void onPlayerJoin(PlayerJoinEvent event) { Object packet = this.manager.cachedUpdateTagsPacket; if (packet != null) { - this.plugin.networkManager().sendPacket(event.getPlayer(), packet); + this.plugin.adapt(event.getPlayer()).sendPacket(packet, false); } } 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 845e9e617..11adfe017 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 @@ -160,7 +160,7 @@ public class BukkitBlockManager extends AbstractBlockManager { } Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.instance$Registries$BLOCK, list)); for (Player player : Bukkit.getOnlinePlayers()) { - this.plugin.networkManager().sendPacket(player, packet); + this.plugin.networkManager().sendPacket(this.plugin.adapt(player), packet); } // 如果空,那么新来的玩家就没必要收到更新包了 if (list.isEmpty()) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java index 6b4aa09cb..dfac86a24 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.block; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; @@ -28,6 +29,8 @@ import java.lang.reflect.Field; import java.util.*; public class BukkitCustomBlock extends AbstractCustomBlock { + private static final Object ALWAYS_FALSE = FastNMS.INSTANCE.method$StatePredicate$always(false); + private static final Object ALWAYS_TRUE = FastNMS.INSTANCE.method$StatePredicate$always(true); protected BukkitCustomBlock( @NotNull Key id, @@ -79,24 +82,24 @@ public class BukkitCustomBlock extends AbstractCustomBlock { BlockStateUtils.setCanOcclude(mcBlockState, BlockStateUtils.isOcclude(state.vanillaBlockState().handle())); } if (settings.isRedstoneConductor() == Tristate.TRUE) { - BlockStateUtils.setIsRedstoneConductor(mcBlockState, StatePredicate.alwaysTrue()); + BlockStateUtils.setIsRedstoneConductor(mcBlockState, ALWAYS_TRUE); } else if (settings.isRedstoneConductor() == Tristate.FALSE) { - BlockStateUtils.setIsRedstoneConductor(mcBlockState, StatePredicate.alwaysFalse()); + BlockStateUtils.setIsRedstoneConductor(mcBlockState, ALWAYS_FALSE); } if (settings.isSuffocating() == Tristate.TRUE) { - BlockStateUtils.setIsSuffocating(mcBlockState, StatePredicate.alwaysTrue()); + BlockStateUtils.setIsSuffocating(mcBlockState, ALWAYS_TRUE); } else if (settings.isSuffocating() == Tristate.FALSE) { - BlockStateUtils.setIsSuffocating(mcBlockState, StatePredicate.alwaysFalse()); + BlockStateUtils.setIsSuffocating(mcBlockState, ALWAYS_FALSE); } if (settings.isViewBlocking() == Tristate.TRUE) { - BlockStateUtils.setIsViewBlocking(mcBlockState, StatePredicate.alwaysTrue()); + BlockStateUtils.setIsViewBlocking(mcBlockState, ALWAYS_TRUE); } else if (settings.isViewBlocking() == Tristate.FALSE) { - BlockStateUtils.setIsViewBlocking(mcBlockState, StatePredicate.alwaysFalse()); + BlockStateUtils.setIsViewBlocking(mcBlockState, ALWAYS_FALSE); } else { if (settings.isSuffocating() == Tristate.TRUE) { - BlockStateUtils.setIsViewBlocking(mcBlockState, StatePredicate.alwaysTrue()); + BlockStateUtils.setIsViewBlocking(mcBlockState, ALWAYS_TRUE); } else if (settings.isSuffocating() == Tristate.FALSE) { - BlockStateUtils.setIsViewBlocking(mcBlockState, StatePredicate.alwaysFalse()); + BlockStateUtils.setIsViewBlocking(mcBlockState, ALWAYS_FALSE); } } // set parent block properties diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index f9bdfdd6b..04e74f456 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -193,14 +193,14 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { furniture.initializeColliders(); for (Player player : display.getTrackedPlayers()) { this.plugin.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); - this.plugin.networkManager().sendPacket(player, furniture.spawnPacket(player)); + this.plugin.networkManager().sendPacket(this.plugin.adapt(player), furniture.spawnPacket(player)); } } } else { BukkitFurniture furniture = addNewFurniture(display, customFurniture); for (Player player : display.getTrackedPlayers()) { this.plugin.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); - this.plugin.networkManager().sendPacket(player, furniture.spawnPacket(player)); + this.plugin.networkManager().sendPacket(this.plugin.adapt(player), furniture.spawnPacket(player)); } if (preventChange) { this.plugin.scheduler().sync().runLater(furniture::initializeColliders, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java index 8a0c4ae70..3e059ee13 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.ComponentTypes; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; +import net.momirealms.craftengine.bukkit.plugin.injector.RecipeInjector; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRecipeTypes; @@ -271,7 +271,7 @@ public class RecipeEventListener implements Listener { Furnace furnace = furnaceInventory.getHolder(); try { Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(furnace); - BukkitInjector.injectCookingBlockEntity(blockEntity); + RecipeInjector.injectCookingBlockEntity(blockEntity); } catch (Exception e) { this.plugin.logger().warn("Failed to inject cooking block entity", e); } @@ -288,7 +288,7 @@ public class RecipeEventListener implements Listener { if (block.getState() instanceof Campfire campfire) { try { Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire); - BukkitInjector.injectCookingBlockEntity(blockEntity); + RecipeInjector.injectCookingBlockEntity(blockEntity); } catch (Exception e) { this.plugin.logger().warn("Failed to inject cooking block entity", e); } @@ -305,7 +305,7 @@ public class RecipeEventListener implements Listener { if (block.getState() instanceof Furnace furnace) { try { Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(furnace); - BukkitInjector.injectCookingBlockEntity(blockEntity); + RecipeInjector.injectCookingBlockEntity(blockEntity); } catch (Exception e) { plugin.logger().warn("Failed to inject cooking block entity", e); } @@ -314,7 +314,7 @@ public class RecipeEventListener implements Listener { if (block.getState() instanceof Campfire campfire) { try { Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire); - BukkitInjector.injectCookingBlockEntity(blockEntity); + RecipeInjector.injectCookingBlockEntity(blockEntity); } catch (Exception e) { this.plugin.logger().warn("Failed to inject cooking block entity", e); } @@ -335,7 +335,7 @@ public class RecipeEventListener implements Listener { if (clicked.getState() instanceof Campfire campfire) { try { Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire); - BukkitInjector.injectCookingBlockEntity(blockEntity); + RecipeInjector.injectCookingBlockEntity(blockEntity); } catch (Exception e) { this.plugin.logger().warn("Failed to inject cooking block entity", e); } 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 b34a5aa8b..d22a666ce 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 @@ -17,9 +17,7 @@ import net.momirealms.craftengine.bukkit.pack.BukkitPackManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitSenderFactory; import net.momirealms.craftengine.bukkit.plugin.gui.BukkitGuiManager; -import net.momirealms.craftengine.bukkit.plugin.injector.BlockGenerator; -import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; -import net.momirealms.craftengine.bukkit.plugin.injector.InjectionException; +import net.momirealms.craftengine.bukkit.plugin.injector.*; import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; import net.momirealms.craftengine.bukkit.plugin.scheduler.BukkitSchedulerAdapter; @@ -130,7 +128,21 @@ public class BukkitCraftEngine extends CraftEngine { @Override public void onPluginLoad() { - BukkitInjector.init(); + try { + WorldStorageInjector.init(); + } catch (Exception e) { + throw new InjectionException("Error injecting world storage", e); + } + try { + RecipeInjector.init(); + } catch (Exception e) { + throw new InjectionException("Error injecting recipes", e); + } + try { + ProtectedFieldVisitor.init(); + } catch (Exception e) { + throw new InjectionException("Error initializing ProtectedFieldVisitor", e); + } super.onPluginLoad(); super.blockManager.init(); super.networkManager = new BukkitNetworkManager(this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugIsSectionInjectedCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugIsSectionInjectedCommand.java index 549d9d5f2..c76a6c51a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugIsSectionInjectedCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugIsSectionInjectedCommand.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; -import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; +import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.sender.Sender; @@ -32,7 +32,7 @@ public class DebugIsSectionInjectedCommand extends BukkitCommandFeature clazz$CraftEngineBlock; private static MethodHandle constructor$CraftEngineBlock; private static Field field$CraftEngineBlock$behavior; private static Field field$CraftEngineBlock$shape; @@ -136,7 +135,7 @@ public final class BlockGenerator { // neighborChanged .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$neighborChanged)) .intercept(MethodDelegation.to(NeighborChangedInterceptor.INSTANCE)); - clazz$CraftEngineBlock = builder.make().load(BlockGenerator.class.getClassLoader()).getLoaded(); + 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)) .asType(MethodType.methodType(CoreReflections.clazz$Block, CoreReflections.clazz$BlockBehaviour$Properties)); @@ -159,7 +158,6 @@ public final class BlockGenerator { return newBlockInstance; } - public static class UpdateShapeInterceptor { public static final UpdateShapeInterceptor INSTANCE = new UpdateShapeInterceptor(); public static final int levelIndex = VersionHelper.isOrAbove1_21_2() ? 1 : 3; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java deleted file mode 100644 index 5918b3fbd..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java +++ /dev/null @@ -1,714 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.injector; - -import com.mojang.datafixers.util.Pair; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.ClassFileVersion; -import net.bytebuddy.description.field.FieldDescription; -import net.bytebuddy.description.modifier.Visibility; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; -import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; -import net.bytebuddy.implementation.FieldAccessor; -import net.bytebuddy.implementation.FixedValue; -import net.bytebuddy.implementation.Implementation; -import net.bytebuddy.implementation.MethodDelegation; -import net.bytebuddy.implementation.bind.annotation.AllArguments; -import net.bytebuddy.implementation.bind.annotation.RuntimeType; -import net.bytebuddy.implementation.bind.annotation.SuperCall; -import net.bytebuddy.implementation.bind.annotation.This; -import net.bytebuddy.implementation.bytecode.assign.Assigner; -import net.bytebuddy.implementation.bytecode.assign.TypeCasting; -import net.bytebuddy.implementation.bytecode.member.FieldAccess; -import net.bytebuddy.implementation.bytecode.member.MethodReturn; -import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; -import net.bytebuddy.matcher.ElementMatchers; -import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; -import net.momirealms.craftengine.bukkit.item.BukkitItemManager; -import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRecipeTypes; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; -import net.momirealms.craftengine.bukkit.util.BlockStateUtils; -import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.core.block.EmptyBlock; -import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.block.StatePredicate; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.recipe.CustomCookingRecipe; -import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem; -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; -import net.momirealms.craftengine.core.registry.Holder; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ReflectionUtils; -import net.momirealms.craftengine.core.util.SectionPosUtils; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.CEWorld; -import net.momirealms.craftengine.core.world.SectionPos; -import net.momirealms.craftengine.core.world.chunk.CEChunk; -import net.momirealms.craftengine.core.world.chunk.CESection; -import net.momirealms.craftengine.core.world.chunk.InjectedHolder; -import org.bukkit.inventory.ItemStack; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.invoke.VarHandle; -import java.lang.reflect.Constructor; -import java.lang.reflect.Modifier; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.Callable; -import java.util.function.Consumer; - -public class BukkitInjector { - - private static Class clazz$InjectedPalettedContainer; - private static Class clazz$InjectedLevelChunkSection; - private static MethodHandle constructor$InjectedLevelChunkSection; - private static VarHandle varHandle$InjectedPalettedContainer$target; - private static Class clazz$OptimizedItemDisplay; - private static Constructor constructor$OptimizedItemDisplay; - private static Class clazz$OptimizedItemDisplayFatory; - private static Object instance$OptimizedItemDisplayFactory; - private static Class clazz$InjectedCacheChecker; - - private static InternalFieldAccessor internalFieldAccessor; - - public static void init() { - try { - ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17); - // Paletted Container - clazz$InjectedPalettedContainer = byteBuddy - .subclass(CoreReflections.clazz$PalettedContainer) - .name("net.minecraft.world.level.chunk.InjectedPalettedContainer") - .implement(InjectedHolder.Palette.class) - .defineField("target", CoreReflections.clazz$PalettedContainer, Visibility.PUBLIC) - .defineField("active", boolean.class, Visibility.PUBLIC) - .defineField("cesection", CESection.class, Visibility.PRIVATE) - .defineField("cechunk", CEChunk.class, Visibility.PRIVATE) - .defineField("cepos", SectionPos.class, Visibility.PRIVATE) - .method(ElementMatchers.any() - .and(ElementMatchers.not(ElementMatchers.is(CoreReflections.method$PalettedContainer$getAndSet))) - .and(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class))) - ) - .intercept(MethodDelegation.toField("target")) - .method(ElementMatchers.is(CoreReflections.method$PalettedContainer$getAndSet)) - .intercept(MethodDelegation.to(GetAndSetInterceptor.INSTANCE)) - .method(ElementMatchers.named("target")) - .intercept(FieldAccessor.ofField("target")) - .method(ElementMatchers.named("setTarget")) - .intercept(FieldAccessor.ofField("target").withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC)) - .method(ElementMatchers.named("isActive").or(ElementMatchers.named("setActive"))) - .intercept(FieldAccessor.ofField("active")) - .method(ElementMatchers.named("ceSection")) - .intercept(FieldAccessor.ofField("cesection")) - .method(ElementMatchers.named("ceChunk")) - .intercept(FieldAccessor.ofField("cechunk")) - .method(ElementMatchers.named("cePos")) - .intercept(FieldAccessor.ofField("cepos")) - .make() - .load(BukkitInjector.class.getClassLoader()) - .getLoaded(); - //varHandle$InjectedPalettedContainer$target = Objects.requireNonNull(ReflectionUtils.findVarHandle(clazz$InjectedPalettedContainer, "target", CoreReflections.clazz$PalettedContainer)); - - // Level Chunk Section - clazz$InjectedLevelChunkSection = byteBuddy - .subclass(CoreReflections.clazz$LevelChunkSection, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) - .name("net.minecraft.world.level.chunk.InjectedLevelChunkSection") - .implement(InjectedHolder.Section.class) - .defineField("active", boolean.class, Visibility.PUBLIC) - .defineField("cesection", CESection.class, Visibility.PRIVATE) - .defineField("cechunk", CEChunk.class, Visibility.PRIVATE) - .defineField("cepos", SectionPos.class, Visibility.PRIVATE) - .method(ElementMatchers.is(CoreReflections.method$LevelChunkSection$setBlockState)) - .intercept(MethodDelegation.to(SetBlockStateInterceptor.INSTANCE)) - .method(ElementMatchers.named("ceSection")) - .intercept(FieldAccessor.ofField("cesection")) - .method(ElementMatchers.named("ceChunk")) - .intercept(FieldAccessor.ofField("cechunk")) - .method(ElementMatchers.named("cePos")) - .intercept(FieldAccessor.ofField("cepos")) - .method(ElementMatchers.named("isActive").or(ElementMatchers.named("setActive"))) - .intercept(FieldAccessor.ofField("active")) - .make() - .load(BukkitInjector.class.getClassLoader()) - .getLoaded(); - - constructor$InjectedLevelChunkSection = MethodHandles.publicLookup().in(clazz$InjectedLevelChunkSection) - .findConstructor(clazz$InjectedLevelChunkSection, MethodType.methodType(void.class, CoreReflections.clazz$PalettedContainer, CoreReflections.clazz$PalettedContainer)) - .asType(MethodType.methodType(CoreReflections.clazz$LevelChunkSection, CoreReflections.clazz$PalettedContainer, CoreReflections.clazz$PalettedContainer)); - - // State Predicate - DynamicType.Unloaded alwaysTrue = byteBuddy - .subclass(CoreReflections.clazz$StatePredicate) - .method(ElementMatchers.named("test")) - .intercept(FixedValue.value(true)) - .make(); - Class alwaysTrueClass = alwaysTrue.load(BukkitInjector.class.getClassLoader()).getLoaded(); - DynamicType.Unloaded alwaysFalse = byteBuddy - .subclass(CoreReflections.clazz$StatePredicate) - .method(ElementMatchers.named("test")) - .intercept(FixedValue.value(false)) - .make(); - Class alwaysFalseClass = alwaysFalse.load(BukkitInjector.class.getClassLoader()).getLoaded(); - StatePredicate.init(alwaysTrueClass.getDeclaredConstructor().newInstance(), alwaysFalseClass.getDeclaredConstructor().newInstance()); - // Optimized Item Display - clazz$OptimizedItemDisplay = byteBuddy - .subclass(CoreReflections.clazz$Display$ItemDisplay, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) - .name("net.minecraft.world.entity.OptimizedItemDisplay") - .make() - .load(BukkitInjector.class.getClassLoader()) - .getLoaded(); - constructor$OptimizedItemDisplay = ReflectionUtils.getConstructor(clazz$OptimizedItemDisplay, CoreReflections.clazz$EntityType, CoreReflections.clazz$Level); - clazz$OptimizedItemDisplayFatory = byteBuddy - .subclass(Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) - .name("net.momirealms.craftengine.bukkit.entity.OptimizedItemDisplayFactory") - .implement(CoreReflections.clazz$EntityType$EntityFactory) - .method(ElementMatchers.named("create")) - .intercept(MethodDelegation.to(OptimizedItemDisplayMethodInterceptor.INSTANCE)) - .make() - .load(BukkitInjector.class.getClassLoader()) - .getLoaded(); - instance$OptimizedItemDisplayFactory = Objects.requireNonNull(ReflectionUtils.getConstructor(clazz$OptimizedItemDisplayFatory, 0)).newInstance(); - - // InternalFieldAccessor Interface - Class internalFieldAccessorInterface = new ByteBuddy() - .makeInterface() - .name("net.momirealms.craftengine.bukkit.plugin.injector.InternalFieldAccessor") - .defineMethod("field$ClientboundMoveEntityPacket$entityId", int.class, Modifier.PUBLIC) - .withParameter(Object.class, "packet") - .withoutCode() - .make() - .load(NetworkReflections.clazz$ClientboundMoveEntityPacket.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) - .getLoaded(); - - // Internal field accessor - FieldDescription moveEntityIdFieldDesc = new FieldDescription.ForLoadedField(NetworkReflections.field$ClientboundMoveEntityPacket$entityId); - Class clazz$InternalFieldAccessor = byteBuddy - .subclass(Object.class) - .name("net.minecraft.network.protocol.game.CraftEngineInternalFieldAccessor") - .implement(internalFieldAccessorInterface) - .method(ElementMatchers.named("field$ClientboundMoveEntityPacket$entityId")) - .intercept(new Implementation.Simple( - MethodVariableAccess.REFERENCE.loadFrom(1), - TypeCasting.to(TypeDescription.ForLoadedType.of(NetworkReflections.clazz$ClientboundMoveEntityPacket)), - FieldAccess.forField(moveEntityIdFieldDesc).read(), - MethodReturn.INTEGER - )) - .make() - .load(NetworkReflections.clazz$ClientboundMoveEntityPacket.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) - .getLoaded(); - internalFieldAccessor = (InternalFieldAccessor) clazz$InternalFieldAccessor.getConstructor().newInstance(); - - - - clazz$InjectedCacheChecker = byteBuddy - .subclass(Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) - .name("net.momirealms.craftengine.bukkit.entity.InjectedCacheChecker") - .implement(CoreReflections.clazz$RecipeManager$CachedCheck) - .implement(InjectedCacheCheck.class) - .defineField("recipeType", Object.class, Visibility.PUBLIC) - .method(ElementMatchers.named("recipeType")) - .intercept(FieldAccessor.ofField("recipeType")) - .defineField("lastRecipe", Object.class, Visibility.PUBLIC) - .method(ElementMatchers.named("lastRecipe")) - .intercept(FieldAccessor.ofField("lastRecipe")) - .method(ElementMatchers.named("setLastRecipe")) - .intercept(FieldAccessor.ofField("lastRecipe")) - .defineField("lastCustomRecipe", Key.class, Visibility.PUBLIC) - .method(ElementMatchers.named("lastCustomRecipe")) - .intercept(FieldAccessor.ofField("lastCustomRecipe")) - .method(ElementMatchers.named("getRecipeFor").or(ElementMatchers.named("a"))) - .intercept(MethodDelegation.to( - VersionHelper.isOrAbove1_21_2() ? - GetRecipeForMethodInterceptor1_21_2.INSTANCE : - (VersionHelper.isOrAbove1_21() ? - GetRecipeForMethodInterceptor1_21.INSTANCE : - VersionHelper.isOrAbove1_20_5() ? - GetRecipeForMethodInterceptor1_20_5.INSTANCE : - GetRecipeForMethodInterceptor1_20.INSTANCE) - )) - .make() - .load(BukkitInjector.class.getClassLoader()) - .getLoaded(); - } catch (Throwable e) { - CraftEngine.instance().logger().severe("Failed to init injector", e); - } - } - - public static InternalFieldAccessor internalFieldAccessor() { - return internalFieldAccessor; - } - - public static void injectCookingBlockEntity(Object entity) throws ReflectiveOperationException { - if (CoreReflections.clazz$AbstractFurnaceBlockEntity.isInstance(entity)) { - Object quickCheck = CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.get(entity); - if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected - Object recipeType = FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$recipeType(entity); - InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); - injectedChecker.recipeType(recipeType); - CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.set(entity, injectedChecker); - } else if (!VersionHelper.isOrAbove1_21_2() && CoreReflections.clazz$CampfireBlockEntity.isInstance(entity)) { - Object quickCheck = CoreReflections.field$CampfireBlockEntity$quickCheck.get(entity); - if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected - InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); - injectedChecker.recipeType(MRecipeTypes.CAMPFIRE_COOKING); - CoreReflections.field$CampfireBlockEntity$quickCheck.set(entity, injectedChecker); - } - } - - public static Object getOptimizedItemDisplayFactory() { - return instance$OptimizedItemDisplayFactory; - } - - public static class OptimizedItemDisplayMethodInterceptor { - public static final OptimizedItemDisplayMethodInterceptor INSTANCE = new OptimizedItemDisplayMethodInterceptor(); - - @RuntimeType - public Object intercept(@AllArguments Object[] args) throws Exception { - return constructor$OptimizedItemDisplay.newInstance(args[0], args[1]); - } - } - -// public synchronized static void injectLevelChunkSection(Object targetSection, CESection ceSection, CEWorld ceWorld, SectionPos pos) { -// try { -// Object container = FastNMS.INSTANCE.field$LevelChunkSection$states(targetSection); -// if (!(container instanceof InjectedPalettedContainerHolder)) { -// InjectedPalettedContainerHolder injectedObject = FastNMS.INSTANCE.createInjectedPalettedContainerHolder(container); -// injectedObject.ceSection(ceSection); -// injectedObject.ceWorld(ceWorld); -// injectedObject.cePos(pos); -// CoreReflections.varHandle$PalettedContainer$data.setVolatile(injectedObject, CoreReflections.varHandle$PalettedContainer$data.get(container)); -// CoreReflections.field$LevelChunkSection$states.set(targetSection, injectedObject); -// } -// } catch (Exception e) { -// CraftEngine.instance().logger().severe("Failed to inject chunk section", e); -// } -// } - - public synchronized static void injectLevelChunkSection(Object targetSection, CESection ceSection, CEChunk chunk, SectionPos pos, Consumer callback) { - try { - if (Config.injectionTarget()) { - Object container = FastNMS.INSTANCE.field$LevelChunkSection$states(targetSection); - if (!(container instanceof InjectedHolder.Palette holder)) { - InjectedHolder.Palette injectedObject; - if (Config.fastInjection()) { - injectedObject = FastNMS.INSTANCE.createInjectedPalettedContainerHolder(container); - } else { - injectedObject = (InjectedHolder.Palette) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedPalettedContainer); - injectedObject.setTarget(container); - //varHandle$InjectedPalettedContainer$target.set(injectedObject, container); - } - injectedObject.ceChunk(chunk); - injectedObject.ceSection(ceSection); - injectedObject.cePos(pos); - injectedObject.setActive(true); - CoreReflections.varHandle$PalettedContainer$data.setVolatile(injectedObject, CoreReflections.varHandle$PalettedContainer$data.get(container)); - CoreReflections.field$LevelChunkSection$states.set(targetSection, injectedObject); - } else { - holder.ceChunk(chunk); - holder.ceSection(ceSection); - holder.cePos(pos); - holder.setActive(true); - } - } else { - if (!(targetSection instanceof InjectedHolder.Section holder)) { - InjectedHolder.Section injectedObject; - if (Config.fastInjection()) { - injectedObject = FastNMS.INSTANCE.createInjectedLevelChunkSectionHolder(targetSection); - } else { - injectedObject = (InjectedHolder.Section) constructor$InjectedLevelChunkSection.invoke( - FastNMS.INSTANCE.field$LevelChunkSection$states(targetSection), FastNMS.INSTANCE.field$LevelChunkSection$biomes(targetSection)); - } - injectedObject.ceChunk(chunk); - injectedObject.ceSection(ceSection); - injectedObject.cePos(pos); - injectedObject.setActive(true); - callback.accept(injectedObject); - } else { - holder.ceChunk(chunk); - holder.ceSection(ceSection); - holder.cePos(pos); - holder.setActive(true); - } - } - } catch (Throwable e) { - CraftEngine.instance().logger().severe("Failed to inject chunk section " + pos, e); - } - } - - public static boolean isSectionInjected(Object section) { - if (Config.injectionTarget()) { - Object container = FastNMS.INSTANCE.field$LevelChunkSection$states(section); - return container instanceof InjectedHolder.Palette; - } else { - return section instanceof InjectedHolder.Section; - } - } - - public synchronized static Object uninjectLevelChunkSection(Object section) { - if (Config.injectionTarget()) { - Object states = FastNMS.INSTANCE.field$LevelChunkSection$states(section); - if (states instanceof InjectedHolder.Palette holder) { - holder.setActive(false); -// try { -// CoreReflections.field$LevelChunkSection$states.set(section, holder.target()); -// } catch (ReflectiveOperationException e) { -// CraftEngine.instance().logger().severe("Failed to uninject palette", e); -// } - } - } else { - if (section instanceof InjectedHolder.Section holder) { - holder.setActive(false); - //return FastNMS.INSTANCE.constructor$LevelChunkSection(holder); - } - } - return section; - } - - public static class GetRecipeForMethodInterceptor1_20 { - public static final GetRecipeForMethodInterceptor1_20 INSTANCE = new GetRecipeForMethodInterceptor1_20(); - - @SuppressWarnings("unchecked") - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { - Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object type = injectedCacheCheck.recipeType(); - Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); - if (optionalRecipe.isPresent()) { - Pair pair = optionalRecipe.get(); - Object resourceLocation = pair.getFirst(); - Key recipeId = Key.of(resourceLocation.toString()); - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - - ItemStack itemStack; - List items; - if (type == MRecipeTypes.CAMPFIRE_COOKING) { - items = (List) CoreReflections.field$SimpleContainer$items.get(args[0]); - } else { - items = (List) CoreReflections.field$AbstractFurnaceBlockEntity$items.get(args[0]); - } - itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(items.get(0)); - - // it's a recipe from other plugins - boolean isCustom = recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - injectedCacheCheck.lastRecipe(resourceLocation); - return Optional.of(pair.getSecond()); - } - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); - if (idHolder.isEmpty()) { - return Optional.empty(); - } - - SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); - CustomCookingRecipe ceRecipe; - Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); - if (type == MRecipeTypes.SMELTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.BLASTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.SMOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.CAMPFIRE_COOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe); - } else { - return Optional.empty(); - } - if (ceRecipe == null) { - return Optional.empty(); - } - - // Cache recipes, it might be incorrect on reloading - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - // It doesn't matter at all - injectedCacheCheck.lastRecipe(resourceLocation); - return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(pair.getSecond())); - } else { - return Optional.empty(); - } - } - } - - public static class GetRecipeForMethodInterceptor1_20_5 { - public static final GetRecipeForMethodInterceptor1_20_5 INSTANCE = new GetRecipeForMethodInterceptor1_20_5(); - - @SuppressWarnings("unchecked") - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { - Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object type = injectedCacheCheck.recipeType(); - Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); - if (optionalRecipe.isPresent()) { - Object holder = optionalRecipe.get(); - Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder); - Key recipeId = Key.of(id.toString()); - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - - ItemStack itemStack; - List items; - if (type == MRecipeTypes.CAMPFIRE_COOKING) { - items = (List) CoreReflections.field$SimpleContainer$items.get(args[0]); - } else { - items = (List) CoreReflections.field$AbstractFurnaceBlockEntity$items.get(args[0]); - } - itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(items.get(0)); - - // it's a recipe from other plugins - boolean isCustom = recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - injectedCacheCheck.lastRecipe(id); - return optionalRecipe; - } - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); - if (idHolder.isEmpty()) { - return Optional.empty(); - } - - SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); - CustomCookingRecipe ceRecipe; - Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); - if (type == MRecipeTypes.SMELTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.BLASTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.SMOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.CAMPFIRE_COOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe); - } else { - return Optional.empty(); - } - if (ceRecipe == null) { - return Optional.empty(); - } - - // Cache recipes, it might be incorrect on reloading - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - // It doesn't matter at all - injectedCacheCheck.lastRecipe(id); - return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder)); - } else { - return Optional.empty(); - } - } - } - - public static class GetRecipeForMethodInterceptor1_21 { - public static final GetRecipeForMethodInterceptor1_21 INSTANCE = new GetRecipeForMethodInterceptor1_21(); - - @SuppressWarnings("unchecked") - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { - Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object type = injectedCacheCheck.recipeType(); - Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); - if (optionalRecipe.isPresent()) { - Object holder = optionalRecipe.get(); - Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder); - Key recipeId = Key.of(id.toString()); - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.field$SingleRecipeInput$item.get(args[0])); - - // it's a recipe from other plugins - boolean isCustom = recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - injectedCacheCheck.lastRecipe(id); - return optionalRecipe; - } - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); - if (idHolder.isEmpty()) { - return Optional.empty(); - } - - SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); - CustomCookingRecipe ceRecipe; - Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); - if (type == MRecipeTypes.SMELTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.BLASTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.SMOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.CAMPFIRE_COOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe); - } else { - return Optional.empty(); - } - if (ceRecipe == null) { - return Optional.empty(); - } - - // Cache recipes, it might be incorrect on reloading - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - // It doesn't matter at all - injectedCacheCheck.lastRecipe(id); - return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder)); - } else { - return Optional.empty(); - } - } - } - - public static class GetRecipeForMethodInterceptor1_21_2 { - public static final GetRecipeForMethodInterceptor1_21_2 INSTANCE = new GetRecipeForMethodInterceptor1_21_2(); - - @SuppressWarnings("unchecked") - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { - Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object type = injectedCacheCheck.recipeType(); - Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); - if (optionalRecipe.isPresent()) { - Object holder = optionalRecipe.get(); - Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder); - Object resourceLocation = FastNMS.INSTANCE.field$ResourceKey$location(id); - Key recipeId = Key.of(resourceLocation.toString()); - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.field$SingleRecipeInput$item.get(args[0])); - - // it's a recipe from other plugins - boolean isCustom = recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - injectedCacheCheck.lastRecipe(id); - return optionalRecipe; - } - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); - if (idHolder.isEmpty()) { - return Optional.empty(); - } - - SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); - CustomCookingRecipe ceRecipe; - Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); - if (type == MRecipeTypes.SMELTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.BLASTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.SMOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); - } else { - return Optional.empty(); - } - if (ceRecipe == null) { - return Optional.empty(); - } - - // Cache recipes, it might be incorrect on reloading - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - // It doesn't matter at all - injectedCacheCheck.lastRecipe(id); - return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder)); - } else { - return Optional.empty(); - } - } - } - - public static class SetBlockStateInterceptor { - public static final SetBlockStateInterceptor INSTANCE = new SetBlockStateInterceptor(); - - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable superMethod) throws Exception { - InjectedHolder.Section holder = (InjectedHolder.Section) thisObj; - int x = (int) args[0]; - int y = (int) args[1]; - int z = (int) args[2]; - Object newState = args[3]; - Object previousState = superMethod.call(); - if (holder.isActive()) { - compareAndUpdateBlockState(x, y, z, newState, previousState, holder); - } - return previousState; - } - } - - public static class GetAndSetInterceptor { - public static final GetAndSetInterceptor INSTANCE = new GetAndSetInterceptor(); - - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) { - InjectedHolder.Palette holder = (InjectedHolder.Palette) thisObj; - Object targetStates = holder.target(); - int x = (int) args[0]; - int y = (int) args[1]; - int z = (int) args[2]; - Object newState = args[3]; - Object previousState = FastNMS.INSTANCE.method$PalettedContainer$getAndSet(targetStates, x, y, z, newState); - if (holder.isActive()) { - compareAndUpdateBlockState(x, y, z, newState, previousState, holder); - } - return previousState; - } - } - - protected static void compareAndUpdateBlockState(int x, int y, int z, Object newState, Object previousState, InjectedHolder holder) { - try { - int stateId = BlockStateUtils.blockStateToId(newState); - CESection section = holder.ceSection(); - // 如果是原版方块 - if (BlockStateUtils.isVanillaBlock(stateId)) { - // 那么应该情况自定义块 - ImmutableBlockState previous = section.setBlockState(x, y, z, EmptyBlock.STATE); - // 如果先前不是空气则标记 - if (!previous.isEmpty()) { - holder.ceChunk().setDirty(true); - if (Config.enableLightSystem()) { - updateLightIfChanged(holder, previousState, newState, newState, x, y, z); - } - } - } else { - ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); - ImmutableBlockState previousImmutableBlockState = section.setBlockState(x, y, z, immutableBlockState); - if (previousImmutableBlockState == immutableBlockState) return; - holder.ceChunk().setDirty(true); - // 如果新方块的光照属性和客户端认为的不同 - if (Config.enableLightSystem() && !immutableBlockState.isEmpty()) { - updateLightIfChanged(holder, previousState, immutableBlockState.vanillaBlockState().handle(), newState, x, y, z); - } - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to intercept setBlockState", e); - } - } - - protected static void updateLightIfChanged(@This InjectedHolder thisObj, Object oldServerSideState, Object clientSideState, Object serverSideState, int x, int y, int z) { - CEWorld world = thisObj.ceChunk().world(); - Object blockPos = LocationUtils.toBlockPos(x, y, z); - Object serverWorld = world.world().serverWorld(); - if (clientSideState != serverSideState && FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(clientSideState, serverSideState, serverWorld, blockPos)) { - SectionPos sectionPos = thisObj.cePos(); - List pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15); - world.sectionLightUpdated(pos); - return; - } - if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(oldServerSideState, serverSideState, serverWorld, blockPos)) { - SectionPos sectionPos = thisObj.cePos(); - List pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15); - world.sectionLightUpdated(pos); - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/InternalFieldAccessor.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/FieldAccessor.java similarity index 76% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/InternalFieldAccessor.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/FieldAccessor.java index 94c5f6be2..95677b7cd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/InternalFieldAccessor.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/FieldAccessor.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.bukkit.plugin.injector; -public interface InternalFieldAccessor { +public interface FieldAccessor { int field$ClientboundMoveEntityPacket$entityId(Object packet); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/ProtectedFieldVisitor.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/ProtectedFieldVisitor.java new file mode 100644 index 000000000..4ebf4c2be --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/ProtectedFieldVisitor.java @@ -0,0 +1,56 @@ +package net.momirealms.craftengine.bukkit.plugin.injector; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.ClassFileVersion; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.implementation.bytecode.assign.TypeCasting; +import net.bytebuddy.implementation.bytecode.member.FieldAccess; +import net.bytebuddy.implementation.bytecode.member.MethodReturn; +import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; +import net.bytebuddy.matcher.ElementMatchers; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; + +import java.lang.reflect.Modifier; + +public class ProtectedFieldVisitor { + private static FieldAccessor internalFieldAccessor; + + public static void init() throws ReflectiveOperationException { + ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17); + // InternalFieldAccessor Interface + Class internalFieldAccessorInterface = byteBuddy + .makeInterface() + .name("net.momirealms.craftengine.bukkit.plugin.injector.InternalFieldAccessor") + .defineMethod("field$ClientboundMoveEntityPacket$entityId", int.class, Modifier.PUBLIC) + .withParameter(Object.class, "packet") + .withoutCode() + .make() + .load(NetworkReflections.clazz$ClientboundMoveEntityPacket.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) + .getLoaded(); + + // Internal field accessor + FieldDescription moveEntityIdFieldDesc = new FieldDescription.ForLoadedField(NetworkReflections.field$ClientboundMoveEntityPacket$entityId); + Class clazz$InternalFieldAccessor = byteBuddy + .subclass(Object.class) + .name("net.minecraft.network.protocol.game.CraftEngineInternalFieldAccessor") + .implement(internalFieldAccessorInterface) + .method(ElementMatchers.named("field$ClientboundMoveEntityPacket$entityId")) + .intercept(new Implementation.Simple( + MethodVariableAccess.REFERENCE.loadFrom(1), + TypeCasting.to(TypeDescription.ForLoadedType.of(NetworkReflections.clazz$ClientboundMoveEntityPacket)), + FieldAccess.forField(moveEntityIdFieldDesc).read(), + MethodReturn.INTEGER + )) + .make() + .load(NetworkReflections.clazz$ClientboundMoveEntityPacket.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) + .getLoaded(); + internalFieldAccessor = (FieldAccessor) clazz$InternalFieldAccessor.getConstructor().newInstance(); + } + + public static FieldAccessor get() { + return internalFieldAccessor; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java new file mode 100644 index 000000000..820b9f556 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java @@ -0,0 +1,341 @@ +package net.momirealms.craftengine.bukkit.plugin.injector; + +import com.mojang.datafixers.util.Pair; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.ClassFileVersion; +import net.bytebuddy.description.modifier.Visibility; +import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; +import net.bytebuddy.implementation.FieldAccessor; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.This; +import net.bytebuddy.matcher.ElementMatchers; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRecipeTypes; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.recipe.CustomCookingRecipe; +import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem; +import net.momirealms.craftengine.core.item.recipe.RecipeTypes; +import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.Holder; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ReflectionUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Optional; + +public class RecipeInjector { + private static Class clazz$InjectedCacheChecker; + + public static void init() { + ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17); + clazz$InjectedCacheChecker = byteBuddy + .subclass(Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) + .name("net.momirealms.craftengine.bukkit.entity.InjectedCacheChecker") + .implement(CoreReflections.clazz$RecipeManager$CachedCheck) + .implement(InjectedCacheCheck.class) + .defineField("recipeType", Object.class, Visibility.PUBLIC) + .method(ElementMatchers.named("recipeType")) + .intercept(FieldAccessor.ofField("recipeType")) + .defineField("lastRecipe", Object.class, Visibility.PUBLIC) + .method(ElementMatchers.named("lastRecipe")) + .intercept(FieldAccessor.ofField("lastRecipe")) + .method(ElementMatchers.named("setLastRecipe")) + .intercept(FieldAccessor.ofField("lastRecipe")) + .defineField("lastCustomRecipe", Key.class, Visibility.PUBLIC) + .method(ElementMatchers.named("lastCustomRecipe")) + .intercept(FieldAccessor.ofField("lastCustomRecipe")) + .method(ElementMatchers.named("getRecipeFor").or(ElementMatchers.named("a"))) + .intercept(MethodDelegation.to( + VersionHelper.isOrAbove1_21_2() ? + GetRecipeForMethodInterceptor1_21_2.INSTANCE : + (VersionHelper.isOrAbove1_21() ? + GetRecipeForMethodInterceptor1_21.INSTANCE : + VersionHelper.isOrAbove1_20_5() ? + GetRecipeForMethodInterceptor1_20_5.INSTANCE : + GetRecipeForMethodInterceptor1_20.INSTANCE) + )) + .make() + .load(RecipeInjector.class.getClassLoader()) + .getLoaded(); + } + + public static void injectCookingBlockEntity(Object entity) throws ReflectiveOperationException { + if (CoreReflections.clazz$AbstractFurnaceBlockEntity.isInstance(entity)) { + Object quickCheck = CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.get(entity); + if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected + Object recipeType = FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$recipeType(entity); + InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); + injectedChecker.recipeType(recipeType); + CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.set(entity, injectedChecker); + } else if (!VersionHelper.isOrAbove1_21_2() && CoreReflections.clazz$CampfireBlockEntity.isInstance(entity)) { + Object quickCheck = CoreReflections.field$CampfireBlockEntity$quickCheck.get(entity); + if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected + InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); + injectedChecker.recipeType(MRecipeTypes.CAMPFIRE_COOKING); + CoreReflections.field$CampfireBlockEntity$quickCheck.set(entity, injectedChecker); + } + } + + public static class GetRecipeForMethodInterceptor1_20 { + public static final GetRecipeForMethodInterceptor1_20 INSTANCE = new GetRecipeForMethodInterceptor1_20(); + + @SuppressWarnings("unchecked") + @RuntimeType + public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { + Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); + InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; + Object type = injectedCacheCheck.recipeType(); + Object lastRecipe = injectedCacheCheck.lastRecipe(); + Optional> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); + if (optionalRecipe.isPresent()) { + Pair pair = optionalRecipe.get(); + Object resourceLocation = pair.getFirst(); + Key recipeId = Key.of(resourceLocation.toString()); + BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); + + ItemStack itemStack; + List items; + if (type == MRecipeTypes.CAMPFIRE_COOKING) { + items = (List) CoreReflections.field$SimpleContainer$items.get(args[0]); + } else { + items = (List) CoreReflections.field$AbstractFurnaceBlockEntity$items.get(args[0]); + } + itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(items.get(0)); + + // it's a recipe from other plugins + boolean isCustom = recipeManager.isCustomRecipe(recipeId); + if (!isCustom) { + injectedCacheCheck.lastRecipe(resourceLocation); + return Optional.of(pair.getSecond()); + } + + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); + if (idHolder.isEmpty()) { + return Optional.empty(); + } + + SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); + CustomCookingRecipe ceRecipe; + Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); + if (type == MRecipeTypes.SMELTING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); + } else if (type == MRecipeTypes.BLASTING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); + } else if (type == MRecipeTypes.SMOKING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); + } else if (type == MRecipeTypes.CAMPFIRE_COOKING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe); + } else { + return Optional.empty(); + } + if (ceRecipe == null) { + return Optional.empty(); + } + + // Cache recipes, it might be incorrect on reloading + injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); + // It doesn't matter at all + injectedCacheCheck.lastRecipe(resourceLocation); + return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(pair.getSecond())); + } else { + return Optional.empty(); + } + } + } + + public static class GetRecipeForMethodInterceptor1_20_5 { + public static final GetRecipeForMethodInterceptor1_20_5 INSTANCE = new GetRecipeForMethodInterceptor1_20_5(); + + @SuppressWarnings("unchecked") + @RuntimeType + public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { + Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); + InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; + Object type = injectedCacheCheck.recipeType(); + Object lastRecipe = injectedCacheCheck.lastRecipe(); + Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); + if (optionalRecipe.isPresent()) { + Object holder = optionalRecipe.get(); + Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder); + Key recipeId = Key.of(id.toString()); + BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); + + ItemStack itemStack; + List items; + if (type == MRecipeTypes.CAMPFIRE_COOKING) { + items = (List) CoreReflections.field$SimpleContainer$items.get(args[0]); + } else { + items = (List) CoreReflections.field$AbstractFurnaceBlockEntity$items.get(args[0]); + } + itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(items.get(0)); + + // it's a recipe from other plugins + boolean isCustom = recipeManager.isCustomRecipe(recipeId); + if (!isCustom) { + injectedCacheCheck.lastRecipe(id); + return optionalRecipe; + } + + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); + if (idHolder.isEmpty()) { + return Optional.empty(); + } + + SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); + CustomCookingRecipe ceRecipe; + Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); + if (type == MRecipeTypes.SMELTING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); + } else if (type == MRecipeTypes.BLASTING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); + } else if (type == MRecipeTypes.SMOKING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); + } else if (type == MRecipeTypes.CAMPFIRE_COOKING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe); + } else { + return Optional.empty(); + } + if (ceRecipe == null) { + return Optional.empty(); + } + + // Cache recipes, it might be incorrect on reloading + injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); + // It doesn't matter at all + injectedCacheCheck.lastRecipe(id); + return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder)); + } else { + return Optional.empty(); + } + } + } + + public static class GetRecipeForMethodInterceptor1_21 { + public static final GetRecipeForMethodInterceptor1_21 INSTANCE = new GetRecipeForMethodInterceptor1_21(); + + @SuppressWarnings("unchecked") + @RuntimeType + public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { + Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); + InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; + Object type = injectedCacheCheck.recipeType(); + Object lastRecipe = injectedCacheCheck.lastRecipe(); + Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); + if (optionalRecipe.isPresent()) { + Object holder = optionalRecipe.get(); + Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder); + Key recipeId = Key.of(id.toString()); + BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); + ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.field$SingleRecipeInput$item.get(args[0])); + + // it's a recipe from other plugins + boolean isCustom = recipeManager.isCustomRecipe(recipeId); + if (!isCustom) { + injectedCacheCheck.lastRecipe(id); + return optionalRecipe; + } + + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); + if (idHolder.isEmpty()) { + return Optional.empty(); + } + + SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); + CustomCookingRecipe ceRecipe; + Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); + if (type == MRecipeTypes.SMELTING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); + } else if (type == MRecipeTypes.BLASTING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); + } else if (type == MRecipeTypes.SMOKING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); + } else if (type == MRecipeTypes.CAMPFIRE_COOKING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe); + } else { + return Optional.empty(); + } + if (ceRecipe == null) { + return Optional.empty(); + } + + // Cache recipes, it might be incorrect on reloading + injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); + // It doesn't matter at all + injectedCacheCheck.lastRecipe(id); + return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder)); + } else { + return Optional.empty(); + } + } + } + + public static class GetRecipeForMethodInterceptor1_21_2 { + public static final GetRecipeForMethodInterceptor1_21_2 INSTANCE = new GetRecipeForMethodInterceptor1_21_2(); + + @SuppressWarnings("unchecked") + @RuntimeType + public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { + Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); + InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; + Object type = injectedCacheCheck.recipeType(); + Object lastRecipe = injectedCacheCheck.lastRecipe(); + Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); + if (optionalRecipe.isPresent()) { + Object holder = optionalRecipe.get(); + Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder); + Object resourceLocation = FastNMS.INSTANCE.field$ResourceKey$location(id); + Key recipeId = Key.of(resourceLocation.toString()); + BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); + ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.field$SingleRecipeInput$item.get(args[0])); + + // it's a recipe from other plugins + boolean isCustom = recipeManager.isCustomRecipe(recipeId); + if (!isCustom) { + injectedCacheCheck.lastRecipe(id); + return optionalRecipe; + } + + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); + if (idHolder.isEmpty()) { + return Optional.empty(); + } + + SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); + CustomCookingRecipe ceRecipe; + Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); + if (type == MRecipeTypes.SMELTING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); + } else if (type == MRecipeTypes.BLASTING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); + } else if (type == MRecipeTypes.SMOKING) { + ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); + } else { + return Optional.empty(); + } + if (ceRecipe == null) { + return Optional.empty(); + } + + // Cache recipes, it might be incorrect on reloading + injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); + // It doesn't matter at all + injectedCacheCheck.lastRecipe(id); + return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder)); + } else { + return Optional.empty(); + } + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java new file mode 100644 index 000000000..02a99e042 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java @@ -0,0 +1,263 @@ +package net.momirealms.craftengine.bukkit.plugin.injector; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.ClassFileVersion; +import net.bytebuddy.description.modifier.Visibility; +import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; +import net.bytebuddy.implementation.FieldAccessor; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.SuperCall; +import net.bytebuddy.implementation.bind.annotation.This; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.matcher.ElementMatchers; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.EmptyBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.util.ReflectionUtils; +import net.momirealms.craftengine.core.util.SectionPosUtils; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.SectionPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; +import net.momirealms.craftengine.core.world.chunk.CESection; +import net.momirealms.craftengine.core.world.chunk.InjectedHolder; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.function.Consumer; + +public class WorldStorageInjector { + private static Class clazz$InjectedPalettedContainer; + private static MethodHandle constructor$InjectedLevelChunkSection; + + public static void init() throws ReflectiveOperationException { + ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17); + // Paletted Container + clazz$InjectedPalettedContainer = byteBuddy + .subclass(CoreReflections.clazz$PalettedContainer) + .name("net.minecraft.world.level.chunk.InjectedPalettedContainer") + .implement(InjectedHolder.Palette.class) + .defineField("target", CoreReflections.clazz$PalettedContainer, Visibility.PUBLIC) + .defineField("active", boolean.class, Visibility.PUBLIC) + .defineField("cesection", CESection.class, Visibility.PRIVATE) + .defineField("cechunk", CEChunk.class, Visibility.PRIVATE) + .defineField("cepos", SectionPos.class, Visibility.PRIVATE) + .method(ElementMatchers.any() + .and(ElementMatchers.not(ElementMatchers.is(CoreReflections.method$PalettedContainer$getAndSet))) + .and(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class))) + ) + .intercept(MethodDelegation.toField("target")) + .method(ElementMatchers.is(CoreReflections.method$PalettedContainer$getAndSet)) + .intercept(MethodDelegation.to(GetAndSetInterceptor.INSTANCE)) + .method(ElementMatchers.named("target")) + .intercept(FieldAccessor.ofField("target")) + .method(ElementMatchers.named("setTarget")) + .intercept(FieldAccessor.ofField("target").withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC)) + .method(ElementMatchers.named("isActive").or(ElementMatchers.named("setActive"))) + .intercept(FieldAccessor.ofField("active")) + .method(ElementMatchers.named("ceSection")) + .intercept(FieldAccessor.ofField("cesection")) + .method(ElementMatchers.named("ceChunk")) + .intercept(FieldAccessor.ofField("cechunk")) + .method(ElementMatchers.named("cePos")) + .intercept(FieldAccessor.ofField("cepos")) + .make() + .load(WorldStorageInjector.class.getClassLoader()) + .getLoaded(); + // Level Chunk Section + Class clazz$InjectedLevelChunkSection = byteBuddy + .subclass(CoreReflections.clazz$LevelChunkSection, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) + .name("net.minecraft.world.level.chunk.InjectedLevelChunkSection") + .implement(InjectedHolder.Section.class) + .defineField("active", boolean.class, Visibility.PUBLIC) + .defineField("cesection", CESection.class, Visibility.PRIVATE) + .defineField("cechunk", CEChunk.class, Visibility.PRIVATE) + .defineField("cepos", SectionPos.class, Visibility.PRIVATE) + .method(ElementMatchers.is(CoreReflections.method$LevelChunkSection$setBlockState)) + .intercept(MethodDelegation.to(SetBlockStateInterceptor.INSTANCE)) + .method(ElementMatchers.named("ceSection")) + .intercept(FieldAccessor.ofField("cesection")) + .method(ElementMatchers.named("ceChunk")) + .intercept(FieldAccessor.ofField("cechunk")) + .method(ElementMatchers.named("cePos")) + .intercept(FieldAccessor.ofField("cepos")) + .method(ElementMatchers.named("isActive").or(ElementMatchers.named("setActive"))) + .intercept(FieldAccessor.ofField("active")) + .make() + .load(WorldStorageInjector.class.getClassLoader()) + .getLoaded(); + + constructor$InjectedLevelChunkSection = MethodHandles.publicLookup().in(clazz$InjectedLevelChunkSection) + .findConstructor(clazz$InjectedLevelChunkSection, MethodType.methodType(void.class, CoreReflections.clazz$PalettedContainer, CoreReflections.clazz$PalettedContainer)) + .asType(MethodType.methodType(CoreReflections.clazz$LevelChunkSection, CoreReflections.clazz$PalettedContainer, CoreReflections.clazz$PalettedContainer)); + } + + public synchronized static void injectLevelChunkSection(Object targetSection, CESection ceSection, CEChunk chunk, SectionPos pos, Consumer callback) { + try { + if (Config.injectionTarget()) { + Object container = FastNMS.INSTANCE.field$LevelChunkSection$states(targetSection); + if (!(container instanceof InjectedHolder.Palette holder)) { + InjectedHolder.Palette injectedObject; + if (Config.fastInjection()) { + injectedObject = FastNMS.INSTANCE.createInjectedPalettedContainerHolder(container); + } else { + injectedObject = (InjectedHolder.Palette) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedPalettedContainer); + injectedObject.setTarget(container); + //varHandle$InjectedPalettedContainer$target.set(injectedObject, container); + } + injectedObject.ceChunk(chunk); + injectedObject.ceSection(ceSection); + injectedObject.cePos(pos); + injectedObject.setActive(true); + CoreReflections.varHandle$PalettedContainer$data.setVolatile(injectedObject, CoreReflections.varHandle$PalettedContainer$data.get(container)); + CoreReflections.field$LevelChunkSection$states.set(targetSection, injectedObject); + } else { + holder.ceChunk(chunk); + holder.ceSection(ceSection); + holder.cePos(pos); + holder.setActive(true); + } + } else { + if (!(targetSection instanceof InjectedHolder.Section holder)) { + InjectedHolder.Section injectedObject; + if (Config.fastInjection()) { + injectedObject = FastNMS.INSTANCE.createInjectedLevelChunkSectionHolder(targetSection); + } else { + injectedObject = (InjectedHolder.Section) constructor$InjectedLevelChunkSection.invoke( + FastNMS.INSTANCE.field$LevelChunkSection$states(targetSection), FastNMS.INSTANCE.field$LevelChunkSection$biomes(targetSection)); + } + injectedObject.ceChunk(chunk); + injectedObject.ceSection(ceSection); + injectedObject.cePos(pos); + injectedObject.setActive(true); + callback.accept(injectedObject); + } else { + holder.ceChunk(chunk); + holder.ceSection(ceSection); + holder.cePos(pos); + holder.setActive(true); + } + } + } catch (Throwable e) { + CraftEngine.instance().logger().severe("Failed to inject chunk section " + pos, e); + } + } + + public static boolean isSectionInjected(Object section) { + if (Config.injectionTarget()) { + Object container = FastNMS.INSTANCE.field$LevelChunkSection$states(section); + return container instanceof InjectedHolder.Palette; + } else { + return section instanceof InjectedHolder.Section; + } + } + + public synchronized static Object uninjectLevelChunkSection(Object section) { + if (Config.injectionTarget()) { + Object states = FastNMS.INSTANCE.field$LevelChunkSection$states(section); + if (states instanceof InjectedHolder.Palette holder) { + holder.setActive(false); + } + } else { + if (section instanceof InjectedHolder.Section holder) { + holder.setActive(false); + } + } + return section; + } + + + public static class SetBlockStateInterceptor { + public static final SetBlockStateInterceptor INSTANCE = new SetBlockStateInterceptor(); + + @RuntimeType + public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable superMethod) throws Exception { + InjectedHolder.Section holder = (InjectedHolder.Section) thisObj; + int x = (int) args[0]; + int y = (int) args[1]; + int z = (int) args[2]; + Object newState = args[3]; + Object previousState = superMethod.call(); + if (holder.isActive()) { + compareAndUpdateBlockState(x, y, z, newState, previousState, holder); + } + return previousState; + } + } + + public static class GetAndSetInterceptor { + public static final GetAndSetInterceptor INSTANCE = new GetAndSetInterceptor(); + + @RuntimeType + public Object intercept(@This Object thisObj, @AllArguments Object[] args) { + InjectedHolder.Palette holder = (InjectedHolder.Palette) thisObj; + Object targetStates = holder.target(); + int x = (int) args[0]; + int y = (int) args[1]; + int z = (int) args[2]; + Object newState = args[3]; + Object previousState = FastNMS.INSTANCE.method$PalettedContainer$getAndSet(targetStates, x, y, z, newState); + if (holder.isActive()) { + compareAndUpdateBlockState(x, y, z, newState, previousState, holder); + } + return previousState; + } + } + + protected static void compareAndUpdateBlockState(int x, int y, int z, Object newState, Object previousState, InjectedHolder holder) { + try { + int stateId = BlockStateUtils.blockStateToId(newState); + CESection section = holder.ceSection(); + // 如果是原版方块 + if (BlockStateUtils.isVanillaBlock(stateId)) { + // 那么应该情况自定义块 + ImmutableBlockState previous = section.setBlockState(x, y, z, EmptyBlock.STATE); + // 如果先前不是空气则标记 + if (!previous.isEmpty()) { + holder.ceChunk().setDirty(true); + if (Config.enableLightSystem()) { + updateLightIfChanged(holder, previousState, newState, newState, x, y, z); + } + } + } else { + ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); + ImmutableBlockState previousImmutableBlockState = section.setBlockState(x, y, z, immutableBlockState); + if (previousImmutableBlockState == immutableBlockState) return; + holder.ceChunk().setDirty(true); + // 如果新方块的光照属性和客户端认为的不同 + if (Config.enableLightSystem() && !immutableBlockState.isEmpty()) { + updateLightIfChanged(holder, previousState, immutableBlockState.vanillaBlockState().handle(), newState, x, y, z); + } + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to intercept setBlockState", e); + } + } + + protected static void updateLightIfChanged(@This InjectedHolder thisObj, Object oldServerSideState, Object clientSideState, Object serverSideState, int x, int y, int z) { + CEWorld world = thisObj.ceChunk().world(); + Object blockPos = LocationUtils.toBlockPos(x, y, z); + Object serverWorld = world.world().serverWorld(); + if (clientSideState != serverSideState && FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(clientSideState, serverSideState, serverWorld, blockPos)) { + SectionPos sectionPos = thisObj.cePos(); + List pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15); + world.sectionLightUpdated(pos); + return; + } + if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(oldServerSideState, serverSideState, serverWorld, blockPos)) { + SectionPos sectionPos = thisObj.cePos(); + List pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15); + world.sectionLightUpdated(pos); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 3dc30dea0..0e6a33c6e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -67,8 +67,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes C2S_BYTE_BUFFER_PACKET_HANDLERS[id] = function; } - private final BiConsumer packetConsumer; - private final BiConsumer> packetsConsumer; + private final BiConsumer packetConsumer; + private final BiConsumer> packetsConsumer; private final BiConsumer immediatePacketConsumer; private final BiConsumer> immediatePacketsConsumer; private final BukkitCraftEngine plugin; @@ -100,7 +100,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes hasViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null; this.plugin = plugin; // set up packet id - this.packetIds = setupPacketIds(); + this.packetIds = VersionHelper.isOrAbove1_20_5() ? new PacketIds1_20_5() : new PacketIds1_20(); // register packet handlers this.registerPacketHandlers(); // set up packet senders @@ -114,6 +114,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); this.immediatePacketConsumer.accept(channel, bundle); }; + // todo 可以删除吗 // set up mod channel this.plugin.javaPlugin().getServer().getMessenger().registerIncomingPluginChannel(this.plugin.javaPlugin(), MOD_CHANNEL, this); this.plugin.javaPlugin().getServer().getMessenger().registerOutgoingPluginChannel(this.plugin.javaPlugin(), MOD_CHANNEL); @@ -132,15 +133,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes }, (object) -> {}); CoreReflections.field$ServerConnectionListener$channels.set(serverConnection, monitor); } catch (ReflectiveOperationException e) { - this.plugin.logger().warn("Failed to init server connection", e); - } - } - - private PacketIds setupPacketIds() { - if (VersionHelper.isOrAbove1_20_5()) { - return new PacketIds1_20_5(); - } else { - return new PacketIds1_20(); + throw new RuntimeException("Failed to init server connection", e); } } @@ -249,7 +242,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes @Override public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] message) { if (channel.equals(VIA_CHANNEL)) { - BukkitServerPlayer user = plugin.adapt(player); + BukkitServerPlayer user = this.plugin.adapt(player); if (user != null) { JsonObject payload = GsonHelper.get().fromJson(new String(message), JsonObject.class); int version = payload.get("version").getAsInt(); @@ -260,37 +253,37 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes @Override public void init() { - Bukkit.getPluginManager().registerEvents(this, plugin.javaPlugin()); + Bukkit.getPluginManager().registerEvents(this, this.plugin.javaPlugin()); } @Override public void disable() { HandlerList.unregisterAll(this); - for (Channel channel : injectedChannels) { + for (Channel channel : this.injectedChannels) { uninjectServerChannel(channel); } for (Player player : Bukkit.getOnlinePlayers()) { handleDisconnection(getChannel(player)); } - injectedChannels.clear(); + this.injectedChannels.clear(); } @Override public void setUser(Channel channel, NetWorkUser user) { ChannelPipeline pipeline = channel.pipeline(); - users.put(pipeline, (BukkitServerPlayer) user); + this.users.put(pipeline, (BukkitServerPlayer) user); } @Override public NetWorkUser getUser(Channel channel) { ChannelPipeline pipeline = channel.pipeline(); - return users.get(pipeline); + return this.users.get(pipeline); } @Override public NetWorkUser removeUser(Channel channel) { ChannelPipeline pipeline = channel.pipeline(); - return users.remove(pipeline); + return this.users.remove(pipeline); } @Override @@ -303,21 +296,13 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } public NetWorkUser getOnlineUser(Player player) { - return onlineUsers.get(player.getUniqueId()); + return this.onlineUsers.get(player.getUniqueId()); } public Channel getChannel(Player player) { return (Channel) FastNMS.INSTANCE.field$Player$connection$connection$channel(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)); } - public void sendPacket(@NotNull Player player, @NotNull Object packet) { - try { - this.immediatePacketConsumer.accept(getUser(player).nettyChannel(), packet); - } catch (Exception e) { - this.plugin.logger().warn("Failed to send packet", e); - } - } - @Override public void sendPacket(@NotNull NetWorkUser player, Object packet, boolean immediately) { if (immediately) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index e64daf64a..4e9c0a759 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -21,7 +21,7 @@ import net.momirealms.craftengine.bukkit.item.behavior.FurnitureItemBehavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.pack.BukkitPackManager; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; +import net.momirealms.craftengine.bukkit.plugin.injector.ProtectedFieldVisitor; import net.momirealms.craftengine.bukkit.plugin.network.handler.*; import net.momirealms.craftengine.bukkit.plugin.network.payload.DiscardedPayload; import net.momirealms.craftengine.bukkit.plugin.network.payload.NetWorkDataTypes; @@ -2289,7 +2289,7 @@ public class PacketConsumers { public static final TriConsumer MOVE_POS_ENTITY = (user, event, packet) -> { try { - int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet); + int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet); if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { event.setCancelled(true); } @@ -2300,7 +2300,7 @@ public class PacketConsumers { public static final TriConsumer MOVE_POS_AND_ROTATE_ENTITY = (user, event, packet) -> { try { - int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet); + int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet); EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); if (handler != null) { handler.handleMoveAndRotate(user, event, packet); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java index 97b41d4cf..c936562f8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.bukkit.plugin.network.handler; import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; +import net.momirealms.craftengine.bukkit.plugin.injector.ProtectedFieldVisitor; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; import net.momirealms.craftengine.core.entity.projectile.CustomProjectile; import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta; @@ -56,7 +56,7 @@ public class ProjectilePacketHandler implements EntityPacketHandler { @Override public void handleMoveAndRotate(NetWorkUser user, NMSPacketEvent event, Object packet) { - int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet); + int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet); event.replacePacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( this.cachedPacket, convertCustomProjectileMovePacket(packet, entityId) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 5e326bf38..b1ce6b3ae 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -681,7 +681,7 @@ public class BukkitServerPlayer extends Player { double d1 = (double) hitPos.y() - otherLocation.getY(); double d2 = (double) hitPos.z() - otherLocation.getZ(); if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D) { - plugin.networkManager().sendPacket(other, packet); + this.plugin.networkManager().sendPacket(this.plugin.adapt(other), packet); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java index 9d49007d4..dbe3e5e5d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.util; import com.mojang.datafixers.util.Pair; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; @@ -27,22 +28,6 @@ public final class PlayerUtils { private PlayerUtils() { } - public static void resendItemInHand(@NotNull final Player player) { - ItemStack itemInHand = player.getInventory().getItemInMainHand(); - if (ItemUtils.isEmpty(itemInHand)) return; - Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player); - try { - Object inventoryMenu = CoreReflections.field$Player$inventoryMenu.get(serverPlayer); - int containerId = CoreReflections.field$AbstractContainerMenu$containerId.getInt(inventoryMenu); - int heldItemSlot = player.getInventory().getHeldItemSlot(); - int stateId = (int) CoreReflections.method$AbstractContainerMenu$incrementStateId.invoke(inventoryMenu); - Object packet = NetworkReflections.constructor$ClientboundContainerSetSlotPacket.newInstance(containerId, stateId, heldItemSlot + 36, FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemInHand)); - BukkitCraftEngine.instance().networkManager().sendPacket(player, packet); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to resend item in hand", e); - } - } - public static void dropItem(@NotNull Player player, @NotNull ItemStack itemStack, boolean retainOwnership, boolean noPickUpDelay, boolean throwRandomly) { requireNonNull(player, "player"); requireNonNull(itemStack, "itemStack"); @@ -185,7 +170,7 @@ public final class PlayerUtils { packets.add(packet3); Object bundlePacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); - BukkitNetworkManager.instance().sendPacket(player, bundlePacket); + BukkitNetworkManager.instance().sendPacket(BukkitAdaptors.adapt(player), bundlePacket); } catch (ReflectiveOperationException e) { BukkitCraftEngine.instance().logger().warn("Failed to send totem animation"); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java index 29923ec10..82931530a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java @@ -2,7 +2,8 @@ package net.momirealms.craftengine.bukkit.world; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; +import net.momirealms.craftengine.bukkit.plugin.injector.RecipeInjector; +import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.block.ImmutableBlockState; @@ -291,7 +292,7 @@ public class BukkitWorldManager implements WorldManager, Listener { for (int i = 0; i < ceSections.length; i++) { CESection ceSection = ceSections[i]; Object section = sections[i]; - BukkitInjector.uninjectLevelChunkSection(section); + WorldStorageInjector.uninjectLevelChunkSection(section); if (Config.restoreVanillaBlocks()) { if (!ceSection.statesContainer().isEmpty()) { for (int x = 0; x < 16; x++) { @@ -394,14 +395,14 @@ public class BukkitWorldManager implements WorldManager, Listener { } } int finalI = i; - BukkitInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), + WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), (injected) -> sections[finalI] = injected); } if (Config.enableRecipeSystem()) { @SuppressWarnings("unchecked") Map blockEntities = (Map) FastNMS.INSTANCE.field$ChunkAccess$blockEntities(levelChunk); for (Object blockEntity : blockEntities.values()) { - BukkitInjector.injectCookingBlockEntity(blockEntity); + RecipeInjector.injectCookingBlockEntity(blockEntity); } } } catch (ReflectiveOperationException e) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/StatePredicate.java b/core/src/main/java/net/momirealms/craftengine/core/block/StatePredicate.java deleted file mode 100644 index 02c72350a..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/block/StatePredicate.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.craftengine.core.block; - -public class StatePredicate { - private static Object TRUE; - private static Object FALSE; - - public static void init(Object alwaysTrue, Object alwaysFalse) { - TRUE = alwaysTrue; - FALSE = alwaysFalse; - } - - public static Object alwaysTrue() { - return TRUE; - } - - public static Object alwaysFalse() { - return FALSE; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java index 1ad85c41e..e02d0ff53 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import static java.util.Objects.requireNonNull; public class VersionHelper { + // todo 在跨平台时候,将其设计到平台实现 private static final Class clazz$DetectedVersion = requireNonNull( ReflectionUtils.getClazz("net.minecraft.DetectedVersion", "net.minecraft.MinecraftVersion")); private static final Class clazz$WorldVersion = requireNonNull( diff --git a/gradle.properties b/gradle.properties index 392555993..523c8905c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -51,7 +51,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.17 -nms_helper_version=0.66.10 +nms_helper_version=0.66.11 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23