[MUD-Dev] Re: DevMUD - thoughts.1

James Wilson jwilson at rochester.rr.com
Sun Oct 25 07:15:01 New Zealand Daylight Time 1998


On Sun, 25 Oct 1998, Chris Gray wrote:

>- it has been suggested that it would be nice if modules could be either
>    native code (C, C++, whatever) or MUD-language code, and that the
>    two ways of writing them would be interchangeable. That could be a
>    problem. There would almost certainly have to be some glue code
>    to convert between the two. The previously suggested method, was,
>    I believe, to have the linkage convention defined to be the one
>    that the MUD-language uses, and require that native code convert
>    as needed. That works for me.
>
>    Note, however, that both C and C++ are essentially strongly typed
>    languages. If a function is written to accept a character pointer
>    argument, then you had better not pass it an arbitrary integer!
>    I take this as an argument that the MUD-language be strongly typed,
>    or at least be able to export strongly typed function declarations.
>    There are ways around this, but are there any non-ugly ones?

the way it is done in Guile and Perl makes good sense - there is a
struct which stands for 'a dynamically-typed object', which can be passed
to C or C++, has some methods, etc. so the internal representation CAN
be dynamically-typed even when expressed in C/C++.

Though I'm usually a big strong-typing partisan (I've been burned enough 
times at work by subtle Perl and Scheme bugs that would have been caught
with strong typing) I'd like to see a _truly_ dynamic type system, in
which an object's type can change dynamically over time. Then adding a 
new capability to an object (such as a new magical power) can be done at
runtime. This could be implemented on top of a strongly-typed system
in exactly the same way Scheme is implemented in C.

I liked JC's suggestion to build it like a unix system, with the core 
as the kernel, the db as the filesystem, and executables as native code 
or shell scripts or perl scripts or... there could be an analogue to
inetd which maps incoming connections to servers, where the servers obey
telnet protocol or some exotic mud protocol or what have you.


>    Alternatively, we could drop the requirement that modules be allowed
>    to be native or MUD-language, interchangeably? That would at least
>    allow native modules to communicate by direct function calls, which
>    is the most efficient.

I think this is an important feature which should be retained if at all
possible. It would allow a builder to try out some code in the interpreter,
do some rapid development, and, once things are working, have the 
interpreted stuff compiled to native code. Moreover, if one is doing funky
graphics or what have you, the extra performance could be a necessity.

>    I'm flexible on this - to me it makes sense that all server stuff be
>    written in the native language, and only scenario (world definition)
>    stuff needs to be in the MUD-language. Thus, the expense of using
>    the MUD-language interface would only be needed for scenario-level
>    stuff, and that would only need to be native code if it was *very*
>    expensive, and that expense would hide the cost of using the
>    MUD-language interface.

*shrug* clearly the mudlang must be able to hook to native code, and
I would argue that the reverse is also essential. Thus the only question
is whether the mudlang can be translated to C/C++. If that is something
people see as desirable, the interpreter would have to be designed from 
the start to make that mapping feasible. (Not hard if you know it from the 
first.)

>- there was some talk about matching up the interfaces of the modules
>    based on function type and parameters. I think instead we need to
>    define a set (which can grow as needed) of interface kinds, which
>    give a purpose to a given exported definition or reference. Those
>    kinds implicitly include the required function prototype. This
>    will hopefully be clearer in some examples.

I'm trying to understand, so let me ask if this example is something along
your lines:

Module 'web_support' is set up to work using an external resource 
'g_net' which is some subclass of interface 'net_manager'. When 'web_support'
is loaded, it needs to find 'g_net' (i.e. resolve its external reference).
It asks some broker to give it 'g_net'. (There can be multiple specialized
brokers, or one huge one.) The broker determines if 'g_net' is currently 
available, and if not tries to find a module which claims to provide it. 
Assuming said module is found, it is loaded and the symbol is provided to
'web_support'.

Note that this is exactly what Linux's kerneld does.

>    Having said that, I don't have any good suggestions for a general
>    method of deciding how threads should be used. One possibility is
>    that each input is run in a thread (the thread can be re-used when
>    it is done with that input). Then, it would be input-generating
>    modules that did thread control. Other modules might choose to
>    use threads internally - that would be their business. A prime
>    example would be an event handler (which in some senses is just
>    another input-generator).

