core: fix: matchmaking and game

This commit is contained in:
starnakin 2024-04-22 11:37:08 +02:00
parent d31d3e053e
commit e125eb16c7
16 changed files with 135 additions and 134 deletions

View File

@ -21,12 +21,12 @@ class MatchMaking
* @param {Number} mode The number of players in a game * @param {Number} mode The number of players in a game
* @returns {Promise<?>} * @returns {Promise<?>}
*/ */
async start(receive_func, disconnect_func, gamemode, mode) async start(receive_func, disconnect_func, game_type, mode)
{ {
if (!await this.client.isAuthenticated()) if (!await this.client.isAuthenticated())
return null; return null;
let url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/matchmaking/${gamemode}/${mode}`; let url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/matchmaking/${game_type}/${mode}`;
this._socket = new WebSocket(url); this._socket = new WebSocket(url);

View File

@ -19,7 +19,7 @@ export default class extends AbstractAuthenticatedView {
} }
else else
{ {
await client.matchmaking.start(this.onreceive.bind(this), this.ondisconnect.bind(this), this.gamemode_input.value, this.nb_players_input.value); await client.matchmaking.start(this.onreceive.bind(this), this.ondisconnect.bind(this), this.game_type_input.value, this.nb_players_input.value);
this.button.innerHTML = lang.get("matchmakingStopSearch"); this.button.innerHTML = lang.get("matchmakingStopSearch");
} }
@ -34,10 +34,10 @@ export default class extends AbstractAuthenticatedView {
{ {
if (data.detail === "game_found") if (data.detail === "game_found")
{ {
if (this.gamemode_input.value == "pong") if (this.game_type_input.value == "pong")
navigateTo(`/games/${data.gamemode}/${data.game_id}`); navigateTo(`/games/${data.game_type}/${data.game_id}`);
else else
navigateTo(`/games/${data.gamemode}/${data.game_id}`); navigateTo(`/games/${data.game_type}/${data.game_id}`);
return; return;
} }
this.display_data(data); this.display_data(data);
@ -51,7 +51,7 @@ export default class extends AbstractAuthenticatedView {
addEnterEvent() addEnterEvent()
{ {
[this.nb_players_input, this.gamemode_input].forEach((input) => { [this.nb_players_input, this.game_type_input].forEach((input) => {
input.addEventListener('keydown', async ev => { input.addEventListener('keydown', async ev => {
@ -74,13 +74,13 @@ export default class extends AbstractAuthenticatedView {
}); });
} }
addChangeGameModeEvent() addChangegame_typeEvent()
{ {
let nb_players_div = document.getElementById("nb-players-div"); let nb_players_div = document.getElementById("nb-players-div");
this.gamemode_input.addEventListener("change", () => { this.game_type_input.addEventListener("change", () => {
if (this.gamemode_input.value === "tictactoe") if (this.game_type_input.value === "tictactoe")
{ {
nb_players_div.style.display = 'none'; nb_players_div.style.display = 'none';
this.nb_players_input.value = 2; this.nb_players_input.value = 2;
@ -96,7 +96,7 @@ export default class extends AbstractAuthenticatedView {
addEvents() addEvents()
{ {
this.addEnterEvent(); this.addEnterEvent();
this.addChangeGameModeEvent(); this.addChangegame_typeEvent();
this.addChangeNbPlayersEvent(); this.addChangeNbPlayersEvent();
} }
@ -104,7 +104,7 @@ export default class extends AbstractAuthenticatedView {
{ {
this.button = document.getElementById("toggle-search"); this.button = document.getElementById("toggle-search");
this.nb_players_input = document.getElementById("nb-players-input"); this.nb_players_input = document.getElementById("nb-players-input");
this.gamemode_input = document.getElementById("gamemode-input"); this.game_type_input = document.getElementById("game-type-input");
this.button.onclick = this.toggle_search.bind(this); this.button.onclick = this.toggle_search.bind(this);
@ -117,12 +117,12 @@ export default class extends AbstractAuthenticatedView {
<div class='border border-2 rounded bg-light-subtle mx-auto p-2 col-md-7 col-lg-4'> <div class='border border-2 rounded bg-light-subtle mx-auto p-2 col-md-7 col-lg-4'>
<h4 class='text-center fw-semibold mb-4' id="title">${lang.get("matchmakingTitle")}</h4> <h4 class='text-center fw-semibold mb-4' id="title">${lang.get("matchmakingTitle")}</h4>
<div> <div>
<div class='form-floating mb-2' id='gamemode-div'> <div class='form-floating mb-2' id='game_type-div'>
<select class='form-control' id='gamemode-input'> <select class='form-control' id='game-type-input'>
<option value='pong'>Pong</option> <option value='pong'>Pong</option>
<option value='tictactoe'>ticTacToe</option> <option value='tictactoe'>ticTacToe</option>
</select> </select>
<label for='gamemode-input'>${lang.get("gamemodeChoice")}</label> <label for='game-type-input'>${lang.get("game_typeChoice")}</label>
</div> </div>
<div class='form-floating mb-2' id='nb-players-div'> <div class='form-floating mb-2' id='nb-players-div'>
<input type='number' min='2' value='2' max='4' class='form-control' id='nb-players-input' placeholder='${lang.get("matchmakingNbPlayers")}'> <input type='number' min='2' value='2' max='4' class='form-control' id='nb-players-input' placeholder='${lang.get("matchmakingNbPlayers")}'>

View File

@ -3,25 +3,24 @@ from __future__ import annotations
from django.db import models from django.db import models
from django.db.models import QuerySet, CASCADE from django.db.models import QuerySet, CASCADE
from profiles.models import ProfileModel from django.contrib.auth.models import User
import time import time
# Create your models here.
class GameModel(models.Model): class GameModel(models.Model):
finished = models.BooleanField(default = False) finished = models.BooleanField(default = False)
started = models.BooleanField(default = False) started = models.BooleanField(default = False)
winner = models.ForeignKey(ProfileModel, on_delete=CASCADE, null=True, blank=True) winner = models.ForeignKey(User, on_delete=CASCADE, null=True, blank=True)
start_timestamp = models.BigIntegerField(null = True, blank = True) start_timestamp = models.BigIntegerField(null = True, blank = True)
stop_timestamp = models.BigIntegerField(null = True, blank = True) stop_timestamp = models.BigIntegerField(null = True, blank = True)
game_type = models.CharField(max_length = 60, default = "pong") game_type = models.CharField(max_length = 60, default = "pong")
def create(self, players: list[ProfileModel]): def create(self, players: list[User]):
self.save() self.save()
for player in players: for player in players:
GameMembersModel(game = self.pk, player=player).save() GameMembersModel(game = self, player=player).save()
return self.pk return self.pk
def start(self): def start(self):
@ -35,9 +34,12 @@ class GameModel(models.Model):
self.stop_timestamp = round(time.time() * 1000, 1) self.stop_timestamp = round(time.time() * 1000, 1)
self.save() self.save()
def get_players(self) -> list[ProfileModel]: def get_players(self) -> list[User]:
return [game_player.player for game_player in GameMembersModel.objects.filter(game = self)] return [game_player.player for game_player in GameMembersModel.objects.filter(game = self)]
def get_players_profiles(self) -> list[User]:
return [game_player.player.profilemodel for game_player in GameMembersModel.objects.filter(game = self)]
def get_score_by_player_id(self, player_id: int) -> list[int]: def get_score_by_player_id(self, player_id: int) -> list[int]:
query: QuerySet = GameGoalModel.objects.filter(game_id = self.pk, player_id = player_id) query: QuerySet = GameGoalModel.objects.filter(game_id = self.pk, player_id = player_id)
score_data: list[int] = [game_goal.timestamp for game_goal in query] score_data: list[int] = [game_goal.timestamp for game_goal in query]
@ -56,10 +58,10 @@ class GameModel(models.Model):
class GameMembersModel(models.Model): class GameMembersModel(models.Model):
game = models.ForeignKey(GameModel, on_delete=CASCADE) game = models.ForeignKey(GameModel, on_delete=CASCADE)
player = models.ForeignKey(ProfileModel, on_delete=CASCADE) player = models.ForeignKey(User, on_delete=CASCADE)
class GameGoalModel(models.Model): class GameGoalModel(models.Model):
game = models.ForeignKey(GameModel, on_delete=CASCADE) game = models.ForeignKey(GameModel, on_delete=CASCADE)
player = models.ForeignKey(ProfileModel, on_delete=CASCADE) player = models.ForeignKey(User, on_delete=CASCADE)
timestamp = models.IntegerField() timestamp = models.IntegerField()

View File

@ -6,6 +6,8 @@ from .ASpectator import ASpectator
from ..models import GameModel from ..models import GameModel
from django.contrib.auth.models import User
class AGame(AbstractRoom): class AGame(AbstractRoom):
def __init__(self, game_type: str, game_id: int, game_manager): def __init__(self, game_type: str, game_id: int, game_manager):
@ -16,23 +18,23 @@ class AGame(AbstractRoom):
self.model: GameModel = GameModel.objects.get(pk = game_id, game_type = game_type) self.model: GameModel = GameModel.objects.get(pk = game_id, game_type = game_type)
players_id: list[int] = self.model.get_players_id() players: list[User] = self.model.get_players()
self.players: list[APlayer] = [APlayer(player_id, None, self) for player_id in players_id] self.players: list[APlayer] = [APlayer(player.pk, None, self) for player in players]
self.spectators: list[ASpectator] = [] self.spectators: list[ASpectator] = []
self.game_id: int = game_id self.game_id: int = game_id
def get_players_id(self) -> list[int]: def get_players_id(self) -> list[int]:
return [player.user_id for player in self.players] return [player.pk for player in self.players]
def get_players_connected(self) -> list[APlayer]: def get_players_connected(self) -> list[APlayer]:
return [player for player in self.players if player.is_connected()] return [player for player in self.players if player.is_connected()]
def get_player_by_user_id(self, user_id: int) -> APlayer: def get_player_by_user_id(self, user_id: int) -> APlayer:
for player in self.players: for player in self.players:
if (player.user_id == user_id): if (player.user.pk == user_id):
return player return player
return None return None

View File

@ -10,9 +10,6 @@ if TYPE_CHECKING:
class APlayer(ASpectator): class APlayer(ASpectator):
def __init__(self, user_id: int, socket: WebsocketConsumer, game: AGame):
super().__init__(user_id, socket, game)
def is_connected(self) -> bool: def is_connected(self) -> bool:
return self.socket != None return self.socket != None

View File

@ -3,6 +3,8 @@ from channels.generic.websocket import WebsocketConsumer
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
from django.contrib.auth.models import User
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
@ -10,8 +12,8 @@ if TYPE_CHECKING:
class ASpectator(AbstractRoomMember): class ASpectator(AbstractRoomMember):
def __init__(self, user_id: int, socket: WebsocketConsumer, game): def __init__(self, user: User, socket: WebsocketConsumer, game):
super().__init__(user_id, socket) super().__init__(user, socket)
self.game: AGame = game self.game: AGame = game

View File

@ -22,7 +22,7 @@ import threading
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
pass from profiles.models import ProfileModel
class PongGame(AGame): class PongGame(AGame):
@ -36,7 +36,7 @@ class PongGame(AGame):
radius: float = min(config.MAP_SIZE_X, config.MAP_SIZE_Y) / 2 - 10 radius: float = min(config.MAP_SIZE_X, config.MAP_SIZE_Y) / 2 - 10
players_id: list[int] = self.model.get_players_id() players: list[ProfileModel] = self.model.get_players()
nb_sides = 4 nb_sides = 4
@ -58,16 +58,16 @@ class PongGame(AGame):
self.walls: list[Wall] self.walls: list[Wall]
self.players: list[PongPlayer] self.players: list[PongPlayer]
nb_players: int = len(players_id) nb_players: int = len(players)
if (nb_players == 2): if (nb_players == 2):
self.players = [PongPlayer(self, players_id[0], None, segments[0]), PongPlayer(self, players_id[1], None, segments[2])] self.players = [PongPlayer(self, players[0], None, segments[0]), PongPlayer(self, players[1], None, segments[2])]
self.walls = [Wall(segments[1].start, segments[1].stop), Wall(segments[3].start, segments[3].stop)] self.walls = [Wall(segments[1].start, segments[1].stop), Wall(segments[3].start, segments[3].stop)]
else: else:
self.players = [] self.players = []
self.walls = [] self.walls = []
for i in range(4): for i in range(4):
if (i < nb_players): if (i < nb_players):
self.players.append(PongPlayer(self, players_id[i], None, segments[i])) self.players.append(PongPlayer(self, players[i], None, segments[i]))
else: else:
self.walls.append(Wall(segments[i])) self.walls.append(Wall(segments[i]))

View File

@ -17,9 +17,9 @@ if TYPE_CHECKING:
class PongPlayer(APlayer): class PongPlayer(APlayer):
def __init__(self, game: PongGame, user_id: int, socket: WebsocketConsumer, rail: Segment) -> None: def __init__(self, game: PongGame, user: User, socket: WebsocketConsumer, rail: Segment) -> None:
super().__init__(user_id, socket, game) super().__init__(user, socket, game)
self.position: Position = Position(0.5, 0) self.position: Position = Position(0.5, 0)
@ -29,8 +29,6 @@ class PongPlayer(APlayer):
self.game: PongGame self.game: PongGame
self.username: str = User.objects.get(pk = self.user_id).username
def eliminate(self): def eliminate(self):
self.disconnect(1000) self.disconnect(1000)
@ -43,7 +41,7 @@ class PongPlayer(APlayer):
if (detail is None): if (detail is None):
return return
if (detail == "update_my_paddle_pos"): if (detail == "update_my_paddle_pos"):
self.update_position(data) self.update_position(data)
@ -119,8 +117,8 @@ class PongPlayer(APlayer):
def to_dict(self) -> dict: def to_dict(self) -> dict:
data = { data = {
"username": self.username, "username": self.user.username,
"id": self.user_id, "id": self.user.pk,
"position": self.position.to_dict(), "position": self.position.to_dict(),
"score": self.score, "score": self.score,

View File

@ -270,7 +270,6 @@ async def render_ball(game: PongGame):
async def render_players(game: PongGame): async def render_players(game: PongGame):
while True: while True:
for player in game._updated_players: for player in game._updated_players:
await SyncToAsync(game.broadcast)("update_player", player.to_dict(), [player]) await SyncToAsync(game.broadcast)("update_player", player.to_dict(), [player])

View File

@ -3,7 +3,8 @@ from rest_framework import serializers
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db.models import QuerySet from django.db.models import QuerySet
from .models import GameModel, GameMembersModel from .models import GameModel
from profiles.serializers.ProfileSerializer import ProfileSerializer
class GameSerializer(serializers.ModelSerializer): class GameSerializer(serializers.ModelSerializer):
@ -28,19 +29,4 @@ class GameSerializer(serializers.ModelSerializer):
return "waiting" return "waiting"
def get_players(self, instance: GameModel): def get_players(self, instance: GameModel):
players_data: list = [] return ProfileSerializer(instance.get_players_profiles(), many=True).data
for player_id in instance.get_players_id():
query: QuerySet = User.objects.filter(pk = player_id)
username: str = "Deleted User"
if (query.exists()):
username = query[0].username
data: dict = {
"id": player_id,
"username": username,
"score": instance.get_score_by_player_id(player_id)
}
players_data.append(data)
return players_data

View File

@ -6,15 +6,10 @@ from games.models import GameModel
import json import json
from .models import Waiter, WaitingRoom, WaitingRoomManager, normal from .models import Waiter, WaitingRoom, waiting_room_manager
class MatchMaking(WebsocketConsumer): class MatchMaking(WebsocketConsumer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.channel_name = "matchmaking"
self.group_name = "matchmaking"
def connect(self): def connect(self):
user: User = self.scope["user"] user: User = self.scope["user"]
@ -22,14 +17,12 @@ class MatchMaking(WebsocketConsumer):
if (user.is_anonymous or not user.is_authenticated): if (user.is_anonymous or not user.is_authenticated):
return return
self.channel_layer.group_add(self.group_name, self.channel_name)
self.mode: int = int(self.scope['url_route']['kwargs']['mode']) self.mode: int = int(self.scope['url_route']['kwargs']['mode'])
self.gamemode: str = self.scope['url_route']['kwargs']['gamemode'] self.game_type: str = self.scope['url_route']['kwargs']['game_type']
self.group_name = self.mode self.group_name = self.mode
waiting_room: WaitingRoom = normal.get(self.gamemode, self.mode) self.waiting_room: WaitingRoom = waiting_room_manager.get(self.game_type, self.mode)
waiting_room.append(Waiter(user.pk, self)) self.waiting_room.append(Waiter(user, self))
if (self.mode < 2 or self.mode > 4): if (self.mode < 2 or self.mode > 4):
data: dict = { data: dict = {
@ -39,23 +32,22 @@ class MatchMaking(WebsocketConsumer):
self.disconnect(1000) self.disconnect(1000)
return return
if (self.gamemode not in ["tictactoe", "pong"]): if (self.game_type not in ["tictactoe", "pong"]):
data: dict = { data: dict = {
"detail": "The gamemode must 'pong' or 'tictactoe'.", "detail": "The game_type must 'pong' or 'tictactoe'.",
} }
self.send(json.dumps(data)) self.send(json.dumps(data))
self.disconnect(1000) self.disconnect(1000)
return return
waiting_room.broadcast(f"{len(waiting_room)} / {waiting_room.mode}") self.waiting_room.broadcast(f"{len(self.waiting_room)} / {self.waiting_room.mode}")
if (len(waiting_room) == waiting_room.mode): if (len(self.waiting_room) == self.waiting_room.mode):
game_id: int = GameModel().create(self.gamemode, waiting_room.get_users_id()) game_id: int = GameModel(game_type=self.game_type).create(self.waiting_room.get_members())
waiting_room.broadcast("game_found", {"game_id": game_id, "gamemode": self.gamemode}) self.waiting_room.broadcast("game_found", {"game_id": game_id, "game_type": self.game_type})
waiting_room.clear()
def disconnect(self, close_code): def disconnect(self, close_code):
super().close(close_code) super().disconnect(close_code)
waiting_room: WaitingRoom = normal.get(self.gamemode, self.mode) waiting_room: WaitingRoom = waiting_room_manager.get(self.game_type, self.mode)
waiter: Waiter = waiting_room.get_member_by_socket(self) waiter: Waiter = waiting_room.get_member_by_socket(self)
if (waiter is not None): if (waiter is not None):
waiting_room.remove(waiter, close_code) waiting_room.remove(waiter)

View File

@ -13,29 +13,44 @@ class Waiter(AbstractRoomMember):
class WaitingRoom(AbstractRoom): class WaitingRoom(AbstractRoom):
def __init__(self, room_manager, gamemode: str, mode: int): def __init__(self, room_manager, game_type: str, mode: int):
super().__init__(room_manager) super().__init__(room_manager)
self.mode = mode self._member_list: set[Waiter]
self.gamemode = gamemode
self.mode: int = mode
self.game_type: str = game_type
def append(self, waiter: Waiter): def append(self, waiter: Waiter):
tmp: Waiter = self.get_member_by_user_id(waiter.user_id)
tmp: Waiter = self.get_member_by_user(waiter.user)
if (tmp is not None): if (tmp is not None):
tmp.send("Connection close: Another connection open with the same user id.") tmp.send("Connection close: Another connection open with the same user id.")
self.remove(tmp) self.remove(tmp)
waiter.socket.accept() waiter.socket.accept()
self._member_list.append(waiter)
super().append(waiter)
class WaitingRoomManager(AbstractRoomManager): class WaitingRoomManager(AbstractRoomManager):
def get(self, gamemode: str, mode: int): def __init__(self):
super().__init__()
self._room_list: set[WaitingRoom]
def get(self, game_type: str, mode: int) -> WaitingRoom:
for waiting_room in self._room_list: for waiting_room in self._room_list:
waiting_room: WaitingRoom waiting_room: WaitingRoom
if (waiting_room.mode == mode and waiting_room.gamemode == gamemode): if (waiting_room.mode == mode and waiting_room.game_type == game_type):
return waiting_room return waiting_room
tmp: WaitingRoom = WaitingRoom(self, gamemode, mode)
tmp: WaitingRoom = WaitingRoom(self, game_type, mode)
super().append(tmp) super().append(tmp)
return tmp return tmp
normal: WaitingRoomManager = WaitingRoomManager() waiting_room_manager: WaitingRoomManager = WaitingRoomManager()

View File

@ -2,5 +2,5 @@ from django.urls import re_path
from . import consumers from . import consumers
websocket_urlpatterns = [ websocket_urlpatterns = [
re_path(r'ws/matchmaking/(?P<gamemode>\w+)/(?P<mode>\d+)$', consumers.MatchMaking.as_asgi()) re_path(r'ws/matchmaking/(?P<game_type>\w+)/(?P<mode>\d+)$', consumers.MatchMaking.as_asgi())
] ]

View File

@ -1,51 +1,57 @@
from __future__ import annotations
from channels.generic.websocket import WebsocketConsumer from channels.generic.websocket import WebsocketConsumer
from .AbstractRoomMember import AbstractRoomMember from .AbstractRoomMember import AbstractRoomMember
from django.contrib.auth.models import User
from profiles.models import ProfileModel
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .AbstractRoomManager import AbstractRoomManager
class AbstractRoom: class AbstractRoom:
def __init__(self, room_manager): def __init__(self, room_manager: AbstractRoomManager):
self._member_list: list[AbstractRoomMember] = [] self._member_list: set[AbstractRoomMember] = set()
self.room_manager = room_manager self._room_manager: AbstractRoomManager = room_manager
def broadcast(self, detail: str, data: dict = {}): def broadcast(self, detail: str, data: dict = {}, excludes: set[AbstractRoomMember] = set()) -> None:
for member in self._member_list:
member: AbstractRoomMember members: set[AbstractRoomMember] = self._member_list - excludes
for member in members:
member.send(detail, data) member.send(detail, data)
def get_member_by_socket(self, socket: WebsocketConsumer) -> AbstractRoomMember | None:
for member in self._member_list:
if member.socket is socket:
return member
def get_member_by_user(self, user: User) -> AbstractRoomMember:
for member in self._member_list:
if member.user == user:
return member
def clear(self): def get_members_profiles(self) -> set[ProfileModel]:
self._member_list.clear() return set(member.user.profilemodel for member in self._member_list)
def get_members(self) -> set[ProfileModel]:
return set(member.user for member in self._member_list)
def get_member_by_socket(self, socket: WebsocketConsumer): def append(self, member: AbstractRoomMember) -> None:
for member in self._member_list: self._member_list.add(member)
member: AbstractRoomMember
if (member.socket is socket):
return member
return None
def get_member_by_user_id(self, user_id: int): def remove(self, member: AbstractRoomMember) -> None:
for member in self._member_list:
member: AbstractRoomMember
if (member.user_id == user_id):
return member
return None
def append(self, member: AbstractRoomMember):
self._member_list.append(member)
member.accept()
def remove(self, member: AbstractRoomMember, code: int = 1000):
self._member_list.remove(member) self._member_list.remove(member)
member.socket.disconnect(code)
def empty(self): def get_users(self) -> set[User]:
for _ in self._member_list: return set(member.user for member in self._member_list)
return False
return True
def get_users_id(self): def __len__(self) -> int:
return [member.user_id for member in self._member_list]
def __len__(self):
return len(self._member_list) return len(self._member_list)

View File

@ -5,8 +5,8 @@ class AbstractRoomManager:
def __init__(self): def __init__(self):
self._room_list: list[AbstractRoom] = [] self._room_list: list[AbstractRoom] = []
def remove(self, room: AbstractRoom): def remove(self, room: AbstractRoom) -> None:
self._room_list.remove(room) self._room_list.remove(room)
def append(self, room: AbstractRoom): def append(self, room: AbstractRoom) -> None:
self._room_list.append(room) self._room_list.append(room)

View File

@ -1,14 +1,16 @@
from channels.generic.websocket import WebsocketConsumer from channels.generic.websocket import WebsocketConsumer
from django.contrib.auth.models import User
import json import json
class AbstractRoomMember: class AbstractRoomMember:
def __init__(self, user_id: int, socket: WebsocketConsumer): def __init__(self, user: User, socket: WebsocketConsumer):
self.user_id: int = user_id self.user: User = user
self.socket: WebsocketConsumer = socket self.socket: WebsocketConsumer = socket
def send(self, detail: str, data: dict = {}): def send(self, detail: str, data: dict = {}) -> None:
raw_data: dict = {"detail": detail} raw_data: dict = {"detail": detail}
raw_data.update(data) raw_data.update(data)
self.socket.send(text_data=json.dumps(raw_data)) self.socket.send(text_data=json.dumps(raw_data))