1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-27 03:35:27 +00:00

172 lines
4.5 KiB
D
Raw Normal View History

// This module contains (or will contain) various routines for
// timing. It is also home of the ubiquitous "sleep" idle function.
module monster.modules.timer;
import std.stdio;
import std.date;
// For some utterly idiotic reason, DMD's public imports will suddenly
// stop working from time to time.
import monster.vm.mclass;
import monster.vm.mobject;
import monster.vm.stack;
import monster.vm.thread;
import monster.vm.idlefunction;
import monster.monster;
const char[] moduleDef =
"singleton timer;
idle sleep(float secs);
"; //"
// Sleep a given amount of time. This implementation uses the system
// clock and is the default.
class IdleSleep_SystemClock : IdleFunction
{
override:
IS initiate(Thread* cn)
{
// Get the parameter
float secs = stack.popFloat;
// Get current time
cn.idleData.l = getUTCtime();
// Calculate when we should return
cn.idleData.l += secs*TicksPerSecond;
// Schedule us
return IS.Poll;
}
bool hasFinished(Thread* cn)
{
// Is it time?
return getUTCtime() >= cn.idleData.l;
}
}
// This implementation uses a user-driven timer instead of the system
// clock. It's more efficient, but requires the user to update the
// given timer manuall each frame. The default sleep (timer.sleep) is
// bound to the default timer, but it's possible to create multiple
// independent timers.
class IdleSleep_Timer : IdleFunction
{
override:
IS initiate(Thread* cn)
{
// The timer is stored in the object's 'extra' pointer
auto t = cast(SleepManager)cn.extraData.obj;
assert(t !is null);
// Calculate the return time
cn.idleData.l = t.current + cast(long)(t.tickSize*stack.popFloat);
// Schedule us
return IS.Poll;
}
bool hasFinished(Thread* cn)
{
// Get the timer
auto t = cast(SleepManager)cn.extraData.obj;
assert(t !is null);
// Is it time?
return t.current >= cn.idleData.l;
}
}
// A manually updated timer. This can be improved quite a lot: Most
// sleep operations (depending on application of course) will skip
// many frames before they return. For example, for sleep(0.5) at 100
// fps, hasFinished will return false approximately 50 times before
// returning true. For bigger sleep values and a large number of
// objects, the impact of this is significant. A good solution would
// be to pool scheduled objects together and only perform one check on
// the entire pool. If the pool is due, all the nodes within it are
// inserted into the scheduler for detailed checking. We could have a
// series of such pools, ordered by expiration time, so that we only
// ever need to check the first pool in the list. The optimal pool
// interval, number of pools etc depends on the application and the
// fps - but it should be possible to find some reasonable defaults. A
// more generalized priority queue implementation is also possible.
class SleepManager
{
private:
// Instance of the timer class that is associated with this timer
MonsterObject *tobj;
// Current tick count
long current;
public:
// Specify a Monster object to associate with this timer. Use 'null'
// if you don't need an object.
this(MonsterObject *obj)
{
if(obj is null) return;
tobj = obj;
tobj.getExtra(_timerClass).obj = this;
}
// By default, create a new object
this()
{ this(_timerClass.createObject); }
// Number of 'ticks' per second
static const long tickSize = 1000;
// Reset the timer to zero
void reset() { current = 0; }
// Return the total number of elapsed seconds since start (or last
// reset)
double read() { return current/cast(double)tickSize; }
// Add time to the timer.
void add(double d) { current += cast(long)(tickSize*d); }
void addl(long l) { current += l; }
MonsterObject *getObj() { return tobj; }
}
SleepManager idleTime;
MonsterClass _timerClass;
void initTimerModule()
{
if(_timerClass !is null)
{
assert(idleTime !is null);
return;
}
_timerClass = new MonsterClass(MC.String, moduleDef, "timer");
assert(idleTime is null);
idleTime = new SleepManager(_timerClass.getSing());
setSleepMethod(SleepMethod.Clock);
}
enum SleepMethod
{ Clock, Timer }
void setSleepMethod(SleepMethod meth)
{
initTimerModule();
if(meth == SleepMethod.Clock)
_timerClass.bind("sleep", new IdleSleep_SystemClock);
else if(meth == SleepMethod.Timer)
_timerClass.bind("sleep", new IdleSleep_Timer);
else assert(0, "unknown timer method");
}