1
# -*- coding: utf-8 -*-
4
"<UPDATE TYPE='player' ACTION='delete' ROOM='40' TOROOM='-1'>"
5
"<PLAYER ID='kostaya'/>"
8
"<UPDATE TYPE='table' ACTION='leave' ROOM='40'>"
9
"<TABLE ID='1' SEATS='2'>"
10
"<SEAT NUM='0' TYPE='player'>kostaya</SEAT>"
14
"<UPDATE TYPE='table' ACTION='status' ROOM='40'>"
15
"<TABLE ID='1' STATUS='3' SEATS='2'/>"
18
"<UPDATE TYPE='table' ACTION='delete' ROOM='40'>"
19
"<TABLE ID='1' STATUS='-1' SEATS='2'/>"
24
def onResult(self, action, code):
27
def onMOTD(self, motd):
30
def onChat(self, chatType, sender, text):
33
def onJoin(self, tableId, isSpectator):
36
def onLeave(self, reason):
39
def gameAdded(self, gameId, name, version, author, url, numPlayers, protocol_engine, protocol_version):
42
def roomAdded(self, roomId, gameId, name, description, nPlayers):
45
def roomPlayersUpdate(self, roomId, nPlayers):
48
def tableAdded(self, roomId, tableId, gameId, status, nSeats, description):
51
def tableStatusChanged(self, tableId, status):
54
def seatChanged(self, roomId, tableId, seatId, seatType, user):
57
def tableRemoved(self, tableId):
60
def onPlayerList(self, room, players):
63
def playerAdded(self, name, playerType, tableId, perms, lag, room, fromRoom):
66
def playerRemoved(self, name, room, toRoom):
77
def startElement(self, name, attributes):
78
if self.parser is not None:
79
self.parser.startElement(name, attributes)
82
method = getattr(self, 'start_%s' % name.lower())
83
except AttributeError:
84
print 'Unknown start element: %s' % name
88
def characters(self, data):
89
if self.parser is not None:
90
self.parser.characters(data)
92
self.handle_data(data)
94
def endElement(self, name):
95
if self.parser is not None:
96
self.parser.endElement(name)
99
method = getattr(self, 'end_%s' % name.lower())
100
except AttributeError:
101
print 'Unknown end element: %s' % name
105
def push(self, parser, attributes):
106
assert(self.parser is None)
107
parser.attributes = attributes
108
parser.decoder = self.decoder
113
assert(self.parent is not None)
114
parser = self.parent.parser
115
self.parent.parser = None
116
self.parent.childFinished(parser)
118
def handle_data(self, data):
121
def childFinished(self, parser):
124
class GameParser(GGZParser):
126
<GAME ID='24' NAME='TicTacToe' VERSION='0.0.9'>
127
<PROTOCOL ENGINE='TicTacToe' VERSION='4'/>
128
<ALLOW PLAYERS='2' BOTS='1' SPECTATORS='true' PEERS='false'/>
129
<BOT NAME='Alfred' CLASS='easy'/>
130
<BOT NAME='Tarantula' CLASS='hard'/>
131
<ABOUT AUTHOR='Brent Hendricks' URL='http://www.ggzgamingzone.org/games/tictactoe/'/>
132
<DESC>Simple GGZ game module for playing Tic-Tac-Toe</DESC>
137
GGZParser.__init__(self)
140
def start_desc(self, attributes):
141
self.push(DescriptionParser(), attributes)
143
def start_protocol(self, attributes):
144
self.push(GameProtocolParser(), attributes)
146
def start_allow(self, attributes):
147
self.push(GameAllowParser(), attributes)
149
def start_bot(self, attributes):
150
self.push(GameBotParser(), attributes)
152
def start_about(self, attributes):
153
self.push(GameAboutParser(), attributes)
156
self.gameId = self.attributes['ID']
157
self.name = self.attributes['NAME']
158
self.version = self.attributes['VERSION']
162
return 'GGZ Game id=%s protocol=%s (%s) description=%s' % (self.gameId, repr(self.protocol.engine), self.protocol.version, repr(self.description))
164
class DescriptionParser(GGZParser):
166
<DESC>Simple GGZ game module for playing Tic-Tac-Toe</DESC>
169
def handle_data(self, data):
170
self.parent.description = data
175
class GameProtocolParser(GGZParser):
177
<PROTOCOL ENGINE='TicTacToe' VERSION='4'/>
180
def end_protocol(self):
181
self.parent.protocol = self
182
self.engine = self.attributes['ENGINE']
183
self.version = self.attributes['VERSION']
186
class GameAllowParser(GGZParser):
188
<ALLOW PLAYERS='2' BOTS='1' SPECTATORS='true' PEERS='false'/>
192
self.parent.allow = self
193
self.numPlayers = self.attributes['PLAYERS']
196
class GameBotParser(GGZParser):
198
<BOT NAME='Alfred' CLASS='easy'/>
199
<BOT NAME='Tarantula' CLASS='hard'/>
203
self.parent.bots.append(self)
204
self.name = self.attributes['NAME']
205
self.difficulty = self.attributes['CLASS']
208
class GameAboutParser(GGZParser):
210
<ABOUT AUTHOR='Brent Hendricks' URL='http://www.ggzgamingzone.org/games/tictactoe/'/>
214
self.parent.about = self
215
self.author = self.attributes['AUTHOR']
216
self.url = self.attributes['URL']
219
class RoomParser(GGZParser):
221
def start_desc(self, attributes):
222
self.push(DescriptionParser(), attributes)
228
return 'GGZ Room id=%s game=%s description=%s' % (self.roomId, self.game, repr(self.description))
230
class PlayerParser(GGZParser):
232
def end_player(self):
236
return 'GGZ Player id=%s type=%s table=%s perms=%s lag=%s' % (self.id, self.type, self.table, self.perms, self.lag)
238
class TableSeatParser(GGZParser):
241
GGZParser.__init__(self)
244
def handle_data(self, data):
250
class TableParser(GGZParser):
253
GGZParser.__init__(self)
255
self.description = ''
257
def start_desc(self, attributes):
258
self.push(DescriptionParser(), attributes)
260
def start_seat(self, attributes):
261
self.push(TableSeatParser(), attributes)
263
def childFinished(self, parser):
264
if isinstance(parser, TableSeatParser):
265
self.seats.append(parser)
270
class GameListParser(GGZParser):
273
GGZParser.__init__(self)
276
def start_game(self, attributes):
277
self.push(GameParser(), attributes)
279
def childFinished(self, parser):
280
self.games.append(parser)
284
self.decoder.feedback.gameAdded(g.gameId, g.name, g.version, g.about.author, g.about.url, g.allow.numPlayers,
285
g.protocol.engine, g.protocol.version)
288
class TableListParser(GGZParser):
291
GGZParser.__init__(self)
294
def start_table(self, attributes):
295
self.push(TableParser(), attributes)
297
def childFinished(self, parser):
298
self.tables.append(parser)
301
for t in self.tables:
302
room = self.attributes['ROOM']
303
tableId = t.attributes['ID']
304
gameId = t.attributes['GAME']
305
status = t.attributes['STATUS']
306
nSeats = int(t.attributes['SEATS'])
307
self.decoder.feedback.tableAdded(room, tableId, gameId, status, nSeats, t.description)
309
self.decoder.feedback.seatChanged(room, tableId, seat.attributes['NUM'], seat.attributes['TYPE'], seat.label)
312
class PlayerListParser(GGZParser):
315
GGZParser.__init__(self)
318
def start_player(self, attributes):
319
self.push(PlayerParser(), attributes)
321
def childFinished(self, playerParser):
322
playerParser.name = playerParser.attributes['ID']
323
playerParser.type = playerParser.attributes['TYPE']
324
playerParser.tableId = playerParser.attributes['TABLE']
326
playerParser.perms = playerParser.attributes['PERMS']
328
playerParser.perms = ''
329
playerParser.lag = playerParser.attributes['LAG']
330
self.players.append(playerParser)
333
self.decoder.feedback.onPlayerList(self.attributes['ROOM'], self.players)
336
class RoomListParser(GGZParser):
339
GGZParser.__init__(self)
342
def start_room(self, attributes):
343
self.push(RoomParser(), attributes)
345
def childFinished(self, parser):
346
parser.roomId = parser.attributes['ID']
347
parser.name = parser.attributes['NAME']
348
parser.game = parser.attributes['GAME']
349
parser.nPlayers = int(parser.attributes['PLAYERS'])
350
self.rooms.append(parser)
354
self.decoder.feedback.roomAdded(r.roomId, r.game, r.name, r.description, r.nPlayers)
357
class ServerOptionsParser(GGZParser):
359
def end_options(self):
362
class ServerParser(GGZParser):
364
def start_options(self, attributes):
365
self.push(ServerOptionsParser(), attributes)
367
def end_server(self):
368
self.decoder.feedback.onConnected()
371
class MOTDParser(GGZParser):
374
GGZParser.__init__(self)
377
def handle_data(self, data):
381
self.decoder.feedback.onMOTD(self.motd)
384
class RoomUpdateParser(GGZParser):
386
def start_room(self, attributes):
387
self.push(RoomParser(), attributes)
389
def childFinished(self, parser):
390
action = self.attributes['ACTION'].lower()
391
if action == 'players':
392
roomId = parser.attributes['ID']
393
nPlayers = int(parser.attributes['PLAYERS'])
394
self.decoder.feedback.roomPlayersUpdate(roomId, nPlayers)
396
print 'Unknown player update action %s' % action
398
def end_update(self):
401
class PlayerUpdateParser(GGZParser):
403
def start_player(self, attributes):
404
self.push(PlayerParser(), attributes)
406
def childFinished(self, parser):
407
action = self.attributes['ACTION'].lower()
409
name = parser.attributes['ID']
410
playerType = parser.attributes['TYPE']
411
table = parser.attributes['TABLE']
413
perms = parser.attributes['PERMS']
416
lag = parser.attributes['LAG']
417
roomId = self.attributes['ROOM']
418
fromRoomId = self.attributes['FROMROOM']
419
self.decoder.feedback.playerAdded(name, playerType, table, perms, lag, roomId, fromRoomId)
420
elif action == 'lag':
421
playerId = parser.attributes['ID']
422
lag = parser.attributes['LAG']
423
print 'Player %s lag changed to %s' % (playerId, lag)
424
elif action == 'join':
426
elif action == 'leave':
428
elif action == 'desc':
430
elif action == 'seat':
432
elif action == 'delete':
433
playerId = parser.attributes['ID']
434
room = self.attributes['ROOM']
435
toRoom = self.attributes['TOROOM']
436
self.decoder.feedback.playerRemoved(playerId, room, toRoom)
438
print 'Unknown player update action %s' % action
440
def end_update(self):
443
class TableUpdateParser(GGZParser):
446
GGZParser.__init__(self)
449
def start_table(self, attributes):
450
self.push(TableParser(), attributes)
452
def childFinished(self, parser):
455
def end_update(self):
456
room = self.attributes['ROOM']
457
action = self.attributes['ACTION']
459
"<UPDATE TYPE='table' ACTION='add' ROOM='3'>"
460
" <TABLE ID='1' GAME='30' STATUS='1' SEATS='2'>"
462
" <SEAT NUM='0' TYPE='reserved'>bob</SEAT>"
463
" <SEAT NUM='1' TYPE='bot'/>"
466
room = self.attributes['ROOM']
467
tableId = self.table.attributes['ID']
468
gameId = self.table.attributes['GAME']
469
status = self.table.attributes['STATUS']
470
nSeats = int(self.table.attributes['SEATS'])
471
description = self.table.description
472
# FIXME: Include the seats with the add event somehow (and other adds)
473
self.decoder.feedback.tableAdded(room, tableId, gameId, status, nSeats, description)
474
for seat in self.table.seats:
475
self.decoder.feedback.seatChanged(room, tableId, seat.attributes['NUM'], seat.attributes['TYPE'], seat.label)
477
elif action == 'join':
478
"<UPDATE TYPE='table' ACTION='join' ROOM='3'>"
479
" <TABLE ID='1' SEATS='2'>"
480
" <SEAT NUM='0' TYPE='player'>bob</SEAT>"
483
room = self.attributes['ROOM']
484
tableId = self.table.attributes['ID']
485
for seat in self.table.seats:
486
self.decoder.feedback.seatChanged(room, tableId, seat.attributes['NUM'], seat.attributes['TYPE'], seat.label)
488
elif action == 'leave':
489
"<UPDATE TYPE='table' ACTION='leave' ROOM='3'>"
490
" <TABLE ID='1' SEATS='2'>"
491
" <SEAT NUM='0' TYPE='player'>bob</SEAT>"
494
room = self.attributes['ROOM']
495
tableId = self.table.attributes['ID']
496
for seat in self.table.seats:
497
self.decoder.feedback.seatChanged(room, tableId, seat.attributes['NUM'], seat.attributes['TYPE'], '') # seat.label)???
499
elif action == 'status':
500
"<UPDATE TYPE='table' ACTION='status' ROOM='3'>"
501
" <TABLE ID='1' STATUS='3' SEATS='2'/>"
503
self.decoder.feedback.tableStatusChanged(self.table.attributes['ID'], self.table.attributes['STATUS'])
505
elif action == 'delete':
506
"<UPDATE TYPE='table' ACTION='delete' ROOM='3'>"
507
" <TABLE ID='1' STATUS='-1' SEATS='2'/>"
509
self.decoder.feedback.tableRemoved(self.table.attributes['ID'])
512
print 'Unknown table update action: %s' % action
516
"<UPDATE TYPE='table' ACTION='add' ROOM='13'>"
517
" <TABLE ID='1' GAME='24' STATUS='1' SEATS='4'>"
518
" <DESC>I play alone...</DESC>"
519
" <SEAT NUM='0' TYPE='reserved'>helg</SEAT>"
520
" <SEAT NUM='1' TYPE='bot'/>"
521
" <SEAT NUM='2' TYPE='bot'/>"
522
" <SEAT NUM='3' TYPE='bot'/>"
525
"<UPDATE TYPE='table' ACTION='join' ROOM='13'>"
526
" <TABLE ID='1' SEATS='4'>"
527
" <SEAT NUM='0' TYPE='player'>helg</SEAT>"
531
class ChatParser(GGZParser):
534
GGZParser.__init__(self)
537
def handle_data(self, data):
541
chatType = self.attributes['TYPE']
542
sender = self.attributes['FROM']
543
self.decoder.feedback.onChat(chatType, sender, self.text)
546
class ResultParser(GGZParser):
548
def start_list(self, attributes):
549
t = attributes['TYPE'].lower()
551
self.push(PlayerListParser(), attributes)
553
self.push(RoomListParser(), attributes)
555
self.push(GameListParser(), attributes)
557
self.push(TableListParser(), attributes)
559
print 'Unknown list: %s' % t
561
def end_result(self):
562
action = self.attributes['ACTION']
563
code = self.attributes['CODE']
564
self.decoder.feedback.onResult(action, code)
567
class JoinParser(GGZParser):
570
tableId = self.attributes['TABLE']
571
isSpectator = self.attributes['SPECTATOR'] == 'true'
572
self.decoder.feedback.onJoin(tableId, isSpectator)
575
class LeaveParser(GGZParser):
576
"<LEAVE REASON='gameover'/>"
579
reason = self.attributes['REASON']
580
self.decoder.feedback.onLeave(reason)
583
class SessionParser(GGZParser):
585
def start_server(self, attributes):
586
self.push(ServerParser(), attributes)
588
def start_motd(self, attributes):
589
self.push(MOTDParser(), attributes)
591
def start_update(self, attributes):
592
t = attributes['TYPE'].lower()
594
self.push(RoomUpdateParser(), attributes)
596
self.push(PlayerUpdateParser(), attributes)
598
self.push(TableUpdateParser(), attributes)
600
print 'Unknown update type: %s' % t
602
def start_join(self, attributes):
603
self.push(JoinParser(), attributes)
605
def start_leave(self, attributes):
606
self.push(LeaveParser(), attributes)
608
def start_result(self, attributes):
609
self.push(ResultParser(), attributes)
611
def start_chat(self, attributes):
612
self.push(ChatParser(), attributes)
614
def start_ping(self, attributes):
615
self.decoder.feedback.send("<PONG/>\n")
620
def end_session(self):
621
self.decoder.feedback.onSessionEnded()
624
class BaseParser(GGZParser):
626
def __init__(self, decoder):
627
GGZParser.__init__(self)
628
self.decoder = decoder
630
def start_session(self, attributes):
631
self.push(SessionParser(), attributes)
633
class Decoder(xml.sax.handler.ContentHandler):
635
def __init__(self, feedback):
636
xml.sax.handler.ContentHandler.__init__(self)
637
self.feedback = feedback
639
self.xparser = xml.sax.make_parser()
640
self.handler = BaseParser(self)
641
self.xparser.setContentHandler(self)
643
def startElement(self, name, attributes):
644
self.handler.startElement(name, attributes)
646
def characters(self, data):
647
self.handler.characters(data)
649
def endElement(self, name):
650
self.handler.endElement(name)
652
def feed(self, data):
653
"""Feed data into the decoder.
655
'data' is the raw data to feed.
657
Returns the next block of data to process, keep feeding until '' is returned.
659
index = data.find('\n')
661
index = len(data) - 1
663
chunk = data[:index+1]
665
self.xparser.feed(chunk)
666
except xml.sax.SAXParseException:
667
print 'Invalid XML: %s' % repr(chunk)
668
return data[index+1:]
670
if __name__ == '__main__':
675
def onSeat(self, seatNum, version):
676
print ('onSeat', seatNum, version)
678
def onPlayers(self, whiteType, whiteName, blackType, blackName):
679
print ('onPlayers', whiteType, whiteName, blackType, blackName)
681
def onTimeRequest(self):
682
print ('onTimeRequest',)
684
def onSetTime(self, time):
685
print ('onSetTime', time)
690
def onMove(self, move):
691
print ('onMove', move)
696
data = '\x01\x01\x06' # Seat seat=1 version=6
700
data = '\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
704
data = '\x04\x00\x00\x00\x00' # rsp time time=0
708
d.decode('\x05') # start
710
data = '\x07\x00\x00\x00\x05F2F4\x00' # move move=F2F4
714
data = '\x0a\x00\x00\x00\x00\x00\x00\x00\x00' # update