added SimplePacket and VariableLengthBytesPacket to simplify some Packet type
This commit is contained in:
parent
37350926b2
commit
b7d147fc3c
12 changed files with 134 additions and 88 deletions
|
@ -4,7 +4,6 @@ from typing import TYPE_CHECKING, Any
|
|||
from source.gui import scene
|
||||
from source.network import game_network
|
||||
from source.network.packet import PacketUsername, PacketSettings
|
||||
from source.network.packet.abc import Packet
|
||||
from source.utils import StoppableThread
|
||||
from source.utils.thread import in_pyglet_context
|
||||
|
||||
|
@ -35,7 +34,7 @@ class Client(StoppableThread):
|
|||
print(f"[Client] Connecté avec {connection}")
|
||||
|
||||
settings: Any = PacketSettings.from_connection(connection)
|
||||
PacketUsername(username=self.username).instance_send_connection(connection)
|
||||
PacketUsername(username=self.username).send_data_connection(connection)
|
||||
packet_username = PacketUsername.from_connection(connection)
|
||||
|
||||
game_scene = in_pyglet_context(
|
||||
|
|
|
@ -44,9 +44,9 @@ class Host(StoppableThread):
|
|||
|
||||
print(f"[Serveur] Connecté avec {address}")
|
||||
|
||||
self.settings.instance_send_connection(connection)
|
||||
self.settings.send_data_connection(connection)
|
||||
packet_username = PacketUsername.from_connection(connection)
|
||||
PacketUsername(username=self.username).instance_send_connection(connection)
|
||||
PacketUsername(username=self.username).send_data_connection(connection)
|
||||
|
||||
game_scene = in_pyglet_context(
|
||||
self.window.set_scene,
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
from source.network.packet.abc import Packet
|
||||
from source.network.packet.abc import SimplePacket
|
||||
|
||||
|
||||
@dataclass
|
||||
class PacketBoatPlaced(Packet):
|
||||
class PacketBoatPlaced(SimplePacket):
|
||||
"""
|
||||
A packet that signal that all the boat of the player have been placed
|
||||
"""
|
||||
|
||||
def to_bytes(self):
|
||||
def to_bytes(self) -> bytes:
|
||||
return b""
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes):
|
||||
def from_bytes(cls, data: bytes) -> "PacketBoatPlaced":
|
||||
return cls()
|
||||
|
|
|
@ -2,12 +2,12 @@ import struct
|
|||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from source.network.packet.abc import Packet
|
||||
from source.network.packet.abc import SimplePacket
|
||||
from source.type import Point2D
|
||||
|
||||
|
||||
@dataclass
|
||||
class PacketBombPlaced(Packet):
|
||||
class PacketBombPlaced(SimplePacket):
|
||||
"""
|
||||
A packet that signal that a bomb have been placed on the board
|
||||
"""
|
||||
|
@ -16,11 +16,11 @@ class PacketBombPlaced(Packet):
|
|||
|
||||
packet_format: str = ">BB"
|
||||
|
||||
def to_bytes(self):
|
||||
def to_bytes(self) -> bytes:
|
||||
x, y = self.position
|
||||
return struct.pack(self.packet_format, x, y)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes):
|
||||
def from_bytes(cls, data: bytes) -> "PacketBombPlaced":
|
||||
x, y = struct.unpack(cls.packet_format, data)
|
||||
return cls(position=(x, y))
|
||||
|
|
|
@ -2,12 +2,12 @@ import struct
|
|||
from dataclasses import dataclass, field
|
||||
|
||||
from source.core.enums import BombState
|
||||
from source.network.packet.abc import Packet
|
||||
from source.network.packet.abc import SimplePacket
|
||||
from source.type import Point2D
|
||||
|
||||
|
||||
@dataclass
|
||||
class PacketBombState(Packet):
|
||||
class PacketBombState(SimplePacket):
|
||||
"""
|
||||
A packet that signal how a bomb exploded on the board
|
||||
"""
|
||||
|
@ -17,11 +17,11 @@ class PacketBombState(Packet):
|
|||
|
||||
packet_format: str = ">BBb"
|
||||
|
||||
def to_bytes(self):
|
||||
def to_bytes(self) -> bytes:
|
||||
x, y = self.position
|
||||
return struct.pack(self.packet_format, x, y, self.bomb_state.value)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes):
|
||||
def from_bytes(cls, data: bytes) -> "PacketBombState":
|
||||
x, y, bomb_state = struct.unpack(cls.packet_format, data)
|
||||
return cls(position=(x, y), bomb_state=BombState(bomb_state))
|
||||
|
|
|
@ -1,40 +1,20 @@
|
|||
import socket
|
||||
import struct
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
from source.network.packet.abc import Packet
|
||||
from source.network.packet.abc import VariableLengthBytesPacket
|
||||
|
||||
|
||||
@dataclass
|
||||
class PacketChat(Packet):
|
||||
class PacketChat(VariableLengthBytesPacket):
|
||||
"""
|
||||
A packet that represent a message from the chat
|
||||
"""
|
||||
|
||||
message: str = field()
|
||||
|
||||
packet_format = ">I"
|
||||
|
||||
def to_bytes(self) -> bytes:
|
||||
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)
|
||||
@property
|
||||
def data(self) -> bytes:
|
||||
return self.message.encode("utf-8")
|
||||
|
||||
@classmethod
|
||||
def from_connection(cls, connection: socket.socket) -> "Packet":
|
||||
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"))
|
||||
def from_bytes(cls, data: bytes) -> "PacketChat":
|
||||
return cls(message=data.decode("utf-8"))
|
||||
|
|
|
@ -14,7 +14,7 @@ class PacketSettings(Packet):
|
|||
|
||||
packet_format: str = ">BB?I"
|
||||
|
||||
def to_bytes(self):
|
||||
def to_bytes(self) -> bytes:
|
||||
boats_len: int = len(self.boats_length)
|
||||
|
||||
return struct.pack(
|
||||
|
@ -30,7 +30,7 @@ class PacketSettings(Packet):
|
|||
)
|
||||
|
||||
@classmethod
|
||||
def from_connection(cls, connection: socket.socket) -> "Packet":
|
||||
def from_connection(cls, connection: socket.socket) -> "PacketSettings":
|
||||
grid_width, grid_height, host_start, boats_len = struct.unpack(
|
||||
cls.packet_format,
|
||||
connection.recv(struct.calcsize(cls.packet_format))
|
||||
|
|
|
@ -1,34 +1,17 @@
|
|||
import socket
|
||||
import struct
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from source.network.packet.abc import Packet
|
||||
from source.network.packet.abc import VariableLengthBytesPacket
|
||||
|
||||
|
||||
@dataclass
|
||||
class PacketUsername(Packet):
|
||||
class PacketUsername(VariableLengthBytesPacket):
|
||||
username: str = field()
|
||||
|
||||
packet_format: str = ">I"
|
||||
|
||||
def to_bytes(self):
|
||||
username = self.username.encode()
|
||||
username_len = len(username)
|
||||
|
||||
return struct.pack(f"{self.packet_format}{username_len}s", username_len, username)
|
||||
@property
|
||||
def data(self) -> bytes:
|
||||
return self.username.encode("utf-8")
|
||||
|
||||
@classmethod
|
||||
def from_connection(cls, connection: socket.socket) -> "PacketUsername":
|
||||
username_len, *_ = struct.unpack(
|
||||
cls.packet_format,
|
||||
connection.recv(struct.calcsize(cls.packet_format))
|
||||
)
|
||||
def from_bytes(cls, data: bytes) -> "PacketUsername":
|
||||
return cls(username=data.decode("utf-8"))
|
||||
|
||||
format_: str = f">{username_len}s"
|
||||
|
||||
username, *_ = struct.unpack(
|
||||
format_,
|
||||
connection.recv(struct.calcsize(format_))
|
||||
)
|
||||
|
||||
return cls(username=username.decode("utf-8"))
|
||||
|
|
|
@ -1,18 +1,30 @@
|
|||
import socket
|
||||
import struct
|
||||
from abc import abstractmethod, ABC
|
||||
from inspect import isabstract
|
||||
from typing import Type, Optional
|
||||
|
||||
|
||||
class Packet(ABC):
|
||||
packed_header: bytes
|
||||
"""
|
||||
A packet that can be sent on a socket.
|
||||
Multiple subtype of packet can be sent and received in an easier way.
|
||||
|
||||
The to_bytes and from_connection method need to be defined.
|
||||
"""
|
||||
|
||||
packet_types: set[Type["Packet"]] = set()
|
||||
|
||||
packet_header: bytes
|
||||
packet_id: int = 0
|
||||
packet_format: str = ">"
|
||||
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
cls.packet_header = Packet.packet_id.to_bytes(1, "big") # give a header to the newly created subclass
|
||||
Packet.packet_id = Packet.packet_id + 1 # increment by one the packet header for the next subclass
|
||||
|
||||
if not isabstract(cls):
|
||||
# si la classe n'est pas abstraite, ajoute-la aux types de packet enregistré.
|
||||
cls.packet_types.add(cls)
|
||||
|
||||
@abstractmethod
|
||||
def to_bytes(self) -> bytes:
|
||||
"""
|
||||
|
@ -23,14 +35,14 @@ class Packet(ABC):
|
|||
|
||||
def send_connection(self, connection: socket.socket):
|
||||
"""
|
||||
Send the packet directly into a socket.
|
||||
Send the packet data preceded with the header directly into a socket.
|
||||
:param connection: the socket where to send the packet to.
|
||||
"""
|
||||
connection.send(self.packet_header + self.to_bytes())
|
||||
|
||||
def instance_send_connection(self, connection: socket.socket):
|
||||
def send_data_connection(self, connection: socket.socket):
|
||||
"""
|
||||
Send the packet directly into a socket.
|
||||
Send the packet data directly into a socket.
|
||||
:param connection: the socket where to send the packet to.
|
||||
"""
|
||||
connection.send(self.to_bytes())
|
||||
|
@ -42,20 +54,12 @@ class Packet(ABC):
|
|||
:param packet_header: the header to find the corresponding subclass
|
||||
:return: the class associated with this header
|
||||
"""
|
||||
|
||||
return next(filter(
|
||||
lambda subcls: subcls.packet_header == packet_header,
|
||||
cls.__subclasses__()
|
||||
cls.packet_types
|
||||
))
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes) -> "Packet":
|
||||
"""
|
||||
Convert a bytes object into a packet.
|
||||
:param data: the data to convert into a packet. Should be "packet_size" long.
|
||||
:return: a packet corresponding to the bytes.
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def type_from_connection(cls, connection: socket.socket) -> Optional[Type["Packet"]]:
|
||||
try:
|
||||
|
@ -67,12 +71,11 @@ class Packet(ABC):
|
|||
return cls.type_from_header(packet_header)
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def from_connection(cls, connection: socket.socket) -> "Packet":
|
||||
"""
|
||||
Receive a packet from a socket.
|
||||
:param connection: the socket where to get the data from
|
||||
:return: the packet, or None if there was nothing in the socket to receive.
|
||||
"""
|
||||
|
||||
packet_size: int = struct.calcsize(cls.packet_format)
|
||||
return cls.from_bytes(connection.recv(packet_size))
|
||||
pass
|
||||
|
|
31
source/network/packet/abc/SimplePacket.py
Normal file
31
source/network/packet/abc/SimplePacket.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
import socket
|
||||
import struct
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from source.network.packet.abc import Packet
|
||||
|
||||
|
||||
class SimplePacket(Packet, ABC):
|
||||
"""
|
||||
A packet with a simple packet format.
|
||||
Only the from_bytes and to_bytes method need to be implemented.
|
||||
"""
|
||||
|
||||
packet_format: str = ">"
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def from_bytes(cls, data: bytes) -> "Packet":
|
||||
"""
|
||||
Convert a bytes object into a packet.
|
||||
:param data: the data to convert into a packet. Should be "packet_size" long.
|
||||
:return: a packet corresponding to the bytes.
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def from_connection(cls, connection: socket.socket) -> "Packet":
|
||||
# récupère la taille du packet en fonction du format et charge
|
||||
# les données dans une nouvelle instance.
|
||||
packet_size: int = struct.calcsize(cls.packet_format)
|
||||
return cls.from_bytes(connection.recv(packet_size))
|
47
source/network/packet/abc/VariableLengthBytesPacket.py
Normal file
47
source/network/packet/abc/VariableLengthBytesPacket.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
import socket
|
||||
import struct
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from source.network.packet.abc import Packet
|
||||
|
||||
|
||||
class VariableLengthBytesPacket(Packet, ABC):
|
||||
"""
|
||||
A Packet that represent a single value that can be encoded with a variable length.
|
||||
The property "data" and the method "from_bytes" need to be defined.
|
||||
"""
|
||||
|
||||
packet_format: str = ">I"
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def data(self) -> bytes:
|
||||
pass
|
||||
|
||||
def to_bytes(self) -> bytes:
|
||||
data: bytes = self.data
|
||||
data_len: int = len(data)
|
||||
|
||||
# envoie la taille du message, suivi des données du message
|
||||
return struct.pack(f"{self.packet_format}{data_len}s", data_len, data)
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def from_bytes(cls, data: bytes):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def from_connection(cls, connection: socket.socket) -> "Packet":
|
||||
data_len, *_ = struct.unpack(
|
||||
cls.packet_format,
|
||||
connection.recv(struct.calcsize(cls.packet_format))
|
||||
)
|
||||
|
||||
format_: str = f">{data_len}s"
|
||||
|
||||
data, *_ = struct.unpack(
|
||||
format_,
|
||||
connection.recv(struct.calcsize(format_))
|
||||
)
|
||||
|
||||
return cls.from_bytes(data)
|
|
@ -1 +1,4 @@
|
|||
from .Packet import Packet
|
||||
|
||||
from .SimplePacket import SimplePacket
|
||||
from .VariableLengthBytesPacket import VariableLengthBytesPacket
|
||||
|
|
Loading…
Reference in a new issue