mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-15 01:24:45 +02:00
Minor fixes for lobby
This commit is contained in:
@ -74,7 +74,13 @@ void SocketLobby::connected()
|
|||||||
isConnected = true;
|
isConnected = true;
|
||||||
emit text("Connected!");
|
emit text("Connected!");
|
||||||
|
|
||||||
const QString greetingConst = ProtocolStrings[GREETING].arg(username, QString::fromStdString(GameConstants::VCMI_VERSION));
|
QByteArray greetingBytes;
|
||||||
|
greetingBytes.append(ProtocolVersion);
|
||||||
|
greetingBytes.append(ProtocolEncoding.size());
|
||||||
|
const QString greetingConst = QString(greetingBytes)
|
||||||
|
+ ProtocolStrings[GREETING].arg(QString::fromStdString(ProtocolEncoding),
|
||||||
|
username,
|
||||||
|
QString::fromStdString(GameConstants::VCMI_VERSION));
|
||||||
send(greetingConst);
|
send(greetingConst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
#include <QAbstractSocket>
|
#include <QAbstractSocket>
|
||||||
|
|
||||||
|
const unsigned int ProtocolVersion = 1;
|
||||||
|
const std::string ProtocolEncoding = "utf8";
|
||||||
|
|
||||||
class ProtocolError: public std::runtime_error
|
class ProtocolError: public std::runtime_error
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -23,7 +26,7 @@ enum ProtocolConsts
|
|||||||
const QMap<ProtocolConsts, QString> ProtocolStrings
|
const QMap<ProtocolConsts, QString> ProtocolStrings
|
||||||
{
|
{
|
||||||
//client consts
|
//client consts
|
||||||
{GREETING, "<GREETINGS>%1<VER>%2"},
|
{GREETING, "%1<GREETINGS>%2<VER>%3"}, //protocol_version byte, encoding bytes, encoding, name, version
|
||||||
{USERNAME, "<USER>%1"},
|
{USERNAME, "<USER>%1"},
|
||||||
{MESSAGE, "<MSG>%1"},
|
{MESSAGE, "<MSG>%1"},
|
||||||
{CREATE, "<NEW>%1<PSWD>%2<COUNT>%3"},
|
{CREATE, "<NEW>%1<PSWD>%2<COUNT>%3"},
|
||||||
|
112
proxyServer.py
112
proxyServer.py
@ -7,6 +7,9 @@ import uuid
|
|||||||
import struct
|
import struct
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
PROTOCOL_VERSION_MIN = 1
|
||||||
|
PROTOCOL_VERSION_MAX = 1
|
||||||
|
|
||||||
# server's IP address
|
# server's IP address
|
||||||
SERVER_HOST = "0.0.0.0"
|
SERVER_HOST = "0.0.0.0"
|
||||||
SERVER_PORT = 5002 # port we want to use
|
SERVER_PORT = 5002 # port we want to use
|
||||||
@ -42,12 +45,10 @@ client_sockets = dict()
|
|||||||
|
|
||||||
|
|
||||||
class GameConnection:
|
class GameConnection:
|
||||||
server: socket
|
server: socket # socket to vcmiserver
|
||||||
client: socket
|
client: socket # socket to vcmiclient
|
||||||
serverInit = False
|
serverInit = False # if vcmiserver already connected
|
||||||
clientInit = False
|
clientInit = False # if vcmiclient already connected
|
||||||
messageQueueIn = []
|
|
||||||
messageQueueOut = []
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.server = None
|
self.server = None
|
||||||
@ -55,16 +56,16 @@ class GameConnection:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class Session:
|
class Session:
|
||||||
total = 1
|
total = 1 # total amount of players
|
||||||
joined = 0
|
joined = 0 # amount of players joined to the session
|
||||||
password = ""
|
password = "" # password to connect
|
||||||
protected = False
|
protected = False # if True, password is required to join to the session
|
||||||
name: str
|
name: str # name of session
|
||||||
host: socket
|
host: socket # player socket who created the session (lobby mode)
|
||||||
host_uuid: str
|
host_uuid: str # uuid of vcmiserver for hosting player
|
||||||
players = []
|
players = [] # list of sockets of players, joined to the session
|
||||||
connections = []
|
connections = [] # list of GameConnections for vcmiclient (game mode)
|
||||||
started = False
|
started = False # True - game mode, False - lobby mode
|
||||||
|
|
||||||
def __init__(self, host: socket, name: str) -> None:
|
def __init__(self, host: socket, name: str) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -87,17 +88,15 @@ class Session:
|
|||||||
self.players.remove(player)
|
self.players.remove(player)
|
||||||
self.joined -= 1
|
self.joined -= 1
|
||||||
|
|
||||||
def addConnection(self, conn: socket, isServer: bool) -> GameConnection:
|
def addConnection(self, conn: socket, isServer: bool):
|
||||||
#find uninitialized server connection
|
#find uninitialized server connection
|
||||||
for gc in self.connections:
|
for gc in self.connections:
|
||||||
if isServer and not gc.serverInit:
|
if isServer and not gc.serverInit:
|
||||||
gc.server = conn
|
gc.server = conn
|
||||||
gc.serverInit = True
|
gc.serverInit = True
|
||||||
return gc
|
|
||||||
if not isServer and not gc.clientInit:
|
if not isServer and not gc.clientInit:
|
||||||
gc.client = conn
|
gc.client = conn
|
||||||
gc.clientInit = True
|
gc.clientInit = True
|
||||||
return gc
|
|
||||||
|
|
||||||
#no existing connection - create the new one
|
#no existing connection - create the new one
|
||||||
gc = GameConnection()
|
gc = GameConnection()
|
||||||
@ -108,7 +107,6 @@ class Session:
|
|||||||
gc.client = conn
|
gc.client = conn
|
||||||
gc.clientInit = True
|
gc.clientInit = True
|
||||||
self.connections.append(gc)
|
self.connections.append(gc)
|
||||||
return gc
|
|
||||||
|
|
||||||
def validPipe(self, conn) -> bool:
|
def validPipe(self, conn) -> bool:
|
||||||
for gc in self.connections:
|
for gc in self.connections:
|
||||||
@ -161,7 +159,7 @@ def handleDisconnection(client: socket):
|
|||||||
def send(client: socket, message: str):
|
def send(client: socket, message: str):
|
||||||
sender = client_sockets[client]
|
sender = client_sockets[client]
|
||||||
if "valid" not in sender or sender["valid"]:
|
if "valid" not in sender or sender["valid"]:
|
||||||
client.send(message.encode())
|
client.send(message.encode(errors='replace'))
|
||||||
|
|
||||||
|
|
||||||
def broadcast(clients: list, message: str):
|
def broadcast(clients: list, message: str):
|
||||||
@ -221,57 +219,51 @@ def dispatch(client: socket, sender: dict, arr: bytes):
|
|||||||
if arr == None or len(arr) == 0:
|
if arr == None or len(arr) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
#if len(sender["prevmessages"]):
|
|
||||||
# arr = sender["prevmessages"] + arr
|
|
||||||
# sender["prevmessages"] = bytes()
|
|
||||||
|
|
||||||
#check for game mode connection
|
#check for game mode connection
|
||||||
msg = str(arr)
|
msg = str(arr)
|
||||||
if msg.find("Aiya!") != -1:
|
if msg.find("Aiya!") != -1:
|
||||||
sender["pipe"] = True
|
sender["pipe"] = True #switch to pipe mode
|
||||||
|
|
||||||
if sender["pipe"]:
|
if sender["pipe"]:
|
||||||
if sender["game"]:
|
if sender["game"]: #if already playing - sending raw bytes as is
|
||||||
sender["prevmessages"].append(arr)
|
sender["prevmessages"].append(arr)
|
||||||
else:
|
else:
|
||||||
sender["prevmessages"].append(struct.pack('<I', len(arr)) + arr)
|
sender["prevmessages"].append(struct.pack('<I', len(arr)) + arr) #pack message
|
||||||
|
#search fo application type in the message
|
||||||
match = re.search(r"\((\w+)\)", msg)
|
match = re.search(r"\((\w+)\)", msg)
|
||||||
_appType = ''
|
_appType = ''
|
||||||
if match != None:
|
if match != None:
|
||||||
_appType = match.group(1)
|
_appType = match.group(1)
|
||||||
sender["apptype"] = _appType
|
sender["apptype"] = _appType
|
||||||
|
|
||||||
|
#extract uuid from message
|
||||||
_uuid = arr.decode()
|
_uuid = arr.decode()
|
||||||
if not _uuid == '' and not sender["apptype"] == '':
|
if not _uuid == '' and not sender["apptype"] == '':
|
||||||
#search for uuid
|
#search for uuid
|
||||||
for session in sessions.values():
|
for session in sessions.values():
|
||||||
if session.started:
|
if session.started:
|
||||||
|
#verify uuid of connected application
|
||||||
if _uuid.find(session.host_uuid) != -1 and sender["apptype"] == "server":
|
if _uuid.find(session.host_uuid) != -1 and sender["apptype"] == "server":
|
||||||
gc = session.addConnection(client, True)
|
session.addConnection(client, True)
|
||||||
#send_msg(gc.server, gc.messageQueueIn)
|
|
||||||
#gc.messageQueueIn = bytes()
|
|
||||||
sender["session"] = session
|
sender["session"] = session
|
||||||
sender["game"] = True
|
sender["game"] = True
|
||||||
#read boolean flag for the endian
|
#read boolean flag for the endian
|
||||||
|
# this is workaround to send only one remaining byte
|
||||||
|
# WARNING: reversed byte order is not supported
|
||||||
sender["prevmessages"].append(client.recv(1))
|
sender["prevmessages"].append(client.recv(1))
|
||||||
#if not gc.clientInit:
|
|
||||||
# gc.messageQueueOut += arr
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if sender["apptype"] == "client":
|
if sender["apptype"] == "client":
|
||||||
for p in session.players:
|
for p in session.players:
|
||||||
if _uuid.find(client_sockets[p]["uuid"]) != -1:
|
if _uuid.find(client_sockets[p]["uuid"]) != -1:
|
||||||
#client connection
|
#client connection
|
||||||
gc = session.addConnection(client, False)
|
session.addConnection(client, False)
|
||||||
#send_msg(gc.client, gc.messageQueueOut)
|
|
||||||
#gc.messageQueueOut = bytes()
|
|
||||||
sender["session"] = session
|
sender["session"] = session
|
||||||
sender["game"] = True
|
sender["game"] = True
|
||||||
#read boolean flag for the endian
|
#read boolean flag for the endian
|
||||||
|
# this is workaround to send only one remaining byte
|
||||||
|
# WARNING: reversed byte order is not supported
|
||||||
sender["prevmessages"].append(client.recv(1))
|
sender["prevmessages"].append(client.recv(1))
|
||||||
#if not gc.serverInit:
|
|
||||||
# gc.messageQueueIn += arr
|
|
||||||
# return
|
|
||||||
break
|
break
|
||||||
|
|
||||||
#game mode
|
#game mode
|
||||||
@ -287,14 +279,42 @@ def dispatch(client: socket, sender: dict, arr: bytes):
|
|||||||
opposite.sendall(x)
|
opposite.sendall(x)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[!] Error: {e}")
|
print(f"[!] Error: {e}")
|
||||||
|
#TODO: handle disconnection
|
||||||
|
|
||||||
sender["prevmessages"].clear()
|
sender["prevmessages"].clear()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
#we are in pipe mode but game still not started - waiting other clients to connect
|
||||||
if sender["pipe"]:
|
if sender["pipe"]:
|
||||||
return
|
return
|
||||||
|
|
||||||
#lobby mode
|
#lobby mode
|
||||||
msg = arr.decode()
|
if not sender["auth"]:
|
||||||
|
if len(arr) < 2:
|
||||||
|
print("[!] Error: unknown client tries to connect")
|
||||||
|
#TODO: block address? close the socket?
|
||||||
|
return
|
||||||
|
|
||||||
|
# first byte is protocol version
|
||||||
|
sender["protocol_version"] = arr[0]
|
||||||
|
if arr[0] < PROTOCOL_VERSION_MIN or arr[0] > PROTOCOL_VERSION_MAX:
|
||||||
|
print(f"[!] Error: client has incompatbile protocol version {arr[0]}")
|
||||||
|
send(client, ":>>ERROR:Cannot connect to remote server due to protocol incompatibility")
|
||||||
|
return
|
||||||
|
|
||||||
|
# second byte is a encoding str size
|
||||||
|
if arr[1] == 0:
|
||||||
|
sender["encoding"] = "utf8"
|
||||||
|
else:
|
||||||
|
if len(arr) < arr[1] + 2:
|
||||||
|
send(client, ":>>ERROR:Protocol error")
|
||||||
|
return
|
||||||
|
|
||||||
|
sender["encoding"] = arr[2:(arr[1] + 2)].decode(errors='ignore')
|
||||||
|
arr = arr[(arr[1] + 2):]
|
||||||
|
msg = str(arr)
|
||||||
|
|
||||||
|
msg = arr.decode(encoding=sender["encoding"], errors='replace')
|
||||||
_open = msg.partition('<')
|
_open = msg.partition('<')
|
||||||
_close = _open[2].partition('>')
|
_close = _open[2].partition('>')
|
||||||
if _open[0] != '' or _open[1] == '' or _open[2] == '' or _close[0] == '' or _close[1] == '':
|
if _open[0] != '' or _open[1] == '' or _open[2] == '' or _close[0] == '' or _close[1] == '':
|
||||||
@ -309,7 +329,18 @@ def dispatch(client: socket, sender: dict, arr: bytes):
|
|||||||
if tag == "GREETINGS":
|
if tag == "GREETINGS":
|
||||||
if sender["auth"]:
|
if sender["auth"]:
|
||||||
print(f"[!] Greetings from authorized user {sender['username']} {sender['address']}")
|
print(f"[!] Greetings from authorized user {sender['username']} {sender['address']}")
|
||||||
|
send(client, ":>>ERROR:User already authorized")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if len(tag_value) < 3:
|
||||||
|
send(client, f":>>ERROR:Too short username {tag_value}")
|
||||||
|
return
|
||||||
|
|
||||||
|
for user in client_sockets:
|
||||||
|
if user['username'] == tag_value:
|
||||||
|
send(client, f":>>ERROR:Can't connect with the name {tag_value}. This login is already occpupied")
|
||||||
|
return
|
||||||
|
|
||||||
print(f"[*] User {sender['address']} autorized as {tag_value}")
|
print(f"[*] User {sender['address']} autorized as {tag_value}")
|
||||||
sender["username"] = tag_value
|
sender["username"] = tag_value
|
||||||
sender["auth"] = True
|
sender["auth"] = True
|
||||||
@ -431,7 +462,6 @@ def listen_for_client(cs):
|
|||||||
msg = cs.recv(4096)
|
msg = cs.recv(4096)
|
||||||
else:
|
else:
|
||||||
msg = recv_msg(cs)
|
msg = recv_msg(cs)
|
||||||
#msg = cs.recv(2048)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# client no longer connected
|
# client no longer connected
|
||||||
print(f"[!] Error: {e}")
|
print(f"[!] Error: {e}")
|
||||||
|
Reference in New Issue
Block a user