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

[vJASS] Does .evaluate() prevent from OpLimit?

Status
Not open for further replies.
Level 25
Joined
Feb 2, 2006
Messages
1,686
Hey,
I run into the OpLimit with JASS due to too many calls so I've started calling some static methods with .evaluate() since then they will be called in another trigger as condition.
I thought that would fix the problem but I was wrong.

The problem with .execute() would be that I rely on the end of the function so I would have to synchronize the functions somehow.

Do you have any idea what I am doing wrong here?
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Last time i checked, every native jass function which have code or boolexpr reset the limit op when it's called.
So ForForce, GroupEnum..., TriggerEvaluate/Execute, triggers conditions/actions/events filter, and so one.
Same for ExecuteFunc.

Now the limit op is reset only inside these blocks, it's still possible to reach it in the main function.
I'm not sure if i'm clear at this point. But i can write pseudo code if i am not.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I prefer to use call ForForce(bj_FORCE_PLAYER[0], function a) to extend op limits, no real advantages other than not requiring handle/agent creation. I only do it once tho. Hitting op limit is not a good practice, it means the operation is way too heavy, moreover if it's done periodically, you better avoid it instead of removing/extending. Usually I prefer to split the operations using periodical timer or some other tricks, but this is obviously my own preference.

Btw, extending op limit requires you to replace local variables with global variables (or using global to pass locals to the other function), in order to synchronize the two functions.

This is just a lousy example how to pass local variables.
JASS:
globals
    integer Pass = 1
endglobals

function a takes nothing returns nothing
    local integer i = Pass
    loop
        exitwhen i > 50000
        call BJDebugMsg(I2S(i))
        set i = i + 1
        // Split the operation every 500 iterations
        exitwhen ModuloInteger(i, 500) == 0
    endloop
    if i <= 50000 then
        // Use variable Pass to pass value of i to the other function
        set Pass = i
        call ForForce(bj_FORCE_PLAYER[0], function a)
    endif
endfunction

// On init
call a()

Hope it helps.
 
Last edited:

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
I prefer to use call ForForce(bj_FORCE_PLAYER[0], function a) to extend op limits, no real advantages other than not requiring handle/agent creation.
That's already a good enough advantage for me. So, yeah, better use ForForce instead of TriggerEvaluate.

I only do it once tho. Hitting op limit is not a good practice, it means the operation is way too heavy, moreover if it's done periodically, you better avoid it instead of removing/extending. Usually I prefer to split the operations using periodical timer or some other tricks, but this is obviously my own preference.
The OP limit can actually be reached very fast with complex algorithms like saving/loading or simply setting up registries in the game.
Obviously, those aren't tasks that need to be lightning fast; but just some examples for when the OP limit actually matters even for the regular user.

That's why you should split up all your map initialization triggers into several sub-triggers over the first 3-4 seconds of the game.
Plus, it reduces the loading time of maps not to do everything directly on map init.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Obviously, those aren't tasks that need to be lightning fast; but just some examples for when the OP limit actually matters even for the regular user.

Extending op limit is okay as long as it doesn't cause any form of spikes within the game. Honestly I haven't found something, which requires op limit extension, that is really urgent to be done that instantly. But in the end, whether you like it or not, no matter how important is it, you need to split things up cz imo lags are one thing you really need to avoid.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
TriggerSleepAction(0), while not instant anymore, you dont need to change locals into globals, because trigger sleep action will store the whole stack somewhere

But it only works for trigger-attached functions, iirc. Stand alone functions can't use it. I really don't know what should I call them.

JASS:
function a takes nothing returns nothing
    ....
    TriggerSleepAction(0)
    ....
endfunction

// on init
    call TriggerAddAction(trig, function a)
    // TriggerSleepAction works
    call TriggerExecute(trig)
    // TriggerSleepAction doesn't work
    call a()
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
the only standalone function inside wc3 is main and anything it calls. All other things are just hooks to triggers/timers anyways. But yes, good point, triggercondition wont like it

