NLS Engine Module Management

posted in Adam Omega
Published December 12, 2011
Advertisement
Last entry was about the basics, and the generic module interface. This time I think we need to discuss how the modules are loaded and managed.

Introducing the ModuleManager


#pragma once

/**
* \file file base name
* \author Adam Martin
* \date 2011-10-23
* \brief A manager class to load and start modules.
*
* A manager that can load/unload and start/stop modules at runtime through dynamically loaded
* libraries. The use of a common interface ModuleInterface allows us to have a uniform loading
* and starting procedure.
*/

// Standard Includes
#include
#include

// Library Includes
#include // Quick simple looping

// Local Includes
#include "../SharedBase/MessageRouter.h" // Needed in several locations throughout the header

// Forward Declarations
class GlobalProperties;
class ModuleInterface;
class EntityManager;

// Typedefs
typedef ModuleInterface* (*ModuleInstanceFactory)(GlobalProperties*, MessageRouter*, EntityManager*); // Used to find the address of the create system function
// Preprocessor selection based on OS
#ifdef _WIN32
#include
typedef HMODULE DLLHANDLE;
#else
#include
typedef void* DLLHANDLE;
#endif

class ModuleManager {
public:
ModuleManager(GlobalProperties* gprops, MessageRouter* msgrouter, EntityManager* emgr);

void Load(std::string name); // The name is required in order to load the new library
void Unload(std::string name = "");

void Update(double dt = 0.0f);

private:
GlobalProperties* gprops;
MessageRouter* msgrouter;
EntityManager* emgr;

std::map modules; // A mapping of each core to its given name
std::map libraries; // A mapping of each loaded library to a given filename
};

*Note that any non-windows code as not been tested, and I really don't know if it works.

This is the meat of the module manager. Simple put you create an instance, call Load/Unload respectively to load a module with the given filename.

The actual meat of the code is as follows:


/**
* \file file base name
* \author Adam Martin
* \date 2011-10-23
* \brief A manager class to load and start modules.
*
* A manager that can load/unload and start/stop modules at runtime through dynamically loaded
* libraries. The use of a common interface ModuleInterface allows us to have a uniform loading
* and starting procedure.
*/

#include "ModuleManager.h"

// Standard Includes

// Library Includes
#include
#include

// Local Includes
#include "../sharedbase/ModuleInterface.h"
#include "../sharedbase/EventLogger.h"
#include "../sharedbase/EntityManager.h"
#include "../ScriptDLL/EntityFactory.h"

// Static class member initialization

// Class methods in the order they are defined within the class header


ModuleManager::ModuleManager( GlobalProperties* gprops, MessageRouter* msgrouter, EntityManager* emgr) : gprops(gprops), msgrouter(msgrouter), emgr(emgr) {

}

void ModuleManager::Load(std::string name) {
char buf[256];

if (this->libraries.find(name) == this->libraries.end()) {
// Load the DLL ONLY if the libary has not already been loaded in the past.
#ifdef _WIN32
HMODULE libdll = LoadLibrary(name.c_str());
#else
void * libdll = dlopen(fname.c_str(), RTLD_LAZY);
#endif
this->libraries[name] = libdll;

if (libdll != NULL) {
LOG(1, "Loaded library '" + name + "' successfully.");
}
else {
#ifdef _WIN32
DWORD errcode = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errcode, 0, buf, 256, NULL);
#else
buf = "Error loading library: " + name;
#endif
LOG(4, buf);

LOG(4, "Module loading aborted due to error.");
return; // *NOTE: This early return may be considered bad style, but was added to maintain modularity between the DLL loader code and the module facotry loading code.
}
}
else {
LOG(2, "Library '" + name + "' already loaded, not reloading.");
}

{
ModuleInstanceFactory fact;
#ifdef _WIN32
fact = (ModuleInstanceFactory)GetProcAddress(this->libraries[name], "ModuleFactory");
#else
fact = (ModuleInstanceFactory)dlsym(this->libraries[name], "ModuleFactory");
#endif

if (fact != nullptr) {
LOG(1, "Module factory acquired successfully.");

ModuleInterface* module = fact(this->gprops, this->msgrouter, this->emgr);
this->modules[name] = module;
}
else {
#ifdef _WIN32
DWORD errcode = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errcode, 0, buf, 256, NULL);
#else
buf = "Error loading module factory";
#endif
LOG(4, buf);

LOG(4, "Module loading aborted due to error.");

Unload(name);

return;
}
}
}

void ModuleManager::Unload( std::string name /*= ""*/ ) {
if (this->libraries.find(name) != this->libraries.end()) { // Lib WAS found
if (this->modules.find(name) != this->modules.end()) { // Mod WAS found
//Shutdown(name);
}

#ifdef _WIN32
if (FreeLibrary(this->libraries[name]) != 0) {
#else
if (dlclose(this->libraries[name])) {
#endif
this->libraries.erase(name);
}
else {
LOG(4, "Unable to unload the library '" + name + "'!");
}
}
}

void ModuleManager::Update( double dt /*= 0.0f*/ ) {
for (auto it = this->modules.begin(); it != this->modules.end(); ++it) {
(*it).second->Update(dt);
}
}

*Note LOG is located in a separate header and just logs a message with a certain log level.

In the loading process a function pointer to get an instance of the modules ModuleInterface is found and called. ModuleManager simple loads and unloads modules and there respective library files from memory.
Next Entry Message Routing
0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement