From c2317d540496acb392bc95a689070008146be095 Mon Sep 17 00:00:00 2001 From: AdrienLSH Date: Mon, 4 Dec 2023 16:51:24 +0100 Subject: [PATCH 01/19] la putain de sa mere --- frontend/static/js/index.js | 5 ++- frontend/static/js/views/Game.js | 63 ++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 frontend/static/js/views/Game.js diff --git a/frontend/static/js/index.js b/frontend/static/js/index.js index e923833..56d5aca 100644 --- a/frontend/static/js/index.js +++ b/frontend/static/js/index.js @@ -8,6 +8,8 @@ import HomeView from "./views/HomeView.js"; import RegisterView from "./views/accounts/RegisterView.js"; import LogoutView from "./views/accounts/LogoutView.js"; +import GameView from "./views/Game.js" + import { Client } from "./api/client.js"; import AbstractRedirectView from "./views/AbstractRedirectView.js"; import MeView from "./views/MeView.js"; @@ -44,6 +46,7 @@ const router = async (uri = "") => { { path: "/chat", view: Chat }, { path: "/home", view: HomeView }, { path: "/me", view: MeView }, + { path: "/game", view: GameView }, ]; // Test each route for potential match @@ -96,4 +99,4 @@ document.addEventListener("DOMContentLoaded", () => { router(location.pathname); }); -export { client, navigateTo } \ No newline at end of file +export { client, navigateTo } diff --git a/frontend/static/js/views/Game.js b/frontend/static/js/views/Game.js new file mode 100644 index 0000000..312c405 --- /dev/null +++ b/frontend/static/js/views/Game.js @@ -0,0 +1,63 @@ +import AbstractView from './AbstractView.js' + +export default class extends AbstractView { + constructor(params) { + super(params, 'Game'); + } + + async getHtml() { + return ` +

Game

+ + `; + } + + async postInit() { + document.getElementById('startGameButton').onclick = this.startGame; + } + + startGame() { + this.game = new Game; + } +} + +class Game { + constructor() { + this.canvas = document.createElement('canvas'); + this.canvas.width = 480; + this.canvas.height = 270; + this.canvas.style.border = '1px solid #d3d3d3'; + this.canvas.style.backgroundColor = '#f1f1f1'; + this.context = this.canvas.getContext('2d'); + this.paddle = new Paddle(this.context); + document.getElementById('app').appendChild(this.canvas); + + this.interval = setInterval(this.updateGame, 20); + } + + clear() { + this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); + } + + updateGame() { + this.clear(); + this.paddle.y += 1; + this.paddle.update(); + } +} + +class Paddle { + constructor(context) { + this.width = 10; + this.height = 70; + this.x = 5; + this.y = 100; + this.ctx = context; + this.update(); + } + + update() { + this.ctx.fillStyle = 'black'; + this.ctx.fillRect(this.x, this.y, this.width, this.height); + } +} From 12056554fc45ffdba038103e54b7643917dcc11f Mon Sep 17 00:00:00 2001 From: AdrienLSH Date: Tue, 5 Dec 2023 12:18:34 +0100 Subject: [PATCH 02/19] game: moving paddle :) --- frontend/static/js/views/Game.js | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/frontend/static/js/views/Game.js b/frontend/static/js/views/Game.js index 312c405..62b8e80 100644 --- a/frontend/static/js/views/Game.js +++ b/frontend/static/js/views/Game.js @@ -3,6 +3,7 @@ import AbstractView from './AbstractView.js' export default class extends AbstractView { constructor(params) { super(params, 'Game'); + this.game = null; } async getHtml() { @@ -17,7 +18,8 @@ export default class extends AbstractView { } startGame() { - this.game = new Game; + if (this.game == null) + this.game = new Game; } } @@ -29,10 +31,15 @@ class Game { this.canvas.style.border = '1px solid #d3d3d3'; this.canvas.style.backgroundColor = '#f1f1f1'; this.context = this.canvas.getContext('2d'); + this.paddle = new Paddle(this.context); document.getElementById('app').appendChild(this.canvas); - this.interval = setInterval(this.updateGame, 20); + this.interval = setInterval(this.updateGame.bind(this), 10); + + this.keys = []; + document.addEventListener('keydown', this.keyDownHandler.bind(this)); + document.addEventListener('keyup', this.keyUpHandler.bind(this)); } clear() { @@ -40,10 +47,25 @@ class Game { } updateGame() { + if (this.keys.includes('s')) + this.paddle.y += 3; + if (this.keys.includes('w')) + this.paddle.y -= 3; + this.clear(); - this.paddle.y += 1; this.paddle.update(); } + + keyUpHandler(ev) { + const idx = this.keys.indexOf(ev.key); + if (idx != -1) + this.keys.splice(idx, 1); + } + + keyDownHandler(ev) { + if (this.keys.indexOf(ev.key) == -1) + this.keys.push(ev.key); + } } class Paddle { From aa35514c4400b63ca9a2f43a85d7fbba236a27cb Mon Sep 17 00:00:00 2001 From: AdrienLSH Date: Sun, 10 Dec 2023 11:05:00 +0100 Subject: [PATCH 03/19] do u guys wanna see my balls ? --- frontend/static/js/views/Game.js | 87 ++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 9 deletions(-) diff --git a/frontend/static/js/views/Game.js b/frontend/static/js/views/Game.js index 62b8e80..4354d0a 100644 --- a/frontend/static/js/views/Game.js +++ b/frontend/static/js/views/Game.js @@ -10,22 +10,41 @@ export default class extends AbstractView { return `

Game

+ `; } async postInit() { - document.getElementById('startGameButton').onclick = this.startGame; + document.getElementById('startGameButton').onclick = this.startGame.bind(this); + document.getElementById('stopGameButton').onclick = this.stopGame.bind(this); } startGame() { - if (this.game == null) + if (this.game == null) { + document.getElementById('startGameButton').innerHTML = 'Reset Game'; this.game = new Game; + } + else { + document.getElementById('app').removeChild(this.game.canvas); + this.game.cleanup(); + this.game = new Game; + } + } + + stopGame() { + if (!this.game) + return; + document.getElementById('app').removeChild(this.game.canvas); + this.game.cleanup(); + this.game = null; + document.getElementById('startGameButton').innerHTML = 'Start Game'; } } class Game { constructor() { this.canvas = document.createElement('canvas'); + this.canvas.id = 'gameCanvas'; this.canvas.width = 480; this.canvas.height = 270; this.canvas.style.border = '1px solid #d3d3d3'; @@ -33,13 +52,22 @@ class Game { this.context = this.canvas.getContext('2d'); this.paddle = new Paddle(this.context); + this.ball = new Ball(this.context); document.getElementById('app').appendChild(this.canvas); this.interval = setInterval(this.updateGame.bind(this), 10); this.keys = []; - document.addEventListener('keydown', this.keyDownHandler.bind(this)); - document.addEventListener('keyup', this.keyUpHandler.bind(this)); + this.keyUpHandler = this.keyUpHandler.bind(this); + this.keyDownHandler = this.keyDownHandler.bind(this); + document.addEventListener('keydown', this.keyDownHandler); + document.addEventListener('keyup', this.keyUpHandler); + } + + cleanup() { + clearInterval(this.interval); + document.removeEventListener('keydown', this.keyDownHandler); + document.removeEventListener('keyup', this.keyUpHandler); } clear() { @@ -47,13 +75,34 @@ class Game { } updateGame() { - if (this.keys.includes('s')) - this.paddle.y += 3; - if (this.keys.includes('w')) - this.paddle.y -= 3; + + if (this.keys.includes('Escape')) + this.cleanup(); + + if (this.keys.includes('s') && + this.paddle.y + this.paddle.height < this.canvas.height - this.paddle.x) + this.paddle.y += this.paddle.speed; + if (this.keys.includes('w') && + this.paddle.y > 0 + this.paddle.x) + this.paddle.y -= this.paddle.speed; + + if (this.ball.x - this.ball.radius <= this.paddle.x + this.paddle.width + && (this.ball.y >= this.paddle.y && this.ball.y <= this.paddle.y + this.paddle.height)) + this.ball.vx *= -1; + if (this.ball.x - this.ball.radius <= 0) + this.ball.vx *= -1; + else if (this.ball.x + this.ball.radius >= this.canvas.width) + this.ball.vx *= -1; + if (this.ball.y - this.ball.radius <= 0) + this.ball.vy *= -1; + else if (this.ball.y + this.ball.radius >= this.canvas.height) + this.ball.vy *= -1; + this.ball.x += this.ball.vx; + this.ball.y += this.ball.vy; this.clear(); this.paddle.update(); + this.ball.update(); } keyUpHandler(ev) { @@ -63,7 +112,7 @@ class Game { } keyDownHandler(ev) { - if (this.keys.indexOf(ev.key) == -1) + if (!this.keys.includes(ev.key)) this.keys.push(ev.key); } } @@ -74,6 +123,7 @@ class Paddle { this.height = 70; this.x = 5; this.y = 100; + this.speed = 3; this.ctx = context; this.update(); } @@ -83,3 +133,22 @@ class Paddle { this.ctx.fillRect(this.x, this.y, this.width, this.height); } } + +class Ball { + constructor(context) { + this.radius = 5; + this.x = 240; + this.y = 135; + this.vx = 2; + this.vy = 2; + this.ctx = context; + this.update(); + } + + update() { + this.ctx.fillStyle = 'black'; + this.ctx.beginPath(); + this.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); + this.ctx.fill(); + } +} From f66b3883c162b24fac36428e7e94c9657971177c Mon Sep 17 00:00:00 2001 From: starnakin Date: Mon, 11 Dec 2023 13:39:53 +0100 Subject: [PATCH 04/19] init app --- matchmaking/__init__.py | 0 matchmaking/admin.py | 3 +++ matchmaking/apps.py | 6 ++++++ matchmaking/models.py | 3 +++ matchmaking/tests.py | 3 +++ matchmaking/views.py | 3 +++ 6 files changed, 18 insertions(+) create mode 100644 matchmaking/__init__.py create mode 100644 matchmaking/admin.py create mode 100644 matchmaking/apps.py create mode 100644 matchmaking/models.py create mode 100644 matchmaking/tests.py create mode 100644 matchmaking/views.py diff --git a/matchmaking/__init__.py b/matchmaking/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/matchmaking/admin.py b/matchmaking/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/matchmaking/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/matchmaking/apps.py b/matchmaking/apps.py new file mode 100644 index 0000000..7ca5434 --- /dev/null +++ b/matchmaking/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class MatchmakingConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'matchmaking' diff --git a/matchmaking/models.py b/matchmaking/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/matchmaking/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/matchmaking/tests.py b/matchmaking/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/matchmaking/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/matchmaking/views.py b/matchmaking/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/matchmaking/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. From 336257d1d0582a0b5d8a42969c185960bd7f5d52 Mon Sep 17 00:00:00 2001 From: starnakin Date: Mon, 11 Dec 2023 13:40:01 +0100 Subject: [PATCH 05/19] init view --- frontend/static/js/index.js | 2 ++ frontend/static/js/views/HomeView.js | 1 + frontend/static/js/views/MatchMakingView.js | 13 +++++++++++++ 3 files changed, 16 insertions(+) create mode 100644 frontend/static/js/views/MatchMakingView.js diff --git a/frontend/static/js/index.js b/frontend/static/js/index.js index c263079..91d1fee 100644 --- a/frontend/static/js/index.js +++ b/frontend/static/js/index.js @@ -11,6 +11,7 @@ import AbstractRedirectView from "./views/AbstractRedirectView.js"; import MeView from "./views/MeView.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) @@ -44,6 +45,7 @@ const router = async (uri = "") => { { path: "/chat", view: Chat }, { path: "/home", view: HomeView }, { path: "/me", view: MeView }, + { path: "/matchmaking", view: MatchMakingView }, ]; // Test each route for potential match diff --git a/frontend/static/js/views/HomeView.js b/frontend/static/js/views/HomeView.js index d9fd194..cb15693 100644 --- a/frontend/static/js/views/HomeView.js +++ b/frontend/static/js/views/HomeView.js @@ -9,6 +9,7 @@ export default class extends AbstractAuthentificateView { async getHtml() { return `