In your code, if the code itself is from triggeraction, it will work just fine if you call it
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I don't remember very well but i think you can also use TriggerSleepAction when it's not inside a trigger action, ExecuteFunc and ForForce i think.
Now, maybe the number of use inside the same function is limited and there must be have drawbacks, like the lost of response events (Get...).
To be clear, it's just an old random piece of my memory, it should be tested and confirmed.
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
You can use ExecuteFunc with a function to enable TriggerSleepAction usage(not sure if this sentence makes sense in English), however it won't work with ForForce.
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
Why would you even use TriggerSleepAction then?
ExecuteFunc opens a new thread anyway.

When you need to keep your local variables/doing lots of calculations and you don't need it to be instant.(For example creating trackables all over your map at the start)

When you need a little delay and you don't need much precision.(Timed special effect)
 
Level 25
Joined
Feb 2, 2006
Messages
1,686
Wow many answers but I just wanted to ask if .evaluate() does increase the op limit. I will check now if the main function reaches the oplimit even with the .evaluate() calls.
The problem is that war3err doesn't work with the current wc3 version and there is no way to check the oplimit.

The ForForce approach is completely useless in my opinion since .execute() and .evaluate() do automatically make the arguments global variables. Besides I don't understand why TriggerSleepAction() should not work in ExecuteFunc()?! Of course it should?! I've also read that TriggerSleepAction() and TriggerSync.. do increase the OpLimit, so I guess everytime some functions have to be called atomically (which is the case without any waits or syncs) there is the OpLimit.

I am talking about a onInit method here so it is only called once at the game's start.
It is called via:
JASS:
call ExecuteFunc("s__Game_onInit")
so this alone should start a new OpLimit block?

It is called in the function jasshelper__initstructs11076819 which itself is called by:
JASS:
call ExecuteFunc("jasshelper__initstructs11076819")
in the function main() but that should not matter at all since everytime ExecuteFunc() is called the OpLimit should be reset?

So in the onInit function there is the call:
JASS:
// map
			call sc__MapData_init()

which is defined as:
JASS:
//Generated method caller for MapData.init
function sc__MapData_init takes nothing returns nothing
    call TriggerEvaluate(st__MapData_init)
endfunction
which should reset the OpLimit again?

The trigger is created by:
JASS:
set st__MapData_init=CreateTrigger()
    call TriggerAddCondition(st__MapData_init,Condition( function sa__MapData_init))

in the function jasshelper__initstructs11076819.
The trigger condition sa__MapData_init is defined as:
JASS:
function sa__MapData_init takes nothing returns boolean

   local integer i= 0
			loop
				exitwhen ( i == s__MapData_maxPlayers )
				call SetPlayerAllianceStateBJ(Player(i), s__MapData_orcPlayer, bj_ALLIANCE_UNALLIED)
				call SetPlayerAllianceStateBJ(s__MapData_orcPlayer, Player(i), bj_ALLIANCE_UNALLIED)
				set i=i + 1
			endloop
			call SetPlayerAllianceStateBJ(s__MapData_orcPlayer, Player(PLAYER_NEUTRAL_AGGRESSIVE), bj_ALLIANCE_ALLIED)
			call SetPlayerAllianceStateBJ(Player(PLAYER_NEUTRAL_AGGRESSIVE), s__MapData_orcPlayer, bj_ALLIANCE_ALLIED)
			call SetPlayerAllianceStateBJ(s__MapData_orcPlayer, s__MapData_alliedPlayer, bj_ALLIANCE_UNALLIED)
			call SetPlayerAllianceStateBJ(s__MapData_alliedPlayer, s__MapData_orcPlayer, bj_ALLIANCE_UNALLIED)
			call sc__Aos_init()
			call s__Arena_init(GetRectCenterX(gg_rct_arena_outside) , GetRectCenterY(gg_rct_arena_outside) , 0.0 , tr("Sie haben die Arena betreten.") , tr("Sie haben die Arena verlassen.") , tr("Ein Arenakampf beginnt nun.") , tr("Ein Arenakampf endet nun. Der Gewinner ist \"%1%\" und er bekommt %2% Goldmünzen."))
			call s__Arena_addRect(gg_rct_arena_0)
			call s__Arena_addRect(gg_rct_arena_1)
			call s__Arena_addRect(gg_rct_arena_2)
			call s__Arena_addRect(gg_rct_arena_3)
			call s__Arena_addRect(gg_rct_arena_4)
			call s__Arena_addStartPoint(GetRectCenterX(gg_rct_arena_enemy_0) , GetRectCenterY(gg_rct_arena_enemy_0) , 180.0)
			call s__Arena_addStartPoint(GetRectCenterX(gg_rct_arena_enemy_1) , GetRectCenterY(gg_rct_arena_enemy_1) , 0.0)
    			call s__NpcRoutines_init()
			call s__Shrines_init()
			call s__SpawnPoints_init()
			call s__Tavern_init()
			call sc__Tomb_init()
    call Print("MapData init 3 - 1")
			call sc___prototype19_evaluate(18)
    call Print("MapData init 3 - 2")
			call sc___prototype19_evaluate(19)
    call Print("MapData init 4")
			call initMapVideos()
    call Print("MapData init 5")
			call s__Fellows_init() // init after talks
    call Print("MapData init 6")
			call s__AWeather_setMinimumChangeTime(s__Game_weather(),20.0)
			call s__AWeather_setMaximumChangeTime(s__Game_weather(),60.0)
			call s__AWeather_setChangeSky(s__Game_weather(),false) // TODO prevent lags?
			call s__AWeather_setWeatherTypeAllowed(s__Game_weather(),s__AWeather_weatherTypeLordaeronRainHeavy , true)
			call s__AWeather_setWeatherTypeAllowed(s__Game_weather(),s__AWeather_weatherTypeLordaeronRainLight , true)
			call s__AWeather_setWeatherTypeAllowed(s__Game_weather(),s__AWeather_weatherTypeNoWeather , true)
			call s__AWeather_addRect(s__Game_weather(),gg_rct_area_playable)
			call SetPlayerColor(s__MapData_neutralPassivePlayer, ConvertPlayerColor(PLAYER_NEUTRAL_PASSIVE))
			set s__MapData_m_welcomeTalrasTrigger=CreateTrigger()
			set s__MapData_m_welcomeRegion=CreateRegion()
			call RegionAddRect(s__MapData_m_welcomeRegion, gg_rct_quest_talras_quest_item_0)
			call TriggerRegisterEnterRegion(s__MapData_m_welcomeTalrasTrigger, s__MapData_m_welcomeRegion, null)
			call TriggerAddCondition(s__MapData_m_welcomeTalrasTrigger, Condition(function s__MapData_triggerConditionWelcomeTalras))
			call TriggerAddAction(s__MapData_m_welcomeTalrasTrigger, function s__MapData_triggerActionWelcomeTalras)
			set s__MapData_m_portalsHintTrigger=CreateTrigger()
			set s__MapData_m_portalsHintRegion=CreateRegion()
			call RegionAddRect(s__MapData_m_portalsHintRegion, gg_rct_hint_portals)
			call TriggerRegisterEnterRegion(s__MapData_m_portalsHintTrigger, s__MapData_m_portalsHintRegion, null)
			call TriggerAddCondition(s__MapData_m_portalsHintTrigger, Condition(function s__MapData_triggerConditionPortalsHint))
			call TriggerAddAction(s__MapData_m_portalsHintTrigger, function s__MapData_triggerActionPortalsHint)
			set s__MapData_m_talkHintRegion=CreateRegion()
			set s__MapData_m_talkHintTrigger=CreateTrigger()
			call RegionAddRect(s__MapData_m_talkHintRegion, gg_rct_hint_talk)
			call TriggerRegisterEnterRegion(s__MapData_m_talkHintTrigger, s__MapData_m_talkHintRegion, null)
			call TriggerAddCondition(s__MapData_m_talkHintTrigger, Condition(function s__MapData_triggerConditionTalkHint))
			call TriggerAddAction(s__MapData_m_talkHintTrigger, function s__MapData_triggerActionTalkHint)
   return true
