From ba0c8499e092e55983303274e10263cdf6f7c0c8 Mon Sep 17 00:00:00 2001 From: Alexander-D-Karpov Date: Sat, 2 Jul 2022 14:20:58 +0300 Subject: [PATCH] Removed files for stable branch --- .gitignore | 133 ----------- README.md | 37 --- chess_backend/__init__.py | 0 chess_backend/asgi.py | 18 -- chess_backend/settings.py | 104 --------- chess_backend/urls.py | 46 ---- chess_backend/wsgi.py | 16 -- common/__init__.py | 0 common/generators.py | 14 -- common/tests.py | 22 -- game/__init__.py | 0 game/admin.py | 6 - game/api/__init__.py | 0 game/api/v1/__init__.py | 0 game/api/v1/serializers.py | 139 ----------- game/api/v1/views.py | 169 -------------- game/apps.py | 6 - game/authentication.py | 35 --- game/migrations/0001_initial.py | 57 ----- game/migrations/__init__.py | 0 game/models.py | 216 ----------------- game/permissions.py | 0 game/services/__init__.py | 0 game/services/jwt.py | 45 ---- game/tests.py | 0 game/urls.py | 21 -- manage.py | 22 -- requirements.txt | 46 ---- room/__init__.py | 0 room/apps.py | 6 - room/consumers.py | 397 -------------------------------- room/middleware.py | 33 --- room/migrations/0001_initial.py | 15 -- room/migrations/__init__.py | 0 room/models.py | 50 ---- room/routing.py | 8 - room/services/__init__.py | 0 room/services/room_create.py | 45 ---- room/tests.py | 3 - room/views.py | 3 - 40 files changed, 1712 deletions(-) delete mode 100644 .gitignore delete mode 100644 README.md delete mode 100644 chess_backend/__init__.py delete mode 100644 chess_backend/asgi.py delete mode 100644 chess_backend/settings.py delete mode 100644 chess_backend/urls.py delete mode 100644 chess_backend/wsgi.py delete mode 100644 common/__init__.py delete mode 100644 common/generators.py delete mode 100644 common/tests.py delete mode 100644 game/__init__.py delete mode 100644 game/admin.py delete mode 100644 game/api/__init__.py delete mode 100644 game/api/v1/__init__.py delete mode 100644 game/api/v1/serializers.py delete mode 100644 game/api/v1/views.py delete mode 100644 game/apps.py delete mode 100644 game/authentication.py delete mode 100644 game/migrations/0001_initial.py delete mode 100644 game/migrations/__init__.py delete mode 100644 game/models.py delete mode 100644 game/permissions.py delete mode 100644 game/services/__init__.py delete mode 100644 game/services/jwt.py delete mode 100644 game/tests.py delete mode 100644 game/urls.py delete mode 100755 manage.py delete mode 100644 requirements.txt delete mode 100644 room/__init__.py delete mode 100644 room/apps.py delete mode 100644 room/consumers.py delete mode 100644 room/middleware.py delete mode 100644 room/migrations/0001_initial.py delete mode 100644 room/migrations/__init__.py delete mode 100644 room/models.py delete mode 100644 room/routing.py delete mode 100644 room/services/__init__.py delete mode 100644 room/services/room_create.py delete mode 100644 room/tests.py delete mode 100644 room/views.py diff --git a/.gitignore b/.gitignore deleted file mode 100644 index f28dba1..0000000 --- a/.gitignore +++ /dev/null @@ -1,133 +0,0 @@ -.idea -static/ -media/ - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ diff --git a/README.md b/README.md deleted file mode 100644 index 12d3433..0000000 --- a/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# chess_rpg_backend -Backend for chess rpg game - -### installation -```shell -$ python3 manage.py makemigrations & python3 manage.py migrate -$ docker run -p 6379:6379 -d redis:5 -``` - -### run -```shell -$ python3 manage.py runserver 0.0.0.0:8000 -``` - -### Описание команд сокетов -```python -# подключиние к очереди(ws://room/) -{ - "type": "connect", - "deck_id": int -} - -# коннект к комнате (сообщение от сервера) -{ - "type": "INFO", - "opponent_score": int, - "coordinates" : [(x: int, y: int, type: str, model_url: url, your: bool), ...], - "opponent_online": true, - "first": bool -} - -# состояние оппонента в комнате(сообщение от сервера) -{ - "type": "INFO", - "message": "opponent is online" / "opponent is offline" -} -``` diff --git a/chess_backend/__init__.py b/chess_backend/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/chess_backend/asgi.py b/chess_backend/asgi.py deleted file mode 100644 index d8f53d7..0000000 --- a/chess_backend/asgi.py +++ /dev/null @@ -1,18 +0,0 @@ -import os - -from django.core.asgi import get_asgi_application -from channels.routing import ProtocolTypeRouter, URLRouter - -import room.routing -from room.middleware import HeaderAuthMiddleware - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "chess_backend.settings") - -application = ProtocolTypeRouter( - { - "http": get_asgi_application(), - "websocket": HeaderAuthMiddleware( - URLRouter(room.routing.websocket_urlpatterns) - ), - } -) diff --git a/chess_backend/settings.py b/chess_backend/settings.py deleted file mode 100644 index a05c22c..0000000 --- a/chess_backend/settings.py +++ /dev/null @@ -1,104 +0,0 @@ -import os -from pathlib import Path - -# Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "django-insecure-%_8sy196w4hzo9^cp9(@r=i+amh47r4mxfhq_(ok&=c(@%bhmk" -TOKEN_EXP = 2678400 # 31 day -DEBUG = True - -ALLOWED_HOSTS = [] - -INSTALLED_APPS = [ - "django.contrib.sessions", - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.messages", - # Packages - "rest_framework", - "channels", - # Apps - "game", - "room", -] - -if DEBUG: - INSTALLED_APPS.append("django.contrib.staticfiles") - INSTALLED_APPS.append("drf_yasg") - - -MIDDLEWARE = [ - "django.middleware.security.SecurityMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", - "django.middleware.common.CommonMiddleware", - "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", -] - -ROOT_URLCONF = "chess_backend.urls" - -ASGI_APPLICATION = "chess_backend.asgi.application" -CHANNEL_LAYERS = { - "default": { - "BACKEND": "channels_redis.core.RedisChannelLayer", - "CONFIG": { - "hosts": [("127.0.0.1", 6379)], - }, - }, -} - - -WSGI_APPLICATION = "chess_backend.wsgi.application" - - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", - } -} - -LANGUAGES = [ - ("en-us", "English"), - ("ru", "Russian"), -] - -TIME_ZONE = "Europe/Moscow" - -USE_I18N = True - -USE_TZ = True - -MEDIA_URL = "/media/" -STATIC_URL = "/static/" - -if DEBUG: - MEDIA_ROOT = os.path.join(BASE_DIR, "media/") - STATIC_ROOT = os.path.join(BASE_DIR, "static/") -else: - MEDIA_ROOT = "/var/www/media/" - STATIC_ROOT = "/var/www/static/" - -DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" - -LOGGING = { - "version": 1, - "disable_existing_loggers": False, - "handlers": { - "file": { - "level": "WARNING", - "class": "logging.FileHandler", - "filename": "debug.log", - }, - }, - "loggers": { - "django": { - "handlers": ["file"], - "level": "DEBUG", - "propagate": True, - }, - }, -} diff --git a/chess_backend/urls.py b/chess_backend/urls.py deleted file mode 100644 index 956f93a..0000000 --- a/chess_backend/urls.py +++ /dev/null @@ -1,46 +0,0 @@ -from django.conf import settings -from django.conf.urls.static import static -from django.template.defaulttags import url -from django.urls import path, include, re_path - -# openapi schema -from rest_framework import permissions -from drf_yasg.views import get_schema_view -from drf_yasg import openapi - - -schema_view = get_schema_view( - openapi.Info( - title="Snippets API", - default_version="v1", - description="Test description", - terms_of_service="https://www.google.com/policies/terms/", - contact=openapi.Contact(email="contact@snippets.local"), - license=openapi.License(name="BSD License"), - ), - public=True, - permission_classes=(permissions.AllowAny,), -) - -urlpatterns = [path("api/", include("game.urls"))] + static( - settings.MEDIA_URL, document_root=settings.MEDIA_ROOT -) - -if settings.DEBUG: - urlpatterns += [ - re_path( - r"^swagger(?P\.json|\.yaml)$", - schema_view.without_ui(cache_timeout=0), - name="schema-json", - ), - re_path( - r"^swagger/$", - schema_view.with_ui("swagger", cache_timeout=0), - name="schema-swagger-ui", - ), - re_path( - r"^redoc/$", - schema_view.with_ui("redoc", cache_timeout=0), - name="schema-redoc", - ), - ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/chess_backend/wsgi.py b/chess_backend/wsgi.py deleted file mode 100644 index f8b1f1e..0000000 --- a/chess_backend/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for chess_backend project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'chess_backend.settings') - -application = get_wsgi_application() diff --git a/common/__init__.py b/common/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/common/generators.py b/common/generators.py deleted file mode 100644 index 154bf66..0000000 --- a/common/generators.py +++ /dev/null @@ -1,14 +0,0 @@ -import string -import secrets - -from random import randint - - -def generate_charset(length: int): - return "".join( - secrets.choice(string.digits + string.ascii_letters) for _ in range(length) - ) - - -def gen_ton(): - return int("".join([str(randint(0, 9)) for _ in range(48)])) diff --git a/common/tests.py b/common/tests.py deleted file mode 100644 index fe1f195..0000000 --- a/common/tests.py +++ /dev/null @@ -1,22 +0,0 @@ -from rest_framework.test import APITestCase -from django.urls import reverse -from rest_framework import status - - -class GetBasicTest(APITestCase): - def __init__(self, url): - super().__init__() - self._url = reverse(url) - - @property - def url(self): - return self._url - - @url.setter - def url(self, value): - self._url = reverse(value) - - def test_accessibility(self): - """Test if POST request is possible""" - response = self.client.post(self._url) - self.assertNotEqual(response, status.HTTP_404_NOT_FOUND) diff --git a/game/__init__.py b/game/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/game/admin.py b/game/admin.py deleted file mode 100644 index b8635f5..0000000 --- a/game/admin.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.contrib import admin - -# Register your models here. -from game.models import HeroImageSet - -admin.site.register(HeroImageSet) diff --git a/game/api/__init__.py b/game/api/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/game/api/v1/__init__.py b/game/api/v1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/game/api/v1/serializers.py b/game/api/v1/serializers.py deleted file mode 100644 index 7b2ef01..0000000 --- a/game/api/v1/serializers.py +++ /dev/null @@ -1,139 +0,0 @@ -from rest_framework import serializers -from rest_framework.exceptions import ValidationError - -from game.models import Hero, Player, HeroInDeck, Deck, PlayerAuthSession -from game.services.jwt import read_jwt - - -class CreateHeroSerializer(serializers.ModelSerializer): - class Meta: - model = Hero - fields = ( - "type", - "health", - "attack", - "speed", - ) - - -class GetHeroSerializer(serializers.ModelSerializer): - class Meta: - model = Hero - fields = ( - "added", - "type", - "idle_img", - "attack_img", - "die_img", - "health", - "attack", - "speed", - ) - - -class ListHeroSerializer(serializers.ModelSerializer): - class Meta: - model = Hero - fields = ( - "uuid", - "type", - "idle_img", - "attack_img", - "die_img", - "health", - "attack", - "speed", - ) - - -class CreatePlayerSerializer(serializers.ModelSerializer): - class Meta: - model = Player - fields = ("ton_wallet", "name") - - -class CreateDeckSerializer(serializers.ModelSerializer): - hero_ids = serializers.ListSerializer( - child=serializers.UUIDField(), min_length=16, max_length=16 - ) - - class Meta: - model = Deck - fields = ("hero_ids",) - - def validate_hero_ids(self, value): - if len(set(value)) != 16: - raise ValidationError("Some of the uuids are not unique") - - for x in value: - if not (hero := Hero.objects.filter(uuid=x)): - raise ValidationError(f"Hero with uuid {x} doesn't exist") - - if hero.first().player.id != self.context["request"].user.id: - raise ValidationError( - f"Attempt to manipulate player with id {hero.first().player.id} hero" - ) - - if self.context["request"].method in ["POST"]: - if deck := HeroInDeck.objects.filter(hero=hero.first()): - raise ValidationError( - f"Hero with uuid {x} is already in deck with id {deck.first().deck.id}" - ) - - return value - - def create(self, validated_data): - deck = Deck.objects.create(player=self.context["request"].user) - for x in validated_data["hero_ids"]: - HeroInDeck.objects.create(hero_id=x, deck=deck) - return deck - - def update(self, instance, validated_data): - for x in instance.get_heroes(): - HeroInDeck.objects.get(hero=x).delete() - - for x in validated_data["hero_ids"]: - HeroInDeck.objects.create(hero_id=x, deck=instance) - - return instance - - -class GetPlayerSerializer(serializers.ModelSerializer): - class Meta: - model = Player - fields = ("id", "name") - - -class GetDeckSerializer(serializers.ModelSerializer): - player = GetPlayerSerializer() - heroes = ListHeroSerializer(many=True) - - class Meta: - model = Deck - fields = ("player", "heroes") - - -class ObtainTokenPairSerializer(serializers.Serializer): - refresh_token = serializers.CharField(max_length=300) - - def __init__(self, instance=None, data=None, **kwargs): - super().__init__(instance, data, **kwargs) - self.player_id = None - - def validate_refresh_token(self, value): - payload = read_jwt(value) - if not payload: - raise ValidationError("Token is incorrect or expired") - - if "jit" not in payload: - raise ValidationError("Token is incorrect") - - jit = payload["jit"] - - try: - session = PlayerAuthSession.objects.get(jit=jit) - except PlayerAuthSession.DoesNotExist: - return ValidationError("Incorrect user session") - - self.player_id = session.player.id - return value diff --git a/game/api/v1/views.py b/game/api/v1/views.py deleted file mode 100644 index 6d6222a..0000000 --- a/game/api/v1/views.py +++ /dev/null @@ -1,169 +0,0 @@ -from rest_framework import status - -from rest_framework.generics import GenericAPIView, UpdateAPIView -from rest_framework.mixins import ( - CreateModelMixin, - RetrieveModelMixin, - ListModelMixin, - DestroyModelMixin, - UpdateModelMixin, -) -from rest_framework.response import Response - -from game.authentication import PlayerAuthentication -from game.models import Hero, Deck -from game.api.v1.serializers import ( - CreateHeroSerializer, - GetHeroSerializer, - CreatePlayerSerializer, - ListHeroSerializer, - CreateDeckSerializer, - GetDeckSerializer, - ObtainTokenPairSerializer, -) -from game.services.jwt import sign_jwt - - -class ListCreateHeroView(GenericAPIView, CreateModelMixin, ListModelMixin): - authentication_classes = (PlayerAuthentication,) - - def perform_create(self, serializer): - return serializer.save() - - def post(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - instance = self.perform_create(serializer) - return Response({"uuid": instance.uuid}, status=status.HTTP_201_CREATED) - - def get(self, request, *args, **kwargs): - return self.list(request, *args, **kwargs) - - def get_serializer_class(self): - if self.request.method == "GET": - return ListHeroSerializer - else: - return CreateHeroSerializer - - def get_queryset(self): - return Hero.objects.filter(player_id=self.request.user.id) - - -class RetrieveHeroView(RetrieveModelMixin, UpdateAPIView, GenericAPIView): - serializer_class = GetHeroSerializer - lookup_field = "uuid" - queryset = Hero.objects.all() - - def get_authenticators(self): - if self.request.method != "GET": - self.authentication_classes = [PlayerAuthentication] - return [auth() for auth in self.authentication_classes] - - def get(self, request, *args, **kwargs): - return self.retrieve(request, *args, **kwargs) - - def put(self, request, *args, **kwargs): - return self.update(request, *args, **kwargs) - - def patch(self, request, *args, **kwargs): - return self.partial_update(request, *args, **kwargs) - - -class PlayerCreateView(GenericAPIView, CreateModelMixin): - serializer_class = CreatePlayerSerializer - - def perform_create(self, serializer): - return serializer.save() - - def post(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - instance = self.perform_create(serializer) - - access_jwt = instance.get_access_token() - refresh_jwt = instance.get_refresh_token() - return Response( - { - "access_token": access_jwt, - "refresh_token": refresh_jwt, - "deck_id": instance.get_last_deck().id, - }, - status=status.HTTP_201_CREATED, - ) - - -class DeckCreateView(GenericAPIView, CreateModelMixin): - serializer_class = CreateDeckSerializer - authentication_classes = (PlayerAuthentication,) - - def perform_create(self, serializer): - return serializer.save(player=self.request.user) - - def post(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - instance = self.perform_create(serializer) - heroes_list = ListHeroSerializer(instance.get_heroes(), many=True) - heroes_list.data["deck_id"] = instance.id - return Response(heroes_list.data, status=status.HTTP_201_CREATED) - - -class RetireUpdateDeleteDeckView( - RetrieveHeroView, DestroyModelMixin, UpdateModelMixin, GenericAPIView -): - lookup_field = "id" - queryset = Deck.objects.all() - - def get_serializer_class(self): - if self.request.method == "GET": - return GetDeckSerializer - else: - return CreateDeckSerializer - - def get(self, request, *args, **kwargs): - return self.retrieve(request, *args, **kwargs) - - def perform_update(self, serializer): - return serializer.update(self.get_object(), self.request.data) - - def put(self, request, *args, **kwargs): - if not self._check_user_identity(kwargs["id"]): - return Response( - "Attempt to change another user's deck", - status=status.HTTP_403_FORBIDDEN, - ) - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - instance = self.perform_update(serializer) - heroes_list = ListHeroSerializer(instance.get_heroes(), many=True) - return Response(heroes_list.data, status=status.HTTP_200_OK) - - def delete(self, request, *args, **kwargs): - if not self._check_user_identity(kwargs["id"]): - return Response( - "Attempt to delete another user's deck", - status=status.HTTP_403_FORBIDDEN, - ) - self.destroy(request, *args, **kwargs) - return Response( - f"Destroyed deck with id {kwargs['id']}", status=status.HTTP_200_OK - ) - - def _check_user_identity(self, deck_id) -> bool: - return deck_id in list( - Deck.objects.filter(player_id=self.request.user.id).values_list( - "id", flat=True - ) - ) - - -class RefreshAuthKey(GenericAPIView): - serializer_class = ObtainTokenPairSerializer - - def post(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - access_jwt = sign_jwt( - {"id": serializer.player_id, "type": "access"}, t_life=3600 - ) - return Response({"access_token": access_jwt}, status=status.HTTP_200_OK) diff --git a/game/apps.py b/game/apps.py deleted file mode 100644 index 8ad49cb..0000000 --- a/game/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class GameConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'game' diff --git a/game/authentication.py b/game/authentication.py deleted file mode 100644 index 75b1ea3..0000000 --- a/game/authentication.py +++ /dev/null @@ -1,35 +0,0 @@ -from jwt import DecodeError -from rest_framework import authentication -from rest_framework import exceptions -from .models import Player -from .services.jwt import read_jwt - - -class PlayerAuthentication(authentication.BaseAuthentication): - def authenticate(self, request): - - if "Authorization" not in request.headers or not ( - token := request.headers["Authorization"] - ): - raise exceptions.AuthenticationFailed("No credentials provided.") - - try: - t = read_jwt(token) - except DecodeError: - raise exceptions.AuthenticationFailed("Token is incorrect") - - if not t: - raise exceptions.AuthenticationFailed("Token is incorrect of expired") - - if "id" not in t and "type" not in t: - raise exceptions.AuthenticationFailed("No user data") - - if t["type"] != "access": - raise exceptions.AuthenticationFailed("Incorrect token type") - - try: - user = Player.objects.get(id=int(t["id"])) - except Player.DoesNotExist: - raise exceptions.AuthenticationFailed("No such user") - - return user, None diff --git a/game/migrations/0001_initial.py b/game/migrations/0001_initial.py deleted file mode 100644 index af633c5..0000000 --- a/game/migrations/0001_initial.py +++ /dev/null @@ -1,57 +0,0 @@ -# Generated by Django 4.0.5 on 2022-06-04 14:16 - -import django.core.validators -from django.db import migrations, models -import uuid - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='Hero', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), - ('added', models.DateTimeField(auto_now_add=True)), - ('type', models.CharField(choices=[('WIZARD', 'wizard'), ('ARCHER', 'archer'), ('WARRIOR', 'warrior')], max_length=7)), - ('idle_img', models.ImageField(upload_to='uploads/idle')), - ('attack_img', models.ImageField(upload_to='uploads/attack')), - ('die_img', models.ImageField(upload_to='uploads/die')), - ('health', models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10)])), - ('speed', models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10)])), - ], - options={ - 'verbose_name': 'hero', - 'verbose_name_plural': 'heroes', - 'ordering': ['-added'], - }, - ), - migrations.CreateModel( - name='Player', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('ton_wallet', models.CharField(max_length=50, verbose_name='TON wallet')), - ('name', models.CharField(blank=True, max_length=100)), - ('added', models.DateTimeField(auto_now_add=True)), - ], - options={ - 'verbose_name': 'player', - 'verbose_name_plural': 'players', - 'ordering': ['-added'], - }, - ), - migrations.AddIndex( - model_name='player', - index=models.Index(fields=['ton_wallet'], name='game_player_ton_wal_47dd93_idx'), - ), - migrations.AddIndex( - model_name='hero', - index=models.Index(fields=['uuid'], name='game_hero_uuid_ada5d9_idx'), - ), - ] diff --git a/game/migrations/__init__.py b/game/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/game/models.py b/game/models.py deleted file mode 100644 index 7976b25..0000000 --- a/game/models.py +++ /dev/null @@ -1,216 +0,0 @@ -import random -import uuid - -from django.core.validators import ( - MinValueValidator, - MaxValueValidator, - MinLengthValidator, - MaxLengthValidator, -) -from django.db import models - -from common.generators import generate_charset -from game.services.jwt import sign_jwt - - -class HeroTypes(models.TextChoices): - wizard = "WIZARD", "wizard" - archer = "ARCHER", "archer" - warrior = "WARRIOR", "warrior" - king = "KING", "king" - - -class Player(models.Model): - """base model to handle and store users""" - - ton_wallet = models.CharField( - verbose_name="TON wallet", - validators=[MinLengthValidator(48), MaxLengthValidator(48)], - max_length=48, - unique=True, - ) - name = models.CharField(max_length=100, blank=True) - created = models.DateTimeField(auto_now_add=True) - - def save( - self, force_insert=False, force_update=False, using=None, update_fields=None - ): - """saves user and creates deck for him with 16 heroes""" - super(Player, self).save() - PlayerAuthSession.objects.create(player=self) - deck = Deck.objects.create(player=self) - types = ( - ["KING"] - + ["ARCHER" for _ in range(4)] - + ["WARRIOR" for _ in range(6)] - + ["WIZARD" for _ in range(2)] - + [random.choice(HeroTypes.choices[:3])[0] for _ in range(3)] - ) - for t in types: - hero = Hero() - hero.player = self - hero.type = t - - hero.health = random.randint(0, 10) - hero.attack = random.randint(0, 10) - hero.speed = random.randint(0, 10) - - hero.save() - HeroInDeck.objects.create(deck=deck, hero=hero) - - def get_last_deck(self): - return Deck.objects.filter(player=self).last() - - def get_auth_session(self): - return PlayerAuthSession.objects.get(player=self).jit - - def get_refresh_token(self): - return sign_jwt({"jit": self.get_auth_session(), "type": "refresh"}) - - def get_access_token(self): - return sign_jwt({"id": self.id, "type": "access"}, t_life=3600) - - def __str__(self): - return self.name - - class Meta: - indexes = [models.Index(fields=["ton_wallet"])] - ordering = ["-created"] - - db_table = "player" - verbose_name = "player" - verbose_name_plural = "players" - - -class Hero(models.Model): - """Model to store heroes and their stats, connected to player""" - - uuid = models.UUIDField( - default=uuid.uuid4, editable=False, unique=True, primary_key=True - ) - player = models.ForeignKey( - Player, - on_delete=models.CASCADE, - related_name="heroes", - related_query_name="hero", - ) - added = models.DateTimeField(auto_now_add=True) - - type = models.CharField(blank=False, choices=HeroTypes.choices, max_length=7) - model = models.ForeignKey("HeroModelSet", on_delete=models.CASCADE) - health = models.IntegerField( - validators=[MinValueValidator(1), MaxValueValidator(10)], blank=False - ) - attack = models.IntegerField( - validators=[MinValueValidator(1), MaxValueValidator(10)], blank=False - ) - speed = models.IntegerField( - validators=[MinValueValidator(1), MaxValueValidator(10)], blank=False - ) - - def idle_img(self): - return self.idle_img_f.image.url - - def attack_img(self): - return self.attack_img_f.image.url - - def die_img(self): - return self.die_img_f.image.url - - def __str__(self): - return f"{self.type} {self.player.name}" - - def save( - self, force_insert=False, force_update=False, using=None, update_fields=None - ): - self.idle_img_f = random.choice( - [x for x in HeroModelSet.objects.filter(hero_type=self.type)] - ) - self.attack_img_f = random.choice( - [x for x in HeroModelSet.objects.filter(hero_type=self.type)] - ) - self.die_img_f = random.choice( - [x for x in HeroModelSet.objects.filter(hero_type=self.type)] - ) - super(Hero, self).save() - - class Meta: - indexes = [models.Index(fields=["uuid"])] - ordering = ["-added"] - - db_table = "hero" - verbose_name = "hero" - verbose_name_plural = "heroes" - - -class HeroModelSet(models.Model): - hero_type = models.CharField(blank=False, choices=HeroTypes.choices, max_length=7) - model = models.ImageField(upload_to="uploads/") - - def __str__(self): - return f"{self.hero_type} model file" - - -class Deck(models.Model): - player = models.ForeignKey( - Player, - on_delete=models.CASCADE, - related_name="decks", - related_query_name="deck", - ) - - def __str__(self): - return f"{self.player.name}'s deck" - - def get_heroes(self): - return [x.hero for x in HeroInDeck.objects.filter(deck=self)] - - def heroes(self): - # added for better DRF view - return self.get_heroes() - - def score(self): - return sum([x.attack + x.health + x.speed for x in self.get_heroes()]) - - class Meta: - db_table = "deck" - verbose_name = "deck" - verbose_name_plural = "decks" - - -class HeroInDeck(models.Model): - deck = models.ForeignKey( - Deck, - on_delete=models.CASCADE, - related_name="hero_in_deck", - related_query_name="heroes", - ) - hero = models.OneToOneField( - Hero, - on_delete=models.CASCADE, - related_name="hero_in_deck", - related_query_name="decks", - ) - - class Meta: - db_table = "hero_in_deck" - verbose_name = "Hero in deck" - verbose_name_plural = "Heroes in decks" - - -class PlayerAuthSession(models.Model): - player = models.OneToOneField( - Player, unique_for_month=True, on_delete=models.CASCADE - ) - jit = models.CharField(max_length=30) - - def save( - self, force_insert=False, force_update=False, using=None, update_fields=None - ): - self.jit = generate_charset(30) - super(PlayerAuthSession, self).save( - force_insert=force_insert, - force_update=force_update, - using=using, - update_fields=update_fields, - ) diff --git a/game/permissions.py b/game/permissions.py deleted file mode 100644 index e69de29..0000000 diff --git a/game/services/__init__.py b/game/services/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/game/services/jwt.py b/game/services/jwt.py deleted file mode 100644 index 87f7e16..0000000 --- a/game/services/jwt.py +++ /dev/null @@ -1,45 +0,0 @@ -import jwt -import pytz - -from datetime import datetime -from django.conf import settings -from jwt import ExpiredSignatureError, InvalidSignatureError - -TIMEZONE = pytz.timezone("Europe/Moscow") - - -def sign_jwt(data: dict, t_life: None | int = None) -> str: - """generate and sign jwt with iat and exp using data from settings""" - iat = int(datetime.now(tz=TIMEZONE).timestamp()) - exp = iat + settings.TOKEN_EXP if not t_life else iat + t_life - payload = {"iat": iat, "exp": exp} - for nm, el in data.items(): - if nm not in ["iat", "exp"]: - payload[nm] = el - - secret = settings.SECRET_KEY - token = jwt.encode(payload=payload, key=secret) - return token - - -def read_jwt(token: str) -> dict | bool: - """reads jwt, validates it and return payload if correct""" - header_data = jwt.get_unverified_header(token) - secret = settings.SECRET_KEY - try: - payload = jwt.decode(token, key=secret, algorithms=[header_data["alg"]]) - except ExpiredSignatureError as e: - return False - except InvalidSignatureError as e: - return False - - if "exp" not in payload: - return False - - if int(datetime.now(tz=TIMEZONE).timestamp()) > payload["exp"]: - return False - - payload.pop("iat", None) - payload.pop("exp", None) - - return payload diff --git a/game/tests.py b/game/tests.py deleted file mode 100644 index e69de29..0000000 diff --git a/game/urls.py b/game/urls.py deleted file mode 100644 index f322001..0000000 --- a/game/urls.py +++ /dev/null @@ -1,21 +0,0 @@ -from django.urls import path - -from game.api.v1.views import ( - ListCreateHeroView, - RetrieveHeroView, - PlayerCreateView, - DeckCreateView, - RetireUpdateDeleteDeckView, - RefreshAuthKey, -) - -urlpatterns = [ - path("v1/hero/", ListCreateHeroView.as_view(), name="hero_api_create"), - path("v1/hero/", RetrieveHeroView.as_view(), name="hero_api_retrieve"), - path("v1/player/refresh", RefreshAuthKey.as_view(), name="player_create_api"), - path("v1/player/", PlayerCreateView.as_view(), name="player_create_api"), - path("v1/deck/", DeckCreateView.as_view(), name="deck_create_api"), - path( - "v1/deck/", RetireUpdateDeleteDeckView.as_view(), name="deck_retire_api" - ), -] diff --git a/manage.py b/manage.py deleted file mode 100755 index 9d7d2e2..0000000 --- a/manage.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -"""Django's command-line utility for administrative tasks.""" -import os -import sys - - -def main(): - """Run administrative tasks.""" - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'chess_backend.settings') - try: - from django.core.management import execute_from_command_line - except ImportError as exc: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) from exc - execute_from_command_line(sys.argv) - - -if __name__ == '__main__': - main() diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 767bdf9..0000000 --- a/requirements.txt +++ /dev/null @@ -1,46 +0,0 @@ -asgiref==3.5.2 -asttokens==2.0.5 -attrs==21.4.0 -autobahn==22.5.1 -Automat==20.2.0 -backcall==0.2.0 -cffi==1.15.0 -channels==3.0.4 -constantly==15.1.0 -cryptography==37.0.2 -daphne==3.0.2 -decorator==5.1.1 -Django==4.0.5 -djangorestframework==3.13.1 -executing==0.8.3 -hyperlink==21.0.0 -idna==3.3 -incremental==21.3.0 -ipython==8.4.0 -jedi==0.18.1 -matplotlib-inline==0.1.3 -parso==0.8.3 -pexpect==4.8.0 -pickleshare==0.7.5 -Pillow==9.1.1 -prompt-toolkit==3.0.29 -ptyprocess==0.7.0 -pure-eval==0.2.2 -pyasn1==0.4.8 -pyasn1-modules==0.2.8 -pycparser==2.21 -Pygments==2.12.0 -PyJWT==2.4.0 -pyOpenSSL==22.0.0 -pytz==2022.1 -service-identity==21.1.0 -six==1.16.0 -sqlparse==0.4.2 -stack-data==0.2.0 -traitlets==5.2.2.post1 -Twisted==22.4.0 -txaio==22.2.1 -typing_extensions==4.2.0 -wcwidth==0.2.5 -zope.interface==5.4.0 -channels_redis diff --git a/room/__init__.py b/room/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/room/apps.py b/room/apps.py deleted file mode 100644 index 38c33e7..0000000 --- a/room/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class RoomConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'room' diff --git a/room/consumers.py b/room/consumers.py deleted file mode 100644 index 04c8fab..0000000 --- a/room/consumers.py +++ /dev/null @@ -1,397 +0,0 @@ -import json - -from asgiref.sync import sync_to_async -from channels.generic.websocket import AsyncWebsocketConsumer -from channels.layers import get_channel_layer - -from game.models import Deck -from room.models import PlayerInQueue, Room, PlayerInRoom, GameState -from room.services.room_create import create_room - -channel_layer = get_channel_layer() - - -class QueueConsumer(AsyncWebsocketConsumer): - def __init__(self, *args, **kwargs): - super().__init__(args, kwargs) - self.room_group_name = None - - async def connect(self): - self.room_group_name = "queue" - - await self.accept() - await self.check_origin() - - await self.channel_layer.group_add(self.room_group_name, self.channel_name) - - async def disconnect(self, close_code): - await self.delete_user_in_queue() - await self.channel_layer.group_discard(self.room_group_name, self.channel_name) - - # Receive message from WebSocket - async def receive(self, text_data): - data = None - - try: - data = json.loads(text_data) - except ValueError: - await self.send( - text_data=json.dumps( - {"type": "ERROR", "message": "data is not JSON serializable"} - ) - ) - - if data: - # TODO move to external function/class - if "type" not in data: - await self.send( - text_data=json.dumps( - {"type": "ERROR", "message": "incorrect data typing"} - ) - ) - else: - if data["type"] == "connect": - if "deck_id" not in data: - await self.send( - text_data=json.dumps( - {"type": "ERROR", "message": "deck id is not provided"} - ) - ) - else: - deck = None - # validate deck and check user originality - try: - deck_id = int(data["deck_id"]) - deck = await self.check_user_deck(deck_id) - except ValueError: - await self.send( - text_data=json.dumps( - {"type": "ERROR", "message": "deck id is incorrect"} - ) - ) - if deck: - # add to que, start finding players - await self.queue_connector(deck) - await self.send( - text_data=json.dumps( - { - "type": "INFO", - "message": f"added to queue deck with score {self.scope['score']}", - } - ) - ) - opponent = await self.find_user_by_score() - - if not opponent: - await self.send( - text_data=json.dumps( - { - "type": "INFO", - "message": "no user found, awaiting in queue", - } - ) - ) - else: - # add to group and send message that opponent found to players - room = await create_room( - deck_id_1=self.scope["deck"], - player_id_1=self.scope["player"], - player_score_1=self.scope["score"], - deck_id_2=opponent[2], - player_id_2=opponent[3], - player_score_2=opponent[1], - ) - - await self.channel_layer.send( - opponent[0], - { - "type": "info", - "message": f"user found, with score {self.scope['score']}", - "room": room, - }, - ) - - await self.send( - text_data=json.dumps( - { - "type": "INFO", - "message": f"user found, with score {opponent[1]}", - "room": room, - } - ) - ) - else: - await self.send( - text_data=json.dumps( - { - "type": "ERROR", - "message": "such deck doesn't exist", - } - ) - ) - - @sync_to_async - def delete_user_in_queue(self): - try: - PlayerInQueue.objects.get(player_id=self.scope["player"]).delete() - except PlayerInQueue.DoesNotExist: - return False - - @sync_to_async - def find_user_by_score(self): - s_min = self.scope["score"] * 0.95 - s_max = self.scope["score"] * 1.05 - for el in PlayerInQueue.objects.all(): - if el.player_id != self.scope["player"]: - if s_min <= el.score <= s_max: - return el.channel_name, el.score, el.deck.id, el.player.id - return False - - @sync_to_async - def check_user_deck(self, deck_id: int): - try: - deck = Deck.objects.get(id=deck_id) - if deck.player.id != self.scope["player"]: - return False - return deck - except Deck.DoesNotExist: - return False - - @sync_to_async - def queue_connector(self, deck): - try: - queue = PlayerInQueue.objects.get(player_id=self.scope["player"]) - queue.score = deck.score() - queue.channel_name = self.channel_name - queue.save() - - except PlayerInQueue.DoesNotExist: - queue = PlayerInQueue.objects.create( - player_id=self.scope["player"], - deck=deck, - score=deck.score(), - channel_name=self.channel_name, - ) - - self.scope["queue"] = queue.id - self.scope["deck"] = deck.id - self.scope["score"] = queue.score - - async def info(self, event): - message = event["message"] - msg = {"type": "INFO", "message": message} - if "room" in event: - msg["room"] = event["room"] - - await self.send(text_data=json.dumps(msg)) - - async def check_origin(self): - if not self.scope["player"]: - await self.send( - text_data=json.dumps( - {"type": "ERROR", "message": "token is incorrect or expired"} - ) - ) - await self.close() - - -class RoomConsumer(AsyncWebsocketConsumer): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.room_group_name = None - self.room_name = None - - async def connect(self): - await self.accept() - await self.check_origin() - - if not await self.connect_to_room(): - await self.close() - else: - message, round = await self.get_state() - - await self.send( - json.dumps( - { - "type": "INFO", - "opponent_score": self.scope["opponent_score"], - "opponent_deck": self.scope["opponent_deck"], - "opponent_online": self.scope["opponent_online"], - "first": self.scope["first"], - "state": message, - "round": round, - }, - ) - ) - if "opponent_channel" in self.scope and self.scope["opponent_channel"]: - await self.channel_layer.send( - self.scope["opponent_channel"], - { - "type": "channel", - "channel": self.channel_name, - }, - ) - await self.channel_layer.send( - self.scope["opponent_channel"], - {"type": "connection_info", "online": True}, - ) - - # Join room group - await self.channel_layer.group_add(self.room_group_name, self.channel_name) - - @sync_to_async - def get_state(self): - state = self.scope["player_in_room"].get_state() - return state.message, state.round - - @sync_to_async - def connect_to_room(self): - slug = self.scope["url_route"]["kwargs"]["room_name"] - - self.room_name = slug - self.room_group_name = f"room_{slug}" - room = Room.objects.filter(slug=slug) - - if not room: - return False - - self.scope["room"] = room - - # check if player can be in a room - p_ids = [x.player.id for x in room.first().players.all()] - if self.scope["player"] not in p_ids: - return False - - # add player info to scope - player = PlayerInRoom.objects.get(player_id=self.scope["player"]) - - self.scope["player_in_room"] = player - self.scope["first"] = player.first - self.scope["score"] = player.score - self.scope["deck"] = player.deck.id - - p_ids.remove(player.player.id) - opponent = PlayerInRoom.objects.get(player_id=p_ids[0]) - - self.scope["opponent"] = opponent.player.id - self.scope["opponent_channel"] = opponent.channel_name - self.scope["opponent_score"] = opponent.score - self.scope["opponent_deck"] = opponent.deck.id - self.scope["opponent_first"] = opponent.first - self.scope["opponent_online"] = opponent.online - - player.online = True - player.channel_name = self.channel_name - player.save(update_fields=["online", "channel_name"]) - return True - - async def disconnect(self, close_code): - # Leave room group - await self.channel_layer.group_discard(self.room_group_name, self.channel_name) - await self.disconnect_player() - - if "opponent_channel" in self.scope and self.scope["opponent_channel"]: - await self.channel_layer.send( - self.scope["opponent_channel"], - {"type": "connection_info", "online": False}, - ) - - @sync_to_async - def disconnect_player(self): - if "player_in_room" in self.scope: - self.scope["player_in_room"].online = False - self.scope["player_in_room"].channel_name = None - self.scope["player_in_room"].save(update_fields=["online", "channel_name"]) - - # Receive message from WebSocket - async def receive(self, text_data): - data = None - - try: - data = json.loads(text_data) - except ValueError: - await self.send( - text_data=json.dumps( - {"type": "ERROR", "message": "data is not JSON serializable"} - ) - ) - - if data: - if data["type"] == "start": - if not await self.start(data): - await self.send( - text_data=json.dumps( - {"type": "ERROR", "message": "opponent is offline"} - ) - ) - - async def start(self, data): - if self.scope["opponent_channel"] and self.scope["opponent_online"]: - await self.channel_layer.send( - self.scope["opponent_channel"], - { - "type": "info", - "message": "opponent is ready to start", - }, - ) - return True - return False - - # info type group message handler - async def info(self, event): - message = event["message"] - msg = {"type": "INFO", "message": message} - - if "opponent_score" in event: - msg["opponent_score"] = event["opponent_score"] - - if "opponent_deck" in event: - msg["opponent_deck"] = event["opponent_deck"] - - if "opponent_online" in event: - msg["opponent_online"] = event["opponent_online"] - - if "first" in event: - msg["first"] = event["first"] - - if "state" in event: - msg["state"] = event["state"] - - if "round" in event: - msg["round"] = event["round"] - - await self.send(text_data=json.dumps(msg)) - - # Receive message from room group - async def chat_message(self, event): - message = event["message"] - - # Send message to WebSocket - await self.send(text_data=json.dumps({"lot": message})) - - async def channel(self, event): - channel = event["channel"] - self.scope["opponent_channel"] = channel - - async def connection_info(self, event): - status = event["online"] - await self.send( - text_data=json.dumps( - { - "type": "INFO", - "message": "opponent is online" - if status - else "opponent is offline", - } - ) - ) - self.scope["opponent_online"] = status - - async def check_origin(self): - if not self.scope["player"]: - await self.send( - text_data=json.dumps( - {"type": "ERROR", "message": "token is incorrect or expired"} - ) - ) - await self.close() diff --git a/room/middleware.py b/room/middleware.py deleted file mode 100644 index 4f3d828..0000000 --- a/room/middleware.py +++ /dev/null @@ -1,33 +0,0 @@ -from channels.db import database_sync_to_async -from django.core.exceptions import PermissionDenied - -from game.models import Player -from game.services.jwt import read_jwt - - -@database_sync_to_async -def get_player(headers): - # WARNING headers type is bytes - if b"authorization" not in headers or not headers[b"authorization"]: - return False - - jwt = headers[b"authorization"].decode() - payload = read_jwt(jwt) - - if not payload or "id" not in payload: - return False - - return payload["id"] - - -class HeaderAuthMiddleware: - """Custom middleware to read user auth token from string.""" - - def __init__(self, app): - # Store the ASGI application we were passed - self.app = app - - async def __call__(self, scope, receive, send): - scope["player"] = await get_player(dict(scope["headers"])) - - return await self.app(scope, receive, send) diff --git a/room/migrations/0001_initial.py b/room/migrations/0001_initial.py deleted file mode 100644 index 8f1d476..0000000 --- a/room/migrations/0001_initial.py +++ /dev/null @@ -1,15 +0,0 @@ -# Generated by Django 4.0.5 on 2022-06-23 22:56 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - ] diff --git a/room/migrations/__init__.py b/room/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/room/models.py b/room/models.py deleted file mode 100644 index 047412b..0000000 --- a/room/models.py +++ /dev/null @@ -1,50 +0,0 @@ -from django.db import models - -# Create your models here. -from game.models import Player, Deck - - -class PlayerInQueue(models.Model): - # TODO use redis for storing - player = models.OneToOneField(Player, unique=True, on_delete=models.CASCADE) - channel_name = models.CharField(max_length=50, blank=False) - deck = models.ForeignKey(Deck, on_delete=models.CASCADE) - score = models.IntegerField() - - def __str__(self): - return f"{self.player.name} in que with score {self.score}" - - -class Room(models.Model): - slug = models.SlugField(max_length=16, unique=True) - created = models.DateTimeField(auto_now_add=True) - ended = models.BooleanField(default=False) - - def __str__(self): - return f"room with slug {self.slug}" - - -class PlayerInRoom(models.Model): - player = models.OneToOneField(Player, unique=True, on_delete=models.CASCADE) - room = models.ForeignKey(Room, on_delete=models.CASCADE, related_name="players") - first = models.BooleanField() - score = models.IntegerField(blank=False) - deck = models.ForeignKey(Deck, on_delete=models.CASCADE, related_name="decks") - online = models.BooleanField(default=False) - channel_name = models.CharField(max_length=50, blank=True, null=True) - - def get_state(self): - return GameState.objects.filter(player=self.player, room=self.room).last() - - def __str__(self): - return f"{self.player.name} in room {self.room.slug}" - - -class GameState(models.Model): - room = models.ForeignKey(Room, on_delete=models.CASCADE) - player = models.ForeignKey(Player, on_delete=models.CASCADE) - round = models.IntegerField(blank=False) - message = models.CharField(max_length=100, blank=False) - - class Meta: - unique_together = ["room", "player", "round"] diff --git a/room/routing.py b/room/routing.py deleted file mode 100644 index db78ff6..0000000 --- a/room/routing.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.urls import path - -from . import consumers - -websocket_urlpatterns = [ - path("room/", consumers.QueueConsumer.as_asgi()), - path("room/", consumers.RoomConsumer.as_asgi()), -] diff --git a/room/services/__init__.py b/room/services/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/room/services/room_create.py b/room/services/room_create.py deleted file mode 100644 index 46e4bfa..0000000 --- a/room/services/room_create.py +++ /dev/null @@ -1,45 +0,0 @@ -from asgiref.sync import sync_to_async -from random import randint - -from common.generators import generate_charset -from game.models import Player -from room.models import Room, PlayerInRoom, GameState - - -@sync_to_async -def create_room( - deck_id_1: int, - player_id_1: int, - player_score_1: int, - deck_id_2: int, - player_id_2: int, - player_score_2: int, -) -> str: - room = Room.objects.create(slug=generate_charset(16)) - player_1 = Player.objects.get(id=player_id_1) - player_2 = Player.objects.get(id=player_id_2) - - first_player = randint(1, 2) - - PlayerInRoom.objects.create( - player=player_1, - room=room, - score=player_score_1, - deck_id=deck_id_1, - first=first_player == 1, - ) - - PlayerInRoom.objects.create( - player=player_2, - room=room, - score=player_score_2, - deck_id=deck_id_2, - first=first_player == 2, - ) - GameState.objects.create( - room=room, player=player_1, round=0, message="Game started" - ) - GameState.objects.create( - room=room, player=player_2, round=0, message="Game started" - ) - return room.slug diff --git a/room/tests.py b/room/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/room/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/room/views.py b/room/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/room/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here.