fixed some bugs, network error are now handled
This commit is contained in:
parent
c4116b64d1
commit
5cbb28df72
22 changed files with 131 additions and 44 deletions
14
NOTE.md
14
NOTE.md
|
@ -1,15 +1,19 @@
|
|||
A faire :
|
||||
- Sauvegarde / Quitter
|
||||
|
||||
|
||||
1. Principal :
|
||||
- Sauvegarde
|
||||
- Historique / Replay
|
||||
- Police d'écriture
|
||||
- Gérer les erreurs (quitter en cours de connexion, ...)
|
||||
- Documenter
|
||||
|
||||
|
||||
2. Visuel :
|
||||
- Rendre le texte de status plus visible
|
||||
|
||||
|
||||
- Police d'écriture
|
||||
- Changer les images, rajouter les fonds, ...
|
||||
|
||||
|
||||
3. Hypothétique :
|
||||
- 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" (?)
|
||||
|
||||
|
|
BIN
assets/sound/game/loose.wav
Normal file
BIN
assets/sound/game/loose.wav
Normal file
Binary file not shown.
BIN
assets/sound/game/missed.wav
Normal file
BIN
assets/sound/game/missed.wav
Normal file
Binary file not shown.
BIN
assets/sound/game/sunken_ally.wav
Normal file
BIN
assets/sound/game/sunken_ally.wav
Normal file
Binary file not shown.
BIN
assets/sound/game/sunken_enemy.wav
Normal file
BIN
assets/sound/game/sunken_enemy.wav
Normal file
Binary file not shown.
BIN
assets/sound/game/touched.wav
Normal file
BIN
assets/sound/game/touched.wav
Normal file
Binary file not shown.
BIN
assets/sound/game/won.wav
Normal file
BIN
assets/sound/game/won.wav
Normal file
Binary file not shown.
|
@ -198,7 +198,7 @@ class Game(Scene):
|
|||
|
||||
self._refresh_turn_text()
|
||||
|
||||
# function
|
||||
# refresh
|
||||
|
||||
def _refresh_chat_box(self):
|
||||
# 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..."
|
||||
)
|
||||
|
||||
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
|
||||
|
@ -280,6 +264,18 @@ class Game(Scene):
|
|||
self._boat_ready_enemy = boat_ready_enemy
|
||||
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
|
||||
|
||||
def network_on_chat(self, packet: PacketChat):
|
||||
|
@ -313,7 +309,6 @@ class Game(Scene):
|
|||
if bomb_state is BombState.WON:
|
||||
# si l'ennemie à gagner, alors l'on a perdu
|
||||
self.game_end(won=False)
|
||||
return True # coupe la connexion
|
||||
|
||||
def network_on_bomb_state(self, packet: PacketBombState):
|
||||
if packet.bomb_state is BombState.ERROR:
|
||||
|
@ -334,10 +329,10 @@ class Game(Scene):
|
|||
if packet.bomb_state is BombState.WON:
|
||||
# si cette bombe a touché le dernier bateau, alors l'on a gagné
|
||||
self.game_end(won=True)
|
||||
return True # coupe la connexion
|
||||
|
||||
def network_on_quit(self, packet: PacketQuit):
|
||||
self.thread.stop()
|
||||
self.thread.stop() # coupe la connexion
|
||||
|
||||
from source.gui.scene import GameError
|
||||
self.window.set_scene(GameError, text="L'adversaire a quitté la partie.")
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ class GameError(Scene):
|
|||
anchor_x="center",
|
||||
|
||||
text=text,
|
||||
align="center",
|
||||
multiline=True,
|
||||
font_size=28,
|
||||
)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
|
|||
|
||||
from source.gui import widget, texture
|
||||
from source.gui.scene.abc.Popup import Popup
|
||||
from source.network.packet import PacketQuit
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.window import Window
|
||||
|
@ -55,4 +56,11 @@ class GameQuit(Popup):
|
|||
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
|
||||
|
|
|
@ -2,7 +2,7 @@ from typing import TYPE_CHECKING
|
|||
|
||||
import pyglet.clock
|
||||
|
||||
from source.gui import texture, widget
|
||||
from source.gui import texture, widget, sound
|
||||
from source.gui.scene.abc.Popup import Popup
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -20,9 +20,12 @@ class GameResult(Popup):
|
|||
widget.Image,
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
|
|
@ -233,8 +233,6 @@ class RoomCreate(Scene):
|
|||
|
||||
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
|
||||
|
||||
self.start = self.add_widget(
|
||||
|
|
15
source/gui/sound/Game.py
Normal file
15
source/gui/sound/Game.py
Normal 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")
|
5
source/gui/sound/__init__.py
Normal file
5
source/gui/sound/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from pathlib import Path
|
||||
|
||||
path: Path = Path("./assets/sound")
|
||||
|
||||
from .Game import Game
|
11
source/gui/sound/abc/SoundGroup.py
Normal file
11
source/gui/sound/abc/SoundGroup.py
Normal 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)
|
1
source/gui/sound/abc/__init__.py
Normal file
1
source/gui/sound/abc/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from .SoundGroup import SoundGroup
|
13
source/gui/sound/type/Sound.py
Normal file
13
source/gui/sound/type/Sound.py
Normal 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)
|
1
source/gui/sound/type/__init__.py
Normal file
1
source/gui/sound/type/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from .Sound import Sound
|
20
source/gui/sound/type/abc/SoundType.py
Normal file
20
source/gui/sound/type/abc/SoundType.py
Normal 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
|
1
source/gui/sound/type/abc/__init__.py
Normal file
1
source/gui/sound/type/abc/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from .SoundType import SoundType
|
|
@ -29,6 +29,7 @@ def game_network(
|
|||
packet.PacketQuit: game_scene.network_on_quit,
|
||||
}
|
||||
|
||||
try:
|
||||
while True:
|
||||
data_type: Type["Packet"] = Packet.type_from_connection(connection)
|
||||
|
||||
|
@ -38,6 +39,15 @@ def game_network(
|
|||
|
||||
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
|
||||
): 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)
|
||||
|
|
|
@ -18,12 +18,12 @@ class Packet(ABC):
|
|||
packet_id: int = 0
|
||||
|
||||
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 isabstract(cls): return # si la classe est abstraite, ignore
|
||||
|
||||
if not isabstract(cls):
|
||||
# si la classe n'est pas abstraite, ajoute-la aux types de packet enregistré.
|
||||
cls.packet_types.add(cls)
|
||||
cls.packet_header = Packet.packet_id.to_bytes(1, "big") # ajoute un header à la sous-classe
|
||||
Packet.packet_id = Packet.packet_id + 1 # incrémente l'id pour que le prochain header soit différent
|
||||
|
||||
cls.packet_types.add(cls) # ajoute la sous-classe aux types de packet enregistré.
|
||||
|
||||
@abstractmethod
|
||||
def to_bytes(self) -> bytes:
|
||||
|
|
Loading…
Reference in a new issue