1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

For loop and wait

Discussion in 'Triggers & Scripts' started by Sk0gsHu[GG]arN, Aug 20, 2019.

  1. Sk0gsHu[GG]arN

    Sk0gsHu[GG]arN

    Joined:
    Jul 4, 2007
    Messages:
    89
    Resources:
    0
    Resources:
    0
    Hey, I'm trying to do a spell that gives you 1+ to stats every second for 40 seconds. I'm using this trigger but it's not working properly. It only does it one time and I guess it's because it doesn't work to use the wait function like this for some reason. Anyone have a suggestion how I could do it in a different way?
    • Attribute Conduction
      • Events
        • Unit - A unit Starts the effect of an ability
      • Conditions
        • (Ability being cast) Equal to Attribute Conduction
      • Actions
        • Custom script: local unit udg_unit
        • Set unit = (Casting unit)
        • Unit - Add (Item) Attribute Conduction (+1 per lvl) to unit
        • For each (Integer A) from 2 to 40, do (Actions)
          • Loop - Actions
            • Wait 1.00 seconds
            • Unit - Set level of (Item) Attribute Conduction (+1 per lvl) for unit to (Integer A)
     
  2. Cespie

    Cespie

    Joined:
    May 21, 2019
    Messages:
    344
    Resources:
    0
    Resources:
    0
    My guess is that the local unit gets garbage collected once the wait is executed inside the loop.
    You can verify if this is the case by putting a reference to the unit inside the loop and showing it through a game message. You can also output the iteration of the loop in a game message as well, to check if the loop is actually running.

    There's a lot of general advice around this forum that discourages using waits in loops. Apparently, they're a real can of worms.
    An alternate approach would be to use a periodic event and one of the many approaches to keeping track of the units and their remaining inclusions in the loop.
    I personally like a combination of a unit group and a hashtable.
    You loop through the unit group which contains every unit currently under the 40 second effect, and then find their current amount of stacks using the unit as a key in the hashtable. If the stack count has reached 40, you remove the unit from the group instead of increasing stats.
     
  3. Sk0gsHu[GG]arN

    Sk0gsHu[GG]arN

    Joined:
    Jul 4, 2007
    Messages:
    89
    Resources:
    0
    Resources:
    0
    Alright so I haven't used hashtables before and couldn't get it to work, can you see what I'm doing wrong? I followed a simple tutorial so it should be working?
    • Attribute Conduction
      • Events
        • Unit - A unit Starts the effect of an ability
      • Conditions
        • (Ability being cast) Equal to Attribute Conduction
      • Actions
        • Unit - Add (Item) Attribute Conduction (+1 per lvl) to (Casting unit)
        • Hashtable - Save 39.00 as 0 of (Key (Casting unit)) in attConHash
        • Unit Group - Add (Casting unit) to attConUnitGrp
    • Attribute con periodic
      • Events
        • Time - Every 1.00 seconds of game time
      • Conditions
      • Actions
        • Unit Group - Pick every unit in attConUnitGrp and do (Actions)
          • Loop - Actions
            • -------- Load remaining time --------
            • Set real = (Load 0 of (Key (Picked unit)) from attConHash)
            • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
              • If - Conditions
                • real Greater than 0.00
              • Then - Actions
                • Unit - Set level of (Item) Attribute Conduction (+1 per lvl) for (Picked unit) to ((Level of (Item) Attribute Conduction (+1 per lvl) for (Picked unit)) + 1)
                • Hashtable - Save (real - 1.00) as 0 of (Key (Picked unit)) in attConHash
              • Else - Actions
                • Unit - Remove (Item) Attribute Conduction (+1 per lvl) from (Picked unit)
                • Unit Group - Remove (Picked unit) from attConUnitGrp
                • Hashtable - Clear all child hashtables of child (Key (Picked unit)) in attConHash
     
  4. Ceday

    Ceday

    Joined:
    Feb 22, 2010
    Messages:
    1,063
    Resources:
    0
    Resources:
    0
    I think your problem is you didn't initialize the attConHash variable. In a separate trigger that runs at map initialization or something similar add these:
    • Hashtable - create hashtable
    • Set attConHash = last created hashtable


    Also it is better to use integer variables instead of real variables for increasing/decreasing counters.
     
  5. Sk0gsHu[GG]arN

    Sk0gsHu[GG]arN

    Joined:
    Jul 4, 2007
    Messages:
    89
    Resources:
    0
    Resources:
    0
    Yes I have it i just forgot to copy it aswell. Thanks for the tip with variables
     
  6. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,905
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Incorrect, wc3 does not have garbage collection. This is the whole reason we have memory leaks and reference leaks.
    Now why might that be? Use your brain. A wait in a loop that uses global variables for the loop start/end/current count... hmm I bet that has a massive opportunity to get overwritten during the wait. The solution: use a custom integer loop and then shadow that integer locally within the function like you did with the unit variable (so multiple simultaneous casts don't muck it up). This method, while technically inferior to using a periodic event, is considerably simpler to write:
    • Attribute Conduction
      • Events
        • Unit - A unit Starts the effect of an ability
      • Conditions
        • (Ability being cast) Equal to Attribute Conduction
      • Actions
        • Custom script: local unit udg_unit
        • Custom script: local integer udg_LoopInt
        • Set unit = (Casting unit)
        • Unit - Add (Item) Attribute Conduction (+1 per lvl) to unit
        • For each (Integer LoopInt) from 1 to 40, do (Actions)
          • Loop - Actions
            • Unit - Set level of (Item) Attribute Conduction (+1 per lvl) for unit to (Integer A)
            • Wait 1.00 seconds
        • Unit - Remove (Item) Attribute Conduction (+1 per lvl) from unit

    Note I changed the loop bound to start at 1 and put the increase in level before the wait. Why? Without doing that the unit instantly loses the ability as soon as it gets to +40. With the order flipped it will retain the ability for 1 second at +40 attributes.

    But there's still another solution. Since you're increasing the level of the ability for the unit, you can use the current level of the ability as your counter! When you try to increase the ability to level 41 the spell is over.
    • Attribute Conduction
      • Events
        • Unit - A unit Starts the effect of an ability
      • Conditions
        • (Ability being cast) Equal to Attribute Conduction
      • Actions
        • Set unit = (Casting unit)
        • Unit - Add (Item) Attribute Conduction (+1 per lvl) to unit
        • Unit Group - Add unit to ConductionGroup

    • Attribute Conduction
      • Events
        • Time - Every 1.00 seconds of game-time
      • Conditions
      • Actions
        • Unit Group - Pick every unit in ConductionGroup and do (Actions)
          • Loop - Actions
            • Set unit = (Picked unit)
            • Set TempInt = (Level of (Item) Attribute Conduction (+1 per lvl) for unit) + 1
            • If (All conditions are true) then do (Then actions) else do (Else actions)
              • If - Conditions
                • TempInt greater than 40
              • Then - Actions
                • Unit - Remove (Item) Attribute Conduction (+1 per lvl) from unit
                • Unit - Remove unit from ConductionGroup
              • Else - Actions
                • Unit - Set level of (Item) Attribute Conduction (+1 per lvl) for unit to TempInt
     
  7. Dr Super Good

    Dr Super Good

    Spell Reviewer

    Joined:
    Jan 18, 2005
    Messages:
    25,547
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    That is not how local variables work.
    Both JASS and Lua have garbage collection. Just complex objects like locations, groups and forces are not collected.

    In any case the issue is described by Pyrogasm very well. The for integer A and B loops are controlled by global variables so multiple simultaneous trigger threads are prone to interfering or causing race conditions with them.

    The use of waits is discouraged purely because the underlying TriggerSleepAction is a network synchronized approximation of real time and not game time. Timers and trigger events are accurate to some unit of game time and hence much more desirable for anything that alters game state.
     
  8. Cespie

    Cespie

    Joined:
    May 21, 2019
    Messages:
    344
    Resources:
    0
    Resources:
    0
    If a local shadow of a global variable doesn't need to be cleaned at the end of a trigger, what exactly happens to it then?
    Genuine question, I have no idea.

    You are a paragon of civil discourse.

    I know this, but I have seen issues with waits in loops that weren't based on variable conflicts, and I have seen other issues pointed out on this forum many times.
    From what I've gathered, there's far more issues with waits in loops than the obvious issues with variable conflicts. Issues such as the poor accuracy of the wait action leading to synchronization issues, and I bet a whole lot more.
    It's kind of a low blow to just strawman a general statement I made, in which I clearly admit my own incomplete understanding of the issues, and then use it to call me an idiot. I'd prefer if you found other ways to vent whatever aggressions you are seemingly harboring. I carry no illusion that I have a better understanding of this software than you, but I'd appreciate your input a whole lot more if it wasn't so condescending.

    That's clever, you should do this OP.

    I wouldn't know. It was a "guess", which is why I asked him to test it out, in lieu of people like you and Pyrogasm who'd know the answer. Thanks for clarifying though.

    I was aware that this was an issue, but it's still very helpful that you're clarifying the technical reason for it, as well as it being the primary concern of the community.
    I am not entirely certain that it's the only 2 issues with waits in loops though. Take OPs trigger for example. The trigger obviously isn't MUI in any case, but with a 1 second interval on the loop, it kinda sounds like the issue isn't related to MUI or variable conflicts. Assuming that there's only 1 unit casting the ability (which I assume would be the case in at least one of his tests, with only a 1 second interval to go on), why would OPs loop only run successfully once? So far, the focus has been on variable conflicts, but this doesn't even seem to be caused by that. Am I missing something, or do you have an idea what might be causing the issue, if the local variable isn't lost one way or another?
     
  9. Sk0gsHu[GG]arN

    Sk0gsHu[GG]arN

    Joined:
    Jul 4, 2007
    Messages:
    89
    Resources:
    0
    Resources:
    0
    Thanks everyone for your help! It turns out it's working but it doesn't wanna change stats when it levels up for some reason, very weird. I'm basing the spell of +1 stats, anyone familiar with this problem? attcon.jpg
     
  10. Dr Super Good

    Dr Super Good

    Spell Reviewer

    Joined:
    Jan 18, 2005
    Messages:
    25,547
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    Same as all local variables...

    Local variables are stored in the function stack frame. When the function returns that stack frame is discarded/popped. No garbage collector involved. This is because the time at which the storage for the local variable must be freed is known at compile time and hence does not need a garbage collector to free.

    Technically with JASS the stack frame is a table. However the same mechanics apply in that the local table is discarded (freed) when the function returns.
    My guess is that other triggers are using Loop A within that time frame.

    Look at the resulting code for such a loop from 1 to 10...
    Code (vJASS):

        set bj_forLoopAIndex = 1 // This is a global variable
        set bj_forLoopAIndexEnd = 10 // This is also a global variable
        loop
            exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
            set bj_forLoopAIndex = bj_forLoopAIndex + 1
        endloop
     

    Hence if any other trigger in the map ran a Integer A loop during the time the trigger is running the result is that the value of bj_forLoopAIndexEnd is modified and so the loop ends at the incorrect time. If that value is small the loop might not seem to run at all.
    Item abilities were not designed with level ups in mind. Although some do support them, others such as health or mana bonus do not.

    Try using a unit version of the attribute bonus hero ability. This is the ability that all player heroes have in the TFT Orc campaign. That does support multiple levels.

    Be aware that every time the ability is cast it will reset the ability level to 1 again and ramp back up to 40. If this is not desired you will need to set it to {the level of the current ability level + 1}.

    Abilities with hundreds or thousands of levels are not very good for map load time or file size. You an either use actions to modify hero stats directly or you can use an attribute mod system which uses a set of stat modification abilities in binary to achieve much better range with fewer entries. To put it in perspective 32 such abilities for a single stat would give a potential range of -2,147,483,648 to +2,147,483,647 which would otherwise require an ability with 4,294,967,296 levels which is likely unsupported by Warcraft III.
     
  11. Sk0gsHu[GG]arN

    Sk0gsHu[GG]arN

    Joined:
    Jul 4, 2007
    Messages:
    89
    Resources:
    0
    Resources:
    0
    Thanks so much it works like a charm :D! I'm only gonna make one spell like this so it's fine but thanks for the information
     
  12. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,905
    Resources:
    1
    Spells:
    1
    Resources:
    1
    As DSG specified: complex objects (handles, agents, and their derivatives) do need to be cleaned. Primitives (real, int, bool, string) do not. To this end I should have added a Custom script: set udg_unit = null at the end of the trigger as well to properly avoid the reference leak of the locally shadowed unit variable.
    The accuracy is not the problem, and using a wait in a loop will not cause any sync errors. As DSG stated above the Wait action does not progress in game-time but rather in real-time. If you wait 10s and at that exact moment a player starts lagging and the 'drop player' screen appears for 9s the wait will be 'over' 1s after the game resumes. A wait under the minimum threshold of ~0.27s will not be 'accurate' to the time you told it to wait but that's not an issue with loops but rather with Wait itself.
    No that's it. There's no "can of worms" involved here, just logically following through everything that would happen when you use a wait command inside a loop.
    First off I would like you to specify what exactly you see as me attacking you in a strawman argument, because that is definitely not happening here. "Use your brain" might be unkind but it's not an attack, nor is it venting viciously at you. I didn't call you stupid or belittle you as you claim; I suggested that the answers should be obvious if you thought through the issue at hand instead of waving your hands and saying "I dunno why but people say this is bad". Second, you in particular do this thing where you show up in a thread and say something like "I have no idea about any of this stuff but if I had to guess..." before listing a bunch of bologna that is 50% related and 50% unrelated or untrue information. It bothers me and it likely leads the people you are trying to help astray or to chase red herrings. I can't stop you from doing this but I can correct it, which I often do. This thread is a great example of your common "it seems like doing X is bad" statement where you fail to actually expand on:
    • What exactly is happening in X
    • Why X is bad
    • Why one should do Y instead of X
    • How to do Y
    Just stop for a second and think about why X seems to be discouraged. For what reason might it be a poor course of action? What effects are occurring? God forbid, maybe even test it yourself so that you understand more thoroughly. (And yes I did read your comment in this thread about not being at a computer with a WE.) That's all I want from you. I'm just annoyed with the way you respond like you are contributing something but are in fact just parroting text you neither understand nor are sure of like it's the gospel. That you admit when you don't know something is commendable and I appreciate that, but please follow through and actually learn.
     
  13. Cespie

    Cespie

    Joined:
    May 21, 2019
    Messages:
    344
    Resources:
    0
    Resources:
    0
    Yeah, I knew the thing about primitives, but what I didn't understand was why nobody told him to null the local unit, since this forum is usually quite militant about pointing out leaks, but if that actually DOES leak, then I get it. I just assumed that it didn't, since nobody was pointing it out, which is why I was asking.

    I am not talking about game sync in the technical sense, but game design sync in an abstract sense. Say that you want certain events to happen within very accurate intervals in your map, having loops blasting through short waits, will cause this to start getting a bit fussy. I had great issues with this in a map where I wanted 12 players to have a more or less identical and syncronized experience in 12 different locations, but I ended up having to use a periodic event loop to achieve the proper accuracy instead.

    Semantics, but yeah, technically it's the wait. The issue compounds when it's used in rapid succession though, which loops would be a great example of.

    I really wouldn't know, I myself had a really odd issue with a loop once, that couldn't be explained by a variable conflict. I solved it by queuing actions outside of the loop instead of relying on the loop itself to offset with the wait (which was 2 seconds), but I went through a lot of hoops to try to keep it inside that loop, without luck.
    Maybe I just didn't know what the hell I was doing wrong, but I tried more or less every solution I could find, without luck. To me, that at the very least shows some form of instabillity, that I'd prefer to work around as a general practise. That's what the take-away from the first part of my message was: "This stuff has some issues, but there's a less error-prone way to go about it." That's all. You take it as if it was some clause in a legal document.

    The strawman would be you basing your retort on the assumption that I didn't understand the concept of variable conflicts, when what I actually said, was that I wasn't aware of the full extent of issues related to using wait inside of a loop.

    Not only was this, once again, based on a total assumption (which was wrong) on your part, you are also in no position to dictate how others perceive your actions. This isn't a court room, I am not trying to hold you accountable, I am telling you that I don't like the way that you are talking to me. There's no logical argument you can make against that, because it isn't a logical matter.

    Given the absence of a concrete answer, I am offering suggestions for angles that people can use to diagnose the issue, as well as the methods that they can use to quickly debug it themselves. Sure, I could spend a few hours searching frantically to find the information on par with what you've build up in your 12 years of roaminig around this forum, and then spoonfeed him the answer. That'd be ideal, wouldn't it? But it's not always the case, nor does it improve his ability to test his own code in the future. You like to go to great lengths to explain the exact mechanics to people, and kudos for that. You are a real asset to this community for doing so. But personally, I like to teach people some basics for problem-solving instead. Then based on the responses to the tests people run, I can often piece together the cause of the issue from that, and offer up a functional solution. From that exchange, not only do I show people a process to finding issues and devising solutions, I also learn something about the mechanics of the game myself.
    Hell, sometimes I can see the issue at first glance, and then I just offer it up instead. I've done that multiple times as well. But stating that I shouldn't be allowed to offer up advice for problem diagnostics, because the only acceptable response is to offer up a full service-desk guidance, is quite opinionated of you. I make it a great point to clearly state whenever something is solid information, and when I'm just spitballing an approach to narrowing down on the issue. This isn't the easiest or quickest way to go about solving an issue, but if noone offers a surefire answer, it's a good way to get the ball rolling.

    I didn't fail to expand on it. I clearly stated that I didn't know X exactly, and offered a possible explanation, as well as an approach to testing that explanation, as well as what was essentially an execution log for the loop itself, to catch issues like variable conflicts. You seem to skip over that part quite readily.

    The premise here is that I'd know the exact answer to X in the first place.
    Once again, the premise is that I know the exact answer to X.
    Not only did I actually do this, but OP actually started working on an implementation of it as a result of my post.
    I didn't wanna write out a complete guide or answer to it, because this forum has plenty of guides, and I wouldn't know how much OP would be able to do for himself. Turns out, he was actually able to start working on that solution himself, so I'd say that taught him a lot more than copy-pasting a ready solution, as well as saving me time that I would have spent doing it for him. If he didn't have the first idea how to implement the solution I suggested, he'd be free to say that, and then I'd point him in the direction of the information or even help him out, but why would I waste time doing that on the assumption that he couldn't do it himself?

    I did. I knew that variable conflicts was an issue, as well as the accuracy of the timer presenting an issue at times. But I wasn't 100% sure about whether or not there was something other than this that I didn't know, so I decided to simply leave it at "I'm not sure, there's some issues with this that you can read about on this forum, but here's an alternative approach that I know will not suffer the same issues:"
    That's not me "not thinking", that's me not wanting to research every possible angle to an issue for hours, finding threads dating back several years, accounting for issues that may have been patched since described in said old posts, and etc etc etc etc.... when I know full well that there's people like you and DSG who'd be able to spit the answer out at in a second. It's just not feasible for me to chase down the "X-road", when I might as well jump straight to "Y" and leave X to people who know more about it than me. What I tried communicating to him, was that there was issues with X, that I wasn't entirely sure what those were in their entirity, and offered him a couple of angles to diagnose X himself.

    I think this is where you would, in my situation, use the opportunity to point out how this contradicts itself.
    Instead, I'd rather a moment to elaborate on my exact circumstances, and my opinion on this in general...
    I can't test this when I'm not at home, you yourself said why, and when I am at home, I have a wife and kid that I'd rather spend time on. I actually have tested issues for people multiple times though. Hell, I'm even helping out a few people with their maps on a running basis. I just don't have time to test every single little issue that's posted on the forum for people, nor do I want to, when A) There's people that already know these answers likely to bump in later, and B) Because I'd rather have them test it themselves. I have no desire to know every little nook and cranny of this horribly outdated and relatively irrelevant software, it's just a hobby of mine. I'd rather dedicate my time to solving issues that nobody else has bothered helping people to solve, or people have attempted to solve themselves without success. When I responded to this thread, it had only been up for a few hours or so. I knew full well that there'd be people coming in and explaining X to him, I wasn't too concerned about that, I just wanted to point him in the direction of Y in the meantime, so he could start getting to work on a solution, instead of wasting time and frustration on X. That was the essence of my post: Y. It's also what OP chose to take from the post. Yet, you came in and focused on X. To a certain extent, it's great that you did. My guess was wrong, it needed to be corrected, and you, with the extension of DSG, knew all the right details to X. What wasn't constructive, was you lambasting me for failing to provide something that I very clearly didn't aim to provide in the first place. Hell, I even dumbed down my explanation of it, just to make it perfectly clear, that I wasn't attempting to make any statement on the matter. It seems like that just triggered you even harder though, so the joke's on me I guess.


    Let me cut out a part of this quote for you:

    Now, the first half says: "You say this like it's objective truth" (like it's the gospel)
    And then in the very next sentence you say: "You admit not knowing it"

    I think this sort of summarizes my viewpoint on this whole debacle. You wanna accuse me of doing something that I didn't do. I never claimed to know X, let alone like "it's the gospel!", in fact, I quite clearly stated the opposite, which is what you admit in the second part.

    I'm not really trying to "get the best of you" here, I just wanna point out how you escalate the situation with your hyperbolic expressions and needlessly hostile attitude.
    I am fully open to the idea that there's things I could improve about how I help people on this forum, and especially from people like you and DSG, who have a lot of experience in that area. That's awesome, I love to learn, and I love self-improvement. I take a lot of the things you write into consideration, and I will be phrasing my future posts with more careful wording in mind, to avoid inadverdently spread misinformation. I was under the impression that I had attempted doing just that, but if I had done that properly, I guess we wouldn't be having this conversation right now.

    That's my take-away from this. What's yours?

    Also, I just wanna adress the rest of this part real quick:

    I wanna make it perfectly clear that I am not in any way motivated by attention or seeking unwarranted acknowledgement. I just like helping people. I don't come into threads parroting stuff I don't understand. I came in saying what it was I didn't understand, offered a possible explanation, a way to test if said explanation was somewhere in the right direction or totally off, a way to test a separate possible issue, and a solution to the problem itself, which I'd argue is the most substantial thing that I did in fact contribute, namely, Y.
    Sure, I didn't contribute X, but we've been over that. Saying that Y is useless without X is wrong. It's better to have both, but they aren't mutually inclusive.
    As for following through and learning. Well... I did! If you scroll back up you'll find several examples of that. Including this response.
    I'd love it if you could take something away from this as well. Have a look at DSG's responses. I think they set a great example for something I'd wish that you'd reflect on a bit yourself.

    In any case, if you wanna talk this out, further, how about we continue on direct messages instead? I'd rather not have this flare up again, and like I've said, I am totally open to input on how I conduct myself around this forum, since it's clearly seen as problematic. I harbor no disrespect or ill feelings towards you, I would just love if we could sort this out in a way that we're both comfortable with.
     
    Last edited: Aug 22, 2019