added more documentation in some files
This commit is contained in:
parent
dc4cb3b1a7
commit
a2d37c37f7
28 changed files with 168 additions and 31 deletions
6
NOTE.md
6
NOTE.md
|
@ -6,6 +6,7 @@ A faire :
|
|||
- Police d'écriture
|
||||
|
||||
- Voir si les event listener intégré à pyglet sont plus pratique que l'event propagation
|
||||
- Documenter
|
||||
|
||||
Bug :
|
||||
- /
|
||||
|
@ -14,8 +15,9 @@ Autre :
|
|||
- Tester sur Linux
|
||||
|
||||
|
||||
|
||||
|
||||
A expliquer :
|
||||
- in_pyglet_context
|
||||
- Packet
|
||||
|
||||
|
||||
Bonus ultime :
|
||||
|
|
|
@ -8,6 +8,11 @@ from source.utils import copy_array_offset
|
|||
|
||||
|
||||
class Board:
|
||||
"""
|
||||
Represent a board for the game.
|
||||
Boat can be added and bomb can be placed.
|
||||
"""
|
||||
|
||||
__slots__ = ("_columns", "_rows", "_boats", "_bombs")
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -4,6 +4,11 @@ from source.core.enums import Orientation
|
|||
|
||||
|
||||
class Boat:
|
||||
"""
|
||||
Represent a boat.
|
||||
It can be added to a board.
|
||||
"""
|
||||
|
||||
__slots__ = ("orientation", "length")
|
||||
|
||||
def __init__(self, length: int, orientation: Orientation):
|
||||
|
|
|
@ -2,16 +2,13 @@ from enum import Enum
|
|||
|
||||
|
||||
class BombState(Enum):
|
||||
NOTHING = 0
|
||||
TOUCHED = 1
|
||||
SUNKEN = 2
|
||||
WON = 3
|
||||
"""
|
||||
This class represent the state of a bomb after being place on the board.
|
||||
"""
|
||||
|
||||
ERROR = 10
|
||||
NOTHING = 0 # the bomb missed
|
||||
TOUCHED = 1 # the bomb touched a boat
|
||||
SUNKEN = 2 # the bomb touched the last part of a boat
|
||||
WON = 3 # the bomb sunk the last boat
|
||||
|
||||
def to_bytes(self) -> bytes:
|
||||
return self.value.to_bytes(1, "big")
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes):
|
||||
return cls(int.from_bytes(data, "big"))
|
||||
ERROR = 10 # the bomb could not be placed
|
||||
|
|
|
@ -2,6 +2,10 @@ from enum import Enum
|
|||
|
||||
|
||||
class Orientation(Enum):
|
||||
"""
|
||||
Represent the orientation of a boat.
|
||||
"""
|
||||
|
||||
HORIZONTAL = "H"
|
||||
VERTICAL = "V"
|
||||
|
||||
|
|
|
@ -2,16 +2,36 @@ from typing import Callable
|
|||
|
||||
|
||||
class Listener:
|
||||
"""
|
||||
The Listener can be subclassed to allow the subclass to add, remove and call event easily.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._events_listener: dict[str, set[Callable]] = {}
|
||||
|
||||
def add_listener(self, name: str, callback: Callable):
|
||||
"""
|
||||
Add a function to an event name
|
||||
:param name: the name of the event to react
|
||||
:param callback: the function to call
|
||||
"""
|
||||
if name not in self._events_listener: self._events_listener[name] = set()
|
||||
self._events_listener[name].add(callback)
|
||||
|
||||
def remove_listener(self, name: str, callback: Callable):
|
||||
"""
|
||||
Remove a function from an event name
|
||||
:param name: the event name where to remove the callback
|
||||
:param callback: the callback function to remove
|
||||
"""
|
||||
self._events_listener[name].remove(callback)
|
||||
|
||||
def trigger_event(self, name: str, *args, **kwargs):
|
||||
"""
|
||||
Call all the callback attached to an event
|
||||
:param name: the name of the event to call
|
||||
:param args: the args of the callbacks
|
||||
:param kwargs: the kwargs of the callbacks
|
||||
"""
|
||||
for listener in self._events_listener.get(name, set()):
|
||||
listener(*args, **kwargs)
|
||||
|
|
|
@ -6,6 +6,11 @@ from source.gui.event import StopEvent
|
|||
|
||||
|
||||
class EventPropagationMixin:
|
||||
"""
|
||||
This class can be subclassed to allow the subclass to propagate all the call to the method that start by
|
||||
"on_" to the object in the "childs" property.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def childs(self):
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
class StopEvent(Exception):
|
||||
"""
|
||||
This error can be raised to prevent an event to propagate to further element.
|
||||
"""
|
||||
pass
|
||||
|
|
|
@ -6,6 +6,14 @@ import pyglet
|
|||
|
||||
|
||||
class Style(ABC):
|
||||
"""
|
||||
This class represent a style that can be attached to a widget.
|
||||
All property of the class will be loaded into a pyglet image.
|
||||
|
||||
If the property is associated to only a Path, a simple image will be loaded.
|
||||
If the property is associated to a list of Path, an animation will be loaded.
|
||||
"""
|
||||
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
atlas = pyglet.image.atlas.TextureAtlas()
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Checkbox(BoxWidget):
|
||||
"""
|
||||
A checkbox widget with a background texture that change depending on if it is checked or unchecked.
|
||||
"""
|
||||
|
||||
def __init__(self, scene: "Scene",
|
||||
|
||||
style: Type[Style],
|
||||
|
|
|
@ -11,6 +11,10 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Image(BoxWidget):
|
||||
"""
|
||||
An image widget with a texture.
|
||||
"""
|
||||
|
||||
def __init__(self, scene: "Scene",
|
||||
|
||||
image: pyglet.image.AbstractImage,
|
||||
|
|
|
@ -14,6 +14,10 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Input(BoxWidget):
|
||||
"""
|
||||
An input widget with a background texture and a label. A regex pattern can be added to validate the input.
|
||||
"""
|
||||
|
||||
def __init__(self, scene: "Scene",
|
||||
|
||||
style: Type[Style],
|
||||
|
|
|
@ -13,6 +13,11 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Scroller(BoxWidget):
|
||||
"""
|
||||
A scroller widget with a background texture, a scroller and a label.
|
||||
The cursor can be moved between the "from" and the "to" value
|
||||
"""
|
||||
|
||||
def __init__(self, scene: "Scene",
|
||||
|
||||
style: Type[Style],
|
||||
|
|
|
@ -11,7 +11,7 @@ if TYPE_CHECKING:
|
|||
|
||||
class BoxWidget(Widget, ABC):
|
||||
"""
|
||||
Same as a basic widget, but represent a box
|
||||
Same as a basic widget, but inside a box
|
||||
"""
|
||||
|
||||
def __init__(self, scene: "Scene",
|
||||
|
|
|
@ -18,6 +18,10 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class GameGridAlly(GameGrid):
|
||||
"""
|
||||
A game grid that represent the ally grid.
|
||||
"""
|
||||
|
||||
def __init__(self, scene: "Scene",
|
||||
|
||||
rows: int,
|
||||
|
|
|
@ -13,6 +13,10 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class GameGridEnemy(GameGrid):
|
||||
"""
|
||||
A game grid that represent the enemy grid.
|
||||
"""
|
||||
|
||||
def __init__(self, scene: "Scene",
|
||||
|
||||
rows: int,
|
||||
|
|
|
@ -13,6 +13,10 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class GameGrid(BoxWidget):
|
||||
"""
|
||||
A widget that represent a game grid.
|
||||
"""
|
||||
|
||||
def __init__(self, scene: "Scene",
|
||||
|
||||
rows: int,
|
||||
|
|
|
@ -9,6 +9,10 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Client(StoppableThread):
|
||||
"""
|
||||
The thread executed on the person who join a room.
|
||||
"""
|
||||
|
||||
def __init__(self, window: "Window", username: str, ip_address: str, port: int = 52321, **kw):
|
||||
super().__init__(**kw)
|
||||
|
||||
|
|
|
@ -9,6 +9,10 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Host(StoppableThread):
|
||||
"""
|
||||
The thread executed on the person who create a room.
|
||||
"""
|
||||
|
||||
def __init__(self, window: "Window", username: str, port: int = 52321, **kw):
|
||||
super().__init__(**kw)
|
||||
|
||||
|
|
|
@ -13,6 +13,13 @@ from source.utils.thread import in_pyglet_context
|
|||
|
||||
|
||||
def game_network(thread: "StoppableThread", window: "Window", connection: socket.socket):
|
||||
"""
|
||||
Run the networking to make the game work and react with the other player
|
||||
:param thread: the thread where this function is called.
|
||||
:param window: the window of the game
|
||||
:param connection: the connection with the other player
|
||||
"""
|
||||
|
||||
game_scene = in_pyglet_context(window.set_scene, scene.Game, connection=connection)
|
||||
|
||||
while True:
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
from dataclasses import dataclass
|
||||
import socket
|
||||
|
||||
from source.network.packet.abc import Packet
|
||||
|
||||
|
||||
@dataclass
|
||||
class PacketBoatPlaced(Packet):
|
||||
"""
|
||||
A packet that signal that all the boat of the player have been placed
|
||||
"""
|
||||
|
||||
packet_size: int = 0
|
||||
|
||||
def to_bytes(self):
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import socket
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from source.network.packet.abc import Packet
|
||||
|
@ -7,6 +6,10 @@ from source.type import Point2D
|
|||
|
||||
@dataclass
|
||||
class PacketBombPlaced(Packet):
|
||||
"""
|
||||
A packet that signal that a bomb have been placed on the board
|
||||
"""
|
||||
|
||||
position: Point2D = field()
|
||||
|
||||
packet_size: int = 2
|
||||
|
|
|
@ -8,6 +8,10 @@ from source.type import Point2D
|
|||
|
||||
@dataclass
|
||||
class PacketBombState(Packet):
|
||||
"""
|
||||
A packet that signal how a bomb exploded on the board
|
||||
"""
|
||||
|
||||
position: Point2D = field()
|
||||
bomb_state: BombState = field()
|
||||
|
||||
|
@ -19,7 +23,7 @@ class PacketBombState(Packet):
|
|||
return (
|
||||
x.to_bytes(1, "big") +
|
||||
y.to_bytes(1, "big") +
|
||||
self.bomb_state.value.to_bytes()
|
||||
self.bomb_state.value.to_bytes(1, "big")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
@ -29,5 +33,5 @@ class PacketBombState(Packet):
|
|||
int.from_bytes(data[0:1], "big"),
|
||||
int.from_bytes(data[1:2], "big"),
|
||||
),
|
||||
bomb_state=BombState.from_bytes(data[2:3])
|
||||
bomb_state=BombState(int.from_bytes(data[2:3], "big"))
|
||||
)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import socket
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from source.network.packet.abc import Packet
|
||||
|
@ -6,6 +5,10 @@ from source.network.packet.abc import Packet
|
|||
|
||||
@dataclass
|
||||
class PacketChat(Packet):
|
||||
"""
|
||||
A packet that represent a message from the chat
|
||||
"""
|
||||
|
||||
message: str = field()
|
||||
|
||||
packet_size: int = 256
|
||||
|
|
|
@ -9,31 +9,54 @@ class Packet(ABC):
|
|||
packet_id: int = 0
|
||||
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
cls.packet_header = Packet.packet_id.to_bytes(1, "big")
|
||||
Packet.packet_id = Packet.packet_id + 1
|
||||
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
|
||||
|
||||
@abstractmethod
|
||||
def to_bytes(self) -> bytes:
|
||||
"""
|
||||
Convert the packet into a bytes object. The size should be "packet_size" long.
|
||||
:return: the packet encoded into a bytes.
|
||||
"""
|
||||
pass
|
||||
|
||||
@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 cls_from_header(cls, packet_header: bytes) -> Type["Packet"]:
|
||||
"""
|
||||
Get a subclass from its packet header.
|
||||
: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__()
|
||||
))
|
||||
|
||||
def send_connection(self, connection: socket.socket) -> None:
|
||||
def 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.packet_header)
|
||||
connection.send(self.to_bytes())
|
||||
|
||||
@classmethod
|
||||
def from_connection(cls, connection: socket.socket) -> Optional["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_header: Optional[bytes] = None
|
||||
try: packet_header = connection.recv(1)
|
||||
except socket.timeout: pass
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
from typing import Union, Callable, Any
|
||||
|
||||
Point2D = tuple[int, int]
|
||||
BBox = tuple[int, int, int, int]
|
||||
Point2D = tuple[int, int] # a 2D point
|
||||
BBox = tuple[int, int, int, int] # a boundary box
|
||||
Percentage = float # a percentage, represented as a number between 0 and 1
|
||||
ColorRGB = tuple[int, int, int]
|
||||
ColorRGBA = tuple[int, int, int, int]
|
||||
ColorRGB = tuple[int, int, int] # a RGB Color
|
||||
ColorRGBA = tuple[int, int, int, int] # a RGBA Color
|
||||
|
||||
DistanceFunction = Callable[[Any], int] # a function that return a distance
|
||||
# a distance, represented either by a whole number, a percentage or a function
|
||||
Distance = Union[Percentage, int, DistanceFunction]
|
||||
Distance = Union[int, Percentage, DistanceFunction] # a distance, represented by a number, a percentage or a function
|
||||
|
|
|
@ -6,9 +6,9 @@ from source.type import Point2D
|
|||
def copy_array_offset(src: np.array, dst: np.array, offset: Point2D) -> None:
|
||||
"""
|
||||
Copy a numpy array into another one with an offset
|
||||
:source: source array
|
||||
:dst: destination array
|
||||
:offset: the offset where to copy the array
|
||||
:param src: source array
|
||||
:param dst: destination array
|
||||
:param offset: the offset where to copy the array
|
||||
"""
|
||||
column, row = offset
|
||||
width, height = src.shape
|
||||
|
|
|
@ -20,6 +20,15 @@ class StoppableThread(Thread):
|
|||
|
||||
|
||||
def in_pyglet_context(func: Callable, *args, **kwargs) -> Any:
|
||||
"""
|
||||
This function can be call in a thread. It will call the "func" in the pyglet event loop, avoiding
|
||||
some operation that are not allowed outside of the pyglet context, and return its result
|
||||
:param func: the function to call in the pyglet context
|
||||
:param args: the args of the function
|
||||
:param kwargs: the kwargs of the function
|
||||
:return: the result of the function
|
||||
"""
|
||||
|
||||
queue = Queue()
|
||||
pyglet.clock.schedule_once(lambda dt: queue.put(func(*args, **kwargs)), 0)
|
||||
return queue.get()
|
||||
|
|
Loading…
Reference in a new issue