diff --git a/requirements.dev.txt b/requirements.dev.txt index 7911e5b..cf6269b 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -2,6 +2,8 @@ asgiref==3.11.0 attrs==25.4.0 certifi==2025.11.12 cffi==2.0.0 +channels +channels-redis charset-normalizer==3.4.4 coverage cssselect==1.3.0 @@ -23,6 +25,7 @@ pycparser==2.23 PySocks==1.7.1 python-dotenv requests==2.32.5 +scipy selenium==4.39.0 sniffio==1.3.1 sortedcontainers==2.4.0 @@ -34,6 +37,7 @@ types-PyYAML==6.0.12.20250915 typing_extensions==4.15.0 tzdata==2025.3 urllib3==2.6.2 +uvicorn[standard] websocket-client==1.9.0 whitenoise==6.11.0 wsproto==1.3.2 diff --git a/requirements.txt b/requirements.txt index c975982..423d5da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ celery +channels +channels-redis cssselect==1.3.0 Django==6.0 dj-database-url @@ -13,5 +15,7 @@ lxml==6.0.2 psycopg2-binary redis requests==2.31.0 +scipy stripe whitenoise==6.11.0 +uvicorn[standard] diff --git a/src/apps/epic/__init__.py b/src/apps/epic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/epic/admin.py b/src/apps/epic/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/src/apps/epic/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/src/apps/epic/apps.py b/src/apps/epic/apps.py new file mode 100644 index 0000000..b8ecac6 --- /dev/null +++ b/src/apps/epic/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class EpicConfig(AppConfig): + name = 'apps.epic' diff --git a/src/apps/epic/consumers.py b/src/apps/epic/consumers.py new file mode 100644 index 0000000..60defaf --- /dev/null +++ b/src/apps/epic/consumers.py @@ -0,0 +1 @@ +# RoomConsumer goes here \ No newline at end of file diff --git a/src/apps/epic/forms.py b/src/apps/epic/forms.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/epic/migrations/__init__.py b/src/apps/epic/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/epic/models.py b/src/apps/epic/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/src/apps/epic/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/src/apps/epic/routing.py b/src/apps/epic/routing.py new file mode 100644 index 0000000..b3600a3 --- /dev/null +++ b/src/apps/epic/routing.py @@ -0,0 +1 @@ +websocket_urlpatterns = [] diff --git a/src/apps/epic/tests/__init__.py b/src/apps/epic/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/epic/tests/integrated/__init__.py b/src/apps/epic/tests/integrated/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/epic/tests/unit/__init__.py b/src/apps/epic/tests/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/epic/urls.py b/src/apps/epic/urls.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/epic/views.py b/src/apps/epic/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/src/apps/epic/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/src/apps/epic/voronoi.py b/src/apps/epic/voronoi.py new file mode 100644 index 0000000..582a356 --- /dev/null +++ b/src/apps/epic/voronoi.py @@ -0,0 +1 @@ +# TODO: toroidal topology (tile seeds across boundary before computing) diff --git a/src/core/asgi.py b/src/core/asgi.py index cf099bf..7efe506 100644 --- a/src/core/asgi.py +++ b/src/core/asgi.py @@ -1,16 +1,19 @@ -""" -ASGI config for core project. - -It exposes the ASGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/ -""" - import os from django.core.asgi import get_asgi_application +from channels.routing import ProtocolTypeRouter, URLRouter +from channels.auth import AuthMiddlewareStack + +import apps.epic.routing + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') -application = get_asgi_application() +application = ProtocolTypeRouter({ + 'http': get_asgi_application(), + 'websocket': AuthMiddlewareStack( + URLRouter( + apps.epic.routing.websocket_urlpatterns + ) + ), +}) diff --git a/src/core/settings.py b/src/core/settings.py index 09c2e46..7c772b7 100644 --- a/src/core/settings.py +++ b/src/core/settings.py @@ -59,11 +59,13 @@ INSTALLED_APPS = [ 'apps.gameboard', # Gamer apps 'apps.lyric', + 'apps.epic', # Custom apps 'apps.api', 'apps.applets', 'functional_tests', # Depend apps + 'channels', 'compressor', 'rest_framework', ] @@ -119,7 +121,7 @@ else: } } -# Celery & Redis +# Celery & Redis & Channels (oh my) CELERY_BROKER_URL = os.environ.get('CELERY_BROKER_URL', 'redis://localhost:6379/0') REDIS_URL = os.environ.get('REDIS_URL') if REDIS_URL: @@ -130,6 +132,14 @@ if REDIS_URL: } } SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' +CHANNEL_LAYERS = { + 'default': { + 'BACKEND': 'channels_redis.core.RedisChannelLayer', + 'CONFIG': { + 'hosts': [os.environ.get('REDIS_URL', 'redis://localhost:6379/1')], + }, + } +} # Password validation # https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators