fixed some bugs, network error are now handled

This commit is contained in:
Faraphel 2023-03-04 11:11:41 +01:00
parent c4116b64d1
commit 5cbb28df72
22 changed files with 131 additions and 44 deletions

14
NOTE.md
View file

@ -1,15 +1,19 @@
A faire : A faire :
- Sauvegarde / Quitter
1. Principal :
- Sauvegarde
- Historique / Replay - Historique / Replay
- Police d'écriture
- Gérer les erreurs (quitter en cours de connexion, ...)
- Documenter - Documenter
2. Visuel :
- Rendre le texte de status plus visible - Rendre le texte de status plus visible
- Police d'écriture
- Changer les images, rajouter les fonds, ... - Changer les images, rajouter les fonds, ...
3. Hypothétique :
- Voir si les event listener intégré à pyglet sont plus pratiques que l'event propagation (?) - Voir si les event listener intégré à pyglet sont plus pratiques que l'event propagation (?)
- Faire une scène incluant par défaut les boutons "Retour" (?) - Faire une scène incluant par défaut les boutons "Retour" (?)

BIN
assets/sound/game/loose.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/sound/game/won.wav Normal file

Binary file not shown.

View file

@ -198,7 +198,7 @@ class Game(Scene):
self._refresh_turn_text() self._refresh_turn_text()
# function # refresh
def _refresh_chat_box(self): def _refresh_chat_box(self):
# supprime des messages jusqu'à ce que la boite soit plus petite que un quart de la fenêtre # supprime des messages jusqu'à ce que la boite soit plus petite que un quart de la fenêtre
@ -217,22 +217,6 @@ class Game(Scene):
"L'adversaire place ses bombes..." "L'adversaire place ses bombes..."
) )
def quit(self):
PacketQuit().send_connection(self.connection)
self.thread.stop()
from source.gui.scene import MainMenu
self.window.set_scene(MainMenu)
def game_end(self, won: bool):
self.window.add_scene(GameResult, game_scene=self, won=won)
def chat_new_message(self, author: str, content: str):
message: str = f"[{author}] - {content}"
self.chat_log.text += "\n" + message
self._refresh_chat_box()
# property # property
@property @property
@ -280,6 +264,18 @@ class Game(Scene):
self._boat_ready_enemy = boat_ready_enemy self._boat_ready_enemy = boat_ready_enemy
self._refresh_turn_text() self._refresh_turn_text()
# function
def game_end(self, won: bool):
self.window.add_scene(GameResult, game_scene=self, won=won) # affiche le résultat
self.thread.stop() # coupe la connexion
def chat_new_message(self, author: str, content: str):
message: str = f"[{author}] - {content}"
self.chat_log.text += "\n" + message
self._refresh_chat_box()
# network # network
def network_on_chat(self, packet: PacketChat): def network_on_chat(self, packet: PacketChat):
@ -313,7 +309,6 @@ class Game(Scene):
if bomb_state is BombState.WON: if bomb_state is BombState.WON:
# si l'ennemie à gagner, alors l'on a perdu # si l'ennemie à gagner, alors l'on a perdu
self.game_end(won=False) self.game_end(won=False)
return True # coupe la connexion
def network_on_bomb_state(self, packet: PacketBombState): def network_on_bomb_state(self, packet: PacketBombState):
if packet.bomb_state is BombState.ERROR: if packet.bomb_state is BombState.ERROR:
@ -334,10 +329,10 @@ class Game(Scene):
if packet.bomb_state is BombState.WON: if packet.bomb_state is BombState.WON:
# si cette bombe a touché le dernier bateau, alors l'on a gagné # si cette bombe a touché le dernier bateau, alors l'on a gagné
self.game_end(won=True) self.game_end(won=True)
return True # coupe la connexion
def network_on_quit(self, packet: PacketQuit): def network_on_quit(self, packet: PacketQuit):
self.thread.stop() self.thread.stop() # coupe la connexion
from source.gui.scene import GameError from source.gui.scene import GameError
self.window.set_scene(GameError, text="L'adversaire a quitté la partie.") self.window.set_scene(GameError, text="L'adversaire a quitté la partie.")

View file

@ -20,6 +20,8 @@ class GameError(Scene):
anchor_x="center", anchor_x="center",
text=text, text=text,
align="center",
multiline=True,
font_size=28, font_size=28,
) )

View file

@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
from source.gui import widget, texture from source.gui import widget, texture
from source.gui.scene.abc.Popup import Popup from source.gui.scene.abc.Popup import Popup
from source.network.packet import PacketQuit
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.window import Window from source.gui.window import Window
@ -55,4 +56,11 @@ class GameQuit(Popup):
style=texture.Button.Style1 style=texture.Button.Style1
) )
self.confirm.add_listener("on_click_release", lambda *_: self.game_scene.quit()) self.confirm.add_listener("on_click_release", lambda *_: self.quit())
def quit(self):
PacketQuit().send_connection(self.game_scene.connection) # envoie le packet
self.game_scene.thread.stop() # coupe le thread & la connexion
from source.gui.scene import MainMenu
self.window.set_scene(MainMenu) # affiche le menu principal

View file

