Skip to content

Instantly share code, notes, and snippets.

@MrSmith33
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MrSmith33/7692328455a19e820a7c to your computer and use it in GitHub Desktop.
Save MrSmith33/7692328455a19e820a7c to your computer and use it in GitHub Desktop.
A draft of modular application engine.
module moddable;
import std.stdio;
import std.exception : enforce;
import std.datetime;
import core.thread : Thread;
/// Basic module interface.
interface IModule
{
// i.e. "Test Module"
string name() @property;
// valid semver version string. i.e. 0.1.0-rc.1
string semver() @property;
// load/create needed resources
void load();
// get references to other modules
void init(IModuleManager moduleman);
}
interface IModuleManager
{
/// Returns reference to module instance if moduleName was registered.
IModule findModule(string moduleName);
}
M getModule(M)(IModuleManager modman, string moduleName = M.stringof)
{
IModule mod = modman.findModule(moduleName);
M exactModule = cast(M)mod;
enforce(exactModule);
return exactModule;
}
/// Simple implementation of IModuleManager
// See GameModule for example usage
class ModuleManager : IModuleManager
{
IModule[string] modules;
void registerModule(IModule moduleInstance)
{
assert(moduleInstance);
modules[moduleInstance.name] = moduleInstance;
}
void loadModules()
{
foreach(IModule m; modules)
{
m.load();
writefln("Loaded module %s %s", m.name, m.semver);
}
}
void initModules()
{
foreach(IModule m; modules)
{
m.init(this);
writefln("Inited module %s %s", m.name, m.semver);
}
}
override IModule findModule(string moduleName)
{
return modules[moduleName];
}
}
/// Updatable module interaface.
/// Look at MainLoopModule.registerUpdatableModule(IUpdatableModule)
interface IUpdatableModule
{
/// Will be called by MainLoopModule every frame
void update(double delta);
}
/// Module where the main loop is located
class MainLoopModule : IModule
{
override string name() @property { return "MainLoopModule"; }
override string semver() @property { return "0.1.0"; }
override void load() {}
override void init(IModuleManager moduleman)
{
evdisp = moduleman.getModule!EventDispatcherModule();
}
private:
EventDispatcherModule evdisp;
IUpdatableModule[] updatableModules;
bool _isRunning;
public:
// There is an option to auto-register all modules found in IModuleManager
// if they can be cast to IUpdatableModule
void registerUpdatableModule(IUpdatableModule updatableModule)
{
updatableModules ~= updatableModule;
}
/// Main loop execution condition. Will run if true.
bool isRunning() @property
{
return _isRunning;
}
/// ditto
bool isRunning(bool newIsRunning) @property
{
return _isRunning = newIsRunning;
}
/// Simple main loop
void mainLoop()
{
isRunning = true;
writefln("mainLoop");
TickDuration lastTime = Clock.currAppTick;
TickDuration newTime = TickDuration.from!"seconds"(0);
while(isRunning)
{
newTime = Clock.currAppTick;
double delta = (newTime - lastTime).usecs / 1_000_000.0;
lastTime = newTime;
update(delta);
Thread.sleep(10.msecs);
}
// loop stop
// event example
evdisp.postEvent(new GameStopEvent);
}
private void update(double delta)
{
foreach(mod; updatableModules)
{
mod.update(delta);
}
}
}
// Basic event
abstract class Event
{
bool continuePropagation = true;
}
class GameStopEvent : Event
{
}
/// Event
class EventDispatcherModule : IModule
{
override string name() @property { return "EventDispatcherModule"; }
override string semver() @property { return "0.1.0"; }
override void load() { }
override void init(IModuleManager moduleman) {}
void subscribeToEvent(E : Event)(void delegate(E event) handler)
{
_eventHandlers[typeid(E)] ~= cast(EventHandler)handler;
}
void postEvent(E : Event)(E event)
{
auto handlers = typeid(E) in _eventHandlers;
if (!handlers) return;
foreach(handler; *handlers)
{
handler(event);
if (!event.continuePropagation) return;
}
}
private:
alias EventHandler = void delegate(Event event);
EventHandler[][TypeInfo] _eventHandlers;
}
struct PacketMapPacket
{
string[] packets;
}
class NetworkModule : IModule, IUpdatableModule
{
override string name() @property { return "NetworkModule"; }
override string semver() @property { return "0.1.0"; }
override void load()
{
// load enet
registerPacket!PacketMapPacket();
}
override void init(IModuleManager moduleman)
{
mainmod = moduleman.getModule!MainLoopModule("MainLoopModule");
mainmod.registerUpdatableModule(this);
}
override void update(double delta)
{
// enet_host_service
}
//
void registerPacket(P)()
{
packets ~= typeid(P);
}
private:
MainLoopModule mainmod;
TypeInfo[] packets;
}
// Example module that stops main loop after some duration.
// Also subscribes to GameStopEvent
class GameStopperModule : IModule, IUpdatableModule
{
override string name() @property { return "GameStopperModule"; }
// valid semver version string. i.e. 0.1.0-rc.1
override string semver() @property { return "1.0.0"; }
// load/create needed resources
override void load() { }
// get references to other modules
override void init(IModuleManager moduleman)
{
mainmod = moduleman.getModule!MainLoopModule();
mainmod.registerUpdatableModule(this);
evdisp = moduleman.getModule!EventDispatcherModule();
evdisp.subscribeToEvent!GameStopEvent(&onGameStop);
}
override void update(double delta)
{
elapsedTime += delta;
if (elapsedTime >= nextPrintTreshold)
{
writefln("Update delta %ssecs, elapsed %ssecs", delta, elapsedTime);
nextPrintTreshold += 0.05;
}
if (elapsedTime > 0.2)
{
mainmod.isRunning = false;
writefln("Stopping");
}
}
private:
MainLoopModule mainmod;
EventDispatcherModule evdisp;
double elapsedTime = 0;
double nextPrintTreshold = 0;
void onGameStop(GameStopEvent event)
{
writefln("Game stopped");
}
}
// Main game module
class GameModule : IModule
{
override string name() @property { return "GameModule"; }
// valid semver version string. i.e. 0.1.0-rc.1
override string semver() @property { return "1.0.0"; }
// load/create needed resources
override void load() { }
// get references to other modules
override void init(IModuleManager moduleman) { }
private:
ModuleManager moduleman = new ModuleManager;
NetworkModule netmod = new NetworkModule;
MainLoopModule mainmod = new MainLoopModule;
EventDispatcherModule evdispatcher = new EventDispatcherModule;
public:
void run()
{
// load modules
// use module loader here
moduleman.registerModule(this);
moduleman.registerModule(netmod);
moduleman.registerModule(mainmod);
moduleman.registerModule(new GameStopperModule);
moduleman.registerModule(evdispatcher);
moduleman.loadModules();
moduleman.initModules();
mainmod.mainLoop();
}
}
unittest
{
GameModule game = new GameModule;
game.run();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment