From 6044f350ddbd8148519da2b3d76c0432ba21de2a Mon Sep 17 00:00:00 2001
From: cidoku <git@cidoku.net>
Date: Thu, 19 Jun 2025 18:22:55 -0400
Subject: [PATCH] preliminary wss support

---
 AOsocket.py | 14 ++++++++++++--
 README.md   |  1 +
 mainmenu.py | 49 +++++++++++++++++++++++++++----------------------
 3 files changed, 40 insertions(+), 24 deletions(-)

diff --git a/AOsocket.py b/AOsocket.py
index fa0952b..c8c0010 100644
--- a/AOsocket.py
+++ b/AOsocket.py
@@ -2,6 +2,7 @@ import socket
 import platform
 
 import websocket
+import ssl
 
 from constants import *
 
@@ -63,9 +64,18 @@ class AOwebSocket(object):
             "User-Agent": "AO2XP %s, Python %s, %s %s %s" % (GAME_VERSION, platform.python_version(), platform.system(), platform.release(), platform.machine())
         }
 
-    def connect(self, ip, port):
+    def connect(self, ip, port, secure_port):
         try:
-            self.sock.connect("ws://%s:%s" % (ip, port), header=self.header)
+            if secure_port:
+                try:
+                    print "[debug]", "Trying secure websocket..."
+                    self.sock.connect("wss://%s:%s" % (ip, secure_port), header=self.header)
+                except:
+                    print "[debug]", "Connecting to secure websocket failed. Trying websocket..."
+                    self.sock.connect("ws://%s:%s" % (ip, port), header=self.header)
+            else:
+                print "[debug]", "Trying websocket..."
+                self.sock.connect("ws://%s:%s" % (ip, port), header=self.header)
         except:
             return False
 
diff --git a/README.md b/README.md
index dfd673b..0f11911 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@ Features added since the last commit of [Headshot's AO2XP](https://github.com/he
 - Demo recording and playback with a seekbar, no need to send messages to OOC
 - Pair with players directly from the player list
 - Playing music over HTTPS (on systems where the BASS library can't do it on its own)
+- Connections over secure websockets (wss)
 - FLAC music support
 - MIDI music support (needs a soundfount file named `gm.sf2` in the root AO2XP directory)
 - Module music support (MOD, XM, IT, S3M)
diff --git a/mainmenu.py b/mainmenu.py
index 53e0d8f..1b50d18 100644
--- a/mainmenu.py
+++ b/mainmenu.py
@@ -118,7 +118,7 @@ class lobby(QtGui.QWidget):
         self.onlineplayers = QtGui.QLabel(self)
         self.onlineplayers.setStyleSheet('color: white')
         self.onlineplayers.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignTop)
-        self.onlineplayers.setText(random.choice(['hi', 'oh, welcome back', 'hello', 'click on a server to begin', 'yo, how you doing?']))
+        self.onlineplayers.setText(random.choice(['Hi', 'Oh, welcome back', 'Hello', 'Click on a server to begin', 'Yo, how are you doing?']))
         self.onlineplayers.move(336, 91)
         self.onlineplayers.resize(173, 16)
         
@@ -196,8 +196,8 @@ class lobby(QtGui.QWidget):
         self.masterserver.start()
         
         if not demo and self.autoconnect:
-            self.aoserverinfo.setIP(self.autoconnect[-1], self.autoconnect[0], self.autoconnect[1], self.autoconnect[2])
-            print '[debug]', 'Connecting automatically to ip: ' + self.autoconnect[0] + ', port: ' + str(self.autoconnect[1]) + ", websocket port: " + str(self.autoconnect[2])
+            self.aoserverinfo.setIP(self.autoconnect[-1], self.autoconnect[0], self.autoconnect[1], self.autoconnect[2], self.autoconnect[3])
+            print '[debug]', 'Connecting automatically to ip: ' + self.autoconnect[0] + ', port: ' + str(self.autoconnect[1]) + ", websocket port: " + str(self.autoconnect[2]) + ", websocket port: " + str(self.autoconnect[3])
             self.aoserverinfo.stop()
             self.aoserverinfo.start()
 
@@ -218,10 +218,11 @@ class lobby(QtGui.QWidget):
             ip = server["ip"]
             port = server["port"]
             ws_port = server["ws_port"] if "ws_port" in server else 0
+            wss_port = server["wss_port"] if "wss_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, ws_port))
+            self.actual_serverlist.append((ip, port, name, desc, ws_port, wss_port))
 
     def moveToGame(self, stuff):
         tcp, playerlist, charlist, musiclist, background, evidence, areas, features, joinooc, hplist, webAO_bucket = stuff
@@ -283,15 +284,16 @@ class lobby(QtGui.QWidget):
                 ip = self.actual_serverlist[i][0]
                 port = str(self.actual_serverlist[i][1])
                 name = self.actual_serverlist[i][2]
-                ws = self.actual_serverlist[i][-1]
+                ws = self.actual_serverlist[i][4]
+                ws = self.actual_serverlist[i][5]
 
         for sv in self.favoriteslist:
             if sv[0] == ip and sv[1] == port:
                 return QtGui.QMessageBox.information(self, "Error", "This server already exists in your favorites list, named '%s'" % sv[2])
         
-        self.favoriteslist.append([ip, port, ws, name])
+        self.favoriteslist.append([ip, port, ws, wss, name])
         with open(AOpath+'serverlist.txt', "a") as file:
-            file.write("%s:%s:%s:%s\n" % (ip, port, ws, name))
+            file.write("%s:%s:%s:%s:%s\n" % (ip, port, ws, wss, name))
             file.close()
 
     def onClicked_connect(self):
@@ -341,12 +343,11 @@ 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][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])
+                    self.aoserverinfo.setIP(text, self.actual_serverlist[i][0], self.actual_serverlist[i][1], self.actual_serverlist[i][4], self.actual_serverlist[i][5])
+                    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][4]) + ", secure websocket port: " + str(self.actual_serverlist[i][5])
                 elif self.tab == 1:
