|
Modular Applications
Submitted by |
Some time ago, I decided to join a game project which is being developped by hobby programmers. As I am something like a lead programmer there, I decided to intoduce my modular system. Maybe it isnt anything new, but I really like it. But please comment this whole system, I am by far not a proffesional programmer, so I might have overseen a big disadvantage. Also my explanation might be not very clear.
Basically the whole program consists out of modules. The executable is only the interface, all modules are placed in dlls. Each dll exports only one function, which creates a new module. This module is then added to the interfaces list of modules.
Now how do these modules communicate? Through messages, like Windows MessageProc(). There are a lot of messages, each one derived from the base message. If your module gets a message, look at the message id, typecast it to the desired message and process it. If you get a message you dont want, ignore it. If you want to send a message, call the SendMessage() function of the interface. That will dispatch the message to all modules. I hope you got the idea so far.
Besides the event-based messages, there is a message "Run" that is being sent every frame. This gives the module the chance to execute regular tasks like render the scene, update all game objects and so on.
And why such a difficult system? Because it has many...
ADVANTAGES:
Change modules at runtime. For example, unload the DirectX renderer and load the OpenGL one while your game is running.
You neednt worry about the implementation of other modules, you just develop your own. This is useful in our spare time project, where communication and synchronization is difficult. Also you dont need the source of the other modules.
Not only that you dont care _how_ a function is implemented, you dont care _where_ it is implemented. Consider a client-server based game. In single player (local) mode, you can just run a server module and all server messages will be handled immediately. In multiplayer mode, run a network module instead of the server module and all server messages will be forwarded (1:1) to the server.
Sometimes it is useful that multiple modules process one message. That way, I can later add a logging module without touching the others.
Missing functionality wont produce any exceptions, your messages will just be ignored
Simple programming of plugins. Actually, everything is a plugin.
All the other advantages of modular programming
DISADVANTAGES
small overhead. In our project, it was about 0.2% per module. But it could be less, because all I was doing when I profiled the thing was sending messages and rendering an empty screen (ran at 1100 fps), so a small part was spent at actual processing.
in the beginning, it is difficult to define the messages.
this was not designed to be multi-threading. Multithreading is anyway not needed for games(I know this statement will produce a lot of discussion...)
HEADERS:
class CModuleInterface;
// ----------------------------------------------------
// Name: CModule
// Desc: The base module
// ----------------------------------------------------
class CModule
{
public:
virtual HRESULT ProcessMessage(MMS::Message* Msg) {return MRV_Err_NotImplemented;}
virtual ~CModule() {};
};
typedef vector<CModule* CModuleVector;
// ----------------------------------------------------
// Name: CModuleInterface
// Desc: The base interface for modules
// ----------------------------------------------------
class CModuleInterface
{
public:
CModuleVector Modules;
public:
virtual HRESULT SendMessage(MMS::Message* Msg) {return MRV_Err_NotImplemented;} // dispatch the message to all modules
};
// ----------------------------------------------------------------------
// The base message type to communicate
// ----------------------------------------------------------------------
typedef DWORD MessageType;
#define MMS_Message 0x0000 // the id of the base message
struct Message
{
MessageType Type; // [in]Message type, begins with MMS_
};
// ----------------------------------------------------------------
// Name: Run
// Desc: Gives a module the chance to execute regular tasks
// This is called every frame once
// ----------------------------------------------------------------
#define MMS_Sys_Run 0x0001
struct Sys_Run:public Message
{
float time; // [in] time passed in seconds
};
// ----------------------------------------------------------------
// Name: AnyMessage
// Desc: example of a message. The sending module fills the AnyInt member,
// the processing module fills the AnyInt2 member, the sending module reads the AnyInt2 member
// ----------------------------------------------------------------
#define MMS_AnyMessage 0x0A37
struct AnyMessage:public Message
{
int AnyInt; // [in] input
int AnyIint2; // [out] output
...
} |
Robert Carnecky
|
The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
|