<!-- MHonArc v2.4.4 --> <!--X-Subject: Re: [MUD-Dev] Custom Server Roll Call? --> <!--X-From-R13: Szvy Svserz <rzvyNcebcurpl.yh> --> <!--X-Date: Fri, 14 May 1999 01:13:07 -0700 --> <!--X-Message-Id: 4.1.19990513131122.0196a630#exchange,windh.net --> <!--X-Content-Type: text/plain --> <!--X-Reference: 4.1.19990506175930.0198ad50#exchange,windh.net --> <!--X-Reference: Pine.GSO.4.10.9905081131510.14338-100000#licia,dtek.chalmers.se --> <!--X-Head-End--> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"> <html> <head> <title>MUD-Dev message, Re: [MUD-Dev] Custom Server Roll Call?</title> <!-- meta name="robots" content="noindex,nofollow" --> <link rev="made" href="mailto:emil#prophecy,lu"> </head> <body background="/backgrounds/paperback.gif" bgcolor="#ffffff" text="#000000" link="#0000FF" alink="#FF0000" vlink="#006000"> <font size="+4" color="#804040"> <strong><em>MUD-Dev<br>mailing list archive</em></strong> </font> <br> [ <a href="../">Other Periods</a> | <a href="../../">Other mailing lists</a> | <a href="/search.php3">Search</a> ] <br clear=all><hr> <!--X-Body-Begin--> <!--X-User-Header--> <!--X-User-Header-End--> <!--X-TopPNI--> Date: [ <a href="msg00238.html">Previous</a> | <a href="msg00241.html">Next</a> ] Thread: [ <a href="msg00177.html">Previous</a> | <a href="msg00189.html">Next</a> ] Index: [ <A HREF="author.html#00240">Author</A> | <A HREF="#00240">Date</A> | <A HREF="thread.html#00240">Thread</A> ] <!--X-TopPNI-End--> <!--X-MsgBody--> <!--X-Subject-Header-Begin--> <H1>Re: [MUD-Dev] Custom Server Roll Call?</H1> <HR> <!--X-Subject-Header-End--> <!--X-Head-of-Message--> <UL> <LI><em>To</em>: <A HREF="mailto:mud-dev#kanga,nu">mud-dev#kanga,nu</A></LI> <LI><em>Subject</em>: Re: [MUD-Dev] Custom Server Roll Call?</LI> <LI><em>From</em>: Emil Eifrem <<A HREF="mailto:emil#prophecy,lu">emil#prophecy,lu</A>></LI> <LI><em>Date</em>: Fri, 14 May 1999 01:57:43 +0200</LI> <LI><em>Reply-To</em>: <A HREF="mailto:mud-dev#kanga,nu">mud-dev#kanga,nu</A></LI> <LI><em>Sender</em>: <A HREF="mailto:mud-dev-admin#kanga,nu">mud-dev-admin#kanga,nu</A></LI> </UL> <!--X-Head-of-Message-End--> <!--X-Head-Body-Sep-Begin--> <HR> <!--X-Head-Body-Sep-End--> <!--X-Body-of-Message--> <PRE> [Sorry for the delayed response. I knew this would take some time to type down and I was unable to find any until tonight.] At Sat, 8 May, 1999, Niklas Elmqvist wrote: > >On Thu, 6 May 1999, Emil Eifrem wrote: >> Lately I've beginning to doubt this modular approach. It sounds very good >> in concept and on an abstract level, but when you get down to some real >> code it's partly really messy and troublesome. > >Hehe, you don't see me *implement* this stuff, do you? *grin* No, no I don't. I have no doubts that you could tho. And I do see the DevMUD team implement it, although I'm not sure how far they've come (I'm subscribed to the list but it's very quiet, I guess most of the discussions are on Jon's slimy or frosty or whatever it's called :). > >> That's probably a result of flawed design decisions on my end, though, >> it's the first time I build a system of this magnitude. > >I guess the difference between good and bad design decisions is very slim >in this area -- do it just right, and you've got a real winner, but if >you're a little off, the results might be less-than-perfect. I'm not >critizing you in any way (I haven't seen your systems architecture), but >rather speaking from my own experience. Nowadays, I think I've outgrown >the start-coding-immediately syndrome and find design and analysis >extremely rewarding (almost TOO rewarding, in fact -- implementation does >not hold that certain thrill anymore). Yes, I think I'm currently in the less-than-perfect area. I get the feeling that many of my design decisions were forced by language-level quirks and specifics, particularly in the area of inter-module communication [IMC]. And I think designing a flexible enough system for IMC is the key issue with this modular architecture. > >> I've been toying with the idea of writing down my overall design >> decisions and post it to the list for comments but never gotten around >> to it. > >Please do! I for one would be very interested. Oh, not after I've talked for a while. But okey, you asked for it. This turned out to be way longer than I thought and I apologize in advance for that. The basic concept (shamelessly stolen from the initial devmud thread ('pdmud'? 'virtual communities'? anyway, it was horrendously misnamed but very interesting, interesting enough, in fact, that I printed out the entire 200 pages thread... but I'm digressing already!)) is that the server consists of a number of separate code entities called modules that are dynamically loaded and unloaded from a static kernel. The kernel's sole responsibility is module loading and unloading -- all other functionality is provided by modules. One of the main advantages with this design is that, if handled properly, one would seldom if ever have to reboot the server. If there's a bug in the economy code -- well, fix the bug off line, recompile the economy module then tell the kernel to ditch the old economy module and load the new one in its place. Beautiful. So far so good. This is all basic DevMUD stuff. Or basic plugin stuff. Whatever. This is familiar to anyone who paid attention to the devmud threads and/or -list and basically to anyone familiar with for example the Linux OS's module loading/unloading features. I don't have a problem with the concepts but I do have some problems with implementing them. In order to elaborate on my problems, I will have to go pretty low level and language centric, sorry about that. So, in my attempt to concretize in Java these very elegant concepts, I (rightly or wrongfully) identified two critical sections: First off, the kernel implementation: If there is a need to modify the kernel, a reboot *is* required. So it's very important to assign as little functionality as possible to the kernel. Secondly, the inter-module communication. IMC is of course the way the different modules talk to each other and it's important in order to get the modules to, once loaded and initialized, behave not as separate pieces but as a coherent whole. My server has two ways of handling IMC. The first, most obvious and most elegant way is event based. I have a standard GoF Observer+Mediator implementation of it where a module registers itself with the kernel for every specific event it's interested in and only then will the kernel relay events of that type to the module. All modules inherit from the abstract superclass kernel.Module, which provides the interface to which all kernel<->module communication is handled -- as well as a number of utility methods for the modules. One such utility method is generateEvent(e, d), which tells the kernel to broadcast an event of type e with data d to all modules subscribed to that type of event. Here's what it may look like in code: --- /* in some random module */ // initialize() is defined in kernel.Module and is called on module bootstrap public void initialize() { // subscribe to the event of type NETWORK_NEW_INPUT registerEvent(IMCEvent.NETWORK_NEW_INPUT); } public void eventReceived(IMCEvent event, IMCEventData data) { if (event == IMCEvent.NETWORK_NEW_INPUT) { parseInput(data); } // ... and all other events we're interested in } private void someRandomMethod() { generateEvent(IMCEvent.MY_EVENT, myData); } --- I like this way. It's decoupled and it's clean and simple. I wish it were enough but, alas, it turns out that we need something more. The second way of of module communication is way uglier. As a short motivation for it, consider a case where I have two modules. One is a Command module that handles command requests from players and the other is a Database module that interacts with a database. Let's say the Command module wants to find out the amount of swords available in the DB. A "traditional" way to solve this may be something like this: --- String query = "SELECT COUNT(*) FROM WEAPONTABLE WHERE TYPE = 'Sword';"; int amountOfSwords = DataBaseBroker.getBroker().issueQuery(query); player.println("There are " + amountOfSwords + " swords in the db."); --- All good. In a server that uses the modular approach (or at least my interpretation of it!) the asynchronousity of the event mechanism is causing some troubles. Consider this snippet: --- String query = "SELECT COUNT(*) FROM WEAPONTABLE WHERE TYPE = 'Sword';"; generateEvent(IMCEvent.DB_ISSUE_QUERY, query); plr.println("uhh... what? We haven't gotten anything back from the db yet."); // ... now what? --- The problem here is that generateEvent() just adds the DB_ISSUE_QUERY event to the event queue and then immediately returns. All the actual processing of the event is handled asynchronously, ie in one or more separate threads in the kernel. Sticking to my example above, this is a big problem for the Command module who just wanted to get an int representing the amount of swords in the DB. What should it do? Sleep until the DB answers with a new event? Continue processing other commands until the DB module answers -- then, continue executing the sword-counting command? None of the above is viable. My solution was to introduce a second way of IMC, tentatively called "exported functions." (Horrible name for something in a Java server, I know.) The idea is to make it possible for a module to execute special, 'exported' methods in other modules. Probably the best way to explain this is to show an example: --- DBModuleInterface = getModuleInterface("database"); int amountOfSwords = DBModuleInterface.issueQuery(query); player.println("There are " + amountOfSwords + " swords in the db."); --- getModuleInterface() is a utility method defined in the module superclass. It takes a string representing a module as argument and returns a Java interface that contains the methods that the target module has chosen to export. This second way of IMC is nice cause it makes stuff like the example above possible -- but it's less nice in that it couples modules together and makes module unloading unsafer. Ok, so in summary, I have two ways of handling communication between modules. One uses a standard event broadcasting (/Observer/Publisher-Subscriber/etc) mechanism. The other uses a way for a module to expose selected "extremely public" methods to the other modules. I guess that was a not-so-brief but yet very-incomplete description of my approach to this modular architecture. I think this all started with me stating that I lately have had doubts as to the (or rather, my) actual implementation of a modular MUD server so I guess I should finish up with elaborating some on that. [Reeaally sorry for the length. :( Is anyone still reading?] Some problems with my approach to the modular server (some of the points are probably applyable to other's as well): - Source code visibility. The way class unloading in Java works, you have to write a custom class loader that load all classes you want to be able to unload. This means that all modules exist in separate namespaces. They can see classes in their own module and classes in the kernel but nothing in the other modules. This means that if I create a 'public class MyClass' in the kernel, then module X can instantiate an object of type MyClass. But if I define a 'public class MyOtherClass' in module Y, module X has no clue about it. So basically, the only thing that is "portable" between modules are classes defined in the standard Java libraries (eg String, int, Hashtable, Socket) and what I have defined in the kernel. This may not sound like a big problem. But it is, believe you me. Let's say that I want to have a Player class that several modules are dealing with. For example, I used to have a "Creation" module, that was responsible for creating new players and characters (player = a rl-world person, character = a fictional person run by a player). I thought this was a neat idea, but in order to have the Creation module pass the newly created players and characters to other modules (say, the 'World' module and the 'Playermode' module) I would have to move the Player and Character classes into the kernel. Remember, classes created in the Creation module are only visible to the Creation module. This whole thing was a big disappointment to me. I originally had planned for about 15 modules. If I can't work out a good solution to this problem, I will probably have to go down to two or three. And that's when I ask myself, why did I make this modular again? - I want my events to be typesafe. I would hate a situation where I'm looking all over the place for a specific bug -- and then realize that I've just mistyped "NETWORK_NEW_INPUT" as "NETWORK_NWE_INPUT". I want the compiler to catch that. Java doesn't have 'enum' but a similar effect is easily fixed by making a singleton class (ie final and with a private constructor) that has static instances of itself as public member variables. I chose to call mine IMCEvent and the code would look something like this: 'IMCEvent e = IMCEvent.MY_EVENT;'. In order for all modules to see and use this class it has to be put into the kernel. Oh. Fun. What was the number one point that was critical to this modular approach again? Oh yea, minimal functionality in the kernel. Why? Cause everytime we change the kernel we have to reboot the entire server. IMCEvents are bound to change almost every time you make some serious changes. Can we put that in the kernel? No. Can we put it in a module? No. Houston? Hmmm. I guess those are the two main probs. I'm gonna quit talking now. If anyone's still reading, congratulations to you. Now tell me what I'm doing wrong. I want to believe in a modular, object-oriented MUD server again! [ - - - - Emil Eifrem [emil#prophecy,lu || www.prophecy.lu/~emil] Implementor of Prophecy [<A HREF="telnet://mud.prophecy.lu:4000">telnet://mud.prophecy.lu:4000</A>] Coordinator of the Jamu effort [<A HREF="http://www.javamud.org">http://www.javamud.org</A>] - - - - ] _______________________________________________ MUD-Dev maillist - MUD-Dev#kanga,nu <A HREF="http://www.kanga.nu/lists/listinfo/mud-dev">http://www.kanga.nu/lists/listinfo/mud-dev</A> </PRE> <!--X-Body-of-Message-End--> <!--X-MsgBody-End--> <!--X-Follow-Ups--> <HR> <!--X-Follow-Ups-End--> <!--X-References--> <UL><LI><STRONG>References</STRONG>: <UL> <LI><STRONG><A NAME="00173" HREF="msg00173.html">Re: [MUD-Dev] Custom Server Roll Call?</A></STRONG> <UL><LI><EM>From:</EM> Emil Eifrem <emil#prophecy,lu></LI></UL></LI> <LI><STRONG><A NAME="00177" HREF="msg00177.html">Re: [MUD-Dev] Custom Server Roll Call?</A></STRONG> <UL><LI><EM>From:</EM> Niklas Elmqvist <d97elm#dtek,chalmers.se></LI></UL></LI> </UL></LI></UL> <!--X-References-End--> <!--X-BotPNI--> <UL> <LI>Prev by Date: <STRONG><A HREF="msg00238.html">Re: [MUD-Dev] Custom Server Roll Call?</A></STRONG> </LI> <LI>Next by Date: <STRONG><A HREF="msg00241.html">Re: [MUD-Dev] Custom Server Roll Call?</A></STRONG> </LI> <LI>Prev by thread: <STRONG><A HREF="msg00177.html">Re: [MUD-Dev] Custom Server Roll Call?</A></STRONG> </LI> <LI>Next by thread: <STRONG><A HREF="msg00189.html">Re: [MUD-Dev] Custom Server Roll Call?</A></STRONG> </LI> <LI>Index(es): <UL> <LI><A HREF="index.html#00240"><STRONG>Date</STRONG></A></LI> <LI><A HREF="thread.html#00240"><STRONG>Thread</STRONG></A></LI> </UL> </LI> </UL> <!--X-BotPNI-End--> <!--X-User-Footer--> <!--X-User-Footer-End--> <ul><li>Thread context: <BLOCKQUOTE><UL> <LI><STRONG>RE: [MUD-Dev] Custom Server Roll Call?</STRONG>, <EM>(continued)</EM> <ul compact> <ul compact> <ul compact> <ul compact> <LI><strong><A NAME="00207" HREF="msg00207.html">RE: [MUD-Dev] Custom Server Roll Call?</A></strong>, Hans-Henrik Staerfeldt <a href="mailto:hhs#cbs,dtu.dk">hhs#cbs,dtu.dk</a>, Tue 11 May 1999, 20:28 GMT </LI> </ul> </ul> <LI><strong><A NAME="00178" HREF="msg00178.html">RE: [MUD-Dev] Custom Server Roll Call?</A></strong>, Ling <a href="mailto:K.L.Lo-94#student,lboro.ac.uk">K.L.Lo-94#student,lboro.ac.uk</a>, Sat 08 May 1999, 18:29 GMT </LI> </ul> <LI><strong><A NAME="00173" HREF="msg00173.html">Re: [MUD-Dev] Custom Server Roll Call?</A></strong>, Emil Eifrem <a href="mailto:emil#prophecy,lu">emil#prophecy,lu</a>, Sat 08 May 1999, 05:06 GMT <UL> <LI><strong><A NAME="00177" HREF="msg00177.html">Re: [MUD-Dev] Custom Server Roll Call?</A></strong>, Niklas Elmqvist <a href="mailto:d97elm#dtek,chalmers.se">d97elm#dtek,chalmers.se</a>, Sat 08 May 1999, 18:29 GMT <UL> <LI><strong><A NAME="00240" HREF="msg00240.html">Re: [MUD-Dev] Custom Server Roll Call?</A></strong>, Emil Eifrem <a href="mailto:emil#prophecy,lu">emil#prophecy,lu</a>, Fri 14 May 1999, 08:13 GMT </LI> </UL> </LI> <LI><strong><A NAME="00189" HREF="msg00189.html">Re: [MUD-Dev] Custom Server Roll Call?</A></strong>, Caliban Tiresias Darklock <a href="mailto:caliban#darklock,com">caliban#darklock,com</a>, Mon 10 May 1999, 07:06 GMT </LI> <LI><strong><A NAME="00197" HREF="msg00197.html">Re: [MUD-Dev] Custom Server Roll Call?</A></strong>, J C Lawrence <a href="mailto:claw#varesearch,com">claw#varesearch,com</a>, Tue 11 May 1999, 20:23 GMT <UL> <LI><strong><A NAME="00227" HREF="msg00227.html">[MUD-Dev] Dynamically changing room descriptions</A></strong>, Ronan Farrell <a href="mailto:rfarrell#ssldesign,com">rfarrell#ssldesign,com</a>, Wed 12 May 1999, 22:39 GMT <UL> <LI><strong><A NAME="00232" HREF="msg00232.html">Re: [MUD-Dev] Dynamically changing room descriptions</A></strong>, Ben Greear <a href="mailto:greear#cyberhighway,net">greear#cyberhighway,net</a>, Fri 14 May 1999, 07:35 GMT </LI> </UL> </LI> </UL> </LI> </UL> </LI> </ul> </LI> </UL></BLOCKQUOTE> </ul> <hr> <center> [ <a href="../">Other Periods</a> | <a href="../../">Other mailing lists</a> | <a href="/search.php3">Search</a> ] </center> <hr> </body> </html>