• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

To hell with callbacks!

Status
Not open for further replies.
Level 13
Joined
Nov 7, 2014
Messages
571
While reading this thread I was thinking if there could be a "better" (for some definition of better) way
of calling code when some events happen. And I came up with this:

JASS:
library EventSwitch

function EventSwitch takes integer ev, integer data returns nothing
    local RedEscKeyPressCount r
    // if we had a lot of type of events I guess one has to come up with creative names
    // (a .. z, can only get you so far, but aa .. zz can get you much further)

    if ev == EVENT_RED_ESC_KEY_PRESS_COUNT_MULTIPLE_OF_3 then
        set r = RedEscKeyPressCount(data)
        call BJDebugMsg("multiple of 3: data: " + I2S(r.count))

    elseif ev == EVENT_RED_ESC_KEY_PRESS_COUNT_MULTIPLE_OF_5 then
        set r = data // don't really need the typecast
        call BJDebugMsg("multiple of 5: data: " + I2S(r.count))

    endif
    // an if statement can have ~127 branches
    // so if we had more events than that we would simply use 2 if statements =)

endfunction

endlibrary

which dispatches events from this source:

JASS:
// As ridiculous of an event as events can be...
library RedEscKeyPressCount initializer init requires EventSwitch

globals
    integer red_esc_key_press_count = 0
    integer EVENT_RED_ESC_KEY_PRESS_COUNT_MULTIPLE_OF_3 = StringHash("EVENT_RED_ESC_KEY_PRESS_COUNT_MULTIPLE_OF_3")
    integer EVENT_RED_ESC_KEY_PRESS_COUNT_MULTIPLE_OF_5 = StringHash("EVENT_RED_ESC_KEY_PRESS_COUNT_MULTIPLE_OF_5")
endglobals

struct RedEscKeyPressCount
    integer count = 0
endstruct

globals
    private RedEscKeyPressCount instance
endglobals

private function on_esc takes nothing returns nothing
    set instance.count = instance.count + 1

    if ModuloInteger(instance.count, 3) == 0 then
        call EventSwitch(EVENT_RED_ESC_KEY_PRESS_COUNT_MULTIPLE_OF_3, instance)
    endif

    if ModuloInteger(instance.count, 5) == 0 then
        call EventSwitch(EVENT_RED_ESC_KEY_PRESS_COUNT_MULTIPLE_OF_5, instance)
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t

    set instance = RedEscKeyPressCount.create()

    set t = CreateTrigger()
    call TriggerRegisterPlayerEventEndCinematic(t, Player(0))
    call TriggerAddAction(t, function on_esc)
endfunction

endlibrary

The only downside that I can see is that it could reach the op-limit if the event source emits many events in a single batch.
Otherwise this should be much faster than TriggerEvaluate/Execute, ExecuteFunc, and ForForce.

Edit: okay there's another major downside, i.e if we try to call a method jasshelper will generated "the bonus" stuff which behind the scenes uses TriggerEvaluate/Execute... :p
 
Last edited:
Level 13
Joined
Nov 7, 2014
Messages
571
I think I got something even better (again... for some definition of better =)):
JASS:
// As ridiculous of an event as they come
library EventEscKeyPressCount

