GAMESRV(2)                                             GAMESRV(2)

     NAME
          Gamesrv - game server module

     SYNOPSIS
          include "draw.m";
          include "gamesrv.m";
          gamesrv := load Gamesrv Gamesrv->PATH;
          Range, Object, Game, Player: import gamesrv;

          Range: adt {
              start:  int;
              end:    int;
          };

          Object: adt {
              transfer:           fn(o: self ref Object,
                      r: Range, dst: ref Object, i: int);
              setvisibility:      fn(o: self ref Object,
                      visibility: int);
              setattrvisibility:  fn(o: self ref Object,
                      name: string, visibility: int);
              setattr:        fn(o: self ref Object,
                      name: string, val: string, vis: int);
              getattr:        fn(o: self ref Object, name: string): string;
              delete:     fn(o: self ref Object);
              deletechildren: fn(o: self ref Object, r: Range);

              id:     int;
              parentid:   int;
              children:   array of ref Object;
              objtype:    string;
              visibility: int;
              # ...private data

          };

          Game: adt {
              newobject:  fn(game: self ref Game, parent: ref Object,
                      visibility: int, objtype: string): ref Object;
              action: fn(game: self ref Game, cmd: string,
                      objs: list of int, rest: string, whoto: int);
              player: fn(game: self ref Game, id: int): ref Player;

              objects: array of ref Object;
              # ...private data
          };

          Player: adt {
              name:   fn(player: self ref Player): string;
              hangup: fn(player: self ref Player);

     Page 1                       Plan 9            (printed 12/21/24)

     GAMESRV(2)                                             GAMESRV(2)

              obj:    fn(player: self ref Player, id: int): ref Object;

              id:     int;
              # ...private data
          };

          Gamemodule: module {
              clienttype: fn(): string;
              init:       fn(game:   ref Gamesrv->Game,   srvmod: Gamesrv): string;
              command:    fn(player: ref Gamesrv->Player, e: string): string;
              join:       fn(player: ref Gamesrv->Player): string;
              leave:      fn(player: ref Gamesrv->Player);
          };

          rand:   fn(n: int): int;

     DESCRIPTION
          Gamesrv provides a general server interface that allows dis-
          tributed clients to interact in a controlled manner, with
          the interaction mediated by Limbo modules, known as game
          engines, or just engines for short.  Each engine decides on
          the rules of its particular game; the engine interface is
          described at the end of this manual page, under ``Module
          Interface''.

          This manual page describes the interface as presented to an
          engine once it has been loaded by gamesrv. An engine is
          responsible for a particular game, in which one or more
          players participate. Messages sent by players are inter-
          preted by the game engine, which responds by making changes
          to the hierarchical object database held by the game.
          Behind the scenes gamesrv distributes updates to this data-
          base to players of the game as appropriate.

        Objects and visibility
          Objects hold a game's visible state. An object has a unique
          integer id, which is an index into the array game.objects;
          it also holds a set of attribute-value pairs, a type, and
          zero or more child objects. Together, all the objects in the
          game form a hierarchical tree, rooted at the root object (id
          0), which always exists.  Each attribute and each object
          also has an associated visibility set, the set of players
          that sees updates to the attribute or the children of the
          object.  A visibility set is an integer, a bitmask where
          each bit represents one player, hence ~0 is visible to all
          players, and 0 is visible to no-one.  In general, each
          player has a unique identifier id; in an integer i repre-
          senting a set of players, the idth bit represents the pres-
          ence of the player with identifier id. Thus, for a player p,
          (1<<p.id) is the set containing only p, (i&~(1<<p.id))
          excludes p from the set, and (i|(1<<p.id)) includes p in the
          set.

     Page 2                       Plan 9            (printed 12/21/24)

     GAMESRV(2)                                             GAMESRV(2)

          Note that the visibility set of an object does not alter the
          visibility of that object's attributes, but only that of its
          children (and of their children: in general an object is
          visible to a player if the intersection of all its ances-
          tors' visibility sets contains that player).

          Objects can be transferred inside the hierarchy from one
          parent to another. If an object is moved to a parent whose
          visibility conceals it from a player, then it will appear to
          that player to have been deleted; if it is later made visi-
          ble, then it will be recreated for that player.  A game
          engine can almost always ignore this technicality, except
          for one thing: the identifier used by a particular player to
          identify an object is not necessarily the same as that used
          by the game engine. Thus when an engine receives an object
          id in a player's message, it should convert it using the
          player.obj() function.

        Game
          The Game type holds all the objects in a game. It allows the
          creation of new objects, and provides way of communicating
          with players outside the object hierarchy.  All data members
          of a Game should be treated as read-only.

          game.objects
                    This array holds the objects in the game. An
                    object with identifier id is found at
                    game.objects[id].

          game.newobject(parent, visibility, objtype)
                    Newobject creates a new object at the end of
                    parent's children; If parent is nil, the new
                    object is created under the root object.  The new
                    object has visibility visibility, and type
                    objtype. An object's type cannot be changed once
                    it has been created.

          game.action(cmd, objs, rest, whoto)
                    Action sends a message to some players without
                    affecting the object hierarchy. It can be used to
                    send transient events that have no meaning when
                    stored statically (for example, network latency
                    probes).  The message is sent to the set of play-
                    ers given by whoto. Objs is assumed to be a list
                    of object ids, which are converted appropriately
                    for each player receiving the message; the final
                    message is a string built by concatenating cmd,
                    the list of object ids, and rest, separated by
                    spaces.

          game.player(id)
                    Player yields the player corresponding to

     Page 3                       Plan 9            (printed 12/21/24)

     GAMESRV(2)                                             GAMESRV(2)

                    identifier id, or nil if there is none.

        Player
          The Player type represents a player of a game.

          player.id The player's identifier, an integer between 0 and
                    31. This is unique across all current players, but
                    ids of players that have left the game will be
                    reused.

          player.obj(id)
                    Obj converts from a player's external object iden-
                    tifier to the game's local Object that it repre-
                    sents. It returns nil if there is no such object.

          player.hangup()
                    Hangup hangs up a player's connection to the game;
                    no more requests from player will be received by
                    the game engine.

          player.name()
                    Name yields the authenticated name of the player.
                    This is not necessarily unique over the players of
                    a game.

        Object
          The Object type is the basic unit of game engine state.  An
          object's children can be selectively concealed from players;
          it holds a set of (attribute, value) pairs, each of which
          can be concealed likewise.  Where an argument r, of Range
          type is used, it refers to a range of an object's children
          starting at index r.start, and finishing at r.end-1.  All
          the data members of an Object should be treated as read-
          only.

          obj.setattr(name, val, vis)
                    Setattr sets attribute name in obj to val. If the
                    attribute is being created for the first time,
                    then it will be given visibility vis. Name should
                    be non-empty, and should not contain any space
                    characters.  Note that it is not possible for an
                    attribute to refer directly to an object by its
                    identifier; if this facility is needed, another
                    identifying scheme should be used. This also
                    applies to player identifiers, which will change
                    if the game is saved and loaded again (not imple-
                    mented yet).

          obj.getattr(name)
                    Getattr yields the current value of the attribute
                    name in obj. If an attribute is not set, it yields
                    nil.

     Page 4                       Plan 9            (printed 12/21/24)

     GAMESRV(2)                                             GAMESRV(2)

          obj.delete()
                    Delete removes obj from the object hierarchy.

          obj.deletechildren(r)
                    Deletechildren deletes children in range r from
                    obj.

          obj.transfer(r, dst, i)
                    Transfer transfers the children in range r from
                    obj to just before the object at index i in dst.
                    It is permissible for obj and dst to be the same
                    object.

          obj.setvisibility(visibility)
                    Setvisibility allows the set of players given in
                    visibility to see the children of obj, and denies
                    access to all others.  Players are notified of the
                    change.

          obj.setattrvisibility(name, visibility)
                    Setattrvisibility allows the set of players given
                    in visibility to see the value of obj's attribute
                    name, and denies access to all others.  Players
                    are not notified of the change; if there is a need
                    to communicate the fact of an attribute becoming
                    invisible to players, it should be done by using
                    another (visible) attribute to communicate the
                    change.

        Module Interface
          A game engine module, mod, must implement the following
          functions. Where a function returns a string, it is inter-
          preted as an error response to the player responsible for
          the request; an empty string signifies no error.

          mod.clienttype()
               Clienttype should return the type of client required by
               the engine (e.g. cards for the card-game client).  Each
               client type has its own conventions as to the meaning
               of object types and attribute names and values.  This
               function may be called before init().

          mod.init(game, srvmod)
               Init initialises the game engine.  Game is the game
               that the engine is controlling, and srvmod is the
               Gamesrv module holding its associated data.  An error
               response from this function causes the game to be
               aborted.

          mod.join(player)
               Player has made a request to join the game; an error
               response causes the request to be refused, otherwise

     Page 5                       Plan 9            (printed 12/21/24)

     GAMESRV(2)                                             GAMESRV(2)

               the player joins the game.

          mod.leave(player)
               Player has left the game.

          mod.command(player, e)
               Player has sent the command e. The command usually fol-
               lows the simple message conventions used in gamesrv(4),
               i.e. simple space-separated tokens.

     EXAMPLE
          The following is a small, but working example of a game
          engine that acts as a chat server (parsing error checking
          omitted, and white-space compressed to save paper):

          implement Gamemodule;
          include "sys.m";
              sys: Sys;
          include "draw.m";
          include "../gamesrv.m";
              gamesrv: Gamesrv;
              Game, Player: import gamesrv;
          game: ref Game;
          clienttype(): string
          {
              return "chat";
          }
          init(g: ref Game, srvmod: Gamesrv): string
          {
              (sys, game, gamesrv) = (load Sys Sys->PATH, g, srvmod);
              return nil;
          }
          join(nil: ref Player): string
          {
              return nil;
          }
          leave(nil: ref Player)
          {
          }
          command(player: ref Player, cmd: string): string
          {
              game.action("say " + string player.id + " " + cmd, nil, nil, ~0);
              return nil;
          }

     SOURCE
          /appl/cmd/games/gamesrv.b

     SEE ALSO
          gamesrv(4)

     BUGS

     Page 6                       Plan 9            (printed 12/21/24)

     GAMESRV(2)                                             GAMESRV(2)

          The reuse of object ids can lead to problems when objects
          are deleted and recreated on the server before clients
          become aware of the changes.

          This interface is new and will change.

     Page 7                       Plan 9            (printed 12/21/24)