6 Commits

Author SHA1 Message Date
ae20be25fb add: matchmaking 2023-12-12 12:06:13 +01:00
ad6cfdf08a Merge branch 'server' into feat/matchmaking 2023-12-12 10:01:12 +01:00
326724930a Merge branch 'server' into feat/matchmaking 2023-12-11 15:55:12 +01:00
fb1b71ade6 Merge branch 'server' into feat/matchmaking 2023-12-11 15:24:12 +01:00
336257d1d0 init view 2023-12-11 13:40:01 +01:00
f66b3883c1 init app 2023-12-11 13:39:53 +01:00
44 changed files with 254 additions and 639 deletions

2
.gitignore vendored
View File

@ -3,4 +3,4 @@
db.sqlite3 db.sqlite3
**/migrations/** **/migrations/**
/profiles/static/avatars/* /profiles/static/avatars/*
!/profiles/static/avatars/default.env !/profiles/static/avatars/default

View File

@ -22,16 +22,10 @@ pip install -r requirements.txt
``` ```
- Setup database - Setup database
``` ```
python manage.py runserver makemigrations profiles python manage.py runserver makemigrations profiles games
python manage.py runserver makemigrations chat python manage.py migrate profiles games
python manage.py migrate
``` ```
- Start the developpement server - Start the developpement server
``` ```
python manage.py runserver 0.0.0.0:8000 python manage.py runserver 0.0.0.0:8000
``` ```
coc nvim
```
pip install django-stubs
```

View File

@ -1,6 +1,3 @@
from django.contrib import admin from django.contrib import admin
from .models import ChannelModel, MemberModel, MessageModel
admin.site.register(ChannelModel) # Register your models here.
admin.site.register(MemberModel)
admin.site.register(MessageModel)

View File

@ -1,24 +1,10 @@
import json import json
from channels.generic.websocket import WebsocketConsumer from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync from asgiref.sync import async_to_sync
from .models import MemberModel, MessageModel
import time
class ChatConsumer(WebsocketConsumer): class ChatConsumer(WebsocketConsumer):
def connect(self): def connect(self):
channel_id : str = self.scope['path'].split('/')[3] self.room_group_name = 'test'
self.room_group_name = 'chat' + channel_id
user = self.scope["user"]
if (user.is_anonymous or not user.is_authenticated):
return
if MemberModel.objects.filter(member_id=user.pk, channel_id=int(channel_id)).count() != 1:
return
if (self.channel_layer == None):
return
async_to_sync(self.channel_layer.group_add)( async_to_sync(self.channel_layer.group_add)(
self.room_group_name, self.room_group_name,
@ -28,58 +14,23 @@ class ChatConsumer(WebsocketConsumer):
self.accept() self.accept()
def receive(self, text_data=None, bytes_data=None): def receive(self, text_data):
if text_data == None:
return
text_data_json = json.loads(text_data) text_data_json = json.loads(text_data)
message = text_data_json['message'] message = text_data_json['message']
channel_id : int = int(self.scope['path'].split('/')[3])
user = self.scope["user"]
if (user.is_anonymous or not user.is_authenticated):
return
if MemberModel.objects.filter(member_id=user.pk, channel_id=channel_id).count() != 1:
return
if (self.channel_layer == None):
return
print(message)
message_time : int = int(time.time() * 1000)
async_to_sync(self.channel_layer.group_send)( async_to_sync(self.channel_layer.group_send)(
self.room_group_name, self.room_group_name,
{ {
'type':'chat_message', 'type':'chat_message',
'author_id':user.pk, 'message':message
'content':message,
'time':message_time,
} }
) )
new_message = MessageModel()
new_message.channel_id = channel_id
new_message.author_id = user.pk
new_message.content = message
new_message.time = message_time
new_message.save()
def chat_message(self, event): def chat_message(self, event):
message = event['message']
channel_id : int = int(self.scope['path'].split('/')[3])
user = self.scope["user"]
if (user.is_anonymous or not user.is_authenticated):
return
if MemberModel.objects.filter(member_id=user.pk, channel_id=channel_id).count() != 1:
return
self.send(text_data=json.dumps({ self.send(text_data=json.dumps({
'type':'chat', 'type':'chat',
'author_id':event['author_id'], 'username':self.scope["user"].username,
'content':event['content'], 'message':message
'time': event['time'],
})) }))

View File

@ -1,24 +1,3 @@
from django.db import models from django.db import models
from django.db.models import IntegerField
from django.contrib.auth.models import User
from django.contrib import admin
# Create your models here. # Create your models here.
class ChannelModel(models.Model):
pass
class MemberModel(models.Model):
member_id = IntegerField(primary_key=False)
channel_id = IntegerField(primary_key=False)
def __str__(self):
return "member_id: " + str(self.member_id) + ", channel_id: " + str(self.channel_id)
class MessageModel(models.Model):
channel_id = IntegerField(primary_key=False)
author_id = IntegerField(primary_key=False)
content = models.CharField(max_length=255)
time = IntegerField(primary_key=False)
def __str__(self):
return "author_id: " + str(self.author_id) + ", channel_id: " + str(self.channel_id) + ", content: " + self.content

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/chat/(?P<chat_id>\d+)/$', consumers.ChatConsumer.as_asgi()) re_path(r'ws/socket-server/', consumers.ChatConsumer.as_asgi())
] ]

View File

@ -1,8 +0,0 @@
from rest_framework import serializers
from .models import ChannelModel
class ChannelSerializer(serializers.ModelSerializer):
class Meta:
model = ChannelModel
fields = []

View File

@ -1,10 +0,0 @@
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
from . import views
urlpatterns = [
path("<int:pk>", views.ChatView.as_view(), name="chat_page"),
path("", views.ChatsView.as_view(), name="chats_page"),
]

View File

@ -1,100 +1,3 @@
from rest_framework.views import APIView from django.shortcuts import render
from rest_framework.response import Response
from rest_framework import authentication, permissions, status
from rest_framework.authentication import SessionAuthentication
from .models import ChannelModel, MemberModel, MessageModel
from django.core import serializers
class ChatView(APIView): # Create your views here.
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
def get(self, request, pk):
if (ChannelModel.objects.filter(pk=pk)):
return Response({'channel_id': pk})
else:
return Response("Channel doesn't exist")
def delete(self, request, pk):
ChannelModel.objects.filter(pk=pk).delete()
MessageModel.objects.filter(pk=pk).delete()
MemberModel.objects.filter(pk=pk).delete()
return Response({'channel_id': pk})
"""
def post(self, request, pk):
channel = ChannelModel.objects.filter(pk=pk)
message = request.data.get("message", [])
print(message)
if (message == []):
return Response('No message', status=status.HTTP_400_BAD_REQUEST)
new_message = MessageModel()
new_message.channel_id = message["channel_id"]
new_message.author_id = message["author_id"]
new_message.content = message["content"]
new_message.time = message["time"]
new_message.save()
messages = MessageModel.objects.filter(channel_id=pk)
messages = serializers.serialize("json", messages)
return Response({'messages':messages}, status=status.HTTP_200_OK)
"""
class ChatsView(APIView):
def post(self, request):
data: dict = request.data
users_id = request.data.get("users_id", [])
if len(users_id) < 2:
return Response('Not enought members to create the channel', status=status.HTTP_400_BAD_REQUEST)
if users_id[0] == users_id[1]:
return Response('Same member', status=status.HTTP_400_BAD_REQUEST)
for user_id1 in users_id:
for member1 in MemberModel.objects.filter(member_id=user_id1):
for user_id2 in users_id:
if user_id1 == user_id2:
continue
for member2 in MemberModel.objects.filter(member_id=user_id2):
if (member1.channel_id == member2.channel_id):
messages = MessageModel.objects.filter(channel_id=member1.channel_id)
messages = serializers.serialize("json", messages)
return Response({'channel_id': member1.channel_id, 'messages':messages}, status=status.HTTP_200_OK)
new_channel = ChannelModel()
new_channel.save()
for user_id in users_id:
new_member = MemberModel()
new_member.channel_id = new_channel.pk
new_member.member_id = user_id
new_member.save()
return Response({'channel_id': new_channel.pk}, status=status.HTTP_201_CREATED)
def delete(self, request):
data: dict = request.data
users_id = request.data.get("users_id", [])
#print(list(MemberModel.objects.all()))
for user_id1 in users_id:
for member1 in MemberModel.objects.filter(member_id=user_id1):
for user_id2 in users_id:
if user_id1 == user_id2:
break
for member2 in MemberModel.objects.filter(member_id=user_id2):
if (member1.channel_id == member2.channel_id):
MessageModel.objects.filter(channel_id=member1.channel_id).delete()
member1.delete()
member2.delete()
ChannelModel.objects.get(pk=member1.channel_id).delete()
return Response("Channel removed", status=status.HTTP_200_OK)
return Response("Channel doesn't exist", status=status.HTTP_404_NOT_FOUND)

View File

@ -1,70 +0,0 @@
#app img
{
max-height: 100px;
max-width: 100px;
}
#app ul
{
margin: 5px 0 0 0;
padding: 0 0 0 0;
list-style-type: none;
}
#app li
{
margin: 10px 10px 0 0;
}
#app #chats {
overflow: hidden;
display: flex;
text-align: left;
}
#app #users {
margin-right: 50px;
}
#app #chat {
position: relative;
height: 100%;
width: 60%;
/*border: 4px solid green;*/
overflow:hidden;
/*overflow-y: scroll;
overflow-x: hidden;*/
}
#app #input_chat{
position: sticky;
bottom: 0;
/*width: calc(100% - 8px);*/
width: 100%;
border: none;
outline: none;
border-bottom: 2px solid green;
caret-color: green;
}
#app #you {
text-align: left;
position: relative;
max-width: 48%;
left: 10px;
margin: 5px 0 0 0;
color: green;
word-wrap: break-word;
}
#app #other {
text-align: right;
position: relative;
max-width: 48%;
margin: 5px 0 0 auto;
right: 10px;
color: red;
/* permet le retour à la ligne à la place de dépasser*/
word-wrap: break-word;
}

