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:
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user