summaryrefslogtreecommitdiff
path: root/appl/spree/man/gamesrv.man2
blob: fd9105197fff1732b4102f5f4683288943ff1c93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
.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.