From 4816bacbcedcac80cc6a1dd20305b055b423354d Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:45:15 +0200 Subject: [PATCH 01/46] replace User by get_user_model call in Pong model --- django/src/games/models.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/django/src/games/models.py b/django/src/games/models.py index a14a46b7..9733eb24 100644 --- a/django/src/games/models.py +++ b/django/src/games/models.py @@ -1,13 +1,12 @@ from django.db import models -from ft_auth.models import User - +from django.contrib.auth import get_user_model class Pong(models.Model): user1 = models.ForeignKey( - User, on_delete=models.SET_NULL, null=True, related_name="user1" + get_user_model(), on_delete=models.SET_NULL, null=True, related_name="user1" ) user2 = models.ForeignKey( - User, on_delete=models.SET_NULL, null=True, related_name="user2" + get_user_model(), on_delete=models.SET_NULL, null=True, related_name="user2" ) score1 = models.PositiveSmallIntegerField() score2 = models.PositiveSmallIntegerField() From aa7d0f1430c66a0b3761d5e61f7de1857daa3c23 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:18:45 +0200 Subject: [PATCH 02/46] remove join tournament button in pong.html --- django/src/games/templates/pong.html | 1 - 1 file changed, 1 deletion(-) diff --git a/django/src/games/templates/pong.html b/django/src/games/templates/pong.html index 2334fd7e..90a68c4a 100644 --- a/django/src/games/templates/pong.html +++ b/django/src/games/templates/pong.html @@ -15,7 +15,6 @@
-
From 3507789fbac40cdfde1ac19ff12ea6a9884b3658 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:19:20 +0200 Subject: [PATCH 03/46] create Tournament page --- django/src/games/templates/tournament.html | 5 +++++ django/src/pages/templates/extends/base.html | 3 ++- django/src/pages/urls.py | 1 + django/src/pages/views.py | 4 ++++ 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 django/src/games/templates/tournament.html diff --git a/django/src/games/templates/tournament.html b/django/src/games/templates/tournament.html new file mode 100644 index 00000000..3b85eebe --- /dev/null +++ b/django/src/games/templates/tournament.html @@ -0,0 +1,5 @@ +{% extends "./extends/base.html" %} + +{% block content %} +

Tournament

