changed the packet code to be simpler and more flexible
This commit is contained in:
parent
58287a0649
commit
37350926b2
13 changed files with 136 additions and 76 deletions
5
NOTE.md
5
NOTE.md
|
@ -1,12 +1,11 @@
|
||||||
A faire :
|
A faire :
|
||||||
- Limite des bateaux, taille des pseudo, ...
|
|
||||||
|
|
||||||
- Nom dans les options
|
|
||||||
- Faire marcher le tchat
|
- Faire marcher le tchat
|
||||||
- Sauvegarde / Quitter
|
- Sauvegarde / Quitter
|
||||||
|
- Historique / Replay
|
||||||
- Police d'écriture
|
- Police d'écriture
|
||||||
- Documenter
|
- Documenter
|
||||||
|
|
||||||
|
|
||||||
- Voir si les event listener intégré à pyglet sont plus pratique que l'event propagation (?)
|
- Voir si les event listener intégré à pyglet sont plus pratique que l'event propagation (?)
|
||||||
- Faire une scène incluant par défaut les boutons "Retour" (?)
|
- Faire une scène incluant par défaut les boutons "Retour" (?)
|
||||||
|
|
||||||
|
|
|
@ -296,14 +296,18 @@ class RoomCreate(Scene):
|
||||||
port = int(self.input_port.text)
|
port = int(self.input_port.text)
|
||||||
|
|
||||||
settings = PacketSettings(
|
settings = PacketSettings(
|
||||||
username=self.input_username.text,
|
|
||||||
grid_width=int(self.input_width.text),
|
grid_width=int(self.input_width.text),
|
||||||
grid_height=int(self.input_height.text),
|
grid_height=int(self.input_height.text),
|
||||||
host_start=self.checkbox_host_start.state,
|
host_start=self.checkbox_host_start.state,
|
||||||
boats_length=[size for size, quantity in self.boat_size_amount.items() for _ in range(quantity)]
|
boats_length=[size for size, quantity in self.boat_size_amount.items() for _ in range(quantity)]
|
||||||
)
|
)
|
||||||
|
|
||||||
self.window.set_scene(RoomHost, port=port, settings=settings)
|
self.window.set_scene(
|
||||||
|
RoomHost,
|
||||||
|
port=port,
|
||||||
|
username=self.input_username.text,
|
||||||
|
settings=settings
|
||||||
|
)
|
||||||
|
|
||||||
def on_draw(self):
|
def on_draw(self):
|
||||||
self.batch_input_background.draw()
|
self.batch_input_background.draw()
|
||||||
|
|
|
@ -14,9 +14,10 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class RoomHost(Scene):
|
class RoomHost(Scene):
|
||||||
def __init__(self, window: "Window", port: int, settings: "PacketSettings", **kwargs):
|
def __init__(self, window: "Window", port: int, username: str, settings: "PacketSettings", **kwargs):
|
||||||
super().__init__(window, **kwargs)
|
super().__init__(window, **kwargs)
|
||||||
|
|
||||||
|
self.username: str = username
|
||||||
self.ip_address: str = "127.0.0.1"
|
self.ip_address: str = "127.0.0.1"
|
||||||
self.port: int = port
|
self.port: int = port
|
||||||
|
|
||||||
|
@ -59,7 +60,13 @@ class RoomHost(Scene):
|
||||||
batch=self.batch_label
|
batch=self.batch_label
|
||||||
)
|
)
|
||||||
|
|
||||||
self.thread_network = network.Host(window=self.window, daemon=True, port=self.port, settings=settings)
|
self.thread_network = network.Host(
|
||||||
|
window=self.window,
|
||||||
|
daemon=True,
|
||||||
|
port=self.port,
|
||||||
|
username=self.username,
|
||||||
|
settings=settings
|
||||||
|
)
|
||||||
self.thread_network.start()
|
self.thread_network.start()
|
||||||
|
|
||||||
self._refresh_ip_text()
|
self._refresh_ip_text()
|
||||||
|
@ -70,10 +77,10 @@ class RoomHost(Scene):
|
||||||
def _refresh_ip(self):
|
def _refresh_ip(self):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
response = requests.get('https://api.ipify.org')
|
response = requests.get('https://api.ipify.org', timeout=10)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
break
|
break
|
||||||
except requests.HTTPError:
|
except (requests.HTTPError, requests.Timeout):
|
||||||
if self.thread_ip.stopped: return
|
if self.thread_ip.stopped: return
|
||||||
|
|
||||||
self.ip_address: str = response.content.decode('utf8')
|
self.ip_address: str = response.content.decode('utf8')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from source.gui import scene
|
from source.gui import scene
|
||||||
from source.network import game_network
|
from source.network import game_network
|
||||||
from source.network.packet import PacketUsername
|
from source.network.packet import PacketUsername, PacketSettings
|
||||||
from source.network.packet.abc import Packet
|
from source.network.packet.abc import Packet
|
||||||
from source.utils import StoppableThread
|
from source.utils import StoppableThread
|
||||||
from source.utils.thread import in_pyglet_context
|
from source.utils.thread import in_pyglet_context
|
||||||
|
@ -34,8 +34,9 @@ class Client(StoppableThread):
|
||||||
|
|
||||||
print(f"[Client] Connecté avec {connection}")
|
print(f"[Client] Connecté avec {connection}")
|
||||||
|
|
||||||
settings: Any = Packet.from_connection(connection)
|
settings: Any = PacketSettings.from_connection(connection)
|
||||||
PacketUsername(username=self.username).send_connection(connection)
|
PacketUsername(username=self.username).instance_send_connection(connection)
|
||||||
|
packet_username = PacketUsername.from_connection(connection)
|
||||||
|
|
||||||
game_scene = in_pyglet_context(
|
game_scene = in_pyglet_context(
|
||||||
self.window.set_scene,
|
self.window.set_scene,
|
||||||
|
@ -45,7 +46,7 @@ class Client(StoppableThread):
|
||||||
|
|
||||||
boats_length=settings.boats_length,
|
boats_length=settings.boats_length,
|
||||||
name_ally=self.username,
|
name_ally=self.username,
|
||||||
name_enemy=settings.username,
|
name_enemy=packet_username.username,
|
||||||
grid_width=settings.grid_width,
|
grid_width=settings.grid_width,
|
||||||
grid_height=settings.grid_height,
|
grid_height=settings.grid_height,
|
||||||
my_turn=not settings.host_start
|
my_turn=not settings.host_start
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import socket
|
import socket
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from source.gui import scene
|
from source.gui import scene
|
||||||
from source.network import game_network
|
from source.network import game_network
|
||||||
from source.network.packet.abc import Packet
|
|
||||||
from source.utils import StoppableThread
|
from source.utils import StoppableThread
|
||||||
from source.utils.thread import in_pyglet_context
|
from source.utils.thread import in_pyglet_context
|
||||||
|
from source.network.packet import PacketUsername
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.gui.window import Window
|
from source.gui.window import Window
|
||||||
|
@ -17,10 +17,11 @@ class Host(StoppableThread):
|
||||||
The thread executed on the person who create a room.
|
The thread executed on the person who create a room.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, window: "Window", port: int, settings: "PacketSettings", **kw):
|
def __init__(self, window: "Window", port: int, username: str, settings: "PacketSettings", **kw):
|
||||||
super().__init__(**kw)
|
super().__init__(**kw)
|
||||||
|
|
||||||
self.window = window
|
self.window = window
|
||||||
|
self.username = username
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.port = port
|
self.port = port
|
||||||
|
|
||||||
|
@ -43,8 +44,9 @@ class Host(StoppableThread):
|
||||||
|
|
||||||
print(f"[Serveur] Connecté avec {address}")
|
print(f"[Serveur] Connecté avec {address}")
|
||||||
|
|
||||||
self.settings.send_connection(connection)
|
self.settings.instance_send_connection(connection)
|
||||||
packet_username: Any = Packet.from_connection(connection)
|
packet_username = PacketUsername.from_connection(connection)
|
||||||
|
PacketUsername(username=self.username).instance_send_connection(connection)
|
||||||
|
|
||||||
game_scene = in_pyglet_context(
|
game_scene = in_pyglet_context(
|
||||||
self.window.set_scene,
|
self.window.set_scene,
|
||||||
|
@ -53,7 +55,7 @@ class Host(StoppableThread):
|
||||||
connection=connection,
|
connection=connection,
|
||||||
|
|
||||||
boats_length=self.settings.boats_length,
|
boats_length=self.settings.boats_length,
|
||||||
name_ally=self.settings.username,
|
name_ally=self.username,
|
||||||
name_enemy=packet_username.username,
|
name_enemy=packet_username.username,
|
||||||
grid_width=self.settings.grid_width,
|
grid_width=self.settings.grid_width,
|
||||||
grid_height=self.settings.grid_height,
|
grid_height=self.settings.grid_height,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import socket
|
import socket
|
||||||
from typing import Any
|
from typing import Type, Callable
|
||||||
|
|
||||||
from source.gui.scene import Game
|
from source.gui.scene import Game
|
||||||
from source.network.packet.abc import Packet
|
from source.network.packet.abc import Packet
|
||||||
|
@ -21,7 +21,7 @@ def game_network(
|
||||||
:param connection: the connection with the other player
|
:param connection: the connection with the other player
|
||||||
"""
|
"""
|
||||||
|
|
||||||
game_methods = {
|
game_methods: dict[Type["Packet"], Callable] = {
|
||||||
packet.PacketChat: game_scene.network_on_chat,
|
packet.PacketChat: game_scene.network_on_chat,
|
||||||
packet.PacketBoatPlaced: game_scene.network_on_boat_placed,
|
packet.PacketBoatPlaced: game_scene.network_on_boat_placed,
|
||||||
packet.PacketBombPlaced: game_scene.network_on_bomb_placed,
|
packet.PacketBombPlaced: game_scene.network_on_bomb_placed,
|
||||||
|
@ -29,13 +29,15 @@ def game_network(
|
||||||
}
|
}
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
data: Any = Packet.from_connection(connection)
|
data_type: Type["Packet"] = Packet.type_from_connection(connection)
|
||||||
|
|
||||||
if data is None:
|
if data_type is None:
|
||||||
if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter
|
if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
data = data_type.from_connection(connection)
|
||||||
|
|
||||||
if in_pyglet_context(
|
if in_pyglet_context(
|
||||||
game_methods[type(data)], # récupère la methode relié ce type de donnée
|
game_methods[data_type], # récupère la methode relié ce type de donnée
|
||||||
connection, data
|
connection, data
|
||||||
): return # Appelle la méthode. Si elle renvoie True, arrête le thread
|
): return # Appelle la méthode. Si elle renvoie True, arrête le thread
|
||||||
|
|
|
@ -9,8 +9,6 @@ class PacketBoatPlaced(Packet):
|
||||||
A packet that signal that all the boat of the player have been placed
|
A packet that signal that all the boat of the player have been placed
|
||||||
"""
|
"""
|
||||||
|
|
||||||
packet_size: int = 0
|
|
||||||
|
|
||||||
def to_bytes(self):
|
def to_bytes(self):
|
||||||
return b""
|
return b""
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ class PacketBombPlaced(Packet):
|
||||||
|
|
||||||
position: Point2D = field()
|
position: Point2D = field()
|
||||||
|
|
||||||
packet_size: int = 2
|
|
||||||
packet_format: str = ">BB"
|
packet_format: str = ">BB"
|
||||||
|
|
||||||
def to_bytes(self):
|
def to_bytes(self):
|
||||||
|
|
|
@ -15,7 +15,6 @@ class PacketBombState(Packet):
|
||||||
position: Point2D = field()
|
position: Point2D = field()
|
||||||
bomb_state: BombState = field()
|
bomb_state: BombState = field()
|
||||||
|
|
||||||
packet_size: int = 3
|
|
||||||
packet_format: str = ">BBb"
|
packet_format: str = ">BBb"
|
||||||
|
|
||||||
def to_bytes(self):
|
def to_bytes(self):
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from source.network.packet.abc import Packet
|
from source.network.packet.abc import Packet
|
||||||
|
|
||||||
|
@ -11,11 +14,27 @@ class PacketChat(Packet):
|
||||||
|
|
||||||
message: str = field()
|
message: str = field()
|
||||||
|
|
||||||
packet_size: int = 256
|
packet_format = ">I"
|
||||||
|
|
||||||
def to_bytes(self) -> bytes:
|
def to_bytes(self) -> bytes:
|
||||||
return self.message.encode("utf-8")
|
message: bytes = self.message.encode("utf-8")
|
||||||
|
message_len: int = len(message)
|
||||||
|
|
||||||
|
# envoie la taille du message, suivi des données du message
|
||||||
|
return struct.pack(f"{self.packet_format}{message_len}s", message_len, message)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bytes(cls, data: bytes):
|
def from_connection(cls, connection: socket.socket) -> "Packet":
|
||||||
return cls(message=data.decode("utf-8"))
|
message_len, *_ = struct.unpack(
|
||||||
|
cls.packet_format,
|
||||||
|
connection.recv(struct.calcsize(cls.packet_format))
|
||||||
|
)
|
||||||
|
|
||||||
|
format_: str = f">{message_len}s"
|
||||||
|
|
||||||
|
message, *_ = struct.unpack(
|
||||||
|
format_,
|
||||||
|
connection.recv(struct.calcsize(format_))
|
||||||
|
)
|
||||||
|
|
||||||
|
return cls(message=message.decode("utf-8"))
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import socket
|
||||||
import struct
|
import struct
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
@ -6,37 +7,45 @@ from source.network.packet.abc import Packet
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PacketSettings(Packet):
|
class PacketSettings(Packet):
|
||||||
username: str = field()
|
|
||||||
grid_width: int = field()
|
grid_width: int = field()
|
||||||
grid_height: int = field()
|
grid_height: int = field()
|
||||||
host_start: bool = field()
|
host_start: bool = field()
|
||||||
boats_length: list = field()
|
boats_length: list = field()
|
||||||
|
|
||||||
packet_size: int = 51
|
packet_format: str = ">BB?I"
|
||||||
packet_format: str = ">16sBB?32B"
|
|
||||||
|
|
||||||
def to_bytes(self):
|
def to_bytes(self):
|
||||||
boat_size = self.boats_length + ([0] * (32 - len(self.boats_length)))
|
boats_len: int = len(self.boats_length)
|
||||||
|
|
||||||
return struct.pack(
|
return struct.pack(
|
||||||
self.packet_format,
|
f"{self.packet_format}{boats_len}B",
|
||||||
|
|
||||||
self.username.encode("utf-8"),
|
|
||||||
self.grid_width,
|
self.grid_width,
|
||||||
self.grid_height,
|
self.grid_height,
|
||||||
self.host_start,
|
self.host_start,
|
||||||
|
|
||||||
*boat_size
|
boats_len,
|
||||||
|
|
||||||
|
*self.boats_length
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bytes(cls, data: bytes):
|
def from_connection(cls, connection: socket.socket) -> "Packet":
|
||||||
username, grid_width, grid_height, host_start, *boats_length = struct.unpack(cls.packet_format, data)
|
grid_width, grid_height, host_start, boats_len = struct.unpack(
|
||||||
|
cls.packet_format,
|
||||||
|
connection.recv(struct.calcsize(cls.packet_format))
|
||||||
|
)
|
||||||
|
|
||||||
|
format_ = f">{boats_len}B"
|
||||||
|
|
||||||
|
boats_length = struct.unpack(
|
||||||
|
format_,
|
||||||
|
connection.recv(struct.calcsize(format_))
|
||||||
|
)
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
username=username.replace(b"\x00", b"").decode("utf-8"),
|
|
||||||
grid_width=grid_width,
|
grid_width=grid_width,
|
||||||
grid_height=grid_height,
|
grid_height=grid_height,
|
||||||
host_start=host_start,
|
host_start=host_start,
|
||||||
boats_length=list(filter(lambda value: value != 0, boats_length))
|
boats_length=list(boats_length)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from source.network.packet.abc import Packet
|
from source.network.packet.abc import Packet
|
||||||
|
@ -7,11 +9,26 @@ from source.network.packet.abc import Packet
|
||||||
class PacketUsername(Packet):
|
class PacketUsername(Packet):
|
||||||
username: str = field()
|
username: str = field()
|
||||||
|
|
||||||
packet_size: int = 16
|
packet_format: str = ">I"
|
||||||
|
|
||||||
def to_bytes(self):
|
def to_bytes(self):
|
||||||
return self.username.encode("utf-8")
|
username = self.username.encode()
|
||||||
|
username_len = len(username)
|
||||||
|
|
||||||
|
return struct.pack(f"{self.packet_format}{username_len}s", username_len, username)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bytes(cls, data: bytes):
|
def from_connection(cls, connection: socket.socket) -> "PacketUsername":
|
||||||
return cls(username=data.decode("utf-8"))
|
username_len, *_ = struct.unpack(
|
||||||
|
cls.packet_format,
|
||||||
|
connection.recv(struct.calcsize(cls.packet_format))
|
||||||
|
)
|
||||||
|
|
||||||
|
format_: str = f">{username_len}s"
|
||||||
|
|
||||||
|
username, *_ = struct.unpack(
|
||||||
|
format_,
|
||||||
|
connection.recv(struct.calcsize(format_))
|
||||||
|
)
|
||||||
|
|
||||||
|
return cls(username=username.decode("utf-8"))
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import socket
|
import socket
|
||||||
|
import struct
|
||||||
from abc import abstractmethod, ABC
|
from abc import abstractmethod, ABC
|
||||||
from typing import Type, Optional
|
from typing import Type, Optional
|
||||||
|
|
||||||
# TODO: struct.calcsize() au lieu de packet_size
|
|
||||||
|
|
||||||
|
|
||||||
class Packet(ABC):
|
class Packet(ABC):
|
||||||
packed_header: bytes
|
packed_header: bytes
|
||||||
packet_size: int
|
|
||||||
packet_id: int = 0
|
packet_id: int = 0
|
||||||
|
packet_format: str = ">"
|
||||||
|
|
||||||
def __init_subclass__(cls, **kwargs):
|
def __init_subclass__(cls, **kwargs):
|
||||||
cls.packet_header = Packet.packet_id.to_bytes(1, "big") # give a header to the newly created subclass
|
cls.packet_header = Packet.packet_id.to_bytes(1, "big") # give a header to the newly created subclass
|
||||||
|
@ -22,18 +21,22 @@ class Packet(ABC):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
def send_connection(self, connection: socket.socket):
|
||||||
@abstractmethod
|
|
||||||
def from_bytes(cls, data: bytes) -> "Packet":
|
|
||||||
"""
|
"""
|
||||||
Convert a bytes object into a packet.
|
Send the packet directly into a socket.
|
||||||
:param data: the data to convert into a packet. Should be "packet_size" long.
|
:param connection: the socket where to send the packet to.
|
||||||
:return: a packet corresponding to the bytes.
|
|
||||||
"""
|
"""
|
||||||
pass
|
connection.send(self.packet_header + self.to_bytes())
|
||||||
|
|
||||||
|
def instance_send_connection(self, connection: socket.socket):
|
||||||
|
"""
|
||||||
|
Send the packet directly into a socket.
|
||||||
|
:param connection: the socket where to send the packet to.
|
||||||
|
"""
|
||||||
|
connection.send(self.to_bytes())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cls_from_header(cls, packet_header: bytes) -> Type["Packet"]:
|
def type_from_header(cls, packet_header: bytes) -> Type["Packet"]:
|
||||||
"""
|
"""
|
||||||
Get a subclass from its packet header.
|
Get a subclass from its packet header.
|
||||||
:param packet_header: the header to find the corresponding subclass
|
:param packet_header: the header to find the corresponding subclass
|
||||||
|
@ -44,31 +47,32 @@ class Packet(ABC):
|
||||||
cls.__subclasses__()
|
cls.__subclasses__()
|
||||||
))
|
))
|
||||||
|
|
||||||
def send_connection(self, connection: socket.socket):
|
@classmethod
|
||||||
|
def from_bytes(cls, data: bytes) -> "Packet":
|
||||||
"""
|
"""
|
||||||
Send the packet directly into a socket.
|
Convert a bytes object into a packet.
|
||||||
:param connection: the socket where to send the packet to.
|
:param data: the data to convert into a packet. Should be "packet_size" long.
|
||||||
|
:return: a packet corresponding to the bytes.
|
||||||
"""
|
"""
|
||||||
connection.send(self.packet_header + self.to_bytes())
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_connection(cls, connection: socket.socket) -> Optional["Packet"]:
|
def type_from_connection(cls, connection: socket.socket) -> Optional[Type["Packet"]]:
|
||||||
|
try:
|
||||||
|
packet_header = connection.recv(1)
|
||||||
|
except socket.timeout:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not packet_header: return None # ignore si le header est invalide
|
||||||
|
return cls.type_from_header(packet_header)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_connection(cls, connection: socket.socket) -> "Packet":
|
||||||
"""
|
"""
|
||||||
Receive a packet from a socket.
|
Receive a packet from a socket.
|
||||||
:param connection: the socket where to get the data from
|
:param connection: the socket where to get the data from
|
||||||
:return: the packet, or None if there was nothing in the socket to receive.
|
:return: the packet, or None if there was nothing in the socket to receive.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# get the packet type
|
packet_size: int = struct.calcsize(cls.packet_format)
|
||||||
|
return cls.from_bytes(connection.recv(packet_size))
|
||||||
packet_header: Optional[bytes] = None
|
|
||||||
try:
|
|
||||||
packet_header = connection.recv(1)
|
|
||||||
except socket.timeout:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not packet_header: return None # ignore si le header est invalide
|
|
||||||
packet_type = cls.cls_from_header(packet_header)
|
|
||||||
|
|
||||||
# renvoie les données instanciées
|
|
||||||
return packet_type.from_bytes(connection.recv(packet_type.packet_size))
|
|
||||||
|
|
Loading…
Reference in a new issue