Skip to main content
GameDev.net gamedev.net

PRO Tired of ads? Read GameDev.net ad-free and help keep the community independent with GameDev Pro — $3/month.

NLS Engine Module Management

NLS Engine Module Management

adam4813
Adam Omega · · 3 min read
2,126 0
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.

Discussion

Loading comments...