HOME

+ Play a game Me Logout `; diff --git a/frontend/static/js/views/MatchMakingView.js b/frontend/static/js/views/MatchMakingView.js new file mode 100644 index 0000000..f079328 --- /dev/null +++ b/frontend/static/js/views/MatchMakingView.js @@ -0,0 +1,13 @@ +import AbstractView from "./AbstractView.js"; + +export default class extends AbstractView { + constructor(params) { + super(params, "Dashboard"); + } + + async getHtml() { + return ` +

finding

+ `; + } +} \ No newline at end of file From ae20be25fbb18992e7f0d52bcf3d01bb2911404a Mon Sep 17 00:00:00 2001 From: starnakin Date: Tue, 12 Dec 2023 12:06:13 +0100 Subject: [PATCH 06/19] add: matchmaking --- README.md | 4 +- frontend/static/js/api/client.js | 2 + frontend/static/js/api/matchmaking.js | 42 ++++++++++++++++++ frontend/static/js/views/MatchMakingView.js | 17 ++++++++ games/__init__.py | 0 games/admin.py | 3 ++ games/apps.py | 6 +++ games/models.py | 14 ++++++ games/tests.py | 3 ++ games/views.py | 3 ++ matchmaking/consumers.py | 48 +++++++++++++++++++++ matchmaking/routing.py | 6 +++ trancendence/asgi.py | 5 ++- trancendence/settings.py | 2 + 14 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 frontend/static/js/api/matchmaking.js create mode 100644 games/__init__.py create mode 100644 games/admin.py create mode 100644 games/apps.py create mode 100644 games/models.py create mode 100644 games/tests.py create mode 100644 games/views.py create mode 100644 matchmaking/consumers.py create mode 100644 matchmaking/routing.py diff --git a/README.md b/README.md index 69e2f03..0416c0d 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ pip install -r requirements.txt ``` - Setup database ``` -python manage.py runserver makemigrations profiles -python manage.py migrate profiles +python manage.py runserver makemigrations profiles games +python manage.py migrate profiles games ``` - Start the developpement server ``` diff --git a/frontend/static/js/api/client.js b/frontend/static/js/api/client.js index 8e7415a..942d9fa 100644 --- a/frontend/static/js/api/client.js +++ b/frontend/static/js/api/client.js @@ -1,4 +1,5 @@ import { Account } from "./account.js"; +import { MatchMaking } from "./matchmaking.js"; import { Profile } from "./profile.js"; import { Profiles } from "./profiles.js"; @@ -19,6 +20,7 @@ class Client this._url = url; this.account = new Account(this); this.profiles = new Profiles(this); + this.matchmaking = new MatchMaking(this); this._logged = undefined; } diff --git a/frontend/static/js/api/matchmaking.js b/frontend/static/js/api/matchmaking.js new file mode 100644 index 0000000..b01db49 --- /dev/null +++ b/frontend/static/js/api/matchmaking.js @@ -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} \ No newline at end of file diff --git a/frontend/static/js/views/MatchMakingView.js b/frontend/static/js/views/MatchMakingView.js index f079328..9313d24 100644 --- a/frontend/static/js/views/MatchMakingView.js +++ b/frontend/static/js/views/MatchMakingView.js @@ -1,13 +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 `

finding

