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

Instead of using your own forces and triggers to fire code and boolexprs, you can use this library.
It doesn't even affect efficiency! You can queue codes/boolexprs instead of firing them if you're going
to need to run a lot of code. That's only one Trigger Evaluation per X amount of code where X is the op-limit.

Also, instead of making your resource responsible for the way it executes/evaluates code/boolexprs, you can blame it
on this one. Pretty sweet, ey?

JASS:
/***********************************************
*
*   FireCode
*   v4.0.0.0
*   By Magtheridon96
*
*   - Executes Codes and Boolexprs
*   - Allows the creation of both static and dynamic
*     queues of code and boolexprs.
*   - Allows all the standardized ways of executing
*     codes and boolexprs to be the responsibilty of
*     one resource and not a thousand.
*
*   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 never use TriggerSleepAction except while using FireCode.
*       - The Jass API must only be used when in need of dynamic 
*         code/boolexpr executions.
*
*   API:
*   ----
*
*       - struct ConditionQueue extends array
*           - static method create takes nothing returns thistype
*               - Creates a static ConditionQueue
*           - method enqueue takes boolexpr b returns triggercondition
*               - Enqueues a boolexpr to the ConditionQueue and returns it.
*           - 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 and destroys the dynamic queue.
*       - 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 and destroys the dynamic queue.
*
***********************************************/
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 ConditionQueue extends array
        private static integer stack = 1
        private static trigger array triggers
        
        // Removal stack
        private static timer stackClearer = CreateTimer()
        private static triggercondition array conditionStack
        private static trigger array triggerStack
        private static integer stackIndex = 0
        
        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 triggercondition
            return TriggerAddCondition(triggers[this], b)
        endmethod
        
        private static method remove takes nothing returns nothing
            loop
                exitwhen stackIndex == 0
                call TriggerRemoveCondition(triggerStack[stackIndex], conditionStack[stackIndex])
                set triggerStack[stackIndex] = null
                set conditionStack[stackIndex] = null
                set stackIndex = stackIndex - 1
            endloop
        endmethod
        
        method dequeue takes triggercondition b returns nothing
            set stackIndex = stackIndex + 1
            set conditionStack[stackIndex] = b
            set triggerStack[stackIndex] = triggers[this]
            if stackIndex == 1 then
                call TimerStart(stackClearer, 0, false, function thistype.remove)
            endif
        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
    
endlibrary

Feel free to comment.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
That wasn't my point.

You use bj_FORCE_PLAYER[0] to save one handle, right ?

Now, do you realize that if you import an empty blizzard.j you will save many more handles ?

Not to mention that one handle less or more really really really really really really really (did i already mention "really" ?) doesn't matter.
 
I think we need to agree on a list of globals and functions inside Blizzard.j that we need to hang on to.

I think InitBlizzard() and all those other 'Init' functions should be changed to functions that simply initialize bj_FORCE_PLAYER and other useful handles like bj_lastCreatedGroup.

Those 'Stock Update' functions seem stupid.
The DayNightSound functions could be simplified.

Everything else except for IntegerTertiaryOp, the Math BJs, GetDyingDestructable and TriggerRegisterPlayerUnitEvent must go.
 
I was never suggesting using this together with a custom blizzard.j ...

I know ^.^
I was talking about writing a custom blizzard.j for vJass-ers to use ^_^

Man, i was just trying to say that using a new force just for a resource isn't that bad.
At least i prefer that, instead of some "random" bj variable.

Is it really that bad? :/
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I'm not an anti-bj dude, but jass is already enough tricky to add more silliness.
Really, using this bj or CreateForce is just the same, except that the latest makes more sense.
This way, you even don't need anymore an initializer (or using CreateForce in a global definition crashes wc3, i haven't tested that).

But i know that you won't change your mind about this, at least i've tried.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I think I am convinced on this. While I love to save handles it is just one handle and it makes sense. A user who doesn't use GUI will probably benefit from a custom blizzard.j file that doesn't initialize that force, anyway.

