1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[JASS] Reverse/Return-Hook

Discussion in 'Triggers & Scripts' started by Pyrogasm, Sep 24, 2019.

  1. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,901
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Has anyone else run into a situation in which you needed a way to hook into a function to get its output rather than the inputs given to it? Like the hook keyword but in reverse. When hooks are used they are called before the function itself and do not provide you with a way to get its return arguments. By way of example:
    Code (vJASS):
    function FuncA takes nothing returns nothing
      call BJDebugMsg("A")
    endfunction

    function FuncB takes nothing returns nothing
      call BJDebugMsg("B")
    endfunction

    hook FuncA FuncB //B is hooked when A is called
    //...
    call FuncA() //The output is B,A not A,B

    This behavior is fine, (somewhat) expected, and useful in many situations. But what about if you want to hook something like CreateUnit so that you can do some actions with the unit after it's created? With the way hooks work now, this is impossible because you can't get the return value of the function. I propose something like:
    Code (vJASS):
    function FuncA nothing returns integer
      call BJDebugMsg("Random math:")
      return GetRandomInt(1,3)
    endfunction

    function FuncB takes integer RET_VALUE returns nothing
      call BJDebugMsg(I2S(RET_VALUE)+"times 5 is "+I2S(RET_VALUE * 5))
    endfunction

    hookreturn FuncA FuncB //A's output is hooked into B after it's
    //...
    call FuncA() //the output is "Random math: _ times 5 is _"

    Why would this be useful because you should theoretically always have access to the return value of a function you called, right? Besides something like the CreateUnit scenario I mentioned before where you might want to set mapwide functions to run whenever a unit is created (from any source in your map) the scenario that got me thinking about this is trying to hook into CreateTrigger to do something with it whenever it's called. Since the function has no arguments hooking immediately seemed pointless since I can't actually access the trigger object that was created by the function! Sure I could write a CreateTriggerEx that called CreateTrigger itself and did whatever I needed to do, but implementing that in a map would require changing all CreateTrigger calls to use the Ex version--could be a lengthy process to find/replace all your code (I guess not with editing the war3map.j file though).


    Thoughts?
     
  2. Wrda

    Wrda

    Joined:
    Nov 18, 2012
    Messages:
    1,098
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    I don't know. I think it's a legit idea, but I see little gain with it. I'd stick with CreateTriggerEx kind of stuff. Obviously if you're doing this since the start of your map, it is effortless, but if you're trying to do this in middle of it it is surely tedious.
    PS: I never really used hooks, just my thoughts.
     
  3. _Guhun_

    _Guhun_

    Joined:
    Jun 12, 2010
    Messages:
    351
    Resources:
    7
    Spells:
    6
    Tutorials:
    1
    Resources:
    7
    I don't use vJASS hooks due to the fact that they evaluate triggers. So I came up with my own approach. It's still limited by a few things:

    1. It only works in scopes and libraries.
    2. It's a bit convoluted.
    3. Supporting multiple hooks would be a pain.

    Code (Text):

    library FuncHooks requires AttachedSFX

    //! runtextmacro FuncHooks("Orgl", "SetUnitPosition", "unit u, real x, real y", "u,x,y")
    //! runtextmacro FuncHooks("Orgl", "SetUnitFacing", "unit u, real a", "u,a")
    //! runtextmacro FuncHooks("Orgl", "SetUnitFacingTimed", "unit u, real a, real t", "u,a,t")
    //! runtextmacro FuncHooks("Orgl", "SetUnitTimeScale", "unit u, real scale", "u,scale")
    //! runtextmacro FuncHooks("Orgl", "SetUnitScale", "unit u, real x, real y, real z", "u,x,y,z")
    //! runtextmacro FuncHooks("Orgl", "SetUnitFlyHeight", "unit u, real h, real r", "u,h,r")
    //! runtextmacro FuncHooks("Orgl", "AddUnitAnimationProperties", "unit u, string p, boolean a", "u,p,a")
    //! runtextmacro FuncHooks("Orgl", "SetUnitColor", "unit u, playercolor c", "u,c")
    //! runtextmacro FuncHooks("Orgl", "SetUnitVertexColor", "unit u, integer r, integer g, integer b, integer a", "u,r,g,b,a")

    //! runtextmacro optional FuncHooks_Definitions()

    //! runtextmacro FuncHooks("Hkd", "SetUnitPosition", "unit u, real x, real y", "u,x,y")
    //! runtextmacro FuncHooks("Hkd", "SetUnitFacing", "unit u, real a", "u,a")
    //! runtextmacro FuncHooks("Hkd", "SetUnitFacingTimed", "unit u, real a, real t", "u,a,t")
    //! runtextmacro FuncHooks("Hkd", "SetUnitTimeScale", "unit u, real scale", "u,scale")
    //! runtextmacro FuncHooks("Hkd", "SetUnitScale", "unit u, real x, real y, real z", "u,x,y,z")
    //! runtextmacro FuncHooks("Hkd", "SetUnitFlyHeight", "unit u, real h, real r", "u,h,r")
    //! runtextmacro FuncHooks("Hkd", "AddUnitAnimationProperties", "unit u, string p, boolean a", "u,p,a")
    //! runtextmacro FuncHooks("Hkd", "SetUnitColor", "unit u, playercolor c", "u,c")
    //! runtextmacro FuncHooks("Hkd", "SetUnitVertexColor", "unit u, integer r, integer g, integer b, integer a", "u,r,g,b,a")

    endlibrary

    //! textmacro FuncHooks takes prefix, name, typedArgs, args
    function $prefix$$name$ takes $typedArgs$ returns nothing
    call $name$($args$)
    endfunction
    //! endtextmacro

    //! textmacro DefineHooks
    private function SetUnitPosition takes unit u, real x, real y returns nothing
    call HkdSetUnitPosition(u, x, y)
    endfunction

    private function SetUnitFacing takes unit u, real a returns nothing
    call HkdSetUnitFacing(u, a)
    endfunction

    private function SetUnitFacingTimed takes unit u, real a, real t returns nothing
    call HkdSetUnitFacingTimed(u, a, t)
    endfunction

    private function SetUnitTimeScale takes unit u, real scale returns nothing
    call HkdSetUnitTimeScale(u,scale)
    endfunction

    private function SetUnitScale takes unit u, real x, real y, real z returns nothing
    call HkdSetUnitScale(u,x,y,z)
    endfunction

    private function SetUnitFlyHeight takes unit u, real h, real r returns nothing
    call HkdSetUnitFlyHeight(u,h,r)
    endfunction

    private function AddUnitAnimationProperties takes unit u, string p, boolean a returns nothing
    call HkdAddUnitAnimationProperties(u,p,a)
    endfunction

    private function SetUnitColor takes unit u, playercolor c returns nothing
    call HkdSetUnitColor(u,c)
    endfunction

    private function SetUnitVertexColor takes unit u, integer r, integer g, integer b, integer a returns nothing
    call HkdSetUnitVertexColor(u,r,g,b,a)
    endfunction

    //! endtextmacro
    // Missing Facing hook\
     

    This is where I define the hooks (this is executed as the FuncHooks_Definitions() textmacro in the code above):
    Code (Text):

    /* Functions that are not defined here will not have any hooks attached. To see which functions can be
    hooked to, check the FuncHooks library.*/
    //! textmacro FuncHooks_Definitions

    private function SetUnitPosition takes unit u, real x, real y returns nothing
    call OrglSetUnitPosition(u, x, y)

    if UnitHasAttachedEffect(u) then
    call AttachedSFX_onSetPosition(u, x, y)
    endif
    endfunction

    private function SetUnitFacing takes unit u, real angle returns nothing
    call OrglSetUnitFacing(u, angle)

    if UnitHasAttachedEffect(u) then
    call AttachedSFX_onSetFacing(u, angle)
    endif
    endfunction

    private function SetUnitFacingTimed takes unit u, real angle, real time returns nothing
    call OrglSetUnitFacingTimed(u, angle, time)

    if UnitHasAttachedEffect(u) then
    call AttachedSFX_onSetFacingTimed(u, angle, time)
    endif
    endfunction

    private function SetUnitTimeScale takes unit u, real scale returns nothing
    call OrglSetUnitTimeScale(u,scale)

    if UnitHasAttachedEffect(u) then
    call AttachedSFX_onSetTimeScale(u, scale)
    endif
    endfunction

    private function SetUnitScale takes unit u, real x, real y, real z returns nothing
    call OrglSetUnitScale(u,x,y,z)

    // Here
    call AttachedSFX_onSetScale(u, x, y, z)
    endfunction

    private function SetUnitFlyHeight takes unit u, real h, real r returns nothing
    call OrglSetUnitFlyHeight(u,h,r)

    // if UnitHasAttachedEffect(u) then
    call AttachedSFX_onSetHeight(u, h, r)
    // endif
    endfunction

    private function AddUnitAnimationProperties takes unit u, string p, boolean a returns nothing
    call OrglAddUnitAnimationProperties(u,p,a)

    if UnitHasAttachedEffect(u) then
    call AttachedSFX_onAddAnimationProperties(u, p, a)
    endif
    endfunction

    private function SetUnitColor takes unit u, playercolor c returns nothing
    call OrglSetUnitColor(u,c)

    if UnitHasAttachedEffect(u) then
    call AttachedSFX_onSetColor(u, c)
    endif
    endfunction

    private function SetUnitVertexColor takes unit u, integer r, integer g, integer b, integer a returns nothing
    if UnitHasAttachedEffect(u) then
    call AttachedSFX_onSetVertexColor(u, r, g, b, a)
    else
    call OrglSetUnitVertexColor(u,r,g,b,a)
    endif
    endfunction


    //! endtextmacro


    So now, whenever I need to hook these functions in any library or scope, I simple include the text macro:
    //! runtextmacro optional DefineHooks()


    This way my code stays pretty decoupled, since I can remove the hook library and it won't generate any compile errors.

    Hopefully this makes sense, I was going to try to explain it better, but it's late and I'm tried. But basically, I'm replacing the natives by taking advtanges of the fact that vJASS allows us to overwrite any functions outside of the library or scope within that library or scope by declaring a function with the same name, but with private or public access.
     
  4. Serenity09

    Serenity09

    Joined:
    Mar 7, 2011
    Messages:
    40
    Resources:
    0
    Resources:
    0
    so the syntactic sugar would sorta like supply a specific eventable api, where a lot of the specifics are determined by the "reverse-hooked" function's signature? i get a lot of eventable-from-a-different-angle vibes from this idea. like a function/method that implements the eventable api at the instance or static level. the creator can broadcast an event with data, but the things that listen to that event effectively are hooking (one of) its results. given that jass is entirely interpreted, the difference between "compiling" this "reverse-hook" behavior VS defining it in memory on load is often not relevant beyond the immediate implementation

    having this feature would mean fairly custom events on warcraft 3 natives (and any other custom function) as such sweet syntactic sugar that there's a little less of a learning curve to dealing with it (but more limitations instead). You'd potentially never even need to worry about a more flexible event system at all, while getting a significant upgrade to how your implementation can extend/maintain its dependencies. On that note, i feel like when you get to the point that you want to depend on a function's output, ie events, you often don't want to lock into generalizations like output type or number of outputs per function. yeah, it's nice when every function has a single clear output and type, but there's going to be a lot of times where you'll want to depend on two parts of a function's "output", or not the obvious candidate. this in turn leads to having your own alternate implementation for custom events, or more granular functions, which can be their own performance liability

    i might like this idea even more if there was better vanilla support for defining/depending on custom events. that way you could maintain one or the other implementation, probably the sugar, to be consistent with the other. do you have any thoughts on that?