3
"<UPDATE TYPE='player' ACTION='delete' ROOM='40' TOROOM='-1'>"
4
"<PLAYER ID='kostaya'/>"
7
"<UPDATE TYPE='table' ACTION='leave' ROOM='40'>"
8
"<TABLE ID='1' SEATS='2'>"
9
"<SEAT NUM='0' TYPE='player'>kostaya</SEAT>"
13
"<UPDATE TYPE='table' ACTION='status' ROOM='40'>"
14
"<TABLE ID='1' STATUS='3' SEATS='2'/>"
17
"<UPDATE TYPE='table' ACTION='delete' ROOM='40'>"
18
"<TABLE ID='1' STATUS='-1' SEATS='2'/>"
21
"<GAME ID=\'24\' NAME=\'TicTacToe\' VERSION=\'0.0.9\'>"
22
"<PROTOCOL ENGINE=\'TicTacToe\' VERSION=\'4\'/>"
23
"<ALLOW PLAYERS=\'2\' BOTS=\'1\' SPECTATORS=\'true\' PEERS=\'false\'/>"
24
"<BOT NAME=\'Alfred\' CLASS=\'easy\'/>"
25
"<BOT NAME=\'Tarantula\' CLASS=\'hard\'/>"
26
"<ABOUT AUTHOR=\'Brent Hendricks\' URL=\'http://www.ggzgamingzone.org/games/tictactoe/\'/>"
27
"<DESC>Simple GGZ game module for playing Tic-Tac-Toe</DESC>"
36
def getAttribute(self, attributes, name, default = None):
38
return attributes[name]
42
def startElement(self, name, attributes):
43
if self.parser is not None:
44
self.parser.startElement(name, attributes)
47
method = getattr(self, 'start_%s' % name.lower())
48
except AttributeError:
49
print 'Unknown start element: %s' % name
53
def characters(self, data):
54
if self.parser is not None:
55
self.parser.characters(data)
57
self.handle_data(data)
59
def endElement(self, name):
60
if self.parser is not None:
61
self.parser.endElement(name)
64
method = getattr(self, 'end_%s' % name.lower())
65
except AttributeError:
66
print 'Unknown end element: %s' % name
70
def push(self, parser, attributes):
71
assert(self.parser is None)
72
parser.attributes = attributes
73
parser.decoder = self.decoder
78
assert(self.parent is not None)
79
parser = self.parent.parser
80
self.parent.parser = None
81
self.parent.childFinished(parser)
83
def handle_data(self, data):
86
def childFinished(self, parser):
89
class DescriptionParser(GGZParser):
91
def handle_data(self, data):
92
self.parent.description = data
97
class GameProtocolParser(GGZParser):
99
def end_protocol(self):
100
self.parent.protocol = self
101
self.engine = self.attributes['ENGINE']
102
self.version = self.attributes['VERSION']
105
class GameAllowParser(GGZParser):
108
self.parent.allow = self
109
self.numPlayers = self.attributes['PLAYERS']
112
class GameBotParser(GGZParser):
115
self.parent.bots.append(self)
116
self.name = self.attributes['NAME']
117
self.difficulty = self.attributes['CLASS']
120
class GameAboutParser(GGZParser):
123
self.parent.about = self
124
self.author = self.attributes['AUTHOR']
125
self.url = self.attributes['URL']
128
class GameParser(GGZParser):
133
def start_desc(self, attributes):
134
self.push(DescriptionParser(), attributes)
136
def start_protocol(self, attributes):
137
self.push(GameProtocolParser(), attributes)
139
def start_allow(self, attributes):
140
self.push(GameAllowParser(), attributes)
142
def start_bot(self, attributes):
143
self.push(GameBotParser(), attributes)
145
def start_about(self, attributes):
146
self.push(GameAboutParser(), attributes)
149
self.gameId = self.attributes['ID']
150
self.name = self.attributes['NAME']
151
self.version = self.attributes['VERSION']
155
return 'GGZ Game id=%s protocol=%s (%s) description=%s' % (self.gameId, repr(self.protocol.engine), self.protocol.version, repr(self.description))
157
class RoomParser(GGZParser):
159
def start_desc(self, attributes):
160
self.push(DescriptionParser(), attributes)
166
return 'GGZ Room id=%s game=%s description=%s' % (self.roomId, self.game, repr(self.description))
168
class PlayerParser(GGZParser):
170
def end_player(self):
174
return 'GGZ Player id=%s type=%s table=%s perms=%s lag=%s' % (self.id, self.type, self.table, self.perms, self.lag)
176
class TableSeatParser(GGZParser):
181
def handle_data(self, data):
187
class TableParser(GGZParser):
191
self.description = ''
193
def start_desc(self, attributes):
194
self.push(DescriptionParser(), attributes)
196
def start_seat(self, attributes):
197
self.push(TableSeatParser(), attributes)
199
def childFinished(self, parser):
200
if isinstance(parser, TableSeatParser):
201
self.seats.append(parser)
206
class GameListParser(GGZParser):
211
def start_game(self, attributes):
212
self.push(GameParser(), attributes)
214
def childFinished(self, parser):
215
self.games.append(parser)
219
self.decoder.feedback.gameAdded(g.gameId, g.name, g.version, g.about.author, g.about.url, g.allow.numPlayers,
220
g.protocol.engine, g.protocol.version)
223
class TableListParser(GGZParser):
228
def start_table(self, attributes):
229
self.push(TableParser(), attributes)
231
def childFinished(self, parser):
232
self.tables.append(parser)
235
for t in self.tables:
236
room = self.attributes['ROOM']
237
tableId = t.attributes['ID']
238
gameId = t.attributes['GAME']
239
status = t.attributes['STATUS']
240
nSeats = int(t.attributes['SEATS'])
241
self.decoder.feedback.tableAdded(room, tableId, gameId, status, nSeats, t.description)
243
self.decoder.feedback.seatChanged(room, tableId, seat.attributes['NUM'], seat.attributes['TYPE'], seat.label)
246
class PlayerListParser(GGZParser):
251
def start_player(self, attributes):
252
self.push(PlayerParser(), attributes)
254
def childFinished(self, playerParser):
255
playerParser.name = playerParser.attributes['ID']
256
playerParser.type = playerParser.attributes['TYPE']
257
playerParser.table = playerParser.attributes['TABLE']
259
playerParser.perms = playerParser.attributes['PERMS']
261
playerParser.perms = ''
262
playerParser.lag = playerParser.attributes['LAG']
263
self.players.append(playerParser)
266
for p in self.players:
267
self.decoder.feedback.playerAdded(p.name, p.type, p.table, p.perms, p.lag, self.attributes['ROOM'], '-1')
270
class RoomListParser(GGZParser):
275
def start_room(self, attributes):
276
self.push(RoomParser(), attributes)
278
def childFinished(self, parser):
279
parser.roomId = parser.attributes['ID']
280
parser.name = parser.attributes['NAME']
281
parser.game = parser.attributes['GAME']
282
parser.nPlayers = int(parser.attributes['PLAYERS'])
283
self.rooms.append(parser)
287
self.decoder.feedback.roomAdded(r.roomId, r.game, r.name, r.description, r.nPlayers)
290
class ServerOptionsParser(GGZParser):
292
def end_options(self):
295
class ServerParser(GGZParser):
297
def start_options(self, attributes):
298
self.push(ServerOptionsParser(), attributes)
300
def end_server(self):
303
class MOTDParser(GGZParser):
308
def handle_data(self, data):
312
print 'MOTD: %s' % repr(self.motd)
315
class RoomUpdateParser(GGZParser):
320
def start_room(self, attributes):
321
self.push(RoomParser(), attributes)
323
def childFinished(self, parser):
324
action = self.attributes['ACTION'].lower()
325
if action == 'players':
326
roomId = parser.attributes['ID']
327
nPlayers = int(parser.attributes['PLAYERS'])
328
self.decoder.feedback.roomPlayersUpdate(roomId, nPlayers)
330
print 'Unknown player update action %s' % action
332
def end_update(self):
335
class PlayerUpdateParser(GGZParser):
337
def start_player(self, attributes):
338
self.push(PlayerParser(), attributes)
340
def childFinished(self, parser):
341
action = self.attributes['ACTION'].lower()
343
name = parser.attributes['ID']
344
playerType = parser.attributes['TYPE']
345
table = parser.attributes['TABLE']
347
perms = parser.attributes['PERMS']
350
lag = parser.attributes['LAG']
351
room = self.attributes['ROOM']
352
fromRoom = self.attributes['FROMROOM']
353
self.decoder.feedback.playerAdded(name, playerType, table, perms, lag, room, fromRoom)
354
elif action == 'lag':
355
playerId = parser.attributes['ID']
356
lag = parser.attributes['LAG']
357
print 'Player %s lag changed to %s' % (playerId, lag)
358
elif action == 'delete':
359
playerId = parser.attributes['ID']
360
room = self.attributes['ROOM']
361
toRoom = self.attributes['TOROOM']
362
self.decoder.feedback.playerRemoved(playerId, room, toRoom)
364
print 'Unknown player update action %s' % action
366
def end_update(self):
369
class TableUpdateParser(GGZParser):
374
def start_table(self, attributes):
375
self.push(TableParser(), attributes)
377
def childFinished(self, parser):
380
def end_update(self):
381
room = self.attributes['ROOM']
382
action = self.attributes['ACTION']
384
"<UPDATE TYPE='table' ACTION='add' ROOM='3'>"
385
" <TABLE ID='1' GAME='30' STATUS='1' SEATS='2'>"
387
" <SEAT NUM='0' TYPE='reserved'>bob</SEAT>"
388
" <SEAT NUM='1' TYPE='bot'/>"
391
room = self.attributes['ROOM']
392
tableId = self.table.attributes['ID']
393
gameId = self.table.attributes['GAME']
394
status = self.table.attributes['STATUS']
395
nSeats = int(self.table.attributes['SEATS'])
396
description = self.table.description
397
# FIXME: Include the seats with the add event somehow (and other adds)
398
self.decoder.feedback.tableAdded(room, tableId, gameId, status, nSeats, description)
399
for seat in self.table.seats:
400
self.decoder.feedback.seatChanged(room, tableId, seat.attributes['NUM'], seat.attributes['TYPE'], seat.label)
402
elif action == 'join':
403
"<UPDATE TYPE='table' ACTION='join' ROOM='3'>"
404
" <TABLE ID='1' SEATS='2'>"
405
" <SEAT NUM='0' TYPE='player'>bob</SEAT>"
408
room = self.attributes['ROOM']
409
tableId = self.table.attributes['ID']
410
for seat in self.table.seats:
411
self.decoder.feedback.seatChanged(room, tableId, seat.attributes['NUM'], seat.attributes['TYPE'], seat.label)
413
elif action == 'leave':
414
"<UPDATE TYPE='table' ACTION='leave' ROOM='3'>"
415
" <TABLE ID='1' SEATS='2'>"
416
" <SEAT NUM='0' TYPE='player'>bob</SEAT>"
419
room = self.attributes['ROOM']
420
tableId = self.table.attributes['ID']
421
for seat in self.table.seats:
422
self.decoder.feedback.seatChanged(room, tableId, seat.attributes['NUM'], seat.attributes['TYPE'], '') # seat.label)???
424
elif action == 'status':
425
"<UPDATE TYPE='table' ACTION='status' ROOM='3'>"
426
" <TABLE ID='1' STATUS='3' SEATS='2'/>"
428
self.decoder.feedback.tableStatusChanged(self.table.attributes['ID'], self.table.attributes['STATUS'])
430
elif action == 'delete':
431
"<UPDATE TYPE='table' ACTION='delete' ROOM='3'>"
432
" <TABLE ID='1' STATUS='-1' SEATS='2'/>"
434
self.decoder.feedback.tableRemoved(self.table.attributes['ID'])
437
print 'Unknown table update action: %s' % action
441
"<UPDATE TYPE='table' ACTION='add' ROOM='13'>"
442
" <TABLE ID='1' GAME='24' STATUS='1' SEATS='4'>"
443
" <DESC>I play alone...</DESC>"
444
" <SEAT NUM='0' TYPE='reserved'>helg</SEAT>"
445
" <SEAT NUM='1' TYPE='bot'/>"
446
" <SEAT NUM='2' TYPE='bot'/>"
447
" <SEAT NUM='3' TYPE='bot'/>"
450
"<UPDATE TYPE='table' ACTION='join' ROOM='13'>"
451
" <TABLE ID='1' SEATS='4'>"
452
" <SEAT NUM='0' TYPE='player'>helg</SEAT>"
456
class ChatParser(GGZParser):
461
def handle_data(self, data):
465
chatType = self.attributes['TYPE']
466
sender = self.attributes['FROM']
467
self.decoder.feedback.onChat(chatType, sender, self.text)
470
class ResultParser(GGZParser):
472
def start_list(self, attributes):
473
t = attributes['TYPE'].lower()
475
self.push(PlayerListParser(), attributes)
477
self.push(RoomListParser(), attributes)
479
self.push(GameListParser(), attributes)
481
self.push(TableListParser(), attributes)
483
def end_result(self):
486
class JoinParser(GGZParser):
489
tableId = self.attributes['TABLE']
490
isSpectator = self.attributes['SPECTATOR'] == 'true'
491
self.decoder.feedback.onJoin(tableId, isSpectator)
494
class LeaveParser(GGZParser):
495
"<LEAVE REASON='gameover'/>"
498
reason = self.attributes['REASON']
499
self.decoder.feedback.onLeave(reason)
502
class SessionParser(GGZParser):
504
def start_server(self, attributes):
505
self.push(ServerParser(), attributes)
507
def start_motd(self, attributes):
508
self.push(MOTDParser(), attributes)
510
def start_update(self, attributes):
511
t = attributes['TYPE'].lower()
513
self.push(RoomUpdateParser(), attributes)
515
self.push(PlayerUpdateParser(), attributes)
517
self.push(TableUpdateParser(), attributes)
519
print 'Unknown update type: %s' % t
521
def start_join(self, attributes):
522
self.push(JoinParser(), attributes)
524
def start_leave(self, attributes):
525
self.push(LeaveParser(), attributes)
527
def start_result(self, attributes):
528
self.push(ResultParser(), attributes)
529
self.decoder.feedback.sendNextCommand()
531
def start_chat(self, attributes):
532
self.push(ChatParser(), attributes)
534
def start_ping(self, attributes):
535
self.decoder.feedback.send("<PONG/>")
540
def end_session(self):
543
class BaseParser(GGZParser):
545
def __init__(self, decoder):
546
self.decoder = decoder
548
def start_session(self, attributes):
549
self.push(SessionParser(), attributes)
551
class Decoder(xml.sax.handler.ContentHandler):
553
def __init__(self, feedback):
554
xml.sax.handler.ContentHandler.__init__(self)
555
self.feedback = feedback
557
self.xparser = xml.sax.make_parser()
558
self.handler = BaseParser(self)
559
self.xparser.setContentHandler(self)
561
def startElement(self, name, attributes):
562
self.handler.startElement(name, attributes)
564
def characters(self, data):
565
self.handler.characters(data)
567
def endElement(self, name):
568
self.handler.endElement(name)
570
def feed(self, data):
571
self.xparser.feed(data)
573
class Channel(xml.sax.handler.ContentHandler):
575
def __init__(self, decoder):
576
xml.sax.handler.ContentHandler.__init__(self)
578
self.inSession = True
579
self.decoder = decoder
580
self.xparser = xml.sax.make_parser()
581
self.xparser.setContentHandler(self)
583
def endElement(self, name):
584
if name == 'SESSION':
585
self.inSession = False
587
def feed(self, data):
588
# Decode each line so can stop XML when session ends
589
while self.inSession and len(data) > 0:
590
index = data.find('\n')
592
self.xparser.feed(data)
595
self.xparser.feed(data[:index+1])
596
data = data[index+1:]
599
self.decoder.decode(c)
601
if __name__ == '__main__':
604
def onSeat(self, seatNum, version):
605
print ('onSeat', seatNum, version)
607
def onPlayers(self, whiteType, whiteName, blackType, blackName):
608
print ('onPlayers', whiteType, whiteName, blackType, blackName)
610
def onTimeRequest(self):
611
print ('onTimeRequest',)
613
def onSetTime(self, time):
614
print ('onSetTime', time)
619
def onMove(self, move):
620
print ('onMove', move)
625
for c in '\x01\x01\x06': # Seat seat=1 version=6
628
for c in '\x02\x03\x00\x00\x00\x0eglchess-test2\x00\x03\x00\x00\x00\x0dglchess-test\x00': # players type1=03 name1=glchess-test2 type2=03 name2=glchess-test
631
for c in '\x04\x00\x00\x00\x00': # rsp time time=0
634
d.decode('\x05') # start
636
for c in '\x07\x00\x00\x00\x05F2F4\x00': # move move=F2F4
639
for c in '\x0a\x00\x00\x00\x00\x00\x00\x00\x00': # update