1. Join Texturing Contest #30 now in a legendary battle of mythological creatures!
    Dismiss Notice
  2. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice
  3. Melee Mapping Contest #3 - Results are out! Congratulate the winners and check plenty of new 4v4 melee maps designed for this competition!
    Dismiss Notice
  4. The winners of our cinematic soundtrack competition have been decided! Step by the Music Contest #11 - Results to check the entries and congratulate the winners!
    Dismiss Notice
  5. Check out the Staff job openings thread.
    Dismiss Notice

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

Discussion in 'Triggers & Scripts' started by Barade, Jun 14, 2015.

  1. Barade

    Barade

    Joined:
    Feb 2, 2006
    Messages:
    545
    Resources:
    1
    Maps:
    1
    Resources:
    1
    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?
     
  2. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    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.
     
  3. Zwiebelchen

    Zwiebelchen

    Joined:
    Sep 17, 2009
    Messages:
    6,779
    Resources:
    11
    Models:
    4
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    JASS:
    4
    Resources:
    11
    Just split your main function into several blocks, using TriggerEvaluate to fire each.
     
  4. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,180
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    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.
    Code (vJASS):

    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: Jun 15, 2015
  5. Zwiebelchen

    Zwiebelchen

    Joined:
    Sep 17, 2009
    Messages:
    6,779
    Resources:
    11
    Models:
    4
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    JASS:
    4
    Resources:
    11
    That's already a good enough advantage for me. So, yeah, better use ForForce instead of TriggerEvaluate.

    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.
     
  6. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,180
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    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.
     
  7. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    TriggerSleepAction(0)
    , while not instant anymore, you dont need to change locals into globals, because trigger sleep action will store the whole stack somewhere
     
  8. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,180
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    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.

    Code (vJASS):

    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()
     
  9. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    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
     
  10. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    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.
     
  11. Ceday

    Ceday

    Joined:
    Feb 22, 2010
    Messages:
    1,003
    Resources:
    0
    Resources:
    0
    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.
     
  12. Zwiebelchen

    Zwiebelchen

    Joined:
    Sep 17, 2009
    Messages:
    6,779
    Resources:
    11
    Models:
    4
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    JASS:
    4
    Resources:
    11
    Why would you even use TriggerSleepAction then?
    ExecuteFunc opens a new thread anyway.
     
  13. Ceday

    Ceday

    Joined:
    Feb 22, 2010
    Messages:
    1,003
    Resources:
    0
    Resources:
    0
    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)
     
  14. Barade

    Barade

    Joined:
    Feb 2, 2006
    Messages:
    545
    Resources:
    1
    Maps:
    1
    Resources:
    1
    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:
    Code (vJASS):

    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:
    Code (vJASS):

    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:
    Code (vJASS):

    // map
                call sc__MapData_init()
     


    which is defined as:
    Code (vJASS):

    //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:
    Code (vJASS):

    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:
    Code (vJASS):

    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.
     
  15. Barade

    Barade

    Joined:
    Feb 2, 2006
    Messages:
    545
    Resources:
    1
    Maps:
    1
    Resources:
    1
    I don't get it. I've placed:
    Code (vJASS):

    // 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:
    Code (vJASS):

    call s__Fellows_init() // init after talks
     

    Is this a JassHelper bug?!
    The other functions are called via:
    Code (vJASS):

    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?!
     
  16. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    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
     
  17. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,018
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    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.
     
  18. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    propagation also goes through TriggerEvaluate/TriggerExecute
     
  19. Barade

    Barade

    Joined:
    Feb 2, 2006
    Messages:
    545
    Resources:
    1
    Maps:
    1
    Resources:
    1
    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
     

    Attached Files:

  20. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    no idea, most likely another bug in the compiler