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

[JASS] ExecuteFunc and new threads/operation limit

Status
Not open for further replies.
Yes, it opens a new thread.

The theoretical limit for abilities is bound to the number of unique Rawcodes. So it's basicly unlimited, as possible ascii IDs are 256^4. However, the practical limit is much lower, as every object created slows down the object editor and you will reach a point eventually where it becomes unusable.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
:amad: 4,294,967,296 ooooooo so close to the number I need.

... nah just kiddin.
I just hope that my pc will be able to handle the amount that I need.


ExecuteFunc() is a relied too... now I can gladly say that my map is made in 14 triggers :grin:
(I mean 14 "CreateTrigger()".)
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
I recently saw a thread with a list of stuff about what is faster than what.
I am pretty sure that I saw that TriggerEvaluate() is 20% slower than ExecuteFunc().

Can't find the link any more though.

EDIT:
I think that you misunderstood.
I said "ExecuteFunc()" and not "TriggerExecute()".
 
EDIT:
I think that you misunderstood.
I said "ExecuteFunc()" and not "TriggerExecute()".
No worries, I understood you well. ExecuteFunc() is slower than TriggerEvaluate and TriggerExecute, as it performs a string search on your function. It gets slower the more custom functions you have.

Also, it's just a bad native to use in general, as it is known to create problems with the compress names feature of Vexorian's MapOptimizer.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
It is just that I need 500 units, 500 items, 1k destructables/doodads... and onl a little 7.9k abilities.
I just hope that the limit is not even 1 less than that 8k.

The problem about execution is that I have to execute a variable.
I can execute a condition using a trigger (variable).
I can execute a function using a string (variable).
I can't execute a function using a code (variable).
At least as far as I know.
 
so creating 300-400 triggers, adding conditions and evaluating those is better to do than ExecuteFunc?
Why would you create so many triggers?

Is there no better way to directly run a function?
You can just call it manually. The proposed TriggerEvaluate is only needed if you actually hit the operation limit. If your map is properly coded, this should almost never be needed.

There's only 3 real-world applications that might come close to hitting the OP limit in any properly coded map:
- initialization trigger
- save/load algorithms
- setting up databases (for example, custom unit stats)


... on a different note, why don't you just tell us what you actually want to do and we give you an example how to code it properly? There's only so much advice we can give you with cryptic explanations.

Do you want to attach a function to a unit or spell ID? In that case, just set up a registry and create triggers to evaluate.


Here's a quick sketch how a function-based spell registry works:
JASS:
//this function registers a function to a spell rawcode by creating a triggercondition, then saving it in a hashtable
private function RegisterSpell takes integer abilraw, code c returns nothing
   local trigger t = CreateTrigger()
   call TriggerAddCondition(t, Condition(c))
   call SaveTriggerHandle(HASH, abilraw, 0, t)
   set t = null
endfunction

//this function fires the code by evaluating the trigger condition
private function RunSpell takes unit u, integer abilraw returns nothing
   local trigger t = LoadTriggerHandle(HASH, abilraw, 0)
   call TriggerEvaluate(t)
   set t = null
endfunction

//this is a wrapper for your spell-effect event
function OnSpell takes nothing returns nothing
   call RunSpell(GetTriggerUnit(), GetAbilityId()) //I don't remember what the event response was called...
endfunction
JASS:
//this is your registry
private function InitRegistry takes nothing returns nothing
   call RegisterSpell('A000', function fireball)
   call RegisterSpell('A001', function foo)
   call RegisterSpell('A002', function bar)
   //...
endfunction
 
Last edited:
Level 24
Joined
Aug 1, 2013
Messages
4,657
Well... that is exacly what I want.
But then also on everything else that is possible to attach a function (trigger) to.

