From a2d37c37f726fd958e67631da5239bf42d76022a Mon Sep 17 00:00:00 2001 From: Faraphel Date: Thu, 23 Feb 2023 00:02:28 +0100 Subject: [PATCH] added more documentation in some files --- NOTE.md | 6 +++-- source/core/Board.py | 5 ++++ source/core/Boat.py | 5 ++++ source/core/enums/bomb.py | 19 +++++++-------- source/core/enums/orientation.py | 4 ++++ source/event/Listener.py | 20 ++++++++++++++++ source/gui/event/EventPropagationMixin.py | 5 ++++ source/gui/event/StopEvent.py | 3 +++ source/gui/texture/abc/Style.py | 8 +++++++ source/gui/widget/Checkbox.py | 4 ++++ source/gui/widget/Image.py | 4 ++++ source/gui/widget/Input.py | 4 ++++ source/gui/widget/Scroller.py | 5 ++++ source/gui/widget/abc/BoxWidget.py | 2 +- source/gui/widget/grid/GameGridAlly.py | 4 ++++ source/gui/widget/grid/GameGridEnemy.py | 4 ++++ source/gui/widget/grid/abc/GameGrid.py | 4 ++++ source/network/Client.py | 4 ++++ source/network/Host.py | 4 ++++ source/network/game_network.py | 7 ++++++ source/network/packet/PacketBoatPlaced.py | 5 +++- source/network/packet/PacketBombPlaced.py | 5 +++- source/network/packet/PacketBombState.py | 8 +++++-- source/network/packet/PacketChat.py | 5 +++- source/network/packet/abc/Packet.py | 29 ++++++++++++++++++++--- source/type.py | 11 ++++----- source/utils/copy_array_offset.py | 6 ++--- source/utils/thread.py | 9 +++++++ 28 files changed, 168 insertions(+), 31 deletions(-) diff --git a/NOTE.md b/NOTE.md index 30a4601..d441fec 100644 --- a/NOTE.md +++ b/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 : diff --git a/source/core/Board.py b/source/core/Board.py index 5d1187a..3bf3760 100644 --- a/source/core/Board.py +++ b/source/core/Board.py @@ -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__( diff --git a/source/core/Boat.py b/source/core/Boat.py index d576d90..9e032e8 100644 --- a/source/core/Boat.py +++ b/source/core/Boat.py @@ -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): diff --git a/source/core/enums/bomb.py b/source/core/enums/bomb.py index d714b08..946f070 100644 --- a/source/core/enums/bomb.py +++ b/source/core/enums/bomb.py @@ -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 diff --git a/source/core/enums/orientation.py b/source/core/enums/orientation.py index 46ca347..0e8d2d5 100644 --- a/source/core/enums/orientation.py +++ b/source/core/enums/orientation.py @@ -2,6 +2,10 @@ from enum import Enum class Orientation(Enum): + """ + Represent the orientation of a boat. + """ + HORIZONTAL = "H" VERTICAL = "V" diff --git a/source/event/Listener.py b/source/event/Listener.py index 3b90e52..67c2a3f 100644 --- a/source/event/Listener.py +++ b/source/event/Listener.py @@ -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) diff --git a/source/gui/event/EventPropagationMixin.py b/source/gui/event/EventPropagationMixin.py index 812a146..29744fe 100644 --- a/source/gui/event/EventPropagationMixin.py +++ b/source/gui/event/EventPropagationMixin.py @@ -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): diff --git a/source/gui/event/StopEvent.py b/source/gui/event/StopEvent.py index f04da2b..435a24a 100644 --- a/source/gui/event/StopEvent.py +++ b/source/gui/event/StopEvent.py @@ -1,2 +1,5 @@ class StopEvent(Exception): + """ + This error can be raised to prevent an event to propagate to further element. + """ pass diff --git a/source/gui/texture/abc/Style.py b/source/gui/texture/abc/Style.py index 246f530..57c8af8 100644 --- a/source/gui/texture/abc/Style.py +++ b/source/gui/texture/abc/Style.py @@ -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() diff --git a/source/gui/widget/Checkbox.py b/source/gui/widget/Checkbox.py index c19a8b7..fc11017 100644 --- a/source/gui/widget/Checkbox.py +++ b/source/gui/widget/Checkbox.py @@ -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], diff --git a/source/gui/widget/Image.py b/source/gui/widget/Image.py index 476fa32..5bf806f 100644 --- a/source/gui/widget/Image.py +++ b/source/gui/widget/Image.py @@ -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, diff --git a/source/gui/widget/Input.py b/source/gui/widget/Input.py index 9275f92..e746620 100644 --- a/source/gui/widget/Input.py +++ b/source/gui/widget/Input.py @@ -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], diff --git a/source/gui/widget/Scroller.py b/source/gui/widget/Scroller.py index 82a4b4b..ee42ed6 100644 --- a/source/gui/widget/Scroller.py +++ b/source/gui/widget/Scroller.py @@ -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], diff --git a/source/gui/widget/abc/BoxWidget.py b/source/gui/widget/abc/BoxWidget.py index fc614c4..4a847ad 100644 --- a/source/gui/widget/abc/BoxWidget.py +++ b/source/gui/widget/abc/BoxWidget.py @@ -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", diff --git a/source/gui/widget/grid/GameGridAlly.py b/source/gui/widget/grid/GameGridAlly.py index 14effee..03c8629 100644 --- a/source/gui/widget/grid/GameGridAlly.py +++ b/source/gui/widget/grid/GameGridAlly.py @@ -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, diff --git a/source/gui/widget/grid/GameGridEnemy.py b/source/gui/widget/grid/GameGridEnemy.py index 40fbfc0..8882da1 100644 --- a/source/gui/widget/grid/GameGridEnemy.py +++ b/source/gui/widget/grid/GameGridEnemy.py @@ -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, diff --git a/source/gui/widget/grid/abc/GameGrid.py b/source/gui/widget/grid/abc/GameGrid.py index d318f95..7c802ca 100644 --- a/source/gui/widget/grid/abc/GameGrid.py +++ b/source/gui/widget/grid/abc/GameGrid.py @@ -13,6 +13,10 @@ if TYPE_CHECKING: class GameGrid(BoxWidget): + """ + A widget that represent a game grid. + """ + def __init__(self, scene: "Scene", rows: int, diff --git a/source/network/Client.py b/source/network/Client.py index 72b0e10..0df6cea 100644 --- a/source/network/Client.py +++ b/source/network/Client.py @@ -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) diff --git a/source/network/Host.py b/source/network/Host.py index cc5217a..cae2786 100644 --- a/source/network/Host.py +++ b/source/network/Host.py @@ -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) diff --git a/source/network/game_network.py b/source/network/game_network.py index f2cae54..0e485f8 100644 --- a/source/network/game_network.py +++ b/source/network/game_network.py @@ -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: diff --git a/source/network/packet/PacketBoatPlaced.py b/source/network/packet/PacketBoatPlaced.py index 18f93fe..c1743c2 100644 --- a/source/network/packet/PacketBoatPlaced.py +++ b/source/network/packet/PacketBoatPlaced.py @@ -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): diff --git a/source/network/packet/PacketBombPlaced.py b/source/network/packet/PacketBombPlaced.py index d45bd71..4d0e84b 100644 --- a/source/network/packet/PacketBombPlaced.py +++ b/source/network/packet/PacketBombPlaced.py @@ -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 diff --git a/source/network/packet/PacketBombState.py b/source/network/packet/PacketBombState.py index 7605cd6..a633087 100644 --- a/source/network/packet/PacketBombState.py +++ b/source/network/packet/PacketBombState.py @@ -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")) ) diff --git a/source/network/packet/PacketChat.py b/source/network/packet/PacketChat.py index bb03c74..25d18ca 100644 --- a/source/network/packet/PacketChat.py +++ b/source/network/packet/PacketChat.py @@ -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 diff --git a/source/network/packet/abc/Packet.py b/source/network/packet/abc/Packet.py index 61fd750..68ad3f4 100644 --- a/source/network/packet/abc/Packet.py +++ b/source/network/packet/abc/Packet.py @@ -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 diff --git a/source/type.py b/source/type.py index af980a6..2dc0786 100644 --- a/source/type.py +++ b/source/type.py @@ -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 diff --git a/source/utils/copy_array_offset.py b/source/utils/copy_array_offset.py index 46e0ce4..ddf297d 100644 --- a/source/utils/copy_array_offset.py +++ b/source/utils/copy_array_offset.py @@ -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 diff --git a/source/utils/thread.py b/source/utils/thread.py index 3679381..8e6c1ea 100644 --- a/source/utils/thread.py +++ b/source/utils/thread.py @@ -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()