From: behrends@arctecserv-1.dfki.uni-kl.de (Karl Reimer Behrends)
Newsgroups: rec.games.mud.lp
Subject: Re: Gamedriver survey [LONG]
Date: 4 Oct 92 16:10:59 GMT
Reply-To: behrends@dfki.uni-kl.de (Karl Reimer Behrends)
Organization: University of Kaiserslautern, Germany

Umm. This is going to be a long reply with lots of details.

In article <1992Oct3.235822.10384@fy.chalmers.se>, jacob@fy.chalmers.se (Jacob Hallen) writes:
> In article <1992Oct2.205121.2309@rhrk.uni-kl.de> behrends@dfki.uni-kl.de (Karl Reimer Behrends) writes:
> >
> >I beg to differ. Amylaar's code may not be a model of readability but the only
> >noticable difference to the CD driver I've seen is that the CD driver has
> >comments in front of every function but nobody who doesn't know about the
> >overall concept of how the gamedriver works or who is an experienced programmer
> >is going to understand EITHER of both (nor MudOS, btw). The point is that
> >you need more documentation on the way the various parts work together. Even
> >then I doubt that the occasional programmer is going to be able of making
> >changes affecting the more complicated parts presented with a program this
> >size. Complex algorithms can't be made easy to understand.
> 
> I'm not the only one to think that Amylaars code is hard to read and
> unneccesarily complex. Apart from yourself and Amylaar, everyone who has
> made his opinions known to me has agreed with me on this subject.

Err. You didn't get my point. I never said that Amylaar's code is that simple
that you could give it to a CS freshman without expecting problems. What I tried
to point out was a) that the gamedriver has an inherent complexity due to its
size that doesn't make it exactly easy to grasp, b) that _none_ of the gamedrivers
available is what I'd call readable and well documented and c) that difficult
algorithms aren't easy to understand even if they're short. a) is obvious, I
think. Even TeX, which is by far the best documented program of this size I'd
not touch without probably hours or days of careful study. b) certainly is a
matter of opinion, but my opinion is shared by several people (on several MUDs).
c) should be clear. Better and faster algorithms usually cause an increase in
complexity. Binary search gives an O(log n) upper bound instead of O(n) for
linear search, but is somewhat more difficult to understand especially if
buried within a complex context - see switch() or alists. That's probably also
the reason for call_out()s to be still stored in an O(n) priority queue while
there are O(log n) or even O(1) average case algorithms.

> >
> >|> stops supporting his gamedriver, or the day you decide that you want to
> >|> implement an idea of your own, you are in trouble.
> >
> >Funny, I've done so already and wasn't in any particular trouble.
> Good for you. I hope you will be as satisfied in the future. We have been
> put off by the code elements that Amylaar contributed to 3.1.2 to such
> an extent that we do our best to eliminate them.

May I suggest (insinuate? ;-)) a different reason? Bertrand Meyer of Eiffel
fame once wrote on the topic of software reuse something like that: That to
convince a programmer to use somebody else's mousetrap, it has to be a damn
good one. ;-) It seems you prefer mousetraps labelled CD. ;-)

Actually, you're overdoing the part about the presumed unreadability of
Amylaar's code a bit.

I remember clearly the time when PaderMUD was converting to the CD driver and
mudlib. Data did as suggested in the docs and renamed the Standard domain
to Padermud. The effect was that creation of new characters was impossible (or
was it the conversion of old ones?). Spending a couple of hours leafing through
dozens of include files and program code, I finally discovered the reason was
that Padermud was not in the list of open domains which caused a file_size()
to fail due to the uid not having read access to /d/Padermud. Sigh. Never again
try telling me what unreadable code is.

> >|> The TMI people have put out enough plugs for MudOS, so I won't have to
> >|> say very much about it. I haven't seen their code lately, so I don't know
> >|> anything about the quality of it. I will study their 0.9 as soon as it's out.
> >|> The demise if TMI puts a shadow over MudOS though. Will there be upgrades
> >|> next year, will there be continued development, will it face the same fate
> >|> as 3.1.2?
> >
> >This is _your_ opinion. The TMI people might take the same stand towards the CD
> >driver with about the same rights. They could even argue you might someday take
> >a step towards an incompatible 4.0 driver like you did when going from 2.4.5 to
> >your 3.1.2 version without COMPAT_MODE and then what about all the people who
> >are using yours? Anyway, this is idle speculation and should not actually be
> >used as an argument.
> 
> I'm not speculating. I'm posing questions that a prospective user should ask
> himself. And there is a very big difference. TMI is without a site at the
> moment, and from my experience the efforts that are put into a mud project
> quickly drop if the project lacks a site where the efforts can be coordinated 