What I have now is this:
JASS:
library aemSystem
    globals
        hashtable udg_AEM_Hashtable1 = InitHashtable()
        hashtable udg_AEM_Hashtable2 = InitHashtable()
    endglobals
    
    function AEM_FunctionRemoveEvent takes integer whichEvent, trigger whichTrigger returns nothing
        local integer i = 0
        local trigger t
        
        loop
            exitwhen not(HaveSavedHandle(udg_AEM_Hashtable1, whichEvent, i))
            set t = LoadTriggerHandle(udg_AEM_Hashtable1, whichEvent, i)
            if t == whichTrigger then
                loop
                    exitwhen not(HaveSavedHandle(udg_AEM_Hashtable1, whichEvent, i+3))
                    
                    call SaveTriggerHandle(udg_AEM_Hashtable1, whichEvent, i, LoadTriggerHandle(udg_AEM_Hashtable1, whichEvent, i+3))
                    call SaveInteger(udg_AEM_Hashtable1, whichEvent, i+1, LoadInteger(udg_AEM_Hashtable1, whichEvent, i+4))
                    call SaveBoolean(udg_AEM_Hashtable1, whichEvent, i+2, LoadBoolean(udg_AEM_Hashtable1, whichEvent, i+5))
                    
                    set i = i + 3
                endloop
                
                call RemoveSavedHandle(udg_AEM_Hashtable1, whichEvent, i)
                call RemoveSavedInteger(udg_AEM_Hashtable1, whichEvent, i+1)
                call RemoveSavedBoolean(udg_AEM_Hashtable1, whichEvent, i+2)
                return
            endif
            set i = i + 3
        endloop
    endfunction

    function AEM_CallEvent takes integer whichEvent returns nothing
        local trigger array triggers
        local integer index = 0
        local integer i = 0
        
        loop
            exitwhen not(HaveSavedHandle(udg_AEM_Hashtable1, whichEvent, i))
            set triggers[index] = LoadTriggerHandle(udg_AEM_Hashtable1, whichEvent, i)
            
            if not(LoadBoolean(udg_AEM_Hashtable1, whichEvent, i+2)) then
                
                call AEM_FunctionRemoveEvent(whichEvent, triggers[index])
                set i = i - 3
            endif
            
            set index = index + 1
            set i = i + 3
        endloop
        
        set i = 0
        loop
            exitwhen i >= index
            call TriggerEvaluate(triggers[i])
            set i = i + 1
        endloop
        
    endfunction

    function AEM_FunctionRegisterEvent takes integer whichEvent, trigger whichTrigger, integer priority, boolean priorize, boolean repeating returns nothing
        local integer i = 0
        local integer i2
        local boolean shiftRemaining = false
        
        loop
            exitwhen not(HaveSavedHandle(udg_AEM_Hashtable1, whichEvent, i))
            set shiftRemaining = true
            if priorize then
                exitwhen LoadInteger(udg_AEM_Hashtable1, whichEvent, i+1) >= priority
            else
                exitwhen LoadInteger(udg_AEM_Hashtable1, whichEvent, i+1) > priority
            endif
            set shiftRemaining = false
            set i = i + 3
        endloop
        
        if shiftRemaining then
            set i2 = i + 3
            loop
                exitwhen not(HaveSavedHandle(udg_AEM_Hashtable1, whichEvent, i2))
                
                set i2 = i2 + 3
            endloop
            
            loop
                exitwhen i2 == i
                
                call SaveTriggerHandle(udg_AEM_Hashtable1, whichEvent, i2, LoadTriggerHandle(udg_AEM_Hashtable1, whichEvent, i2-3))
                call SaveInteger(udg_AEM_Hashtable1, whichEvent, i2+1, LoadInteger(udg_AEM_Hashtable1, whichEvent, i2-2))
                call SaveBoolean(udg_AEM_Hashtable1, whichEvent, i2+2, LoadBoolean(udg_AEM_Hashtable1, whichEvent, i2-1))
                
                set i2 = i2 - 3
            endloop
        endif
        
        call SaveTriggerHandle(udg_AEM_Hashtable1, whichEvent, i, whichTrigger)
        call SaveInteger(udg_AEM_Hashtable1, whichEvent, i+1, priority)
        call SaveBoolean(udg_AEM_Hashtable1, whichEvent, i+2, repeating)
    endfunction
endlibrary

udg_AEM_Hashtable2 will be used later to save keys (which are a unique event) by using 2 other keys.

With that, I can save a list of triggers and their priorities etc to a unittype + abilityid for example.

The thing is that I will be having about 300 spells. Each spell will have at least 2 triggers.
That is only counting the spells. (Which are indeed the one that will be having so much triggers.)

So having 600-900 triggers only for spells seems like a bit heavy to me.
I don't know if that actually is heavy but yea, I think you can see where that thought came from.
 
There is no problem having thousands of triggers as long as you index them properly to allow fast access. Memory is cheap. ExecuteFunc does a string search, which is a multiple times slower than just loading a trigger from a table.

Just as a comparison: Unit indexers usually have 1 trigger per unit so depending on your map will spam thousands of triggers without causing any trouble.


That being said; your script is pretty convoluted with weird stuff that I don't have a clue about why it's even in there (like priorities). You should probably simplify it, but that's up to you.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Well... It is an event system.
I make events (unique integers) and attach triggers to that event.
I call the event in the order of priorities of the actions (triggers).
Equal as for "Repeating", it is just a feature for event actions.

I use this one for my spells too because it is just easy to use the same system over and over again.
 
Status
Not open for further replies.
Top