struct EventEscKeyPressCount extends array
    integer p
    integer count


    // we put the methods that we want to give access to the event handlers here (above the event handler implementations),
    // because we don't want jasshelper to generate any "bonus stuff" (TriggerEvaluate(s))
    //
    method foo takes nothing returns string
        return "foo(" + I2S(this.count) + ")"
    endmethod

    method bar takes nothing returns string
        return GetPlayerName(Player(this.p)) + " bar"
    endmethod


    // the event handler implementations go here (below the methods we want to give access to)
    //
    implement optional EventEscKeyPressCountHandler1
    implement optional EventEscKeyPressCountHandler2
    implement optional EventEscKeyPressCountHandler3
    implement optional EventEscKeyPressCountHandler4
    implement optional EventEscKeyPressCountHandler5
    implement optional EventEscKeyPressCountHandler6
    implement optional EventEscKeyPressCountHandler7
    implement optional EventEscKeyPressCountHandler8
    //
    // a good question would be how many should we provide?
    // I don't know... 8? Should be enough for this silly event... =)


    // our library/system logic goes bellow the handler implementations becase we
    // are going to call them (at some point)
    //
    private static method on_esc takes nothing returns nothing
        local thistype this = GetPlayerId(GetTriggerPlayer())
        set this.count = this.count + 1
        set this.p = this // redundant but meh...

        if ModuloInteger(this.count, 3) == 0 then

            static if thistype.on_multiple_of_3_1.exists then
                call this.on_multiple_of_3_1()
            endif

            // we use a textmacro to save 2 lines? I guess...

            //! textmacro EventEscKeyPressCountMo3Handler takes HN
            static if thistype.on_multiple_of_3_$HN$.exists then
                call this.on_multiple_of_3_$HN$()
            endif
            //! endtextmacro

            //! runtextmacro EventEscKeyPressCountMo3Handler("2")
            //! runtextmacro EventEscKeyPressCountMo3Handler("3")
            //! runtextmacro EventEscKeyPressCountMo3Handler("4")
            //! runtextmacro EventEscKeyPressCountMo3Handler("5")
            //! runtextmacro EventEscKeyPressCountMo3Handler("6")
            //! runtextmacro EventEscKeyPressCountMo3Handler("7")
            //! runtextmacro EventEscKeyPressCountMo3Handler("8")

        endif

        if ModuloInteger(this.count, 5) == 0 then

            static if thistype.on_multiple_of_5_1.exists then
                call this.on_multiple_of_5_1()
            endif

            // we use a textmacro to save 2 lines? I guess...

            //! textmacro EventEscKeyPressCountMo5Handler takes HN
            static if thistype.on_multiple_of_5_$HN$.exists then
                call this.on_multiple_of_5_$HN$()
            endif
            //! endtextmacro

            //! runtextmacro EventEscKeyPressCountMo5Handler("2")
            //! runtextmacro EventEscKeyPressCountMo5Handler("3")
            //! runtextmacro EventEscKeyPressCountMo5Handler("4")
            //! runtextmacro EventEscKeyPressCountMo5Handler("5")
            //! runtextmacro EventEscKeyPressCountMo5Handler("6")
            //! runtextmacro EventEscKeyPressCountMo5Handler("7")
            //! runtextmacro EventEscKeyPressCountMo5Handler("8")

        endif
    endmethod

    private static method onInit takes nothing returns nothing
        local trigger t
        local integer i
        local thistype this

        set t = CreateTrigger()
        set i = 0
        loop
            exitwhen i >= bj_MAX_PLAYERS // could use PlayerArray here =)
            call TriggerRegisterPlayerEventEndCinematic(t, Player(i))
            set i = i + 1
        endloop
        call TriggerAddAction(t, function thistype.on_esc)
    endmethod
endstruct

endlibrary


// Users of our library/system implement one of the handlers. Now which one should they implement?
// The answer is of course the one which hasn't been taken yet... LOL =)
//
module EventEscKeyPressCountHandler1
    method on_multiple_of_3_1 takes nothing returns nothing
        call BJDebugMsg("Handler1: on_multiple_of_3 called for thistype(" + I2S(this) + "), .count: " + I2S(this.count) + ", " + this.foo())
    endmethod

    method on_multiple_of_5_1 takes nothing returns nothing
        call BJDebugMsg("Handler1: on_multiple_of_5 called for thistype(" + I2S(this) + "), .count: " + I2S(this.count) + ", " + this.bar())
    endmethod
endmodule

// This module will not get used...
module EventEscKeyPressCountHandler2
    method on_multiple_of_3_2 takes nothing returns nothing
        call BJDebugMsg("Handler2: what " + this.foo())
    endmethod

    method on_multiple_of_5_2 takes nothing returns nothing
        call BJDebugMsg("Handler2: ever " + this.foo())
    endmethod
endmodule

// ... because the last declaration of a module get's used!!!!
// Apparently there is no "redeclaration of module X" kind of error...
module EventEscKeyPressCountHandler2
    method on_multiple_of_3_2 takes nothing returns nothing
        call BJDebugMsg("I overwrote someone else's EventEscKeyPressCountHandler2::on_multiple_of_3_2 method!")
    endmethod
    method on_multiple_of_5_2 takes nothing returns nothing
        call BJDebugMsg("I overwrote someone else's EventEscKeyPressCountHandler2::on_multiple_of_5_2 method!")
    endmethod
endmodule

The above is a bit "bananas" but "To hell with callbacks"! =)
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
coupling: running in the same thread, they share the same fate (one dividing by zero: everything else is skipped, one using TriggerSleepAction: exactolucci)
inflexible: the static writing implies a more constrained scheduler (e.g. you cannot form a priority list depending on another runtime parameter unless you atomically split every target into one callable branch)
main problem: unless you stuff all the calls (and subcalls) into the scheduler or all of them are declared further above in the code, you will still need at least one dynamic invocation which is to dive to the bottom to gain universal access
 
Status
Not open for further replies.
Top