[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

cord example

Networker mentioned on a MUD somewhere that he hadn't seen a
convincing example of a cord message.  I don't know about convincing,
but I'll make up a short example here.


Consider a whiteboard (shared drawing surface) with exactly two
operations: "Draw a line from point A to point B" and "Clear the
whiteboard".  When the user performs one of these actions, a message
is sent from the client to the server indicating the user's activity;
the server then sends a message back to the client to confirm that it
happened.  The server may also send messages to other clients using
the same whiteboard, to inform them that there is a new line or that
the whiteboard has been cleared.

We want to have not just a single whiteboard that everyone on the MUD
uses, but as many whiteboards as people have a use for.  Drawing on
one whiteboard has no effect on any others; it is a way to communicate
only with the people who are using the same whiteboard.  This need for
multiple channels of the same type is generally what leads to the use
of a cord.


For using whiteboards, we define a simple MCP package; call it
"whiteboard" for now, although really we'd have to jump through hoops
to ensure no namespace collision.  The whiteboard package defines no
messages; it defines a single cord type.  For the sake of confusion,
let's call the cord "whiteboard" as well.

The whiteboard cord defines four cord messages:

  C->S draw-c x1: NUM y1: NUM x2: NUM x3: NUM
  S->C draw-s x1: NUM y1: NUM x2: NUM x3: NUM
  C->S clear-c
  S->C clear-s

(Really, the C->S and S->C messages could have the same names, but for
the sake of discussion it might be easier to make them distinct.)

Note that these are CORD messages.  This means that each of them is
defined implicitly to take place within the context of a particular
cord, which is identified with an _id argument, and which corresponds
to the particular whiteboard being used.  The message names above
(draw-c, draw-s, clear-c, clear-s) are cord message names, supplied as
the _message argument to an MCP #$#cord message.


So, in a particular session, we might have the following series of

  C->S #$#mcp min-version: 2.0 max-version: 2.0
  S->C #$#mcp min-version: 2.0 max-version: 2.0
  C->S #$#mcp-negotiate package: whiteboard min-version: 0.0 max-version: 0.0
  S->C #$#mcp-negotiate package: whiteboard min-version: 0.0 max-version: 0.0

Yay, we can do whiteboards.  Now, the user types "work with Joe's
whiteboard", and the server sends this message to the client:

  S->C #$#cord-open _type: whiteboard _id: R777

The client receives this, and opens up a window for the user to draw
on.  For the sake of the example, we'll assume the whiteboard was
blank when opened.  The user draws a few lines, the client sends them
to the server, and the server reflects them back:

  C->S #$#cord _id: R777 _message: draw-c X1: 5 Y1: 15 X2: 25 Y2: 35
  C->S #$#cord X1: 10 Y1: 20 X2: 30 X3: 40 _message: draw-c _id: R777
  S->C #$#cord _id: R777 _message: draw-s X1: 5 Y1: 15 X2: 25 Y2: 35
  C->S #$#cord _message: draw-c X1: 7 X2: 21 Y2: 28 Y1: 14 _id: R777
  S->C #$#cord X1: 10 Y1: 20 X2: 30 X3: 40 _message: draw-s _id: R777
  S->C #$#cord _message: draw-s X1: 7 X2: 21 Y2: 28 Y1: 14 _id: R777

Note that the arguments can all go every which way all over the place.
The _id argument is used to indicate that we're talking about the
whiteboard opened with cord id R777 a few lines back; the user could
have a different whiteboard up with a different cord id (say, R666) and
the ids would prevent confusion.

(Also, the server is sending back one draw-s message for each draw-c
message from the client.  This isn't anything inherent to cords; in
fact, if I were really designing a whiteboard cord, I wouldn't do it
this way.  But the simplest way to implement shared drawing is to make
a user's operations simply not show up until they're acknowledged by
the server.  Trust me.)

Anyway, at this point, another user starts working with the whiteboard
(I won't show all the messages here), presses the "Clear" button,
which causes the client to send a message to the server, which in
response sends this message to our client:

  S->C #$#cord _message: clear-s _id: R777

Frustrated at this wanton destruction of art, our user clicks the
close button, and the client sends this message:

  C->S #$#cord-closed _id: R777

The client at this point forgets about cord R777.  However, while this
message is wending its way across the net to the server, the other
user has already started drawing again, and the server sends this
message to the client:

  S->C #$#cord _id: R777 _message: draw-s X1: 0 Y1: 0 X2: 5000000 Y2: 99999

The client, having eliminated R777 from its cord list, simply ignores
this message.


Another way to implement multiple whiteboards would be to use regular
MCP messages and simply use a hand-rolled ID argument (call it 'wb')
to specify which whiteboard is being used:

  #$#whiteboard-open wb: 777
  #$#whiteboard-draw-c wb: 777 X1: 1 Y1: 1 X2: 3 Y2: 5
  #$#whiteboard-draw-s wb: 777 X1: 1 Y1: 1 X2: 3 Y2: 5
  #$#whiteboard-clear-c wb: 777
  #$#whiteboard-clear-s wb: 777
  #$#whiteboard-close wb: 777

The reason we like cords (and I think I'm speaking for myself, Dave
and Ken, here; as far as I know, no one else has used them) is this:
It provides us with a uniform way of doing multiple objects of the
same type, for many different types.  Without cords, we would have to
spec an open message, a close message, and an ID argument (wb) for
whiteboards; we'd have to spec open, close, and ID for text editing;
for shared CD players; for GUI toolkits; and so on and so forth.  With
cords, we've done that part once, and in designing and implementing
new functionality we can concentrate on the parts that are actually