.TH GAMESRV 2 .SH NAME Gamesrv \- game server module .SH SYNOPSIS .EX .ps -1 .vs -1 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); 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; .ps +1 .vs +1 .EE .SH DESCRIPTION .I Gamesrv provides a general server interface that allows distributed clients to interact in a controlled manner, with the interaction mediated by Limbo modules, known as .IR "game engines" , or just .I 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''. .PP This manual page describes the interface as presented to an engine once it has been loaded by .IR gamesrv . An engine is responsible for a particular .IR game , in which one or more .I players participate. Messages sent by players are interpreted by the game engine, which responds by making changes to the hierarchical .I object database held by the game. Behind the scenes .I gamesrv distributes updates to this database to players of the game as appropriate. .SS "Objects and visibility" Objects hold a game's visible state. An object has a unique integer .IR id , which is an index into the array .IB game .objects\fR;\fP 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 .IR "root object" (id 0), which always exists. Each attribute and each object also has an associated .IR "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 .B ~0 is visible to all players, and .B 0 is visible to no-one. In general, each player has a unique identifier .IR id ; in an integer .I i representing a set of players, the .IR id th bit represents the presence of the player with identifier .IR id . Thus, for a player .IR p , .BI "(1<<" p ".id)" is the set containing only .IR p , .BI "(" i "&~(1<<" p ".id))" excludes .I p from the set, and .BI "(" i "|(1<<" p ".id))" includes .I p in the set. .PP 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 ancestors' visibility sets contains that player). .PP 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 visible, 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 .IB player .obj() function. .SS \fBGame\fP The .B 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 .B Game should be treated as read-only. .TP 10 .IB game .objects This array holds the objects in the game. An object with identifier .I id is found at .IB game .objects[ id ]\fR.\fP .TP .IB game .newobject(\fIparent\fP,\ \fIvisibility\fP,\ \fIobjtype\fP) .B Newobject creates a new object at the end of .IR parent 's children; If .I parent is nil, the new object is created under the root object. The new object has visibility .IR visibility , and type .IR objtype . An object's type cannot be changed once it has been created. .TP .IB game .action(\fIcmd\fP,\ \fIobjs\fP,\ \fIrest\fP,\ \fIwhoto\fP) .B 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 players given by .IR whoto . .I 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 .IR cmd , the list of object ids, and .IR rest , separated by spaces. .TP .IB game .player(\fIid\fP) .B Player yields the player corresponding to identifier .IR id , or .B nil if there is none. .SS Player The .B Player type represents a player of a game. .TP 10 .IB 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. .TP .IB player .obj(\fIid\fP) .B Obj converts from a player's external object identifier to the game's local .B Object that it represents. It returns .B nil if there is no such object. .TP .IB player .hangup() .B Hangup hangs up a player's connection to the game; no more requests from .I player will be received by the game engine. .TP .IB player .name() .B Name yields the authenticated name of the player. This is not necessarily unique over the players of a game. .SS \fBObject\fP The .B 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 .RI ( attribute ,\ value ) pairs, each of which can be concealed likewise. Where an argument .IR r , of .B Range type is used, it refers to a range of an object's children starting at index .IB r .start\fR,\fP and finishing at .IB r .end-1\fR.\fP All the data members of an .B Object should be treated as read-only. .TP 10 .IB obj .setattr(\fIname\fP,\ \fIval\fP,\ \fIvis\fP) .B Setattr sets attribute .I name in .I obj to .IR val. If the attribute is being created for the first time, then it will be given visibility .IR vis . .I 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 implemented yet). .TP .IB obj .getattr(\fIname\fP) .B Getattr yields the current value of the attribute .I name in .IR obj . If an attribute is not set, it yields .BR nil . .TP .IB obj .delete() .B Delete removes .I obj from the object hierarchy. .TP .IB obj .deletechildren(\fIr\fP) .B Deletechildren deletes children in range .I r from .IR obj . .TP .IB obj .transfer(\fIr\fP,\ \fIdst\fP,\ \fIi\fP) .B Transfer transfers the children in range .I r from .I obj to just before the object at index .I i in .IR dst . It is permissible for .I obj and .I dst to be the same object. .TP .IB obj .setvisibility(\fIvisibility\fP) .B Setvisibility allows the set of players given in .I visibility to see the children of .IR obj , and denies access to all others. Players are notified of the change. .TP .IB obj .setattrvisibility(\fIname\fP,\ \fIvisibility\fP) .B Setattrvisibility allows the set of players given in .I visibility to see the value of .IR obj 's attribute .IR 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. .SS "Module Interface" A game engine module, .IR mod , must implement the following functions. Where a function returns a string, it is interpreted as an error response to the player responsible for the request; an empty string signifies no error. .TP .IB mod .clienttype() .B Clienttype should return the type of client required by the engine (e.g. .B 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 .BR init() . .TP .IB mod .init(\fIgame\fP,\ \fIsrvmod\fP) .B Init initialises the game engine. .I Game is the game that the engine is controlling, and .I srvmod is the .B Gamesrv module holding its associated data. An error response from this function causes the game to be aborted. .TP .IB mod .join(\fIplayer\fP) .I Player has made a request to join the game; an error response causes the request to be refused, otherwise the player joins the game. .TP .IB mod .leave(\fIplayer\fP) .I Player has left the game. .TP .IB mod .command(\fIplayer\fP,\ \fIe\fP) .I Player has sent the command .IR e . The command usually follows the simple message conventions used in .IR gamesrv (4), i.e. simple space-separated tokens. .SH 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): .PP .EX .ps -1 .vs -1 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; } .ps +1 .vs +1 .EE .SH SOURCE .B /appl/cmd/games/gamesrv.b .SH "SEE ALSO" .IR gamesrv (4) .SH BUGS 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. .PP This interface is new and will change.