+{% endblock %} diff --git a/django/src/pages/templates/extends/base.html b/django/src/pages/templates/extends/base.html index 8f1c05de..6f273ca5 100644 --- a/django/src/pages/templates/extends/base.html +++ b/django/src/pages/templates/extends/base.html @@ -38,6 +38,7 @@
  • Home
  • {% if user.is_authenticated %}
  • Pong
  • +
  • Tournament
  • Logout
  • {% else %}
  • Login
  • @@ -69,4 +70,4 @@ document.getElementById("logout-button-navbar")?.addEventListener("click", function (e) { logout(); }) - \ No newline at end of file + diff --git a/django/src/pages/urls.py b/django/src/pages/urls.py index afca1a70..8165b4b8 100644 --- a/django/src/pages/urls.py +++ b/django/src/pages/urls.py @@ -4,6 +4,7 @@ urlpatterns = [ path('index/', views.index), path('pong/', views.pong), + path('tournament/', views.tournament), path('login/', views.login), path('register/', views.register), path('authorize/', views.authorize), diff --git a/django/src/pages/views.py b/django/src/pages/views.py index 2e085598..dd6a8695 100644 --- a/django/src/pages/views.py +++ b/django/src/pages/views.py @@ -27,6 +27,10 @@ def index(request): def pong(request): return create_response(request, 'pong.html', title="Pong", need_authentication=True) +@require_GET +def tournament(request): + return create_response(request, 'tournament.html', title="Tournament", need_authentication=True) + @require_GET def login(request: HttpRequest): if (request.user.is_authenticated): From 80f00d62e001340bf43563438d8e93cd6520b371 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:34:51 +0200 Subject: [PATCH 04/46] create PongTournament model --- django/src/games/models.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/django/src/games/models.py b/django/src/games/models.py index 9733eb24..58fedb10 100644 --- a/django/src/games/models.py +++ b/django/src/games/models.py @@ -14,3 +14,15 @@ class Pong(models.Model): def __str__(self): return f"{self.user1.username} vs {self.user2.username} - {self.score1}:{self.score2}" + +class PongTournament(models.Model): + name = models.CharField(max_length=255) + participants = models.ManyToManyField(get_user_model(), related_name='tournaments') + games = models.ManyToManyField(Pong, related_name='tournaments') + current_round = models.PositiveSmallIntegerField(default=1) + max_rounds = models.PositiveSmallIntegerField() + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return f"Tournament: {self.name} (Round {self.current_round}/{self.max_rounds})" From 8bb6c01ad7b671bd77d8cdd54c94258e7bcf9abf Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:03:31 +0200 Subject: [PATCH 05/46] create menu.js --- django/src/games/templates/pong.html | 19 +++---------------- nginx/src/index.html | 1 + nginx/src/js/menu.js | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 nginx/src/js/menu.js diff --git a/django/src/games/templates/pong.html b/django/src/games/templates/pong.html index 972ae7be..03e7bd90 100644 --- a/django/src/games/templates/pong.html +++ b/django/src/games/templates/pong.html @@ -29,23 +29,9 @@
    {% endblock %} diff --git a/nginx/src/index.html b/nginx/src/index.html index 0263dd7a..b6d87ba7 100644 --- a/nginx/src/index.html +++ b/nginx/src/index.html @@ -24,6 +24,7 @@ + diff --git a/nginx/src/js/menu.js b/nginx/src/js/menu.js new file mode 100644 index 00000000..1aebbd1e --- /dev/null +++ b/nginx/src/js/menu.js @@ -0,0 +1,16 @@ +function showMenu(menuId, menuClass, backBtn=undefined) { + document.querySelectorAll(`[${menuClass}]`).forEach(menu => { + menu.getAttribute(`${menuClass}`) === menuId ? menu.classList.remove("hidden") : menu.classList.add("hidden"); + }); + if (backBtn !== undefined) + menuId === "main" ? backBtn.classList.add("hidden") : backBtn.classList.remove("hidden"); +} + +function addBtnToMenusEvents(menuClass, backBtn=undefined) { + document.querySelectorAll('[menu-id]').forEach(element => { + element.addEventListener("click", (e) => { + e.preventDefault(); + showMenu(element.getAttribute("menu-id"), menuClass, backBtn); + }); + }); +} From 91d0211ec688c7881c8a5ca83e6cc38cc9588374 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:55:01 +0200 Subject: [PATCH 06/46] add required on join form & change redirection to tournament page --- django/src/games/templates/pong.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django/src/games/templates/pong.html b/django/src/games/templates/pong.html index 03e7bd90..84daf593 100644 --- a/django/src/games/templates/pong.html +++ b/django/src/games/templates/pong.html @@ -15,12 +15,12 @@
    Create a Room - Join a Tournament + Join a Tournament
    - +
    From 783e0baa68ac0bc76e3852c1020da4405bc6692e Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 2 Oct 2024 17:03:50 +0200 Subject: [PATCH 07/46] create tournament selection page --- django/src/games/templates/tournament.html | 55 +++++++++++++++++++++- django/src/pages/views.py | 14 +++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/django/src/games/templates/tournament.html b/django/src/games/templates/tournament.html index 3b85eebe..2e57ca08 100644 --- a/django/src/games/templates/tournament.html +++ b/django/src/games/templates/tournament.html @@ -1,5 +1,58 @@ {% extends "./extends/base.html" %} {% block content %} -

    Tournament

    +
    +
    + + +
    + +
    +
    + + +
    +
    + +
    +
    + + + +
    +
    + + +
    + + {% endblock %} diff --git a/django/src/pages/views.py b/django/src/pages/views.py index b5bf6233..ee7c93c0 100644 --- a/django/src/pages/views.py +++ b/django/src/pages/views.py @@ -58,7 +58,19 @@ def pong_online(request, id=None): @require_GET def tournament(request): - return create_response(request, 'tournament.html', title="Tournament", need_authentication=True) + games = [ + {"value": "pong", "label": "Pong"}, + ] + + return create_response( + request=request, + template_name='tournament.html', + title="Tournament", + context={ + "games": games, + }, + need_authentication=True, + ) @require_GET def login(request: HttpRequest): From 0234e4935e3aa2826b4c484debded789605464f6 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:50:23 +0200 Subject: [PATCH 08/46] delete menus in tournament page --- django/src/games/templates/tournament.html | 66 ++++++---------------- 1 file changed, 17 insertions(+), 49 deletions(-) diff --git a/django/src/games/templates/tournament.html b/django/src/games/templates/tournament.html index 2e57ca08..5301ffe0 100644 --- a/django/src/games/templates/tournament.html +++ b/django/src/games/templates/tournament.html @@ -2,57 +2,25 @@ {% block content %}
    -
    - - -
    - -
    -
    - - -
    -
    - -
    -
    - - - -
    -
    - - +
    + + + +
    {% endblock %} From c2ee7c1a0c7612c0639f66fb7385e564046ef307 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:50:43 +0200 Subject: [PATCH 09/46] create pong_tournament page --- django/src/games/templates/pong_tournament.html | 5 +++++ django/src/pages/urls.py | 1 + django/src/pages/views.py | 13 +++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 django/src/games/templates/pong_tournament.html diff --git a/django/src/games/templates/pong_tournament.html b/django/src/games/templates/pong_tournament.html new file mode 100644 index 00000000..3d99cb20 --- /dev/null +++ b/django/src/games/templates/pong_tournament.html @@ -0,0 +1,5 @@ +{% extends "./extends/base.html" %} + +{% block content %} + +{% endblock %} diff --git a/django/src/pages/urls.py b/django/src/pages/urls.py index f43a0e72..0b04e04a 100644 --- a/django/src/pages/urls.py +++ b/django/src/pages/urls.py @@ -8,6 +8,7 @@ path('pong/online/', views.pong_online), path('pong/online//', views.pong_online), path('tournament/', views.tournament), + path('tournament/pong//', views.pong_tournament), path('login/', views.authentification), path('register/', views.authentification), path('authorize/', views.authorize), diff --git a/django/src/pages/views.py b/django/src/pages/views.py index 031bc692..a7f38739 100644 --- a/django/src/pages/views.py +++ b/django/src/pages/views.py @@ -74,6 +74,19 @@ def tournament(request): need_authentication=True, ) +def pong_tournament(request, name: str): + if not name.isalpha(): + return JsonResponse({'redirect': '/'}, status=403) + return create_response( + request=request, + template_name='pong_tournament.html', + title="Pong Tournament", + context={ + "name": name, + }, + need_authentication=True, + ) + @require_GET def authorize(request: HttpRequest): if (request.user.is_authenticated): From c01a76f71a0675af8afdf2385108e87953a8f2c2 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:08:21 +0200 Subject: [PATCH 10/46] rename Pong consumer to PongGame --- django/src/games/admin.py | 6 +++--- django/src/games/migrations/0001_initial.py | 2 +- django/src/games/models.py | 4 ++-- django/src/games/pong.py | 6 +++--- django/src/games/routing.py | 2 +- django/src/games/tests.py | 14 +++++++------- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/django/src/games/admin.py b/django/src/games/admin.py index 5c6dd63b..d7c26c8d 100644 --- a/django/src/games/admin.py +++ b/django/src/games/admin.py @@ -1,8 +1,8 @@ from django.contrib import admin -from .models import Pong +from .models import PongGame -@admin.register(Pong) -class PongAdmin(admin.ModelAdmin): +@admin.register(PongGame) +class PongGameAdmin(admin.ModelAdmin): list_display = ('user1', 'user2', 'score1', 'score2', 'created_at') search_fields = ('user1__username', 'user2__username') list_filter = ('created_at',) diff --git a/django/src/games/migrations/0001_initial.py b/django/src/games/migrations/0001_initial.py index 370ea3ec..0dd0a97c 100644 --- a/django/src/games/migrations/0001_initial.py +++ b/django/src/games/migrations/0001_initial.py @@ -15,7 +15,7 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Pong', + name='PongGame', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('score1', models.PositiveSmallIntegerField()), diff --git a/django/src/games/models.py b/django/src/games/models.py index 58fedb10..2a4548f2 100644 --- a/django/src/games/models.py +++ b/django/src/games/models.py @@ -1,7 +1,7 @@ from django.db import models from django.contrib.auth import get_user_model -class Pong(models.Model): +class PongGame(models.Model): user1 = models.ForeignKey( get_user_model(), on_delete=models.SET_NULL, null=True, related_name="user1" ) @@ -18,7 +18,7 @@ def __str__(self): class PongTournament(models.Model): name = models.CharField(max_length=255) participants = models.ManyToManyField(get_user_model(), related_name='tournaments') - games = models.ManyToManyField(Pong, related_name='tournaments') + games = models.ManyToManyField(PongGame, related_name='tournaments') current_round = models.PositiveSmallIntegerField(default=1) max_rounds = models.PositiveSmallIntegerField() created_at = models.DateTimeField(auto_now_add=True) diff --git a/django/src/games/pong.py b/django/src/games/pong.py index 1f802c9d..c080d97e 100644 --- a/django/src/games/pong.py +++ b/django/src/games/pong.py @@ -12,7 +12,7 @@ logger = logging.getLogger(__name__) -class Consumer(AsyncWebsocketConsumer): +class PongGame(AsyncWebsocketConsumer): win_goal = 5 async def connect(self): @@ -242,9 +242,9 @@ async def send_game_state(self): ) async def save_pong_to_db(self, winner): - Pong = apps.get_model('games', 'Pong') + PongGame = apps.get_model('games', 'PongGame') try: - await sync_to_async(Pong.objects.create)( + await sync_to_async(PongGame.objects.create)( user1=await sync_to_async(get_user_model().objects.get)(id=self.info.players[0]), user2=await sync_to_async(get_user_model().objects.get)(id=self.info.players[1]), score1=self.info.score[0], diff --git a/django/src/games/routing.py b/django/src/games/routing.py index 0a5936b2..e586f2f2 100644 --- a/django/src/games/routing.py +++ b/django/src/games/routing.py @@ -2,5 +2,5 @@ from . import pong websocket_urlpatterns = [ - re_path(r'ws/pong/?$', pong.Consumer.as_asgi()), + re_path(r'ws/pong/?$', pong.PongGame.as_asgi()), ] diff --git a/django/src/games/tests.py b/django/src/games/tests.py index 709fc22f..d4012d96 100644 --- a/django/src/games/tests.py +++ b/django/src/games/tests.py @@ -1,14 +1,14 @@ from channels.testing import ChannelsLiveServerTestCase, WebsocketCommunicator from django.contrib.auth import get_user_model from asgiref.sync import sync_to_async -from .pong import Consumer +from .pong import PongGame class PongConsumerTest(ChannelsLiveServerTestCase): async def test_anonymous_user(self): """ Test if anonymous user is rejected. """ - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/?mode=local&player_needed=1") + communicator = WebsocketCommunicator(PongGame.as_asgi(), "/ws/pong/?mode=local&player_needed=1") connected, _ = await communicator.connect() self.assertFalse(connected) @@ -17,7 +17,7 @@ async def test_wrong_user(self): """ Test if wrong user is rejected. """ - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/?mode=local&player_needed=1") + communicator = WebsocketCommunicator(PongGame.as_asgi(), "/ws/pong/?mode=local&player_needed=1") communicator.scope['user'] = "wrong_user" connected, _ = await communicator.connect() @@ -28,7 +28,7 @@ async def test_missing_parameters(self): Test if wrong parameters request is rejected. """ user = await sync_to_async(get_user_model().objects.create_user)(username='testuser', password='password123') - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/") + communicator = WebsocketCommunicator(PongGame.as_asgi(), "/ws/pong/") communicator.scope['user'] = user connected, _ = await communicator.connect() @@ -39,7 +39,7 @@ async def test_websocket_connect(self): Test if the websocket consumer connects successfully. """ user = await sync_to_async(get_user_model().objects.create_user)(username='testuser', password='password123') - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") + communicator = WebsocketCommunicator(PongGame.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") communicator.scope['user'] = user connected, _ = await communicator.connect() @@ -54,7 +54,7 @@ async def test_invalid_message(self): Test if the consumer handles invalid messages properly. """ user = await sync_to_async(get_user_model().objects.create_user)(username='testuser', password='password123') - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") + communicator = WebsocketCommunicator(PongGame.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") communicator.scope['user'] = user connected, _ = await communicator.connect() @@ -84,7 +84,7 @@ async def test_websocket_receive_game_ready(self): Test if the consumer can send and receive messages, such as 'game_ready'. """ user = await sync_to_async(get_user_model().objects.create_user)(username='testuser', password='password123') - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") + communicator = WebsocketCommunicator(PongGame.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") communicator.scope['user'] = user connected, _ = await communicator.connect() From a7da60312d323df208b976d96ab699469f8a7b67 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:15:18 +0200 Subject: [PATCH 11/46] create join message in tournament --- django/src/games/pong.py | 52 +++++++++++++++++++ django/src/games/routing.py | 1 + .../src/games/templates/pong_tournament.html | 39 ++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/django/src/games/pong.py b/django/src/games/pong.py index c080d97e..8110982a 100644 --- a/django/src/games/pong.py +++ b/django/src/games/pong.py @@ -315,3 +315,55 @@ def reset(self, width=0.02, height=0.2, color="white"): self.y = 0.4 self.step = 0.05 self.move = 0 + +class PongTournament(AsyncWebsocketConsumer): + async def connect(self): + query_params = parse_qs(self.scope["query_string"].decode()) + + try: + self.user = self.scope.get("user") + if type(self.user) != get_user_model and self.user.is_anonymous: + raise ValueError("Invalid user") + self.name = query_params.get("name", [None])[0] + if self.name == None: + raise ValueError("Missing name") + else: + logger.info(f"User {self.user.id} is attempting to connect.") + except Exception as e: + logger.warning(f"Connection refused: {str(e)}") + await self.close() + return + + await self.accept() + self.redis = redis.Redis(host="redis") + players = await self.redis.lrange(f'pong_{self.name}_username', 0, -1) + if self.user.username.encode('utf-8') not in players: + await self.redis.rpush(f"pong_{self.name}_id", self.user.id) + await self.redis.rpush(f"pong_{self.name}_username", self.user.username) + + await self.channel_layer.group_add(self.name, self.channel_name) + await self.channel_layer.group_send(self.name, {"type": "join"}) + + async def disconnect(self, close_code): + await self.channel_layer.group_discard(self.group_name, self.channel_name) + if hasattr(self, "redis"): + await self.redis.close() + + logger.info(f"User {self.scope['user'].id} has disconnected.") + + async def join(self, event): + usernames = await self.redis.lrange(f'pong_{self.name}_username', 0, -1) + decoded_usernames = [username.decode('utf-8') for username in usernames] + await self.send(text_data=json.dumps({ + "type": "join", + "content": { + "players": decoded_usernames + } + })) + + async def send_message(self, destination="client", msg_type="", args={}): + message = {"type": msg_type, "content": args} if msg_type else args + if destination == "group": + await self.channel_layer.group_send(self.group_name, message) + elif destination == "client": + await self.send(text_data=json.dumps(message)) diff --git a/django/src/games/routing.py b/django/src/games/routing.py index e586f2f2..7626a4ad 100644 --- a/django/src/games/routing.py +++ b/django/src/games/routing.py @@ -3,4 +3,5 @@ websocket_urlpatterns = [ re_path(r'ws/pong/?$', pong.PongGame.as_asgi()), + re_path(r'ws/pong_tournament/?$', pong.PongTournament.as_asgi()), ] diff --git a/django/src/games/templates/pong_tournament.html b/django/src/games/templates/pong_tournament.html index 3d99cb20..97682dfe 100644 --- a/django/src/games/templates/pong_tournament.html +++ b/django/src/games/templates/pong_tournament.html @@ -1,5 +1,44 @@ {% extends "./extends/base.html" %} {% block content %} +
    + + + + +
    Players
    +
    + {% endblock %} From 8bbf6afa350dbad0d40cbe2556ea5a9f2a47a727 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:12:24 +0200 Subject: [PATCH 12/46] add lock system to tournament --- django/src/games/pong.py | 55 +++++++++++++------ .../src/games/templates/pong_tournament.html | 17 ++++++ 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/django/src/games/pong.py b/django/src/games/pong.py index 8110982a..25c43b8e 100644 --- a/django/src/games/pong.py +++ b/django/src/games/pong.py @@ -1,6 +1,7 @@ import asyncio import json import random +from time import sleep import redis.asyncio as redis import logging from typing import Any, List @@ -319,51 +320,69 @@ def reset(self, width=0.02, height=0.2, color="white"): class PongTournament(AsyncWebsocketConsumer): async def connect(self): query_params = parse_qs(self.scope["query_string"].decode()) + self.redis = redis.Redis(host="redis") try: self.user = self.scope.get("user") if type(self.user) != get_user_model and self.user.is_anonymous: raise ValueError("Invalid user") + + logger.info(f"User {self.user.id} is attempting to connect.") self.name = query_params.get("name", [None])[0] if self.name == None: raise ValueError("Missing name") - else: - logger.info(f"User {self.user.id} is attempting to connect.") + + lock = await self.redis.get(f"pong_{self.name}_lock") + players = await self.redis.lrange(f'pong_{self.name}_username', 0, -1) + if lock != None and self.user.username.encode('utf-8') not in players: + raise ConnectionRefusedError("Tournament is locked") + except Exception as e: logger.warning(f"Connection refused: {str(e)}") await self.close() return await self.accept() - self.redis = redis.Redis(host="redis") - players = await self.redis.lrange(f'pong_{self.name}_username', 0, -1) + await self.channel_layer.group_add(self.name, self.channel_name) + if await self.redis.get(f"pong_{self.name}_creator") == None: + await self.redis.set(f"pong_{self.name}_creator", self.user.username) + await self.send(text_data=json.dumps({"type": "creator"})) + if self.user.username.encode('utf-8') not in players: await self.redis.rpush(f"pong_{self.name}_id", self.user.id) await self.redis.rpush(f"pong_{self.name}_username", self.user.username) - await self.channel_layer.group_add(self.name, self.channel_name) await self.channel_layer.group_send(self.name, {"type": "join"}) async def disconnect(self, close_code): - await self.channel_layer.group_discard(self.group_name, self.channel_name) + await self.channel_layer.group_discard(self.name, self.channel_name) if hasattr(self, "redis"): await self.redis.close() logger.info(f"User {self.scope['user'].id} has disconnected.") + async def receive(self, text_data): + data = json.loads(text_data) + + try: + msg_type = data.get("type") + if not msg_type: + raise AttributeError("Missing 'type'") + + logger.debug(f"Received '{msg_type}' message from user {self.scope['user'].id}.") + match msg_type: + case "lock": + await self.lock() + case _: + raise ValueError("Unknown 'type' in data") + except Exception as e: + logger.warning(f"Invalid message received from user {self.scope['user'].id}: {str(e)}") + await self.send_error("Invalid message") + async def join(self, event): usernames = await self.redis.lrange(f'pong_{self.name}_username', 0, -1) decoded_usernames = [username.decode('utf-8') for username in usernames] - await self.send(text_data=json.dumps({ - "type": "join", - "content": { - "players": decoded_usernames - } - })) + await self.send(text_data=json.dumps({"type": "join", "content": {"players": decoded_usernames}})) - async def send_message(self, destination="client", msg_type="", args={}): - message = {"type": msg_type, "content": args} if msg_type else args - if destination == "group": - await self.channel_layer.group_send(self.group_name, message) - elif destination == "client": - await self.send(text_data=json.dumps(message)) + async def lock(self): + await self.redis.set(f"pong_{self.name}_lock", 1) diff --git a/django/src/games/templates/pong_tournament.html b/django/src/games/templates/pong_tournament.html index 97682dfe..a92d3cb1 100644 --- a/django/src/games/templates/pong_tournament.html +++ b/django/src/games/templates/pong_tournament.html @@ -7,10 +7,13 @@ Players + + {% endblock %} From 67225ad894c83135c5ae4f03b4bcc009e8b70cac Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:23:20 +0200 Subject: [PATCH 13/46] rename variable --- django/src/games/pong.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django/src/games/pong.py b/django/src/games/pong.py index 25c43b8e..36dc3d35 100644 --- a/django/src/games/pong.py +++ b/django/src/games/pong.py @@ -243,9 +243,9 @@ async def send_game_state(self): ) async def save_pong_to_db(self, winner): - PongGame = apps.get_model('games', 'PongGame') + pong_game = apps.get_model('games', 'PongGame') try: - await sync_to_async(PongGame.objects.create)( + await sync_to_async(pong_game.objects.create)( user1=await sync_to_async(get_user_model().objects.get)(id=self.info.players[0]), user2=await sync_to_async(get_user_model().objects.get)(id=self.info.players[1]), score1=self.info.score[0], From dad5bde6103be7087792fee8b81e208cb2e4c21e Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:08:21 +0200 Subject: [PATCH 14/46] rename Consumer to Game --- django/src/games/pong.py | 63 ++++++++++++++++++- django/src/games/routing.py | 3 +- .../src/games/templates/pong_tournament.html | 19 ++++++ django/src/games/tests.py | 14 ++--- 4 files changed, 90 insertions(+), 9 deletions(-) diff --git a/django/src/games/pong.py b/django/src/games/pong.py index 1f802c9d..3e565cb1 100644 --- a/django/src/games/pong.py +++ b/django/src/games/pong.py @@ -12,7 +12,7 @@ logger = logging.getLogger(__name__) -class Consumer(AsyncWebsocketConsumer): +class Game(AsyncWebsocketConsumer): win_goal = 5 async def connect(self): @@ -315,3 +315,64 @@ def reset(self, width=0.02, height=0.2, color="white"): self.y = 0.4 self.step = 0.05 self.move = 0 + +class Tournament(AsyncWebsocketConsumer): + async def connect(self): + query_params = parse_qs(self.scope["query_string"].decode()) + self.redis = redis.Redis(host="redis") + + try: + self.user = self.scope.get("user") + if type(self.user) != get_user_model and self.user.is_anonymous: + raise ValueError("Invalid user") + self.name = query_params.get("name", [None])[0] + if self.name == None: + raise ValueError("Missing name") + else: + logger.info(f"User {self.user.id} is attempting to connect.") + except Exception as e: + logger.warning(f"Connection refused: {str(e)}") + await self.close() + return + + await self.accept() + await self.channel_layer.group_add(self.name, self.channel_name) + players = await self.redis.lrange(f'pong_{self.name}_username', 0, -1) + if self.user.username.encode('utf-8') not in players: + await self.redis.rpush(f"pong_{self.name}_id", self.user.id) + await self.redis.rpush(f"pong_{self.name}_username", self.user.username) + + await self.channel_layer.group_send(self.name, {"type": "join"}) + + async def disconnect(self, close_code): + await self.channel_layer.group_discard(self.group_name, self.channel_name) + if hasattr(self, "redis"): + await self.redis.close() + + logger.info(f"User {self.scope['user'].id} has disconnected.") + + async def receive(self, text_data): + data = json.loads(text_data) + + try: + msg_type = data.get("type") + if not msg_type: + raise AttributeError("Missing 'type'") + logger.debug(f"Received '{msg_type}' message from user {self.scope['user'].id}.") + match msg_type: + case _: + raise ValueError("Unknown 'type' in data") + except Exception as e: + logger.warning(f"Invalid message received from user {self.scope['user'].id}: {str(e)}") + await self.send(text_data=json.dumps({'type': 'error', 'content': str(e)})) + + async def join(self, event): + usernames = await self.redis.lrange(f'pong_{self.name}_username', 0, -1) + decoded_usernames = [username.decode('utf-8') for username in usernames] + logger.info(decoded_usernames) + await self.send(text_data=json.dumps({ + "type": "join", + "content": { + "players": decoded_usernames + } + })) diff --git a/django/src/games/routing.py b/django/src/games/routing.py index 0a5936b2..5bf77e11 100644 --- a/django/src/games/routing.py +++ b/django/src/games/routing.py @@ -2,5 +2,6 @@ from . import pong websocket_urlpatterns = [ - re_path(r'ws/pong/?$', pong.Consumer.as_asgi()), + re_path(r'ws/pong/?$', pong.Game.as_asgi()), + re_path(r'ws/pong_tournament/?$', pong.Tournament.as_asgi()), ] diff --git a/django/src/games/templates/pong_tournament.html b/django/src/games/templates/pong_tournament.html index 3d99cb20..dca5597e 100644 --- a/django/src/games/templates/pong_tournament.html +++ b/django/src/games/templates/pong_tournament.html @@ -1,5 +1,24 @@ {% extends "./extends/base.html" %} {% block content %} +
    + + + + +
    Players
    +
    + {% endblock %} diff --git a/django/src/games/tests.py b/django/src/games/tests.py index 709fc22f..214d9db1 100644 --- a/django/src/games/tests.py +++ b/django/src/games/tests.py @@ -1,14 +1,14 @@ from channels.testing import ChannelsLiveServerTestCase, WebsocketCommunicator from django.contrib.auth import get_user_model from asgiref.sync import sync_to_async -from .pong import Consumer +from .pong import Game class PongConsumerTest(ChannelsLiveServerTestCase): async def test_anonymous_user(self): """ Test if anonymous user is rejected. """ - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/?mode=local&player_needed=1") + communicator = WebsocketCommunicator(Game.as_asgi(), "/ws/pong/?mode=local&player_needed=1") connected, _ = await communicator.connect() self.assertFalse(connected) @@ -17,7 +17,7 @@ async def test_wrong_user(self): """ Test if wrong user is rejected. """ - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/?mode=local&player_needed=1") + communicator = WebsocketCommunicator(Game.as_asgi(), "/ws/pong/?mode=local&player_needed=1") communicator.scope['user'] = "wrong_user" connected, _ = await communicator.connect() @@ -28,7 +28,7 @@ async def test_missing_parameters(self): Test if wrong parameters request is rejected. """ user = await sync_to_async(get_user_model().objects.create_user)(username='testuser', password='password123') - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/") + communicator = WebsocketCommunicator(Game.as_asgi(), "/ws/pong/") communicator.scope['user'] = user connected, _ = await communicator.connect() @@ -39,7 +39,7 @@ async def test_websocket_connect(self): Test if the websocket consumer connects successfully. """ user = await sync_to_async(get_user_model().objects.create_user)(username='testuser', password='password123') - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") + communicator = WebsocketCommunicator(Game.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") communicator.scope['user'] = user connected, _ = await communicator.connect() @@ -54,7 +54,7 @@ async def test_invalid_message(self): Test if the consumer handles invalid messages properly. """ user = await sync_to_async(get_user_model().objects.create_user)(username='testuser', password='password123') - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") + communicator = WebsocketCommunicator(Game.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") communicator.scope['user'] = user connected, _ = await communicator.connect() @@ -84,7 +84,7 @@ async def test_websocket_receive_game_ready(self): Test if the consumer can send and receive messages, such as 'game_ready'. """ user = await sync_to_async(get_user_model().objects.create_user)(username='testuser', password='password123') - communicator = WebsocketCommunicator(Consumer.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") + communicator = WebsocketCommunicator(Game.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") communicator.scope['user'] = user connected, _ = await communicator.connect() From c427a169fb433b88b8aec21a549889cd7cc080a4 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:18:19 +0200 Subject: [PATCH 15/46] create onPageChange event in pong_tournament.html --- django/src/games/templates/pong_tournament.html | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/django/src/games/templates/pong_tournament.html b/django/src/games/templates/pong_tournament.html index a92d3cb1..f97e1716 100644 --- a/django/src/games/templates/pong_tournament.html +++ b/django/src/games/templates/pong_tournament.html @@ -46,10 +46,16 @@ lockBtn.disabled = false; } - function exit() { + function exit(reroute=true) { if (socket) socket.close(); - route("/tournament"); + if (reroute) + route("/tournament"); + } + + function onPageChange(e) { + exit(false); + e.srcElement.removeEventListener('pagechange', onPageChange); } lockBtn.onclick = e => { @@ -57,5 +63,7 @@ lockBtn.disabled = true; socket.send(JSON.stringify({ type: "lock" })); } + + document.getElementById('content').addEventListener('pagechange', onPageChange); {% endblock %} From 7e4d85f836c28942a46a6d7ee5d32f91b9f12b74 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:39:50 +0200 Subject: [PATCH 16/46] add check for creator comming back --- django/src/games/pong.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/django/src/games/pong.py b/django/src/games/pong.py index f3390762..50ef3885 100644 --- a/django/src/games/pong.py +++ b/django/src/games/pong.py @@ -352,6 +352,8 @@ async def connect(self): await self.channel_layer.group_add(self.name, self.channel_name) if await self.redis.get(f"pong_{self.name}_creator") == None: await self.redis.set(f"pong_{self.name}_creator", self.user.username) + + if await self.redis.get(f"pong_{self.name}_creator") == self.user.username.encode("utf-8") and lock == None: await self.send(text_data=json.dumps({"type": "creator"})) if self.user.username.encode('utf-8') not in players: From ca93d61b2fe108549093083ecb847980aacfc9ff Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:12:36 +0200 Subject: [PATCH 17/46] add toast when locking tournament alone --- .../src/games/templates/pong_tournament.html | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/django/src/games/templates/pong_tournament.html b/django/src/games/templates/pong_tournament.html index f97e1716..1a822593 100644 --- a/django/src/games/templates/pong_tournament.html +++ b/django/src/games/templates/pong_tournament.html @@ -2,18 +2,22 @@ {% block content %}
    - - - - +
    Players
    + + + + + +
    Players
    - +
    {% endblock %} diff --git a/django/src/pages/views.py b/django/src/pages/views.py index 79763fb8..33a7923e 100644 --- a/django/src/pages/views.py +++ b/django/src/pages/views.py @@ -65,19 +65,7 @@ def pong_online(request, id=None): @require_GET def tournament(request): - games = [ - {"value": "pong", "label": "Pong"}, - ] - - return create_response( - request=request, - template_name='tournament.html', - title="Tournament", - context={ - "games": games, - }, - need_authentication=True, - ) + return create_response(request, 'tournament.html', title="Tournament", need_authentication=True) def pong_tournament(request, name: str): if not name.isalpha(): From 9fe34bf252fc8f732db43de71c5bef7c4829edf2 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:59:39 +0200 Subject: [PATCH 19/46] wip --- django/src/games/pong.py | 19 ++++++++++++++++-- .../src/games/templates/pong_tournament.html | 20 +++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/django/src/games/pong.py b/django/src/games/pong.py index 50ef3885..4480b09d 100644 --- a/django/src/games/pong.py +++ b/django/src/games/pong.py @@ -1,7 +1,6 @@ import asyncio import json import random -from time import sleep import redis.asyncio as redis import logging from typing import Any, List @@ -380,6 +379,8 @@ async def receive(self, text_data): match msg_type: case "lock": await self.lock() + case "mix": + await self.mix() case _: raise ValueError("Unknown 'type' in data") except Exception as e: @@ -389,7 +390,21 @@ async def receive(self, text_data): async def join(self, event): usernames = await self.redis.lrange(f'pong_{self.name}_username', 0, -1) decoded_usernames = [username.decode('utf-8') for username in usernames] - await self.send(text_data=json.dumps({"type": "join", "content": {"players": decoded_usernames}})) + await self.send(text_data=json.dumps({"type": "join", "players": decoded_usernames})) async def lock(self): await self.redis.set(f"pong_{self.name}_lock", 1) + usernames = await self.redis.lrange(f'pong_{self.name}_username', 0, -1) + self.players = [username.decode('utf-8') for username in usernames] + + async def mix(self): + random.shuffle(self.players) + self.last_player = None + if len(self.players) % 2 != 0: + self.last_player = self.players.pop() + + pairs = [(self.players[i], self.players[i + 1]) for i in range(0, len(self.players), 2)] + if self.last_player: + pairs.append((self.last_player, 'bye')) + + await self.send(text_data=json.dumps({"type": "mix", "pairs": pairs})) diff --git a/django/src/games/templates/pong_tournament.html b/django/src/games/templates/pong_tournament.html index 1a822593..50d5abd4 100644 --- a/django/src/games/templates/pong_tournament.html +++ b/django/src/games/templates/pong_tournament.html @@ -2,6 +2,7 @@ {% block content %}
    +

    Tournament name: {{ name }}

    @@ -12,11 +13,13 @@
    +
    {% endblock %} diff --git a/django/src/pages/urls.py b/django/src/pages/urls.py index 449b2dee..fc98bdfc 100644 --- a/django/src/pages/urls.py +++ b/django/src/pages/urls.py @@ -9,8 +9,8 @@ path('pong/online///', views.pong_online), path('pong/online//', views.pong_online), path('profiles//', views.profiles), - path('tournament/', views.tournament), - path('tournament/pong//', views.pong_tournament), + path('tournaments/', views.tournaments), + path('tournaments/pong//', views.pong_tournament), path('login/', views.authentification), path('register/', views.authentification), path('authorize/', views.authorize), diff --git a/django/src/pages/views.py b/django/src/pages/views.py index e9aa569c..f24c049b 100644 --- a/django/src/pages/views.py +++ b/django/src/pages/views.py @@ -72,7 +72,7 @@ def pong_online(request, id=None, tournament_name=""): ) @require_GET -def tournament(request): +def tournaments(request): return create_response(request, 'tournament.html', title="Tournament", need_authentication=True) def pong_tournament(request, name: str): From 20d39d402610d79dc61f43c83c80a339d7d504bf Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Tue, 15 Oct 2024 20:22:59 +0200 Subject: [PATCH 28/46] wip --- django/src/games/tournament.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/django/src/games/tournament.py b/django/src/games/tournament.py index 2e9a97da..858afe72 100644 --- a/django/src/games/tournament.py +++ b/django/src/games/tournament.py @@ -106,7 +106,7 @@ async def join(self, event): text_data=json.dumps( { "type": "join", - "players": self.get_decoded_list( + "players": await self.get_decoded_list( f"pong_{self.name}_username" ), } @@ -115,7 +115,7 @@ async def join(self, event): async def lock(self): await self.redis.set(f"pong_{self.name}_lock", 1) - self.players = self.get_decoded_list(f"pong_{self.name}_username") + self.players = await self.get_decoded_list(f"pong_{self.name}_username") async def mix(self): random.shuffle(self.players) @@ -134,7 +134,7 @@ async def mix(self): if last_player: player_pairs.append((last_player, "-")) - self.players.push(last_player) + self.players.append(last_player) await self.channel_layer.group_send( self.name, @@ -179,7 +179,7 @@ async def send_games_and_archive(self): text_data=json.dumps( { "type": "current_games", - "current_games": self.get_decoded_list( + "current_games": await self.get_decoded_list( f"pong_{self.name}_current_games" ), } @@ -189,7 +189,7 @@ async def send_games_and_archive(self): text_data=json.dumps( { "type": "history", - "history": self.get_decoded_list( + "history": await self.get_decoded_list( f"pong_{self.name}_history" ), } From e3b7783b5d7ba76f814d9eb939c430653f49cfc8 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 16 Oct 2024 08:04:56 +0200 Subject: [PATCH 29/46] fix missing tournament_name --- django/src/games/pong.py | 37 +++++-------------------------------- django/src/pages/views.py | 4 ++-- 2 files changed, 7 insertions(+), 34 deletions(-) diff --git a/django/src/games/pong.py b/django/src/games/pong.py index af6f4ceb..c4af41e2 100644 --- a/django/src/games/pong.py +++ b/django/src/games/pong.py @@ -34,21 +34,14 @@ async def connect(self): return await self.accept() + await self.channel_layer.group_add(self.room_id, self.channel_name) + self.valid = True logger.info( f"User {self.user.id} successfully connected to room {self.room_id}." ) - - if self.watch: - await self.channel_layer.group_add( - f"{self.room_id}_watcher", self.channel_name - ) - return - - self.valid = True - await self.channel_layer.group_add(self.room_id, self.channel_name) await self.send_message("group", "game_join", {"user": self.user.id}) await self.send_message("client", "game_pad", {"game_pad": self.pad_n}) - if self.tournament_name != "": + if self.tournament_name != "0": await self.send_message( "client", "tournament_name", @@ -58,11 +51,6 @@ async def connect(self): async def initialize_game(self): query_params = parse_qs(self.scope["query_string"].decode()) self.room_id = self.check_missing_param(query_params, "room_id") - self.watch = query_params.get("watch", [None])[0] != None - - if self.watch: - return - self.mode = self.check_missing_param(query_params, "mode") player_needed = self.check_missing_param(query_params, "player_needed") self.tournament_name = self.check_missing_param( @@ -126,11 +114,6 @@ async def disconnect(self, close_code): self.room_id, self.channel_name ) - if self.watch: - await self.channel_layer.group_discard( - f"{self.room_id}_watcher", self.channel_name - ) - await self.redis.close() logger.info(f"User {self.scope['user'].id} has disconnected.") @@ -207,10 +190,6 @@ async def game_stop(self, event): await self.send_message("client", args=event) await self.close() - async def watcher_stop(self, event): - await self.send_message("client", args=event) - await self.close() - def punish_coward(self, coward): if not coward: return @@ -223,7 +202,6 @@ def punish_coward(self, coward): async def loop(self): fps = 0.033 self.reset_game() - await self.send_game_state("watch") while True: try: self.ball.move() @@ -291,7 +269,6 @@ def check_pad_collision(self): async def score_point(self, winner: int): self.info.score[winner] += 1 self.reset_game() - await self.send_game_state("watch") def reset_game(self): self.ball.reset() @@ -302,19 +279,15 @@ async def send_message(self, destination="client", msg_type="", args={}): message = {"type": msg_type, "content": args} if msg_type else args if destination == "group": await self.channel_layer.group_send(self.room_id, message) - elif destination == "watch": - await self.channel_layer.group_send( - f"{self.room_id}_watcher", message - ) elif destination == "client": await self.send(text_data=json.dumps(message)) async def send_error(self, error: str): await self.send_message("client", "game_error", {"error": error}) - async def send_game_state(self, destination="group"): + async def send_game_state(self): await self.send_message( - destination, + "group", "game_state", { "score": self.info.score, diff --git a/django/src/pages/views.py b/django/src/pages/views.py index f24c049b..0efcde24 100644 --- a/django/src/pages/views.py +++ b/django/src/pages/views.py @@ -47,13 +47,13 @@ def pong_local(request): context={ "mode": "local", "room_id": str(uuid4()), - "tournament_name": "" + "tournament_name": "0" }, need_authentication=True, ) @require_GET -def pong_online(request, id=None, tournament_name=""): +def pong_online(request, id=None, tournament_name="0"): uuid_regex = re.compile(r'^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$', re.IGNORECASE) if id != None and not uuid_regex.match(str(id)): From 96cabdb05dba0432b4fb70fd6acd156bb3388fff Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 16 Oct 2024 11:19:50 +0200 Subject: [PATCH 30/46] create ready btn --- .../src/games/templates/pong_tournament.html | 85 +++++-------------- django/src/games/tournament.py | 72 ++++++++++------ 2 files changed, 67 insertions(+), 90 deletions(-) diff --git a/django/src/games/templates/pong_tournament.html b/django/src/games/templates/pong_tournament.html index d629f6d0..362f1de6 100644 --- a/django/src/games/templates/pong_tournament.html +++ b/django/src/games/templates/pong_tournament.html @@ -30,29 +30,32 @@

    History

    + {% endblock %} diff --git a/django/src/games/tournament.py b/django/src/games/tournament.py index d60f9407..b1690e47 100644 --- a/django/src/games/tournament.py +++ b/django/src/games/tournament.py @@ -55,16 +55,8 @@ async def connect(self): creator = creator_encoded.decode("utf-8") - if creator == self.user.username: - if lock == None: - await self.send_message("client", {"type": "unlock_lock"}) - else: - await self.archive() - else: - await self.send_history() - - if lock: - await self.check_current_games_state() + if creator == self.user.username and lock == None: + await self.send_message("client", {"type": "unlock_lock"}) if self.user.username not in players: await self.redis.rpush( @@ -76,6 +68,10 @@ async def connect(self): await self.send_message("group", {"type": "join"}) + if lock: + await self.archive() + await self.check_current_games_state() + async def disconnect(self, close_code): try: creator_encoded = await self.redis.get(f"pong_{self.name}_creator") From 91bfe829ea8a28aa47c30ab7513f388d71a39488 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 16 Oct 2024 23:29:10 +0200 Subject: [PATCH 43/46] tournament done --- django/src/games/tournament.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/django/src/games/tournament.py b/django/src/games/tournament.py index b1690e47..c68ccac6 100644 --- a/django/src/games/tournament.py +++ b/django/src/games/tournament.py @@ -89,6 +89,7 @@ async def disconnect(self, close_code): await self.redis.delete(f"pong_{self.name}_current_games") await self.redis.delete(f"pong_{self.name}_history") await self.redis.delete(f"pong_{self.name}_history_ids") + await self.redis.delete(f"pong_{self.name}_ready_players") except AttributeError as e: logger.debug(str(e)) @@ -137,12 +138,22 @@ async def join(self, event): async def lock(self): await self.redis.set(f"pong_{self.name}_lock", 1) + await self.redis.copy( + f"pong_{self.name}_players", f"pong_{self.name}_ready_players" + ) await self.send_message("group", {"type": "unlock_ready"}) async def unlock_ready(self, event): - await self.send_message("client", {"type": "unlock_ready"}) + ready_players = await self.get_decoded_list( + f"pong_{self.name}_ready_players" + ) + if self.user.username in ready_players: + await self.send_message("client", {"type": "unlock_ready"}) async def ready(self): + await self.redis.lrem( + f"pong_{self.name}_ready_players", 1, self.user.username + ) players = await self.get_decoded_list(f"pong_{self.name}_players") await self.redis.incr(f"pong_{self.name}_ready") ready_encoded = await self.redis.get(f"pong_{self.name}_ready") @@ -150,7 +161,7 @@ async def ready(self): if ready >= len(players): await self.send_message("group", {"type": "unlock_mix"}) - self.redis.set(f"pong_{self.name}_ready", 0) + await self.redis.set(f"pong_{self.name}_ready", 0) async def unlock_mix(self, event): if await self.redis.get( @@ -238,6 +249,10 @@ async def archive(self): await self.redis.rpush(f"pong_{self.name}_history_ids", uuid) await self.redis.lrem(f"pong_{self.name}_players", 1, loser) + await self.redis.copy( + f"pong_{self.name}_players", f"pong_{self.name}_ready_players" + ) + if broadcast: await self.send_message("group", {"type": "broadcast_history"}) else: From 598c5d8ee5cc756b4268c3d3e1bd79fd73fdb8ed Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 16 Oct 2024 23:46:33 +0200 Subject: [PATCH 44/46] sonarcloud --- .../src/games/templates/pong_tournament.html | 4 +- django/src/games/tournament.py | 45 ++++++++++--------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/django/src/games/templates/pong_tournament.html b/django/src/games/templates/pong_tournament.html index 7c856f94..aaac2721 100644 --- a/django/src/games/templates/pong_tournament.html +++ b/django/src/games/templates/pong_tournament.html @@ -185,8 +185,8 @@

    History

    } return response.json(); }).then(data => { - usernames = []; - games = []; + const usernames = []; + const games = []; data.participants.forEach(participant => { usernames.push(participant.username); diff --git a/django/src/games/tournament.py b/django/src/games/tournament.py index c68ccac6..0ae835b6 100644 --- a/django/src/games/tournament.py +++ b/django/src/games/tournament.py @@ -181,9 +181,9 @@ async def mix(self): for i in range(0, len(players), 2): player_pairs.append((players[i], players[i + 1])) - id = str(uuid4()) - uuid_list.append(id) - await self.redis.rpush(f"pong_{self.name}_current_games", id) + game_id = str(uuid4()) + uuid_list.append(game_id) + await self.redis.rpush(f"pong_{self.name}_current_games", game_id) if last_player: player_pairs.append((last_player, "-")) @@ -227,28 +227,29 @@ async def archive(self): for uuid in current_games: try: game = await sync_to_async(pong.objects.get)(uuid=uuid) + broadcast = True + user1 = await sync_to_async(lambda: game.user1.username)() + user2 = await sync_to_async(lambda: game.user2.username)() + loser = user2 if game.score1 > game.score2 else user1 + await self.redis.lrem( + f"pong_{self.name}_current_games", 1, uuid + ) + await self.redis.rpush( + f"pong_{self.name}_history", + json.dumps( + { + "user1": user1, + "user2": user2, + "score1": game.score1, + "score2": game.score2, + } + ), + ) + await self.redis.rpush(f"pong_{self.name}_history_ids", uuid) + await self.redis.lrem(f"pong_{self.name}_players", 1, loser) except pong.DoesNotExist: continue - broadcast = True - user1 = await sync_to_async(lambda: game.user1.username)() - user2 = await sync_to_async(lambda: game.user2.username)() - loser = user2 if game.score1 > game.score2 else user1 - await self.redis.lrem(f"pong_{self.name}_current_games", 1, uuid) - await self.redis.rpush( - f"pong_{self.name}_history", - json.dumps( - { - "user1": user1, - "user2": user2, - "score1": game.score1, - "score2": game.score2, - } - ), - ) - await self.redis.rpush(f"pong_{self.name}_history_ids", uuid) - await self.redis.lrem(f"pong_{self.name}_players", 1, loser) - await self.redis.copy( f"pong_{self.name}_players", f"pong_{self.name}_ready_players" ) From 2e74d3459c8760f18cb54ee1f8775bdee431c5f0 Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Wed, 16 Oct 2024 23:48:56 +0200 Subject: [PATCH 45/46] corrected test --- django/src/games/tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/django/src/games/tests.py b/django/src/games/tests.py index eff6227d..4e078d00 100644 --- a/django/src/games/tests.py +++ b/django/src/games/tests.py @@ -39,7 +39,7 @@ async def test_websocket_connect(self): Test if the websocket consumer connects successfully. """ user = await sync_to_async(get_user_model().objects.create_user)(username='testuser', password='password123') - communicator = WebsocketCommunicator(Pong.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") + communicator = WebsocketCommunicator(Pong.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1&tournament_name=0") communicator.scope['user'] = user connected, _ = await communicator.connect() @@ -54,7 +54,7 @@ async def test_invalid_message(self): Test if the consumer handles invalid messages properly. """ user = await sync_to_async(get_user_model().objects.create_user)(username='testuser', password='password123') - communicator = WebsocketCommunicator(Pong.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") + communicator = WebsocketCommunicator(Pong.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1&tournament_name=0") communicator.scope['user'] = user connected, _ = await communicator.connect() @@ -84,7 +84,7 @@ async def test_websocket_receive_game_ready(self): Test if the consumer can send and receive messages, such as 'game_ready'. """ user = await sync_to_async(get_user_model().objects.create_user)(username='testuser', password='password123') - communicator = WebsocketCommunicator(Pong.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1") + communicator = WebsocketCommunicator(Pong.as_asgi(), "/ws/pong/?mode=local&player_needed=1&host=True&room_id=1&tournament_name=0") communicator.scope['user'] = user connected, _ = await communicator.connect() From 8391db1ea56db969c2d38e466dbd89b60663045e Mon Sep 17 00:00:00 2001 From: Neffi42 <114996380+Neffi42@users.noreply.github.com> Date: Thu, 17 Oct 2024 00:11:01 +0200 Subject: [PATCH 46/46] sonarcloud --- django/src/games/templates/pong_tournament.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/django/src/games/templates/pong_tournament.html b/django/src/games/templates/pong_tournament.html index aaac2721..929a3f83 100644 --- a/django/src/games/templates/pong_tournament.html +++ b/django/src/games/templates/pong_tournament.html @@ -6,6 +6,11 @@

    {{ name }}

    Players

    + + + + +
    Players