endfunction
The output "MapData init 5" is never reached so I will try to place simply more .evaluate() calls.
 
Level 25
Joined
Feb 2, 2006
Messages
1,686
I don't get it. I've placed:
JASS:
// evaluate this call since it contains many many calls which should be executed in a different trigger to avoid OpLimit.
			call Fellows.init.evaluate() // init after talks
but in outputwar3map.j it still calls:
JASS:
call s__Fellows_init() // init after talks
Is this a JassHelper bug?!
The other functions are called via:
JASS:
call sc__Tomb_init()
    call Print("MapData init 3 - 1")
			call sc___prototype19_evaluate(18)
but not all.

Does the JassHelper only call via .evaluate() if it is really necessary even if specified so?!
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
no idea about the bug, but .evaluate does not really reset OPLimit in sense that I got it from this post. Basically, when you call something via .evaluate(), it will take the trigger that was auto-filled with that function on initialization, and then it will run that as triggercondition, so the block you .evaluate()-ed has reset OPLimit, but the function that calls .evaluate is unchanged.

You didnt provide enough information to recreate the bug
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
It's strange that ExecuteFunc allows TriggerSleepAction because it is not a trigger. And there are several ways to not be inside a trigger: TimerStart, ExecuteFunc, ForForce, ForGroup, EnumItems, EnumDestructables, event filters and other boolexprs. Though you may create chains of threads and that may have some propagating properties. Like when you call ExecuteFunc from a trigger thread and that trigger was fired by an event, the new ExecuteFunc-thread will still be able to see event responses.
 
Level 25
Joined
Feb 2, 2006
Messages
1,686
Okay did I get something wrong? I've tried it in a test map and METHODS are indeed not called by TriggerEvaluate() although I explicitely place .evaluate() there. Is this intended?
I am using Vexorian's JassHelper 0.A.2.A but the ChangeLog doesn't state anything about .evaluate() in 0.A.2.B.

edit:
It works calling functions and it works calling methods as well with .execute() but not with .evaluate().

edit2:
I will use Quilnez's suggestion now since my static methods do not have any parameters anyway. It seems that I was wrong about it being useless :D
 

Attachments

  • ExplicitEvaluate.w3x
    17.7 KB · Views: 53

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Just to clear up your obvious confusion on this:
Execute and Evaluate, such as ForForce does not actually reset the OP limit. Rather, it runs the function that is passed to these commands in a seperate thread, not counting what is inside to the OP limit of the currently running main thread.

If you main function has 20.000 maths operations, and at the 10.000 operation you call a TriggerEvaluate with another function inside, nothing about the OP limit in the main thread will change. It's still on 10.000/20.000. However, the function outsourced via TriggerEvaluation will start fresh at 0/20.000.
The only way to "reset" the OP limit for the main thread is if you actually split up your main thread into blocks, either via using TriggerSleepAction(0) or by using a switch case structure and Evaluate/ForForce.
I prefer the latter, for obvious reasons (TriggerSleepAction not being instant).

JASS:
globals
    integer count = 0
endglobals

function main takes nothing returns nothing
    if count == 0 then
        //heavy shit block 1
    elseif count == 1 then
        //more heavy shit
    elseif count == 2 then
        //much wow, such constipation!
    endif
    if count < 2 then
        set count = count + 1
        call ForForce(Player(0), function main)
    endif
endfunction
 
Level 25
Joined
Feb 2, 2006
Messages
1,686
Thanks, I got that already and yes the word "reset" was used wrongly but I meant what you describe here.

The issue is fixed now since it obviously was the vJass bug/feature. I use ForForce now and everything works fine.

I've also reported this bug at http://www.wc3c.net/showpost.php?p=1135508&postcount=3651 but I doubt that anyone there is still active.

I can understand that the optimization would make sense but on the other hand .evaluate() for me was the first method in vJass to start a new thread/OpLimit counter.
 
Status
Not open for further replies.
Top