Viewing file: network.py (16.74 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# -*- coding: utf-8 -*- import os import socket import errno import gettext
import ggz import ui import game
from defaults import *
_ = gettext.gettext
class GGZServer:
def __init__(self, name): self.name = name
class GGZLine:
TYPE_BLANK = 'BLANK' TYPE_COMMENT = 'COMMENT' TYPE_SECTION = 'SECTION' TYPE_FIELD = 'FIELD' TYPE_INVALID = 'INVALID' def __init__(self, text): self.text = text self.section = None try: self.decode(text) except ValueError, e: self.type = GGZLine.TYPE_INVALID print e
def decode(self, text): content = text.strip() if len(content) == 0: self.type = GGZLine.TYPE_BLANK elif content[0] == '#': self.type = GGZLine.TYPE_COMMENT elif content[0] == '[': self.type = GGZLine.TYPE_SECTION if content[-1] != ']': raise ValueError('Invalid section line: %s' % repr(content)) self.name = content[1:-1].strip() self.value = None else: self.type = GGZLine.TYPE_FIELD try: (name, value) = content.split('=', 1) except ValueError: raise ValueError('Invalid field line: %s' % repr(content)) else: self.name = name.strip() self.value = value.strip() def setValue(self, value): assert(self.type is GGZLine.TYPE_FIELD) self.value = value self.text = '%s=%s\n' % (self.name, value)
class GGZConfig: TYPE_NORMAL = 0 TYPE_GUEST = 1 TYPE_FIRST = 2 def __init__(self): try: f = file(GGZ_CONFIG_FILE) lines = f.readlines() except IOError, e: print 'Failed to load GGZ config: %s: %s' % (e.strerror, e.filename) lines = [] self.lines = []
section = None for text in lines: line = GGZLine(text) self.lines.append(line)
if line.type is GGZLine.TYPE_SECTION: section = line.name line.section = section else: line.section = section
def getField(self, section, name): for line in self.lines: if line.section != section or line.type is not GGZLine.TYPE_FIELD: continue if line.name == name: return line.value raise KeyError('No field %s in section %s' % (name, section)) def setField(self, section, name, value): wasInSection = False inSection = False for (index, line) in enumerate(self.lines): wasInSection = inSection if line.section == section: inSection = True if line.type is GGZLine.TYPE_FIELD and line.name == name: line.setValue(value) return elif wasInSection and not inSection: line = GGZLine('%s=%s\n' % (name, value)) line.section = section self.lines.insert(index, line) return # New section if not wasInSection: if len(self.lines) > 0: self.lines.append(GGZLine('\n')) line = GGZLine('[%s]\n' % section) line.section = section self.lines.append(line)
line = GGZLine('%s=%s\n' % (name, value)) line.section = section self.lines.append(line) def removeSection(self, name): keptLines = [] for line in self.lines: if line.section != name: keptLines.append(line) self.lines = keptLines def getServers(self): try: value = self.getField('Servers', 'ProfileList') except KeyError, e: return []
servers = [] for n in value.replace('\\ ', '\x00').split(' '): name = n.replace('\x00', ' ') try: server = self.getServer(name) except KeyError, e: print e else: servers.append(server) return servers def getServer(self, name): server = GGZServer(name)
try: server.host = self.getField(name, 'Host') server.port = int(self.getField(name, 'Port')) server.login = self.getField(name, 'Login') server.loginType = int(self.getField(name, 'Type')) except (KeyError, ValueError): raise KeyError('Missing/invalid basic configuration for server %s' % repr(server.name)) try: server.password = self.getField(name, 'Password') except KeyError: server.password = None
return server def updateServer(self, name, username, host, port): self.setField(name, 'Host', host) self.setField(name, 'Port', port) self.setField(name, 'Login', username) self.setField(name, 'Type', GGZConfig.TYPE_GUEST) # FIXME: Hardcoded try: servers = self.getField('Servers', 'ProfileList') except KeyError, e: self.setField('Servers', 'ProfileList', name.replace(' ', '\\ ')) else: self.setField('Servers', 'ProfileList', servers + ' ' + name.replace(' ', '\\ '))
self.save() def save(self): lines = [] for line in self.lines: lines.append(line.text) try: f = file(GGZ_CONFIG_FILE, 'w') f.writelines(lines) except IOError, e: print 'Failed to save GGZ config: %s: %s' % (e.strerror, e.filename)
class GGZChannel(ggz.Channel): def __init__(self, ui, feedback): self.buffer = '' self.ui = ui self.feedback = feedback self.socket = None self.fd = -1 def logXML(self, text): self.logWindow.addText(text, 'input')
def logBinary(self, data): self.logWindow.addBinary(data, 'input')
def connect(self, host, port): self.close() self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.fd = self.socket.fileno() self.socket.setblocking(False) self.ui.application.ioHandlers[self.fd] = self self.ui.controller.watchFileDescriptor(self.fd) try: self.socket.connect((host, port)) except socket.error, e: # FIXME: Abort/retry if error if e.args[0] == errno.EINPROGRESS: print 'connecting...' else: print e name = '%s:%d' % (host, port) self.logWindow = self.ui.controller.addLogWindow(name, name, name) def send(self, data, isBinary = False): if isBinary: self.logWindow.addBinary(data, 'output') else: self.logWindow.addText(data, 'output') self.buffer += data try: nSent = self.socket.send(self.buffer) except socket.error: nSent = 0 self.buffer = self.buffer[nSent:] def close(self): if self.socket is None: return self.ui.controller.unwatchFileDescriptor(self.fd) self.socket.close() self.socket = None self.fd = -1 def read(self): try: (data, address) = self.socket.recvfrom(65535) except socket.error, e: try: self.socket.close() except socket.error: pass self.feedback.closed(e.args[0]) return False else: if len(data) == 0: self.feedback.closed() return False self.feedback.registerIncomingData(data) return True
class GGZConnection(ggz.ClientFeedback):
def __init__(self, dialog): self.dialog = dialog self.profile = dialog.profile self.client = ggz.Client(self) self.commands = [] self.sending = False self.players = {} self.actions = [] def _getPlayerIcon(self, player): try: return {'guest': 'stock_person', 'normal': 'stock_person', 'admin': 'stock_person', # FIXME 'bot': 'stock_notebook'}[player.type] except KeyError: return '' def _performAction(self, method, args): if self.client.isBusy(): self.actions.insert(0, (method, args)) else: method(*args)
def close(self): self.client.close('')
def setBusy(self, isBusy): self.dialog.controller.setBusy(isBusy) # Perform next queued action if not isBusy and len(self.actions) > 0: (method, args) = self.actions.pop() method(*args)
def onConnected(self): self.dialog.controller.setSensitive(True) self.dialog.controller.clearError()
def onDisconnected(self, reason): self.dialog.controller.setError(_('Disconnected from server'), reason) self.dialog.controller.setSensitive(False)
def openChannel(self, feedback): print 'Open Channel' self.setBusy(True) socket = GGZChannel(self.dialog.ui, feedback) socket.connect(self.profile.host, self.profile.port) return socket
def getLogin(self): return (self.profile.login, self.profile.password) def getPassword(self, username): self.client.setPassword(self.profile.password) def onMOTD(self, motd): self.dialog.controller.addText(motd, 'motd')
def roomAdded(self, room): isChess = room.game is None or (room.game.protocol_engine == 'Chess' and room.game.protocol_version == '3') if room.game is None: protocol = None else: protocol = room.game.protocol_engine self.dialog.controller.addRoom(int(room.id), room.name, room.nPlayers, room.description, room, protocol)
def roomUpdated(self, room): self.dialog.controller.updateRoom(room, room.nPlayers)
def roomEntered(self, room): self.room = room self.dialog.controller.clearPlayers() self.dialog.controller.clearTables() for player in self.client.players.itervalues(): self.dialog.controller.addPlayer(player, player.name, self._getPlayerIcon(player)) for table in self.client.tables.itervalues(): self.tableAdded(table)
self.dialog.controller.enterRoom(room)
def tableAdded(self, table): self.tableUpdated(table)
def tableUpdated(self, table): if table.room != self.room: return description = table.description if len(description) == 0: description = _('No description') nUsed = 0 for seat in table.seats: if seat.type == 'bot' or seat.user != '': nUsed += 1
# Can connect to Chess tables with free spaces isChess = table.room.game is not None and table.room.game.protocol_engine == 'Chess' canConnect = isChess and nUsed != len(table.seats)
self.dialog.controller.updateTable(table, '%s' % table.id, '%i/%i' % (nUsed, len(table.seats)), description, canConnect) for (i, seat) in enumerate(table.seats): self.dialog.controller.updateSeat(table, i, seat.type, seat.user)
def tableRemoved(self, table): if table.room == self.room: self.dialog.controller.removeTable(table)
def onJoin(self, table, isSpectator, channel): self.dialog.controller.joinTable(table) g = GGZChess(self.dialog.game, channel) return g
def onLeave(self, reason): print 'Leave table: %s' % reason self.dialog.controller.joinTable(None)
def playerAdded(self, player): self.dialog.controller.addPlayer(player, player.name, self._getPlayerIcon(player))
def playerRemoved(self, player): self.dialog.controller.removePlayer(player)
def onChat(self, chatType, sender, text): self.dialog.controller.addText('\n%s: %s' % (sender, text), 'chat')
class GGZChess: """ """ def __init__(self, game, channel): self.game = game self.channel = channel self.protocol = ggz.Chess(self)
def registerIncomingData(self, data): for o in data: self.protocol.decode(o)
def onSeat(self, seatNum, version): self.seatNum = seatNum print ('onSeat', seatNum, version) def seatIsFull(self, seatType): return seatType == self.protocol.GGZ_SEAT_PLAYER or seatType == self.protocol.GGZ_SEAT_BOT
def onPlayers(self, whiteType, whiteName, blackType, blackName): print ('onPlayers', whiteType, whiteName, blackType, blackName) self.whiteName = whiteName self.blackName = blackName
def onClockRequest(self): print ('onTimeRequest',) self.channel.send(self.protocol.sendClock(self.protocol.CLOCK_NONE, 0), True) def onClock(self, mode, seconds): print ('onClock', mode, seconds)
def onStart(self): print ('onStart',) # Create remote player if self.seatNum == 0: name = self.blackName else: name = self.whiteName self.remotePlayer = game.ChessPlayer(name) self.remotePlayer.onPlayerMoved = self.onPlayerMoved # FIXME: HACK HACK HACK!
p = self.game.addHumanPlayer('Human') if self.seatNum == 0: self.game.setWhite(p) self.game.setBlack(self.remotePlayer) else: self.game.setWhite(self.remotePlayer) self.game.setBlack(p)
self.game.start()
def onPlayerMoved(self, player, move): #FIXME: HACK HACK HACK! if player is not self.remotePlayer: self.channel.send(self.protocol.sendMove(move.canMove.upper()), True)
def onMove(self, move): print ('onMove', move) # FIXME: Only remote players should be used self.remotePlayer.move(move.lower())
class GGZNetworkDialog(ui.NetworkFeedback): """ """ def __init__(self, ui): self.ui = ui self.buffer = '' self.profile = None self.decoder = None def addProfile(self, profile): """Called by ui.NetworkFeedback""" (name, username, host, port) = profile
# Make name unique n = name dupCount = 1 while True: try: self.ui.ggzConfig.getServer(n) except KeyError: break else: dupCount += 1 n = '%s (%d)' % (name, dupCount) name = n self.ui.ggzConfig.updateServer(name, username, host, port) # FIXME: Return value is temporary return self.ui.ggzConfig.getServer(name)
def setProfile(self, profile): """Called by ui.NetworkFeedback""" if profile is None: name = '(none)' else: name = profile.name print 'Profile=%s' % name #FIXME Make proper log
if self.decoder is not None: self.decoder.close() self.decoder = None
self.controller.clearError() self.controller.setSensitive(False) self.controller.clearRooms() self.controller.clearPlayers() self.controller.clearTables() self.controller.clearText() self.profile = profile if profile is None: return self.decoder = GGZConnection(self) self.decoder.client.start() def enterRoom(self, room): """Called by ui.NetworkFeedback""" print 'Entering room %s' % room.id self.decoder._performAction(self.decoder.client.enterRoom, (room,)) def _addGame(self): self.game = self.ui.application.addGame('Network Game') if self.game is None: # FIXME: Notify user game aborted return False return True def startTable(self): """Called by ui.NetworkFeedback""" if self.decoder.room.game is None: return if not self._addGame(): return print 'Starting table' self.decoder.client.startTable(self.decoder.room.game.id, '', self.profile.login)
def joinTable(self, table): """Called by ui.NetworkFeedback""" if not self._addGame(): return print 'Starting table %s' % table.id self.decoder.client.joinTable(table)
def leaveTable(self): """Called by ui.NetworkFeedback""" self.decoder.client.leaveTable()
def sendChat(self, text): """Called by ui.NetworkFeedback""" self.decoder.client.sendChat(text)
|