`; } + + async leavePage() + { + await client.matchmaking.stop(); + } } \ No newline at end of file diff --git a/games/__init__.py b/games/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/games/admin.py b/games/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/games/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/games/apps.py b/games/apps.py new file mode 100644 index 0000000..1a3efec --- /dev/null +++ b/games/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class GamesConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'games' diff --git a/games/models.py b/games/models.py new file mode 100644 index 0000000..a2d8315 --- /dev/null +++ b/games/models.py @@ -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() \ No newline at end of file diff --git a/games/tests.py b/games/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/games/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/games/views.py b/games/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/games/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/matchmaking/consumers.py b/matchmaking/consumers.py new file mode 100644 index 0000000..dfa65e8 --- /dev/null +++ b/matchmaking/consumers.py @@ -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) \ No newline at end of file diff --git a/matchmaking/routing.py b/matchmaking/routing.py new file mode 100644 index 0000000..eed5072 --- /dev/null +++ b/matchmaking/routing.py @@ -0,0 +1,6 @@ +from django.urls import re_path +from . import consumers + +websocket_urlpatterns = [ + re_path(r'ws/matchmaking/', consumers.MatchMaking.as_asgi()) +] diff --git a/trancendence/asgi.py b/trancendence/asgi.py index a229228..699b4d8 100644 --- a/trancendence/asgi.py +++ b/trancendence/asgi.py @@ -10,7 +10,9 @@ https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ import os from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack + import chat.routing +import matchmaking.routing from django.core.asgi import get_asgi_application @@ -20,7 +22,8 @@ application = ProtocolTypeRouter({ 'http':get_asgi_application(), 'websocket':AuthMiddlewareStack( URLRouter( - chat.routing.websocket_urlpatterns + chat.routing.websocket_urlpatterns + + matchmaking.routing.websocket_urlpatterns ) ) }) diff --git a/trancendence/settings.py b/trancendence/settings.py index 4e89778..4e21503 100644 --- a/trancendence/settings.py +++ b/trancendence/settings.py @@ -43,6 +43,8 @@ INSTALLED_APPS = [ 'channels', 'daphne', + 'matchmaking.apps.MatchmakingConfig', + 'games.apps.GamesConfig', 'accounts.apps.AccountsConfig', 'profiles.apps.ProfilesConfig', 'frontend.apps.FrontendConfig', From ca6dba276306d2650a44c531a9d2fe078cde166e Mon Sep 17 00:00:00 2001 From: AdrienLSH Date: Tue, 12 Dec 2023 12:10:48 +0100 Subject: [PATCH 07/19] add: ball angle calculations --- frontend/static/js/views/Game.js | 113 +++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 29 deletions(-) diff --git a/frontend/static/js/views/Game.js b/frontend/static/js/views/Game.js index 4354d0a..2851c04 100644 --- a/frontend/static/js/views/Game.js +++ b/frontend/static/js/views/Game.js @@ -43,18 +43,40 @@ export default class extends AbstractView { class Game { constructor() { + //Global variables + this.globVars = { + CANVASHEIGHT: 270, + CANVASWIDTH: 480, + PADDLEHEIGHT: 70, + PADDLEWIDTH: 10, + PADDLEMARGIN: 5, + PADDLESPEED: 3, + BALLRADIUS: 5, + BALLSPEED: 2, + MAXBOUNCEANGLE: 10 * (Math.PI / 12) + }; + this.canvas = document.createElement('canvas'); this.canvas.id = 'gameCanvas'; - this.canvas.width = 480; - this.canvas.height = 270; + this.canvas.width = this.globVars.CANVASWIDTH; + this.canvas.height = this.globVars.CANVASHEIGHT; this.canvas.style.border = '1px solid #d3d3d3'; this.canvas.style.backgroundColor = '#f1f1f1'; this.context = this.canvas.getContext('2d'); - - this.paddle = new Paddle(this.context); - this.ball = new Ball(this.context); document.getElementById('app').appendChild(this.canvas); + this.paddles = [ + new Paddle(this.context, + this.globVars.PADDLEMARGIN, + this.globVars + ), + new Paddle(this.context, + this.globVars.CANVASWIDTH - this.globVars.PADDLEMARGIN - this.globVars.PADDLEWIDTH, + this.globVars + ) + ]; + this.ball = new Ball(this.context, this.globVars); + this.interval = setInterval(this.updateGame.bind(this), 10); this.keys = []; @@ -79,20 +101,27 @@ class Game { if (this.keys.includes('Escape')) this.cleanup(); + //Paddle movement if (this.keys.includes('s') && - this.paddle.y + this.paddle.height < this.canvas.height - this.paddle.x) - this.paddle.y += this.paddle.speed; + this.paddles[0].y + this.globVars.PADDLEHEIGHT < this.globVars.CANVASHEIGHT - this.globVars.PADDLEMARGIN) + this.paddles[0].y += this.globVars.PADDLESPEED; if (this.keys.includes('w') && - this.paddle.y > 0 + this.paddle.x) - this.paddle.y -= this.paddle.speed; + this.paddles[0].y > 0 + this.globVars.PADDLEMARGIN) + this.paddles[0].y -= this.globVars.PADDLESPEED; + + if (this.keys.includes('ArrowDown') && + this.paddles[1].y + this.globVars.PADDLEHEIGHT < this.globVars.CANVASHEIGHT - this.globVars.PADDLEMARGIN) + this.paddles[1].y += this.globVars.PADDLESPEED; + if (this.keys.includes('ArrowUp') && + this.paddles[1].y > 0 + this.globVars.PADDLEMARGIN) + this.paddles[1].y -= this.globVars.PADDLESPEED; + + //Ball collisions + if (this.detectCollision(this.paddles[0], this.ball)) + this.calculateBallVelocity(this.paddles[0].getCenter().y, this.ball); + else if (this.detectCollision(this.paddles[1], this.ball)) + this.calculateBallVelocity(this.paddles[1].getCenter().y, this.ball, -1); - if (this.ball.x - this.ball.radius <= this.paddle.x + this.paddle.width - && (this.ball.y >= this.paddle.y && this.ball.y <= this.paddle.y + this.paddle.height)) - this.ball.vx *= -1; - if (this.ball.x - this.ball.radius <= 0) - this.ball.vx *= -1; - else if (this.ball.x + this.ball.radius >= this.canvas.width) - this.ball.vx *= -1; if (this.ball.y - this.ball.radius <= 0) this.ball.vy *= -1; else if (this.ball.y + this.ball.radius >= this.canvas.height) @@ -101,10 +130,30 @@ class Game { this.ball.y += this.ball.vy; this.clear(); - this.paddle.update(); + this.paddles[0].update(); + this.paddles[1].update(); this.ball.update(); } + detectCollision(paddle, ball) { + let paddleCenter = paddle.getCenter(); + let dx = Math.abs(ball.x - paddleCenter.x); + let dy = Math.abs(ball.y - paddleCenter.y); + if (dx <= ball.radius + paddle.width / 2 && + dy <= ball.radius + paddle.height / 2) + return true; + return false; + } + + calculateBallVelocity(paddleCenterY, ball, side = 1) { + let relativeIntersectY = paddleCenterY - ball.y; + let normRelIntersectY = relativeIntersectY / this.globVars.PADDLEHEIGHT / 2; + let bounceAngle = normRelIntersectY * this.globVars.MAXBOUNCEANGLE; + + ball.vx = this.globVars.BALLSPEED * side * Math.cos(bounceAngle); + ball.vy = this.globVars.BALLSPEED * -Math.sin(bounceAngle); + } + keyUpHandler(ev) { const idx = this.keys.indexOf(ev.key); if (idx != -1) @@ -118,12 +167,11 @@ class Game { } class Paddle { - constructor(context) { - this.width = 10; - this.height = 70; - this.x = 5; - this.y = 100; - this.speed = 3; + constructor(context, paddleSide, globVars) { + this.width = globVars.PADDLEWIDTH; + this.height = globVars.PADDLEHEIGHT; + this.x = paddleSide; + this.y = globVars.CANVASHEIGHT / 2 - this.height / 2; this.ctx = context; this.update(); } @@ -132,15 +180,22 @@ class Paddle { this.ctx.fillStyle = 'black'; this.ctx.fillRect(this.x, this.y, this.width, this.height); } + + getCenter() { + return { + x: this.x + this.width / 2, + y: this.y + this.height / 2 + }; + } } class Ball { - constructor(context) { - this.radius = 5; - this.x = 240; - this.y = 135; - this.vx = 2; - this.vy = 2; + constructor(context, globVars) { + this.radius = globVars.BALLRADIUS; + this.x = globVars.CANVASWIDTH / 2; + this.y = globVars.CANVASHEIGHT / 2; + this.vx = -globVars.BALLSPEED; + this.vy = 0; this.ctx = context; this.update(); } From f0aaf0f29e133e7ff3fbd02781badfbe291b52a5 Mon Sep 17 00:00:00 2001 From: starnakin Date: Tue, 12 Dec 2023 14:43:57 +0100 Subject: [PATCH 08/19] clean: remove print --- frontend/static/js/api/matchmaking.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frontend/static/js/api/matchmaking.js b/frontend/static/js/api/matchmaking.js index b01db49..806349e 100644 --- a/frontend/static/js/api/matchmaking.js +++ b/frontend/static/js/api/matchmaking.js @@ -18,17 +18,12 @@ class MatchMaking 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) }; } From 926ac0dd5424e8c0becc79cdfc35546ca51ad1cd Mon Sep 17 00:00:00 2001 From: starnakin Date: Tue, 12 Dec 2023 15:20:26 +0100 Subject: [PATCH 09/19] clean: print --- frontend/static/js/views/MatchMakingView.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/static/js/views/MatchMakingView.js b/frontend/static/js/views/MatchMakingView.js index 9313d24..df9cd9a 100644 --- a/frontend/static/js/views/MatchMakingView.js +++ b/frontend/static/js/views/MatchMakingView.js @@ -14,7 +14,6 @@ export default class extends AbstractView { async postInit() { await client.matchmaking.start(game_found) - console.log("start matchmaking") } async getHtml() { From 243c5f266a0738100a0a0c662e73393c5af162fa Mon Sep 17 00:00:00 2001 From: starnakin Date: Tue, 12 Dec 2023 15:20:53 +0100 Subject: [PATCH 10/19] clean: remove bozo print --- frontend/static/js/index.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/static/js/index.js b/frontend/static/js/index.js index 09affed..10e0878 100644 --- a/frontend/static/js/index.js +++ b/frontend/static/js/index.js @@ -1,12 +1,11 @@ +import { Client } from "./api/client.js"; + import LoginView from "./views/accounts/LoginView.js"; import Dashboard from "./views/Dashboard.js"; -import Settings from "./views/Settings.js"; import Chat from "./views/Chat.js"; import HomeView from "./views/HomeView.js"; import RegisterView from "./views/accounts/RegisterView.js"; import LogoutView from "./views/accounts/LogoutView.js"; - -import { Client } from "./api/client.js"; import AbstractRedirectView from "./views/AbstractRedirectView.js"; import MeView from "./views/MeView.js"; import ProfilePageView from "./views/profiles/ProfilePageView.js"; @@ -38,7 +37,6 @@ const router = async (uri) => { { path: "/", view: Dashboard }, { path: "/profiles", view: ProfilesView}, { path: "/profiles/:id", view: ProfilePageView }, - { path: "/settings", view: Settings }, { path: "/login", view: LoginView }, { path: "/logout", view: LogoutView }, { path: "/register", view: RegisterView }, From a15c59fbf7e1303916aeb507dd2ae08db4c1e712 Mon Sep 17 00:00:00 2001 From: starnakin Date: Tue, 12 Dec 2023 15:21:06 +0100 Subject: [PATCH 11/19] clean: remove trash file --- frontend/static/js/views/Settings.js | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 frontend/static/js/views/Settings.js diff --git a/frontend/static/js/views/Settings.js b/frontend/static/js/views/Settings.js deleted file mode 100644 index 1c9f85f..0000000 --- a/frontend/static/js/views/Settings.js +++ /dev/null @@ -1,14 +0,0 @@ -import AbstractView from "./AbstractView.js"; - -export default class extends AbstractView { - constructor(params) { - super(params, "Settings"); - } - - async getHtml() { - return ` -