@ -2,7 +2,7 @@ from typing import TYPE_CHECKING
import pyglet.clock import pyglet.clock
from source.gui import texture, widget from source.gui import texture, widget, sound
from source.gui.scene.abc.Popup import Popup from source.gui.scene.abc.Popup import Popup
if TYPE_CHECKING: if TYPE_CHECKING:
@ -20,9 +20,12 @@ class GameResult(Popup):
widget.Image, widget.Image,
x=0, y=0, width=1.0, height=1.0, x=0, y=0, width=1.0, height=1.0,
image=texture.Result.Style1.victory if won else texture.Result.Style1.defeat image=texture.Result.Style1.get("victory" if won else "defeat")
) )
sound.Game.get("won" if won else "loose").play()
# TODO: rendre l'image transparente si possible # TODO: rendre l'image transparente si possible
pyglet.clock.schedule_once(lambda dt: self.game_scene.quit(), 5.0) from source.gui.scene import MainMenu
pyglet.clock.schedule_once(lambda dt: self.window.set_scene(MainMenu), 5.0)

View file

@ -233,8 +233,6 @@ class RoomCreate(Scene):
update_boat_size_text() update_boat_size_text()
# TODO: si on diminue la taille de la grille après avoir mis des bateaux de plus longue taille, faire un check
# Démarrer # Démarrer
self.start = self.add_widget( self.start = self.add_widget(

15
source/gui/sound/Game.py Normal file
View file

@ -0,0 +1,15 @@
from . import path
from .type import Sound
from .abc import SoundGroup
path = path / "game"
class Game(SoundGroup):
touched = Sound(path / "touched.wav")
sunken_ally = Sound(path / "sunken_ally.wav")
won = Sound(path / "won.wav")
missed = Sound(path / "missed.wav")
sunken_enemy = Sound(path / "sunken_enemy.wav")
loose = Sound(path / "loose.wav")

View file

@ -0,0 +1,5 @@
from pathlib import Path
path: Path = Path("./assets/sound")
from .Game import Game

View file

@ -0,0 +1,11 @@
from abc import ABC
class SoundGroup(ABC):
"""
This class represent a music group that can be played.
"""
@classmethod
def get(cls, item, default=None):
return getattr(cls, item, default)

View file

@ -0,0 +1 @@
from .SoundGroup import SoundGroup

View file

@ -0,0 +1,13 @@
from pathlib import Path
import pyglet
from source.gui.sound.type.abc import SoundType
class Sound(SoundType):
def __init__(self, path: Path):
self.path = path
def __get__(self, instance, owner) -> pyglet.media.Source:
return self.get_sound(self.path)

View file

@ -0,0 +1 @@
from .Sound import Sound

View file

@ -0,0 +1,20 @@
from abc import ABC, abstractmethod
from pathlib import Path
import pyglet
class SoundType(ABC):
loaded_sound: dict[Path, pyglet.media.Source] = {}
@classmethod
def get_sound(cls, path: Path) -> pyglet.media.Source:
if (sound := cls.loaded_sound.get(path)) is None:
sound = pyglet.media.load(path)
cls.loaded_sound[path] = sound
return sound
@abstractmethod
def __get__(self, instance, owner) -> pyglet.image.AbstractImage:
pass

View file

@ -0,0 +1 @@
from .SoundType import SoundType

View file

@ -29,6 +29,7 @@ def game_network(
packet.PacketQuit: game_scene.network_on_quit, packet.PacketQuit: game_scene.network_on_quit,
} }
try:
while True: while True:
data_type: Type["Packet"] = Packet.type_from_connection(connection) data_type: Type["Packet"] = Packet.type_from_connection(connection)
@ -38,6 +39,15 @@ def game_network(
data = data_type.from_connection(connection) data = data_type.from_connection(connection)
if in_pyglet_context( in_pyglet_context(
game_methods[data_type], data # récupère la methode relié ce type de donnée game_methods[data_type], data # récupère la methode relié ce type de donnée
): return # Appelle la méthode. Si elle renvoie True, arrête le thread ) # Appelle la méthode.
if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter
except Exception as e:
# TODO: meilleur messages
from source.gui.scene import GameError
in_pyglet_context(game_scene.window.set_scene, GameError, text=f"Une erreur est survenu :\n{str(e)}")
print(type(e), e)

View file

@ -18,12 +18,12 @@ class Packet(ABC):
packet_id: int = 0 packet_id: int = 0
def __init_subclass__(cls, **kwargs): def __init_subclass__(cls, **kwargs):
cls.packet_header = Packet.packet_id.to_bytes(1, "big") # give a header to the newly created subclass if isabstract(cls): return # si la classe est abstraite, ignore
Packet.packet_id = Packet.packet_id + 1 # increment by one the packet header for the next subclass
if not isabstract(cls): cls.packet_header = Packet.packet_id.to_bytes(1, "big") # ajoute un header à la sous-classe
# si la classe n'est pas abstraite, ajoute-la aux types de packet enregistré. Packet.packet_id = Packet.packet_id + 1 # incrémente l'id pour que le prochain header soit différent
cls.packet_types.add(cls)
cls.packet_types.add(cls) # ajoute la sous-classe aux types de packet enregistré.
@abstractmethod @abstractmethod
def to_bytes(self) -> bytes: def to_bytes(self) -> bytes: