mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-22 16:39:28 +00:00
拆分注入代码
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.injector;
|
||||
|
||||
public interface InternalFieldAccessor {
|
||||
public interface FieldAccessor {
|
||||
|
||||
int field$ClientboundMoveEntityPacket$entityId(Object packet);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user