Add initial AccessWidener

This commit is contained in:
2026-06-11 12:22:44 +02:00
parent e176b3bca8
commit 786257ad0e
15 changed files with 979 additions and 6 deletions
@@ -0,0 +1,117 @@
package de.steamwar.core;
import java.lang.reflect.Method;
import java.nio.file.Path;
import java.util.logging.Logger;
/**
* Public API for Paper plugins that want to attach the access widener agent.
*
* <h2>Usage</h2>
* Call {@link #ensureAttached(Path)} from your plugin's {@code onLoad()} method
* (not {@code onEnable()} — onLoad fires before classes start loading):
*
* <pre>{@code
* public class MyPlugin extends JavaPlugin {
* @Override
* public void onLoad() {
* try {
* AccessWidenerAttacher.ensureAttached(getDataFolder());
* } catch (Exception e) {
* getSLF4JLogger().error("Failed to attach access widener agent", e);
* }
* }
* }
* }</pre>
*
* <h2>Resource file</h2>
* Drop a {@code plugin.accesswidener} file in your plugin's resources:
*
* <pre>
* accessWidener v2 named
*
* accessible method net/minecraft/server/level/ServerPlayer getStats ()V
* mutable field net/minecraft/world/entity/Entity id I
* </pre>
*
* The agent discovers it automatically via your plugin's ClassLoader.
*/
public final class AccessWidenerAttacher {
private static final Logger LOG = Logger.getLogger("AccessWidenerAttacher");
private static final Object LOCK = new Object();
private static volatile boolean attached = false;
private AccessWidenerAttacher() {}
/**
* Attaches the access widener agent to the running JVM if it has not already
* been attached. Safe to call from multiple plugins — the agent is only
* attached once.
*
* @param agentJar the agentJar
* @throws AccessWidenerException if the agent could not be attached
*/
public static void ensureAttached(Path agentJar) throws AccessWidenerException {
if (attached) return; // fast path — no locking needed after first attach
synchronized (LOCK) {
if (attached) return; // double-checked
try {
attachAgent(agentJar);
attached = true;
LOG.info("[AccessWidener] Agent attached successfully.");
} catch (Exception e) {
throw new AccessWidenerException("Failed to attach access widener agent", e);
}
}
}
/** Returns true if the agent has been successfully attached. */
public static boolean isAttached() {
return attached;
}
private static void attachAgent(Path agentJar) throws Exception {
// Verify the Attach API is available before trying
try {
Class.forName("com.sun.tools.attach.VirtualMachine");
} catch (ClassNotFoundException e) {
throw new AccessWidenerException(
"The JDK Attach API is not available. "
+ "Make sure you are running on a JDK (not a JRE) "
+ "and add '--add-opens jdk.attach/sun.tools.attach=ALL-UNNAMED' "
+ "to your JVM flags if on Java 9+.", e);
}
String pid = String.valueOf(ProcessHandle.current().pid());
// Use reflection so the Attach API is not a hard compile-time dependency
Class<?> vmClass = Class.forName("com.sun.tools.attach.VirtualMachine");
Method attachMethod = vmClass.getMethod("attach", String.class);
Method loadAgentMethod = vmClass.getMethod("loadAgent", String.class);
Method detachMethod = vmClass.getMethod("detach");
Object vm = attachMethod.invoke(null, pid);
try {
loadAgentMethod.invoke(vm, agentJar.toAbsolutePath().toString());
} finally {
detachMethod.invoke(vm);
}
}
// -------------------------------------------------------------------------
// Exception type
// -------------------------------------------------------------------------
public static class AccessWidenerException extends Exception {
public AccessWidenerException(String message, Throwable cause) {
super(message, cause);
}
public AccessWidenerException(String message) {
super(message);
}
}
}