Re: [CODE]: Auto-Completion

From: Daniel A. Koepke (dkoepke@california.com)
Date: 11/11/99


On Thu, 11 Nov 1999, Chris Gilbert wrote:

> Circle mud (like most muds) switches the client into line mode (Send a
> line at a time to the mud)  to do tab completion or similair you'd
> need to use char mode (send every char typed to the game).  Then
> pickup on the tab char, and do someting with it, how you go about this
> I don't know, but you also need to handle delete's CTRL-U, W, R, IE
> standard ctrl sequences that the telnet/mud client handles in line
> mode.  You'd actually be writing a telnet server in reality.  It would
> also increase your bandwidth usage as every char typed creates a small
> packet, rather than a line that sends just 1 packet containing the
> line.

This is only one method for implementing auto completion, and it is
heavily reliant upon the TELNET protocol.  Unfortunately, most MUD clients
don't implement much or any of the TELNET protocol -- some implement the
terminal type negotiation (if anyone wants information on that, I can send
some details to the list) so that a MUD may autodetect ISO6429 ("ANSI")
color code support.  This means that anyone using such clients or any
non-TELNET compliant TCP client (and this is the vast majority of Windows
users, who are supplied only with "telnet.exe," which is little more than
a TCP client, and a lot less than a TELNET client) would be left
out.  That's probably not a big difference from the second method, which
is using a specialized client.

The specialized client does not interfere with or change the basic method
of communication between server and client -- that is, the traditional
MUD, TELNET, or flat TCP client will work as always without experiencing
any oddities for your specialized client support.  The client's job is to
receive information from the server and use that information for the
user's benefit.

There are a variety of extensions over the standard view that could be
implemented, but strictly in relation to tab completion, the specialized
client model allows us two separate ways to implement this model without
resorting to "high" bandwidth solutions (such as is the TELNET
char-by-char method).