View File

@ -1,77 +0,0 @@
import {Message} from "./message.js"
class Channel {
constructor(client, channel_id, members_id, messages, reload) {
this.client = client;
this.channel_id = channel_id;
this.members_id = members_id;
this.messages = [];
if (messages != undefined)
this.updateMessages(messages);
this.connect(reload);
}
// reload = function to use when we receive a message
async connect(reload) {
let url = `ws://${window.location.host}/ws/chat/${this.channel_id}/`;
this.chatSocket = new WebSocket(url);
this.chatSocket.onmessage = (event) =>{
let data = JSON.parse(event.data)
this.messages.push(new Message(
this.channel_id,
data.author_id,
data.content,
data.time,
));
reload();
};
}
async disconnect() {
this.chatSocket.close();
}
updateMessages(messages) {
messages = JSON.parse(messages);
let new_messages = [];
messages.forEach((message) => {
message = message["fields"];
new_messages.push(new Message(
message["channel_id"],
message["author_id"],
message["content"],
message["time"],
));
});
//console.log(new_messages);
this.messages = new_messages;
return new_messages;
}
async sendMessageChannel(message) {
if (this.chatSocket == undefined)
return;
this.chatSocket.send(JSON.stringify({
'message':message
}));
}
async deleteChannel() {
let response = await this.client._delete("/api/chat/" + this.channel_id, {
});
let data = await response.json();
return data;
}
}
export {Channel}