the issue of 'what goes into a module' is quite different from 'what should
be a thread'. Module 'web_support' could simply provide a set of functions
and classes for other modules to use, if they want. There is no need for a
thread there (although the underlying network functionality might well be
threaded). On the other hand, one might conceivably want a weather module
to start up a weather daemon thread when it loads (or multiple threads).
Depending on the event model, this might not make sense. (i.e. perhaps
all these modules do is provide types of events and event contexts, and
the threading strategy is chosen by the engine.)

>OK, now to get into a bit more detail for my thought experiment.
>
>- some module interface kinds that I think a MUD-type game needs:
>
>input - a source of input (typically from a user). This will come
>    with an identification of where the input came from, so that output
>    that needs to go back to the same place can be properly routed.
>    In my system I've found it useful to have more than one kind of
>    input, (e.g. text, keypad presses, mouse-clicks, etc.), so I suggest
>    that the prototype for an input handler be something like:
>
>	void inputHandler(void *source, uint kind, void *data, ulong len)

right here is where the C++ goons come and break your kneecaps. Using a
straight function call doesn't allow someone to write a handler which 
has its own individual state, e.g.

static string s_magic;

void inputHandler (/* rep */)
{
	// manipulate s_magic in a non-reentrant way
}

is BAD in a multi-threaded environment (and inflexible in any case). The
alternative is quite appealing:

struct input_handler
{
	virtual void handle (/* rep */) = 0;
};

can be specialized to be thread-safe and have any state it likes:

struct my_input_handler
{
	virtual void handle (/* rep */) 
	{ /* magic */ }
private:
	string _magic;
	mutex _lock;
};

>    Alternatively, we could make the kind of input be part of the
>    interface kind, so that, potentially, different modules could
>    handle different kinds of input. E.g.
>
>textInput - void textInputHandler(void *source, char *data, ulong len)

IMO, interfaces should be up to the module programmers. Think of a module
as a shared library; to use that shared library, you write a program that
#includes the proper headers, and link it against libfoo.so. It's up to
you to use libfoo's api according to the headers, and up to the compiler
to check your errors. If you want to write a new module with some funky
interface, and Bob wants to use said funky interface, that should be as
easy as #including your headers and linking against your library (or 
whatever analogue applies).

[more io interfaces snipped]

>Parsing
>
>This is a tricky issue. There is a whole range of ways that parsing can
>be done. For example, in LPC, a lot of parsing is done by the individual
>objects in the world. How does that fit into the module scheme? I'll
>let others suggest how other schemes might fit in - I don't know enough
>about them to say anything reasonable.
>
>I *do* know what my system is, however, so I can talk about how it would
>fit into this scheme. In my scheme, there are things call "grammars"
>that describe how to handle text input. (Is a parser required for
>non-text input?)

yes. although it might not properly be called a parser any more,
something still has to understand the non-text stuff.

[parser stuff snipped]

>How do we handle ambiguity? Is a system of separate modules like this
>going to limit the sophistication of the parsing, simply because of
>the structure imposed by having these inter-module interphases? Can
>there be multiple parser modules loaded? If so, perhaps each client
>or robot connection that provides an input has a parser selection
>associated with it, and it is that parser's input handler that is
>used. That requires that the input generators explicitly know about the
>existence of parsers. Perhaps that sort of thing could be done in an
>input filter, which just maintained a mapping between input streams
>and parsers, and the main parser input handler is never directly called
>from an input generator, but only indirectly via that filter module.
>How would that affect other filter modules - they would have to be
>inserted *before* the parser-chooser filter, else they would never
>see any data!

right. 

socket -> filter out dirty words -> fix all typos -> lexer -> parser
-> actual function calls

works fine. By defining suitable interfaces, anything in the chain could
be replaced by its functional equivalent, e.g.

robot -> lexer -> parser

or

script file -> lexer -> parser

>Where in all of this have we switched from native code to MUD-language
>code? In my system, its done when going from the filtered input into
>the parser, but, with loadable parsers, it could also be done at
>the point of the callouts from the parser to other (scenario) modules.

as I have suggested, with a suitable design this distinction should not 
be an issue. 

>Does it make any sense for there to be more than one database module
>loaded? How would anything choose which one to use for something?

not unless they contain unrelated things. if one contains apples and
the other contains oranges, it's easy to choose which one to go to.
if they both contain apples, you might as well call them a single 
database and wrap them up so client modules don't have to know that 
there's really two (or three, etc).

James




More information about the MUD-Dev mailing list