The first method is have the client cache certain important data (such as
the objects in the room, in the player's inventory, etc.) for the
lookup.  This means that the tab completion can be done without reference
to the server at all: the user types "get sw[tab]", the client inspects
its cache of objects in the room and finds one which suits the given
criterion.  The "problem" with this approach is synchronizing the cache
and the actual mud.  Let's look at an example:

    [ Server SENDS expect-room-description ]
    [ Client PREPARES cache ]

    0x90At the Head of a Winding Road0xFF
    [ Client CACHES "At the Head of a Winding Road" as room-title ]
    0x91    You are standing at the head of a road that snakes through a
    grove of trees that rise to the north.  The road, oddly enough,
    begins here, as to the south, east, and west there is nothing but
    rows of yellow grass to be seen.0xFF
    [ Client CACHES "..." as room-description ]

    0x92Your bodyguard, Hans, is standing here, looking as menancing as
    always.0xFF
    [ Client CACHES mobile Hans as char-in-room ]
    0x92Princess Chesapeak Rowle of Alyandri is resting here.0xFF
    [ Client CACHES player Chesapeak as char-in-room ]
    0x92A small group of the princesses' hand-maidens are here.0xFF
    [ Client CACHES mobilegroup ChesHands as char-in-room ]
    0x92A chipmunk is looking at you nervously.0xFF
    [ Client CACHES mobile chipmunk as char-in-room ]

    [ Server SENDS end-room-description ]
    [ Server SENDS waiting ]
    [ Client SHOWS prompt to User ]
    > look c[tab]
    [ Client INSPECTING room cache ]
    [ Client FINDS ambiguous match: Chesapeak, Chipmunk, ChesHands ]
    [ Client FILLS-IN 'h' ]
    > look ch<beep>[tab]
    [ Client FINDS ambiguous match: Chesapeak, Chipmunk, ChesHands ]
    [ Client NOTICES second ambiguity with same options, display options ]
    [ Client DISPLAYS options ]
    [ User adds 'e' to eliminate "Chipmunk" choice ]
    > look che[tab]
    [ Client FINDS ambiguous match: Chesapeak, ChesHands ]
    [ User adds "sa" to eliminate "ChesHands" choice ]
    > look che<beep>sa[tab]
    [ Client FINDS match: Chesapeak ]
    .
    .
    .

The hexidecimal codes inside of the room description are used to identify
the elements to the specialized client.  Note that this is very similar to
HTML: there is a starting element ("tag") and an ending element that
identify the type of data.  Note that there is one and only one ending
element.  It always closes for the element most recently opened.  That is,
unlike with HTML, you can NEVER close an element before closing any and
all of the elements nested inside of it (i.e., "<b><i>hello</b></i>" is
technically valid HTML).  This sort of mark-up for the client allows the
client to identify elements from the server, cache them locally, and then
not have to recommunicate these things whenever the user requests
them.  The server must, however, keep the client in synch with the latest
updates, and so it should have some sort of system for efficiently
updating the client's data.  The efficiency of bandwidth is already given
us by output buffering, so if twelve objects are dropped into the room,
the server does not communicate a separate packet for each of these twelve
objects, but groups them together in the user's output buffer to be sent a
little later.  Anyway, the only real cost of this method is determining on
each update of the room if there's someone in it who needs to have their
client cache updated.  It's simple enough to do this by keeping a list of
client users on the room so that it may update them as it sees fit.  Since
when the player first enters the room their data is all the same, the
client shouldn't need to worry about User A and User B having different
data that needs to be updated.

The second method puts more work on the server side.  Basically, the
client catches the [TAB] as a special character and then sends the
incomplete input to server for observation.  The server finds a suitable
target (or doesn't) and returns the relevant information to the client.  A
typical communication might look like:

    PLAYER> get sw[tab]
    CLIENT> CSI 0x01 "sw" 0xFF
    SERVER> (Inspecting room contents for takable object.)
    SERVER> (Found a sword, no ambiguity.)
    SERVER> CSI 0x01 "sword" 0xFF
    CLIENT> (Received response, filling in as appropriate.)
    PLAYER> get sword[ENTER]
    CLIENT> (Got complete line, sending as is.)
    SERVER> (Got complete line, interpreting as is.)

Note that unlike the previous method, this one shifts the burden of
computation of what "sw" refers to to the server.  The previous method
does this on the client, with the server's only cost being a short loop
through room->clientUsersList to update them on XXX_to_room() and
XXX_from_room().  Neither method seems difficult to implement from the
server side.  From the client side, the first method would definately be
harder.  I couldn't say which method is better, that depends upon whether
you want to have that sort of thing up to the client (it's basically
harmless, though) and whether you feel reducing server CPU usage justifies
the extra effort to shift the burden to the client (maybe or maybe not in
a text game).

Please, if you're going to be using specialized clients that perform a lot
of the work for you, remember the first rule of specialized clients, as
most recently proven by problems with Diablo, Ultima Online,
distributed.net, and others: Never trust the client.  It is, after all, in
the hands of the enemy -- the user stops becoming a player and starts
becoming a threat when they are given the power to become a threat to the
game's integrity.  As such, certain things should NEVER be for the
client's consideration: damage calculations, etc., are best left on the
server side unless you are willing to risk the player modifying the client
to cheat.  Of course, the best way to catch this sort of cheating is to
occassionally have the server compute the amount of damage the player's
attack should do and then compare that with what the client is telling us
it should do.  It's a pretty good method of detecting something fishy
going on with the client, but it's hardly foolproof and entirely based on
chance.  That is, some players, in a pinch, might be willing to risk
enabling a super-damage attack based on the idea that the server probably
won't be checking up on you on that one specific attack in a thousand
others.  The fact is, there's no real good way to entrust the client with
such calculations, and so it should best not be left to the client at all.

-dak


     +------------------------------------------------------------+
     | Ensure that you have read the CircleMUD Mailing List FAQ:  |
     |  http://qsilver.queensu.ca/~fletchra/Circle/list-faq.html  |
     +------------------------------------------------------------+



This archive was generated by hypermail 2b30 : 12/15/00 PST