Wc3C.net

Wc3C.net (http://www.wc3c.net/forums.php)
-   JASS/AI scripts tutorials (http://www.wc3c.net/forumdisplay.php?f=650)
-   -   Timers, the Tricks and the Traps (http://www.wc3c.net/showthread.php?t=89072)

Rising_Dusk 11-14-2006 01:54 AM

Timers, the Tricks and the Traps
 
T I M E R S
The ins and the outs
The tricks and the traps

Understanding the Timer
Expand JASS:


The timer is the most accurate means in Warcraft III coding to render short intervals of time. Because call TriggerSleepAction() and call PolledWait() are so inaccurate in short intervals and that playing on BNet does nothing but compound this inaccuracy, Jassers needed to find a way to measure short intervals of time for fast acting and reliable spells and code. Timers were the answer to that call.

Make note, because a timer variable extends a handle, you will want to nullify any locally declared timers at the end of your code to prevent leaks. For help fixing memory leaks, see PipeDream's tutorial here.
How Timers Work
The below tidbit of code is the native used for starting a timer variable. The following shows the arguments of the TimerStart() native.
Expand JASS:
If the native wasn't self-explanatory, let me explain the arguments one by one.
  • The first argument is the timer itself. Which timer are you starting?
  • The second argument is the interval of time after which the timer callback will run. (See fourth argument)
  • The third argument is a boolean asking if you want the timer to automatically repeat itself indefinitely when it finishes.
  • The fourth argument is the function that will run when the timer's interval has completed.
How to Use Timers
Okay, so now that you realize what a timer is and how it works, you need to know how to apply them to your code. There are multiple ways with which they can be useful, but for our purposes we're going to make a very simple spell that will fire a missile from the caster to the target after 1/4 second.

Pretty simple spell, but as you can see we need to accurately create that missile after 1/4 seconds have elapsed. Since the other methods are miserable, we have thus decided to use timers. For the purposes of this demonstration, I will use global variables to move variables between functions.

This first step assumes you understand how to create a trigger, add it to a map, and practically just know the basics of JASS. If you don't, I suggest reading Vex's Introduction to Jass tutorial here.
Expand JASS:
Now, assuming you've been able to follow the basic steps of creating a spell, let's jump into how we make those nasty timers work. For the following steps, I'm not going to show the entire spell but just the parts that directly apply to timers and how to work them. This isn't a spell-making tutorial, afterall.
Expand JASS:
This is the basis of every timer declaration and use; declare, call, nullify.
Now fill in the blanks of the arguments and store the units.
Expand JASS:
Alright, now we've finished declaring and starting the timer! Now when a hero casts the spell, the timer will begin. We're not done yet, though, we still need to run a function when the timer completes.
Expand JASS:
See the part of the code highlighted in red? That is an important aspect of using a timer. When you're totally done using the timer, you need to destroy it so it does not leak (Or recycle it, see below). So now you know the very basics of using a non-periodic timer. Go out and experiment with them on your own now!
Periodic/Repeating Timers
Expand JASS:
By setting the red area above to true, the timer will automatically run again after it completes the interval of time you specify. This can be very useful in creating spells that do something over a period of time in very short intervals, such as moving units.
Moving Values to the Callback Function
Game Cache Applications
Expand JASS:
What if we don't want to use global variables? The next few steps will require either enough knowledge of game cache to substitute the cache natives in, or enough knowledge to use the Handle Variables coded by KaTTaNa. The Handle Variable system can be found here with a tutorial for use here by KaTTaNa as well.
Expand JASS:
That is what the handle-variable enhanced function would look like. It's pretty simple if you understand the concept of cache. The timer callback looks something like this --
Expand JASS:
You will also notice I used the H2I()/I2Timer() technique. It is reviewed just below, don't worry.

Griffen's TimerAttach Function
There is also a trick discovered by one of our resident project development moderators, Captain Griffen. This shows us an interesting way to attach integers to a timer, which can be useful in some cases.

Expand JASS:
As you can see hopefully, it allows us to attach a single integer to a timer with which we can get via TimerGetRemaining(). Original post regarding it can be found here. Notably, this process "actually" stores a real to 8 digits.

As soon as you try to store something with more digits than that (A unit type for example), the method isn't accurate. It appears as though this is simply a limitation of timers. In this case, cache or vJass applications may be useful. (See above)

vJASS Applications
vJass, through the Jass NewGen pack linked below, allows for excellent passage of multiple values through functions into other functions. You can store several things into a struct, or a group of variables, and then all you have to move between functions is the struct reference which is an integer. All of the type safety issues of game cache vanish if you're just moving integers, making cache and vJass powerful in unison. There are of course more advanced methods to attaching values to timers, you can find several systems regarding it in WC3C's System resource area.
Timer Efficiency
Timer Recycling
Many advanced users will use a process termed recycling timers to prevent the need to ever destroy a timer. Effectively, there is an array of timers where whenever you need a timer, you pull it from the stack and whenever you're done with a timer you return it to the stack to be used later. It's very simple in principle and in code, as you'll see below.

A bit of code written by our Technical Director, Vexorian, reveals this in greater detail. The original post can be found here. The code shown below requires preprocessor help to fit into WC3, which can be done through the Jass NewGen Pack located here.

Expand JASS:
Once you replace CreateTimer with NewTimer and DestroyTimer with ReleaseTimer you no longer have to care about setting timers to null nor about timer related issues with the handle index stack.This means you no longer need to worry about destroying timers or whatnot, also eliminating potential bugging of handle indicies. This has very quickly become the most efficient and valuable way to handle timers in any map. I highly recommend using this if you plan on using timers with any relative frequency in your maps.

Pitfalls to Timers
Timer Nullifying
This is perhaps one of the most annoying issues with the timer variable. While the timer is very useful for jassers all over the world, there is a severe pitfall if it is used in conjunction with game cache storage. If you nullify a locally declared timer with set SomeTimer = null and you've set handles onto that timer, there is a chance the handles will return null the next time you attempt to recall them. This bug can be averted nearly entirely with the above timer recycling, since it removes the need to ever nullify timers. (As they are re-used)

Expand JASS:
By declaring your timers as integers, you avoid having to nullify timers altogether. The problem here is that by declaring them as integers you lose type safety. This means you have to be careful in how you handle those integers or very unpredictable bugs may surface in your maps.

Example:
Expand JASS:
There are other options to fix this issue, all addressed in this tutorial by PipeDream.

Destroying An Unfinished Timer
This issue is a little more subtle than some of the others. If in some function you call DestroyTimer() on a timer that might not have finished running, it can fail some handle indicies as well as cause bugs in-game. Fortunately, the fix is relatively simple.
Expand JASS:
Pausing the timer before destroying it will fix any issue this could have caused.

TimerGetElapsed()
If in the off-chance you happen to be working with a periodic timer, you CAN NOT get the total time the timer has been running by calling TimerGetElapsed()! If you want to maintain an index of how long the timer has been running, you'll need to store an integer to the timer that increments once every iteration of the timer callback.
Conclusion
The aforementioned techniques and coding maneurisms are all of the basics as well as most of the advanced things that can be done or done to timers. What has been covered in this tutorial is everything your everyday mapper will find a need for. If there is any aspect to timers you feel this tutorial has left out, please let me know either by private message or posting in this thread and I will update it accordingly.

Thank you for reading!

Vexorian 11-14-2006 01:58 AM

It IS a good tutorial, but I wouldn't approve it, it is outdated... Timers shouldn't be destroyed but recycled, really. It saves us of all the esoteric things about when to set timers to null. It is safe and doesn't leak, since you need to pause the timer to recycle it you don't worry about the "destroyed timer still expires" bug either.

moyack 11-14-2006 03:01 AM

I like it, it's useful and I think it has all that a JASS noob like me needs.

Good Job Rising :)

blu_da_noob 11-14-2006 12:52 PM

Couple of comments:
I suggest you mention this bug found by Griffen. It can be useful at times.
Kept for reference: scratch the below, Vex is right. I think you might have used repeating in some place though, so might wanna change that for consistency.
Hidden information:
There are also a few places where you talk about 'periodic timers'. I think calling them repeating timers would be a little more consistant and logical (periodic is a GUI word :P).

Vexorian 11-14-2006 12:55 PM

periodic is not a GUI word.

native TimerStart takes timer whichTimer, real timeout, boolean periodic, code handlerFunc returns nothing

Rising_Dusk 11-14-2006 05:16 PM

Updated to remove the only instance of the word "repeating" in the tutorial.
Updated to include the bug Griff discovered.

And if it is obsolete... Well I have no way of updating it to include "recycling" timers.
I didn't even know that was possible, let alone how to do it such that I could explain it to someone else.
*Sigh*

Captain Griffen 11-14-2006 05:44 PM

Actually, you ought to point out my method actually stores a real, rather than an int. Normally, this isn't significant, and is fine for 8 digit numbers (say, handle indexes). With 10 digits, it will fail (eg: unit types). Reals simply don't have the accuracy.

Rising_Dusk 11-14-2006 06:36 PM

Updated again to include a (hopefully) comprehensive explanation of timer recycling as well as a better explanation of Griff's trick.
Hopefully it's more complete as a whole now.

Rising_Dusk 11-15-2006 12:48 PM

Just a thought --

Not to seem picky, but I think maybe this was placed in the wrong section.
I hardly think any "beginners" would find it useful how a timer functions in jass.
If anything, I think it would fit best in the "JASS" tutorial section.
The entire tutorial is virtually made of Jass, afterall.

taste 12-23-2006 04:33 PM

thanks very much for the tutorial. it's very helpful. one question though maybe kind of directed at vexorian because it's about his code... how do i insert that without a preprocessor.. or with one, if i knew where to obtain one of those?

Vexorian 12-27-2006 06:53 PM

The processor is jasshelper and there's a link to it in my signature(bellow):

taste 12-29-2006 06:11 AM

yes i'm very grateful for that. i've also got wehelper as well. and the caster system too. blizz need to include all this in the next patch.

Maley 04-18-2007 05:19 PM

Quote:

you no longer have to care about setting timers to null
Does that mean, I don't have to set a local timer variable to null or did I get that wrong?

WFR Maley

Euroset 05-10-2007 01:26 AM

This is a greatest tutorial in the world!!! It contains ALL, what can help about timers (with links) - very good. 6/5, 100/10 :)

Rising_Dusk 05-10-2007 02:53 AM

I'm glad it was helpful. :)


All times are GMT. The time now is 11:20 AM.

Powered by vBulletin (Copyright ©2000 - 2019, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services