Skip to content

Commit

Permalink
Scripting|libcore: Added Scheduler for time-based script execution
Browse files Browse the repository at this point in the history
  • Loading branch information
skyjake committed Aug 16, 2015
1 parent 5b726d6 commit 5e75b33
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 0 deletions.
1 change: 1 addition & 0 deletions doomsday/sdk/libcore/include/de/Scheduler
@@ -0,0 +1 @@
#include "scriptsys/scheduler.h"
87 changes: 87 additions & 0 deletions doomsday/sdk/libcore/include/de/scriptsys/scheduler.h
@@ -0,0 +1,87 @@
/** @file scheduler.h Script scheduling utility.
*
* @authors Copyright (c) 2015 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy of
* the GNU Lesser General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#ifndef LIBDENG2_SCHEDULER_H
#define LIBDENG2_SCHEDULER_H

#include "../libcore.h"
#include "../Time"
#include "../String"

namespace de {

class Script;
class Record;

/**
* Script scheduling utility.
*
* Scheduler owns the parsed scripts, and can be re-run if rewound.
*/
class DENG2_PUBLIC Scheduler
{
public:
Scheduler();

void clear();

/**
* Sets the execution context, i.e., global namespace for the scripts. All
* scripts of the scheduler run in the same context.
*
* @param context Global namespace.
*/
void setContext(Record &context);

/**
* Adds a new script to the scheduler.
*
* @param at Point in time when the script is to be executed.
* @param source Script source. This will be parsed before the method returns.
* @param sourcePath Path where the source comes from.
*
* @return Scheduled Script (owned by Scheduler).
*/
Script &addScript(TimeDelta at, String const &source, String const &sourcePath = "");

/**
* Returns the current time of the scheduler.
*/
TimeDelta at() const;

/**
* Rewinds the current time of the Scheduler back to zero.
*/
void rewind();

/**
* Advances the current time of the Schduler and executes any scripts whose
* execution time has arrived.
*
* @param elapsed Time elapsed since the previous call.
*/
void advanceTime(TimeDelta const &elapsed);

private:
DENG2_PRIVATE(d)
};

} // namespace de

#endif // LIBDENG2_SCHEDULER_H

157 changes: 157 additions & 0 deletions doomsday/sdk/libcore/src/scriptsys/scheduler.cpp
@@ -0,0 +1,157 @@
/** @file scheduler.cpp Script scheduling utility.
*
* @authors Copyright (c) 2015 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy of
* the GNU Lesser General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#include "de/Scheduler"
#include "de/Record"
#include "de/Script"
#include "de/Process"

#include <queue>
#include <deque>

namespace de {

DENG2_PIMPL(Scheduler)
, DENG2_OBSERVES(Record, Deletion)
{
Record *context = nullptr;
TimeDelta at = 0.0;

struct Event {
TimeDelta at;
Script script;

Event(TimeDelta at, String const &source, String const &sourcePath)
: at(at)
, script(source)
{
script.setPath(sourcePath); // where the source comes from
}

struct Compare {
bool operator () (Event const *a, Event const *b) { return a->at > b->at; }
};
};
std::priority_queue<Event *, std::deque<Event *>, Event::Compare> events;
QList<Event *> done;

Instance(Public *i) : Base(i)
{}

~Instance()
{
setContext(nullptr);
clear();
}

void clear()
{
while(!events.empty())
{
delete events.top();
events.pop();
}
qDeleteAll(done);
done.clear();
rewind();
}

void setContext(Record *rec)
{
if(context) context->audienceForDeletion() -= this;
context = rec;
if(context) context->audienceForDeletion() += this;
}

void recordBeingDeleted(Record &record)
{
if(context == &record)
{
context = nullptr;
}
}

void rewind()
{
at = 0.0;

// Restore all the past events into the queue.
for(Event *ev : done)
{
events.push(ev);
}
done.clear();
}

void advanceTime(TimeDelta const &elapsed)
{
at += elapsed;

while(!events.empty())
{
Event *ev = events.top();
if(ev->at > at) break;

events.pop();
done.append(ev);

// Execute the script in the specified context.
Process process(context);
process.run(ev->script);
process.execute();
}
}
};

Scheduler::Scheduler()
: d(new Instance(this))
{}

void Scheduler::clear()
{
d->clear();
}

void Scheduler::setContext(Record &context)
{
d->setContext(&context);
}

Script &Scheduler::addScript(TimeDelta at, String const &source, String const &sourcePath)
{
auto *ev = new Instance::Event(at, source, sourcePath);
d->events.push(ev);
return ev->script;
}

TimeDelta Scheduler::at() const
{
return d->at;
}

void Scheduler::rewind()
{
d->rewind();
}

void Scheduler::advanceTime(TimeDelta const &elapsed)
{
d->advanceTime(elapsed);
}

} // namespace de

0 comments on commit 5e75b33

Please sign in to comment.