MOOcode WTF



  • Okay. You may or may not be familiar with MOOs and MUDs and such. (If you are intimately familiar, ask me about my verb player:* tnt, it does amazing things. :D) On a standard MOO, there is a standard sort of command like...

    [code]@create $thing called Something[/code]

    Normally, if you just type @create by itself something like this happens:

    Usage:  @create <parent-class> named [name:]alias,...,alias
       or:  @create <parent-class> named name-and-alias,alias,...,alias
    
    where [parent-class] is one of the standard classes ($note, $letter, $thing, or $container) or an object
        number (e.g., #999), or the name of some object in the current room.
    You can use "called" instead of "named", if you wish.

    The MOO's chief wizard decided this wasn't good enough, and decided to give a friendly menu instead, to make things easier for new players. I finally realized this when a new player started talking about the menu. Okay. I don't like silly menus on MOOs like this in general, but I thought it would be worth a shot if I could edit it to include many of our quality newly developed objects. So I took a look.
    #4:@create   any any any
     1:  set_task_perms(player);
     2:  nargs = length(args);
     3:  pos = "named" in args;
     4:  if (pos <= 1 || pos == nargs)
     5:    pos = "called" in args;
     6:  endif
     7:  if (pos <= 1 || pos == nargs)
     8:    "New menu of popular generics make it easier for new users to create stuff. Jan 4/9/98";
     9:    finished = 0;
    10:    while (!finished)
    11:      player:tell("*** Creation Menu");
    12:      player:tell("What do you want to create?");
    13:      player:tell();
    14:      player:tell("Basic Objects  Special Objects");
    15:      player:tell("-------------  ---------------------------------------");
    16:      player:tell("1) A Thing     5) A Robot           13) A Recorder");
    17:      player:tell("2) A Container 6) A Lecture         14) A Recording System");
    18:      player:tell("3) A Note      7) A Video tape      15) A Publicly Writable Note");
    19:      player:tell("4) A Letter    8) A Video camera    16) A Recitable Note");
    20:      player:tell("               9) A VCR             17) A Self-Cleaning Room");
    21:      player:tell("              10) A TV              18) A Pet");
    22:      player:tell("              11) A Slide Projector 19) A Bot");
    23:      player:tell("              12) A Note Board      20) A Compass");
    24:      player:tell();
    25:      player:tell("Please enter your choice (1-20), or type Q to quit");
    26:      ans = read(player);
    27:      if (toint(ans) >= 1 && toint(ans) <= 20)
    28:        finished = 1;
    29:        player:tell("Please type a name for this new object");
    30:        namestr = read(player);
    31:        if (ans == "1")
    32:          parentstr = "$thing";
    33:        elseif (ans == "2")
    34:          parentstr = "$container";
    35:        elseif (ans == "3")
    36:          parentstr = "$note";
    37:        elseif (ans == "4")
    38:          parentstr = "$letter";
    39:        elseif (ans == "5")
    40:          parentstr = "$bot";
    41:        elseif (ans == "6")
    42:          parentstr = "$lecture";
    43:        elseif (ans == "7")
    44:          parentstr = "$tape";
    45:        elseif (ans == "8")
    46:          parentstr = "$camera";
    47:        elseif (ans == "9")
    48:          parentstr = "$vcr";
    49:        elseif (ans == "10")
    50:          parentstr = "$tv";
    51:        elseif (ans == "11")
    52:          parentstr = "$slide_projector";
    53:        elseif (ans == "12")
    54:          parentstr = "$note_board";
    55:        elseif (ans == "13")
    56:          parentstr = "$intercom";
    57:        elseif (ans == "14")
    58:          parentstr = "$recording_system";
    59:        elseif (ans == "15")
    60:          parentstr = "$public_writable_note";
    61:        elseif (ans == "16")
    62:          parentstr = "$recitable_note";
    63:        elseif (ans == "17")
    64:          parentstr = "$self_clean_room";
    65:        elseif (ans == "18")
    66:          parentstr = "$pet";
    67:        elseif (ans == "19")
    68:          parentstr = "$bot";
    69:        elseif (ans == "20")
    70:          parentstr = "$compass";
    71:        endif
    72:      else
    73:        return player:tell("Creation procedure aborted...");
    
    ... normal code resumed here about here.
    (I've anonymized line 11.)


  • It's not going to get any awards, but I'm not sure I see what the WTF is.

    Is it "#4:@create any any any" ? Is this an issue where the user can create delightful security problems?

    It's not the list, is it? It's stupid and ugly but for only 20 elements I'm not sure it's exactly a wtf. I mean, yes, the user should have been given a tabular list of names for popular things instead, but...



  • Look at items #5 and #19, both in the player:tell commands and in the elsif frenzy block...



  • So he doubled up one item.  Some players may be more familiar with one term rather then another.

    I'm with the other guy, where is the WTF? 



  • The WTF seems obvious enough to me.

    The menu is hard-coded, complete with with an huge if ... elseif block. A saner approach would of course be to make the whole thing generic and read the object types from a file or database or whatever.

    This approach reminds me of my very early days of programming where I'd write games that were basically computerized versions of 'Choose your own adventure' games where the 'gameplay' consisted of the player selecting from a number of different numbered choices to make each time his input was required. Seriously, it looked just like this. Except copy-pasted over and over. And written in BASIC.


     



  • @Volmarias said:

    It's not going to get any awards, but I'm not sure I see what the WTF is.

    Is it "#4:@create any any any" ? Is this an issue where the user can create delightful security problems?

    It's not the list, is it? It's stupid and ugly but for only 20 elements I'm not sure it's exactly a wtf. I mean, yes, the user should have been given a tabular list of names for popular things instead, but...


    'any any any' is actually the syntax (any direct object, any preposition, any indirect object => anything, really). Other popular syntax descriptions include, 'this none none' (for example, @verb ball:kick this none none) and 'this none this' (which is impossible with the parser, so it's typically used as the standard arguments for an executable verb (like player:tell - there's also a flag for executable-or-not, though).



  • @tharfagreinir said:

    This approach reminds me of my very early days of programming where I'd write games that were basically computerized versions of 'Choose your own adventure' games where the 'gameplay' consisted of the player selecting from a number of different numbered choices to make each time his input was required. Seriously, it looked just like this. Except copy-pasted over and over. And written in BASIC.


    Gah! Don't remind me. I did the same thing. LOL



  • High five!


    And yeah - I forgot to mention the GOTOs. There was a GOTO for every case, so the code was basically one big chunk broken into sections, where you'd jump from section to section based on the numbers entered. And of course the printing of the menu and getting input from the user was copy-pasted in each section. Ah, memories ...



  • @tharfagreinir said:

    The WTF seems obvious enough to me.

    The menu is hard-coded, complete with with an huge if ... elseif block. A saner approach would of course be to make the whole thing generic and read the object types from a file or database or whatever.

    This approach reminds me of my very early days of programming where I'd write games that were basically computerized versions of 'Choose your own adventure' games where the 'gameplay' consisted of the player selecting from a number of different numbered choices to make each time his input was required. Seriously, it looked just like this. Except copy-pasted over and over. And written in BASIC.


     

    So the WTF here is that the programmer was a novice, instead of an experienced 1337 h4x0r?

    I'll grant the doubling of "bot", though in all fairness it could be for the sake of people who know what one is, but not the other. I understand "bot" perfectly, but when I hear "robot" I think of clanking monstrosities circa 1950 instead of "bots". Conversely, the layman might be familiar with "robot", but not understand what "bot" is supposed to mean. Bot? Bit? Bort? Huh?

    Maybe you have more experience than I do at this (My experience is limited to replying to this topic), but your "solution" seems to scream "ENTERPRISY!" at the top of its lungs. This is, if I'm reading correctly, a little script that one of the programmers threw together to give a few popular choices to new players. Else if instead of switch is kind of dumb, but we (well, perhaps just I) don't know if this language even has a switch constructor. File IO or Database calls (!) for something this small would be a real WTF, imo, assuming that the script even allows this. For something this small, hardcoding is simply not a problem. The "correct" solution as far as I'm concerned is to have created an array and map the user's input into the array, but this is definately far from a real WTF.



  • @Volmarias said:

    @tharfagreinir said:

    The WTF seems obvious enough to me.

    The menu is hard-coded, complete with with an huge if ... elseif block. A saner approach would of course be to make the whole thing generic and read the object types from a file or database or whatever.

    This approach reminds me of my very early days of programming where I'd write games that were basically computerized versions of 'Choose your own adventure' games where the 'gameplay' consisted of the player selecting from a number of different numbered choices to make each time his input was required. Seriously, it looked just like this. Except copy-pasted over and over. And written in BASIC.


     

    So the WTF here is that the programmer was a novice, instead of an experienced 1337 h4x0r?

    I'll grant the doubling of "bot", though in all fairness it could be for the sake of people who know what one is, but not the other. I understand "bot" perfectly, but when I hear "robot" I think of clanking monstrosities circa 1950 instead of "bots". Conversely, the layman might be familiar with "robot", but not understand what "bot" is supposed to mean. Bot? Bit? Bort? Huh?

    Maybe you have more experience than I do at this (My experience is limited to replying to this topic), but your "solution" seems to scream "ENTERPRISY!" at the top of its lungs. This is, if I'm reading correctly, a little script that one of the programmers threw together to give a few popular choices to new players. Else if instead of switch is kind of dumb, but we (well, perhaps just I) don't know if this language even has a switch constructor. File IO or Database calls (!) for something this small would be a real WTF, imo, assuming that the script even allows this. For something this small, hardcoding is simply not a problem. The "correct" solution as far as I'm concerned is to have created an array and map the user's input into the array, but this is definately far from a real WTF.

    A few things. First, MOO essentially maintains an in-memory object database, periodically synced to disk - there are some patches for file access functions, but they're relatively uncommon, and are primarily used for accessing "bulk data" like MOOmail messages and such. And, yes, the language also has no switch.

    That said, I'm going to amend the WTF. This isn't actually the work of a wizard. This is apparently in the core - the database they give you to start up a MOO. It's somewhat analogous to, oh, the libraries you get with Java, or something like that (except there are a variety of different cores, and usually just the one Java library... you get the idea perhaps though.) Or another analogy- LambdaMOO is the kernel, and the core is all the user-space stuff. Like distros of Linux. :) This takes it up just a bit from lone-random-programmer I think.

    The good news is that because of the object database, you don't need to go for a file for a menu like this - you could make a grand little start by not hard-coding the menu and putting it into a property somewhere, then saying [code]player:tell_lines($code_utils.create_menu)[/code] so that you can edit the menu without a bunch of player:tell()s getting in the way. Secondly, the array of objects mappings could be done with another property, say, [code]$code_utils.create_menu_objects[/code] set to [code]{"$thing","$container","$bot"...}[/code] (which could ALSO be edited without editing code!) and then say [code]parentstr = $code_utils.create_menu_objects[ans][/code] - replacing, oh, 40 lines of code or so with 2. A little code-data separation goes a long way. Tip: if you have more than, oh, 8 lines all in a row that look almost exactly the same as one another... it's time to consider some abstraction!

    Personally, I'd prefer a list of objects by object number, and some business with $string_utils:columnize, but that's just me being fancy.

    You're right it's not an award-winner, just a little sidebar entry, but I see that some people are looking back upon the bad old if-else-elseif-elseif-elseif-elseif days and smiling (well, wincing, anyway :D) Perhaps it's more of an "OMG" than a "WTF", I suppose...



  • I've written MUSH code and MUCK code, but I can say I never generated a giant hardcoded list like this. Well... Not often. You can do some very interesting crap with it, and if you're a good programmer you can occasionally get the wizards to say "Ooh, how did you do that?" Like getting a pet object to "see" you leave the room and follow you, or come if you yell in an adjacent room, or respond creatively to spoken commands... These things are deceptively hard in MUSH, for one, partly because the language is structured around preventing certain disruptive hacks and partly because it's a strapped on half-Lisp, half Tcl for a giant object world with a constant number of slots per object.



  • It has no switch construct, but the LambdaCore database does have an associative-array tool in $list_utils:assoc and friends.

    This terrifying waste of real estate is not in any version of LambdaCore I can find.  It may be in some other cores, but given that LambdaCore, JHCore, and Minimal.db are the most-used cores, I suspect this is Joe's Retarded Little Brother Core...

    The Real WTF(tm) here is that someone with near-god levels of power in the MOO is this bad at programming.  It's only a matter of time before he forgets a set_task_perms somewhere critical, or screws up argument parsing, and creates a verb that gives Joe User admin powers.
     




  • @Angstrom said:

    It has no switch construct, but the LambdaCore database does have an associative-array tool in $list_utils:assoc and friends.

    This terrifying waste of real estate is not in any version of LambdaCore I can find.  It may be in some other cores, but given that LambdaCore, JHCore, and Minimal.db are the most-used cores, I suspect this is Joe's Retarded Little Brother Core...

    The Real WTF(tm) here is that someone with near-god levels of power in the MOO is this bad at programming.  It's only a matter of time before he forgets a set_task_perms somewhere critical, or screws up argument parsing, and creates a verb that gives Joe User admin powers.
     


    Oh, wow! Someone else who's familiar with MOOs. :) Yeah, I think this is EduCore, but I don't really know (heh, I didn't set the place up). It's going to be a new core by the time I'm done with it :P I have relatively few qualms about rewriting verbs like 'say'.

    As long as you're here, let me also mention the old @away system (which added a new column in the @who listing, showing whether someone was @away or @busy or such.) Not a bad idea! But... the implementation... well, when you typed @away, it would actually add a new property on the player named .away with the contents of your away message, and remove that property when you came @back. There was a separate property for .brb and .busy. I don't know how anyone could find the functions to add and remove properties without actually learning about basic inheritance... *sigh*

    You might also appreciate the fun verb I added to one of the standard player classes, near the top, named * (+x, this none this). I'm still not through dealing with all the security ramifications (for now there are simply whitelists involved, which is okay, but inadequately flexible). But it does wonderful amazing things. =) I think it's one of the few WTFs out there that's actually very well designed :)



  • I got my real start in programming on moo.ca, back when it was still called "Schoolnet MOO".  Man, that was ages ago.

    A callable-only verb named *...  hmm.  Just at a long, evil guess, are you trying to implement something like $feature objects, for APIs instead of input commands?  If so, that's terrifying.  Also, the MOO security model is broken as designed; moo.ca did a relatively good job at locking it down at the expense of breaking a lot of potentially-cool things (code is not allowed to move any objects except those owned by the original programmer, unless the verb is owned by a wizard, for a start).  You really have to just trust anyone you give programmer privs to almost as much as wizards.

    That implementation of @away isn't the most evil misuse of property_add I've ever seen, but it probably means the guy used exception handling for flow control (or `expression ! E_PROPNF => 0' expressions*, which are morally equivalent), rather than explicitly testing for the property.  Plus, it's relatively easy to add idle-time based auto-@back to @who as long as you don't monkey around with dynamic properties.

    Other fun WTF: the MOO language supports (in fact, requires) parallel processes.  Tasks (running programs) are broken into slices and scheduled into the server's single thread of execution.  The MOO engine contains an entire preemptive task scheduler complete with privilege levels, forks, IO-waits, and sleep.  But it provides no synchronization primitives.  You just have to hope your slice is long enough for the whole verb to run (often, but not always, true), or write code that can cope with properties changing between read and write.

    I miss :title() competitions.

    * Inline exception handling.  When used very, very carefully, it's the coolest thing since sliced bread.  `expression ! exceptions => value' evaluates expression and returns the result, unless one of the exceptions in exceptions is thrown and not caught in the expression, in which case value is returned instead.  It's about on par with the ?: operator in C for potential misuse, though.

    Late edit: oh, and I forgot to mention that the associative array tools in $list_utils are hideously, mind-blowingly stupid.  O(n) amortized time for access by key?  Don't mind if I do!



  • @Angstrom said:

    A callable-only verb named *...  hmm.  Just at a long, evil guess, are you trying to implement something like $feature objects, for APIs instead of input commands?  If so, that's terrifying.


    Be afraid. Be very afraid. Mwuahahahaha!!! I'm actually looking for a little more flexibility than feature objects (more dynamic adding/removing). The per-verb overhead isn't all that bad - 47 ticks or so per verb call, at present, for one hook installed? That can and should be optimized more, though. It does have the potential to break a lot of things that check for caller == this and such, so I've done several passes through the core to look for code to secure against this... And, of course, it's so incredibly potentially-abusable that my security system is for now just 'whitelisting objects'. A more flexible security system is the main thing holding it back from general usefulness; it might still be good for a mostly nonprogrammer MOO of some sort, however. Oh! There's a $do_command hook there too. :) which I hope to exploit to add new sorts of interfaces... and a $do_out_of_band_command as an emergency 'disable all of these' feature. And I'm planning some sort of do_huh hook as well, so you can dynamically add commands which take a player as an argument.

    All this, now, is strictly for civilians (no wizards, please, omg).

    On a related note, are you familiar with the "Cyan Programmer's Token?" I blame that for the idea. :)
    @Angstrom said:

    You really have to just trust anyone you give programmer privs to almost as much as wizards.

    Ehh, not quite as much. You can do some pretty nasty stuff as a programmer, by default, true (heck, one might crash the MOO) and can certainly make players' lives miserable if they don't know what to do about you (and even sometimes if they do). But you can't go around recycling their stuff, editing their descriptions, reprogramming their objects, changing gross system settings (like connecting through the network - heck, you could send email spam from the MOO), or anything like that.
    @Angstrom said:
    The MOO engine contains an entire preemptive task scheduler complete with privilege levels, forks, IO-waits, and sleep.

    Now that's false. The MOO engine runs tasks sequentially. The tasks will be promptly aborted when they run out of their allocated time (30,000 ticks by default, reset to 15,000 whenever you suspend, or in a fork), and you will notice it:

    ;while(1) endwhile
    #-1:Input to EVAL, line 3:  Task ran out of ticks
    ... called from built-in function eval()
    ... called from #58:eval_cmd_string (this == #373), line 19
    ... called from #567:* (this == #373), line 8
    ... called from #58:eval*-d (this == #373), line 13
    (End of traceback)

    But while your task is executing it is the [b]only[/b] task executing. Unless you're suspend()ing or fork()ing, you can rest assured your properties will remain intact unless you set them (or call something that sets them).


    At least on the LambdaMOO server, anyway. I don't know of any others. :P
    @Angstrom said:

    That implementation of @away isn't the most evil misuse of property_add I've ever seen, but it probably means the guy used exception handling for flow control

    No. He checked for the existence of the property (I forget how, the code is gone now :D).
    @Angstrom said:
    Late edit: oh, and I forgot to mention that the associative array tools in $list_utils are hideously, mind-blowingly stupid. O(n) amortized time for access by key? Don't mind if I do!

    I wrote a set of hashtable utilities (to work with hastable-type lists - x number of slots, with plain association lists for the buckets) but found the overhead from computing hashes in MOOcode easily exceeded the time to just use the associative array tools, at least for small values of n. There's a Generic Database object that works by adding properties dynamically. This is not suitable for quick computation mid-code, though, so there's a middle ground that's not really well-served by either. Perhaps if I had some faster arbitrary-data-to-hash function of some sort... anyway, I had been using this for an implementation of Dijkstra's algorithm, where the alist tools were indeed inadequate.

    [EDIT]: Oh, look at that. ... called from #567:* (this == #373), line 8. =D

    On a mostly unrelated topic, I've hacked $string_utils:pronoun_sub to accept lists instead of just objects in many cases. I also want a new edition of $you (some sort of $anybody...)



  • @tharfagreinir said:

    High five!


    And yeah - I forgot to mention the GOTOs. There was a GOTO for every case, so the code was basically one big chunk broken into sections, where you'd jump from section to section based on the numbers entered. And of course the printing of the menu and getting input from the user was copy-pasted in each section. Ah, memories ...

    Ough.

    Copy-pasted? We didn't have copy-paste. We were lucky enough if we got turbo cartridges for our Commodore 64s =)

    Me and my friends basically did the same thing... until we read one book about writing text adventures. After that, we learned to do this sort of stuff:

    1000 DATA far away from home,2,0,0,0,0,0
    1010 DATA in some place or other,0,0,1,0,0,0

    Where the numbers say which room you can go when you try to go N,E,S,W,U,D, and it's zero if you can't go that way. This is then read to memory and boom, instant grid movement.

    Anyway, BEFORE that magic moment, we made some awful GOTO-based adventures. My friend didn't. He thought GOSUB was the same thing as GOTO. ...annnd the adventures bombed mysteriously at some point. =)

    I even translated one of these awful adventures to Inform to compile them to Z-Code so they can be run easily on modern systems. Regrettably, Z-Code didn't quite allow for some of the awful features our programs had. =)


Log in to reply