Settings

-

Manage your privacy and configuration.

- `; - } -} \ No newline at end of file From 754e5867f2f0778e2122292a5f06cc592e6cf9ca Mon Sep 17 00:00:00 2001 From: AdrienLSH Date: Tue, 12 Dec 2023 15:58:50 +0100 Subject: [PATCH 12/19] welp I guess we're done --- frontend/static/js/views/Game.js | 124 ++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 45 deletions(-) diff --git a/frontend/static/js/views/Game.js b/frontend/static/js/views/Game.js index 2851c04..57b936e 100644 --- a/frontend/static/js/views/Game.js +++ b/frontend/static/js/views/Game.js @@ -35,6 +35,7 @@ export default class extends AbstractView { if (!this.game) return; document.getElementById('app').removeChild(this.game.canvas); + document.getElementById('app').removeChild(this.game.scoresDisplay); this.game.cleanup(); this.game = null; document.getElementById('startGameButton').innerHTML = 'Start Game'; @@ -44,7 +45,7 @@ export default class extends AbstractView { class Game { constructor() { //Global variables - this.globVars = { + this.def = { CANVASHEIGHT: 270, CANVASWIDTH: 480, PADDLEHEIGHT: 70, @@ -53,29 +54,39 @@ class Game { PADDLESPEED: 3, BALLRADIUS: 5, BALLSPEED: 2, - MAXBOUNCEANGLE: 10 * (Math.PI / 12) + BALLSPEEDINCR: 0.15, + MAXBOUNCEANGLE: 10 * (Math.PI / 12), + MAXSCORE: 6 }; this.canvas = document.createElement('canvas'); this.canvas.id = 'gameCanvas'; - this.canvas.width = this.globVars.CANVASWIDTH; - this.canvas.height = this.globVars.CANVASHEIGHT; + this.canvas.width = this.def.CANVASWIDTH; + this.canvas.height = this.def.CANVASHEIGHT; this.canvas.style.border = '1px solid #d3d3d3'; this.canvas.style.backgroundColor = '#f1f1f1'; this.context = this.canvas.getContext('2d'); document.getElementById('app').appendChild(this.canvas); + this.scoresDisplay = document.createElement('p'); + this.scoresDisplay.innerHTML = 'Scores: 0 - 0'; + document.getElementById('app').appendChild(this.scoresDisplay); - this.paddles = [ - new Paddle(this.context, - this.globVars.PADDLEMARGIN, - this.globVars - ), - new Paddle(this.context, - this.globVars.CANVASWIDTH - this.globVars.PADDLEMARGIN - this.globVars.PADDLEWIDTH, - this.globVars - ) + this.players = [ + { + paddle: new Paddle(this.context, + this.def.PADDLEMARGIN, + this.def), + score: 0 + }, + { + paddle: new Paddle(this.context, + this.def.CANVASWIDTH - this.def.PADDLEMARGIN - this.def.PADDLEWIDTH, + this.def), + score: 0 + } ]; - this.ball = new Ball(this.context, this.globVars); + this.ballStartSide = 0; + this.ball = new Ball(this.context, this.def, this.ballStartSide); this.interval = setInterval(this.updateGame.bind(this), 10); @@ -90,6 +101,7 @@ class Game { clearInterval(this.interval); document.removeEventListener('keydown', this.keyDownHandler); document.removeEventListener('keyup', this.keyUpHandler); + this.canvas.style.display = 'none'; } clear() { @@ -97,30 +109,32 @@ class Game { } updateGame() { - - if (this.keys.includes('Escape')) - this.cleanup(); - //Paddle movement if (this.keys.includes('s') && - this.paddles[0].y + this.globVars.PADDLEHEIGHT < this.globVars.CANVASHEIGHT - this.globVars.PADDLEMARGIN) - this.paddles[0].y += this.globVars.PADDLESPEED; + this.players[0].paddle.y + this.def.PADDLEHEIGHT < this.def.CANVASHEIGHT - this.def.PADDLEMARGIN) + this.players[0].paddle.y += this.def.PADDLESPEED; if (this.keys.includes('w') && - this.paddles[0].y > 0 + this.globVars.PADDLEMARGIN) - this.paddles[0].y -= this.globVars.PADDLESPEED; + this.players[0].paddle.y > 0 + this.def.PADDLEMARGIN) + this.players[0].paddle.y -= this.def.PADDLESPEED; if (this.keys.includes('ArrowDown') && - this.paddles[1].y + this.globVars.PADDLEHEIGHT < this.globVars.CANVASHEIGHT - this.globVars.PADDLEMARGIN) - this.paddles[1].y += this.globVars.PADDLESPEED; + this.players[1].paddle.y + this.def.PADDLEHEIGHT < this.def.CANVASHEIGHT - this.def.PADDLEMARGIN) + this.players[1].paddle.y += this.def.PADDLESPEED; if (this.keys.includes('ArrowUp') && - this.paddles[1].y > 0 + this.globVars.PADDLEMARGIN) - this.paddles[1].y -= this.globVars.PADDLESPEED; + this.players[1].paddle.y > 0 + this.def.PADDLEMARGIN) + this.players[1].paddle.y -= this.def.PADDLESPEED; + + //GOOAAAAL + if (this.ball.x <= 0) + this.updateScore(this.players[0].score, ++this.players[1].score); + else if (this.ball.x >= this.def.CANVASWIDTH) + this.updateScore(++this.players[0].score, this.players[1].score); //Ball collisions - if (this.detectCollision(this.paddles[0], this.ball)) - this.calculateBallVelocity(this.paddles[0].getCenter().y, this.ball); - else if (this.detectCollision(this.paddles[1], this.ball)) - this.calculateBallVelocity(this.paddles[1].getCenter().y, this.ball, -1); + if (this.detectCollision(this.players[0].paddle, this.ball)) + this.calculateBallVelocity(this.players[0].paddle.getCenter().y, this.ball); + else if (this.detectCollision(this.players[1].paddle, this.ball)) + this.calculateBallVelocity(this.players[1].paddle.getCenter().y, this.ball, -1); if (this.ball.y - this.ball.radius <= 0) this.ball.vy *= -1; @@ -130,11 +144,26 @@ class Game { this.ball.y += this.ball.vy; this.clear(); - this.paddles[0].update(); - this.paddles[1].update(); + this.players[0].paddle.update(); + this.players[1].paddle.update(); this.ball.update(); } + updateScore(p1Score, p2Score) { + if (p1Score > this.def.MAXSCORE) { + this.scoresDisplay.innerHTML = 'Player 1 wins!! GGS'; + this.cleanup(); + } + else if (p2Score > this.def.MAXSCORE) { + this.scoresDisplay.innerHTML = 'Player 2 wins!! GGS'; + this.cleanup(); + } else { + this.scoresDisplay.innerHTML = `Scores: ${p1Score} - ${p2Score}`; + this.ballStartSide = 1 - this.ballStartSide; + this.ball = new Ball(this.context, this.def, this.ballStartSide); + } + } + detectCollision(paddle, ball) { let paddleCenter = paddle.getCenter(); let dx = Math.abs(ball.x - paddleCenter.x); @@ -147,11 +176,12 @@ class Game { calculateBallVelocity(paddleCenterY, ball, side = 1) { let relativeIntersectY = paddleCenterY - ball.y; - let normRelIntersectY = relativeIntersectY / this.globVars.PADDLEHEIGHT / 2; - let bounceAngle = normRelIntersectY * this.globVars.MAXBOUNCEANGLE; + let normRelIntersectY = relativeIntersectY / this.def.PADDLEHEIGHT / 2; + let bounceAngle = normRelIntersectY * this.def.MAXBOUNCEANGLE; - ball.vx = this.globVars.BALLSPEED * side * Math.cos(bounceAngle); - ball.vy = this.globVars.BALLSPEED * -Math.sin(bounceAngle); + ball.speed += this.def.BALLSPEEDINCR; + ball.vx = ball.speed * side * Math.cos(bounceAngle); + ball.vy = ball.speed * -Math.sin(bounceAngle); } keyUpHandler(ev) { @@ -167,11 +197,11 @@ class Game { } class Paddle { - constructor(context, paddleSide, globVars) { - this.width = globVars.PADDLEWIDTH; - this.height = globVars.PADDLEHEIGHT; + constructor(context, paddleSide, def) { + this.width = def.PADDLEWIDTH; + this.height = def.PADDLEHEIGHT; this.x = paddleSide; - this.y = globVars.CANVASHEIGHT / 2 - this.height / 2; + this.y = def.CANVASHEIGHT / 2 - this.height / 2; this.ctx = context; this.update(); } @@ -190,12 +220,16 @@ class Paddle { } class Ball { - constructor(context, globVars) { - this.radius = globVars.BALLRADIUS; - this.x = globVars.CANVASWIDTH / 2; - this.y = globVars.CANVASHEIGHT / 2; - this.vx = -globVars.BALLSPEED; + constructor(context, def, startSide) { + this.radius = def.BALLRADIUS; + this.speed = def.BALLSPEED; + this.x = def.CANVASWIDTH / 2; + this.y = def.CANVASHEIGHT / 2; this.vy = 0; + if (startSide === 0) + this.vx = -this.speed; + else + this.vx = this.speed; this.ctx = context; this.update(); } From 63e1520e6af153666e110c1d6cc76c4f712d82f1 Mon Sep 17 00:00:00 2001 From: AdrienLSH Date: Tue, 12 Dec 2023 18:25:16 +0100 Subject: [PATCH 13/19] add: ball respawn timeout --- frontend/static/js/views/Game.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/frontend/static/js/views/Game.js b/frontend/static/js/views/Game.js index 57b936e..87dd0cb 100644 --- a/frontend/static/js/views/Game.js +++ b/frontend/static/js/views/Game.js @@ -86,6 +86,7 @@ class Game { } ]; this.ballStartSide = 0; + this.ballRespawned = false; this.ball = new Ball(this.context, this.def, this.ballStartSide); this.interval = setInterval(this.updateGame.bind(this), 10); @@ -140,8 +141,11 @@ class Game { this.ball.vy *= -1; else if (this.ball.y + this.ball.radius >= this.canvas.height) this.ball.vy *= -1; - this.ball.x += this.ball.vx; - this.ball.y += this.ball.vy; + + if (!this.ballRespawned) { + this.ball.x += this.ball.vx; + this.ball.y += this.ball.vy; + } this.clear(); this.players[0].paddle.update(); @@ -161,6 +165,9 @@ class Game { this.scoresDisplay.innerHTML = `Scores: ${p1Score} - ${p2Score}`; this.ballStartSide = 1 - this.ballStartSide; this.ball = new Ball(this.context, this.def, this.ballStartSide); + this.ballRespawned = true; + new Promise(r => setTimeout(r, 300)) + .then(_ => this.ballRespawned = false); } } From 5d46ff51238729d719c16198475612659d282fe5 Mon Sep 17 00:00:00 2001 From: starnakin Date: Wed, 13 Dec 2023 12:40:47 +0100 Subject: [PATCH 14/19] add: offline button --- frontend/static/js/index.js | 2 +- frontend/static/js/views/HomeView.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/static/js/index.js b/frontend/static/js/index.js index 3d58006..8057fae 100644 --- a/frontend/static/js/index.js +++ b/frontend/static/js/index.js @@ -47,7 +47,7 @@ const router = async (uri) => { { path: "/home", view: HomeView }, { path: "/me", view: MeView }, { path: "/matchmaking", view: MatchMakingView }, - { path: "/game", view: GameView }, + { path: "/game/offline", view: GameView }, ]; // Test each route for potential match diff --git a/frontend/static/js/views/HomeView.js b/frontend/static/js/views/HomeView.js index cb15693..3569edb 100644 --- a/frontend/static/js/views/HomeView.js +++ b/frontend/static/js/views/HomeView.js @@ -10,6 +10,7 @@ export default class extends AbstractAuthentificateView { return `

