EventHooker is a coremod that can be used to painlessly inject function-based event hooks into arbitrary methods.

To use it, you just need to do two things:

1. Create a file named injector_methods.txt in your resources folder (next to mcmod.info) (really, it just needs to end up at the root of your mod jar)
   containing a list of method definitions that you wish to inject into.
   Each definition should be on one line. Here is an example definition:
   
   net/minecraft/entity/item/EntityItem, attackEntityFrom, func_70097_a, true, Z, Lnet/minecraft/util/DamageSource;, F
   
   It has six distinct parts:
      The fully-qualified name of the class that contains your desired method.                               (net/minecraft/entity/item/EntityItem)
      The deobfuscated name of the method that you wish to inject into.                                      (attackEntityFrom)
      The SRG name of the method you wish to inject into.                                                    (func_70097_a)
      A boolean, either true or false. If true, the event will be cancelable. More on that later.            (true)
      A type descriptor representing the method's return type. Use "V" for void methods.                     (Z)
      0 or more additional entries, each of which is a type descriptor representing the method's parameters. (Lnet/minecraft/util/DamageSource;,F)
   
   Each part is separated by a comma. You can have more or less whitespace between them if you want, although no newlines.
   Type descriptors are used to specify the method's return type and parameters. The descriptors for primitive values are as follows:
      "I": int
      "S": short
      "B": byte
      "C": char
      "J": long
      "F": float
      "D": double
      "Z": boolean
      "V": void      *keep in mind that void is only valid as a return type, not a parameter.
      
   In addition to the basic primitives, there are object references and arrays, although they are slightly more complicated.
   An array is specified simply by adding a "[" to the beginning of the type descriptor. Add more of them for layered arrays. 
   For example, "[[I" would be a double-layered array of ints. (I.E. int[][])
   Object references are a bit more complicated though. The are specified as follows:
      "L{Type};"
   Replace {Type} with a fully-qualified type name, such as "net/minecraft/util/DamageSource", which gives you the end result of:
      "Lnet/minecraft/util/DamageSource;"
   Finally, you can combine arrays and references together, to get things like this:
      "[Ljava/lang/String;"
   Which specifies an array of Strings.
   
   
2. Register your event-handling function(s) with the InjectionTransformer.
   You should use either registerInjectedEventHandler, or registerInjectedCancelableEventHandler, depending on whether or not
   you specified it as cancelable in the injector_methods.txt. The method parameters are as follows:
      registerInjected(Cancelable)EventHandler(String classToInject, String methodName, Class<T> returnType, 
      Injected(Cancelable)EventHandler eventHandler, (T cancelReturnValue), Class<?>... params)
      
   Note that the cancelReturnValue parameter isn't there at all if you are registering a non-cancelable handler.
   The parameters are as follows:
      classToInject: The fully-qualified name of the class that contains your desired method.
      methodName: The name of the method that you wish to inject into. It's up to you to ensure that you pass the correct value here!
                  If Launch.blackboard.get("fml.deobfuscatedEnvironment") is true, then pass the deobfuscated name. Otherwise, pass the SRG name.
      returnType: A Class representing the return type of the method. (I.E. int.class). This must be null for void return types.
      eventHandler: An object derived from Injected(Cancelable)EventHandler that will have its onEvent(Object, Object[]) method called whenever the event fires.
      cancelReturnValue: The value to return from the function if the event is canceled. If the function has a void return type, this must be null. 
                         This parameter only exists for cancelable events.
      params: A list of Classes representing the function's parameters. These cannot be null.
      
   The 'returnType' and 'params' parameters can be swapped out for String-based type descriptors like you did before, if you want.
   If you choose to do so, make sure you use "V" rather than null for the returnType.
   
   As for the Injected(Cancelable)EventHandler, simply override its
      onEvent(Object classInstance, Object[] parameters)
      
   classInstance is basically 'this' from the calling class - it's safe to cast directly to the class Type.
   Be aware that classInstance will be null if the method that the event was fired from is static.
   
   parameters is a list of Objects matching the parameters that were passed to the method.
   They are safe to directly cast to their matching parameters, but be sure to verify that they aren't null before doing so.
   
   Finally, note that if you are using a cancelable event, onEvent's return type will be a boolean, rather than zero. Return true to cancel the event,
   and prevent the calling method from executing. If the method had a non-void return type, it will return the previously specified cancelReturnValue to its caller.
   
   Note that, if multiple different mods inject a cancelable event into the same method, they will all share the event call.
   Handlers will get their onEvent calls in the order that they were registered.
   Additionally, even if one of the handlers cancels the event, all registered handlers will still get the event!
   There is no way to know if another handler has canceled the event, sorry.
   However, the last call to "registerInjectedCancelableEventHandler" decides what the value of cancelReturnValue is.

That's about it. Have fun coremodding without having to touch any actual ASM! One thing to keep in mind though: each injection does add
a minor performance cost to the method being injected into. So if possible, avoid injecting into very frequently-called methods.
This is slightly worse for cancelable events, so don't use them if you don't need to.

Oh, and of course: I can't be blamed for any incompatibilities or other problems caused by this. If it's a fault in EventHooker itself, then I will try to fix it,
but keep in mind that all this coremod actually does on its own is provide an API for hooking events with ASM. It doesn't do much on its own.

Also, I learned ASM and made this all in one day, (18 near-continuous hours actually), so there will probably be bugs, at least for a while.
Please let me know if you are certain that an error is the fault of EventHooker.