diff --git a/source/network/Client.py b/source/network/Client.py index 68215e9..8e01eb5 100644 --- a/source/network/Client.py +++ b/source/network/Client.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Optional, Callable from source.path import path_save from source.gui import scene -from source.network import game_network +from source.network import game_network, handle_error from source.network.packet import PacketUsername, PacketSettings, PacketHaveSaveBeenFound, PacketLoadOldSave from source.utils import StoppableThread from source.utils.thread import in_pyglet_context @@ -36,107 +36,99 @@ class Client(StoppableThread): self.port = port def run(self) -> None: - print("[Client] Thread démarré") + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as connection: + try: + connection.connect((self.ip_address, self.port)) + except ConnectionRefusedError: + # Appelle l'événement lorsque la connexion échoue + if self.on_connexion_refused is not None: + in_pyglet_context(self.on_connexion_refused) + return - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as connection: - try: - connection.connect((self.ip_address, self.port)) - except ConnectionRefusedError: - # Appelle l'événement lorsque la connexion échoue - if self.on_connexion_refused is not None: - in_pyglet_context(self.on_connexion_refused) - return + connection.settimeout(1) # défini le timeout à 1 seconde - connection.settimeout(1) # défini le timeout à 1 secondes + # sauvegarde - print(f"[Client] Connecté avec {connection}") + load_old_save: bool = False - # sauvegarde + # attend que l'hôte indique s'il a trouvé une ancienne sauvegarde + packet_save_found = PacketHaveSaveBeenFound.from_connection(connection).value - load_old_save: bool = False + if packet_save_found: + # si l'hôte a trouvé une ancienne sauvegarde, vérifier de notre côté également. - # attend que l'hôte indique s'il a trouvé une ancienne sauvegarde - packet_save_found = PacketHaveSaveBeenFound.from_connection(connection).value + path_old_save: Optional[Path] = None + ip_address, _ = connection.getpeername() - if packet_save_found: - # si l'hôte a trouvé une ancienne sauvegarde, vérifier de notre côté également. - - path_old_save: Optional[Path] = None - ip_address, _ = connection.getpeername() - - for file in reversed(list(path_save.iterdir())): - # la liste est inversée dans le cas où le fichier est en localhost, afin que l'hôte - # prenne le fichier en -0.bn-save et le client en -1.bn-save - if file.stem.startswith(ip_address): - path_old_save = file - break - - # envoie à l'hôte si l'on possède également la sauvegarde - PacketHaveSaveBeenFound(value=path_old_save is not None).send_data_connection(connection) - - if path_old_save: - # si l'on possède la sauvegarde, attend que l'hôte confirme son utilisation - - from source.gui.scene import GameWaitLoad - in_pyglet_context(self.window.set_scene, GameWaitLoad, path=path_old_save) - - while True: - # attend la décision de l'hôte - try: - load_old_save = PacketLoadOldSave.from_connection(connection).value + for file in reversed(list(path_save.iterdir())): + # la liste est inversée dans le cas où le fichier est en localhost, afin que l'hôte + # prenne le fichier en -0.bn-save et le client en -1.bn-save + if file.stem.startswith(ip_address): + path_old_save = file break - except socket.timeout: - if self.stopped: return - except ConnectionResetError: - from source.gui.scene import GameError - in_pyglet_context( - self.window.set_scene, - GameError, - text="Perte de connexion avec l'adversaire" - ) - return - if load_old_save: + # envoie à l'hôte si l'on possède également la sauvegarde + PacketHaveSaveBeenFound(value=path_old_save is not None).send_data_connection(connection) - # charge la sauvegarde - with open(path_old_save, "r", encoding="utf-8") as file: - save_data = json.load(file) + if path_old_save: + # si l'on possède la sauvegarde, attend que l'hôte confirme son utilisation - # paramètres & jeu + from source.gui.scene import GameWaitLoad + in_pyglet_context(self.window.set_scene, GameWaitLoad, path=path_old_save) - settings = PacketSettings.from_connection(connection) - PacketUsername(username=self.username).send_data_connection(connection) - enemy_username = PacketUsername.from_connection(connection).username + while True: + # attend la décision de l'hôte + try: + load_old_save = PacketLoadOldSave.from_connection(connection).value + break + except socket.timeout: + if self.stopped: return - if load_old_save: - game_scene = in_pyglet_context( - self.window.set_scene, - scene.Game.from_json, # depuis le fichier json + if load_old_save: - data=save_data, + # charge la sauvegarde + with open(path_old_save, "r", encoding="utf-8") as file: + save_data = json.load(file) - thread=self, - connection=connection - ) + # paramètres & jeu - else: - game_scene = in_pyglet_context( - self.window.set_scene, - scene.Game, + settings = PacketSettings.from_connection(connection) + PacketUsername(username=self.username).send_data_connection(connection) + enemy_username = PacketUsername.from_connection(connection).username + if load_old_save: + game_scene = in_pyglet_context( + self.window.set_scene, + scene.Game.from_json, # depuis le fichier json + + data=save_data, + + thread=self, + connection=connection + ) + + else: + game_scene = in_pyglet_context( + self.window.set_scene, + scene.Game, + + thread=self, + connection=connection, + + boats_length=settings.boats_length, + name_ally=self.username, + name_enemy=enemy_username, + grid_width=settings.grid_width, + grid_height=settings.grid_height, + my_turn=not settings.host_start + ) + + game_network( thread=self, connection=connection, - - boats_length=settings.boats_length, - name_ally=self.username, - name_enemy=enemy_username, - grid_width=settings.grid_width, - grid_height=settings.grid_height, - my_turn=not settings.host_start + game_scene=game_scene, ) - game_network( - thread=self, - connection=connection, - game_scene=game_scene, - ) + except Exception as exception: + handle_error(self.window, exception) diff --git a/source/network/Host.py b/source/network/Host.py index c99117e..3516d47 100644 --- a/source/network/Host.py +++ b/source/network/Host.py @@ -6,7 +6,7 @@ from threading import Condition from source.path import path_save from source.gui import scene -from source.network import game_network +from source.network import game_network, handle_error from source.utils import StoppableThread from source.utils.thread import in_pyglet_context from source.network.packet import PacketUsername, PacketLoadOldSave, PacketHaveSaveBeenFound @@ -33,103 +33,93 @@ class Host(StoppableThread): self.accept_load: bool = False def run(self) -> None: - print("[Serveur] Thread démarré") + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server: + server.bind(("", self.port)) # connecte le socket au port indiqué - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server: - server.bind(("", self.port)) # connecte le socket au port indiqué - - server.settimeout(1) # défini le timeout à 1 seconde - server.listen() # écoute de nouvelle connexion - - while True: - try: - connection, (ip_address, port) = server.accept() # accepte la première connexion entrante - break # sort de la boucle - except socket.timeout: # en cas de timeout - if self.stopped: return # vérifie si le thread n'est pas censé s'arrêter - # sinon, réessaye - - print(f"[Serveur] Connecté avec {ip_address}") - - # ancienne sauvegarde - - path_old_save: Optional[Path] = None - - for file in path_save.iterdir(): # cherche une ancienne sauvegarde correspondant à l'ip de l'adversaire - if file.stem.startswith(ip_address): - path_old_save = file - break - - # envoie à l'adversaire si une ancienne sauvegarde a été trouvée - PacketHaveSaveBeenFound(value=path_old_save is not None).send_data_connection(connection) - - if path_old_save is not None: - # si une ancienne sauvegarde a été trouvée, attend que l'adversaire confirme avoir également la save - packet_save_found = PacketHaveSaveBeenFound.from_connection(connection).value - - # si l'adversaire à également la sauvegarde, demande à l'hôte de confirmer l'utilisation de la save - if packet_save_found: - - from source.gui.scene import GameLoad - in_pyglet_context(self.window.set_scene, GameLoad, path=path_old_save, thread_host=self) - - with self.condition_load: self.condition_load.wait() # attend que l'utilisateur choisisse l'option + server.settimeout(1) # défini le timeout à 1 seconde + server.listen() # écoute de nouvelle connexion + while True: try: + connection, (ip_address, port) = server.accept() # accepte la première connexion entrante + break # sort de la boucle + except socket.timeout: # en cas de timeout + if self.stopped: return # vérifie si le thread n'est pas censé s'arrêter + # sinon, réessaye + + print(f"[Serveur] Connecté avec {ip_address}") + + # ancienne sauvegarde + + path_old_save: Optional[Path] = None + + for file in path_save.iterdir(): # cherche une ancienne sauvegarde correspondant à l'ip de l'adversaire + if file.stem.startswith(ip_address): + path_old_save = file + break + + # envoie à l'adversaire si une ancienne sauvegarde a été trouvée + PacketHaveSaveBeenFound(value=path_old_save is not None).send_data_connection(connection) + + if path_old_save is not None: + # si une ancienne sauvegarde a été trouvée, attend que l'adversaire confirme avoir également la save + packet_save_found = PacketHaveSaveBeenFound.from_connection(connection).value + + # si l'adversaire à également la sauvegarde, demande à l'hôte de confirmer l'utilisation de la save + if packet_save_found: + + from source.gui.scene import GameLoad + in_pyglet_context(self.window.set_scene, GameLoad, path=path_old_save, thread_host=self) + + with self.condition_load: self.condition_load.wait() # attend que l'utilisateur choisisse l'option + PacketLoadOldSave(value=self.accept_load).send_data_connection(connection) - except ConnectionResetError: - from source.gui.scene import GameError - in_pyglet_context( - self.window.set_scene, - GameError, - text="Perte de connexion avec l'adversaire" - ) - return - if self.accept_load: + if self.accept_load: - # charge la sauvegarde - with open(path_old_save, "r", encoding="utf-8") as file: - save_data = json.load(file) + # charge la sauvegarde + with open(path_old_save, "r", encoding="utf-8") as file: + save_data = json.load(file) - # paramètres et jeu + # paramètres et jeu - self.settings.send_data_connection(connection) - enemy_username = PacketUsername.from_connection(connection).username - PacketUsername(username=self.username).send_data_connection(connection) + self.settings.send_data_connection(connection) + enemy_username = PacketUsername.from_connection(connection).username + PacketUsername(username=self.username).send_data_connection(connection) - if self.accept_load: - game_scene = in_pyglet_context( - self.window.set_scene, - scene.Game.from_json, # depuis le fichier json + if self.accept_load: + game_scene = in_pyglet_context( + self.window.set_scene, + scene.Game.from_json, # depuis le fichier json - data=save_data, + data=save_data, - thread=self, - connection=connection - ) + thread=self, + connection=connection + ) - else: - game_scene = in_pyglet_context( - self.window.set_scene, - scene.Game, + else: + game_scene = in_pyglet_context( + self.window.set_scene, + scene.Game, + thread=self, + connection=connection, + + boats_length=self.settings.boats_length, + name_ally=self.username, + name_enemy=enemy_username, + grid_width=self.settings.grid_width, + grid_height=self.settings.grid_height, + my_turn=self.settings.host_start + ) + + game_network( thread=self, connection=connection, - - boats_length=self.settings.boats_length, - name_ally=self.username, - name_enemy=enemy_username, - grid_width=self.settings.grid_width, - grid_height=self.settings.grid_height, - my_turn=self.settings.host_start + game_scene=game_scene ) - game_network( - thread=self, - connection=connection, - game_scene=game_scene - ) - - # TODO: englober les threads de try sur ConnectionResetError pour ramener au menu d'erreur directement - # au lieu de le faire manuellement à chaque fois + except Exception as exception: + handle_error(self.window, exception) diff --git a/source/network/__init__.py b/source/network/__init__.py index 87b026e..66ffeaa 100644 --- a/source/network/__init__.py +++ b/source/network/__init__.py @@ -1,4 +1,5 @@ from .game_network import game_network +from .error import handle_error from .Client import Client from .Host import Host diff --git a/source/network/error.py b/source/network/error.py new file mode 100644 index 0000000..195f42f --- /dev/null +++ b/source/network/error.py @@ -0,0 +1,25 @@ +import builtins +from typing import TYPE_CHECKING + +from source.utils.thread import in_pyglet_context + + +if TYPE_CHECKING: + from source.gui.window import Window + + +def handle_error(window: "Window", exception: Exception): + message: str = "Erreur :\n" + + match type(exception): + case builtins.ConnectionResetError: + message += "Perte de connexion avec l'adversaire." + case _: + message += str(exception) + + from source.gui.scene import GameError + in_pyglet_context( + window.set_scene, + GameError, + text=message + ) diff --git a/source/network/game_network.py b/source/network/game_network.py index 62079f8..6e65b1e 100644 --- a/source/network/game_network.py +++ b/source/network/game_network.py @@ -32,30 +32,17 @@ def game_network( packet.PacketResponseSave: game_scene.network_on_response_save, } - try: - while True: - data_type = Packet.type_from_connection(connection) - - if data_type is None: - if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter - continue - - data = data_type.from_connection(connection) - - in_pyglet_context( - game_methods[data_type], data # récupère la methode relié ce type de donnée - ) # Appelle la méthode. + while True: + data_type = Packet.type_from_connection(connection) + if data_type is None: if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter + continue - except Exception as exception: - message: str = "Erreur :\n" + data = data_type.from_connection(connection) - match type(exception): - case builtins.ConnectionResetError: - message += "Perte de connexion avec l'adversaire." - case _: - message += str(exception) + in_pyglet_context( + game_methods[data_type], data # récupère la methode relié ce type de donnée + ) # Appelle la méthode. - from source.gui.scene import GameError - in_pyglet_context(game_scene.window.set_scene, GameError, text=message) + if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter