added SimplePacket and VariableLengthBytesPacket to simplify some Packet type

This commit is contained in:
Faraphel 2023-02-28 18:15:08 +01:00
parent 37350926b2
commit b7d147fc3c
12 changed files with 134 additions and 88 deletions

View file

@ -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(

View file

@ -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,

View file

@ -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()

View file

@ -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))

View file

@ -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))

View file

@ -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"))

View file

@ -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))

View file

@ -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"))

View file

@ -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

View 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))

View 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)

View file

@ -1 +1,4 @@
from .Packet import Packet
from .SimplePacket import SimplePacket
from .VariableLengthBytesPacket import VariableLengthBytesPacket