0ad/binaries/data/mods/public/simulation/components/Timer.js
elexis fd5ba1e882 Timer component cleanup. Patch by Stan, fixes #4158.
Use objects instead of arrays to achieve more readable code.
Use two for-of loops, a continue instead of an if-else pattern and
remove an unneeded variable dt.

This was SVN commit r18981.
2016-11-22 12:28:22 +00:00

147 lines
3.4 KiB
JavaScript

function Timer() {}
Timer.prototype.Schema =
"<a:component type='system'/><empty/>";
Timer.prototype.Init = function()
{
this.id = 0;
this.time = 0;
this.timers = new Map();
this.turnLength = 0;
};
/**
* Returns time since the start of the game in milliseconds.
*/
Timer.prototype.GetTime = function()
{
return this.time;
};
/**
* Returns the duration of the latest turn in milliseconds.
*/
Timer.prototype.GetLatestTurnLength = function()
{
return this.turnLength;
};
/**
* Create a new timer, which will call the 'funcname' method with arguments (data, lateness)
* on the 'iid' component of the 'ent' entity, after at least 'time' milliseconds.
* 'lateness' is how late the timer is executed after the specified time (in milliseconds).
* Returns a non-zero id that can be passed to CancelTimer.
*/
Timer.prototype.SetTimeout = function(ent, iid, funcname, time, data)
{
let id = ++this.id;
this.timers.set(id, {
"entity": ent,
"iid": iid,
"functionName": funcname,
"time": this.time + time,
"repeatTime": 0,
"data": data
});
return id;
};
/**
* Create a new repeating timer, which will call the 'funcname' method with arguments (data, lateness)
* on the 'iid' component of the 'ent' entity, after at least 'time' milliseconds
* and then every 'repeattime' milliseconds thereafter.
* It will run multiple times per simulation turn if necessary.
* 'repeattime' must be non-zero.
* 'lateness' is how late the timer is executed after the specified time (in milliseconds).
* Returns a non-zero id that can be passed to CancelTimer.
*/
Timer.prototype.SetInterval = function(ent, iid, funcname, time, repeattime, data)
{
if (typeof repeattime != "number" || !(repeattime > 0))
error("Invalid repeattime to SetInterval of "+funcname);
let id = ++this.id;
this.timers.set(id, {
"entity": ent,
"iid": iid,
"functionName": funcname,
"time": this.time + time,
"repeatTime": repeattime,
"data": data
});
return id;
};
/**
* Cancels an existing timer that was created with SetTimeout/SetInterval.
*/
Timer.prototype.CancelTimer = function(id)
{
this.timers.delete(id);
};
Timer.prototype.OnUpdate = function(msg)
{
this.turnLength = Math.round(msg.turnLength * 1000);
this.time += this.turnLength;
// Collect the timers that need to run
// (We do this in two stages to avoid deleting from the timer list while
// we're in the middle of iterating through it)
let run = [];
for (let [id, timer] of this.timers)
if (timer.time <= this.time)
run.push(id);
for (let id of run)
{
let timer = this.timers.get(id);
// An earlier timer might have cancelled this one, so skip it
if (!timer)
continue;
// The entity was probably destroyed; clean up the timer
let cmpTimer = Engine.QueryInterface(timer.entity, timer.iid);
if (!cmpTimer)
{
this.timers.delete(id);
continue;
}
try
{
cmpTimer[timer.functionName](timer.data, this.time - timer.time);
}
catch (e)
{
error(
"Error in timer on entity " + timer.entity + ", " +
"IID" + timer.iid + ", " +
"function " + timer.functionName + ": " +
e + "\n" +
// Indent the stack trace
e.stack.trimRight().replace(/^/mg, ' ') + "\n");
}
if (!timer.repeatTime)
{
this.timers.delete(id);
continue;
}
timer.time += timer.repeatTime;
// Add it to the list to get re-executed if it's soon enough
if (timer.time <= this.time)
run.push(id);
}
};
Engine.RegisterSystemComponentType(IID_Timer, "Timer", Timer);