HOME

Play a game + Play offline Me Logout `; From 4c3dd1be4af765da453a79c02f3173a5ab4bfb72 Mon Sep 17 00:00:00 2001 From: starnakin Date: Wed, 13 Dec 2023 13:01:51 +0100 Subject: [PATCH 15/19] update: requirement --- requirements.txt | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 53e09ab..be1d817 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,30 @@ asgiref==3.7.2 +attrs==23.1.0 +autobahn==23.6.2 +Automat==22.10.0 +cffi==1.16.0 +channels==4.0.0 +constantly==23.10.4 +cryptography==41.0.7 +daphne==4.0.0 Django==4.2.6 django-cors-headers==4.3.0 django-restapi==0.1.3 djangorestframework==3.14.0 +hyperlink==21.0.0 +idna==3.6 +incremental==22.10.0 install==1.3.5 +Pillow==10.1.0 +pyasn1==0.5.1 +pyasn1-modules==0.3.0 +pycparser==2.21 +pyOpenSSL==23.3.0 pytz==2023.3.post1 +service-identity==23.1.0 +six==1.16.0 sqlparse==0.4.4 -channels==4.0.0 -daphne +Twisted==23.10.0 +txaio==23.1.1 +typing_extensions==4.8.0 +zope.interface==6.1 From 8e375c4805b7a142424595e7acba7ede1a4a2c1a Mon Sep 17 00:00:00 2001 From: starnakin Date: Wed, 13 Dec 2023 13:45:29 +0100 Subject: [PATCH 16/19] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0416c0d..c8fa597 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,10 @@ pip install -r requirements.txt ``` - Setup database ``` -python manage.py runserver makemigrations profiles games -python manage.py migrate profiles games +python manage.py makemigrations games +python manage.py migrate games +python manage.py makemigrations profiles +python manage.py migrate profiles ``` - Start the developpement server ``` From 072c97d1c0d1229562447a39e8a7dc10f44269f2 Mon Sep 17 00:00:00 2001 From: starnakin Date: Wed, 13 Dec 2023 14:55:17 +0100 Subject: [PATCH 17/19] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index c8fa597..bb9827f 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,8 @@ pip install -r requirements.txt - Setup database ``` python manage.py makemigrations games -python manage.py migrate games python manage.py makemigrations profiles -python manage.py migrate profiles +python manage.py migrate ``` - Start the developpement server ``` From c3030425882d84a0e849a1ccdbcc8e337920288b Mon Sep 17 00:00:00 2001 From: AdrienLSH Date: Thu, 14 Dec 2023 13:55:31 +0100 Subject: [PATCH 18/19] add: 404 not found --- frontend/static/js/index.js | 6 +++++- frontend/static/js/views/PageNotFoundView.js | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 frontend/static/js/views/PageNotFoundView.js diff --git a/frontend/static/js/index.js b/frontend/static/js/index.js index 8057fae..57a01c2 100644 --- a/frontend/static/js/index.js +++ b/frontend/static/js/index.js @@ -8,6 +8,7 @@ import RegisterView from "./views/accounts/RegisterView.js"; import LogoutView from "./views/accounts/LogoutView.js"; import GameView from "./views/Game.js" +import PageNotFoundView from './views/PageNotFoundView.js' import AbstractRedirectView from "./views/AbstractRedirectView.js"; import MeView from "./views/MeView.js"; @@ -62,7 +63,10 @@ const router = async (uri) => { if (!match) { match = { - route: routes[0], + route: { + path: uri, + view: PageNotFoundView + }, result: [uri] }; } diff --git a/frontend/static/js/views/PageNotFoundView.js b/frontend/static/js/views/PageNotFoundView.js new file mode 100644 index 0000000..cff748e --- /dev/null +++ b/frontend/static/js/views/PageNotFoundView.js @@ -0,0 +1,14 @@ +import AbstractView from "./AbstractView.js"; + +export default class extends AbstractView { + constructor(params) { + super(params, "Dashboard"); + } + + async getHtml() { + return ` +

404 Bozo

+

Git gud

+ `; + } +} From d03f90367f1fd075e79f4a69d31c5a49b9e53d01 Mon Sep 17 00:00:00 2001 From: Xamora Date: Fri, 15 Dec 2023 20:39:31 +0100 Subject: [PATCH 19/19] bug issue --- frontend/static/js/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/static/js/index.js b/frontend/static/js/index.js index 9f55fd7..12dc11b 100644 --- a/frontend/static/js/index.js +++ b/frontend/static/js/index.js @@ -2,7 +2,6 @@ import { Client } from "./api/client.js"; import LoginView from "./views/accounts/LoginView.js"; import Dashboard from "./views/Dashboard.js"; -import Settings from "./views/Settings.js"; import Search from "./views/Search.js"; import Chat from "./views/Chat.js"; import HomeView from "./views/HomeView.js";