[MUD-Dev] Re: PDMud thread summary

Niklas Elmqvist d97elm at dtek.chalmers.se
Sun Oct 25 20:42:36 New Zealand Daylight Time 1998


On Sun, 25 Oct 1998, Chris Gray wrote:

> [Niklas Elmqvist:]
>  >This is also why I think some sort of bus-based message communication
>  >system is a Good Thing<tm>. It is a quite good abstraction in that it
>  >captures normal function calls, message queueing, RPC mechanisms and
>  >distributed proxy calls under one concept, while still not introducing too
>  >much inefficiency. It is much easier for a newbie developer to think of
>  >inter-module communication in terms of "here is a channel leading directly
>  >to this module you requested contact with" instead of having to learn the
>  >implementation details.
> 
> Call me dumb. Call me poor at abstraction. Please show me what you really
> mean by this. Exactly *how* would it work? Go to as low a level as you
> think I will need. Avoid assuming I'm good at C++, because I'm not.
> Please?

I'll try :) However, I don't have any working source code and have not
really mapped this out in my head (since these are quite new versions of
old ideas), so I'll have to wing it. Semantics and syntax are, of course,
subject to flaws, as are the individual functions. You'll get the idea,
though.

If this is too low-level (or low-tech?) for this list, I apologize.

Participants:

class Core {
private:
	EventManager hEventManager;
	ModuleManager hModuleManager;
	CommManager hCommManager;
	..
public:
	// Event managing
	void ScheduleEvent(Event *hEvent);
	..	
	// Intermodule communication
	void Broadcast(Message *hMsg);
	Pipe GetPipe(ModuleID *hID);
	..
	// Module management
	void LoadModule(string sFilename, ..);
	void UnloadModule(ModuleID *hID);
	..
}

class Module {
	ModuleID mID;
	..
public:
	Module(Core *hCore); 	// Constructor
	~Module();
	..
	// The following are quite empty and to be overridden
	virtual void Init(..);
	virtual bool HandleEvent(..); 
	..
}

// Pipe abstract class: no functionality
class Pipe {
private:
	ModuleID *hNode1, *hNode2;
public:
	..
	virtual void Send(ModuleID hSrc, ModuleID hDst, Message *hMsg);
	virtual Msg *Recv(ModuleID hCurr); 
	..
}
(Not satisified about this, but am pressed for time.)

class CallPipe : public Pipe {
private:
	FunctionPtr *hFun1, *hFun2;
public:
	..
	virtual void Send(ModuleID hSrc, ModuleID hDst, Message *hMsg);
	virtual void Recv(..)
	..
}

class QueuePipe : Public Pipe {
private:
	FunctionPtr *hFun1, *hFun2;
	MessageList hMsgList;
..
}

class ProxyPipe : public Pipe { // CORBA-like stuff
..
}

class RPCPipe : public Pipe { // RPC or something :)
..
}

(And whatever additional intermodule communication ways we need.)

Okay. What I have in mind is this: Say we have a running system with one
module loaded, call it A. A belongs to the ParserModule class which has
been subclassed outside of the executable (external inheritance) and
dynamically loaded into the running MUD. 

Module B is loaded in some way (signal or command). B belongs to the
SocialModule class, also externally inherited from the core. What happens
is this:

1. The module manager takes charge of the situation. The shared lib is
	opened using dlopen() (depending on OS, of course).  
2. The executable extracts a pointer to the factory function in the lib
	(yes, it has to be there) using dlsym().
3. The executable calls the function, passing it a pointer to the Core
	class. This is the basic set of primitives the module may access.
	The pointer is passed to the constructor of module B (see class
	Module) and the function then returns with the new pointer to
	the module B object. 
4. The module manager adds the new module object to a list.
5. The executable calls B->Init() (that is SocialModule::Init()) and
	passes it its new ModuleID.
6. The Init function, which is written specifically for the SocialModule
	(it is virtual and overrides the original Module::Init()), does
	some initialization and then decides it needs to talk to a parser.
	It calls Core::Broadcast() along with a generic status message ("I
	am a CommandHandler")
7. Object A (the parser module) receives this message on the broadcast
	channel/pipe and decides that it is interested in CommandHandlers.
	It calls Core::GetPipe() with the ModuleID it received from the
	Message.
8. The intermodule communication manager (CommManager) receives the
	request and queries the module manager about the location of the
	two ModuleIDs. If on the same system, it creates a CallPipe
	(function pointers). If multi-threading is required, it creates a
	QueuePipe, and so on. It returns the pipe to the calling module
	(A).
9. Module A receives the pipe and immediately calls B on it, telling it
	that it is a parser and wants a command grammar and a way to
	package data for passing it.
10. Module B provides A with this and then sits idle until information
	from A comes through the pipe.

I haven't ironed out any details, as you can tell, and most of this I made
up as I went along. It may therefore be totally demented; I make no
guarantees. But hopefully, it will help in seeing what I envision.

> Chris Gray     cg at ami-cg.GraySage.Edmonton.AB.CA

-- Niklas Elmqvist (d97elm at dtek.chalmers.se) ----------------------
  "The trouble with being a god is that you've got no one to 
   pray to."
		-- Terry Pratchett, Small Gods





More information about the MUD-Dev mailing list