9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-22 16:39:28 +00:00

拆分注入代码

This commit is contained in:
XiaoMoMi
2025-06-01 23:05:51 +08:00
parent 0ef1b859c1
commit afa01980c3
23 changed files with 731 additions and 819 deletions

View File

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

View File

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

View File

@@ -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()) {

View File

@@ -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

View File

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

View File

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

View File

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

View File

@@ -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<CommandS
int i = 0;
Sender sender = plugin().senderFactory().wrap(player);
for (Object section : sections) {
sender.sendMessage(Component.text("Section #" + i + ": " + BukkitInjector.isSectionInjected(section)));
sender.sendMessage(Component.text("Section #" + i + ": " + WorldStorageInjector.isSectionInjected(section)));
i++;
}
});

View File

@@ -32,7 +32,6 @@ import java.util.concurrent.Callable;
public final class BlockGenerator {
private static final BukkitBlockShape STONE_SHAPE =
new BukkitBlockShape(MBlocks.STONE$defaultState, MBlocks.STONE$defaultState);
private static Class<?> 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;

View File

@@ -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<Object> 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<Pair<Object, Object>> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe);
if (optionalRecipe.isPresent()) {
Pair<Object, Object> pair = optionalRecipe.get();
Object resourceLocation = pair.getFirst();
Key recipeId = Key.of(resourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack;
List<Object> items;
if (type == MRecipeTypes.CAMPFIRE_COOKING) {
items = (List<Object>) CoreReflections.field$SimpleContainer$items.get(args[0]);
} else {
items = (List<Object>) 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<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) 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<Object> optionalRecipe = (Optional<Object>) 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<Object> items;
if (type == MRecipeTypes.CAMPFIRE_COOKING) {
items = (List<Object>) CoreReflections.field$SimpleContainer$items.get(args[0]);
} else {
items = (List<Object>) 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<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) 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<Object> optionalRecipe = (Optional<Object>) 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<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) 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<Object> optionalRecipe = (Optional<Object>) 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<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) 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<Object> 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<SectionPos> 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<SectionPos> pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15);
world.sectionLightUpdated(pos);
}
}
}

View File

@@ -1,6 +1,6 @@
package net.momirealms.craftengine.bukkit.plugin.injector;
public interface InternalFieldAccessor {
public interface FieldAccessor {
int field$ClientboundMoveEntityPacket$entityId(Object packet);
}

View File

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

View File

@@ -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<Pair<Object, Object>> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe);
if (optionalRecipe.isPresent()) {
Pair<Object, Object> pair = optionalRecipe.get();
Object resourceLocation = pair.getFirst();
Key recipeId = Key.of(resourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack;
List<Object> items;
if (type == MRecipeTypes.CAMPFIRE_COOKING) {
items = (List<Object>) CoreReflections.field$SimpleContainer$items.get(args[0]);
} else {
items = (List<Object>) 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<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) 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<Object> optionalRecipe = (Optional<Object>) 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<Object> items;
if (type == MRecipeTypes.CAMPFIRE_COOKING) {
items = (List<Object>) CoreReflections.field$SimpleContainer$items.get(args[0]);
} else {
items = (List<Object>) 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<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) 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<Object> optionalRecipe = (Optional<Object>) 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<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) 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<Object> optionalRecipe = (Optional<Object>) 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<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) 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();
}
}
}
}

View File

@@ -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<Object> 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<Object> 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<SectionPos> 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<SectionPos> pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15);
world.sectionLightUpdated(pos);
}
}
}

View File

@@ -67,8 +67,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
C2S_BYTE_BUFFER_PACKET_HANDLERS[id] = function;
}
private final BiConsumer<Object, Object> packetConsumer;
private final BiConsumer<Object, List<Object>> packetsConsumer;
private final BiConsumer<ChannelHandler, Object> packetConsumer;
private final BiConsumer<ChannelHandler, List<Object>> packetsConsumer;
private final BiConsumer<Channel, Object> immediatePacketConsumer;
private final BiConsumer<Channel, List<Object>> 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) {

View File

@@ -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<NetWorkUser, NMSPacketEvent, Object> 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<NetWorkUser, NMSPacketEvent, Object> 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);

View File

@@ -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)

View File

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

View File

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

View File

@@ -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<Object, Object> blockEntities = (Map<Object, Object>) FastNMS.INSTANCE.field$ChunkAccess$blockEntities(levelChunk);
for (Object blockEntity : blockEntities.values()) {
BukkitInjector.injectCookingBlockEntity(blockEntity);
RecipeInjector.injectCookingBlockEntity(blockEntity);
}
}
} catch (ReflectiveOperationException e) {

View File

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

View File

@@ -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(

View File

@@ -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