Snippet Gametime

Solmyr

Ultra Cool Member
Reaction score
30
Gametime​
Introduction:


Gametime is a snippet that is used to provide a standardized platform for tracking the duration of the game using a global timer and a time counter.

It is written in Zinc and requires the Jass NewGen Pack along with the latest version of JassHelper.​


Credits:

  • Vexorian for Zinc / JassHelper and showing that timers with high timeouts can be a bit inaccurate.
  • All of the contributors to the NewGen editor.

The snippet:
JASS:
/**  
 *                               GAMETIME
 *                               written by: Solmyr
 *
 *  Requires the latest version of JassHelper.
 *  
 *  This is just an ordinary snippet that you might find useful.
 *
 *  The main purpose is to simply provide a standardized platform for tracking the
 *  duration of the game using a global timer and a time counter.
 *  It is done the way it is because timers with high expiration times can
 *  a bit inaccurate.
 *
 *  The output can be:
 *  
 *    1. A real that is returned by the Time.getExact(), the Time.getRounded() and the 
 *       Time.getDecimal() functions. The first two return the whole elapsed game time,
 *       either in an exact accurate or a rounded manner, while the third one returns the
 *       decimal part of the currently elapsed time.
 *
 *    2. An integer that is returned by the Time.getSeconds() function. The integer
 *       equals the amount of fully elapsed seconds.
 *       
 *    3. A string that is returned by the Time.getStamp() function. The string represents 
 *       a timestamp in the HH:MM:SS format. 
 *
 */

//! zinc
library Gametime {

    public struct Time extends array {

        private {
            static timer mainTimer = CreateTimer();
            static integer timeCounter = 0;
            static string timeStamp = "";
        }

        /* All of the following static methods are inline-friendly, except for getStamp(). */

        static method getSeconds() -> integer {
            return thistype.timeCounter;
        }

        static method getDecimal() -> real {
            return TimerGetElapsed(thistype.mainTimer);
        }
        
        static method getExact() -> real {
            return (TimerGetElapsed(thistype.mainTimer) + thistype.timeCounter); 
        }

        static method getRounded() -> real {
            return ((R2I(thistype.getExact() + 0.5)) * 1.0);
        }

        static method getStamp() -> string {
            integer seconds = R2I(thistype.getExact() + 0.5), minutes = 0, hours = 0;
            thistype.timeStamp = "";

            hours = (seconds / 3600);
            seconds -= (3600 * hours);

            minutes = (seconds / 60);
            seconds -= (60 * minutes);

            if (hours < 10) { thistype.timeStamp = "0"; }
            thistype.timeStamp += I2S(hours) + ":";

            if (minutes < 10) { thistype.timeStamp += "0"; }
            thistype.timeStamp += I2S(minutes) + ":";

            if (seconds < 10) { thistype.timeStamp += "0"; }

            return (thistype.timeStamp + I2S(seconds));
        }

        private static method onInit() {
            TimerStart(thistype.mainTimer, 1.00, true, static method() { timeCounter += 1; });
        }
    }
}
//! endzinc

This snippet provides the following functions (static methods) to the user:
  • [ljass]function Time.getExact takes nothing returns real[/ljass]
  • [ljass]function Time.getRounded takes nothing returns real[/ljass]
  • [ljass]function Time.getDecimal takes nothing returns real[/ljass]
  • [ljass]function Time.getSeconds takes nothing returns integer[/ljass]
  • [ljass]function Time.getStamp takes nothing returns string[/ljass]
 

Weep

Godspeed to the sound of the pounding
Reaction score
400
timers with high timeouts can be a bit inaccurate
Because I haven't seen this and am curious, and to promote use of your system, could you please cite where this was discovered as well as how inaccurate is "a bit"?
 

Nestharus

o-o
Reaction score
84
Because I haven't seen this and am curious, and to promote use of your system, could you please cite where this was discovered as well as how inaccurate is "a bit"?

You pass in a floating point number, which is the thing that gets inaccurate =P.

Remember that .03125 is .031250002.

As the timeouts get larger, the inaccuracy grows larger. Check out IEEE specs for floating point numbers to learn more =P.

As for timers themselves being inaccurate, I'm not sure... I've never done a proper test for it as reading the float out of a timestamp in RtC will make the value lose accuracy. Even passing it directly into DisplayTimeTextToPlayer with an R2SW will still make it lose accuracy.

It's those dern floats that are ruining our tests ;P.

Actually, an interesting thing is that wc3 timers themselves (that run corpse decay, attack expiration, etc) aren't very accurate. I had the tests somewhere, but they had a pretty wide range of values offset by .3125 I think it was.
 

Romek

Super Moderator
Reaction score
963
> constant real GAME_SPEED = 1.0
Don't timers change speed already with the game speed?
 

Nestharus

o-o
Reaction score
84
yup

and then there is already a game time script for keeping time w/ time stamps or w/e, so this is kinda useless ;D.
 

Solmyr

Ultra Cool Member
Reaction score
30
Because I haven't seen this and am curious, and to promote use of your system, could you please cite where this was discovered as well as how inaccurate is "a bit"?
TimerGetElasped/Remaining precision - Wc3C.net

> constant real GAME_SPEED = 1.0
Don't timers change speed already with the game speed?
I guess it's my fault for making it sound like that, but I wasn't talking about modifying the game speed via [ljass]SetGameSpeed()[/ljass] or anything similar (and I am perfectly aware that in such cases the speed of the timers changes accordingly).

