posted by Jeremy Friesner on Mon 12th Aug 2002 20:52 UTC

"Overview of the FRC server"
In the last few pages, we've seen how the various components of the server-side architecture are implemented, and how they relate to one another. "That's nice", you are probably saying, "But how does it actually work? What's the big picture?" So let's take a few minutes now to go over that. We'll start with the easy bit: the on-line chat.

Like any modern multiplayer networked game, FoxRabbitCarrot has a text-chat facility which players can use to talk with (or threaten) each other, and spectators can use to kibitz. To implement this, we don't need any custom server-side logic; the basic MUSCLE implementation does just fine. Specifically, whenever any user types a text chat string into his FoxRabbitCarrot client's chat box, the client sends a FRC_COMMAND_CHAT_TEXT Message to the server:


   MessageRef chatMsg = GetMessageFromPool(FRC_COMMAND_CHAT_TEXT);
   if (chatMsg())
   {
      chatMsg()->AddString(PR_NAME_KEYS, "frc/uname");  // send only to other FRC clients
      chatMsg()->AddString("text", chatText);
      SendMessageToServer(chatMsg);
   }

In the above code (excerpted from the client's FRCWindow class) we simply allocate a Message from the Message pool, add the user's chat text to it, and send it to the server. The only non-obvious thing about it is the PR_NAME_KEYS field that is added to the Message. The PR_NAME_KEYS field is there to tell the server that only clients who have a database node whose node path matches wildcard pattern "/*/*/frc/uname" should have this Message forwarded to them. This is done to ensure that the Message goes only to other FoxRabbitCarrot clients, and not to any unrelated clients who just happen to be logged in to the same server (not that there will probably be any, but just in case).

Once this Message is sent to the server, it is received by the appropriate FRCPlayerSession's MessageReceivedFromGateway() method, which promptly passes it up the superclass's MessageReceivedFromGateway() method, which then uses the standard MUSCLE pattern-matching procedure to distribute it to the other FRCPlayerSessions. These sessions then send the Message to their clients, who receive it and display the chat text on screen. And thus the chat text is spread around to all participants.

In addition to the chat text itself, it is traditional for a multiplayer game to show a list of the "handles" (or usernames) of all the connected participants, so that people can easily see who they are chatting with. Again, the vanilla MUSCLE logic provides for this. To accomplish this, we use the MUSCLE database node subscription mechanism. Each FoxRabbitCarrot uploads a single database node to the server, containing the client's username:


void FRCWindow :: UploadUserName()
{
   MessageRef uploadMsg = GetMessageFromPool(PR_COMMAND_SETDATA);
   MessageRef nameMsg   = GetMessageFromPool();
   if ((uploadMsg())&&(nameMsg()))
   {
      nameMsg()->AddString("uname", (const char *) _userName->text());
      uploadMsg()->AddMessage("frc/uname", nameMsg);
      SendMessageToServer(uploadMsg);
   }
}     

In addition to doing that, each client tells the server it wants to subscribe to the node path "/*/*/frc/uname". That means that whenever a database node whose node path matches that pattern appears on the server, the client will be notified. The client will also be notified if a matching node disappears, or if its Message payload is changed. In this way, each client makes sure that is always notified of any changes to the set of logged in users, and can immediately update its users list to reflect the changes. Here is the code where the FoxRabbitCarrot client generates its database subscription requests and sends them to the server:


   MessageRef subscribeRef = GetMessageFromPool(PR_COMMAND_SETPARAMETERS);
   if (subscribeRef())
   {
      // We want to know what other users are logged in (for chatting, etc)
      subscribeRef()->AddBool("SUBSCRIBE:frc/uname", false);

      // We also want to know the state of the game board
      subscribeRef()->AddBool("SUBSCRIBE:/*/0/frc/board", false);

      // And the current turn number
      subscribeRef()->AddBool("SUBSCRIBE:/*/0/frc/turn", false);

      // And the player statistics (color, score, etc)
      subscribeRef()->AddBool("SUBSCRIBE:/*/0/frc/pieces/*", false);

      // And which pieces are on the board
      subscribeRef()->AddBool("SUBSCRIBE:/*/0/frc/pieces/*/*", false);

      SendMessageToServer(subscribeRef);
   }                        

As you can see, the first AddBool() line specifies the subscription to the user list... but there are four other subscriptions being added as well! Well, as you probably guessed, these other subscriptions tell the server to notify the client whenever the nodes representing the current state of the game change. The FRCGameStateSession places one node into the database for each piece on the board, as well as a node for the board itself. Underneath the "pieces" node, the FRCGameStateSession places nodes whose names represent which sessions are currently playing -- and the children of those nodes represent the pieces themselves. This way, all the other clients can keep informed of the state of the board simply by subscribing to "/*/0/frc/pieces/*/*", And the owner of any given piece can be easily determined by looking at the parent of the piece's node. So the state of the MUSCLE node tree during a FoxRabbitCarrot game with two players and one spectator might look like this:

So by merely adding the server subscription requests with the appropriate wildcarded path patterns, the clients tell the server which parts of the database they need to have "live updates" of. In this way, they can get instant notification of important changes to the database, without wasting bandwidth getting notifications about parts of the database that they are not interested in. In the next section, we will look more at the FoxRabbitCarrot client program, and how it interacts with the server.

Table of contents
  1. "Foxes, Rabbits, and Carrots"
  2. "The Game and How to Play it"
  3. "A Brief Review of the MUSCLE Networking Layer"
  4. "muscled, The basic MUSCLE server"
  5. "Customizing the MUSCLE server"
  6. "How to Set up the Custom Server Logic"
  7. "The FRCPlayerSession Class"
  8. "The FRCGameStateSession class"
  9. "Overview of the FRC server"
  10. "The FoxRabbitCarrot Client Program"
  11. "How the Client Handles Database Update Messages"
  12. "MUSCLE gaming Performance Issues"
e p (0)    8 Comment(s)

Technology White Papers

See More