diff --git a/chess_backend/settings.py b/chess_backend/settings.py index f9604f8..a05c22c 100644 --- a/chess_backend/settings.py +++ b/chess_backend/settings.py @@ -24,6 +24,11 @@ INSTALLED_APPS = [ "room", ] +if DEBUG: + INSTALLED_APPS.append("django.contrib.staticfiles") + INSTALLED_APPS.append("drf_yasg") + + MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", diff --git a/chess_backend/urls.py b/chess_backend/urls.py index b810c4f..956f93a 100644 --- a/chess_backend/urls.py +++ b/chess_backend/urls.py @@ -1,9 +1,46 @@ from django.conf import settings from django.conf.urls.static import static -from django.urls import path, include +from django.template.defaulttags import url +from django.urls import path, include, re_path -urlpatterns = ( - [path("api/", include("game.urls"))] - + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) - + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) +# 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/common/generators.py b/common/generators.py index cd01384..154bf66 100644 --- a/common/generators.py +++ b/common/generators.py @@ -11,4 +11,4 @@ def generate_charset(length: int): def gen_ton(): - return "".join([str(randint(0, 9)) for _ in range(48)]) + return int("".join([str(randint(0, 9)) for _ in range(48)])) diff --git a/game/models.py b/game/models.py index c640ae3..7976b25 100644 --- a/game/models.py +++ b/game/models.py @@ -1,7 +1,6 @@ import random import uuid -from django.core.files import File from django.core.validators import ( MinValueValidator, MaxValueValidator, @@ -9,18 +8,16 @@ from django.core.validators import ( MaxLengthValidator, ) from django.db import models -from django.conf import settings from common.generators import generate_charset from game.services.jwt import sign_jwt -HER0_TYPES = [ - ("WIZARD", "wizard"), - ("ARCHER", "archer"), - ("WARRIOR", "warrior"), - ("KING", "king"), -] -HER0_IMAGE_TYPES = [("DIE", "die"), ("IDLE", "idle"), ("ATTACK", "attack")] + +class HeroTypes(models.TextChoices): + wizard = "WIZARD", "wizard" + archer = "ARCHER", "archer" + warrior = "WARRIOR", "warrior" + king = "KING", "king" class Player(models.Model): @@ -47,7 +44,7 @@ class Player(models.Model): + ["ARCHER" for _ in range(4)] + ["WARRIOR" for _ in range(6)] + ["WIZARD" for _ in range(2)] - + [random.choice(HER0_TYPES[:3])[0] for _ in range(3)] + + [random.choice(HeroTypes.choices[:3])[0] for _ in range(3)] ) for t in types: hero = Hero() @@ -99,16 +96,8 @@ class Hero(models.Model): ) added = models.DateTimeField(auto_now_add=True) - type = models.CharField(blank=False, choices=HER0_TYPES, max_length=7) - idle_img_f = models.ForeignKey( - "HeroImageSet", on_delete=models.CASCADE, related_name="idle_image_fkey" - ) - attack_img_f = models.ForeignKey( - "HeroImageSet", on_delete=models.CASCADE, related_name="attack_image_fkey" - ) - die_img_f = models.ForeignKey( - "HeroImageSet", on_delete=models.CASCADE, related_name="die_image_fkey" - ) + 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 ) @@ -135,13 +124,13 @@ class Hero(models.Model): self, force_insert=False, force_update=False, using=None, update_fields=None ): self.idle_img_f = random.choice( - [x for x in HeroImageSet.objects.filter(hero_type=self.type, type="IDLE")] + [x for x in HeroModelSet.objects.filter(hero_type=self.type)] ) self.attack_img_f = random.choice( - [x for x in HeroImageSet.objects.filter(hero_type=self.type, type="ATTACK")] + [x for x in HeroModelSet.objects.filter(hero_type=self.type)] ) self.die_img_f = random.choice( - [x for x in HeroImageSet.objects.filter(hero_type=self.type, type="DIE")] + [x for x in HeroModelSet.objects.filter(hero_type=self.type)] ) super(Hero, self).save() @@ -154,13 +143,12 @@ class Hero(models.Model): verbose_name_plural = "heroes" -class HeroImageSet(models.Model): - type = models.CharField(max_length=10, choices=HER0_IMAGE_TYPES) - hero_type = models.CharField(blank=False, choices=HER0_TYPES, max_length=7) - image = models.ImageField(upload_to="uploads/") +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} {self.type} image" + return f"{self.hero_type} model file" class Deck(models.Model): diff --git a/room/consumers.py b/room/consumers.py index 0e38cee..04c8fab 100644 --- a/room/consumers.py +++ b/room/consumers.py @@ -5,7 +5,7 @@ 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 +from room.models import PlayerInQueue, Room, PlayerInRoom, GameState from room.services.room_create import create_room channel_layer = get_channel_layer() @@ -208,15 +208,18 @@ class RoomConsumer(AsyncWebsocketConsumer): if not await self.connect_to_room(): await self.close() else: + message, round = await self.get_state() + await self.send( json.dumps( { "type": "INFO", - "message": f"welcome to room {self.room_name}", "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, }, ) ) @@ -236,6 +239,11 @@ class RoomConsumer(AsyncWebsocketConsumer): # 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"] @@ -346,6 +354,12 @@ class RoomConsumer(AsyncWebsocketConsumer): 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 diff --git a/room/migrations/0001_initial.py b/room/migrations/0001_initial.py index 12c53ff..8f1d476 100644 --- a/room/migrations/0001_initial.py +++ b/room/migrations/0001_initial.py @@ -9,38 +9,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('game', '0002_deck_heroimageset_heroindeck_playerauthsession_and_more'), ] operations = [ - migrations.CreateModel( - name='Room', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('slug', models.SlugField(max_length=16, unique=True)), - ('created', models.DateTimeField(auto_now_add=True)), - ('ended', models.BooleanField(default=False)), - ], - ), - migrations.CreateModel( - name='PlayerInRoom', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('first', models.BooleanField()), - ('score', models.IntegerField()), - ('deck', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='decks', to='game.deck')), - ('player', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='game.player')), - ('room', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='players', to='room.room')), - ], - ), - migrations.CreateModel( - name='PlayerInQueue', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('channel_name', models.CharField(max_length=50)), - ('score', models.IntegerField()), - ('deck', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='game.deck')), - ('player', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='game.player')), - ], - ), ] diff --git a/room/models.py b/room/models.py index 236a165..047412b 100644 --- a/room/models.py +++ b/room/models.py @@ -33,5 +33,18 @@ class PlayerInRoom(models.Model): 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/services/room_create.py b/room/services/room_create.py index 0993808..46e4bfa 100644 --- a/room/services/room_create.py +++ b/room/services/room_create.py @@ -3,7 +3,7 @@ from random import randint from common.generators import generate_charset from game.models import Player -from room.models import Room, PlayerInRoom +from room.models import Room, PlayerInRoom, GameState @sync_to_async @@ -36,4 +36,10 @@ def create_room( 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