The description in your bullets is very close to what i'm trying to say. Let me change the last bullet a bit:
Let's say you've got a delayedCall that's scheduled to be triggered 30 seconds from now...
...but the user then sets their global clock to a time in the past (like 2 weeks ago) before that delayedCall() fires...
...I still want the delayedCall to fire 30 seconds after it was initiated.
What is happening now: When a user sets the clock to 2 weeks in the past, the delayedCall is fired 2 weeks and 30 seconds after the initial invocation.
A second part of the problem is when the getTimer() method starts to return negative values. This actually happens when the application runs for more than 25 days. The returning value of getTimer() is an int. The int can only contain values between -2,147,483,648 and 2,147,483,647. The getTimer() method suddenly goes from 2,147,483,647 to -2,147,483,648.
The getTimer() method aslo switches from positive to negative values when the systems clock is changed for more than 25 days. So when the users changes his clock to one month in the future, the values are suddenly negative. The GSAP framework currently is not handling this correct.
I can understand that this does not bother the vast majority of users. It will not happen a lot and when it happens, most users will just restart the app. In our case, we actually have users that want our app to run for weeks. I know it is a bit crazy to run a large flash application for that long..
Based on your advise, I have made some changes to the _updateRoot method of the Animation.as class. With these changes, everything seems to survive a large system clock change. If you see any mistakes that I overlooked, please let me know. I'm not familiar with the internal working of the GSAP framework, but i gave it my best shot to fix this.
protected static var _previousTimerTick:int;
/** @private This method gets called on every frame and is responsible for rendering/updating the root timelines. If you want to unhook the engine from its ticker, you could do <code>Animation.ticker.removeEventListener("enterFrame", _updateRoot)</code> and then call it yourself whenever you want to update. **/
public static function _updateRoot(event:Event=null):void {
_rootFrame++;
//START OF ADDED CODE
var newTimerTick:int = getTimer();
//check for a timejump
//A -> getTimer() flips between a large positive and negative value
//B -> there is a gap of 10 seconds between frames
if ((newTimerTick < -5000 && _previousTimerTick > 5000) || (_previousTimerTick < -5000 && newTimerTick > 5000)
|| Math.abs(newTimerTick - _previousTimerTick) > 10000){
//reset root startTime. As a result, _rootTimeline._time will be set to 0 in the next render call
_rootTimeline._startTime = newTimerTick / 1000;
//RESET ALL RootTimeLine TWEENS
var tween:Animation = _rootTimeline._first, next:Animation;
while (tween) {
next = tween._next;
var remainingDelay:Number = tween._startTime - tween._time;
//OPTION 1: keep the remaining delay when the time jumps
//tween._startTime = remainingDelay;
//OPTION 2: all planned tweens and delayed calls will fire immediatly when the time jumps
if (remainingDelay > 0){
//setting this to 0 will start all tweens with a delay (and trigger al delayedCalls)
tween._startTime = 0;
}else{
//running tweens
tween._startTime = remainingDelay;
}
tween = next;
}
}
_previousTimerTick = newTimerTick;
//END OF ADDED CODE.
_rootTimeline.render(getTimer() / 1000 - _rootTimeline._startTime * _rootTimeline._timeScale, false, false);
_rootFramesTimeline.render((_rootFrame - _rootFramesTimeline._startTime) * _rootFramesTimeline._timeScale, false, false);
ticker.dispatchEvent(_tickEvent);
}
As you can see, I believe there are 2 options:
With option 1 all delays are taken into account when the time jumps. This looks the most 'correct' way to handle the big time jumps. In our case however, we just want to have all delayed calls triggered when the time jumps for a large chunk. This is what happens with option 2.
Note that the if check with < -5000 and > 5000 is needed because we can't perform aritmatic operations to int values that go out of bounds: If the getTimer() goes from 2,147,483,647 to -2,147,483,648 and you substract them from each other, you just get a very small value.
Again, thank you for looking into this.