• 🏆 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!

[Snippet] FireCode

*ok*

For modularity purposes, I will do that.
I'll add two structs: CodeQueue and ConditionQueue
They'll have the following APIs:

JASS:
struct CodeQueue extends array
    static method create takes nothing returns thistype
    method enqueue takes code returns nothing
    method execute takes nothing returns nothing
    method destroy takes nothing returns nothing
endstruct

struct ConditionQueue extends array
    static method create takes nothing returns thistype
    method enqueue takes boolexpr returns nothing
    method evaluate takes nothing returns boolean
    method destroy takes nothing returns nothing
endstruct

I might add a dequeue method :eek: (If I figure out how I can make one that doesn't add a considerable amount of overhead (like some O(n)-complex function))
 
Ok, I decided to get something done today, so I added the new API and added more documentation since the learning curve is not negligible for this resource:

JASS:
/***********************************************
*
*   FireCode
*   v2.1.1.1
*   By Magtheridon96
*
*   - Executes Codes and Boolexprs
*   - Allows the creation of both static and dynamic
*     queues of code and boolexprs.
*
*   Notes:
*   ------
*
*       - You must never use EnqueueCode/EnqueueCondition without 
*         calling NewCodeQueue/NewConditionQueue.
*       - Firing a Queue destroys it.
*       - Using the Struct API allows you to create static queues.
*       - You can only safely use TriggerSleepAction within static
*         CodeQueues. Dynamic CodeQueues and both static and dynamic
*         ConditionQueues would crash.
*       - Never dequeue a condition or action from a static queue
*         inside queued code/boolexpr. This will crash the trigger.
*       - The Jass API must only be used when in need of dynamic 
*         code/boolexpr executions.
*
*   API:
*   ----
*
*       - struct CodeQueue extends array
*           - static method create takes nothing returns thistype
*               - Creates a static CodeQueue
*           - method enqueue takes code c returns nothing
*               - Enqueues a code to the CodeQueue
*           - method dequeue takes triggeraction c returns nothing
*               - Dequeues a code from the CodeQueue
*           - method execute takes nothing returns nothing
*               - Executes all the codes enqueued in the CodeQueue
*           - method destroy takes nothing returns nothing
*               - Destroys a CodeQueue
*
*       - struct ConditionQueue extends array
*           - static method create takes nothing returns thistype
*               - Creates a static ConditionQueue
*           - method enqueue takes boolexpr b returns nothing
*               - Enqueues a boolexpr to the ConditionQueue
*           - method dequeue takes triggercondition b returns nothing
*               - Dequeues a boolexpr from the ConditionQueue
*           - method evaluate takes nothing returns boolean
*               - Executes all the boolexprs enqueued in the ConditionQueue
*           - method destroy takes nothing returns nothing
*               - Destroys a ConditionQueue
*
*       - function FireCode takes code c returns nothing
*           - Executes a code.
*       - function NewCodeQueue takes nothing returns nothing
*           - Creates a very dynamic code queue that gets destroyed after firing it.
*       - function EnqueueCode takes code c returns nothing
*           - Enqueues a code.
*       - function FireCodeQueue takes nothing returns nothing
*           - Fires all enqueued codes.
*       - function FireCondition takes boolexpr b returns boolean
*           - Fires a boolexpr.
*       - function NewConditionQueue takes nothing returns nothing
*           - Creates a very dynamic boolexpr queue that gets destroyed after firing it.
*       - function EnqueueCondition takes boolexpr b returns nothing
*           - Enqueues a boolexpr.
*       - function FireConditionQueue takes nothing returns boolean
*           - Fires all enqueued boolexprs.
*
*   Extra SpeedFreak API:
*   ---------------------
*
*       - //!textmacro FIRE_CODE takes code
*           - Executes a code.
*       - //!textmacro FIRE_CONDITION takes boolexpr
*           - Fires a boolexpr.
*
*       - //!textmacro NEW_CODE_QUEUE
*           - Creates a very dynamic code queue that gets destroyed after firing it.
*       - //!textmacro ENQUEUE_CODE takes code
*           - Enqueues a code.
*       - //!textmacro FIRE_CODE_QUEUE
*           - Fires all enqueued codes.
*
*       - //!textmacro NEW_CONDITION_QUEUE
*           - Creates a very dynamic boolexpr queue that gets destroyed after firing it.
*       - //!textmacro ENQUEUE_CONDITION takes boolexpr
*           - Enqueues a boolexpr.
*       - //!textmacro FIRE_CONDITION_QUEUE
*           - Fires all enqueued boolexprs.
*
***********************************************/
library FireCode

    globals
        public trigger array btQ
        public trigger array ctQ
        public integer bi = 0
        public integer ci = 0
        public trigger t = CreateTrigger()
    endglobals
    
    struct CodeQueue extends array
        private static integer stack = 1
        private static trigger array triggers
        
        static method create takes nothing returns thistype
            local thistype this = stack
            set stack = stack + 1
            if triggers[this] == null then
                set triggers[this] = CreateTrigger()
            endif
            return this
        endmethod
        
        method enqueue takes code c returns nothing
            call TriggerAddAction(triggers[this], c)
        endmethod
        
        method dequeue takes triggeraction c returns nothing
            call TriggerRemoveAction(triggers[this], c)
        endmethod
        
        method execute takes nothing returns nothing
            call TriggerExecute(triggers[this])
        endmethod
        
        method destroy takes nothing returns nothing
            set stack = stack - 1
            set triggers[this] = triggers[stack]
            call TriggerClearActions(triggers[this])
        endmethod
    endstruct
    
    struct ConditionQueue extends array
        private static integer stack = 1
        private static trigger array triggers
        
        static method create takes nothing returns thistype
            local thistype this = stack
            set stack = stack + 1
            if triggers[this] == null then
                set triggers[this] = CreateTrigger()
            endif
            return this
        endmethod
        
        method enqueue takes boolexpr b returns nothing
            call TriggerAddCondition(triggers[this], b)
        endmethod
        
        method dequeue takes triggercondition b returns nothing
            call TriggerRemoveCondition(triggers[this], b)
        endmethod
        
        method evaluate takes nothing returns boolean
            return TriggerEvaluate(triggers[this])
        endmethod
        
        method destroy takes nothing returns nothing
            set stack = stack - 1
            set triggers[this] = triggers[stack]
            call TriggerClearConditions(triggers[this])
        endmethod
    endstruct
    
    function FireCode takes code c returns nothing
        call ForForce(bj_FORCE_PLAYER[0], c)
    endfunction
    
    function NewCodeQueue takes nothing returns nothing
        set ci = ci + 1
        if ctQ[ci] == null then
            set ctQ[ci] = CreateTrigger()
        endif
    endfunction
    
    function EnqueueCode takes code c returns nothing
        call TriggerAddCondition(ctQ[ci], Filter(c))
    endfunction
    
    function FireCodeQueue takes nothing returns nothing
        call TriggerEvaluate(ctQ[ci])
        call TriggerClearConditions(ctQ[ci])
        set ci = ci - 1
    endfunction
    
    function FireCondition takes boolexpr b returns nothing
        // Do not change the order of these.
        // That would kill recursion-safety.
        call TriggerClearConditions(t)
        call TriggerAddCondition(t, b)
        call TriggerEvaluate(t)
    endfunction
    
    function NewConditionQueue takes nothing returns nothing
        set bi = bi + 1
        if btQ[bi] == null then
            set btQ[bi] = CreateTrigger()
        endif
    endfunction
    
    function EnqueueCondition takes boolexpr b returns nothing
        call TriggerAddCondition(btQ[bi], b)
    endfunction
    
    function FireConditionQueue takes nothing returns nothing
        call TriggerEvaluate(btQ[bi])
        call TriggerClearConditions(btQ[bi])
        set bi = bi - 1
    endfunction
    
    /* For Speedfreaks/Nestharus: */
    
        //! textmacro FIRE_CODE takes code
            call ForForce(bj_FORCE_PLAYER[0], $code$)
        //! endtextmacro
        
        //! textmacro NEW_CODE_QUEUE
            set FireCode_ci = FireCode_ci + 1
            if FireCode_ctQ[FireCode_ci] == null then
                set FireCode_ctQ[FireCode_ci] = CreateTrigger()
            endif
        //! endtextmacro
        
        //! textmacro ENQUEUE_CODE takes code
            call TriggerAddCondition(FireCode_ctQ[FireCode_ci], Filter($code$))
        //! endtextmacro
        
        //! textmacro FIRE_CODE_QUEUE
            call TriggerEvaluate(FireCode_ctQ[FireCode_ci])
            call TriggerClearConditions(FireCode_ctQ[FireCode_ci])
            set FireCode_ci = FireCode_ci - 1
        //! endtextmacro
        
        //! textmacro FIRE_CONDITION takes boolexpr
            call TriggerClearConditions(FireCode_t)
            call TriggerAddCondition(FireCode_t, $boolexpr$)
            call TriggerEvaluate(FireCode_t)
        //! endtextmacro
        
        //! textmacro NEW_CONDITION_QUEUE
            set FireCode_bi = FireCode_bi + 1
            if FireCode_btQ[FireCode_bi] == null then
                set FireCode_btQ[FireCode_bi] = CreateTrigger()
            endif
        //! endtextmacro
        
        //! textmacro ENQUEUE_CONDITION takes boolexpr
            call TriggerAddCondition(FireCode_btQ[FireCode_bi], $boolexpr$)
        //! endtextmacro
        
        //! textmacro FIRE_CONDITION_QUEUE
            call TriggerEvaluate(FireCode_btQ[FireCode_bi])
            call TriggerClearConditions(FireCode_btQ[FireCode_bi])
            set FireCode_bi = FireCode_bi - 1
        //! endtextmacro
    
endlibrary

What do you think?
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Talking about using bj_FORCE_PLAYER[0] :

I really don't care, and that's pretty negligible anyway, but i'm really suprised that you prefer to save one handle despite some speed loss (using a global array variable which the name can't be easily shorten up, instead of a not array one).

Also do you realise that three of your textmacros are completly useless, since the functions calls will be inlined anyway ?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I recommend doing away with the textmacros. Those things should be avoided except in the case where they either: 1) improve readability, 2) tremendously simplify your coding (such as the NewTable library or in lua scripting) or 3) provide some otherwise impossible utility to the user.

In this case, none of the textmacros do any of those.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Why doesn't this have dynamic code queue support still? I think the "dequeue" function for trigger conditions should use a timer as well, because it's hard work for the user to do that himself.

Trigger Actions don't need a timer. You can remove triggeractions while it's executing and it won't crash the thread unless that action was currently running and had a triggersleepaction in the mix, but even then the trigger action right afterward will still run.

The naming convention of the function API is very similar to the naming convention of the CodeQueue struct.
 
Why doesn't this have dynamic code queue support still?

In what sense? :eek:
Dynamic as in dequeuable boolexprs (lol) or dynamic as in only existing in the scope of one function? ;o

I think the "dequeue" function for trigger conditions should use a timer as well, because it's hard work for the user to do that himself.

K.

Trigger Actions don't need a timer. You can remove triggeractions while it's executing and it won't crash the thread unless that action was currently running and had a triggersleepaction in the mix, but even then the trigger action right afterward will still run.

Awesome.

The naming convention of the function API is very similar to the naming convention of the CodeQueue struct.

K.


:)ogre_hurrhurr:)
 
And that's why you have CodeQueue and ConditionQueue structs ._.

JASS:
globals
    CodeQueue toExecute
endglobals

//....
call toExecute.enqueue(function hi)
call toExecute.enqueue(function bye)
//....

//....
call toExecute.fire()
call toExecute.fire()
//....

private module Init
    private static method onInit takes nothing returns nothing
        set toExecute = CodeQueue.create()
    endmethod
endmodule
private struct Inits extends array
    implement Init
endstruct

Remember Bribe, coffee before Jass :p
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
You are not properly recycling indices in the struct (which is supposed to retain longer-term condition queues than the temporal function-interface condition queues which you have). You should be using a common struct-indexing approach for this one.

Not to mention that the function-based method, since you're going to use it dynamically, are pretty darned slow! ExecuteFunc is a lighter process than "FireCondition".
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Am I the only one here who lost track on what this discussion is actually about? :D

I mean ... if you guys can't share the same oppinion about this snippet and how it should work, how can you expect anyone serious mapper out there to actually use it over TriggerEvaluate?
It's like "sure, I could paint my walls white, but I could also just change the light spectrum of my lightsource to make it appear white although it isn't. I clearly go for the second solution. Seems more simple." ... if you catch my drift. ;)
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
How does it actually matter which is faster?
We are not talking about some maths operation here or something that needs to be used thousands of times per second.

I can see speed being an issue for something like damage detection, missile systems, etc. ...
But then again, if you get trouble with performance just because you use trigger evaluations here and there, you are clearly doing something terribly wrong.
You should redesign your map if you need that many trigger instances running in a split second so that the speed of the way you call your code actually matters.


Before you put your effort into creating a trigger call snippet:
Give me an example where this would actually make a noticable difference.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
What would be fastest?

JASS:
call ForForce(bj_FORCE_PLAYER[0], c)
call TriggerExecute(someTrigger with c)
call TriggerEvaluate(someTrigger with c)

I'm thinking that the trigger evaluate would be fastest followed by execute followed by for force ;p.

I have benchmarked those. For force and trigger evaluate are the same. Trigger execute is four times slower. Execute func is even slower.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I don't remember where i've posted a benchmark code, but ideally the variable name (force,trigger) should be 2 characters length (or 3, but no more).
I've found that ForForce was the fastest, but i've not tried to compare in which scale. (just remember that in a stress test, fps drop is not supposed to be linear according to the script efficiency)
And, also, i've not tried to use a ForForce in a ForForce callback (could happen)
It has been reported that a such case bug on wc3c.net, but i've never tested it myself (blame my laziness).

Anyway, too bad we can't store code in an array :/

For this resource i'm quite agree with the graveyard option, most of time it's just better to inline your own way, like i do with my resource UnitLL.
 
Top