diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 33a4a7e..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,240 +0,0 @@ -services: - setup: - image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} - container_name: setup - user: "0" - volumes: - - certs:/usr/share/elasticsearch/config/certs - command: > - bash -c ' - if [ x${ELASTIC_PASSWORD} == x ]; then - echo "Set the ELASTIC_PASSWORD environment variable in the .env file"; - exit 1; - elif [ x${KIBANA_PASSWORD} == x ]; then - echo "Set the KIBANA_PASSWORD environment variable in the .env file"; - exit 1; - fi; - if [ ! -f config/certs/ca.zip ]; then - echo "Creating CA"; - bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip; - unzip config/certs/ca.zip -d config/certs; - fi; - if [ ! -f config/certs/certs.zip ]; then - echo "Creating certs"; - echo -ne \ - "instances:\n"\ - " - name: es01\n"\ - " dns:\n"\ - " - es01\n"\ - " - localhost\n"\ - " ip:\n"\ - " - 127.0.0.1\n"\ - " - name: kibana\n"\ - " dns:\n"\ - " - kibana\n"\ - " - localhost\n"\ - " ip:\n"\ - " - 127.0.0.1\n"\ - > config/certs/instances.yml; - - bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key; - unzip config/certs/certs.zip -d config/certs; - fi; - - echo "Setting file permissions" - chown -R root:root config/certs; - find . -type d -exec chmod 750 \{\} \;; - find . -type f -exec chmod 640 \{\} \;; - - echo "Waiting for Elasticsearch availability"; - until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done; - echo "Setting kibana_system password"; - until curl -s -X POST --cacert config/certs/ca/ca.crt -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done; - echo "All done!"; - ' - healthcheck: - test: ["CMD-SHELL", "[ -f config/certs/es01/es01.crt ]"] - interval: 1s - timeout: 5s - retries: 120 - - backend: - build: - context: . - dockerfile: Dockerfile - image: backend - container_name: backend - restart: always - command: /bin/sh -c "sleep 5 && - venv/bin/python manage.py makemigrations --noinput && - venv/bin/python manage.py migrate --noinput && - venv/bin/python manage.py collectstatic --noinput && - venv/bin/daphne -b 0.0.0.0 -p 8080 pong.asgi:application" - volumes: - - pong:/transcendence/pong - - pong_django_logs:/transcendence/logs - ports: - - 8080:8080 - networks: - - app-network - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${POSTGRES_DB} - DB_USER: ${POSTGRES_USER} - DB_PASSWORD: ${POSTGRES_PASSWORD} - depends_on: - - db - healthcheck: - test: ["CMD-SHELL", "curl", "http://localhost:8080"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 10s - - db: - image: postgres:latest - container_name: postgres - restart: always - volumes: - - pong_pg_data:/var/lib/postgresql/data - ports: - - "5432:5432" - networks: - - app-network - environment: - POSTGRES_DB: ${POSTGRES_DB} - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] - interval: 10s - timeout: 5s - retries: 5 - - es01: - image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} - container_name: es01 - depends_on: - setup: - condition: service_healthy - volumes: - - certs:/usr/share/elasticsearch/config/certs:ro - - pong_es_data_01:/usr/share/elasticsearch/data - labels: - co.elastic.logs/module: elasticsearch - ports: - - 9200:9200 - environment: - - node.name=es01 - - cluster.name=${CLUSTER_NAME} - - discovery.type=single-node - - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} - - bootstrap.memory_lock=true - - xpack.security.enabled=true - - xpack.security.http.ssl.enabled=true - - xpack.security.http.ssl.key=certs/es01/es01.key - - xpack.security.http.ssl.certificate=certs/es01/es01.crt - - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt - - xpack.security.transport.ssl.enabled=true - - xpack.security.transport.ssl.key=certs/es01/es01.key - - xpack.security.transport.ssl.certificate=certs/es01/es01.crt - - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt - - xpack.security.transport.ssl.verification_mode=certificate - - xpack.license.self_generated.type=${LICENSE} - healthcheck: - test: - [ - "CMD-SHELL", - "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'", - ] - interval: 10s - timeout: 10s - retries: 120 - - kibana: - image: docker.elastic.co/kibana/kibana:${STACK_VERSION} - container_name: kibana - labels: - co.elastic.logs/module: kibana - depends_on: - es01: - condition: service_healthy - volumes: - - certs:/usr/share/kibana/config/certs:ro - - pong_kibana:/usr/share/kibana/data - ports: - - 5601:5601 - environment: - - SERVERNAME=kibana - - ELASTICSEARCH_HOSTS=https://es01:9200 - - ELASTICSEARCH_USERNAME=${KIBANA_USERNAME} - - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD} - - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt - - XPACK_SECURITY_ENCRYPTIONKEY=${ENCRYPTION_KEY} - - XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=${ENCRYPTION_KEY} - - XPACK_REPORTING_ENCRYPTIONKEY=${ENCRYPTION_KEY} - healthcheck: - test: - [ - "CMD-SHELL", - "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'" - ] - interval: 10s - timeout: 10s - retries: 120 - - logstash01: - image: docker.elastic.co/logstash/logstash:${STACK_VERSION} - container_name: logstash01 - labels: - co.elastic.logs/module: logstash - user: root - depends_on: - es01: - condition: service_healthy - kibana: - condition: service_healthy - volumes: - - certs:/usr/share/logstash/certs - - pong_logstash_data01:/usr/share/logstash/data - - ./config/logstash.conf:/usr/share/logstash/pipeline/logstash.conf:ro - - pong_django_logs:/usr/share/logstash/logs - ports: - - "5044:5044/udp" - command: logstash -f /usr/share/logstash/pipeline/logstash.conf - environment: - - NODE_NAME="logstash" - - ELASTIC_HOSTS=https://es01:9200 - - ELASTIC_USER=${ELASTIC_USERNAME} - - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} - - xpack.monitoring.enabled=false - -volumes: - pong: - driver: local - driver_opts: - type: none - device: ${PROJECT_PATH} - o: bind - pong_django_logs: - driver: local - driver_opts: - type: none - device: ${DJANGO_LOGS} - o: bind - pong_pg_data: - driver: local - pong_es_data_01: - driver: local - pong_kibana: - driver: local - pong_logstash_data01: - driver: local - certs: - driver: local - -networks: - app-network: - name: app-network - driver: bridge diff --git a/makefile b/makefile index f1b37a8..45a9a55 100644 --- a/makefile +++ b/makefile @@ -4,7 +4,7 @@ CONTAINER=$(c) up: down $(COMPOSE) build - $(COMPOSE) up $(CONTAINER) + $(COMPOSE) up --remove-orphans $(CONTAINER) build: $(COMPOSE) build $(CONTAINER) diff --git a/pong/game/consumers.py b/pong/game/consumers.py index a71eff0..8412496 100644 --- a/pong/game/consumers.py +++ b/pong/game/consumers.py @@ -6,6 +6,7 @@ from django.contrib.auth.models import User from channels.db import database_sync_to_async from .matchmaking import match_maker from .tournament import tournament_match_maker +import asyncio class GameConsumer(AsyncWebsocketConsumer): async def connect(self): @@ -24,11 +25,10 @@ class GameConsumer(AsyncWebsocketConsumer): elif data['type'] == 'key_press': if self.game: await self.game.handle_key_press(self, data['key']) - else: - await match_maker.handle_key_press(self, data['key']) elif data['type'] == 'start_tournament': - print("Start TOURNAMENT received..") - await tournament_match_maker.start_tournament() + print(f"Start TOURNAMENT received by {self.user}") + # Run the tournament in the background + asyncio.create_task(tournament_match_maker.start_tournament()) async def authenticate(self, token): user = await self.get_user_from_token(token) @@ -102,4 +102,5 @@ class GameConsumer(AsyncWebsocketConsumer): print(f"User {self.user.username if hasattr(self, 'user') else 'Unknown'} disconnected") async def set_game(self, game): + print(f"({self.user}) Game set to: {game}") self.game = game diff --git a/pong/game/game.py b/pong/game/game.py index e619da5..56fe7ed 100644 --- a/pong/game/game.py +++ b/pong/game/game.py @@ -4,8 +4,9 @@ import json import asyncio import random from datetime import datetime -from .utils import handle_game_data +from .utils import handle_game_data, getlen from asgiref.sync import sync_to_async +from .models import Tournoi class Game: def __init__(self, game_id, player1, player2, localgame): @@ -27,9 +28,10 @@ class Game: 'game_text': '' } else: - self.botgame = player2 is None + # Set botgame to True if either player1 or player2 is None + self.botgame = player1 is None or player2 is None self.game_state = { - 'player1_name': player1.user.username, + 'player1_name': player1.user.username if player1 else 'BOT', 'player2_name': player2.user.username if player2 else 'BOT', 'player1_position': 150, 'player2_position': 150, @@ -49,7 +51,7 @@ class Game: self.start_time = datetime.now() async def start_game(self): - print(f"- Game #{self.game_id} STARTED") + print(f"- Game #{self.game_id} STARTED ({self.game_state['player1_name']} vs {self.game_state['player2_name']}) --- ({self})") self.game_loop_task = asyncio.create_task(self.game_loop()) print(f" Begin MATCH at: {self.start_time}") @@ -77,8 +79,10 @@ class Game: if player2_position < target_y < player2_position + 80: pass #bot already placed elif player2_position < target_y: + #self.p2_mov = 1 self.game_state['player2_position'] = min(player2_position + (50 * self.speed), 300) elif player2_position + 80 > target_y: + #self.p2_mov = -1 self.game_state['player2_position'] = max(player2_position - (50 * self.speed), 0) def predict_ball_trajectory(self, steps=60): @@ -90,10 +94,16 @@ class Game: for _ in range(steps): future_x += velocity_x - future_y += velocity_y + if future_x <= 10: + future_x = 10 + velocity_x = -velocity_x + elif future_x >= 790: + future_x = 790 + else: + future_y += velocity_y # Dealing with bounces off walls - if future_y <= 0 or future_y >= 300: + if future_y <= 10 or future_y >= 390: velocity_y = -velocity_y # Reverse the direction of vertical movement return {'x': future_x, 'y': future_y} @@ -208,7 +218,7 @@ class Game: self.ended = True if self.game_loop_task: self.game_loop_task.cancel() - print(f"- Game #{self.game_id} ENDED") + print(f"- Game #{self.game_id} ENDED --- ({self})") end_time = datetime.now() duration = (end_time - self.start_time).total_seconds() / 60 @@ -233,6 +243,14 @@ class Game: if not self.botgame: if not self.localgame: await self.player2.send(end_message) - await sync_to_async(handle_game_data)(self.game_state['player1_name'], self.game_state['player2_name'], + if hasattr(self, 'tournament'): + len_tournament = await sync_to_async(getlen)() + name_tournament = self.tournament.name + " #" + str(len_tournament + 1) + print(f"- Saving match game #{self.game_id} of tournament: {name_tournament}") + await sync_to_async(handle_game_data)(self.game_state['player1_name'], self.game_state['player2_name'], + self.game_state['player1_score'], self.game_state['player2_score'], + self.bt1, self.bt2, duration, True, name_tournament) + else: + await sync_to_async(handle_game_data)(self.game_state['player1_name'], self.game_state['player2_name'], self.game_state['player1_score'], self.game_state['player2_score'], self.bt1, self.bt2, duration, False, None) diff --git a/pong/game/matchmaking.py b/pong/game/matchmaking.py index 989a1dd..6382b95 100644 --- a/pong/game/matchmaking.py +++ b/pong/game/matchmaking.py @@ -26,7 +26,8 @@ class MatchMaker: for game in self.active_games.values(): if player in [game.player1, game.player2]: await game.end_game(disconnected_player=player) - del self.active_games[game.game_id] + if game.game_id in self.active_games: + del self.active_games[game.game_id] break async def match_loop(self): @@ -102,11 +103,5 @@ class MatchMaker: 'player2': 'BOT' })) - async def handle_key_press(self, player, key): - for game in self.active_games.values(): - if player in [game.player1, game.player2]: - await game.handle_key_press(player, key) - break - # Instance of the class match_maker = MatchMaker() diff --git a/pong/game/templates/pong/tournament_brackets.html b/pong/game/templates/pong/tournament_brackets.html index 85719a3..ca65553 100644 --- a/pong/game/templates/pong/tournament_brackets.html +++ b/pong/game/templates/pong/tournament_brackets.html @@ -7,78 +7,53 @@
{% for round in tournament_rounds %} -
-