I have been told that there are ways to alter the game speed by modifying the files of the game itself (single player-only), but to be honest, I never tested anything regarding that.

You pass in a floating point number, which is the thing that gets inaccurate =P.

Remember that .03125 is .031250002.

As the timeouts get larger, the inaccuracy grows larger. Check out IEEE specs for floating point numbers to learn more =P.

As for timers themselves being inaccurate, I'm not sure... I've never done a proper test for it as reading the float out of a timestamp in RtC will make the value lose accuracy. Even passing it directly into DisplayTimeTextToPlayer with an R2SW will still make it lose accuracy.

It's those dern floats that are ruining our tests ;P.

Actually, an interesting thing is that wc3 timers themselves (that run corpse decay, attack expiration, etc) aren't very accurate. I had the tests somewhere, but they had a pretty wide range of values offset by .3125 I think it was.
It is not caused solely by the floating point number arithmetic, but by the implementation of timers as well.

and then there is already a game time script for keeping time w/ time stamps or w/e, so this is kinda useless ;D.
Well, excuse me, but this is much better than your script that has the same purpose. That being said, I am not surprised that you are just going around saying "this is useless" and screaming "graveyard it" without giving any explanation.

And to be honest, simply graveyarding this without a reason seems quite stupid to me.
 

tooltiperror

Super Moderator
Reaction score
231
It would be nice if the Mod that mysteriously graveyarded this commented.
 

Nestharus

o-o
Reaction score
84
Yea... anyways

it is smart to store the counter into an integer so that the timer only returns decimals, thus increasing accuracy, but the prob is that it will remain inaccurate due to the real returned.

Perhaps doing TimeGetSeconds() and TimeGetDecimal() would be smart. I would prob say that this is a better choice than GameTime then =).

this is also rather useless -> public function GetTimestamp() -> string {

Just make the timeout always 1 (1 second)

so you can get rid of these
JASS:

constant real TIMEOUT_PERIOD = 6.0;
constant real GAME_SPEED = 1.0;


thus lowering the size of the script and simplifying it =).
 

Solmyr

Ultra Cool Member
Reaction score
30
Perhaps doing TimeGetSeconds() and TimeGetDecimal() would be smart. I would prob say that this is a better choice than GameTime then =).
Yes, I was thinking of doing that, but couldn't think of proper function names. Initially I thought of doing it with a boolean parameter that would determine if the real should be rounded, but that'd have killed the inline-friendliness.

Just make the timeout always 1 (1 second)

so you can get rid of these

[ljass]constant real TIMEOUT_PERIOD = 6.0;[/ljass]
[ljass]constant real GAME_SPEED = 1.0;[/ljass]

thus lowering the size of the script and simplifying it =).
Okay, I did this, but I'm not sure if removing all of the configurables is a good thing. Nonetheless, this is an über-low-level script, so I guess it wouldn't matter much.

Anyway, I have updated this thing and revamped the whole API (check the opening post).

Thank you for your feedback.
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
I don't see this as being overly useful over the other one. According to the thread you linked on the inaccuracy, the inaccuracy doesn't really get noticeable unless the timer is set to several hours. And by noticeable, I mean off by more than 1/1000 of a second.

I guess it does provide the timestamp function, which could be useful.

please update original post with suggestions for usage.

I think that's unnecessary for this, it's pretty self-explanatory.
 

Sim

Forum Administrator
Staff member
Reaction score
534
The change history states it was me who did it, though I have no memory of such thing. :p

Well, I guess it can be used with the timestamp, as Darthfett said. It's actually more complete than Nestharus'
 

Nestharus

o-o
Reaction score
84
Don't merge the integer and real together, keep them separate >.>

Have a getSeconds and a getDecimal or w/e otherwise you might as well just get rid of the integer all together because the instant you merge them, you lose accuracy.

Keep get getExact as well in case people don't need accuracy.

The round seems semi useless.

I guess it does provide the timestamp function, which could be useful.

How so? When working with timers on boards, blizzard natives show how much time has elapsed etc.

The timestamp would have the exact same accuracy as seconds + real w/ added bonus of string leaks.
 

Solmyr

Ultra Cool Member
Reaction score
30
Don't merge the integer and real together, keep them separate >.>

Have a getSeconds and a getDecimal or w/e otherwise you might as well just get rid of the integer all together because the instant you merge them, you lose accuracy.
I really don't see a need nor a use for such methods. And I don't see any accuracy loss either.

The round seems semi useless.
I beg to differ.

Anyhow, I fixed the "string leaks" by using a single static string (something I should've done earlier).
 

Bribe

vJass errors are legion
Reaction score
67
The "string leaks" will not be fixed by using a static variable. The last time this was able to be proven was during the return bug era, so the problem may no longer exist, but each unique string is stored to some kind of table that gives it a unique index. Using a global variable won't affect that index any more than a local. WC3 strings are just a sad story overall, especially with the lack of a proper Ord native, having to concatenate every 800 chars and of course your string operations in general are highly limited (SubString being the only way to split a string).
 

Bribe

vJass errors are legion
Reaction score
67
I've heard it may cause a problem when you get to around 100,000 strings or so. I have yet to see proof that the strings are still cached in any post-1.24 setting.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top