What experience? Was Genesis ever without a site? Anything else is rumours
and hearsay. If desired, I can give evidence to the contrary with similar
arguments.

> and tested. Also, your analogy with 2.4.5 -> 3.whatever doesn't hold.
> It's no big deal to convert a 2.4.5 mud to use the CD gamedriver or 3.1.2
> non compat, so noone is really left out in the cold. However, you may be

Hmpf. No. Converting 2.4.5 to native mode is a major task. Simply because
of the changes concerning reset() and move_object() you will have to touch
almost every single object. On a mudlib the size of TUBMUD (50 MBytes or so)
this is unthinkable. So we'll stick to compat mode and make slow changes which
won't hurt. And only Amylaar's driver supports compat mode. Did I mention that
we have uptimes of two weeks and more?

> far from the mainstream with a very long way back if you utilise the
> special features of a new gamedriver and find that support of it has
> gone away.

You mean if I use move_object() and find its behaviour is suddenly changed
to either accepting only one argument or the first one having to be
this_object()?

> >|> The CD gamedriver is of course the one I want to make a plug for. It is a
> >|> stable gamedriver, even if it doesn't have all the nifties that some other
> >
> >Now this point is debatable. IMHO the CD driver isn't stable at all (at least
> >version 1.57) but prone to unpredictable crashs as well as MudOS 0.8.14 and the
> >vanilla 3.1.2 driver. I tried four wellknown (hmmm, not that wellknown at all,
> >it appears) ways to crash vanilla 3.1.2 on the CD and MudOS driver. 
> 
> I would be very interested in hearing about these bugs. Do you have code
> that can be used to repeat them? Do you have fixes or bug descriptions?

Certainly. But you could easily have found them by studying Amylaar's done file.
1) continue within a switch. The CD driver prints a nice traceback before
   dumping core, MudOS just dies silently. Easy fix: disallow continue within
   switches. Amylaar did better and made it possible to use continue within
   a switch.
2) throw(0). This causes an underflow of the catch context stack since throw(0)
   is also used as an end_of_catch code. Fix: disallow throw() or use a different
   stack machine code as an end of catch instruction. Thanks to the people at
   Nanny for pointing out this bug to me.
3) Mutual recursive inherits, i.e. A inherits B and B inherits A. This will
   lead to infinite recursion and crash as soons as stack overflow occurs.
   Fix: limit the maximal inheritance depth allowed.
4) save_object() within objects containg circular arrays. Example:
   mixed *a; create() { a = allocate(1); a[0]=a; save_object(SOME_FILE); }
   Easy fix: disallow saving of objects with arrays more than a given depth.
   Difficult fix (employed by Amylaar): save the circular structure in a special
   format and make it recoverable by restore_object(). Circular structures of
   course mean memory leaks and will sooner or later make a garbage collector
   a necessity. But probably you will think of this solution as 'unnecessarily
   complex.
5) lvalue lifetime bug: There isn't an easy fix for it and it leads to delayed
   and unpredictable crashes. It's simplest form is something like a[0]=(a=0).
   There the reference to a[0] is pushed on the stack, a gets freed and then
   the area formerly occupied by a gets assigned something. This leads to
   especially nasty effects if you do something more on the right hand side,
   i.e. a function call which deallocates a and then loads an object into the
   free space - has rather devastating effects, I assure you. Fixing that means
   major changes in the gamedriver. Need I say that Amylaar fixed it?

The problem is that your attitude towards bugs seems to be based on a
producer-consumer relation. You seem to handle the bugs whenever your mud
produces them instead of actively looking for them. And your mudlib isn't really
apt for testing the driver. I do not deny that it has some very nice features,
and it is not lacking complexity in terms of size. But you don't stretch LPC to
its limits. I've just studied query_auto_load() and it's almost unchanged since
2.4.5.

> >All four
> >of them 'worked'. All four are likely to be stumbled upon by an unsuspecting
> >user sooner or later. None of said drivers have the lvalue lifetime bug fixed
> >which tends to make the game crash without an apparent feature. Second, you're
> >forced to live with all the basic design flaws of native mode.
> 
> You are insinuating that there are design flaws that were put in in the
> transition from version 2 to version 3, without saying anything about
> what the flaws are. also, you are insinuating that we would be stuck
> with the flaws, if in fact there were some. I think you need to give
> us some meat if this is to be considered a valid argument.

Again you didn't get my point. Nobody was happy with all the bugs and problems
in version 2. A fresh approach was the best that could be done at the time.
What I'm talking about are differences between native and compat mode.
And then, I'm not insinuating. I'm quite happy to be given the opportunity to
shed some light on the problems associated with native mode. Most of my
arguments stem from discussions on Nightfall when they decided to change to
native mode. There are in fact three points I'd like to handle in some
detail.