Round {{ forloop.counter }}

- {% for match in round %} -
-
- {{ match.player1 }} - {% if match.score1 is not None %}{{ match.score1 }}{% endif %} -
-
- {{ match.player2|default:"BYE" }} - {% if match.score2 is not None %}{{ match.score2 }}{% endif %} -
-
- {% if not forloop.last %}
{% endif %} - {% endfor %} +
+ {% for match in round %} +
+
+ {{ match.player1 }} +
+
+ {{ match.player2|default:"BYE" }} +
+ {% endfor %} +
{% endfor %}
diff --git a/pong/game/tournament.py b/pong/game/tournament.py index 1f039fb..c2b944c 100644 --- a/pong/game/tournament.py +++ b/pong/game/tournament.py @@ -7,16 +7,18 @@ import random from .matchmaking import match_maker from .game import Game +TOURNAMENT_NAMES = [ + "Champion's Clash", "Ultimate Showdown", "Battle Royale", + "Victory's Cup", "Legends Tournament", "Elite Series", "Clash of 42", + "Shibuya Incident", "Cunning Game", "Elite of the Stars", "Chaku's Disciples" +] + class TournamentMatch(Game): def __init__(self, game_id, player1, player2, tournament): # Initialize the parent Game class with the provided parameters super().__init__(game_id, player1, player2, False) # Store the current game instance in active games match_maker.active_games[game_id] = self - # Set the game for the players - player1.set_game(self) - if player2: - player2.set_game(self) # Store the tournament instance self.tournament = tournament @@ -25,7 +27,8 @@ class TournamentMatch(Game): await super().end_game(disconnected_player) # Handle the end of the match in the tournament context await self.tournament.handle_match_end(self) - + if self.game_id in match_maker.active_games: + del match_maker.active_games[self.game_id] class TournamentMatchMaker: def __init__(self): @@ -33,17 +36,17 @@ class TournamentMatchMaker: self.matches = [] self.rounds = [] self.current_round = 0 - self.tournament_state = "waiting" # Can be "waiting", "in_progress", or "ended" + self.games = 0 + self.tournament_state = "waiting" #Can be "waiting", "in_progress", or "ended" + self.name = random.choice(TOURNAMENT_NAMES) async def add_player(self, player): if self.tournament_state == "waiting" and player not in self.waiting_players: self.waiting_players.append(player) - print(f"User {player.user.username} joins the TOURNAMENT WAITING ROOM") - await self.update_waiting_room() - - async def remove_player(self, player): - if player in self.waiting_players: - self.waiting_players.remove(player) + if player: + print(f"User {player.user.username} joins the TOURNAMENT WAITING ROOM") + else: + print("BOT joins the TOURNAMENT WAITING ROOM") await self.update_waiting_room() async def update_waiting_room(self): @@ -56,32 +59,74 @@ class TournamentMatchMaker: def generate_waiting_room_html(self): context = { - 'players': [player.user.username for player in self.waiting_players], + 'players': [player.user.username if player else 'BYE' for player in self.waiting_players], 'tournament_state': self.tournament_state, 'players_count': len(self.waiting_players), - 'min_players_to_start': 2 # You can adjust this number as needed + 'min_players_to_start': 3 # You can adjust this number as needed } return render_to_string('pong/tournament_waiting_room.html', context) + async def send_to_player(self, player, data): + if player: + await player.send(json.dumps(data)) + + async def remove_player(self, player): + if player in self.waiting_players: + self.waiting_players.remove(player) + await self.update_waiting_room() + + # Tournament start method async def start_tournament(self): + if len(self.waiting_players) < 2: return False - + if len(self.waiting_players) % 2 == 0: + await self.add_player(None) self.tournament_state = "in_progress" random.shuffle(self.waiting_players) - self.current_round = 1 - self.create_matches(self.waiting_players) - await self.update_brackets() - await self.start_round_matches() + self.current_round = 0 + await self.advance_tournament() return True - def create_matches(self, players): + async def advance_tournament(self): + players = self.waiting_players + while len(players) > 1: + self.current_round += 1 + print(f"Starting round {self.current_round} with {len(players)} players") + await self.create_matches(players) + await self.update_brackets() + await self.start_round_matches() + # Wait for all matches in the current round to finish + current_round_matches = self.rounds[-1] + while not all(match.ended for match in current_round_matches): + await asyncio.sleep(1) # Wait for 1 second before checking again + # Get winners for the next round + players = self.get_round_winners() + print(f"Round {self.current_round} finished. {len(players)} players advancing.") + # Tournament has ended + await self.update_brackets() + await self.end_tournament(players[0] if players else None) + + async def create_matches(self, players): matches = [] for i in range(0, len(players), 2): + self.games += 1 if i + 1 < len(players): - matches.append(TournamentMatch(len(self.matches) + 1, players[i], players[i + 1], self)) + # Create a new instance of TournamentMatch for this round + match = TournamentMatch(self.games, players[i], players[i + 1], self) + matches.append(match) else: - matches.append(TournamentMatch(len(self.matches) + 1, players[i], None, self)) # Bye + # Create a BYE match where the second player is None + match = TournamentMatch(self.games, players[i], None, self) # BYE match + matches.append(match) + + # Assign the new match instance to the players + if players[i]: + await players[i].set_game(match) + if i + 1 < len(players): + if players[i + 1]: + await players[i + 1].set_game(match) + self.rounds.append(matches) self.matches.extend(matches) @@ -115,45 +160,46 @@ class TournamentMatchMaker: ] async def start_round_matches(self): + print(f"Starting TOURNAMENT round #{self.current_round}") for match in self.rounds[-1]: if match.player1 and match.player2: await match_maker.notify_players(match.player1, match.player2, match.game_id, False) asyncio.create_task(match.start_game()) elif match.player1: - # Handle bye + # Handle BYE match + await match_maker.notify_players(match.player1, match.player2, match.game_id, False) + #asyncio.create_task(match.start_game()) match.game_state['player1_score'] = 3 match.game_state['player2_score'] = 0 await match.end_game() - async def handle_match_end(self, match): - await self.update_brackets() - if all(m.ended for m in self.rounds[-1]): - await self.advance_tournament() - - async def advance_tournament(self): - winners = [match.player1 if match.game_state['player1_score'] > match.game_state['player2_score'] else match.player2 for match in self.rounds[-1] if match.player1 and match.player2] - if len(winners) > 1: - self.current_round += 1 - self.create_matches(winners) - await self.update_brackets() - await self.start_round_matches() - else: - await self.end_tournament(winners[0]) + def get_round_winners(self): + winners = [] + for match in self.rounds[-1]: + if match.ended: + winner = match.player1 if match.game_state['player1_score'] > match.game_state['player2_score'] else match.player2 + #if winner: + winners.append(winner) + return winners async def end_tournament(self, winner): self.tournament_state = "ended" + winner_username = winner.user.username if winner else "No winner" + print(f"The TOURNAMENT winner is {winner_username}") for player in self.waiting_players: await self.send_to_player(player, { 'type': 'tournament_end', - 'winner': winner.user.username + 'winner': winner_username }) + # Reset tournament state self.waiting_players = [] self.matches = [] self.rounds = [] self.current_round = 0 + self.games = 0 - async def send_to_player(self, player, data): - await player.send(json.dumps(data)) + async def handle_match_end(self, match): + await self.update_brackets() # Instance of the class tournament_match_maker = TournamentMatchMaker() \ No newline at end of file diff --git a/pong/game/utils.py b/pong/game/utils.py index d30ae3f..8e9c2db 100644 --- a/pong/game/utils.py +++ b/pong/game/utils.py @@ -172,3 +172,5 @@ def get_player_p_win(player_name): player = get_object_or_404(Player, name=player_name) return player.p_win +def getlen(): + return Tournoi.objects.count() diff --git a/pong/settings.py b/pong/settings.py deleted file mode 100644 index b44ccdf..0000000 --- a/pong/settings.py +++ /dev/null @@ -1,172 +0,0 @@ -# /pong/settings.py - -""" -Django settings for pong project. - -Generated by 'django-admin startproject' using Django 3.2. -""" - -import os -import logging.config -from pathlib import Path - -# Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '12345678' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = ['*'] - - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'channels', - 'pong.game', - 'rest_framework' -] - -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -] - -ROOT_URLCONF = 'pong.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'pong', 'static')], # Ensure templates are found - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -ASGI_APPLICATION = 'pong.asgi.application' - -# Database -# https://docs.djangoproject.com/en/3.2/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': os.getenv('DB_NAME'), - 'USER': os.getenv('DB_USER'), - 'PASSWORD': os.getenv('DB_PASSWORD'), - 'HOST': os.getenv('DB_HOST'), - 'PORT': '5432', - } -} - -# Password validation -# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - -# Internationalization -# https://docs.djangoproject.com/en/3.2/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/3.2/howto/static-files/ - -STATIC_URL = '/static/' -STATICFILES_DIRS = [os.path.join(BASE_DIR, 'pong/static')] -STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') - -# Default primary key field type -# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field - -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' - -# Channels -# Define the channel layers for WebSockets -CHANNEL_LAYERS = { - 'default': { - 'BACKEND': 'channels.layers.InMemoryChannelLayer', - }, -} - -LOGGING = { - 'version': 1, # The version of the logging configuration schema - 'disable_existing_loggers': False, # Allows existing loggers to keep logging - 'formatters': { # Defines how log messages will be formatted - 'json': { - '()': 'pythonjsonlogger.jsonlogger.JsonFormatter', - # Formatter that outputs logs in JSON format, which is ideal for ingestion by Logstash. - }, - 'default': { - 'format': '[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s', - # This is a basic text formatter with timestamp, log level, logger name, line number, and the actual message. - }, - }, - 'handlers': { # Handlers determine where the log messages are sent - 'file': { - 'level': 'INFO', # Minimum log level to be handled (INFO and above) - 'class': 'logging.FileHandler', - 'filename': os.path.join(BASE_DIR, 'logs/django.log'), # The file where logs will be saved - 'formatter': 'json', # Uses the JSON formatter defined above - }, - 'console': { - 'level': 'DEBUG', # Minimum log level to be handled (DEBUG and above) - 'class': 'logging.StreamHandler', - 'formatter': 'default', # Uses the default text formatter - }, - }, - 'loggers': { # Loggers are the actual log streams that get configured - 'django': { # The Django logger catches all messages sent by the Django framework - 'handlers': ['file', 'console'], # Sends logs to both the file and the console - 'level': 'DEBUG', # Minimum log level to be logged - 'propagate': True, # If True, messages will be passed to the parent loggers as well - }, - }, -} diff --git a/pong/static/game.js b/pong/static/game.js index 1d79dcf..1b5a53c 100644 --- a/pong/static/game.js +++ b/pong/static/game.js @@ -64,7 +64,6 @@ document.addEventListener('DOMContentLoaded', () => { quickMatchButton.addEventListener('click', startQuickMatch); tournamentButton.addEventListener('click', startTournament); - async function handleCheckNickname() { const nickname = nicknameInput.value.trim(); if (nickname) { @@ -185,7 +184,6 @@ document.addEventListener('DOMContentLoaded', () => { return data.authenticated; } - async function handleCheckNickname2() { const nickname2 = nicknameInput2.value.trim(); if (nickname2) { @@ -388,32 +386,31 @@ document.addEventListener('DOMContentLoaded', () => { } else if (data.type === 'game_state_update') { updateGameState(data.game_state); } else if (data.type === 'player_disconnected') { - console.log("Player disconnected:", data.player); + console.log('Player disconnected:', data.player); } else if (data.type === 'game_ended') { - console.log("Game ended:", data.game_id); + console.log('Game ended:', data.game_id); } else if (data.type === 'error') { console.error(data.message); - // Assuming you're inside some WebSocket message handling function } else if (data.type === 'update_tournament_waiting_room') { // Update the HTML content of the tournament bracket document.getElementById('tournament-bracket').innerHTML = data.html; - // Reattach the event listener to the "Start Tournament" button const startButton = document.getElementById('start-tournament-btn'); if (startButton) { startButton.addEventListener('click', function() { if (typeof socket !== 'undefined' && socket.readyState === WebSocket.OPEN) { - console.log("Start TOURNAMENT sent.."); + console.log('Start TOURNAMENT sent..'); socket.send(JSON.stringify({type: 'start_tournament'})); } else { - console.error("WebSocket is not open or undefined"); + console.error('WebSocket is not open or undefined'); } }); - } else { - console.error('Start button not found'); } } else if (data.type === 'update_brackets') { + // Update the HTML content of the tournament bracket document.getElementById('tournament-bracket').innerHTML = data.html; + } else if (data.type === 'tournament_end') { + console.log('Tournament ended, the winner is:', data.winner); } else { console.log('Message from server:', data.type, data.message); } @@ -443,7 +440,7 @@ document.addEventListener('DOMContentLoaded', () => { function updateGameState(newState) { gameState = newState; renderGame(); - checkForWinner(); + //checkForWinner(); } function renderGame() { diff --git a/pong/static/index.html b/pong/static/index.html index 0cf0b7f..aa89490 100644 --- a/pong/static/index.html +++ b/pong/static/index.html @@ -110,8 +110,8 @@