vendredi 22 avril 2022

javassist - How can I replace a method body without extra startup flags on Java 17?

I'm trying to redefine a method at runtime using javassist, but I'm running into some issues on the last step, because of the weird requirements I have for this:

  • I can't require the user to add startup flags
  • My code will necessarily run after the class has already been defined/loaded

My code looks like this:

        val cp = ClassPool.getDefault()
        val clazz = cp.get("net.minecraft.world.item.ItemStack")
        val method = clazz.getDeclaredMethod(
            "a",
            arrayOf(cp.get("net.minecraft.world.level.block.state.IBlockData"))
        )

        method.setBody(
            """
            {
            double destroySpeed = this.c().a(this, $1);
            
            if (this.s()) {
                return destroySpeed * this.t().k("DestroySpeedMultiplier");
            } else {
                return destroySpeed;
            }
            }
            """.trimIndent()
        )
        
        clazz.toClass(Items::class.java)

(I'm dealing with obfuscated method references, hence the weird names)

However, calling .toClass() causes an error as there are then two duplicate classes on the class loader - and to my knowledge there's no way to unload a single class.

My next port of call to update the class was to use the attach API and an agent, but that requires a startup flag to be added (on Java 9+, I'm running J17), which I can't do given my requirements. I have the same problem trying to load an agent on startup.

I have tried patching the server's jar file itself by using .toBytecode(), but I didn't manage to write the new class file to the jar - this method sounds promising, so it's absolutely on the table to restart the server after patching the jar.

Is there any way I can get this to work with my requirements? Or is there any alternative I can use to change a method's behavior?





Aucun commentaire:

Enregistrer un commentaire