1) The subdivision of reset(arg) in create() and reset() without arguments.
   As can be easily seen, there are three different parts of the original
   reset(arg). One executed when arg == 0, one when arg != 0 and one no matter
   what the value of arg is. Of course, most time you only need the part
   with arg == 0. Thus the introduction of create() must have appeared to be
   quite useful. On the other hand, the second most needed part is the one
   independent of arg. You need it especially for populating a room with stuff.
   The part with arg != 0 is rarely ever used. Thus the new reset() is chiefly
   used for emulating the behaviour of the old reset(arg) and leads to
   unnecessary duplication of code which has to be done both in create() and
   reset(), even if it's a simple function call. This is not only ugly but also
   incurs unnecessary overhead. If create() is to be introduced, the old
   behaviour of reset() should still be used.
2) Second, there is the point of having done moves by local functions. While
   the basic idea of getting more control over moves is sound, the implementation
   again leaves much to be desired. Let us consider how the environment()/
   inventory() scheme is usually done: Every object maintains a list of
   objects it contains and a pointer to its environment. Thus, moving object
   A from B to C affects three objects. The object A that is moved and thus needs
   its environment updated; B, from whose list of contents A has to be removed;
   and finally C into whose list of contents A has to be inserted. But this so
   far is only a technical issue affecting the internals of the gamedriver. The
   point I am trying to make is that these technical details naturally extend to
   other problems like updating of weight and light which is to be handled by the
   mudlib. But now in native mode B and C are at complete mercy of A. If A decides
   to move itself around without consulting B and C at all, they can't do anything
   about it. This can quite easily happen by accident if you redefine move() and
   forget to call ::move() or do it in the wrong place. Then you'll have to
   fight the same undetectable carry bugs as if using pure move_object().
3) Third, there are uids. With all the hype about the supposedly better security
   of native mode it may come as a surprise that uids actually decrease overall
   security. There are two reasons behind this. First, uids implement access
   rights in a dynamic way - especially export_uid() should be considered a
   potential security leak for if as soon as it is used without _utmost_
   precaution it will be easy to do damage this way. And then uids are by far too
   weak a concept to grasp the partial ordering defined by A >= B iff A's objects
   may modify B's. I will give two examples from the CDlib 0.25 to illustrate
   this. First, consider a domain which has an archwizard in it who's not the
   domainlord. Then the domainlord has write access to the archwizard's directory
   whose objects can be set to the archwizard's uid. And these objects have write
   access within the entire mudlib. This of course due to the violation of the
   partial order mentioned above. Another example is the trust command. If
   used with ids (i.e. trust me:sometool) the following code is quite dangerous:

   id(/* No argument necessary for this hack */) {
      enable_commands(); /* _we_ get the write()s, not the wizard and can modify
                         them and pass them on not to let him suspect anything */
      if (query_verb()=="trust")
      {
        call_out("wreak_havoc",1);
        return 1;
      }
   }

   This shows how dangerous export_uid() can be. And if you just restrict yourself
   to seteuid(getuid()), you don't need the security stuff at all.
   Please note that I'm not denying the usefulness of moving valid_read() and
   valid_write() to the master object or moving the hardcoded access checks
   of save_object() to valid_write() master object. But this has been done by
   Amylaar, still maintaining compatibility with 2.4.5 mudlibs.

If you had just kept reset(arg) working, perhaps adding create() all the same
and made move_object() a simul_efun with perhaps an additional parameter, this
would have even maintained compatibility, achieving the same. I strongly believe
in what Popper calls piecemeal engineering, i.e. not making changes which are
too radical and only let everything collapse.

> >|> drivers have and we have no plans on stopping the development of it. We
> >|> do think that readability and maintainability is just as important as
> >|> performance, and some of the work going on at the moment is devoted to
> >|> improvement in this area.
> >
> >Agreed. But they can only be means on the way to a more stable and
> >reliable gamedriver.
> 
> And means to better extendability, and a source of information for people
> who want to make performance improvements.

If you want better performance you'll have to sacrifice ease of understanding
which I understand is an absolute no with you? Besides, I challenge you to beat
or even approach the speed of Amylaar's driver.

> Jacob Hallen
> 
> PS. If Amylaar gets a job without net access, a girlfriend or some other
> mudfree life, where does that leave you? Relying on a one man show isn't
> the best of options. DS

So you're going to devote your life to gamedriver support, huh? ;-)
Actually, what you suggested for Amylaar to happen, did already occur to
Lars (quite intelligent of him - MUD != Life, come to think of it, and he
already spent enough of it for muds).

And so far his is the only choice for people whose MUDs still run in compat
mode and want to have a stable gamedriver.

					Reimer Behrends
Newsgroups: rec.games.mud.lp