added settings for the game

This commit is contained in:
Faraphel 2023-02-24 16:11:44 +01:00
parent 5dd5663ac6
commit 385adb2cf1
13 changed files with 361 additions and 34 deletions

View file

@ -1,5 +1,6 @@
A faire :
- Ecran de configuration de la partie
- Limite des bateaux, taille des pseudo, ...
- Nom dans les options
- Faire marcher le tchat
- Sauvegarde / Quitter

View file

@ -15,10 +15,26 @@ if TYPE_CHECKING:
class Game(Scene):
def __init__(self, window: "Window", connection: socket.socket, **kwargs):
def __init__(self, window: "Window",
connection: socket.socket,
boat_sizes: list,
name_ally: str,
name_enemy: str,
grid_width: int,
grid_height: int,
my_turn: bool,
**kwargs):
super().__init__(window, **kwargs)
self.connection = connection
self.boat_sizes = boat_sizes
self.name_ally = name_ally
self.name_enemy = name_enemy
self.grid_width = grid_width
self.grid_height = grid_height
self.my_turn = my_turn # is it the player turn ?
self.batch_label = pyglet.graphics.Batch()
self.batch_button_background = pyglet.graphics.Batch()
@ -42,12 +58,12 @@ class Game(Scene):
x=75, y=0.25, width=0.35, height=0.5,
boats_length=[5, 5, 4, 3, 2],
boats_length=self.boat_sizes,
grid_style=texture.Grid.Style1,
boat_style=texture.Grid.Boat.Style1,
bomb_style=texture.Grid.Bomb.Style1,
rows=8, columns=8,
rows=self.grid_height, columns=self.grid_width,
background_batch=self.batch_grid_background,
line_batch=self.batch_grid_line,
@ -74,7 +90,7 @@ class Game(Scene):
grid_style=texture.Grid.Style1,
boat_style=texture.Grid.Boat.Style1,
bomb_style=texture.Grid.Bomb.Style1,
rows=8, columns=8,
rows=self.grid_height, columns=self.grid_width,
background_batch=self.batch_grid_background,
line_batch=self.batch_grid_line,
@ -91,24 +107,24 @@ class Game(Scene):
self.grid_enemy.add_listener("on_request_place_bomb", board_enemy_bomb)
self.name_ally = self.add_widget(
self.add_widget(
widget.Text,
x=0.27, y=0.995,
text="Raphael",
text=self.name_ally,
font_size=20,
anchor_x="center", anchor_y="center",
batch=self.batch_label,
)
self.name_enemy = self.add_widget(
self.add_widget(
widget.Text,
x=0.73, y=0.995,
text="Leo",
text=self.name_enemy,
font_size=20,
anchor_x="center", anchor_y="center",
@ -212,10 +228,9 @@ class Game(Scene):
batch=self.batch_label
)
self.board_ally = core.Board(rows=8, columns=8)
self.board_enemy = core.Board(rows=8, columns=8)
self.board_ally = core.Board(rows=self.grid_height, columns=self.grid_width)
self.board_enemy = core.Board(rows=self.grid_height, columns=self.grid_width)
self.my_turn: bool = False # is it the player turn ?
self.boat_ready_ally: bool = False # does the player finished placing his boat ?
self.boat_ready_enemy: bool = False # does the opponent finished placing his boat ?
self._boat_broken_ally: int = 0

View file

@ -5,6 +5,7 @@ import pyglet
from source.gui import widget, texture
from source.gui.scene import RoomHost
from source.gui.scene.abc import Scene
from source.network.packet import PacketSettings
if TYPE_CHECKING:
from source.gui.window import Window
@ -17,6 +18,7 @@ class RoomCreate(Scene):
self.batch_label = pyglet.graphics.Batch()
self.batch_input_background = pyglet.graphics.Batch()
self.batch_button_background = pyglet.graphics.Batch()
self.batch_checkbox = pyglet.graphics.Batch()
self.back = self.add_widget(
widget.Button,
@ -33,6 +35,35 @@ class RoomCreate(Scene):
from source.gui.scene import MainMenu
self.back.add_listener("on_click_release", lambda *_: self.window.set_scene(MainMenu))
# Username
self.add_widget(
widget.Text,
x=0.1, y=0.5,
anchor_x="center", anchor_y="center",
text="Pseudonyme",
batch=self.batch_label
)
self.input_username = self.add_widget(
widget.Input,
x=0.2, y=0.45, width=0.15, height=0.1,
style=texture.Input.Style1,
label_text="Host",
background_batch=self.batch_input_background,
label_batch=self.batch_label
)
# Grid configuration
self.add_widget(
widget.Text,
@ -43,7 +74,7 @@ class RoomCreate(Scene):
batch=self.batch_label
)
input_width = self.add_widget(
self.input_width = self.add_widget(
widget.Input,
x=0.2, y=0.86, width=0.1, height=0.08,
@ -68,7 +99,7 @@ class RoomCreate(Scene):
batch=self.batch_label
)
input_height = self.add_widget(
self.input_height = self.add_widget(
widget.Input,
x=0.2, y=0.76, width=0.1, height=0.08,
@ -83,6 +114,141 @@ class RoomCreate(Scene):
label_batch=self.batch_label
)
# Tour
self.checkbox_host_start = self.add_widget(
widget.Checkbox,
x=0.4, y=0.8, width=0.05, height=0.1,
style=texture.Checkbox.Style1,
state=True,
batch=self.batch_checkbox
)
self.add_widget(
widget.Text,
x=0.46, y=0.85,
anchor_y="center",
text="Premier tour pour l'hôte",
batch=self.batch_label
)
# taille et quantité des bateaux
self.boat_size: int = 1
self.boat_size_amount: dict[int, int] = {2: 1, 3: 1, 4: 2, 5: 1}
def update_boat_size_text():
self.label_boat_size.text = f"Taille: {self.boat_size}"
self.input_boat_amount.text = str(self.boat_size_amount.get(self.boat_size, 0))
self.label_boat_recap.text = ""
for size, amount in self.boat_size_amount.items():
self.label_boat_recap.text += f"Taille: {size}, Quantité: {amount}\n"
self.button_boat_size_previous = self.add_widget(
widget.Button,
x=0.7, y=0.8, width=0.03, height=0.1,
label_text="<",
label_font_size=25,
style=texture.Button.Style1,
background_batch=self.batch_button_background,
label_batch=self.batch_label
)
def previous_boat_size():
if self.boat_size <= 1: return
self.boat_size -= 1
update_boat_size_text()
self.button_boat_size_previous.add_listener("on_click_release", lambda *_: previous_boat_size())
self.label_boat_size = self.add_widget(
widget.Text,
x=0.8, y=0.85,
anchor_x="center", anchor_y="center",
batch=self.batch_label,
)
self.button_boat_size_next = self.add_widget(
widget.Button,
x=0.87, y=0.8, width=0.03, height=0.1,
label_text=">",
label_font_size=25,
style=texture.Button.Style1,
background_batch=self.batch_button_background,
label_batch=self.batch_label
)
def next_boat_size():
if self.boat_size >= min(int(self.input_width.text), int(self.input_height.text)): return
self.boat_size += 1
update_boat_size_text()
self.button_boat_size_next.add_listener("on_click_release", lambda *_: next_boat_size())
self.input_boat_amount = self.add_widget(
widget.Input,
x=0.7, y=0.68, width=0.2, height=0.08,
regex=r"\d+",
style=texture.Input.Style1,
label_text="8",
background_batch=self.batch_input_background,
label_batch=self.batch_label
)
def change_boat_amount():
quantity = int(self.input_boat_amount.text)
if quantity > 0:
self.boat_size_amount[self.boat_size] = quantity
elif self.boat_size in self.boat_size_amount:
self.boat_size_amount.pop(self.boat_size)
update_boat_size_text()
self.input_boat_amount.add_listener("on_valid_text", lambda *_: change_boat_amount())
self.label_boat_recap = self.add_widget(
widget.Text,
x=0.7, y=0.60, width=0.2, height=0.1,
multiline=True,
batch=self.batch_label
)
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(
widget.Button,
x=lambda widget: widget.scene.window.width - 20 - widget.width, y=20, width=0.2, height=0.1,
@ -95,9 +261,21 @@ class RoomCreate(Scene):
label_batch=self.batch_label
)
self.start.add_listener("on_click_release", lambda *_: self.window.set_scene(RoomHost))
self.start.add_listener("on_click_release", lambda *_: self.confirm())
def confirm(self):
settings = PacketSettings(
username=self.input_username.text,
grid_width=int(self.input_width.text),
grid_height=int(self.input_height.text),
host_start=self.checkbox_host_start.state,
boat_size=[size for size, quantity in self.boat_size_amount.items() for _ in range(quantity)]
)
self.window.set_scene(RoomHost, settings=settings)
def on_draw(self):
self.batch_input_background.draw()
self.batch_button_background.draw()
self.batch_checkbox.draw()
self.batch_label.draw()

View file

@ -9,10 +9,11 @@ from source.gui import widget, texture
if TYPE_CHECKING:
from source.gui.window import Window
from source.network.packet import PacketSettings
class RoomHost(Scene):
def __init__(self, window: "Window", **kwargs):
def __init__(self, window: "Window", settings: "PacketSettings", **kwargs):
super().__init__(window, **kwargs)
"""r = requests.get('https://api.ipify.org')
@ -63,7 +64,7 @@ class RoomHost(Scene):
batch=self.batch_label
)
self.thread = network.Host(window=self.window, daemon=True, username="Host")
self.thread = network.Host(window=self.window, daemon=True, settings=settings)
self.thread.start()
def button_back_callback(self, *_):

View file

@ -32,9 +32,25 @@ class RoomJoin(Scene):
self.back.add_listener("on_click_release", self.button_back_callback)
# Pseudo
self.entry_username = self.add_widget(
widget.Input,
x=0.4, y=0.55, width=0.2, height=0.1,
style=texture.Input.Style1,
label_text="Client",
background_batch=self.batch_input_background,
label_batch=self.batch_label
)
# IP / Port
self.entry_ip = self.add_widget(
widget.Input,
x=0.4, y=0.5, width=0.13, height=0.1,
x=0.4, y=0.45, width=0.13, height=0.1,
regex=r"\d{1,3}(\.\d{1,3}){3}",
@ -48,7 +64,7 @@ class RoomJoin(Scene):
self.entry_port = self.add_widget(
widget.Input,
x=0.53, y=0.5, width=0.07, height=0.1,
x=0.53, y=0.45, width=0.07, height=0.1,
regex=r"\d{0,5}",
@ -60,7 +76,7 @@ class RoomJoin(Scene):
self.connect = self.add_widget(
widget.Button,
x=0.4, y=0.4, width=0.2, height=0.1,
x=0.4, y=0.35, width=0.2, height=0.1,
label_text="Se connecter",
@ -77,7 +93,7 @@ class RoomJoin(Scene):
window=self.window,
ip_address=self.entry_ip.text,
daemon=True,
username="Client"
username=self.entry_username.text
).start()
def button_back_callback(self, widget, *_):

View file

@ -121,6 +121,9 @@ class Input(BoxWidget):
if self.regex is not None: # si il y a un regex de validation, applique le pour vérifier le texte
self.invalid = self.regex.fullmatch(self.text) is None
if not self.invalid:
self.trigger_event("on_valid_text")
def on_resize(self, width: int, height: int):
self._refresh_size()

View file

@ -1,8 +1,12 @@
import socket
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from source.gui import scene
from source.network import game_network
from source.network.packet import PacketUsername
from source.network.packet.abc import Packet
from source.utils import StoppableThread
from source.utils.thread import in_pyglet_context
if TYPE_CHECKING:
from source.gui.window import Window
@ -30,4 +34,25 @@ class Client(StoppableThread):
print(f"[Client] Connecté avec {connection}")
game_network(self, self.window, connection, False)
settings: Any = Packet.from_connection(connection)
PacketUsername(username=self.username).send_connection(connection)
game_scene = in_pyglet_context(
self.window.set_scene,
scene.Game,
connection=connection,
boat_sizes=settings.boat_size,
name_ally=self.username,
name_enemy=settings.username,
grid_width=settings.grid_width,
grid_height=settings.grid_height,
my_turn=not settings.host_start
)
game_network(
thread=self,
connection=connection,
game_scene=game_scene,
)

View file

@ -1,11 +1,15 @@
import socket
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from source.gui import scene
from source.network import game_network
from source.network.packet.abc import Packet
from source.utils import StoppableThread
from source.utils.thread import in_pyglet_context
if TYPE_CHECKING:
from source.gui.window import Window
from source.network.packet import PacketSettings
class Host(StoppableThread):
@ -13,11 +17,11 @@ class Host(StoppableThread):
The thread executed on the person who create a room.
"""
def __init__(self, window: "Window", username: str, port: int = 52321, **kw):
def __init__(self, window: "Window", settings: "PacketSettings", port: int = 52321, **kw):
super().__init__(**kw)
self.window = window
self.username = username
self.settings = settings
self.port = port
def run(self) -> None:
@ -39,4 +43,25 @@ class Host(StoppableThread):
print(f"[Serveur] Connecté avec {address}")
game_network(self, self.window, connection, True)
self.settings.send_connection(connection)
packet_username: Any = Packet.from_connection(connection)
game_scene = in_pyglet_context(
self.window.set_scene,
scene.Game,
connection=connection,
boat_sizes=self.settings.boat_size,
name_ally=self.settings.username,
name_enemy=packet_username.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,
game_scene=game_scene
)

View file

@ -3,26 +3,26 @@ from typing import Any
from source.core.enums import BombState
from source.core.error import InvalidBombPosition, PositionAlreadyShot
from source.gui import scene
from source.gui.scene import Game
from source.network.packet.abc import Packet
from source.network import packet
from source.gui.window import Window
from source.utils import StoppableThread
from source.utils.thread import in_pyglet_context
def game_network(thread: "StoppableThread", window: "Window", connection: socket.socket, host: bool):
def game_network(
thread: "StoppableThread",
connection: socket.socket,
game_scene: Game,
):
"""
Run the networking to make the game work and react with the other player
:param game_scene: the scene of the game
: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)
game_scene.my_turn = host
while True:
data: Any = Packet.from_connection(connection)

View file

@ -0,0 +1,42 @@
import struct
from dataclasses import dataclass, field
from source.network.packet.abc import Packet
@dataclass
class PacketSettings(Packet):
username: str = field()
grid_width: int = field()
grid_height: int = field()
host_start: bool = field()
boat_size: list = field()
packet_size: int = 51
packet_format: str = ">16sBB?32B"
def to_bytes(self):
boat_size = self.boat_size + ([0] * (32 - len(self.boat_size)))
return struct.pack(
self.packet_format,
self.username.encode("utf-8"),
self.grid_width,
self.grid_height,
self.host_start,
*boat_size
)
@classmethod
def from_bytes(cls, data: bytes):
username, grid_width, grid_height, host_start, *boat_size = struct.unpack(cls.packet_format, data)
return cls(
username=username.replace(b"\x00", b"").decode("utf-8"),
grid_width=grid_width,
grid_height=grid_height,
host_start=host_start,
boat_size=list(filter(lambda value: value != 0, boat_size))
)

View file

@ -0,0 +1,17 @@
from dataclasses import dataclass, field
from source.network.packet.abc import Packet
@dataclass
class PacketUsername(Packet):
username: str = field()
packet_size: int = 16
def to_bytes(self):
return self.username.encode("utf-8")
@classmethod
def from_bytes(cls, data: bytes):
return cls(username=data.decode("utf-8"))

View file

@ -2,3 +2,5 @@ from .PacketChat import PacketChat
from .PacketBombPlaced import PacketBombPlaced
from .PacketBombState import PacketBombState
from .PacketBoatPlaced import PacketBoatPlaced
from .PacketSettings import PacketSettings
from .PacketUsername import PacketUsername

View file

@ -2,6 +2,8 @@ import socket
from abc import abstractmethod, ABC
from typing import Type, Optional
# TODO: struct.calcsize() au lieu de packet_size
class Packet(ABC):
packed_header: bytes