I am too lazy to benchmark but I think something like "call GroupEnumUnitsOfType(g, "custom_n000", boolexpr)" should actually be faster than TCC/TAC/TE. I don't think it matters to store the return value either. I don't think a trigger evaluation queue is a good idea because it isn't recursion friendly (think of one of the callbacks wanting to use the queue too)
 
Last edited:
I don't think a trigger evaluation queue is a good idea because it isn't recursion friendly (think of one of the callbacks wanting to use the queue too)

You're right.
I should make a trigger array and use a stack. I should also add new functions like "NewCodeQueue" and "NewConditionQueue" that you should call at the beginning of functions where you're going to use QueueCondition/QueueCode.

The reason I want to do a Queue so badly is because ModeManager needs to run multiple boolexprs and only using FireCondition would be really bad :O

something like "call GroupEnumUnitsOfType(g, "custom_n000", boolexpr)" should actually be faster than TCC/TAC/TE

Thanks for the suggestion ^.^
Will do. (only for FireCondition)

edit

I'm not sure about GroupEnumUnitsOfType.
It could run 0 or multiple times if you're not careful ;/

edit

Update. Fixed Recursion problems.

NOTE: I don't know if it compiles :O

edit
Removed signature.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Well, i can't host, but i could bet my ass than ForceEnum...Count is (forever) Not Yet Implanted, just as the other Enum...Count functions.
I mean the integer argument has no effect.

But seriously you're the one which is submitting a resource using it, it's not to the others to prove that your code works properly.
 
Wouldn't that affect the force bj_FORCE_PLAYER[1]?
One sec, going to test it out and debug it.

edit
Updated.
You were right.
For FireCondition, the return value can be anything :)
I tested out by executing 3 functions (one that returns nothing, one that returns false and another that returns true)
They all executed perfectly and recursively well ^.^

edit

Unsatisfied with my results, I found that setting f2 to bj_FORCE_PLAYER and returning nothing or false won't affect recursion, but it WILL affect the force.
Returning nothing/false -> bj_FORCE_PLAYER[1] will have no players
Returning true -> bj_FORCE_PLAYER[1] will have player 1 in it :< (not player 2)

I guess using CreateForce for f2 is the only way to go to avoid this.

edit
Update: Reverted.
 
Last edited:
Level 14
Joined
Nov 18, 2007
Messages
1,084
FireCondition is broken. It fires the same amount of times as the number of players that are playing the map.

Test Map

JASS:
// Test Code:
scope Test initializer Init
    private function hey takes nothing returns nothing
        call BJDebugMsg("HEY") // Correctly displays once
    endfunction
    
    private function hi takes nothing returns boolean
        call BJDebugMsg("HI") // Displays three times due to the extra players
        return false
    endfunction
    
    private function Test takes nothing returns nothing
        call FireCode(function hey)
        call FireCondition(Filter(function hi))
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterPlayerEventEndCinematic(t, Player(0))
        call TriggerAddAction(t, function Test)
    endfunction
endscope
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
watermelon_1234 said:
FireCondition is broken. It fires the same amount of times as the number of players that are playing the map.

Mag has a good excuse, it's not like someone has warning him multiple times about the Enum...Counted function.

Guess the only alternative to triggers then would be the GroupEnumUnitsOfType trick with only one unit on that map which has the specified name.

Using a unit just for that is a terrible practice.
 
Mag has a good excuse, it's not like someone has warning him multiple times about the Enum...Counted function.

Well, I never saw tangible evidence :c

Guess the only alternative to triggers then would be the GroupEnumUnitsOfType trick with only one unit on that map which has the specified name.

Using a unit just for that is a terrible practice.

I'll browse common.j and look for an alternative.
GroupEnumUnitsOfType will be my last resort for now :/
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Mag said:
Well, I never saw tangible evidence :c

I'm not the first random guy and already posted a sample which proved that the integer argument was useless with GroupEnum...Counted functions, i also said that i was quite sure that i've already tested ForceEnum...Counted.
So at least you could have test it before.

Bribe said:
Well the unit could be a dummy-caster too ;)

You only need one rawcode for dummies units (effects and dummy caster), so obviously you can have more than one unit at the same time.
Now ofc you can still create one unit rawcode just for that, but using the most interactive thing (unit) just for executing code is really really terrible, but nothing will stop Mag i suppose, if he can win some nano seconds.
 
@Troll-Brain:

TriggerEvaluation takes a few microseconds according to some test I did (Likely inaccurate)
Other functions got values like a 5-15 nanoseconds. I know my test was bad, but the difference
between the results for common natives and TriggerEvaluate indicate something: TriggerEvaluate is a slow-ass native.
Using a TriggerEvaluate call to run a condition/boolexpr is a bad idea :/
That should be much slower than some other function that should take ~5 - ~15 nanoseconds to execute (more or less)
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Well, it makes sense that TriggerEvaluate is much slower than other functions that actually don't open a new thread (unless you were comparing it against ForceEnumPlayer or any other function with a boolexpr argument), now compare it against GroupEnumUnitOfType, i wouldn't expect a noticeable difference.
Not to mention that GroupEnumUnitOfType will maybe become slower and slower according how many units are on the map.
 
Ok, I skimmed through the entire thing 3 times in Notepad and not NewGen because I'm such an idiot:

JASS:
// bad
native GroupEnumUnitsOfType                 takes group whichGroup, string unitname, boolexpr filter returns nothing
// maybe I could create a dummy unit for Player(14) use this. Besides, who uses that player anyway? lol
native GroupEnumUnitsOfPlayer               takes group whichGroup, player whichPlayer, boolexpr filter returns nothing
// sucks
native GroupEnumUnitsOfTypeCounted          takes group whichGroup, string unitname, boolexpr filter, integer countLimit returns nothing
// troublesome
native GroupEnumUnitsInRect                 takes group whichGroup, rect r, boolexpr filter returns nothing
// sucks
native GroupEnumUnitsInRectCounted          takes group whichGroup, rect r, boolexpr filter, integer countLimit returns nothing
// troublesome
native GroupEnumUnitsInRange                takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
// locations... nay
native GroupEnumUnitsInRangeOfLoc           takes group whichGroup, location whichLocation, real radius, boolexpr filter returns nothing
// sucks
native GroupEnumUnitsInRangeCounted         takes group whichGroup, real x, real y, real radius, boolexpr filter, integer countLimit returns nothing
// locations and Counted ... nay x2
native GroupEnumUnitsInRangeOfLocCounted    takes group whichGroup, location whichLocation, real radius, boolexpr filter, integer countLimit returns nothing
// Selection -> delay, nay
native GroupEnumUnitsSelected               takes group whichGroup, player whichPlayer, boolexpr filter returns nothing

// Static if Single-player -> use this?
native ForceEnumPlayers         takes force whichForce, boolexpr filter returns nothing
// No.
native ForceEnumPlayersCounted  takes force whichForce, boolexpr filter, integer countLimit returns nothing
// No.
native ForceEnumAllies          takes force whichForce, player whichPlayer, boolexpr filter returns nothing
// No.
native ForceEnumEnemies         takes force whichForce, player whichPlayer, boolexpr filter returns nothing

// A dummy destructable in the corner of the map is a good idea. No one ever puts anything other than units there. :D
native EnumDestructablesInRect     takes rect r, boolexpr filter, code actionFunc returns nothing

// Same as above, but it's better if I use destructables.
native EnumItemsInRect     takes rect r, boolexpr filter, code actionFunc returns nothing
 
Obviously not :/
I can still move the destructable/item to the corner of the map though.

To be exact, I'm using a program called TextWrangler (for Mac)
It does highlighting for "some" things.

edit

I just realized that Nestharus is creating a unit for Player(14) in his Costs library. My GroupEnumUnitsOfPlayer idea is thus bad.
ffffffffffffuuuuuuuuuuuuu
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I think it's about time to give up the hopes that there is a reasonable alternative to triggers when using conditions. I think TriggerAddCondition/TriggerEvaluate/TriggerClearConditions is the best way to go here, unfortunately. And the overhead is "too much" for Nestharus' timer systems meaning it's 100% fine for every non-timer scenario.

Another thing would be ExecuteFunc, the speed used to be argued but really I use ExecuteFunc extensively on my horrible PC and it goes very unnoticed. Obviously the strings would be stored into an array which would win here in terms of dynamism, simplicity and low handle count. I have been spending a lot of my time lately on fixing WarChasers 2 and I use "function PrivateFunction__LibraryName__FunctionName takes nothing returns nothing" so as to not break the optimizer and also to let the optimizer shorten this function name.

ExecuteFunc is also nice because it allows the use of TriggerSleepAction which is a very good tool in real-game situations albeit it's taboo for systems.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
The main problem of ExecuteFunc, regardless that is the slowest way to execute code :
string based, meaning typos can't be handled on map saving.

Trigger seems the only valid way here, now if you find a faster way (a valid benchmark is needed), i suppose you can use static ifs and constants, by default it would use TriggerEvaluate.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Another problem with using strings is trying to use it with struct methods (you'd have to use concatenation, which breaks the optimizer -_-).

However, Troll-Brain, I am surprised to see you arguing the speed issue with ExecuteFunc. It's only about 2x or 2.5x slower than TriggerEvaluate, from benchmarks several years ago. The speed may have improved or worsened in recent patches, but it really doesn't matter for my purposes.
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
Another problem with using strings is trying to use it with struct methods (you'd have to use concatenation, which breaks the optimizer -_-).
No, you don't have to resort to concatenation.

I have been spending a lot of my time lately on fixing WarChasers 2 and I use "function PrivateFunction__LibraryName__FunctionName takes nothing returns nothing" so as to not break the optimizer and also to let the optimizer shorten this function name.
That is quite silly.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
1) You do if the struct is private. Keep in mind private underscores are either double or triple, and it's random.

2) I do it so there is no naming clash during compilation. Now I could say "Priv_LibraryName_FunctionName" but I really don't care enough and you'd probably still think it's "quite silly". Your responses seem to fit the "quite silly" definition, and I've seen your work you tend to (ab)use function interfaces quite a lot, which is the worst option because it duplicates the code.
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
JASS:
library Test
{

/* 
    The "private" keyword is not needed,
    but for the sake of vJass, I will use it.
*/
    
    private struct foo[]
    {
        static method bar()
        {
            DoNothing();
            return;
        }
    }
    
    private function baz()
    {
        DoNothing();
        return;
    }
    
    function onInit()
    {
        ExecuteFunc(foo.bar.name);
        ExecuteFunc(baz.name);
    }
}
Where did you see the need to concatenate?
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
I have seen how it compiles.

But, your reason (and I believe everyone else's) to use the name member is to prevent the optimizer from "breaking" the map. As such, the prototype functions that are generated will never be referenced by anything (why are they even generated?), and so will be removed by the optimizer. I don't see a problem. If you do, that's okay.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I wonder what situation would make the "ci" variable useful in these functions:
JASS:
    function NewCodeQueue takes nothing returns nothing
    function EnqueueCode takes code c returns nothing
    function FireCodeQueue takes nothing returns nothing

Since EnqueueCode and FireCodeQueue do not take arguments, there's not really going to be any recursion.

You'd do: NewCodeQueue; EnqueueCode * N; FireCodeQueue...

Unless you do support for dynamic indexing, I don't see the point of this.
 
JASS:
EnqueueCode(code1)
EnqueueCode(code2)
EnqueueCode(code3)
EnqueueCode(code4)

FireCodeQueue()
[Getting registered boolexprs:
Found: code1, code2, code3, code4
code1:
    DoNothing()
code2:
    DoNothing()
code3:
    EnqueueCode(code5)
    EnqueueCode(code6)
    
    FireCodeQueue()
    [Getting registered boolexprs:
    Found: code1, code2, code3, code4, code5, code6 // DOH!
    code1: // executed twice
        DoNothing()
    code2: // executed twice
        DoNothing()
    code3: // Infinite Loop!! :@
        EnqueueCode(code6)
        EnqueueCode(code7)
        
        FireCodeQueue()
        -
        -
[Thread Terminated]
 
Top