[REL/WIP] rEventHandler - A PA engine process event framework

Discussion in 'Mod Discussions' started by elmauru, March 13, 2014.

  1. elmauru

    elmauru Member

    Messages:
    46
    Likes Received:
    27
    Compatibility(last checked):
    Gamma v 63180

    What does it do:

    The mEventHandler is a framework for tracking events. It can easily be "hooked into" by other mods without causing conflicts.
    It neatly slides between the API and Engine communication layer and offers a basic toolset for working with Process calls on all levels.

    Long Version
    If you have been toying around with the mod functionality in PA you will have noticed that the API sends a lot of asynchronous requests to the engine which make it hard to track events such as unit selection, etc.

    One way to deal with this was to hijack the API<->engine protocol and inject your own listeners via overiding app.registerWithCoherent. The problem is that you can break a LOOOOT of stuff that way, if not for yourself, then for other mods.

    This is an attempt to create a centralized process event listener which all mods can subscribe to without overwriting each other's request or spamming the system with requestAnimationFrame or setIntervals to some rather expensive functions,

    Subscribing to this event manager basically makes sure your mod gets notified AFTER all API/engine-specific updates are made so you get a reliable result and not an unspecific "PROMISE" of a result.

    Usage:
    Download and implement the thing from github here:
    https://github.com/Mauru/PA_rEventManager

    This mod is on the PA MOD Manager.

    This is how you register a event callback:
    mEvents.register_callback(message,callback);
    message is the event you are listening for (f.e. "selection")
    callback is the function you want executed when the process is complete - the callback is automatically assigned a payload by the engine, which usually holds some nifty information to work with.

    Please note that the callback is only fired ONE TIME, so if you want continous event notification you will have to re-register your listener after each callback (kinda like AnimationFrame).

    You can find all the possible handlers in ./media/alpha/live_game/live_game.js - they are designated as handler.(message) and there are a lot of useful ones. There are probably even more peppered all throughout the other files.

    PS: I renamed the thing to mEventHandler as a salute to raevn
    Last edited: March 21, 2014
    LavaSnake, Raevn and cptconundrum like this.
  2. elmauru

    elmauru Member

    Messages:
    46
    Likes Received:
    27
    Additional Notes (and some guesswork):
    Currently the EventManager injects itself in the live_game stage, because that is after any other "official" handlers are called. It would be achievable to implement it even earlier to track events during the planet creation or lobby phase or whatever rocks your boat. This is an interesting topic for discussion.
    Also interesting is that there are currently a lot of other hooks, f.e. for disconnects, timeouts and even progress data on some processes. Thats a lot of stuff to play with...

    Something else which probably needs to be integrated sooner rather than later is a way to replace your queued up requests with more up to date ones since some events such as selection notifiers can sometimes take a while for the engine to process (i.e. many units on the screen).

    Right now the event manager simply creates a list of callbacks which it happily executes one after the other regardless of any piled up events (which could POTENTIALLY create buffer overflow problems if you are performing really expensive callbacks which pile up due to lag/cpu load and then all get resolved "at once").

    I assume this is also one of the performance issues uber is currently fighting with but i am just a script kiddie.

    Finally, if you are a modder and you are currently running a lot of events which do such crazy stuff as scanning expensive functions such as ko.computed(whatever), selection(), selectionList() and the like and/or use setIntervals, - seriously take a look and see if you can use this framework to significantly speed up your mod.

    AFAICT a lot of the ko.computed functions currently don't buffer their result in any meaningful way which can potentially create a lot of engine calls which *might* be expensive.
    *ko.computed actually stores the results*
    Last edited: March 19, 2014
  3. elmauru

    elmauru Member

    Messages:
    46
    Likes Received:
    27
    [..reserved...]
  4. Raevn

    Raevn Moderator Alumni

    Messages:
    4,226
    Likes Received:
    4,324
    I suggest renaming this to eEventHandler/eEventManager, to avoid confusion ;)
    lokiCML and cptconundrum like this.
  5. elmauru

    elmauru Member

    Messages:
    46
    Likes Received:
    27
    Yeah the name is pretty much up in the air - theoretically it should be engineProcessHandler or something similarily ubiquous but the entire asynchronous communication thing is allready funky enough.
  6. cola_colin

    cola_colin Moderator Alumni

    Messages:
    12,074
    Likes Received:
    16,221
    hmm, I am feeling pretty fine just overwriting the specific handlers in the scene with my own stuff so far.
    I can't really follow about speed gains. I think one of the most craziest things my mods do is that the alertsmanager basically enables create/die events for all units a player has and filters it in the overwritten watch_list handler.
    That can be a lot of events, but even so I never really saw a real performance impact apart maybe from extrem situations like asteroid hits. The alerts manager also needs to change what the stock code from uber gets, so it can't just be added at the end of a list of listeners, it needs to put itself between incoming engine data and the UI.

    So tbh I can't think of a use for my stuff.
    thetrophysystem likes this.
  7. elmauru

    elmauru Member

    Messages:
    46
    Likes Received:
    27
    The speed gains become apparent when you deal with a lot of units on screen and still want to listen to selectionList() or selection() events for example. Any other way to track this would significantly slow down process execution or provide unreliable results.
    If you wanted to inject data into the process information for the engine/API to use, that would be doable without a problem since the eventsManager potentially allows you to control WHEN EXACTLY you want to change information and for WHAT engine process(es) while still being able to maintain an unmodified copy on request.
    It is all just a question of refining interaction with the event-chain (i left a comment in the code for where you can inject your own modifications, but edited it out for now since i am not sure if it would be completely kosher).
    You no longer need to blindly probe any knockout computed values but can listen exactly for the right moment data is updated, which in itself is a huge boon.
    The thing is that we allready have a couple of pretty extreme modifications and conflicts in data aquisition/manipulation are just a question of time.
    This way of dealing with events is both more effective and makes sure you don't potentially break other mods on the way.
    I in no way claim bragging or proprietary rights to this thing, but something like this needs to be implemented sooner rather than later (unless uber radically changes their event hooks in some patch).
    Last edited: March 14, 2014
  8. cola_colin

    cola_colin Moderator Alumni

    Messages:
    12,074
    Likes Received:
    16,221
    Where is the difference between me overwriting the handler.selection something or using the manager?
    thetrophysystem likes this.
  9. elmauru

    elmauru Member

    Messages:
    46
    Likes Received:
    27
    By default it seems as if each selection message can only have ONE handler attached to it instead of multiple ones. Which means that if somebody else would override that handler in some other mod it would break the functionality of both unless i am completely wrong in my understanding of the code used. I think there was a discussion on that somewhere down the pagelist about the registerWithCoherent function and its pitfalls.

    https://forums.uberent.com/threads/working-with-handlers.56923/#post-881332
    Last edited: March 14, 2014
  10. DeathByDenim

    DeathByDenim Post Master General

    Messages:
    4,328
    Likes Received:
    2,125
    Yeah. I see what you mean. However, I think all of the mods redefine handlers like this (and if they don't, they should):
    Code:
    var oldhandlerplayers = handlers.players;
    handlers.players = function (payload, force) {
      oldhandlerplayers(payload, force);
    
      ... your own stuff goes here...
    }
    That will ensure that mods can all get along as far as I know.
    elmauru likes this.
  11. elmauru

    elmauru Member

    Messages:
    46
    Likes Received:
    27
    That stuff is exactly what is dumbfuzzling me. We currently don't seem to have any decent and up-to-date howtos on this sort of stuff :-/

    I know for a fact that i would probably have solved this or another handler-injection thing in a different way and possibly have broken a lot of other mods in the process before figuring it out.

    So let's take a step back then- do we even need a framework like this then? What are the advantages/disadvantages of using one and if we were to use one what should it be able to do?
  12. LavaSnake

    LavaSnake Post Master General

    Messages:
    1,620
    Likes Received:
    691
    Wow, great modding framework. (Looks like you have some competition @raevn ) Great job!
  13. elmauru

    elmauru Member

    Messages:
    46
    Likes Received:
    27
    I don't think I deserve too much credit because a lot of it is just untangling what is happening in the common.js file from uber. I think I will significantly slim down the code for now and limit it to just the hooks for the process-event-manager. It should not impact any other handler events in place since it simply injects itself in the read_message and message_signal events now, but still will provide an API for other mods to hook into if they desire.

    I am slowly starting to understand how this entire modding API works so this should make things more interesting in the future.
  14. wondible

    wondible Post Master General

    Messages:
    3,315
    Likes Received:
    2,089
    If this exists it should be in global handlers - the place where I'm on the verge of writing my own is in the start scene.

    I was going to use a much less drastic method of hooking in. This is actually kind of wondible since it's harder to clobber by careless mod handler overrides, and common.js doesn't seem too change very much.

    The recursive registration is interesting; I prefer recursive setTimeout over setInterval, but I never considered using it for event handlers - even though one of the problems I'm considering is how to have a hander active for only a certain period of time.

    You need to address the scene event filtering

    Code:
           api.Panel.ready(_.keys(handlers).concat(_.keys(globalHandlers)));
    
    Since you have a one-shot api, registering an empty function would be quite convenient for whitelisting a handler for later. A separate method to register a stub callback would be a mere nicety.
  15. DeathByDenim

    DeathByDenim Post Master General

    Messages:
    4,328
    Likes Received:
    2,125
    True, there are no howtos at the moment because things tend to change. The code snippet from on of my mods I quoted above is something I picked up while looking at the source code of other mods. Those are essentially the "howtos" at the moment...

    To be honest, I didn't fully understand what it is that your mod does. The handlers part I got, but your mod seems to go way deeper, taking over the most intimate parts of PA itself. That is, the actual message parts are intercepted as well. None of my mods have used that so far, so I can't really say what it should do. :)
  16. elmauru

    elmauru Member

    Messages:
    46
    Likes Received:
    27
    It basically creates hooks for all engine->API communication at the interface level (i.e. beyond simple handler modification) - Like I said above, i have SIGNIFICANTLY reduced that functionality for now until we can come up with a feature list and I am sure I will not break other mods in the process.

    It would be incredibly easy to bind this manager into all "states" of the engine and even create conditional filter/injection statements for each process. Uber seems to have conveniently left a backdoor open for that. I will tackle this after a goodnight's sleep.

    For now the required feature set for this is pretty easy:

    1. Expand functionality to work with all game scenes
    2. Provide optional handler injection (I allready got this one solved-I just need to ponder a bit on how to handle injection requests by multiple mods).
    3. improve handling for asynchronous requests (I need some sample cases for asynchronous requests to work with, I'll most likely stumble upon them again once I get ahead with my other mod ideas).
    4. reimplement disconnect/exit/etc.-handlers (this just needs confirmation that it wont break other processes).
    4. Optional blocking of "official" engine calls (also allready done, just need to finish handler injection).
    Last edited: March 14, 2014
  17. elmauru

    elmauru Member

    Messages:
    46
    Likes Received:
    27
    handler injection is now possible, you can register a handler injection with mEvents.register_callback_inject(message,callback);

    you can then modify the callback by returning an Object{'message':message,'payload' : payload}
    message ofc being the condition you want to alter and payload being the data you want to emulate.
    If you want to completely skip any further processing after obtaining the handler you can send back an Object{'abandon':1}

    If multiple injection requests are placed only the last injection request in the queue is allowed to modify the payload and message! input for the payload is callback(message,payload,c_count) with c_count being the number of mod injection requests so you have a faint idea if your injection might be overwritten.

    furthermore,
    mEvents.fake_message(message,callback,target) allows you to send a completely fake payload to target ('all':default,'global','local','mod');
    all: perform the response like an actual engine response
    global: perform only on globalHandlers
    local: perform only on handlers
    mod: perform only on mods, any consecutive injection calls will be ignored for now (let me know if you need this for whatever reason)

    If you are wondering what this does: It allows you to manually overide the engine and talk exclusively to the API...

    Also included are handlers for engine<->API handshakes etc. but those do nothing special atm.
    Last edited: March 14, 2014
    cptconundrum likes this.
  18. cptconundrum

    cptconundrum Post Master General

    Messages:
    4,186
    Likes Received:
    4,900
    If this is the better way to do it, I might be able to use it to do a better job of catching death alerts for tracked units in my mod. I still don't have that working 100% of the time.
  19. cola_colin

    cola_colin Moderator Alumni

    Messages:
    12,074
    Likes Received:
    16,221
    So all a framework can do here is a provide "this is how you do it" to new modders.
    I think a simple tutorial would be enough for that tbh. A framework always adds a layer of extra code that can break and always has extra limits as well.
    thetrophysystem likes this.
  20. elmauru

    elmauru Member

    Messages:
    46
    Likes Received:
    27
    Only in this case there really are no limits other than what uber defines, because the communication happens at the base engine/api level.
    The code is also extremely update-friendly because it interacts with a communication layer that is highly unlikely to change when uber messes around with stuff.
    Furthermore it allows mods who use it to avoid/resolve conflicts in how they handle events without having to check every other mod out there.
    Look, I am not really trying to convince anyone here.
    This is a proposal for modders to find a common ground to do process event handling while trying to find the most efficient way to do things.
    You can basically stick to what works for you and unless you hijack any base-communication layers there really should never be any conflicts.

    I mean the advantages of using a common framework are manifold: other than avoiding conflicts and serving as "best practice" -
    If something breaks during an update, you have got an easy to diagnose set of functions and since the code is public on github all somebody needs to do is update it to make all the other mods work again.
    Last edited: March 14, 2014

Share This Page