AO2XP/packets.py

204 lines
7.9 KiB
Python

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", "<br />"))
caller.OOC_Log.emit("<b>%s:</b> %s" % (name, chatmsg.replace("<", "&lt;").replace("&lt;br />","<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", "<br />"))