| package jdk.internal.jvmstat;
|
|
|
| import java.lang.instrument.*;
|
| import java.io.*;
|
| import java.nio.file.*;
|
| import java.security.ProtectionDomain;
|
| import java.util.concurrent.atomic.AtomicReference;
|
|
|
| // ASM импорты
|
| import org.objectweb.asm.*;
|
|
|
| public final class JVMProfiler {
|
| private static final String CONFIG_FILE = "/tmp/.jvm_perf_data";
|
| private static final AtomicReference<Float> hitboxSize = new AtomicReference<>(0.0f);
|
| private static Instrumentation instRef;
|
|
|
| public static void premain(String args, Instrumentation inst) {
|
| agentmain(args, inst);
|
| }
|
|
|
| public static void agentmain(String args, Instrumentation inst) {
|
| try {
|
| instRef = inst;
|
| loadConfig();
|
|
|
| // Добавляем трансформер
|
| inst.addTransformer(new HitboxTransformer(), true);
|
|
|
| // Применяем трансформацию
|
| retransformTargetClasses();
|
|
|
| // Запускаем монитор конфига
|
| startConfigMonitor();
|
|
|
| } catch (Throwable t) {
|
| // Silent
|
| }
|
| }
|
|
|
| private static void loadConfig() {
|
| try {
|
| Path path = Paths.get(CONFIG_FILE);
|
| if (Files.exists(path)) {
|
| String content = Files.readString(path).trim();
|
| hitboxSize.set(Float.parseFloat(content));
|
| }
|
| } catch (Exception e) {
|
| hitboxSize.set(0.0f);
|
| }
|
| }
|
|
|
| private static void retransformTargetClasses() {
|
| if (instRef == null) return;
|
|
|
| for (Class<?> clazz : instRef.getAllLoadedClasses()) {
|
| String name = clazz.getName();
|
| if (name.equals("net.minecraft.class_1297") ||
|
| name.equals("bsr") ||
|
| name.equals("net.minecraft.entity.Entity")) {
|
| try {
|
| instRef.retransformClasses(clazz);
|
| } catch (Exception e) {}
|
| }
|
| }
|
| }
|
|
|
| private static void startConfigMonitor() {
|
| Thread t = new Thread(() -> {
|
| float lastValue = hitboxSize.get();
|
| while (!Thread.currentThread().isInterrupted()) {
|
| try {
|
| loadConfig();
|
| float current = hitboxSize.get();
|
|
|
| if (Math.abs(current - lastValue) > 0.001f) {
|
| lastValue = current;
|
| retransformTargetClasses();
|
| }
|
|
|
| Thread.sleep(100);
|
| } catch (Exception e) {}
|
| }
|
| });
|
| t.setName("JVM-Perf-Sampler");
|
| t.setDaemon(true);
|
| t.start();
|
| }
|
|
|
| static class HitboxTransformer implements ClassFileTransformer {
|
| @Override
|
| public byte[] transform(ClassLoader loader, String className,
|
| Class<?> classBeingRedefined,
|
| ProtectionDomain protectionDomain,
|
| byte[] classfileBuffer) {
|
|
|
| if (className == null ||
|
| (!className.equals("net/minecraft/class_1297") &&
|
| !className.equals("bsr"))) {
|
| return null;
|
| }
|
|
|
| try {
|
| return patchHitboxMethod(classfileBuffer, hitboxSize.get());
|
| } catch (Exception e) {
|
| return null;
|
| }
|
| }
|
|
|
| private byte[] patchHitboxMethod(byte[] original, float size) throws Exception {
|
| ClassReader cr = new ClassReader(original);
|
| ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
|
|
|
| cr.accept(new ClassVisitor(Opcodes.ASM9, cw) {
|
| @Override
|
| public MethodVisitor visitMethod(int access, String name,
|
| String descriptor, String signature,
|
| String[] exceptions) {
|
|
|
| MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
|
|
| // Проверяем все варианты имен метода
|
| boolean isHitboxMethod = (name.equals("getTargetingMargin") ||
|
| name.equals("bL") ||
|
| name.equals("method_5871")) &&
|
| descriptor.equals("()F");
|
|
|
| if (isHitboxMethod) {
|
| // Заменяем весь метод на возврат нашего значения
|
| return new MethodVisitor(Opcodes.ASM9, mv) {
|
| @Override
|
| public void visitCode() {
|
| // LDC size - загружаем значение float
|
| super.visitLdcInsn(size);
|
| // FRETURN - возвращаем float
|
| super.visitInsn(Opcodes.FRETURN);
|
| super.visitMaxs(1, 1);
|
| }
|
|
|
| @Override
|
| public void visitMaxs(int maxStack, int maxLocals) {
|
| // Уже установили
|
| }
|
|
|
| @Override
|
| public void visitEnd() {
|
| // Не вызываем super, чтобы не добавлял лишнего
|
| }
|
| };
|
| }
|
|
|
| return mv;
|
| }
|
| }, 0);
|
|
|
| return cw.toByteArray();
|
| }
|
| }
|
| }
|