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 11/1/25)
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 11/1/25)
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 11/1/25)
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 11/1/25)
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 11/1/25)
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 11/1/25)
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 11/1/25)