core: recreation of matchmaking, add: matchmaking support multiple modes

This commit is contained in:
starnakin 2023-12-23 12:54:33 +01:00
parent 278e2cbe54
commit 6c39a13aca
8 changed files with 143 additions and 41 deletions

View File

@ -14,7 +14,7 @@ class Channel {
// reload = function to use when we receive a message // reload = function to use when we receive a message
async connect(reload) { 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 = new WebSocket(url);
this.chatSocket.onmessage = (event) =>{ this.chatSocket.onmessage = (event) =>{

View File

@ -11,18 +11,21 @@ class MatchMaking
* @type {Client} * @type {Client}
*/ */
this.client = client this.client = client
this.searching = false;
} }
async start(func) async start(func, mode)
{ {
if (!await this.client.isAuthentificate()) if (!await this.client.isAuthentificate())
return null; 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); const data = JSON.parse(event.data);
func(data.game_id) func(data.game_id)
}; };
@ -30,7 +33,8 @@ class MatchMaking
async stop() async stop()
{ {
this._chatSocket.close() this.searching = false;
this._socket.close()
} }
} }

View File

@ -7,18 +7,42 @@ function game_found(game_id)
} }
export default class extends AbstractView { export default class extends AbstractView {
constructor(params) { constructor(params)
super(params, "Dashboard"); {
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() async postInit()
{ {
await client.matchmaking.start(game_found) document.getElementById("button").onclick = this.matchmaking
} }
async getHtml() { async getHtml() {
return ` return `
<h1>finding<h1> <h1>Select mode<h1>
<input type="number" value="2" id="nb_players-input">
<input type="button" value="Find a game" id="button">
`; `;
} }

View File

@ -5,7 +5,7 @@ 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_id = models.IntegerField(null=True, blank=True) winner_id = models.IntegerField(default=-1)
def create(self, users_id: [int]): def create(self, users_id: [int]):
self.save() self.save()

View File

@ -6,8 +6,7 @@ from games.models import GameModel
import json import json
queue_id: [int] = [] from .models import Waiter, WaitingRoom, WaitingRoomManager, normal
queue_ws: [WebsocketConsumer] = []
class MatchMaking(WebsocketConsumer): class MatchMaking(WebsocketConsumer):
@ -24,25 +23,20 @@ class MatchMaking(WebsocketConsumer):
self.channel_layer.group_add(self.group_name, self.channel_name) 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 waiting_room: WaitingRoom = normal.get(self.mode)
queue_id.append(user.pk) waiting_room.append(Waiter(user.pk, self))
queue_ws.append(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): def disconnect(self, close_code):
user: User = self.scope["user"] waiting_room: WaitingRoom = normal.get(self.mode)
global queue_id, queue_ws waiter: Waiter = waiting_room.get_waiter_by_socket(self)
if (user.pk in queue_id): if (waiter is not None):
queue_ws.pop(queue_id.index(user.pk)) waiting_room.remove(self.scope["user"].pk)
queue_id.remove(user.pk)
self.channel_layer.group_discard(self.group_name, self.channel_name)

View File

@ -1,11 +1,92 @@
from django.db import models from django.db import models
from channels.generic.websocket import WebsocketConsumer
import json
# Create your models here. # Create your models here.
in_matchmaking = { class Waiter:
"tournaments": {
}, def __init__(self, user_id: int, socket: WebsocketConsumer):
"normal": { 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()

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/', consumers.MatchMaking.as_asgi()) re_path(r'ws/matchmaking/(?P<mode>\d+)$', consumers.MatchMaking.as_asgi())
] ]

View File

@ -7,7 +7,6 @@ from django.http import HttpRequest
from django.contrib.auth import login from django.contrib.auth import login
from django.db.models import QuerySet from django.db.models import QuerySet
from matchmaking.models import in_matchmaking
from .models import TournamentModel from .models import TournamentModel
from .serializers import TournamentSerializer from .serializers import TournamentSerializer