def handleJoiningReceive(stream: SendStream, socket: ClientSocket): command = stream.readBytes() if command == b'host': socket.unregister() session = Session(stream, socket) openSessions[session.id] = session elif command == b'join': sessionId = stream.readInt() session = openSessions.get(sessionId, None) if session: error = session.addClient(socket) if not error: return else: error = 'The session has been canceled.' sendStream = SendStream() sendStream.writeBytes(b'cancel') sendStream.writeString(error) sendStream.send(socket.socket) elif command == b'client': freeSessions = [ x for x in openSessions.values() if len(x.clients) < x.maxPlayers and socket.secret == x.secret ] sendStream = socket.sendStream sendStream.writeBytes(b'addsessions') sendStream.writeInt(len(freeSessions)) for session in freeSessions: sendStream.writeInt(session.id) sendStream.writeString(session.hostName) sendStream.writeString(session.gameName) sendStream.send()
def __init__(self): super().__init__() self.clientSocket = ClientSocket(QSslSocket()) self.socket = self.clientSocket.socket self.sendStream = self.clientSocket.sendStream self.clientSocket.setReceiveHandler(self.handleConnectorReceive) self.socket.setCaCertificates( [QSslCertificate(base64.b64decode(certificate))]) self.socket.encrypted.connect(self.handleConnected) self.socket.error.connect(self.handleError) self.socket.connectToHostEncrypted(Client.serverSettings.address, listenPort, QIODevice.ReadWrite, QSslSocket.IPv4Protocol)
def addClient(self, socket: ClientSocket): if self.clients.get(socket.name, None): # client name already used return 'The client name ' + socket.name + ' is already used in this session.' if len(self.clients) + 1 > self.maxPlayers: return 'The maximum client count has been reached for this session.' if len(self.clients) + 1 == self.maxPlayers: self.announceLater() joiningClients.remove(socket) socket.setDisconnectHandler(self.handleClientDisconnect) socket.setReceiveHandler(self.handleClientReceive) self.clients[socket.name] = socket sendStream = self.hostSocket.sendStream sendStream.writeBytes(b'join') sendStream.writeString(socket.name) sendStream.send() return None
def handleClientDisconnect(self, socket: ClientSocket): del self.clients[socket.name] if self.state == Session.StateOpen: if len(self.clients) < self.maxPlayers: self.announceLater() stream = SendStream() stream.writeBytes(b'disjoin') stream.writeString(socket.name) stream.send(self.hostSocket.socket) elif self.state == Session.StateInit: if socket in self.init.missingClientHashes: self.init.missingClientCount -= 1 del self.init.missingClientHashes[socket] self.handleClientLeftDuringInit(socket) else: # game phase sendStream = SendStream() sendStream.writeBytes(b'left') sendStream.writeString(socket.name) for socket in self.clients.values(): socket.send(sendStream) if not self.clients and self.id in activeSessions: del activeSessions[self.id]
def __init__(self, stream: SendStream, hostSocket: ClientSocket): self.gameName = stream.readString() self.minPlayers = stream.readInt() self.maxPlayers = stream.readInt() self.playerCanLeave = stream.readBool() returnId = stream.readBool() self.hostSocket = hostSocket self.hostName = hostSocket.name self.secret = hostSocket.secret self.clients = {hostSocket.name: hostSocket} # map names to sockets self.state = Session.StateOpen self.lastAnnounced = None self.announcePending = False self.init = None # holds during the init state some objects Session.IdCounter += 1 self.id = Session.IdCounter if returnId: hostSocket.sendStream.writeBytes(b'id') hostSocket.sendStream.writeInt(self.id) hostSocket.setReceiveHandler(self.handleHostReceive) hostSocket.setDisconnectHandler(self.handleHostDisconnect) self.announce()
def handleClientReceive(self, stream: SendStream, socket: ClientSocket): try: command = stream.readBytes() if command == b'update': finished = stream.readBool() stream.readBytes() # state data = stream.data() for othername, othersocket in self.clients.items(): if othername != socket.name: writeData(othersocket.socket, data) if finished: othersocket.setDisconnectHandler(None) othersocket.disconnectWhenWritten() if finished: socket.setDisconnectHandler(None) socket.disconnect() del activeSessions[self.id] elif command == b'message': stream.readString() # title stream.readString() # text data = stream.data() for othername, othersocket in self.clients.items(): if othername != socket.name: writeData(othersocket.socket, data) elif command == b'disjoin': if len(self.clients) == self.maxPlayers: self.announceLater() self.handleClientDisconnect(socket) socket.register(joiningClients) socket.setReceiveHandler(self.handleJoiningReceive) elif command == b'data': count = stream.readInt() if not self.init: socket.socket.disconnect() return missingClientHashes = [] for i in range(count): missingClientHashes.append(stream.readBytes()) if self.init.missingFileHashes is None: self.sendMissingClientData(socket, missingClientHashes) else: self.init.missingClientHashes[socket] = missingClientHashes except Exception as e: self.abortSession() log('session error 3 : ' + str(e) + ' - ' + traceback.format_exc()) raise HandledError()
def handleReceive(socket = socket, timerId = timerId): if timerId: self.killTimer(timerId) unvalidatedSockets.remove(socket) socket.disconnected.disconnect(handleDisconnect) def blockAddress(): address = socket.peerAddress() remaining = blockedAddresses.setdefault(address, 3) blockedAddresses[address] -= 1 if remaining == 1: # was last trial socket.write(b"your address '" + address.toString().encode() + b"' has been blocked\n") log("block address '" + address.toString() + "'") socket.disconnectFromHost() try: secretLen = int(socket.read(1)) except: blockAddress() return secret = socket.read(secretLen) if secret == b'ctrl4eip': blockedAddresses.pop(socket.peerAddress(), None) controlHandlers.add(ControlHandler(socket)) socket.readyRead.disconnect(handleReceive) elif secret == sessionClientSecret: blockedAddresses.pop(socket.peerAddress(), None) clientSocket = ClientSocket(socket) clientSocket.register(validatedSockets) clientSocket.setReceiveHandler(handleValidatedReceive) socket.readyRead.disconnect(handleReceive) clientSocket.handleReceive() else: address = socket.peerAddress() remaining = blockedAddresses.setdefault(address, 3) blockedAddresses[address] -= 1 if remaining == 1: # was last trial socket.write(b"your address '" + address.toString().encode() + b"' has been blocked\n") log("block address '" + address.toString() + "'") socket.disconnectFromHost() return
def handleValidatedReceive(stream : SendStream, socket : ClientSocket): socket.unregister() protocolVersion = stream.readInt() if protocolVersion != sessionProtocolVersion: if protocolVersion < sessionProtocolVersion: text = 'Client uses outdated protocol version. Please update the client.' else: text = 'Client uses newer protocol version. Please inform the server provider that the server is outdated.' stream = socket.sendStream stream.writeBytes(b'error') stream.writeString(text) stream.send() socket.disconnectWhenWritten() raise HandledError() socket.name = stream.readString() socket.secret = stream.readString() Session.startHandling(stream, socket)
def handleHostDisconnect(self, socket: ClientSocket): self.announceLater() del self.clients[self.hostName] if self.state == Session.StateOpen: stream = SendStream() stream.writeBytes(b'cancel') stream.writeString('The host has been disconnected.') for socket in self.clients.values(): socket.send(stream) socket.register(joiningClients) socket.setReceiveHandler(self.handleJoiningReceive) self.state = Session.StateDone del openSessions[self.id] elif self.init: if self.init.missingFileHashes: # host disconnected before sending files self.handleErrorWhenOpen('Connection to host has been lost.') else: self.handleClientLeftDuringInit(socket) else: pass # this case should not occur
def startHandling(cls, stream: SendStream, socket: ClientSocket): socket.register(joiningClients) socket.setReceiveHandler(cls.handleJoiningReceive) if not stream.atEnd(): cls.handleJoiningReceive(stream, socket)
class ClientConnector(QObject): def __init__(self): super().__init__() self.clientSocket = ClientSocket(QSslSocket()) self.socket = self.clientSocket.socket self.sendStream = self.clientSocket.sendStream self.clientSocket.setReceiveHandler(self.handleConnectorReceive) self.socket.setCaCertificates( [QSslCertificate(base64.b64decode(certificate))]) self.socket.encrypted.connect(self.handleConnected) self.socket.error.connect(self.handleError) self.socket.connectToHostEncrypted(Client.serverSettings.address, listenPort, QIODevice.ReadWrite, QSslSocket.IPv4Protocol) def disconnect(self): self.socket.disconnectFromHost() def handleConnected(self): self.socket.write( str(len(sessionClientSecret)).encode() + sessionClientSecret) self.sendStream.writeInt(sessionProtocolVersion) self.sendStream.writeString(Client.serverSettings.name) self.sendStream.writeString(Client.serverSettings.secret) self.sendStream.send() self.connected.emit() def host(self, returnId=False): self.sendStream.writeBytes(b'host') self.sendStream.writeString(Client.gameObject.gameName) self.sendStream.writeInt(Client.gameObject.minPlayerCount) self.sendStream.writeInt(Client.gameObject.maxPlayerCount) self.sendStream.writeBool(Client.gameObject.playerCanLeave) self.sendStream.writeBool(returnId) self.sendStream.send() def getSessions(self): self.sendStream.writeBytes(b'client') self.sendStream.send() def join(self, sessionId): self.sendStream.writeBytes(b'join') self.sendStream.writeInt(sessionId) self.sendStream.send() def disjoin(self): self.sendStream.writeBytes(b'disjoin') self.sendStream.send() def startSession(self): self.sendStream.writeBytes(b'start') self.sendStream.writeInt(0) # no files self.sendStream.send() def sendGameData(self, data, state): self.sendStream.writeBytes(b'data') self.sendStream.writeInt(0) # no files self.sendStream.writeBytes(data) self.sendStream.writeBytes(state) self.sendStream.send() def sendUpdate(self, finished, state): self.sendStream.writeBytes(b'update') self.sendStream.writeBool(finished) self.sendStream.writeBytes(state) self.sendStream.send() def sendMessage(self, title, text): self.sendStream.writeBytes(b'message') self.sendStream.writeString(title) self.sendStream.writeString(text) self.sendStream.send() connected = pyqtSignal() sessionIdReceived = pyqtSignal(int) sessionsAdded = pyqtSignal(list) sessionRemoved = pyqtSignal(int) sessionCanceled = pyqtSignal(str) playerJoined = pyqtSignal(str) playerDisjoined = pyqtSignal(str) playerLeft = pyqtSignal(str) errorOccurred = pyqtSignal(str) gameDataRequested = pyqtSignal() gameDataReceived = pyqtSignal(bytes, bytes) updateReceived = pyqtSignal(bool, bytes) messageReceived = pyqtSignal(str, str) def handleError(self, error): self.socket.disconnectFromHost() if type(error) != str: if error == QSslSocket.SocketTimeoutError: error = 'A timeout error occurred.' elif error == QSslSocket.ConnectionRefusedError: error = 'The connection has been refused.' else: error = 'Socket error {} occurred.'.format(int(error)) self.errorOccurred.emit(error) def handleConnectorReceive(self, stream: SendStream, socket: ClientSocket): command = stream.readBytes() if command == b'id': self.sessionIdReceived.emit(stream.readInt()) elif command == b'error': self.handleError(stream.readString()) elif command == b'join': self.playerJoined.emit(stream.readString()) elif command == b'disjoin': self.playerDisjoined.emit(stream.readString()) elif command == b'left': self.playerLeft.emit(stream.readString()) elif command == b'cancel': self.sessionCanceled.emit(stream.readString()) elif command == b'rmsession': self.sessionRemoved.emit(stream.readInt()) elif command == b'addsessions': sessions = [] for i in range(stream.readInt()): id = stream.readInt() host = stream.readString() game = stream.readString() sessions.append(Session(game, host, id)) self.sessionsAdded.emit(sessions) elif command == b'start': fileMap = dict() for i in range(stream.readInt()): fileName = stream.readString() fileHash = stream.readBytes() fileMap[fileHash] = fileName # no file support self.sendStream.writeBytes(b'data') self.sendStream.writeInt(0) self.sendStream.send() elif command == b'missing': missingHashes = [] for i in range(stream.readInt()): missingHashes.append(stream.readBytes()) self.gameDataRequested.emit() elif command == b'data': files = [] for i in range(stream.readInt()): files.append(stream.readBytes()) data = stream.readBytes() state = stream.readBytes() self.gameDataReceived.emit(data, state) elif command == b'update': finished = stream.readBool() state = stream.readBytes() self.updateReceived.emit(finished, state) elif command == b'message': title = stream.readString() text = stream.readString() self.messageReceived.emit(title, text)