View File

@ -1,37 +0,0 @@
import {Channel} from "./channel.js"
import {Message} from "./message.js"
class Channels {
constructor(client) {
this.client = client;
}
async createChannel(users_id, reload) {
let response = await this.client._post("/api/chat/", {
users_id:users_id
});
let data = await response.json();
let exit_code = await response.status;
if (exit_code >= 300)
return undefined;
let messages = undefined;
if (exit_code == 200)
messages = data.messages;
return new Channel(this.client, data.channel_id, users_id, messages, reload);
}
async deleteChannel(users_id) {
let response = await this.client._delete("/api/chat/", {
users_id:users_id
});
let data = await response.json();
console.log(response.status)
return data;
}
}
export {Channels}

View File

@ -1,10 +0,0 @@
class Message {
constructor(channel_id, author_id, content, time) {
this.channel_id = channel_id;
this.author_id = author_id;
this.content = content;
this.time = time;
}
}
export {Message}

View File

@ -1,7 +1,7 @@
import { Account } from "./account.js"; import { Account } from "./account.js";
import { MatchMaking } from "./matchmaking.js";
import { Profile } from "./profile.js"; import { Profile } from "./profile.js";
import { Profiles } from "./profiles.js"; import { Profiles } from "./profiles.js";
import { Channels } from './chat/channels.js';
function getCookie(name) function getCookie(name)
{ {
@ -20,10 +20,8 @@ class Client
this._url = url; this._url = url;
this.account = new Account(this); this.account = new Account(this);
this.profiles = new Profiles(this); this.profiles = new Profiles(this);
this.matchmaking = new MatchMaking(this);
this._logged = undefined; this._logged = undefined;
this.channels = new Channels(this);
this.channel = undefined;
} }
async isAuthentificate() async isAuthentificate()

