| 04-29-2008, 12:54 PM | #1 | ||||||||||||||||
If anyone remembers this other thread in which I was testing that circular queue stuff, I said TimerGetElapsed is not precise enough, well that's not the whole story, I made some other tests today. JASS:library onetest initializer init globals private timer t endglobals private function disp takes nothing returns nothing call BJDebugMsg("rem"+R2S(TimerGetRemaining(t))) call BJDebugMsg("ela"+R2S(TimerGetElapsed(t))) endfunction private function init takes nothing returns nothing set t=CreateTimer() call TimerStart(t,100,true, null) call TimerStart(CreateTimer(),0.04,true, function disp) endfunction endlibrary The difference between each ela/rem displayed is 0.040. But if you did: call TimerStart(t,1000000000,true, null) The difference goes crazy as I described in the other thread, three numbers in a row are equal then they raise abruptly . So, I figured that timeout is quite inflated, I tried seconds in a day: call TimerStart(t,86400,true, null) The difference now is variable between 0.039 and 0.041 So, it looks like blizz does some float stuff here. I think the difference in the last test is not that much, can anyone think of a reasonable upper bound for a game's duration? Edit: Just tested and the precision for 12 hours is also 0.001, good enough, even so I think I need to use R2SW here... I'd say a map that breaks after 12 hours is good enough, if you actually play continuously for 12 hours you probably need something to stop you... Edit 2: Not bad at all, the error for 12 hours is around 0.00001 Edit 3: I had a mistake in my tests for 12 hours, retesting. edit 4: The error for 12 hours can get as big as 0.003 Edit 5: Table:
Seems linear, so I guess what happens here is that they never store/maintain elapsed, only remaining, and then do a subtraction to get remaining, but then that would require to also store the timeout... |
| 04-29-2008, 02:28 PM | #2 |
I think that Blizzard made this bug to cause games to crash after playing for 12 hours, to prevent people from over-playing. Have you tried running these tests with lots of other laggy triggers around? In the past I noticed that certain lag can cause certain triggers to malfunction or not run the amount of time specified, etc. |
| 04-29-2008, 02:32 PM | #3 |
This has nothing to do with crashing. It will not break the game alone, and it is not really a bug but some implementation specificity. Anyways, I'll stick to 90 minutes, after the 90 minutes timer expires, I'll iterate through all pending time outs and update their expiration times (subtrack 90 minutes) . Or should I just use 6 hours? I think an error of 0.001 ought not to cause big issues, need to redo the tests for "AwesomeTimeEvent"... |
| 04-29-2008, 03:56 PM | #4 |
Nothing to do with timers. Try this, and you'll see that the two numbers printed at the same, showing that the issue lies in the inaccuracy of reals rather than anything to do with timers: JASS:library onetest initializer init globals private timer t real total = 86400. real x = 0 endglobals private function disp takes nothing returns nothing set x = x + 1 call BJDebugMsg("the " + R2S(total - x*0.04)) call BJDebugMsg("rem "+R2S(TimerGetRemaining(t))) //call BJDebugMsg("ela"+R2S(TimerGetElapsed(t))) endfunction private function init takes nothing returns nothing set t=CreateTimer() call TimerStart(t,total,true, null) call TimerStart(CreateTimer(),0.04,true, function disp) endfunction endlibrary Big numbers get more and more inaccuracies as absolute values due to the floating point increasing in scale. Timers themselves seem to be perfectly accurate. |
| 04-29-2008, 04:31 PM | #5 | |
Duh. It's wrong though. This got everything to do with how timers are implemented. Both TimerGetElapsed and TimerGetRemaining have the issue, and TimerGetElapsed got it since the beginning of the game - its return values are fairly low -, for some reason they made Elapsed depend on the timeout duration. Probably they only store remaining and do Elapsed based on it and then the precision is lost. Quote:
|
| 04-29-2008, 04:34 PM | #6 |
I compared time remaining with the starting time minus the number of itinerations times by 0.04, and get exactly the same answer each time - implying that the discrepency is nothing to do with timers (since the the(oretical) value isn't affected by timers at all, except for counting natural numbers). |
| 04-29-2008, 04:40 PM | #7 |
JASS:library onetest initializer init globals private timer t real total = 1000000000. real x = 0 endglobals private function disp takes nothing returns nothing set x = x + 1 call BJDebugMsg("the " + R2S(x*0.04)) call BJDebugMsg("ela"+R2S(TimerGetElapsed(t))) endfunction private function init takes nothing returns nothing set t=CreateTimer() call TimerStart(t,total,true, null) call TimerStart(CreateTimer(),0.04,true, function disp) endfunction endlibrary Compare that. |
| 04-29-2008, 05:02 PM | #8 |
Hmm...yea, that's screwed up. In fact in more ways than one; I don't think timers can go beyond 1231380. The problem persists below there too. Nice find. |
| 04-29-2008, 05:05 PM | #9 |
lol. Captain Griffen +1 cause it's the problem of real values and not timers. it's not the first time what large reals are inaccurate... try for example call echo(R2S(i2r(r2i(565664534.3445)))) // i2r r2i are return bug conversions... and try call echo(R2S(565664534.3445)) or try to do some math operation etc. |
| 04-29-2008, 05:11 PM | #10 | |
Quote:
I just wish blizzard would have implemented a game elapsed time native. toad: the joke's on you. |
| 04-29-2008, 05:18 PM | #11 | ||
Quote:
Printing the time elapsed plus the time remaining gave a fairly constant 123180 (0.25 or something off, presumably due to timer weirdness/real inaccuracy). Quote:
Indeed. Guess you can get past it with repeating timers, but meh. |
| 04-29-2008, 10:16 PM | #12 |
If you want it to work forever with high accuracy, you could set the timeout much lower (1 min or less) and calculate the time elapsed just as griffin did. |
| 04-29-2008, 10:36 PM | #13 |
that also seems to hit performance somehow. I tried it in the other thread. |
| 07-29-2008, 01:01 PM | #14 |
As inferred from the results, it seems that the result is precise enough when given a reasonable timeout. Have you tested with 3600 (number of seconds in an hour)? I think that would give a pretty precise result without much performance loss. |
| 07-29-2008, 03:27 PM | #15 |
I stopped the whole idea of queue based timer attachment because... err, can't explain without code. JASS:library sandbox initializer init // requires UnitStruct globals private integer A=0 private integer B=0 private constant integer CYCLE = 10 private constant real TIME1 =0.02 private constant real TIME21=0.01999 private constant real TIME22=0.00001 private constant real TIME3 = 0.2 endglobals private function Bu takes nothing returns nothing set B=B+1 if(ModuloInteger(B,CYCLE)==0) then //call BJDebugMsg("B: "+I2S(B)) endif endfunction globals private code AiFunc endglobals private function Au takes nothing returns nothing set A=A+1 if(ModuloInteger(A,CYCLE)==0) then //call BJDebugMsg("A: "+I2S(A)) endif call TimerStart(GetExpiredTimer(), TIME21, false, AiFunc) endfunction private function Ai takes nothing returns nothing call TimerStart(GetExpiredTimer(), TIME22, false, function Au) endfunction private function C takes nothing returns nothing call BJDebugMsg(I2S(A)+" ; "+I2S(B)) endfunction private function init takes nothing returns nothing set AiFunc= function Ai call TimerStart(CreateTimer(), TIME1, true, function Bu) call TimerStart(CreateTimer(), TIME21, false, function Ai) call TimerStart(CreateTimer(), TIME3, true, function C) endfunction endlibrary |
