diff --git a/frontend/static/css/index.css b/frontend/static/css/index.css deleted file mode 100644 index a3a39b6..0000000 --- a/frontend/static/css/index.css +++ /dev/null @@ -1,10 +0,0 @@ - -body { - margin: 0.5em; - font-family: 'Quicksand', sans-serif; - font-size: 30px; -} - -a { - color: #009579; -} diff --git a/frontend/static/js/index.js b/frontend/static/js/index.js index db5c608..4b7e01d 100644 --- a/frontend/static/js/index.js +++ b/frontend/static/js/index.js @@ -109,10 +109,15 @@ document.addEventListener("DOMContentLoaded", () => { document.body.addEventListener("click", e => { if (e.target.matches("[data-link]")) { e.preventDefault(); + document.querySelectorAll('[data-link]').forEach(e => { + e.classList.remove('active'); + }); + e.target.classList.add('active'); navigateTo(e.target.href.slice(location.origin.length)); } }); router(location.pathname); + document.querySelector('a[href=\'' + location.pathname + '\']')?.classList.add('active'); }); export { client, navigateTo } diff --git a/frontend/templates/index.html b/frontend/templates/index.html index 70f227e..185a016 100644 --- a/frontend/templates/index.html +++ b/frontend/templates/index.html @@ -4,18 +4,23 @@ - Single Page App - + Bozo Pong + - - -
+ + +
+
diff --git a/games/config.py b/games/config.py new file mode 100644 index 0000000..feed4aa --- /dev/null +++ b/games/config.py @@ -0,0 +1,9 @@ +PADDLE_SPEED_MAX = 1 +PADDLE_SIZE_HEIGHT = 10 +PADDLE_SIZE_WIDTH = 100 + +PADDLE_RAIL = PADDLE_SIZE_WIDTH * 5 + +BALL_SPEED_INC = 1 +BALL_SPEED_START = 1 +BALL_SIZE = 4 diff --git a/games/consumers.py b/games/consumers.py new file mode 100644 index 0000000..814fe93 --- /dev/null +++ b/games/consumers.py @@ -0,0 +1,42 @@ +from channels.generic.websocket import AsyncWebsocketConsumer + +from django.contrib.auth.models import User + +import json + +from .objects.GameRoomManager import GameRoomManager + +game_room_manager: GameRoomManager = GameRoomManager() + +class GameWebSocket(AsyncWebsocketConsumer): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.channel_name = "games" + self.group_name = "games" + + def connect(self): + + self.user: User = self.scope["user"] + if (self.user.is_anonymous or not self.user.is_authenticated): + return + + self.channel_layer.group_add(self.group_name, self.channel_name) + + self.game_id = int(self.scope['url_route']['kwargs']['game_id']) + + self.room = game_room_manager.get(self.game_id) + + if (self.room is None): + self.member.send("Tournament not found") + self.disconnect(1017) + + self.room.append(self.member) + + def receive(self, text_data: str = None, bytes_data: bytes = None): + self.member.receive(text_data, bytes_data) + + def disconnect(self, close_code): + member = self.room.get_member_by_socket(self) + if (member is not None): + self.room.remove(self.member, close_code) \ No newline at end of file diff --git a/games/models.py b/games/models.py index 36d5394..3113eaf 100644 --- a/games/models.py +++ b/games/models.py @@ -1,5 +1,7 @@ from django.db import models +from channels.generic.websocket import AsyncWebsocketConsumer + # Create your models here. class GameModel(models.Model): diff --git a/games/objects/GameMember.py b/games/objects/GameMember.py new file mode 100644 index 0000000..a2cdcb9 --- /dev/null +++ b/games/objects/GameMember.py @@ -0,0 +1,17 @@ +from channels.generic.websocket import AsyncWebsocketConsumer + +from transcendence.abstract.AbstractRoomMember import AbstractRoomMember + +class GameMember(AbstractRoomMember): + + def __init__(self, user_id: int, socket: AsyncWebsocketConsumer): + super().__init__(user_id, socket) + self.is_a_player = False + + def receive(self, data: dict): + if (not self.is_a_player): + self.send("You are not a player.") + return + + def send_ball(self, ball): + pass \ No newline at end of file diff --git a/games/objects/GameRoom.py b/games/objects/GameRoom.py new file mode 100644 index 0000000..2f76e4b --- /dev/null +++ b/games/objects/GameRoom.py @@ -0,0 +1,7 @@ +from transcendence.abstract.AbstractRoom import AbstractRoom + +class GameRoom(AbstractRoom): + + def __init__(self, game_room_manager, game_id: int): + super().__init__(game_room_manager) + self.game_id = game_id \ No newline at end of file diff --git a/games/objects/GameRoomManager.py b/games/objects/GameRoomManager.py new file mode 100644 index 0000000..26a43b8 --- /dev/null +++ b/games/objects/GameRoomManager.py @@ -0,0 +1,19 @@ +from transcendence.abstract.AbstractRoomManager import AbstractRoomManager + +from ..models import GameModel +from . import GameRoom + +class GameRoomManager(AbstractRoomManager): + + def get(self, game_id: int): + + for room in self._room_list: + if (room.game_id == game_id): + return room + + if (GameModel.objects.filter(pk = game_id).exists()): + room = GameRoom(self, game_id) + self.append(room) + return room + + return None \ No newline at end of file diff --git a/games/routing.py b/games/routing.py new file mode 100644 index 0000000..eed3b1c --- /dev/null +++ b/games/routing.py @@ -0,0 +1,6 @@ +from django.urls import re_path +from . import consumers + +websocket_urlpatterns = [ + re_path(r'ws/games/(?P\d+)$', consumers.GameWebSocket.as_asgi()) +] diff --git a/games/serializers.py b/games/serializers.py index 5bd2fc6..0c19b0b 100644 --- a/games/serializers.py +++ b/games/serializers.py @@ -21,4 +21,5 @@ class GameSerializer(serializers.ModelSerializer): return "waiting" def get_players_id(self, instance: GameModel): - players_id = [player_game.member_id for player_game in GameMembersModel.objects.filter(game_id=instance.pk)] \ No newline at end of file + players_id = [player_game.player_id for player_game in GameMembersModel.objects.filter(game_id = instance.pk)] + return players_id \ No newline at end of file diff --git a/games/urls.py b/games/urls.py new file mode 100644 index 0000000..8495b34 --- /dev/null +++ b/games/urls.py @@ -0,0 +1,11 @@ +from django.urls import path, re_path +from django.conf import settings +from django.conf.urls.static import static + +from .viewset import GameViewSet +from .views import GameConfigView + +urlpatterns = [ + path("", GameViewSet.as_view({"get": "retrieve"}), name="game_page"), + path("", GameConfigView.as_view(), name = "game_config") +] \ No newline at end of file diff --git a/games/views.py b/games/views.py index 91ea44a..2675031 100644 --- a/games/views.py +++ b/games/views.py @@ -1,3 +1,23 @@ -from django.shortcuts import render +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import permissions, status -# Create your views here. +from django.http import HttpRequest + +from . import config + +class GameConfigView(APIView): + + permission_classes = (permissions.AllowAny,) + + def get(self, request): + config_data = { + "BALL_SIZE": config.BALL_SIZE, + "PADDLE_SPEED_MAX": config.PADDLE_SPEED_MAX, + "PADDLE_SIZE_HEIGHT": config.PADDLE_SIZE_HEIGHT, + "PADDLE_SIZE_WIDTH": config.PADDLE_SIZE_WIDTH, + "PADDLE_RAIL": config.PADDLE_RAIL, + "BALL_SPEED_INC": config.BALL_SPEED_INC, + "BALL_SPEED_START": config.BALL_SPEED_START + } + return Response(config_data, status = status.HTTP_200_OK) \ No newline at end of file diff --git a/games/viewset.py b/games/viewset.py new file mode 100644 index 0000000..17e25a1 --- /dev/null +++ b/games/viewset.py @@ -0,0 +1,27 @@ +from rest_framework import viewsets +from rest_framework.response import Response +from rest_framework import permissions, status +from rest_framework.authentication import SessionAuthentication + +from django.http import HttpRequest +from django.db.models import QuerySet + +from .models import GameModel +from .serializers import GameSerializer + +# Create your views here. +class GameViewSet(viewsets.ModelViewSet): + + queryset = GameModel.objects + serializer_class = GameSerializer + permission_classes = (permissions.AllowAny,) + authentication_classes = (SessionAuthentication,) + + def retrieve(self, request: HttpRequest, pk): + + if (not self.queryset.filter(pk = pk).exists()): + return Response({"detail": "Game not found."}, status=status.HTTP_404_NOT_FOUND) + + game = self.queryset.get(pk=pk) + + return Response(self.serializer_class(game).data, status=status.HTTP_200_OK) \ No newline at end of file diff --git a/profiles/views.py b/profiles/views.py index 702e94f..dbcbcab 100644 --- a/profiles/views.py +++ b/profiles/views.py @@ -12,7 +12,7 @@ class BlockView(APIView): def get(self, request, pk): block = BlockModel.objects.filter(pk=pk) - if (block): + if (block.exists()): return Response(serializers.serialize("json", block), status=status.HTTP_200_OK) else: return Response("Not Found", status=status.HTTP_404_NOT_FOUND) diff --git a/tournament/routing.py b/tournament/routing.py index 7271972..4434723 100644 --- a/tournament/routing.py +++ b/tournament/routing.py @@ -2,5 +2,5 @@ from django.urls import re_path from . import consumers websocket_urlpatterns = [ - re_path(r'ws/tournaments/(?P\d+)$', consumers.TournamentWebConsumer.as_asgi()) + re_path(r'ws/games/(?P\d+)$', consumers.TournamentWebConsumer.as_asgi()) ] diff --git a/transcendence/asgi.py b/transcendence/asgi.py index 939a8ed..cb8ac86 100644 --- a/transcendence/asgi.py +++ b/transcendence/asgi.py @@ -14,6 +14,7 @@ from channels.auth import AuthMiddlewareStack import chat.routing import matchmaking.routing import tournament.routing +import games.routing from django.core.asgi import get_asgi_application @@ -25,7 +26,8 @@ application = ProtocolTypeRouter({ URLRouter( chat.routing.websocket_urlpatterns + matchmaking.routing.websocket_urlpatterns + - tournament.routing.websocket_urlpatterns + tournament.routing.websocket_urlpatterns + + games.routing.websocket_urlpatterns ) ) }) diff --git a/transcendence/urls.py b/transcendence/urls.py index 38eb6d0..d0d06ad 100644 --- a/transcendence/urls.py +++ b/transcendence/urls.py @@ -23,5 +23,6 @@ urlpatterns = [ path('api/accounts/', include('accounts.urls')), path('api/chat/', include('chat.urls')), path('api/tournaments/', include('tournament.urls')), + path('api/games/', include('games.urls')), path('', include('frontend.urls')), ]