View File

@ -0,0 +1,42 @@
import { client, navigateTo } from "../index.js"
class MatchMaking
{
/**
* @param {client} client
*/
constructor(client)
{
/**
* @type {client}
*/
this.client = client
}
async start(func)
{
if (!await this.client.isAuthentificate())
return null;
console.log(func)
this.callback = func
console.log(this.callback)
let url = `wss://${window.location.host}/ws/matchmaking/`;
this._chatSocket = new WebSocket(url);
this._chatSocket.onmessage = function (event) {
const data = JSON.parse(event.data);
console.log(func, data)
func(data.game_id)
};
}
async stop()
{
this._chatSocket.close()
}
}
export {MatchMaking}

View File

@ -1,7 +1,6 @@
import LoginView from "./views/accounts/LoginView.js"; import LoginView from "./views/accounts/LoginView.js";
import Dashboard from "./views/Dashboard.js"; import Dashboard from "./views/Dashboard.js";
import Settings from "./views/Settings.js"; import Settings from "./views/Settings.js";
import Search from "./views/Search.js";
import Chat from "./views/Chat.js"; import Chat from "./views/Chat.js";
import HomeView from "./views/HomeView.js"; import HomeView from "./views/HomeView.js";
import RegisterView from "./views/accounts/RegisterView.js"; import RegisterView from "./views/accounts/RegisterView.js";
@ -11,6 +10,8 @@ import { Client } from "./api/client.js";
import AbstractRedirectView from "./views/AbstractRedirectView.js"; import AbstractRedirectView from "./views/AbstractRedirectView.js";
import MeView from "./views/MeView.js"; import MeView from "./views/MeView.js";
import ProfilePageView from "./views/profiles/ProfilePageView.js"; import ProfilePageView from "./views/profiles/ProfilePageView.js";
import ProfilesView from "./views/profiles/ProfilesView.js";
import MatchMakingView from "./views/MatchMakingView.js";
let client = new Client(location.protocol + "//" + location.host) let client = new Client(location.protocol + "//" + location.host)
@ -35,15 +36,16 @@ const navigateTo = async (uri) => {
const router = async (uri) => { const router = async (uri) => {
const routes = [ const routes = [
{ path: "/", view: Dashboard }, { path: "/", view: Dashboard },
{ path: "/profiles", view: ProfilesView},
{ path: "/profiles/:id", view: ProfilePageView }, { path: "/profiles/:id", view: ProfilePageView },
{ path: "/settings", view: Settings }, { path: "/settings", view: Settings },
{ path: "/login", view: LoginView }, { path: "/login", view: LoginView },
{ path: "/logout", view: LogoutView }, { path: "/logout", view: LogoutView },
{ path: "/register", view: RegisterView }, { path: "/register", view: RegisterView },
{ path: "/search", view: Search },
{ path: "/chat", view: Chat }, { path: "/chat", view: Chat },
{ path: "/home", view: HomeView }, { path: "/home", view: HomeView },
{ path: "/me", view: MeView }, { path: "/me", view: MeView },
{ path: "/matchmaking", view: MatchMakingView },
]; ];
// Test each route for potential match // Test each route for potential match

View File

@ -1,10 +1,10 @@
import AbstractView from "./AbstractView.js"; import AbstractAuthentifiedView from "./AbstractAuthentifiedView.js";
export default class extends AbstractView { export default class extends AbstractAuthentifiedView {
constructor(params) { constructor(params) {
super(params, "Chat"); super(params, "Chat");
let url = `ws://${window.location.host}/ws/chat/` let url = `wss://${window.location.host}/ws/socket-server/`
this.chatSocket = new WebSocket(url) this.chatSocket = new WebSocket(url)
this.chatSocket.onmessage = function(e){ this.chatSocket.onmessage = function(e){
@ -44,7 +44,7 @@ export default class extends AbstractView {
<h1>Chat</h1> <h1>Chat</h1>
<form id="form"> <form id="form">
<input type="text" name="message" placeholder="message"/> <input type="text" name="message" />
</form> </form>
<div id="messages"> <div id="messages">

View File

@ -9,6 +9,7 @@ export default class extends AbstractAuthentificateView {
async getHtml() { async getHtml() {
return ` return `
<h1>HOME</h1> <h1>HOME</h1>
<a href="/matchmaking" class="nav__link" data-link>Play a game</a>
<a href="/me" class="nav__link" data-link>Me</a> <a href="/me" class="nav__link" data-link>Me</a>
<a href="/logout" class="nav__link" data-link>Logout</a> <a href="/logout" class="nav__link" data-link>Logout</a>
`; `;

View File

@ -0,0 +1,30 @@
import { client, navigateTo } from "../index.js";
import AbstractView from "./AbstractView.js";
function game_found(game_id)
{
navigateTo(`/games/${game_id}`)
}
export default class extends AbstractView {
constructor(params) {
super(params, "Dashboard");
}
async postInit()
{
await client.matchmaking.start(game_found)
console.log("start matchmaking")
}
async getHtml() {
return `
<h1>finding<h1>
`;
}
async leavePage()
{
await client.matchmaking.stop();
}
}

View File

@ -1,208 +0,0 @@
import AbstractView from "./AbstractView.js";
import {client} from "../index.js";
import {Message} from "../api/chat/message.js"
export default class extends AbstractView {
constructor(params) {
super(params, "Search");
}
async postInit() {
let search = document.getElementById("input_user");
search.addEventListener("input", this.users)
let chat_input = document.getElementById("input_chat");
//chat_input.addEventListener("keydown", this.chat_manager)
this.users();
this.chat();
}
async users() {
let search = document.getElementById("input_user").value.toLowerCase();
let logged = await client.isAuthentificate();
let users = await client.profiles.all();
let list_users = document.getElementById('list_users');
list_users.innerHTML = "";
users.filter(user => user.username.toLowerCase().startsWith(search) == true).forEach((user) => {
var new_user = document.createElement("li");
// username
let username = document.createElement("a");
username.href = `/profiles/${user.user_id}`;
username.appendChild(document.createTextNode(user.username));
new_user.appendChild(username);
// space
new_user.appendChild(document.createTextNode(" "));
// chat
if (logged && client.me.user_id != user.user_id) {
let add_chat = document.createElement("a");
add_chat.addEventListener("click", async () => {
if (client.channel != undefined) {
client.channel.members_id.forEach((member_id) => {
if (member_id == user.user_id)
client.channel = undefined;
});
if (client.channel == undefined)
return this.hideChat();
client.channel.disconnect();
}
client.channel = await client.channels.createChannel([client.me.user_id , user.user_id], this.chat);
this.chat();
});
add_chat.appendChild(document.createTextNode("Chat"));
new_user.appendChild(add_chat);
/*new_user.appendChild(document.createTextNode(" "));
let remove = document.createElement("a");
remove.addEventListener("click", async () => {
if (client.me.user_id != user.user_id) {
await client.channels.deleteChannel([client.me.user_id , user.user_id]);
if
client.channel.disconnect();
client.channel = undefined;
this.chat()
}
});
remove.appendChild(document.createTextNode("Remove"));
new_user.appendChild(remove);*/
}
// break line
new_user.appendChild(document.createElement("br"));
// avatar
var img = document.createElement("img");
img.src = user.avatar_url;
new_user.appendChild(img);
list_users.appendChild(new_user);
});
//console.log(list_users);
}
async chat() {
let logged = await client.isAuthentificate();
let reload = document.getElementById("messages");
if (reload != null)
reload.remove();
reload = document.getElementById("members");
if (reload != null)
reload.remove();
if (client.channel == undefined || !logged)
return ;
let chats = document.getElementById("chats");
if (document.getElementById("chat") == null) {
let chat = document.createElement("div");
chat.id = "chat";
chats.appendChild(chat);
}
// div des messages
let messages = document.createElement("div");
messages.id = "messages";
if (document.getElementById("input_chat") == null)
chat.appendChild(messages);
else
document.getElementById("input_chat").before(messages);
// Input pour rentrer un message
if (document.getElementById("input_chat") == null) {
let chat_input = document.createElement("input");
chat_input.id="input_chat";
chat_input.type="text";
chat_input.name="message";
chat_input.placeholder="message bozo";
chat_input.maxLength=255;
chat.appendChild(chat_input);
chat_input.addEventListener("keydown", async () => {
if (event.keyCode == 13 && client.channel != undefined) {
//let chat_input = document.getElementById("input_chat");
let chat_text = chat_input.value;
await client.channel.sendMessageChannel(chat_text)
// Reset
chat_input.value = "";
}
});
}
// nom des membres du chat
let users = await client.profiles.all();
let members = document.createElement("h2");
members.id = "members";
let usernames = "";
client.channel.members_id.forEach((member_id) => {
if (member_id != client.me.user_id) {
if (usernames.length > 0)
usernames += ", ";
usernames += (users.filter(user => user.user_id == member_id)[0].username);
}
});
members.appendChild(document.createTextNode(usernames));
messages.before(members);
// les messages
client.channel.messages.forEach((message) => {
let text = document.createElement("p");
text.appendChild(document.createTextNode(message.content));
if (message.author_id == client.me.user_id)
text.id = "you";
else
text.id = "other";
messages.appendChild(text);
});
}
async hideChat() {
let close = document.getElementById("chat");
if (close != null)
close.remove();
}
async leavePage() {
if (client.channel != undefined)
client.channel.disconnect();
client.channel = undefined
}
async getHtml() {
return `
<link rel="stylesheet" href="/static/css/search.css">
<div id="chats">
<div id="users">
<input id="input_user" type="text" name="message" placeholder="userbozo"/>
<ul id="list_users">
</ul>
</div>
</div>
`;
}
}

View File

@ -0,0 +1,40 @@
import AbstractView from "../AbstractView.js";
import { client } from "../../index.js"
export default class extends AbstractView {
constructor(params) {
super(params, "Profiles");
}
async postInit()
{
let profiles = await client.profiles.all()
let profile_list_element = document.getElementById("profile-list")
profiles.forEach((profile) => {
let profile_element = document.createElement("div");
profile_element.className = "item";
let avatar = document.createElement("img");
avatar.src = profile.avatar_url;
let username = document.createElement("a");
username.href = `/profiles/${profile.user_id}`;
username.appendChild(document.createTextNode(profile.username));
profile_element.appendChild(avatar);
profile_element.appendChild(username);
profile_list_element.appendChild(profile_element)
});
}
async getHtml() {
return `
<link rel="stylesheet" href="/static/css/profiles/profiles.css">
<div id="profile-list">
</div>
`;
}
}

View File

@ -10,10 +10,10 @@
<body> <body>
<nav class="nav"> <nav class="nav">
<a href="/" class="nav__link" data-link>Dashboard</a> <a href="/" class="nav__link" data-link>Dashboard</a>
<a href="/search" class="nav__link" data-link>Search</a> <a href="/profiles" class="nav__link" data-link>Profiles</a>
<a href="/home" class="nav__link" data-link>Home</a>
<a href="/login" class="nav__link" data-link>Login</a> <a href="/login" class="nav__link" data-link>Login</a>
<a href="/register" class="nav__link" data-link>Register</a> <a href="/register" class="nav__link" data-link>Register</a>
<a href="/chat" class="nav__link" data-link>Chat</a>
</nav> </nav>
<div id="app"></div> <div id="app"></div>
<script type="module" src="{% static 'js/index.js' %}"></script> <script type="module" src="{% static 'js/index.js' %}"></script>

0
games/__init__.py Normal file
View File

3
games/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
games/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class GamesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'games'

14
games/models.py Normal file
View File

@ -0,0 +1,14 @@
from django.db import models
# Create your models here.
class GameModel(models.Model):
def create(self, users_id: [int]):
self.save()
for user_id in users_id:
GameMembersModel(game_id=self.pk, member_id=user_id)
return self.pk
class GameMembersModel(models.Model):
game_id = models.IntegerField()
member_id = models.IntegerField()

3
games/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
games/views.py Normal file
View File

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

0
matchmaking/__init__.py Normal file
View File

3
matchmaking/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
matchmaking/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class MatchmakingConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'matchmaking'

48
matchmaking/consumers.py Normal file
View File

@ -0,0 +1,48 @@
from channels.generic.websocket import WebsocketConsumer
from django.contrib.auth.models import User
from games.models import GameModel
import json
queue_id: [int] = []
queue_ws: [WebsocketConsumer] = []
class MatchMaking(WebsocketConsumer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.channel_name = "matchmaking"
self.group_name = "matchmaking"
def connect(self):
user: User = self.scope["user"]
if (user.is_anonymous or not user.is_authenticated):
return
self.channel_layer.group_add(self.group_name, self.channel_name)
self.accept()
global queue_id, queue_ws
queue_id.append(user.pk)
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()
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)

3
matchmaking/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

6
matchmaking/routing.py Normal file
View File

@ -0,0 +1,6 @@
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/matchmaking/', consumers.MatchMaking.as_asgi())
]

3
matchmaking/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
matchmaking/views.py Normal file
View File

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

6
package-lock.json generated
View File

@ -1,6 +0,0 @@
{
"name": "ft_transcendence",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

View File

@ -10,7 +10,9 @@ https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
import os import os
from channels.routing import ProtocolTypeRouter, URLRouter from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack from channels.auth import AuthMiddlewareStack
import chat.routing import chat.routing
import matchmaking.routing
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
@ -20,7 +22,8 @@ application = ProtocolTypeRouter({
'http':get_asgi_application(), 'http':get_asgi_application(),
'websocket':AuthMiddlewareStack( 'websocket':AuthMiddlewareStack(
URLRouter( URLRouter(
chat.routing.websocket_urlpatterns chat.routing.websocket_urlpatterns +
matchmaking.routing.websocket_urlpatterns
) )
) )
}) })

View File

@ -43,10 +43,11 @@ INSTALLED_APPS = [
'channels', 'channels',
'daphne', 'daphne',
'matchmaking.apps.MatchmakingConfig',
'games.apps.GamesConfig',
'accounts.apps.AccountsConfig', 'accounts.apps.AccountsConfig',
'profiles.apps.ProfilesConfig', 'profiles.apps.ProfilesConfig',
'frontend.apps.FrontendConfig', 'frontend.apps.FrontendConfig',
'chat.apps.ChatConfig',
'corsheaders', 'corsheaders',
'rest_framework', 'rest_framework',

View File

@ -21,6 +21,5 @@ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('api/profiles/', include('profiles.urls')), path('api/profiles/', include('profiles.urls')),
path('api/accounts/', include('accounts.urls')), path('api/accounts/', include('accounts.urls')),
path('api/chat/', include('chat.urls')),
path('', include('frontend.urls')), path('', include('frontend.urls')),
] ]