9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-30 12:29:15 +00:00

使用javaagent注册方块

This commit is contained in:
XiaoMoMi
2025-06-02 17:47:27 +08:00
parent 3ece1a9bb4
commit 2fd2323412
10 changed files with 206 additions and 69 deletions

View File

@@ -84,6 +84,8 @@ public class BukkitBlockManager extends AbstractBlockManager {
// cached tag packet
protected Object cachedUpdateTagsPacket;
private final List<Tuple<Object, Key, Boolean>> blocksToDeceive = new ArrayList<>();
public BukkitBlockManager(BukkitCraftEngine plugin) {
super(plugin);
instance = this;
@@ -100,6 +102,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
@Override
public void init() {
this.initMirrorRegistry();
this.deceiveBukkit();
boolean enableNoteBlocks = this.blockAppearanceArranger.containsKey(BlockKeys.NOTE_BLOCK);
this.blockEventListener = new BlockEventListener(plugin, this, enableNoteBlocks);
if (enableNoteBlocks) {
@@ -753,7 +756,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
builder2.put(stateId, blockHolder);
stateIds.add(stateId);
deceiveBukkit(newRealBlock, clientSideBlockType, isNoteBlock);
this.blocksToDeceive.add(Tuple.of(newRealBlock, clientSideBlockType, isNoteBlock));
order.add(realBlockKey);
counter++;
}
@@ -794,9 +797,20 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
@SuppressWarnings("unchecked")
private void deceiveBukkit(Object newBlock, Key replacedBlock, boolean isNoteBlock) throws IllegalAccessException {
Map<Object, Material> magicMap = (Map<Object, Material>) CraftBukkitReflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null);
Map<Material, Object> factories = (Map<Material, Object>) CraftBukkitReflections.field$CraftBlockStates$FACTORIES.get(null);
private void deceiveBukkit() {
try {
Map<Object, Material> magicMap = (Map<Object, Material>) CraftBukkitReflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null);
Map<Material, Object> factories = (Map<Material, Object>) CraftBukkitReflections.field$CraftBlockStates$FACTORIES.get(null);
for (Tuple<Object, Key, Boolean> tuple : this.blocksToDeceive) {
deceiveBukkit(tuple.left(), tuple.mid(), tuple.right(), magicMap, factories);
}
this.blocksToDeceive.clear();
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to deceive bukkit", e);
}
}
private void deceiveBukkit(Object newBlock, Key replacedBlock, boolean isNoteBlock, Map<Object, Material> magicMap, Map<Material, Object> factories) {
if (isNoteBlock) {
magicMap.put(newBlock, Material.STONE);
} else {

View File

@@ -117,7 +117,8 @@ public class BukkitCraftEngine extends CraftEngine {
this.config = new Config(this);
}
protected void injectRegistries() {
public void injectRegistries() {
if (super.blockManager != null) return;
try {
BlockGenerator.init();
super.blockManager = new BukkitBlockManager(this);

View File

@@ -0,0 +1,41 @@
package net.momirealms.craftengine.bukkit.plugin.agent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class BlocksAgent {
public static void agentmain(String args, Instrumentation instrumentation) {
new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.type(ElementMatchers.named("net.minecraft.server.Bootstrap")
.or(ElementMatchers.named("net.minecraft.server.DispenserRegistry")))
.transform((builder, typeDescription, classLoader, module, protectionDomain) ->
builder.visit(Advice.to(BlocksAdvice.class)
.on(ElementMatchers.named("validate")
.or(ElementMatchers.named("c")))))
.installOn(instrumentation);
}
public static class BlocksAdvice {
@Advice.OnMethodExit
public static void onExit() {
try {
Class<?> holder = Class.forName("net.momirealms.craftengine.bukkit.plugin.agent.PluginHolder");
Field field = holder.getField("plugin");
Object plugin = field.get(null);
Method injectRegistries = plugin.getClass().getMethod("injectRegistries");
injectRegistries.invoke(plugin);
} catch (Exception e) {
throw new RuntimeException("Failed to inject registries", e);
}
}
}
}

View File

@@ -0,0 +1,54 @@
package net.momirealms.craftengine.bukkit.plugin.agent;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.jar.asm.Opcodes;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class RuntimePatcher {
public static void patch(BukkitCraftEngine plugin) throws ReflectiveOperationException {
Class<?> holderClass = new ByteBuddy()
.subclass(Object.class)
.name("net.momirealms.craftengine.bukkit.plugin.agent.PluginHolder")
.defineField("plugin", Object.class, Modifier.PUBLIC | Modifier.STATIC)
.defineMethod("setPlugin", void.class, Modifier.PUBLIC | Modifier.STATIC)
.withParameters(Object.class)
.intercept(new Implementation() {
@Override
public @NotNull InstrumentedType prepare(@NotNull InstrumentedType instrumentedType) {
return instrumentedType;
}
@Override
public @NotNull ByteCodeAppender appender(@NotNull Target implementationTarget) {
return (methodVisitor, implementationContext, instrumentedMethod) -> {
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.PUTSTATIC,
"net/momirealms/craftengine/bukkit/plugin/agent/PluginHolder",
"plugin",
"Ljava/lang/Object;");
methodVisitor.visitInsn(Opcodes.RETURN);
return new ByteCodeAppender.Size(1, 1);
};
}
})
.make()
.load(Bukkit.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
Method setPlugin = holderClass.getMethod("setPlugin", Object.class);
setPlugin.invoke(null, plugin);
Instrumentation inst = ByteBuddyAgent.install();
BlocksAgent.agentmain(null, inst);
}
}

View File

@@ -14,52 +14,27 @@ public final class BukkitReflectionUtils {
private static final String PREFIX_CRAFTBUKKIT = "org.bukkit.craftbukkit";
private static final String CRAFT_SERVER = "CraftServer";
private static final String CB_PKG_VERSION;
public static final int MAJOR_REVISION;
private BukkitReflectionUtils() {}
static {
final Class<?> serverClass;
if (Bukkit.getServer() == null) {
// Paper plugin Bootstrapper 1.20.6+
serverClass = Objects.requireNonNull(ReflectionUtils.getClazz("org.bukkit.craftbukkit.CraftServer"));
if (VersionHelper.isMojmap()) {
CB_PKG_VERSION = ".";
} else {
serverClass = Bukkit.getServer().getClass();
}
final String pkg = serverClass.getPackage().getName();
final String nmsVersion = pkg.substring(pkg.lastIndexOf(".") + 1);
if (!nmsVersion.contains("_")) {
int fallbackVersion = -1;
if (Bukkit.getServer() != null) {
try {
final Method getMinecraftVersion = serverClass.getDeclaredMethod("getMinecraftVersion");
fallbackVersion = Integer.parseInt(getMinecraftVersion.invoke(Bukkit.getServer()).toString().split("\\.")[1]);
} catch (final Exception ignored) {
}
} else {
// Paper plugin bootstrapper 1.20.6+
try {
final Class<?> sharedConstants = Objects.requireNonNull(ReflectionUtils.getClazz("net.minecraft.SharedConstants"));
final Method getCurrentVersion = sharedConstants.getDeclaredMethod("getCurrentVersion");
final Object currentVersion = getCurrentVersion.invoke(null);
final Method getName = currentVersion.getClass().getDeclaredMethod("getName");
final String versionName = (String) getName.invoke(currentVersion);
String name;
label: {
for (int i = 0; i <= VersionHelper.minorVersion(); i++) {
try {
fallbackVersion = Integer.parseInt(versionName.split("\\.")[1]);
} catch (final Exception ignored) {
name = ".v1_" + VersionHelper.majorVersion() + "_R" + i + ".";
Class.forName(PREFIX_CRAFTBUKKIT + name + CRAFT_SERVER);
break label;
} catch (ClassNotFoundException ignored) {
}
} catch (final ReflectiveOperationException e) {
throw new RuntimeException(e);
}
throw new RuntimeException("Could not find CraftServer version");
}
MAJOR_REVISION = fallbackVersion;
} else {
MAJOR_REVISION = Integer.parseInt(nmsVersion.split("_")[1]);
CB_PKG_VERSION = name;
}
String name = serverClass.getName();
name = name.substring(PREFIX_CRAFTBUKKIT.length());
name = name.substring(0, name.length() - CRAFT_SERVER.length());
CB_PKG_VERSION = name;
}
public static String assembleCBClass(String className) {