import time from constants import decodeAOString, CHATMSG, TEXT_COLOR, C_RAINBOW from os.path import exists from PyQt4 import QtCore class TCPThread(QtCore.QThread): connectionError = QtCore.pyqtSignal(str, str, str) MS_Chat = QtCore.pyqtSignal(list) newChar = QtCore.pyqtSignal(int) newBackground = QtCore.pyqtSignal(str, bool) IC_Log = QtCore.pyqtSignal(str) OOC_Log = QtCore.pyqtSignal(str) charSlots = QtCore.pyqtSignal(list) showCharSelect = QtCore.pyqtSignal() loadAllEvidence = QtCore.pyqtSignal(list) rainbowColor = QtCore.pyqtSignal(str) updatePlayerList = QtCore.pyqtSignal(str, int, int, str) timerUpdate = QtCore.pyqtSignal(int, int, int) authStatusChanged = QtCore.pyqtSignal(int) updateAreaList = QtCore.pyqtSignal(list) setCharList = QtCore.pyqtSignal(list) playMusicRequested = QtCore.pyqtSignal(str, int, str) modCall = QtCore.pyqtSignal(str) discardMessageFromQueue = QtCore.pyqtSignal() send_attempts = 0 max_attempts = 5 stop_now = False def __init__(self, parent): super(TCPThread, self).__init__(parent) self.parent = parent def run(self): pingtimer = 150 rainbow = 0 sendtick = 0 while True: if self.stop_now: self.parent.tcp.close() self.parent.tcp = None self.quit() return if self.parent.disconnectNow: self.parent.disconnectCommon() self.quit() return pingtimer -= 1 if pingtimer == 0: # pingbefore = time.time() self.parent.tcp.send('CH#%') pingtimer = 150 if self.parent.mChatMessage[TEXT_COLOR] == str(C_RAINBOW): rainbow += 5 if rainbow > 255: rainbow = 0 self.rainbowColor.emit('background-color: rgba(0, 0, 0, 0); color: hsv(%s,255,255)' % rainbow) if sendtick: sendtick -= 1 if self.parent.messageQueue and not sendtick: self.parent.tcp.send(self.parent.messageQueue[0]) sendtick = 4 error, total = self.parent.tcp.recv() if error == -2: # if the message can't be sent, discard it if sendtick == 4: self.send_attempts += 1 if self.send_attempts >= self.max_attempts: self.send_attempts = 0 #print "[warning] message discarded" self.discardMessageFromQueue.emit() continue elif error == -1: self.parent.willDisconnect = True self.connectionError.emit('critical', 'Connection lost', "%s connection to server lost." % ("WebSocket" if self.parent.tcp.isWS else "TCP")) self.quit() return elif error == -3: self.parent.willDisconnect = True self.connectionError.emit('critical', 'Connection lost', "There was a critical connection failure. Please check your internet connection.\n\nDetails: %s." % total) self.quit() return else: self.send_attempts = 0 handlePackets(self, total) self.msleep(5) def stop(self): self.stop_now = True def handlePackets(caller, total, record=True): # Record the packet if demos enabled if record and caller.parent.demoRecorder: caller.parent.demoRecorder.record(total) for network in total: header = network[0] if header == 'MS': if len(network) < 15: print '[warning]', 'Malformed or incomplete MS packet was received' continue if isinstance(network[CHATMSG], unicode): network[CHATMSG] = decodeAOString(network[CHATMSG]) else: network[CHATMSG] = decodeAOString(network[CHATMSG].decode('utf-8')) caller.MS_Chat.emit(network) elif header == 'MC': del network[0] music = decodeAOString(network[0]) if not music: return charid = int(network[1]) showname = decodeAOString(network[2].decode("utf-8")) if len(network) > 2 else None caller.playMusicRequested.emit(music, charid, showname) # TODO: TEMPORARY, TRYING TO FIND A WAY TO GET THIS OUT OF HERE WITHOUT FREEZING THE GUI THREAD caller.parent.playMusic(music) elif header == 'BN': caller.newBackground.emit(network[1].lower(), True) elif header == 'CT': name = decodeAOString(network[1].decode('utf-8')) chatmsg = decodeAOString(network[2].decode('utf-8').replace("\n", "
")) caller.OOC_Log.emit("%s: %s" % (name, chatmsg.replace("<", "<").replace("<br />","
") if len(network) > 3 and network[3] == "0" else chatmsg)) elif header == 'PV': caller.newChar.emit(int(network[3])) elif header == 'LE': del network[0] caller.loadAllEvidence.emit([evi.split('&') for evi in network]) elif header == 'CharsCheck': del network[0] caller.charSlots.emit(network) elif header == 'RT': testimony = network[1] variant = int(network[2]) if testimony == 'judgeruling' else 0 caller.parent.wtceSignal.emit(testimony, variant) elif header == 'HP': kind = int(network[1]) health = int(network[2]) caller.parent.healthbars.emit(kind, health) elif header == 'AUTH': # login status (AO 2.9) caller.authStatusChanged.emit(int(network[1])) # elif header == "CHECK": #ping # pingafter = time.time() # caller.parent.gotPing.emit(int((pingafter - pingbefore)*1000)) elif header == 'DONE': caller.showCharSelect.emit() elif header == 'PR': del network[0] caller.updatePlayerList.emit(network[0], 0, int(network[1]), "") elif header == 'PU': del network[0] caller.updatePlayerList.emit(network[0], 1, int(network[1]), network[2].decode('utf-8')) elif header == 'ARUP': del network[0] caller.updateAreaList.emit(network) elif header == 'TI': del network[0] timer_id = int(network[0]) command = int(network[1]) time_ms = 0 if len(network) == 3: time_ms = int(network[2]) caller.timerUpdate.emit(command, timer_id, time_ms) elif header == 'SC': # Used in demos del network[0] caller.setCharList.emit([[char.split('&')[0].decode('utf-8'), 0, "male", True] for char in network]) elif header == 'KK': reason = network[1] caller.connectionError.emit('critical', 'Connection lost', 'You were kicked from the server. (%s)' % reason) elif header == 'KB': reason = network[1] caller.connectionError.emit('critical', 'Connection lost', 'You have been banned from the server. (%s)' % reason) elif header == 'BB': # message popup (AO 2.9) message = network[1] caller.connectionError.emit('information', 'Message from server', message) elif header == 'ZZ': caller.modCall.emit(network[1].replace("\n", "
"))