tkMOO-light .. Other stuff

XMCP/1.1 server software for MOO

Overview

XMCP/1.1 is a simple protocol that allows structured messages to be sent from the server to the client. XMCP/1.1 is based on JHCore's MCP/1.0 plus a few tweaks to make it more robust for large messages. The client understands these messages and uses them to perform complex operations like manipulating the objects on a desktop, drawing on a shared whiteboard, or opening a local editing session.

Installing XMCP/1.1 on a MOO

You need to install the driver and feature object in order to allow the available XMCP/1.1 applications to work. Each MOO requires only one pair of driver and feature objects.

XMCP/1.1 is implemented on the MOO server by a suite of cooperating objects. The following '@dump's can now be used without modification on JHCore and LambdaCore MOOs.

The Driver and Feature are linked to each other by special properties on the objects. The following MOO commands will link newly installed objects, when object's real object identifiers are substituted for #xdr and #xfo.
    ;setadd(#xdr.trusted, #xfo)
    ;#xfo.driver = #xdr
A step by step installation example is also available, which will show you what steps to take to take to install XMCP/1.1 for the first time on a new MOO.

About XMCP/1.1 Messages

Every XMCP/1.1 message that the MOO sends to your client contains an 'authentication key', usually a text-string or random number. The key is generated by the client and sent to the MOO when you connect. When an XMCP/1.1 message arrives from the MOO its key is compared with the client's own key, and if both keys are the same then the client will process the message. If the MOO's key does not match the client's key then the message is ignored and the client produces an error report to tell you what has happened.

The following error report means that the MOO has tried to send your client a message 'whiteboard-show', but that the key it provided did not match the key in the client:

    XMCP/1.1 message 'whiteboard-show' not authenticated by key '1'.
This usually happens when an XMCP/1.1 application (like the whiteboard) is started before the client has been able to set an authentication key.

Setting an XMCP/1.1 Authentication Key

When you connect to a MOO you need to set the value of your authentication key before you can use any XMCP/1.1 applications.

To set the key you should use the '@xmcp_challenge' command provided on the XMCP/1.1 Feature Object (#xfo). The feature object will ask your client to send an authentication key to the MOO which will be acknowledged by the message 'XMCP/1.1 Authentication set.'. Once the authentication key has been set you can proceed to use any of the MOO's XMCP/1.1 applications.

The following commands ADD the feature object to your list of $features, SET the authentication key and START a whiteboard application.

    @addfeature #xfo
    @xmcp_challenge
    [wait for 'XMCP/1.1 Authentication set.']
    watch board

Shortcuts

You can use tkMOO-light's
worlds.tkm file to automate the sequence of commands that you need to type when you connect to the MOO. The following entry sets up a list of commands that get sent to SomewhereMOO when the user 'Hiroshi' connects.
    World: SomewhereMOO
    Host: some.host.name
    Port: 1234
    Login: Hiroshi
    Password: <password>
    ConnectScript: connect %u %p
    ConnectScript: @xmcp_challenge
    XMCP11_AfterAuth: watch board
The directive 'XMCP11_AfterAuth' is sent to the MOO only after the MOO has accepted the client's authentication key.

XMCP/1.1 Authentication Handshake

When the tkMOO-light client connects to the server it initiates a simple handshake to set up a server-side copy of the client's authentication key. The client is assumed to have been configured by the user to send the '@xmcp_challenge' message after connection. However an alternative implementation would be to allow the user's :confunc verb to issue the '$#$xmcp version: 1.1' message when the user connected to the server.
    CLIENT				    SERVER
    ------				    ------
    @xmcp_challenge ---------------------->

	       <--------------------------- $#$xmcp version: 1.1

    @xmcp_authentication_key <key> ------->

	       <--------------------------- $#$<message> <key> [attributes]
JHCore's native MCP protocol performs a similar handshake, but the handshake is initiated by the server when it sends '#$#mcp version: 1.0' on connection.

XMCP/1.1 Mesage Format

XMCP/1.1 messages are in two forms, single-line messages and multi-line messages. A single-line message has the form:
    $#$<message> <auth> <key1>: <val1> <key2>: <val2> ...
A multi-line message has the form:
    $#$<message>* <auth> tag: <tag> <key1>: <val1> <key2>: <val2> ...
    $#$data tag: <tag> data: <1st line of data>
    $#$data tag: <tag> data: <2nd line of data>
    ...
    $#$data tag: <tag> data: <nth line of data>
    $#$END tag: <tag>
Note that the initial out-of-band header is the string '$#$' and not the MOO builtin Out-of-band prefix '#$#'.

<auth> is a client-generated authentication key generted at the start of the session and used for the duration of the session.

tag: is a reserved keyword, <tag> is a server-generated token, a different one generated for each multi-line message. The token plays no part in message authentication and so a simple routine that increments an integer variable can be used to generate successive unique tags.

<keyn> is a keyword string not containing '*', '\', '"', ' ', ':'.

An example of a single-line message, from the desktop. Remove the desktop relating to MOO id #71 from the client:

    $#$desktop-remove 25165847 object: #71
An example of a multi-line message, from the desktop. Put this room on the desktop. This causes data relating to the room's contents to be uploaded, the client then uses this information to construct a desktop with icons representing each if the objects in the room.
    $#$desktop* 25165847 object: #71 type: room name: first room location: #-1
parent: #3 tag: 113094869
    $#$data tag: 113094869 data: <line of data for 1st object>
    $#$data tag: 113094869 data: <line of data for 2st object>
    $#$data tag: 113094869 data: <line of data for 3rd object>
    $#$data tag: 113094869 data: <line of data for 4th object>
    $#$END tag: 113094869

XMCP/1.1 and MOO Permissions

The XMCP/1.1 Permission System is intended to protect the user in several ways. The permissions mechanism employed at each site may vary, these details relate to XMCP/1.1's operation on normal MOO databases and have yet to be fully tested in a production environment (should be interesting...) , the approach taken may differ in future versions of the software. The XMCP/1.1 Driver's :client_notify verb can be invoked under the following circumstances:.

An XMCP/1.1 Programming Example

The client has an XMCP handler built in and right now you can add to the range of things it understand by adding new plugins with extra handling procedures. UNIX boxes plugins are TCL scripts (*.tcl) that sit in your ~/.tkMOO-lite/plugins/ directory. The client sources these when it starts up.

There are two basic forms of XMCP/1.1 message. A single line message and a multiline message. The multiline messages are just single line messages followed by a variable number of data lines and a final 'END' message.

Let's try an example. The following MOO code fragment sends 2 XMCP/1.1 messages to your client. 'driver' is the XMCP/1.1 Driver Object, available from the client's supporting website.

    @program me:@xmcpexample none none none
    "some example XMCP/1.1 messages";
    if (!this.driver:xmcp_aware(player))
        player:tell("Sorry you need to be using XMCP/1.1. to use this verb.");
        return;
    endif
    message = "xmcp-example-one";
    kv = {};
    kv = {@kv, {"param1", "one"}};
    kv = {@kv, {"param2", "parameter 2"}};
    this.driver:client_notify(player, message, kv);
    message = "xmcp-example-two";
    lines = {};
    lines = {@lines, "line one"};
    lines = {@lines, "line two"};
    lines = {@lines, "line three"};
    this.driver:client_notify(player, message, kv, lines);
    .
The second call to :client_notify sends the multiline message, the difference is that a 4th optional parameter is passed, 'line'. this causes a '*' to be appended to the message name, the client looks for '*' and understand it to mean 'this is a multiline message'.

Multiline messages also have a 'tag' paramater which identifies the succeeding data lines and END line as being part of this message's opening line.

When you run this verb the server will send the following lines of text. The authentication key (that big number) and the 'tag:' parameter's value will be different for you.

    $#$xmcp-example-one 134217733 param1: one param2: parameter 2 
    $#$xmcp-example-two* 134217733 param1: one param2: parameter 2 tag: 329 
    $#$data tag: 329 data: line one 
    $#$data tag: 329 data: line two 
    $#$data tag: 329 data: line three 
    $#$END tag: 329 
If you call this verb before your plugin is in place then you'll see the first message and the beginning of the second message. All unrecognised XMCP messages are punted to the main window. The data and END lines are silently eaten by the client.

Ok, now we need a plugin to catch this information. More information on plugins is available.

    #
    #       tkMOO
    #       ~/.tkMOO-lite/plugins/xmcp_example.tcl
    #
    #       some examples of using XMCP/1.1
    
    # Example 1.  a simple one-line message it just passes parameters.
    # Extract the values of each parameter and print them out on the main
    # window.
    #
    # make a procedure called 'xmcp11.do_'
    proc xmcp11.do_xmcp-example-one {} {
    
        # it's possible to spoof the opening $#$ in MOO so we check the
        # authentication key, since only a trusted task can extract the key
        # from the driver when it builds the XMCP message.
        if { [xmcp11.authenticated silent] == 0 } {
            window.displayCR "*** Unauthenticated message xmcp-example-one"
            return
        }
    
        # get a token from the client which identifies this message
        set which [request.current]
    
        set param1 [request.get $which param1]
        set param2 [request.get $which param2]
    
        window.displayCR "Processing xmcp-example-one"
        window.displayCR "  param1=$param1"
        window.displayCR "  param2=$param2"
    }
    
    # make a procedure called 'xmcp11.do_*', the '*' denotes
    # a multiline message
    proc xmcp11.do_xmcp-example-two* {} {
        if { [xmcp11.authenticated silent] == 0 } {
            window.displayCR "*** Unauthenticated message xmcp-example-two"
            return
        }
    
        # Set a special variable, the name of the procedure to be called
        # when all the lines of data have been read in.  this procedure is
        # called when the END message for this upload arrives.
    
        # we can call this anything we want, but for sake of documentation
        # I've called it 'xmcp-example-two*'.  When it gets called the client
        # will call 'xmcp11.do_callback_xmcp-example-two*'
        request.set current xmcp11_multiline_procedure "xmcp-example-two*"
    }
    
    # make a callback procedure with the same name as the value you put in
    # the 'xmcp11_multiline_procedure' line above.
    proc xmcp11.do_callback_xmcp-example-two* {} {
        set which [request.current]
        set param1 [request.get $which param1]
        set param2 [request.get $which param2]
    
        # the data lines are held in a special variable '_lines'
        set lines [request.get $which _lines]
        window.displayCR "Processing xmcp-example-two"
        window.displayCR "  param1=$param1"
        window.displayCR "  param2=$param2"
        window.displayCR "  lines:"
        foreach line $lines {
            window.displayCR "    $line"
        }
        window.displayCR "  ---"
    }
    
    # Just be sure the TCL actually compiled!
    window.displayCR "Loaded Plugin xmcp_example.tcl"
If all this works then you should see the following printed on your client window, when you type '@xmcpexample':
    Processing xmcp-example-one
      param1=one
      param2=parameter 2
    Processing xmcp-example-two
      param1=one
      param2=parameter 2
      lines:
        line one
        line two
        line three
      ---
For more clues try setting the 'Controls->XMCP/1.1 [x] log xmcp/1.1' check-box. You should see the incoming messages logged to your screen.