From 878cf92bbb5391821e3603219b1656c37aae972f Mon Sep 17 00:00:00 2001 From: CHIBOUB Chakib Date: Mon, 2 Sep 2024 23:24:42 +0200 Subject: [PATCH] tournament working but still some problems --- .gitignore | 3 + docker-compose.yml | 165 ------------------ makefile | 2 +- pong/game/game.py | 2 +- .../templates/pong/tournament_brackets.html | 96 +++++----- pong/game/tournament.py | 89 ++++++---- pong/settings.py | 4 +- pong/static/game.js | 17 +- pong/static/styles.css | 7 +- 9 files changed, 123 insertions(+), 262 deletions(-) diff --git a/.gitignore b/.gitignore index a8727e7..acddfd3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ __pycache__/ data/ .env makefile +docker-compose.old.yml +docker-compose.yml logs/django.log +pong/settings.py \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 33a4a7e..aa21870 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,63 +1,4 @@ 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: . @@ -112,104 +53,6 @@ services: 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 @@ -225,14 +68,6 @@ volumes: 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: 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/game.py b/pong/game/game.py index e619da5..92bf0de 100644 --- a/pong/game/game.py +++ b/pong/game/game.py @@ -49,7 +49,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.game_loop_task = asyncio.create_task(self.game_loop()) print(f" Begin MATCH at: {self.start_time}") diff --git a/pong/game/templates/pong/tournament_brackets.html b/pong/game/templates/pong/tournament_brackets.html index 85719a3..9207797 100644 --- a/pong/game/templates/pong/tournament_brackets.html +++ b/pong/game/templates/pong/tournament_brackets.html @@ -7,78 +7,82 @@
{% 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 }} + {% 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 %} +
+ {% if not forloop.last %} +
+ {% endif %} {% endfor %}
diff --git a/pong/game/tournament.py b/pong/game/tournament.py index 1f039fb..1baf7e4 100644 --- a/pong/game/tournament.py +++ b/pong/game/tournament.py @@ -33,6 +33,7 @@ class TournamentMatchMaker: self.matches = [] self.rounds = [] self.current_round = 0 + self.games = 0 self.tournament_state = "waiting" # Can be "waiting", "in_progress", or "ended" async def add_player(self, player): @@ -41,11 +42,6 @@ class TournamentMatchMaker: 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) - await self.update_waiting_room() - async def update_waiting_room(self): html = self.generate_waiting_room_html() for player in self.waiting_players: @@ -63,25 +59,51 @@ class TournamentMatchMaker: } return render_to_string('pong/tournament_waiting_room.html', context) + async def send_to_player(self, player, data): + 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 - + return False 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 + 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") + 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) + 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)) + matches.append(TournamentMatch(self.games, players[i], players[i + 1], self)) else: - matches.append(TournamentMatch(len(self.matches) + 1, players[i], None, self)) # Bye + matches.append(TournamentMatch(self.games, players[i], None, self)) # BYE match self.rounds.append(matches) self.matches.extend(matches) @@ -103,8 +125,8 @@ class TournamentMatchMaker: return [ [ { - 'player1': match.player1.user.username if match.player1 else 'BYE', - 'player2': match.player2.user.username if match.player2 else 'BYE', + 'player1': match.player1.user.username if match.player1 else 'BOT', + 'player2': match.player2.user.username if match.player2 else 'BOT', 'winner': match.game_state['player1_name'] if match.game_state['player1_score'] > match.game_state['player2_score'] else match.game_state['player2_name'] if match.ended else None, 'score1': match.game_state['player1_score'], 'score2': match.game_state['player2_score'] @@ -115,45 +137,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) match.game_state['player1_score'] = 3 match.game_state['player2_score'] = 0 await match.end_game() + #asyncio.create_task(match.start_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/settings.py b/pong/settings.py index b44ccdf..446ec08 100644 --- a/pong/settings.py +++ b/pong/settings.py @@ -136,7 +136,7 @@ CHANNEL_LAYERS = { }, } -LOGGING = { +'''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 @@ -169,4 +169,4 @@ LOGGING = { '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..9a19b2a 100644 --- a/pong/static/game.js +++ b/pong/static/game.js @@ -388,32 +388,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 +442,7 @@ document.addEventListener('DOMContentLoaded', () => { function updateGameState(newState) { gameState = newState; renderGame(); - checkForWinner(); + //checkForWinner(); } function renderGame() { diff --git a/pong/static/styles.css b/pong/static/styles.css index 90e42fe..38d78f0 100644 --- a/pong/static/styles.css +++ b/pong/static/styles.css @@ -279,7 +279,6 @@ button:hover { color: #ffffff; } - .dropdown-content { display: none; position: absolute; @@ -298,7 +297,6 @@ button:hover { display: block; } - .dropdown-content a { color: #ffffff; padding: 12px 16px; @@ -308,7 +306,6 @@ button:hover { transition: background-color 0.3s ease; } - .dropdown-content a:hover { background-color: #333; color: #00ffff; @@ -404,7 +401,7 @@ pre { white-space: pre-wrap; word-wrap: break-word; } - +/* .tournament-waiting-room { background-color: rgba(0, 0, 0, 0.6); padding: 20px; @@ -435,7 +432,7 @@ pre { list-style-type: none; padding: 0; } - +*/ body { color: rgb(0, 255, 255); /* Valeur par défaut */ font-family: Arial, sans-serif;