-                    self.aoserverinfo.setIP(text, self.favoriteslist[i][0], self.favoriteslist[i][1], self.favoriteslist[i][2])
-                    print '[debug]', 'ind: ' + str(i) + ', ip: ' + self.favoriteslist[i][0] + ', port: ' + str(self.favoriteslist[i][1]) + ", websocket port: " + str(self.favoriteslist[i][2])
-
+                    self.aoserverinfo.setIP(text, self.favoriteslist[i][0], self.favoriteslist[i][1], self.favoriteslist[i][2], self.favoriteslist[i][3])
+                    print '[debug]', 'ind: ' + str(i) + ', ip: ' + self.favoriteslist[i][0] + ', port: ' + str(self.favoriteslist[i][1]) + ", websocket port: " + str(self.favoriteslist[i][2]) + ", secure websocket port: " + str(self.favoriteslist[i][3])
                 self.aoserverinfo.stop()
                 self.aoserverinfo.start()
 
@@ -404,15 +405,18 @@ class AOServerInfo(QtCore.QThread):
         self.ip = ""
         self.port = 0
         self.ws_port = 0
+        self.wss_port = 0
         self.name = "jm"
         self.webAO_bucket = ""
         self.useWS = False
+        self.connected = False
         self.disconnect = False
 
-    def setIP(self, name, ip, port, ws_port=0):
+    def setIP(self, name, ip, port, ws_port=0, wss_port=0):
         self.ip = ip
         self.port = int(port)
         self.ws_port = int(ws_port)
+        self.wss_port = int(wss_port)
         self.name = name
 
     def stop(self):
@@ -423,21 +427,21 @@ class AOServerInfo(QtCore.QThread):
 
     def run(self):
         self.disconnect = False
-        self.tcp = AOsocket.AOwebSocket()
+        
         try:
-            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)
+            if self.ws_port == 0 and self.wss_port == 0: raise Exception # make it jump to except: and use TCP
+            self.tcp = AOsocket.AOwebSocket()
+            self.connected = self.tcp.connect(self.ip, self.ws_port, self.wss_port)
         except:
             self.tcp = AOsocket.AOtcpSocket()
             try:
-                print "[debug]", "trying TCP..."
-                self.tcp.connect(self.ip, self.port)
+                print "[debug]", "Trying TCP..."
+                self.connected = self.tcp.connect(self.ip, self.port)
             except:
-                self.setOnlinePlayers.emit("couldn't retrieve players")
+                self.setOnlinePlayers.emit("Couldn't retrieve players")
                 return
 
-        print "[debug]", "connected! websocket: %s" % self.tcp.isWS
+        print "[debug]", "Connected! websocket: %s" % self.tcp.isWS
         self.tcp.sock.settimeout(0.1)
 
         got_stuff = False
@@ -498,7 +502,8 @@ class AOServerInfo(QtCore.QThread):
                     got_stuff = True
                 
                 elif header == "decryptor":
-                    self.tcp.send("HI#AO2XP %s#%%" % hardware.get_hdid())
+                    #self.tcp.send("HI#AO2XP %s#%%" % hardware.get_hdid())
+                    self.tcp.send("HI#%s#%%" % hardware.get_hdid())
 
                 elif header == "ASS": # ha ha ha...
                     self.webAO_bucket = network[1]