From a2ad80303d75df9e1305e94e22bc225e86a4b1e6 Mon Sep 17 00:00:00 2001 From: headshot2017 <> Date: Sun, 27 Mar 2022 21:01:18 -0400 Subject: [PATCH] websocket support --- AOsocket.py | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ gameview.py | 39 ++++++--------------- mainmenu.py | 80 +++++++++++++++++++----------------------- quicksetup.py | 7 ++-- 4 files changed, 144 insertions(+), 78 deletions(-) create mode 100644 AOsocket.py diff --git a/AOsocket.py b/AOsocket.py new file mode 100644 index 0000000..5c17a8d --- /dev/null +++ b/AOsocket.py @@ -0,0 +1,96 @@ +import socket +import platform + +import websocket + +from game_version import * + + +class AOtcpSocket(object): + def __init__(self): + self.sock = socket.socket() + self.isWS = False + + def connect(self, ip, port): + try: + self.sock.connect((ip, port)) + except: + return False + + self.sock.settimeout(0.1) + return True + + def recv(self): + tempdata = "" + gotIt = False + while not gotIt: + try: + contents = self.sock.recv(16384) + except (socket.timeout, socket.error) as err: + error = err.args[0] + if error == "timed out" or error == 10035 or err.errno == 11: + return -2, [] + else: # connection lost + return -1, [] + + if not contents.endswith("%"): + tempdata += contents + continue + else: + gotIt = True + if tempdata: + contents = tempdata + contents + tempdata = "" + + totals = contents.split('%') + del totals[-1] + for i in range(len(totals)): + totals[i] = totals[i].split("#") + del totals[i][-1] + + return 0, totals + + def send(self, data): + return self.sock.send(data) + + def close(self): + self.sock.close() + +class AOwebSocket(object): + def __init__(self): + self.sock = websocket.WebSocket() + self.isWS = True + self.header = { + "User-Agent": "AO2XP %s, Python %s, %s %s %s %s %s" % (GAME_VERSION, platform.python_version(), platform.system(), platform.release(), platform.version(), platform.machine(), platform.processor()) + } + + def connect(self, ip, port): + try: + self.sock.connect("ws://%s:%s" % (ip, port), header=self.header) + except: + return False + + self.sock.settimeout(0.1) + return True + + def recv(self): + try: + contents = self.sock.recv() + except websocket.WebSocketTimeoutException: + return -2, [] + except websocket.WebSocketConnectionClosedException: + return -1, [] + + totals = contents.split('%') + del totals[-1] + for i in range(len(totals)): + totals[i] = totals[i].split("#") + del totals[i][-1] + + return 0, totals + + def send(self, data): + return self.sock.send(str(data)) + + def close(self): + self.sock.close() diff --git a/gameview.py b/gameview.py index d5fcc87..55388fa 100644 --- a/gameview.py +++ b/gameview.py @@ -1,4 +1,4 @@ -import socket, thread, time, os, buttons, urllib, charselect, ini, random +import thread, time, os, buttons, urllib, charselect, ini, random from os.path import exists from ConfigParser import ConfigParser @@ -6,6 +6,7 @@ from pybass_constants import * from PyQt4 import QtGui, QtCore from functools import partial +import AOsocket import images AOpath = "base/" @@ -2486,7 +2487,6 @@ class gui(QtGui.QWidget): self.soundslider.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Sound volume", 100)) self.blipslider.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Blip volume", 100)) - self.tcp.settimeout(0.1) #thread.start_new_thread(self.tcp_thread, ()) self.tcpthread = TCP_Thread(self) self.tcpthread.MS_Chat.connect(self.netmsg_ms) @@ -2747,38 +2747,19 @@ class TCP_Thread(QtCore.QThread): if self.parent.msgqueue and not sendtick: self.parent.tcp.send(self.parent.msgqueue[0]) sendtick = 4 - - try: - contents = self.parent.tcp.recv(8192) - except (socket.timeout, socket.error) as err: - error = err.args[0] - if error == "timed out" or error == 10035: - continue - else: - self.parent.emit(QtCore.SIGNAL('showMessage(QString, QString, QString)'), 'critical', 'Connection lost', str(err)) - self.parent.willDisconnect = True - self.quit() - return - if not contents: - self.parent.emit(QtCore.SIGNAL('showMessage(QString, QString, QString)'), 'critical', 'Connection lost', 'the server closed the connection') + error, total = self.parent.tcp.recv() + if error == -2: + continue + elif error == -1: + self.parent.emit(QtCore.SIGNAL('showMessage(QString, QString, QString)'), 'critical', 'Connection lost', "%s connection to server lost." % ("WebSocket" if self.parent.tcp.isWS else "TCP")) self.parent.willDisconnect = True self.quit() return - - if not contents.endswith("%"): - tempdata += contents - continue - else: - if tempdata: - contents = tempdata + contents - tempdata = "" - - total = contents.split('%') - for msg in total: - network = msg.split('#') + + for network in total: header = network[0] - del network[-1] + #del network[-1] if header == 'MS': if len(network) < 15: print '[warning]', 'malformed/incomplete MS#chat (IC chat) network message was received' diff --git a/mainmenu.py b/mainmenu.py index 8f115b1..2f51ada 100644 --- a/mainmenu.py +++ b/mainmenu.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -import socket, thread, time, random, traceback, requests, json +import thread, time, random, traceback, requests, json from os.path import exists from PyQt4 import QtGui, QtCore import hardware +import AOsocket from game_version import * AOpath = "base/" @@ -199,10 +200,11 @@ class lobby(QtGui.QWidget): desc = server["description"] ip = server["ip"] port = server["port"] + ws_port = server["ws_port"] if "ws_port" in server else 0 serveritem = QtGui.QListWidgetItem(name) if self.tab == 0: self.serverlist.addItem(serveritem) - self.actual_serverlist.append((ip, port, name, desc)) + self.actual_serverlist.append((ip, port, name, desc, ws_port)) def moveToGame(self, stuff): tcp, charlist, musiclist, background, evidence, areas, features, joinooc, hplist, webAO_bucket = stuff @@ -315,8 +317,8 @@ class lobby(QtGui.QWidget): if self.serverlist.item(i) == item: if self.tab == 0: self.serverinfo.setText(self.actual_serverlist[i][3]) - self.aoserverinfo.setIP(text, *self.actual_serverlist[i][:2]) - print '[debug]', 'ind: ' + str(i) + ', ip: ' + self.actual_serverlist[i][0] + ', port: ' + str(self.actual_serverlist[i][1]) + self.aoserverinfo.setIP(text, self.actual_serverlist[i][0], self.actual_serverlist[i][1], self.actual_serverlist[i][-1]) + print '[debug]', 'ind: ' + str(i) + ', ip: ' + self.actual_serverlist[i][0] + ', port: ' + str(self.actual_serverlist[i][1]) + ", websocket port: " + str(self.actual_serverlist[i][-1]) elif self.tab == 1: self.aoserverinfo.setIP(text, *self.favoriteslist[i][:2]) print '[debug]', 'ind: ' + str(i) + ', ip: ' + self.favoriteslist[i][0] + ', port: ' + str(self.favoriteslist[i][1]) @@ -417,13 +419,16 @@ class AOServerInfo(QtCore.QThread): super(AOServerInfo, self).__init__() self.ip = "" self.port = 0 + self.ws_port = 0 self.name = "jm" self.webAO_bucket = "" + self.useWS = False self.disconnect = False - def setIP(self, name, ip, port): + def setIP(self, name, ip, port, ws_port=0): self.ip = ip self.port = int(port) + self.ws_port = int(ws_port) self.name = name def stop(self): @@ -434,15 +439,24 @@ class AOServerInfo(QtCore.QThread): def run(self): self.disconnect = False - self.tcp = socket.socket() + self.tcp = AOsocket.AOwebSocket() try: - self.tcp.connect((self.ip, self.port)) + if self.ws_port == 0: raise Exception # make it jump to except: and use TCP + print "[debug]", "trying websocket..." + self.tcp.connect(self.ip, self.ws_port) except: - self.setOnlinePlayers.emit("couldn't retrieve players") - return - self.tcp.settimeout(0.2) + self.tcp = AOsocket.AOtcpSocket() + try: + print "[debug]", "trying TCP..." + self.tcp.connect(self.ip, self.port) + except: + self.setOnlinePlayers.emit("couldn't retrieve players") + return - tempdata = "" + print "[debug]", "connected! websocket: %s" % self.tcp.isWS + self.tcp.sock.settimeout(0.1) + + got_stuff = False gotChars = False hplist = [] joinooc = [] @@ -477,27 +491,14 @@ class AOServerInfo(QtCore.QThread): self.setWindowTitle.emit("AO2XP: "+self.name) return - try: - contents = self.tcp.recv(16384) - except (socket.timeout, socket.error) as err: - error = err.args[0] - if error == "timed out" or error == 10035: - continue - else: - self.setOnlinePlayers.emit("Something wrong happened" if not got_stuff else "Connection lost") - return - - if not contents.endswith("%"): - tempdata += contents + error, totals = self.tcp.recv() + if error == -2: # timeout continue - else: - if tempdata: - contents = tempdata + contents - tempdata = "" + elif error == -1: # disconnected + self.setOnlinePlayers.emit("Something wrong happened" if not got_stuff else "Connection lost") + return - totals = contents.split('%') - for g in totals: - network = g.split('#') + for network in totals: header = network[0] if header == 'PN': @@ -543,7 +544,6 @@ class AOServerInfo(QtCore.QThread): if self.disconnect: continue del network[0] - del network[len(network)-1] gotChars = True charlist = [ [char.split('&')[0], 0, "male"] for char in network ] self.setConnectProgress.emit('Requesting music list (%d)...' % maxmusic) @@ -554,7 +554,6 @@ class AOServerInfo(QtCore.QThread): if self.disconnect: continue del network[0] - del network[len(network)-1] musiclist = [music for music in network] @@ -565,8 +564,7 @@ class AOServerInfo(QtCore.QThread): elif header == 'CharsCheck': if self.disconnect or not gotChars: continue - network.pop(0) - network.pop(len(network)-1) + del network[0] for i in range(len(network)): charlist[i][1] = int(network[i]) @@ -580,7 +578,6 @@ class AOServerInfo(QtCore.QThread): if self.disconnect: continue del network[0] - del network[len(network)-1] if len(network) > 0: evidence = [evi.split("&") for evi in network] else: @@ -597,15 +594,12 @@ class AOServerInfo(QtCore.QThread): elif header == 'HP': if self.disconnect: continue - del network[0] - del network[len(network)-1] - type = int(network[0]) - health = int(network[1]) + type = int(network[1]) + health = int(network[2]) hplist.append([type, health]) elif header == "ARUP": #AO2 2.6 new feature: area update del network[0] - del network[len(network)-1] type = int(network[0]) if type == 0: #player count areas[type] = [network[i] for i in range(1, len(network))] @@ -623,8 +617,6 @@ class AOServerInfo(QtCore.QThread): elif header == 'CT': if self.disconnect: continue - del network[0] - del network[len(network)-1] - name = network[0].decode("utf-8").replace('', '$').replace('', '%').replace('', '&').replace('', '#').replace('', '#') - chatmsg = network[1].decode("utf-8").replace('', '$').replace('', '%').replace('', '&').replace('', '#').replace('', '#') + name = network[1].decode("utf-8").replace('', '$').replace('', '%').replace('', '&').replace('', '#').replace('', '#') + chatmsg = network[2].decode("utf-8").replace('', '$').replace('', '%').replace('', '&').replace('', '#').replace('', '#') joinooc.append("%s: %s" % (name, chatmsg)) \ No newline at end of file diff --git a/quicksetup.py b/quicksetup.py index 95a09fd..aa39f7a 100644 --- a/quicksetup.py +++ b/quicksetup.py @@ -6,11 +6,11 @@ import os import platform def pip_install(*args): + print "installing " + ", ".join(args) command = [sys.executable, "-m", "pip", "install"] command.extend(args) subprocess.call(command) -print "installing requests" pip_install('requests') import requests @@ -72,7 +72,6 @@ zip_ref = zipfile.ZipFile(BASSOPUSZIP, 'r') zip_ref.extract(BASSOPUSDLL) zip_ref.close() -print "installing apng, six, appdirs, packaging" pip_install("apng", "six", "appdirs", "packaging") try: @@ -80,17 +79,15 @@ try: if Image.__version__ != "5.3.0": jm = raw_input("Pillow version 5.3.0 is recommended for compatibility with AO2XP; You have version %s\nReplace with version 5.3.0? (Y/N) > " % Image.__version__).lower() if jm == "y": - print "installing Pillow 5.3.0" pip_install("Pillow==5.3.0") else: print "Pillow 5.3.0 already exists, skipping" except ImportError: - print "installing Pillow 5.3.0" pip_install("Pillow==5.3.0") -print "installing pyinstaller" pip_install('pyinstaller==3.6') +pip_install("websocket-client") if platform.system() == "Windows": print "downloading pyqt4"