From 6c39a13aca321bb8e1ac5ca8711260f09f88f5ad Mon Sep 17 00:00:00 2001 From: starnakin Date: Sat, 23 Dec 2023 12:54:33 +0100 Subject: [PATCH] core: recreation of matchmaking, add: matchmaking support multiple modes --- frontend/static/js/api/chat/channel.js | 2 +- frontend/static/js/api/matchmaking.js | 14 +-- frontend/static/js/views/MatchMakingView.js | 32 ++++++- games/models.py | 2 +- matchmaking/consumers.py | 34 +++----- matchmaking/models.py | 97 +++++++++++++++++++-- matchmaking/routing.py | 2 +- tournament/viewset.py | 1 - 8 files changed, 143 insertions(+), 41 deletions(-) diff --git a/frontend/static/js/api/chat/channel.js b/frontend/static/js/api/chat/channel.js index 13c3db6..5b36dd9 100644 --- a/frontend/static/js/api/chat/channel.js +++ b/frontend/static/js/api/chat/channel.js @@ -14,7 +14,7 @@ class Channel { // reload = function to use when we receive a message async connect(reload) { - let url = `ws://${window.location.host}/ws/chat/${this.channel_id}/`; + let url = `wss://${window.location.host}/ws/chat/${this.channel_id}/`; this.chatSocket = new WebSocket(url); this.chatSocket.onmessage = (event) =>{ diff --git a/frontend/static/js/api/matchmaking.js b/frontend/static/js/api/matchmaking.js index ce0adc8..8dd76e5 100644 --- a/frontend/static/js/api/matchmaking.js +++ b/frontend/static/js/api/matchmaking.js @@ -11,18 +11,21 @@ class MatchMaking * @type {Client} */ this.client = client + this.searching = false; } - async start(func) + async start(func, mode) { if (!await this.client.isAuthentificate()) return null; - let url = `wss://${window.location.host}/ws/matchmaking/`; + let url = `wss://${window.location.host}/ws/matchmaking/${mode}`; - this._chatSocket = new WebSocket(url); + this._socket = new WebSocket(url); - this._chatSocket.onmessage = function (event) { + this.searching = true; + + this._socket.onmessage = function (event) { const data = JSON.parse(event.data); func(data.game_id) }; @@ -30,7 +33,8 @@ class MatchMaking async stop() { - this._chatSocket.close() + this.searching = false; + this._socket.close() } } diff --git a/frontend/static/js/views/MatchMakingView.js b/frontend/static/js/views/MatchMakingView.js index ca02363..01c0522 100644 --- a/frontend/static/js/views/MatchMakingView.js +++ b/frontend/static/js/views/MatchMakingView.js @@ -7,18 +7,42 @@ function game_found(game_id) } export default class extends AbstractView { - constructor(params) { - super(params, "Dashboard"); + constructor(params) + { + super(params, "Matchmaking"); + } + + async press_button() + { + if (client.matchmaking.searching) + { + client.matchmaking.stop(); + document.getElementById("button").value = "Find a game" + } + else + { + await this.matchmaking(); + document.getElementById("button").value = "Stop matchmaking" + } + } + + async matchmaking() + { + let nb_players = document.getElementById("nb_players-input").value + + client.matchmaking.start(game_found, nb_players); } async postInit() { - await client.matchmaking.start(game_found) + document.getElementById("button").onclick = this.matchmaking } async getHtml() { return ` -

finding

+

Select mode

+ + `; } diff --git a/games/models.py b/games/models.py index 0493dc5..514e52c 100644 --- a/games/models.py +++ b/games/models.py @@ -5,7 +5,7 @@ class GameModel(models.Model): finished = models.BooleanField(default=False) started = models.BooleanField(default=False) - winner_id = models.IntegerField(null=True, blank=True) + winner_id = models.IntegerField(default=-1) def create(self, users_id: [int]): self.save() diff --git a/matchmaking/consumers.py b/matchmaking/consumers.py index dfa65e8..56b0280 100644 --- a/matchmaking/consumers.py +++ b/matchmaking/consumers.py @@ -6,8 +6,7 @@ from games.models import GameModel import json -queue_id: [int] = [] -queue_ws: [WebsocketConsumer] = [] +from .models import Waiter, WaitingRoom, WaitingRoomManager, normal class MatchMaking(WebsocketConsumer): @@ -24,25 +23,20 @@ class MatchMaking(WebsocketConsumer): self.channel_layer.group_add(self.group_name, self.channel_name) - self.accept() + self.mode = int(self.scope['url_route']['kwargs']['mode']) + self.group_name = self.mode - global queue_id, queue_ws - queue_id.append(user.pk) - queue_ws.append(self) + waiting_room: WaitingRoom = normal.get(self.mode) + waiting_room.append(Waiter(user.pk, self)) - if len(set(queue_id)) == 2: - game_id: int = GameModel().create(set(queue_id)) - event = {"game_id": game_id} - for ws in queue_ws: - ws.send(text_data=json.dumps({'game_id': game_id})) - queue_id.clear() - queue_ws.clear() + print(len(waiting_room), "/", self.mode, [len(waiting_room),self.mode]) + if (len(waiting_room) == self.mode): + game_id: int = GameModel().create(waiting_room.get_users_id()) + waiting_room.broadcast({"detail": "Game found !", "game_id": game_id}) + waiting_room.clear() - def disconnect(self, close_code): - user: User = self.scope["user"] - global queue_id, queue_ws - if (user.pk in queue_id): - queue_ws.pop(queue_id.index(user.pk)) - queue_id.remove(user.pk) - self.channel_layer.group_discard(self.group_name, self.channel_name) \ No newline at end of file + waiting_room: WaitingRoom = normal.get(self.mode) + waiter: Waiter = waiting_room.get_waiter_by_socket(self) + if (waiter is not None): + waiting_room.remove(self.scope["user"].pk) \ No newline at end of file diff --git a/matchmaking/models.py b/matchmaking/models.py index 78dda1b..421bef8 100644 --- a/matchmaking/models.py +++ b/matchmaking/models.py @@ -1,11 +1,92 @@ from django.db import models -# Create your models here. -in_matchmaking = { - "tournaments": { +from channels.generic.websocket import WebsocketConsumer +import json - }, - "normal": { - - } -} \ No newline at end of file +# Create your models here. +class Waiter: + + def __init__(self, user_id: int, socket: WebsocketConsumer): + self.user_id: int = user_id + self.socket: WebsocketConsumer = socket + + def send(self, data: dict): + self.socket.send(text_data=json.dumps(data)) + + def accept(self): + self.socket.accept() + + def disconnect(self): + self.socket.disconnect() + + def __eq__(self, obj): + return self.user_id == obj.user_id + +class WaitingRoom: + + def __init__(self, waiting_room_manager, mode: int): + self._waiter_list: [Waiter] = [] + self._mode: int = mode + self._waiting_room_manager = waiting_room_manager + + def broadcast(self, data: dict): + for waiter in self._waiter_list: + waiter: Waiter + waiter.send(data) + + def clear(self): + self._waiter_list.clear() + + def get_waiter_by_socket(self, socket: WebsocketConsumer): + for waiter in self._waiter_list: + waiter: Waiter + if (waiter.socket is socket): + return waiter + return None + + def append(self, waiter: Waiter): + + self.remove(waiter) + waiter.accept() + self._waiter_list.append(waiter) + + def remove(self, users_id): + for waiter in self._waiter_list: + waiter: Waiter = waiter + if (waiter == users_id): + waiter.disconnect() + if (self.empty()): + self._waiting_room_manager.remove(self) + return + + def empty(self): + for _ in self._waiter_list: + return False + return True + + def get_users_id(self): + return [waiter.user_id for waiter in self._waiter_list] + + def __len__(self): + return len(self._waiter_list) + + + +class WaitingRoomManager: + + def __init__(self): + self._waiting_rooms: [WaitingRoom] = [] + + def get(self, mode: int): + for waiting_room in self._waiting_rooms: + waiting_room: WaitingRoom = waiting_room + if (waiting_room._mode == mode): + return waiting_room + tmp: WaitingRoom = WaitingRoom(self, mode) + self._waiting_rooms.append(tmp) + return tmp + + def remove(self, waiting_room: WaitingRoom): + self._waiting_rooms.remove(waiting_room) + +normal: WaitingRoomManager = WaitingRoomManager() \ No newline at end of file diff --git a/matchmaking/routing.py b/matchmaking/routing.py index eed5072..417779b 100644 --- a/matchmaking/routing.py +++ b/matchmaking/routing.py @@ -2,5 +2,5 @@ from django.urls import re_path from . import consumers websocket_urlpatterns = [ - re_path(r'ws/matchmaking/', consumers.MatchMaking.as_asgi()) + re_path(r'ws/matchmaking/(?P\d+)$', consumers.MatchMaking.as_asgi()) ] diff --git a/tournament/viewset.py b/tournament/viewset.py index a99caa9..bc27f11 100644 --- a/tournament/viewset.py +++ b/tournament/viewset.py @@ -7,7 +7,6 @@ from django.http import HttpRequest from django.contrib.auth import login from django.db.models import QuerySet -from matchmaking.models import in_matchmaking from .models import TournamentModel from .serializers import TournamentSerializer