fabric-loader
fabric-loader copied to clipboard
EntityDamageEvent
This is a floader issue and not a fapi issue I swear: Gib mass ASM api
Luckily when you want to hook into a Block's method you can usually just inject into the equivalent BlockState method because nothing extends it (well, I hope so :p). However this is not always the case, and especially not for Block's not so grid aligned buddy, the entity.
The problem with entites is that their methods can be overriden without the implementation calling super, and sometimes they are programmed with the assumption the super method hasn't been tampered with. And to make matters worse, they're important methods likely to be called by other mods, and all over the mc codebase.
Take for example the infamous Entity#damage method.
As mentioned, this method is called all over the mc codebase, and by mods, so redirecting every call to it for a general EntityDamageEvent isn't an option. (With mixin or mass ASM)
The method is also overriden by multiple classes, not all of which call super, which means you can't just inject into Entity#damage, as when the method is overriden by a mod (or mc itself) and the mod doesn't call super, the event will never fire.
And tamable entities iirc will stop loving in their entity damage method, but call super after they've stopped loving, so cancelling head on Entity#damage is futile anyways.
The only way around this issue is mass-ASM. I don't know the actual terminology, but it just means the ability for mods to transform all classes loaded by knot.
The ASM is simple enough, rename Entity#damage to Entity#damage$internal, and redirect all super calls and overriding methods to reflect that change. Then create a new Entity#damage method that's public and final, which throws the event, that way all invocations of Entity#damage would go through this method first, allowing us to reliably cancel and listen to it.
The api should also include a global pool for classnodes (to verify the superclass), which are run through mixin, transformers, and AWs. Ideally knot should be able to just query the pool and write whatever comes back when it's asked to load a class so we know the classnode were getting is what's going to be written. It's also nice for performance since you can avoid reading and writing the same node multiple times. Though you'd need some way of preventing people from meddling with classnodes in the global pool.
It should also expose a raw byte array version for people who use bad bytecode libraries (aka anything that isn't ASM or bytebuddy) or binary patches. But first class support for ASM should exist because 1) mixin uses it 2) you should use ASM iritat 3) the more redundant bytecode reading we can remove the better
It'll also need caching integration or smth
For fune pun it should be called mASsM
An explanation without the memelord.
Some events such as EntityDamageEvents
are not possible without injecting into about 30 classes with mixin. All mods also do not have this injection by default if they extend the method and call super.
The other issue is side effects of damage. For example, tamable entities will stop loving before the super
call occurs. Also logic such as armor durability, shield durability, etc is a side effect that occurs before the resulting setHealth
call at the end of the method once damage
returns false.
The ASM is simple enough, rename Entity#damage to Entity#damage$internal, and redirect all super calls and overriding methods to reflect that change. Then create a new Entity#damage method that's public and final, which throws the event, that way all invocations of Entity#damage would go through this method first, allowing us to reliably cancel and listen to it.
The specifics of this would require being able to determine superclasses via a global pool of classes.