Just a bit of musing

coder at ibm.net coder at ibm.net
Thu Mar 6 20:48:46 CET 1997


On 03/03/97 at 08:27 AM, "Carter T Shock" <ctso at umiacs.umd.edu> said:

>...The code tends to be littered with for loops that iterate thru
>lists (room lists, player lists, etc etc). I, among others, feel this is
>a Bad Thing (tip o' the hat to coder@).

Yup.  I find it utterly mazing that nobody (else) has done a fully event
driven server.  No polling loops, no list scanning, no wasted iterations. 
Just grab the event, process what's needed, and get out.

>I posit that, in general, command interpreters for muds are Bad Things.
>The proposal runs sorta like this:
>	The command interface to a mud can be viewed as a context free grammar.

A good MUD grammar is incredibly context sensitive.  A simple example is
the case where the presence or proximity of an object adds verbs to the
players.  There is no need for this crap to wander thru the global
namespace, or even warp the general grammar.

>As such, one should be able to whip up a command interpreter with
>lex/yacc. I've started on it at least a dozen times but never managed to
>quite finish. Is this worthwhile? Thoughts?

Nope.  That approach tends to preclude having a dynamic language where
users or objects can add, redefine, mutate or delete verbs and nouns
freely at runtime.  A Very Bad Idea.

>While I'm at it, let's try another pet peeve. Most mud codes I've run
>into run as a unified process. That is to say, there is one huge
>executable that does everything. One nasty side-effect is that any
>socket-related problems can bring the game to a grinding halt...

I don't.  I take an explicitily multi-threaded event driven approach.  To
describe:  (please excuse typo's.  I have the flu, and my eyes are
burning)

  The server itself knows nothing about the game it is representing.  It has
  no parser, game knowedge, or anything else application specific.  All it 
  does is represent a database.

  The database consists of records.  Each record defines an object in the 
  MUD world.  An object may have attributes, methods, and verbs defined on 
  it.

  The server is entirely event driven.

  Every event executes asynchronously in its own thread using a lockless 
  model.

  The server core consists of the following base units:
    -- DB
    -- Dispatchor
    -- Executor
    -- Connection

  The Dispatchor consists of two threads which own and operate the Event List.  

  The Event list contains an entry for every event which has been logged 
  with the system but not processed yet.  Logged events are of two forms:
  1) execute at XXX time, or 2) Percentage chance of execution within time.

  One thread in the dispatchor handles placement of new entries onto the 
  Event List.  The other thread processes the list looking for "ripe" events 
  (ie ones ready to be executed.

  Ripe events are sent to the Executor where they are placed in an Event 
  Queue.  The Event Queue is a priority queue with events ordered by their 
  own execution priority.

  The Executor manages the Event pool, a local pool of threads (the number 
  dynamically grows and shrinks at runtime depending on load) which are used 
  to execute the events pulled off the Event Queue.  Threads in the Event Pool 
  are re-used to execute ripe events.

  Events are pulled off the Event Queue in priority order and handed to the 
  first available thread.  

  Compleated events can log futher/later events back to the Dispatchor for 
  subsequent animation.  This is how mobiles are naimated for instance.
  
  User IO arrives thru the Connector.  The connector is essentially a pool of 
  threads which asynchronously manage the general pool of outside connections.  
  A seperate monitor is responsible for keeping the IO network tree happy.

  User commands arrive at the Connector and are immediately sent to the 
  Dispatchor as Execute-In-Zero-Time (ie no delay) events.  The event logged 
  is acutally to parse the command as entered.  The dispatchor then routes 
  the event to the Executor, it runs the event and the resultant parse creates 
  a new event which is logged with the Dispatchor to actually execute the 
  intended action.  

>...(in
>particular, a user connects on the mother socket and ends up in the
>queue. The OS then steps thru the mother socket's queue and creates new
>sockets for everybody it finds there. If one of the connections has died
>on the queue, you go into a linger state regardless of the socket
>settings while the OS tries to find the socket it thought was on the
>queue). 

You are presuming *nix/Berkely-stlye handling of sockets here.  Not all
OS'es work that way.

>The suggestion goes like this: run the game as a daemon. run
>another process to host the mother socket. For each connection on mom,
>spawn a new process that acts as a comm link between the user and the
>daemon. 

Threads are cheaper than processes.  The pro/con is the lack of protection
from rogue threads.  You can either view this as a source of stability, or
a way to ensure coorect programming.  Its sort of like being const
correct.

>if the user has link troubles, or is idle or whatever, it's the proxy
>that hangs.

This is also why I went with an asynchronous model for paring the IO
threads with the sockets.

>the proxy-daemon comm method is thru semaphores/shared memory. daemon
>writes updates to a shared segment that is read-only to all proxies.

Seeing as most *nix'es don't implement fast RAM sempahores, this can get a
little expensive at runtime if the contention rate gets high.

>PS: one more advantage to splitting up the mud process. One huge process
>is _much_ more likely to get swapped out by the OS than several "small"
>processes. 

The real arument here is the price of a higher context shift frequency
versus the proability and cost of swapping.

>Extra bonus points if you're lucky enough to run on a
>multi-processor. Who wants to open the can of worms called "MUDs in a
>distributed processing environment"?

Well, given an SMP machine, with an OS that will intelligently distributes
threads -- that all semi happens for free with me (its not quite so good
as I try to minimise thread creations, as few to no current OS'es will
migrate a thread across processors for load sharing).  On the other side,
a definite design goal of my server is for it so support running in
clustered enviroments where the entire cluster presents a single
representation of a game.

--
J C Lawrence                               Internet: claw at null.net
----------(*)                              Internet: coder at ibm.net
...Honourary Member of Clan McFud -- Teamer's Avenging Monolith...




More information about the mud-dev-archive mailing list