From 21b73a37ae054aff608b90cc3262b5a899058ef3 Mon Sep 17 00:00:00 2001 From: Ladebeze66 Date: Thu, 12 Sep 2024 17:32:46 +0200 Subject: [PATCH] 01 --- .gitignore | 5 +- docker-compose.yml | 20 +- env_template | 8 +- logs/172.20.2.60-1726141292617.log | 56 + logs/172.20.2.60-1726142931992.log | 72 + logs/172.20.2.60-1726144089884.log | 126 ++ logs/172.20.2.60-1726150242355.log | 78 + logs/172.31.141.12-1726064615544.log | 41 + logs/172.31.141.12-1726065549172.log | 38 + logs/172.31.141.12-1726069036744.log | 40 + logs/django_errors.log | 9 + makefile | 14 +- pong/asgi.py | 9 + pong/game/consumers.py | 449 ++++-- pong/game/game.py | 74 +- pong/game/matchmaking.py | 9 +- .../0001_initial.py:Zone.Identifier | 0 .../migrations/0002_alter_match_winner.py | 19 - ...0002_alter_match_winner.py:Zone.Identifier | 0 .../migrations/0003_alter_tournoi_date.py | 18 - pong/game/models.py | 29 +- pong/game/routing.py | 11 +- .../templates/pong/tournament_brackets.html | 60 - .../pong/tournament_waiting_room.html | 20 +- pong/game/tournament.py | 223 +-- pong/game/urls.py | 3 +- pong/game/utils.py | 98 +- pong/game/views.py | 130 +- pong/settings.py | 220 +-- pong/static/burger.js | 13 +- pong/static/flags/de.svg | 0 pong/static/flags/es.svg | 0 pong/static/flags/fr.svg | 0 pong/static/flags/it.svg | 0 pong/static/flags/us.svg | 0 pong/static/game.js | 1327 +++++++++++------ pong/static/index.html | 452 +++--- pong/static/styles.css | 715 +++++---- 38 files changed, 2803 insertions(+), 1583 deletions(-) create mode 100644 logs/172.20.2.60-1726141292617.log create mode 100644 logs/172.20.2.60-1726142931992.log create mode 100644 logs/172.20.2.60-1726144089884.log create mode 100644 logs/172.20.2.60-1726150242355.log create mode 100644 logs/172.31.141.12-1726064615544.log create mode 100644 logs/172.31.141.12-1726065549172.log create mode 100644 logs/172.31.141.12-1726069036744.log create mode 100644 logs/django_errors.log create mode 100644 pong/game/migrations/0001_initial.py:Zone.Identifier delete mode 100644 pong/game/migrations/0002_alter_match_winner.py create mode 100644 pong/game/migrations/0002_alter_match_winner.py:Zone.Identifier delete mode 100644 pong/game/migrations/0003_alter_tournoi_date.py delete mode 100644 pong/game/templates/pong/tournament_brackets.html mode change 100755 => 100644 pong/static/flags/de.svg mode change 100755 => 100644 pong/static/flags/es.svg mode change 100755 => 100644 pong/static/flags/fr.svg mode change 100755 => 100644 pong/static/flags/it.svg mode change 100755 => 100644 pong/static/flags/us.svg diff --git a/.gitignore b/.gitignore index c5bd6df..a8727e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ venv/ __pycache__/ -.env \ No newline at end of file +data/ +.env +makefile +logs/django.log diff --git a/docker-compose.yml b/docker-compose.yml index 062be5a..c283dec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,7 +50,7 @@ services: 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!"; + echo "All done!"; ' healthcheck: test: ["CMD-SHELL", "[ -f config/certs/es01/es01.crt ]"] @@ -67,7 +67,7 @@ services: 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 migrate --noinput && venv/bin/python manage.py collectstatic --noinput && venv/bin/daphne -b 0.0.0.0 -p 8080 pong.asgi:application" volumes: @@ -175,21 +175,21 @@ services: - XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=${ENCRYPTION_KEY} - XPACK_REPORTING_ENCRYPTIONKEY=${ENCRYPTION_KEY} healthcheck: - test: + test: [ - "CMD-SHELL", + "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 + co.elastic.logs/module: logstash + user: root depends_on: es01: condition: service_healthy @@ -209,7 +209,7 @@ services: - ELASTIC_USER=${ELASTIC_USERNAME} - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} - xpack.monitoring.enabled=false - + volumes: pong: driver: local @@ -219,6 +219,10 @@ volumes: 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: diff --git a/env_template b/env_template index 92f05fd..b297aeb 100644 --- a/env_template +++ b/env_template @@ -6,7 +6,7 @@ DJANGO_ALLOWED_HOSTS=['*'] # PostgreSQL settings POSTGRES_DB=players_db POSTGRES_USER=42student -POSTGRES_PASSWORD=postgre_pass +POSTGRES_PASSWORD= # Django settings DB_HOST=db @@ -20,11 +20,11 @@ CLUSTER_NAME=docker-cluster LICENSE=basic ELASTIC_USERNAME=elastic -ELASTIC_PASSWORD=elastic_pass +ELASTIC_PASSWORD= # Kibana settings KIBANA_PORT=5601 -KIBANA_USERNAME=kibana_system -KIBANA_PASSWORD=kibana_pass +KIBANA_USERNAME= +KIBANA_PASSWORD= ENCRYPTION_KEY=c34d38b3a14956121ff2170e5030b471551370178f43e5626eec58b04a30fae2 diff --git a/logs/172.20.2.60-1726141292617.log b/logs/172.20.2.60-1726141292617.log new file mode 100644 index 0000000..64d21c5 --- /dev/null +++ b/logs/172.20.2.60-1726141292617.log @@ -0,0 +1,56 @@ +:8080/:1 The Cross-Origin-Opener-Policy header has been ignored, because the URL's origin was untrustworthy. It was defined either in the final response or a redirect. Please deliver the response using the HTTPS protocol. You can also use the 'localhost' origin instead. See https://www.w3.org/TR/powerful-features/#potentially-trustworthy-origin and https://html.spec.whatwg.org/#the-cross-origin-opener-policy-header. +game.js:2 DOM fully loaded and parsed +game.js:48 DOM elements initialized +game.js:71 Event listeners added +game.js:186 checkUserExists called with username: didi +game.js:203 User existence check response: {exists: false} +game.js:212 handleRegister called +game.js:225 Attempting to register user: didi +game.js:250 registerUser called with username: didi +game.js:273 Registration response data: {registered: true, token: '391a5261-4dac-4555-9c5f-7d1000557dc0'} +game.js:275 User registered successfully: {registered: true, token: '391a5261-4dac-4555-9c5f-7d1000557dc0'} +game.js:229 Token stored: 391a5261-4dac-4555-9c5f-7d1000557dc0 +game.js:230 User registered successfully +game.js:964 Joining room: main_room with username: didi and token: 391a5261-4dac-4555-9c5f-7d1000557dc0 +game.js:971 Joining new room: main_room +game.js:888 createRoomTab: main_room with username: didi and token: 391a5261-4dac-4555-9c5f-7d1000557dc0 +game.js:898 Tab for room main_room already exists. +game.js:912 Showing tab for room: main_room +game.js:807 Initializing chat WebSocket... +game.js:808 Initializing chat WebSocket for room: main_room with username: didi +game.js:811 startChatWebSocket: main_room with username: didi and token: 391a5261-4dac-4555-9c5f-7d1000557dc0 +game.js:875 WebSocket connection stored for room: main_room +game.js:919 switchRoom: main_room with username: didi and token: 391a5261-4dac-4555-9c5f-7d1000557dc0 +game.js:925 Attempting to switch to room: main_room +game.js:931 Switching from room null to room main_room +game.js:816 Chat WebSocket connection established in room: main_room +game.js:825 Authentication message sent for room: main_room with username: didi +game.js:831 Message received from server in room main_room: {type: 'authenticated', username: 'didi'} +game.js:835 User authenticated successfully in room: main_room +game.js:831 Message received from server in room main_room: {type: 'chat_message', message: 'didi: a rejoint le chat main_room', room: 'main_room'} +game.js:846 Message displayed in chat log for room: main_room +game.js:831 Message received from server in room main_room: {type: 'chat_message', message: 'didi: kikou', room: 'main_room'} +game.js:846 Message displayed in chat log for room: main_room +game.js:831 Message received from server in room main_room: {type: 'chat_message', message: 'didi: /b', room: 'main_room'} +game.js:846 Message displayed in chat log for room: main_room +game.js:831 Message received from server in room main_room: {type: 'success', message: 'You have blocked dd'} +game.js:855 Unhandled message type: {type: 'success', message: 'You have blocked dd'} +chatSocket.onmessage @ game.js:855 +game.js:831 Message received from server in room main_room: {type: 'chat_message', message: 'fifi: a rejoint le chat main_room', room: 'main_room'} +game.js:846 Message displayed in chat log for room: main_room +game.js:831 Message received from server in room main_room: {type: 'chat_message', message: 'fifi: hello', room: 'main_room'} +game.js:846 Message displayed in chat log for room: main_room +game.js:831 Message received from server in room main_room: {type: 'chat_message', message: 'didi: hello', room: 'main_room'} +game.js:846 Message displayed in chat log for room: main_room +game.js:831 Message received from server in room main_room: {type: 'chat_message', message: 'didi: tu veux jouer', room: 'main_room'} +game.js:846 Message displayed in chat log for room: main_room +game.js:831 Message received from server in room main_room: {type: 'chat_message', message: 'fifi: oui', room: 'main_room'} +game.js:846 Message displayed in chat log for room: main_room +game.js:831 Message received from server in room main_room: {type: 'error', message: 'Erreur interne du serveur'} +game.js:852 Error message received: Erreur interne du serveur +chatSocket.onmessage @ game.js:852 +game.js:831 Message received from server in room main_room: {type: 'success', message: 'You have blocked fifi'} +game.js:855 Unhandled message type: {type: 'success', message: 'You have blocked fifi'} +chatSocket.onmessage @ game.js:855 +game.js:831 Message received from server in room main_room: {type: 'chat_message', message: 'fifi: conard', room: 'main_room'} +game.js:846 Message displayed in chat log for room: main_room diff --git a/logs/172.20.2.60-1726142931992.log b/logs/172.20.2.60-1726142931992.log new file mode 100644 index 0000000..b064e74 --- /dev/null +++ b/logs/172.20.2.60-1726142931992.log @@ -0,0 +1,72 @@ +:8080/:1 The Cross-Origin-Opener-Policy header has been ignored, because the URL's origin was untrustworthy. It was defined either in the final response or a redirect. Please deliver the response using the HTTPS protocol. You can also use the 'localhost' origin instead. See https://www.w3.org/TR/powerful-features/#potentially-trustworthy-origin and https://html.spec.whatwg.org/#the-cross-origin-opener-policy-header. +game.js:2 DOM fully loaded and parsed +game.js:48 DOM elements initialized +game.js:71 Event listeners added +game.js:186 checkUserExists called with username: dudu +game.js:203 User existence check response: {exists: false} +game.js:212 handleRegister called +game.js:225 Attempting to register user: dudu +game.js:250 registerUser called with username: dudu +game.js:273 Registration response data: {registered: true, token: '5265ee4d-d0f3-4d7e-a465-e152d90f43f3'} +game.js:275 User registered successfully: {registered: true, token: '5265ee4d-d0f3-4d7e-a465-e152d90f43f3'} +game.js:229 Token stored: 5265ee4d-d0f3-4d7e-a465-e152d90f43f3 +game.js:230 User registered successfully +game.js:973 Joining room: main_room with username: dudu and token: 5265ee4d-d0f3-4d7e-a465-e152d90f43f3 +game.js:980 Joining new room: main_room +game.js:897 createRoomTab: main_room with username: dudu and token: 5265ee4d-d0f3-4d7e-a465-e152d90f43f3 +game.js:907 Tab for room main_room already exists. +game.js:921 Showing tab for room: main_room +game.js:818 Initializing chat WebSocket... +game.js:819 Initializing chat WebSocket for room: main_room with username: dudu +game.js:822 startChatWebSocket: main_room with username: dudu and token: 5265ee4d-d0f3-4d7e-a465-e152d90f43f3 +game.js:886 WebSocket connection stored for room: main_room +game.js:734 ChatInput initialized for room: main_room, username: dudu +game.js:928 switchRoom: main_room with username: dudu and token: 5265ee4d-d0f3-4d7e-a465-e152d90f43f3 +game.js:934 Attempting to switch to room: main_room +game.js:940 Switching from room null to room main_room +game.js:827 Chat WebSocket connection established in room: main_room +game.js:836 Authentication message sent for room: main_room with username: dudu +game.js:842 Message received from server in room main_room: {type: 'authenticated', username: 'dudu'} +game.js:846 User authenticated successfully in room: main_room +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'dudu: a rejoint le chat main_room', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:749 Send button clicked, attempting to send message... +game.js:756 Attempting to send message: frfrfr +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'dudu: frfrfr', room: 'main_room'}message: "dudu: frfrfr"room: "main_room"type: "chat_message"[[Prototype]]: Object +game.js:857 Message displayed in chat log for room: main_room +game.js:749 Send button clicked, attempting to send message... +game.js:756 Attempting to send message: frfr +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'dudu: frfr', room: 'main_room'}message: "dudu: frfr"room: "main_room"type: "chat_message"[[Prototype]]: Object +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: /b +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'dudu: /b', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: /b b +game.js:761 Detected block command for user: b +game.js:784 Sending block command to WebSocket for user: b +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'success', message: 'You have blocked b'} +game.js:866 Unhandled message type: {type: 'success', message: 'You have blocked b'} +chatSocket.onmessage @ game.js:866 +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: /i +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'dudu: /i', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: /i hhuhhihi hihi +game.js:765 Detected invite command for user: hhuhhihi hihi +game.js:794 Sending invite command to WebSocket for user: hhuhhihi hihi +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'error', message: 'Erreur interne du serveur'}message: "Erreur interne du serveur"type: "error"[[Prototype]]: Object +game.js:863 Error message received: Erreur interne du serveur +chatSocket.onmessage @ game.js:863 diff --git a/logs/172.20.2.60-1726144089884.log b/logs/172.20.2.60-1726144089884.log new file mode 100644 index 0000000..751fd1a --- /dev/null +++ b/logs/172.20.2.60-1726144089884.log @@ -0,0 +1,126 @@ +:8080/:1 The Cross-Origin-Opener-Policy header has been ignored, because the URL's origin was untrustworthy. It was defined either in the final response or a redirect. Please deliver the response using the HTTPS protocol. You can also use the 'localhost' origin instead. See https://www.w3.org/TR/powerful-features/#potentially-trustworthy-origin and https://html.spec.whatwg.org/#the-cross-origin-opener-policy-header. +game.js:2 DOM fully loaded and parsed +game.js:48 DOM elements initialized +game.js:71 Event listeners added +game.js:186 checkUserExists called with username: rere +game.js:203 User existence check response: {exists: false} +game.js:212 handleRegister called +game.js:225 Attempting to register user: rere +game.js:250 registerUser called with username: rere +game.js:273 Registration response data: {registered: true, token: 'e07d4752-1960-43d2-a971-4cb24ae2486f'} +game.js:275 User registered successfully: {registered: true, token: 'e07d4752-1960-43d2-a971-4cb24ae2486f'} +game.js:229 Token stored: e07d4752-1960-43d2-a971-4cb24ae2486f +game.js:230 User registered successfully +game.js:978 Joining room: main_room with username: rere and token: e07d4752-1960-43d2-a971-4cb24ae2486f +game.js:985 Joining new room: main_room +game.js:902 createRoomTab: main_room with username: rere and token: e07d4752-1960-43d2-a971-4cb24ae2486f +game.js:912 Tab for room main_room already exists. +game.js:926 Showing tab for room: main_room +game.js:818 Initializing chat WebSocket... +game.js:819 Initializing chat WebSocket for room: main_room with username: rere +game.js:822 startChatWebSocket: main_room with username: rere and token: e07d4752-1960-43d2-a971-4cb24ae2486f +game.js:891 WebSocket connection stored for room: main_room +game.js:734 ChatInput initialized for room: main_room, username: rere +game.js:933 switchRoom: main_room with username: rere and token: e07d4752-1960-43d2-a971-4cb24ae2486f +game.js:939 Attempting to switch to room: main_room +game.js:945 Switching from room null to room main_room +game.js:827 Chat WebSocket connection established in room: main_room +game.js:836 Authentication message sent for room: main_room with username: rere +game.js:842 Message received from server in room main_room: {type: 'authenticated', username: 'rere'} +game.js:846 User authenticated successfully in room: main_room +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: a rejoint le chat main_room', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: cdcdc +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: cdcdc', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: cdcd +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: cdcd', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: cdcdcdcd +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: cdcdcdcd', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: cdcdcdc +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: cdcdcdc', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: cdcdcd +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: cdcdcd', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: ccccc +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: ccccc', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: cdcdcdcd +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: cdcdcdcd', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: cdcdcdc +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: cdcdcdc', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: cdcdcdc +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: cdcdcdc', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: cdcdcdcdcd +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: cdcdcdcdcd', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: cdcdcdc +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: cdcdcdc', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'aaaa: a rejoint le chat main_room', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'aaaa: cdcdcdcdc', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'aaaa: ccddcdcd', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: cdcdcdc +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'chat_message', message: 'rere: cdcdcdc', room: 'main_room'} +game.js:857 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: /b aaaa +game.js:761 Detected block command for user: aaaa +game.js:784 Sending block command to WebSocket for user: aaaa +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'error', message: 'Target user aaaa not found in room'} +game.js:867 Error message received: Target user aaaa not found in room +chatSocket.onmessage @ game.js:867 +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: /i aaaa +game.js:765 Detected invite command for user: aaaa +game.js:794 Sending invite command to WebSocket for user: aaaa +game.js:777 Message input cleared. +game.js:842 Message received from server in room main_room: {type: 'error', message: 'Erreur interne du serveur'} +game.js:867 Error message received: Erreur interne du serveur +chatSocket.onmessage @ game.js:867 diff --git a/logs/172.20.2.60-1726150242355.log b/logs/172.20.2.60-1726150242355.log new file mode 100644 index 0000000..78a214f --- /dev/null +++ b/logs/172.20.2.60-1726150242355.log @@ -0,0 +1,78 @@ +172.20.2.60/:1 The Cross-Origin-Opener-Policy header has been ignored, because the URL's origin was untrustworthy. It was defined either in the final response or a redirect. Please deliver the response using the HTTPS protocol. You can also use the 'localhost' origin instead. See https://www.w3.org/TR/powerful-features/#potentially-trustworthy-origin and https://html.spec.whatwg.org/#the-cross-origin-opener-policy-header. +game.js:2 DOM fully loaded and parsed +game.js:48 DOM elements initialized +game.js:71 Event listeners added +game.js:186 checkUserExists called with username: vivi +game.js:203 User existence check response: Object +game.js:212 handleRegister called +game.js:225 Attempting to register user: vivi +game.js:250 registerUser called with username: vivi +game.js:273 Registration response data: Object +game.js:275 User registered successfully: Object +game.js:229 Token stored: 66ce0c1a-dfbc-4f0f-9ec9-a4a9d4b990fe +game.js:230 User registered successfully +game.js:980 Joining room: main_room with username: vivi and token: 66ce0c1a-dfbc-4f0f-9ec9-a4a9d4b990fe +game.js:987 Joining new room: main_room +game.js:904 createRoomTab: main_room with username: vivi and token: 66ce0c1a-dfbc-4f0f-9ec9-a4a9d4b990fe +game.js:914 Tab for room main_room already exists. +game.js:928 Showing tab for room: main_room +game.js:820 Initializing chat WebSocket... +game.js:821 Initializing chat WebSocket for room: main_room with username: vivi +game.js:824 startChatWebSocket: main_room with username: vivi and token: 66ce0c1a-dfbc-4f0f-9ec9-a4a9d4b990fe +game.js:893 WebSocket connection stored for room: main_room +game.js:734 ChatInput initialized for room: main_room, username: vivi +game.js:935 switchRoom: main_room with username: vivi and token: 66ce0c1a-dfbc-4f0f-9ec9-a4a9d4b990fe +game.js:941 Attempting to switch to room: main_room +game.js:947 Switching from room null to room main_room +game.js:829 Chat WebSocket connection established in room: main_room +game.js:838 Authentication message sent for room: main_room with username: vivi +game.js:844 Message received from server in room main_room: Object +game.js:848 User authenticated successfully in room: main_room +game.js:844 Message received from server in room main_room: Object +game.js:859 Message displayed in chat log for room: main_room +game.js:749 Send button clicked, attempting to send message... +game.js:756 Attempting to send message: cscscsc +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:844 Message received from server in room main_room: Object +game.js:859 Message displayed in chat log for room: main_room +game.js:749 Send button clicked, attempting to send message... +game.js:756 Attempting to send message: cscscs +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:844 Message received from server in room main_room: Object +game.js:859 Message displayed in chat log for room: main_room +game.js:844 Message received from server in room main_room: Object +game.js:859 Message displayed in chat log for room: main_room +game.js:844 Message received from server in room main_room: Object +game.js:859 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: /b sasa +game.js:761 Detected block command for user: sasa +game.js:784 Sending block command to WebSocket for user: sasa +game.js:777 Message input cleared. +game.js:844 Message received from server in room main_room: Object +game.js:869 Error message received: Target user sasa not found in room main_room +chatSocket.onmessage @ game.js:869 +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: /isasa +game.js:768 Sending chat message to WebSocket... +game.js:777 Message input cleared. +game.js:844 Message received from server in room main_room: {type: 'chat_message', message: 'vivi: /isasa', room: 'main_room'} +game.js:859 Message displayed in chat log for room: main_room +game.js:742 Enter key pressed, attempting to send message... +game.js:756 Attempting to send message: /i sas +game.js:765 Detected invite command for user: sas +game.js:794 Sending invite command to WebSocket for user: sas +game.js:777 Message input cleared. +game.js:844 Message received from server in room main_room: {type: 'error', message: 'Target user sas not found in room main_room'} +game.js:869 Error message received: Target user sas not found in room main_room +chatSocket.onmessage @ game.js:869 +game.js:749 Send button clicked, attempting to send message... +game.js:756 Attempting to send message: /i sasa +game.js:765 Detected invite command for user: sasa +game.js:794 Sending invite command to WebSocket for user: sasa +game.js:777 Message input cleared. +game.js:844 Message received from server in room main_room: {type: 'error', message: 'Target user sasa not found in room main_room'} +game.js:869 Error message received: Target user sasa not found in room main_room +chatSocket.onmessage @ game.js:869 diff --git a/logs/172.31.141.12-1726064615544.log b/logs/172.31.141.12-1726064615544.log new file mode 100644 index 0000000..b185656 --- /dev/null +++ b/logs/172.31.141.12-1726064615544.log @@ -0,0 +1,41 @@ +:8080/:1 The Cross-Origin-Opener-Policy header has been ignored, because the URL's origin was untrustworthy. It was defined either in the final response or a redirect. Please deliver the response using the HTTPS protocol. You can also use the 'localhost' origin instead. See https://www.w3.org/TR/powerful-features/#potentially-trustworthy-origin and https://html.spec.whatwg.org/#the-cross-origin-opener-policy-header. +game.js:2 DOM fully loaded and parsed +game.js:48 DOM elements initialized +game.js:71 Event listeners added +game.js:186 checkUserExists called with username: dede +game.js:203 User existence check response: {exists: false} +game.js:212 handleRegister called +game.js:225 Attempting to register user: dede +game.js:250 registerUser called with username: dede +game.js:273 Registration response data: {registered: true, token: 'c4ab5fd7-4b04-4353-8d4e-ae1afb2ba793'} +game.js:275 User registered successfully: {registered: true, token: 'c4ab5fd7-4b04-4353-8d4e-ae1afb2ba793'} +game.js:229 Token stored: c4ab5fd7-4b04-4353-8d4e-ae1afb2ba793 +game.js:230 User registered successfully +game.js:922 Joining room: main_room with username: dede and token: c4ab5fd7-4b04-4353-8d4e-ae1afb2ba793 +game.js:929 Joining new room: main_room +game.js:846 createRoomTab: main_room with username: dede and token: c4ab5fd7-4b04-4353-8d4e-ae1afb2ba793 +game.js:856 Tab for room main_room already exists. +game.js:870 Showing tab for room: main_room +game.js:778 Initializing chat WebSocket... +game.js:779 Initializing chat WebSocket for room: main_room with username: dede +game.js:877 switchRoom: main_room with username: dede and token: c4ab5fd7-4b04-4353-8d4e-ae1afb2ba793 +game.js:883 Attempting to switch to room: main_room +game.js:889 Switching from room null to room main_room +game.js:783 startChatWebSocket: main_room with username: dede and token: c4ab5fd7-4b04-4353-8d4e-ae1afb2ba793 +game.js:785 Attempting to connect to WebSocket for room: main_room +game.js:835 WebSocket connection stored for room: main_room +game.js:784 WebSocket connection to 'ws://172.31.141.12:8080/ws/chat/main_room/' failed: +(anonymous) @ game.js:784 +game.js:831 Chat WebSocket error in room main_room: Event {isTrusted: true, type: 'error', target: WebSocket, currentTarget: WebSocket, eventPhase: 2, …} +chatSocket.onerror @ game.js:831 +error +(anonymous) @ game.js:830 +setTimeout +startChatWebSocket @ game.js:781 +joinRoom @ game.js:932 +handleRegister @ game.js:235 +game.js:826 Chat WebSocket closed unexpectedly for room main_room +chatSocket.onclose @ game.js:826 +game.js:754 WebSocket is already in CLOSING or CLOSED state. +sendMessage @ game.js:754 +(anonymous) @ game.js:747 diff --git a/logs/172.31.141.12-1726065549172.log b/logs/172.31.141.12-1726065549172.log new file mode 100644 index 0000000..7a3b513 --- /dev/null +++ b/logs/172.31.141.12-1726065549172.log @@ -0,0 +1,38 @@ +:8080/:1 The Cross-Origin-Opener-Policy header has been ignored, because the URL's origin was untrustworthy. It was defined either in the final response or a redirect. Please deliver the response using the HTTPS protocol. You can also use the 'localhost' origin instead. See https://www.w3.org/TR/powerful-features/#potentially-trustworthy-origin and https://html.spec.whatwg.org/#the-cross-origin-opener-policy-header. +game.js:2 DOM fully loaded and parsed +game.js:48 DOM elements initialized +game.js:71 Event listeners added +game.js:186 checkUserExists called with username: ferdy +game.js:203 User existence check response: {exists: false} +game.js:212 handleRegister called +game.js:225 Attempting to register user: ferdy +game.js:250 registerUser called with username: ferdy +game.js:273 Registration response data: {registered: true, token: '4d383e70-92b8-4e04-8efb-d7bdfc0dcfac'} +game.js:275 User registered successfully: {registered: true, token: '4d383e70-92b8-4e04-8efb-d7bdfc0dcfac'} +game.js:229 Token stored: 4d383e70-92b8-4e04-8efb-d7bdfc0dcfac +game.js:230 User registered successfully +game.js:922 Joining room: main_room with username: ferdy and token: 4d383e70-92b8-4e04-8efb-d7bdfc0dcfac +game.js:929 Joining new room: main_room +game.js:846 createRoomTab: main_room with username: ferdy and token: 4d383e70-92b8-4e04-8efb-d7bdfc0dcfac +game.js:856 Tab for room main_room already exists. +game.js:870 Showing tab for room: main_room +game.js:778 Initializing chat WebSocket... +game.js:779 Initializing chat WebSocket for room: main_room with username: ferdy +game.js:877 switchRoom: main_room with username: ferdy and token: 4d383e70-92b8-4e04-8efb-d7bdfc0dcfac +game.js:883 Attempting to switch to room: main_room +game.js:889 Switching from room null to room main_room +game.js:783 startChatWebSocket: main_room with username: ferdy and token: 4d383e70-92b8-4e04-8efb-d7bdfc0dcfac +game.js:785 Attempting to connect to WebSocket for room: main_room +game.js:835 WebSocket connection stored for room: main_room +game.js:784 WebSocket connection to 'ws://172.31.141.12:8080/ws/chat/main_room/' failed: +(anonymous) @ game.js:784 +game.js:831 Chat WebSocket error in room main_room: Event {isTrusted: true, type: 'error', target: WebSocket, currentTarget: WebSocket, eventPhase: 2, …}isTrusted: truebubbles: falsecancelBubble: falsecancelable: falsecomposed: falsecurrentTarget: WebSocket {url: 'ws://172.31.141.12:8080/ws/chat/main_room/', readyState: 3, bufferedAmount: 0, onopen: ƒ, onerror: ƒ, …}defaultPrevented: falseeventPhase: 0returnValue: truesrcElement: WebSocket {url: 'ws://172.31.141.12:8080/ws/chat/main_room/', readyState: 3, bufferedAmount: 0, onopen: ƒ, onerror: ƒ, …}target: WebSocket {url: 'ws://172.31.141.12:8080/ws/chat/main_room/', readyState: 3, bufferedAmount: 0, onopen: ƒ, onerror: ƒ, …}timeStamp: 13273.79999999702type: "error"[[Prototype]]: Event +chatSocket.onerror @ game.js:831 +error +(anonymous) @ game.js:830 +setTimeout +startChatWebSocket @ game.js:781 +joinRoom @ game.js:932 +handleRegister @ game.js:235 +game.js:826 Chat WebSocket closed unexpectedly for room main_room +chatSocket.onclose @ game.js:826 diff --git a/logs/172.31.141.12-1726069036744.log b/logs/172.31.141.12-1726069036744.log new file mode 100644 index 0000000..8c94772 --- /dev/null +++ b/logs/172.31.141.12-1726069036744.log @@ -0,0 +1,40 @@ +:8080/:1 The Cross-Origin-Opener-Policy header has been ignored, because the URL's origin was untrustworthy. It was defined either in the final response or a redirect. Please deliver the response using the HTTPS protocol. You can also use the 'localhost' origin instead. See https://www.w3.org/TR/powerful-features/#potentially-trustworthy-origin and https://html.spec.whatwg.org/#the-cross-origin-opener-policy-header. +game.js:2 DOM fully loaded and parsed +game.js:48 DOM elements initialized +game.js:71 Event listeners added +game.js:186 checkUserExists called with username: gggg +game.js:203 User existence check response: {exists: false} +game.js:212 handleRegister called +game.js:225 Attempting to register user: gggg +game.js:250 registerUser called with username: gggg +game.js:273 Registration response data: {registered: true, token: 'a6010523-60c5-460b-ac49-39097e66a376'} +game.js:275 User registered successfully: {registered: true, token: 'a6010523-60c5-460b-ac49-39097e66a376'} +game.js:229 Token stored: a6010523-60c5-460b-ac49-39097e66a376 +game.js:230 User registered successfully +game.js:926 Joining room: main_room with username: gggg and token: a6010523-60c5-460b-ac49-39097e66a376 +game.js:933 Joining new room: main_room +game.js:850 createRoomTab: main_room with username: gggg and token: a6010523-60c5-460b-ac49-39097e66a376 +game.js:860 Tab for room main_room already exists. +game.js:874 Showing tab for room: main_room +game.js:780 Initializing chat WebSocket... +game.js:781 Initializing chat WebSocket for room: main_room with username: gggg +game.js:881 switchRoom: main_room with username: gggg and token: a6010523-60c5-460b-ac49-39097e66a376 +game.js:887 Attempting to switch to room: main_room +game.js:893 Switching from room null to room main_room +game.js:785 startChatWebSocket: main_room with username: gggg and token: a6010523-60c5-460b-ac49-39097e66a376 +game.js:787 Attempting to connect to WebSocket for room: main_room +game.js:838 WebSocket connection stored for room: main_room +game.js:839 Authentication username for chat-input: gggg +game.js:790 Chat WebSocket connection established in room: main_room +game.js:798 Authentication message sent with username: gggg +game.js:799 Authentication message sent for room: main_room +game.js:804 Message received from server in room main_room: {type: 'chat_message', message: ': Anonyme Vous avez rejoint le chat main_room', room: 'main_room'}message: ": Anonyme Vous avez rejoint le chat main_room"room: "main_room"type: "chat_message"[[Prototype]]: Object +game.js:814 Message displayed in chat log for room: main_room +game.js:804 Message received from server in room main_room: {type: 'authenticated', username: 'gggg'}type: "authenticated"username: "gggg"[[Prototype]]: Object +game.js:806 User authenticated for chat successfully in room: main_room +game.js:754 Sending message from username: gggg +game.js:804 Message received from server in room main_room: {type: 'chat_message', message: 'gggg: hhfghfh', room: 'main_room'} +game.js:814 Message displayed in chat log for room: main_room +game.js:754 Sending message from username: gggg +game.js:804 Message received from server in room main_room: {type: 'chat_message', message: 'gggg: fhfhfh', room: 'main_room'} +game.js:814 Message displayed in chat log for room: main_room diff --git a/logs/django_errors.log b/logs/django_errors.log new file mode 100644 index 0000000..08ff9f5 --- /dev/null +++ b/logs/django_errors.log @@ -0,0 +1,9 @@ +{"asctime": "2024-09-01 18:09:35,223", "levelname": "ERROR", "name": "django.request", "module": "log", "process": 25, "thread": 131470748288704, "message": "Internal Server Error: /register_user/", "taskName": null, "status_code": 500, "request": ""} +{"asctime": "2024-09-01 18:19:53,264", "levelname": "ERROR", "name": "django.request", "module": "log", "process": 25, "thread": 131966181574336, "message": "Internal Server Error: /register_user/", "taskName": null, "status_code": 500, "request": ""} +{"asctime": "2024-09-01 18:34:25,868", "levelname": "ERROR", "name": "django.request", "module": "log", "process": 25, "thread": 138740796229312, "message": "Internal Server Error: /register_user/", "taskName": null, "status_code": 500, "request": ""} +{"asctime": "2024-09-01 18:35:04,063", "levelname": "ERROR", "name": "django.request", "module": "log", "process": 25, "thread": 138740796229312, "message": "Internal Server Error: /register_user/", "taskName": null, "status_code": 500, "request": ""} +{"asctime": "2024-09-01 18:57:12,326", "levelname": "ERROR", "name": "django.request", "module": "log", "process": 25, "thread": 125916130838208, "message": "Internal Server Error: /register_user/", "taskName": null, "status_code": 500, "request": ""} +{"asctime": "2024-09-01 19:05:27,423", "levelname": "ERROR", "name": "django.request", "module": "log", "process": 25, "thread": 132460855690944, "message": "Internal Server Error: /register_user/", "taskName": null, "status_code": 500, "request": ""} +{"asctime": "2024-09-02 09:24:00,957", "levelname": "ERROR", "name": "django.request", "module": "log", "process": 25, "thread": 138073176278720, "message": "Internal Server Error: /register_user/", "taskName": null, "status_code": 500, "request": ""} +{"asctime": "2024-09-02 09:31:57,272", "levelname": "ERROR", "name": "django.request", "module": "log", "process": 25, "thread": 129740826478272, "message": "Internal Server Error: /register_user/", "taskName": null, "status_code": 500, "request": ""} +{"asctime": "2024-09-02 09:38:30,671", "levelname": "ERROR", "name": "django.request", "module": "log", "process": 25, "thread": 136981996635840, "message": "Internal Server Error: /register_user/", "taskName": null, "status_code": 500, "request": ""} diff --git a/makefile b/makefile index e7fbc31..b6c604c 100644 --- a/makefile +++ b/makefile @@ -3,8 +3,8 @@ COMPOSE=docker compose -f $(COMPOSE_FILE) CONTAINER=$(c) up: down - $(COMPOSE) build - $(COMPOSE) up -d $(CONTAINER) || true + $(COMPOSE) build + $(COMPOSE) up $(CONTAINER) || true build: $(COMPOSE) build $(CONTAINER) @@ -20,13 +20,8 @@ down: destroy: $(COMPOSE) down -v --rmi all - -kill-pid: sudo lsof -i :5432 | awk 'NR>1 {print $$2}' | xargs sudo kill -9 || true - sudo lsof -i :5601 | awk 'NR>1 {print $$2}' | xargs sudo kill -9 || true - sudo lsof -i :9200 | awk 'NR>1 {print $$2}' | xargs sudo kill -9 || true - sudo lsof -i :8080 | awk 'NR>1 {print $$2}' | xargs sudo kill -9 || true - sudo lsof -i :5044 | awk 'NR>1 {print $$2}' | xargs sudo kill -9 || true + sudo lsof -i :80 | awk 'NR>1 {print $$2}' | xargs sudo kill -9 || true logs: $(COMPOSE) logs -f $(CONTAINER) @@ -35,7 +30,7 @@ ps: $(COMPOSE) ps db-shell: - $(COMPOSE) exec db psql -U 42student players_db + $(COMPOSE) exec db psql -U 42student players_db re: destroy up @@ -52,4 +47,3 @@ help: @echo " make help # Show this help" .PHONY: up build start stop down destroy logs ps db-shell help - diff --git a/pong/asgi.py b/pong/asgi.py index 3a8dc68..9c90f25 100644 --- a/pong/asgi.py +++ b/pong/asgi.py @@ -11,8 +11,14 @@ https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ import os import django +import logging +logger = logging.getLogger(__name__) + +logger.debug("Setting default DJANGO_SETTINGS_MODULE to 'pong.settings'") os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pong.settings') + +logger.debug("Initializing Django setup") django.setup() from django.core.asgi import get_asgi_application @@ -20,6 +26,7 @@ from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack import pong.game.routing +logger.debug("Configuring ProtocolTypeRouter") application = ProtocolTypeRouter({ "http": get_asgi_application(), "websocket": AuthMiddlewareStack( @@ -28,3 +35,5 @@ application = ProtocolTypeRouter({ ) ), }) + +logger.info("ASGI application configurée et prête à accepter les connexions") diff --git a/pong/game/consumers.py b/pong/game/consumers.py index 8412496..86211ee 100644 --- a/pong/game/consumers.py +++ b/pong/game/consumers.py @@ -1,106 +1,379 @@ -# /pong/game/consumers.py - import json from channels.generic.websocket import AsyncWebsocketConsumer +from asgiref.sync import sync_to_async 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 +import logging + +logger = logging.getLogger(__name__) class GameConsumer(AsyncWebsocketConsumer): - async def connect(self): - await self.accept() - self.game = None - print("User connected") + async def connect(self): + try: + await self.accept() + self.game = None + logger.info("User connected via WebSocket") + except Exception as e: + logger.error(f"Error during WebSocket connection: {str(e)}") - async def receive(self, text_data): - data = json.loads(text_data) - if data['type'] == 'authenticate': - await self.authenticate(data['token']) - elif data['type'] == 'authenticate2': - await self.authenticate2(data['token_1'], data['token_2']) - elif data['type'] == 'authenticate3': - await self.authenticate3(data['token']) - elif data['type'] == 'key_press': - if self.game: - await self.game.handle_key_press(self, data['key']) - elif data['type'] == '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 receive(self, text_data): + try: + logger.debug(f"Received data: {text_data}") + data = json.loads(text_data) + message_type = data.get('type') - async def authenticate(self, token): - user = await self.get_user_from_token(token) - if user: - self.user = user - await self.send(text_data=json.dumps({'type': 'authenticated'})) - print(f"User {self.user} authenticated") - await self.join_waiting_room() - else: - await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'})) - print("Authentication failed") + if message_type == 'authenticate': + await self.authenticate(data.get('token')) + elif message_type == 'authenticate2': + await self.authenticate2(data.get('token_1'), data.get('token_2')) + elif message_type == 'authenticate3': + await self.authenticate3(data.get('token')) + elif message_type == 'key_press': + if self.game: + await self.game.handle_key_press(self, data.get('key')) + else: + await match_maker.handle_key_press(self, data.get('key')) + else: + logger.warning(f"Received unknown message type: {message_type}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Unknown message type'})) + except json.JSONDecodeError as e: + logger.error(f"JSON decode error: {str(e)} - Data received: {text_data}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Invalid JSON format'})) + except Exception as e: + logger.error(f"Error in WebSocket receive: {str(e)}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Internal server error'})) - @database_sync_to_async - def get_user_from_token(self, token): - try: - user = User.objects.filter(auth_token=token).first() - return user - except User.DoesNotExist: - return None + async def authenticate(self, token): + if not token: + logger.error("Token is None, authentication cannot proceed") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Token is missing'})) + return + try: + user = await self.get_user_from_token(token) + if user: + self.user = user + await self.send(text_data=json.dumps({'type': 'authenticated'})) + logger.info(f"User {self.user} authenticated") + await self.join_waiting_room() + else: + logger.warning(f"Authentication failed for token: {token}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'})) + except Exception as e: + logger.error(f"Error during authentication: {str(e)}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Internal server error'})) - async def join_waiting_room(self): - await self.send(text_data=json.dumps({'type': 'waiting_room'})) - await match_maker.add_player(self) + async def authenticate2(self, token_1, token_2): + try: + user = await self.get_user_from_token(token_1) + if user: + self.user = user + await self.send(text_data=json.dumps({'type': 'authenticated'})) + logger.info(f"User {self.user} authenticated with token_1") - async def authenticate2(self, token, token2): - user = await self.get_user_from_token(token) - if user: - self.user = user - await self.send(text_data=json.dumps({'type': 'authenticated'})) - print(f"User {self.user} authenticated") - user2 = await self.get_user_from_token2(token2) - if user2: - self.user2 = user2 - await self.send(text_data=json.dumps({'type': 'authenticated'})) - print(f"User {self.user2} authenticated") - await match_maker.create_game(self, None, True) - else: - await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'})) - print("Authentication failed") - else: - await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'})) - print("Authentication failed") + user2 = await self.get_user_from_token2(token_2) + if user2: + self.user2 = user2 + await self.send(text_data=json.dumps({'type': 'authenticated'})) + logger.info(f"User {self.user2} authenticated with token_2") + await match_maker.create_game(self, None, True) + else: + logger.warning(f"Authentication failed for token_2: {token_2}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed for user 2'})) + else: + logger.warning(f"Authentication failed for token_1: {token_1}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed for user 1'})) + except Exception as e: + logger.error(f"Error during dual authentication: {str(e)}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Internal server error'})) - @database_sync_to_async - def get_user_from_token2(self, token): - try: - user2 = User.objects.filter(auth_token=token).first() - return user2 - except User.DoesNotExist: - return None + async def authenticate3(self, token): + try: + user = await self.get_user_from_token(token) + if user: + self.user = user + await self.send(text_data=json.dumps({'type': 'authenticated'})) + logger.info(f"User {self.user} authenticated for tournament") + await self.join_tournament_waiting_room() + else: + logger.warning(f"Authentication failed for token: {token}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'})) + except Exception as e: + logger.error(f"Error during tournament authentication: {str(e)}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Internal server error'})) - async def authenticate3(self, token): - user = await self.get_user_from_token(token) - if user: - self.user = user - await self.send(text_data=json.dumps({'type': 'authenticated'})) - print(f"User {self.user.username} authenticated for tournament") - await self.join_tournament_waiting_room() - else: - await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'})) - print("Tournament authentication failed") + @database_sync_to_async + def get_user_from_token(self, token): + try: + user = User.objects.filter(auth_token=token).first() + logger.debug(f"User found: {user} for token: {token}") + return user + except User.DoesNotExist: + logger.warning(f"User not found for token: {token}") + return None - async def join_tournament_waiting_room(self): - await tournament_match_maker.add_player(self) + @database_sync_to_async + def get_user_from_token2(self, token): + try: + user2 = User.objects.filter(auth_token=token).first() + logger.debug(f"User2 found: {user2} for token: {token}") + return user2 + except User.DoesNotExist: + logger.warning(f"User not found for token_2: {token}") + return None - async def disconnect(self, close_code): - if self.game: - await self.game.end_game(disconnected_player=self) - await match_maker.remove_player(self) - await tournament_match_maker.remove_player(self) - print(f"User {self.user.username if hasattr(self, 'user') else 'Unknown'} disconnected") + async def join_waiting_room(self): + logger.info("Joining waiting room") + await self.send(text_data=json.dumps({'type': 'waiting_room'})) + await match_maker.add_player(self) - async def set_game(self, game): - print(f"({self.user}) Game set to: {game}") - self.game = game + async def join_tournament_waiting_room(self): + logger.info("Joining tournament waiting room") + await tournament_match_maker.add_player(self) + + async def disconnect(self, close_code): + try: + if self.game: + await self.game.end_game(disconnected_player=self) + await match_maker.remove_player(self) + await tournament_match_maker.remove_player(self) + logger.info(f"User {self.user.username if hasattr(self, 'user') else 'Unknown'} disconnected") + except Exception as e: + logger.error(f"Error during WebSocket disconnection: {str(e)}") + + async def set_game(self, game): + logger.info(f"Setting game: {game}") + self.game = game +###############################CHAT############################################ +class ChatConsumer(AsyncWebsocketConsumer): + groups = {} + + async def connect(self): + try: + # Récupérer le nom de la room + self.room_group_name = self.scope['url_route']['kwargs']['room_name'] + + # Accepter la connexion WebSocket + await self.accept() + + logger.info(f"Connexion au WebSocket de chat dans la room {self.room_group_name}") + + # Ajouter l'utilisateur au groupe en mémoire + if self.room_group_name not in self.groups: + self.groups[self.room_group_name] = [] + self.groups[self.room_group_name].append(self.channel_name) + + logger.info(f"User {self.channel_name} added to group {self.room_group_name}") + + except Exception as e: + logger.error(f"Erreur lors de la connexion WebSocket: {str(e)}") + + + async def disconnect(self, close_code): + try: + # Retirer l'utilisateur du groupe en mémoire + if self.room_group_name in self.groups: + self.groups[self.room_group_name].remove(self.channel_name) + if not self.groups[self.room_group_name]: + del self.groups[self.room_group_name] + + await self.send_group_message( + self.room_group_name, + { + 'type': 'chat_message', + 'message': f'{self.user.username if hasattr(self, "user") else "Unknown"} a quitté le chat', + 'username': self.user.username if hasattr(self, "user") else "Unknown", + 'room': self.room_group_name + } + ) + logger.info(f"{self.user.username if hasattr(self, 'user') else 'Unknown'} déconnecté du WebSocket de chat dans la room {self.room_group_name}") + except Exception as e: + logger.error(f"Erreur lors de la déconnexion WebSocket du chat: {str(e)}") + + async def receive(self, text_data): + try: + data = json.loads(text_data) + message_type = data.get('type') + username = data.get('username').strip().lower() # Normalisation du nom d'utilisateur + + # Log pour vérifier que le username est bien reçu + logger.info(f"Message reçu avec username: {username}") + if not username: + logger.error(f"Username missing in message: {data}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Username is missing'})) + return + + # Gestion des types de messages + if message_type == 'authenticate': + await self.authenticate(data.get('token'), username) + + elif message_type == 'chat_message': + if 'message' not in data: + logger.error(f"Format de message incorrect : {data}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Format de message incorrect'})) + return + + message = data['message'] + + # Envoyer le message à tous les autres utilisateurs de la room + await self.send_group_message( + self.room_group_name, + { + 'type': 'chat_message', + 'message': message, + 'username': username, + 'room': self.room_group_name + } + ) + + # Gestion de la commande /b pour bloquer un utilisateur + elif message_type == 'block_user': + target_user = data.get('target_user').strip().lower() + if target_user == username: + await self.send(text_data=json.dumps({'type': 'error', 'message': 'You cannot block yourself'})) + else: + await self.handle_block_user(data) + + # Gestion de la commande /i pour inviter un utilisateur + elif message_type == 'invite_user': + target_user = data.get('target_user').strip().lower() + if target_user == username: + await self.send(text_data=json.dumps({'type': 'error', 'message': 'You cannot invite yourself'})) + else: + await self.handle_invite_user(data) + + else: + logger.warning(f"Unhandled message type: {message_type}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Unhandled message type'})) + + except json.JSONDecodeError as e: + logger.error(f"Erreur de décodage JSON : {str(e)} - Données reçues : {text_data}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Format JSON invalide'})) + except Exception as e: + logger.error(f"Erreur lors de la réception du message du chat: {str(e)}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Erreur interne du serveur'})) + + async def handle_block_user(self, data): + username = data['username'].strip().lower() # Normalisation du nom d'utilisateur + target_user = data['target_user'].strip().lower() + + # Utiliser self.room_group_name pour vérifier que l'utilisateur ciblé est bien dans la bonne room + if target_user not in self.groups.get(self.room_group_name, []): + logger.error(f"Target user {target_user} does not exist in room {self.room_group_name}") + await self.send(text_data=json.dumps({'type': 'error', 'message': f'Target user {target_user} not found in room {self.room_group_name}'})) + return + + logger.info(f"Block request: {username} wants to block {target_user} in room {self.room_group_name}") + + if target_user == username: + logger.warning(f"Block attempt failed: {username} tried to block themselves") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'You cannot block yourself'})) + else: + logger.info(f"{username} successfully blocked {target_user}") + await self.send(text_data=json.dumps({'type': 'success', 'message': f'You have blocked {target_user} in room {self.room_group_name}'})) + + async def handle_invite_user(self, data): + username = data['username'].strip().lower() + target_user = data['target_user'].strip().lower() + + # Utiliser self.room_group_name pour inviter l'utilisateur dans la room active + room = self.room_group_name + + # Vérification que le joueur à inviter est dans la room + if target_user not in self.groups.get(room, []): + logger.error(f"Target user {target_user} does not exist in room {room}") + await self.send(text_data=json.dumps({'type': 'error', 'message': f'Target user {target_user} not found in room {room}'})) + return + + logger.info(f"Invitation request: {username} wants to invite {target_user} to a quick match in room {room}") + + if target_user == username: + logger.warning(f"Invite attempt failed: {username} tried to invite themselves") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'You cannot invite yourself'})) + else: + logger.info(f"{username} successfully sent an invitation to {target_user} in room {room}") + await self.send_group_message(room, { + 'type': 'invite', + 'message': f'{username} invited {target_user} to a quick match', + 'username': username, + 'target_user': target_user + }) + await self.send(text_data=json.dumps({'type': 'success', 'message': f'Invitation sent to {target_user} in room {room}'})) + + + async def authenticate(self, token, username): + if not token: + logger.error("Token is None, authentication cannot proceed") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Token is missing'})) + return + try: + user = await self.get_user_from_token(token) + if user: + self.user = user + logger.info(f"User {username} authenticated successfully with token: {token}") + + # Envoyer un message d'authentification réussie au client + await self.send(text_data=json.dumps({'type': 'authenticated', 'username': username})) + + # Envoyer le message de bienvenue après l'authentification réussie + await self.send_group_message( + self.room_group_name, + { + 'username': username, + 'room': self.room_group_name, + 'type': 'chat_message', + 'message': f' a rejoint le chat {self.room_group_name}', + } + ) + else: + logger.warning(f"Authentication failed for token: {token}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'})) + except Exception as e: + logger.error(f"Error during authentication: {str(e)}") + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Internal server error'})) + + @sync_to_async + def get_user_from_token(self, token): + try: + user = User.objects.filter(auth_token=token).first() + logger.debug(f"User found: {user} for token: {token}") + return user + except User.DoesNotExist: + logger.warning(f"User not found for token: {token}") + return None + + async def send_group_message(self, group_name, message): + # Utilisation de self.room_group_name pour s'assurer que la bonne room est utilisée + group_name = self.room_group_name # Utilisation explicite de self.room_group_name + + if group_name in self.groups: + logger.debug(f"Sending message to group {group_name}: {message}") + for channel_name in self.groups[group_name]: + try: + await self.channel_layer.send(channel_name, { + 'type': 'chat_message', + 'message': message['message'], + 'username': message['username'], + 'room': message['room'] + }) + logger.debug(f"Message sent to {channel_name} in room {message['room']}: {message}") + except Exception as e: + logger.error(f"Failed to send message to {channel_name} in room {group_name}: {str(e)}") + else: + logger.error(f"Group {group_name} does not exist, unable to send message") + + async def chat_message(self, event): + message = event['message'] + username = event.get('username', 'Anonyme') + room = event.get('room', 'unknown') + + # Log pour vérifier le username avant envoi + logger.info(f"Sending chat message from username: {username} in room: {room}") + + # Envoyer le message au WebSocket + await self.send(text_data=json.dumps({ + 'type': 'chat_message', + 'message': f'{username}: {message}', + 'room': room + })) diff --git a/pong/game/game.py b/pong/game/game.py index 42bfda6..515dccd 100644 --- a/pong/game/game.py +++ b/pong/game/game.py @@ -4,14 +4,11 @@ import json import asyncio import random from datetime import datetime -from .utils import handle_game_data, getlen +from .utils import handle_game_data from asgiref.sync import sync_to_async -from .models import Tournoi + class Game: - # Global variable to handle the using of the database - USING_DB = False - def __init__(self, game_id, player1, player2, localgame): self.game_id = game_id self.player1 = player1 @@ -31,10 +28,9 @@ class Game: 'game_text': '' } else: - # Set botgame to True if either player1 or player2 is None - self.botgame = player1 is None or player2 is None + self.botgame = player2 is None self.game_state = { - 'player1_name': player1.user.username if player1 else 'BOT', + 'player1_name': player1.user.username, 'player2_name': player2.user.username if player2 else 'BOT', 'player1_position': 150, 'player2_position': 150, @@ -51,64 +47,57 @@ class Game: self.p2_mov = 0 self.bt1 = 0 self.bt2 = 0 - self.start_time = datetime.now() - self.future_ball_position = {'x': 390, 'y': 190} + self.start_time = None async def start_game(self): - print(f"- Game #{self.game_id} STARTED ({self.game_state['player1_name']} vs {self.game_state['player2_name']}) --- ({self})") + print(f"- Game #{self.game_id} STARTED") self.game_loop_task = asyncio.create_task(self.game_loop()) + self.start_time = datetime.now() print(f" Begin MATCH at: {self.start_time}") async def game_loop(self): print(" In the game loop..") - x = 59 + x = 0 while not self.ended: if self.botgame: x += 1 if x == 60: - # Random BOT difficulty.. - steps = 60#random.randint(10, 60) - self.future_ball_position = await self.predict_ball_trajectory(steps) + await self.update_bot_position() x = 0 - if self.botgame: - await self.update_bot_position() await self.handle_pad_movement() await self.update_game_state() await self.send_game_state() await asyncio.sleep(1/60) # Around 60 FPS async def update_bot_position(self): - #future_ball_position = self.predict_ball_trajectory() - target_y = self.future_ball_position['y'] + future_ball_position = self.predict_ball_trajectory() + + target_y = future_ball_position['y'] player2_position = self.game_state['player2_position'] # Adjusts bot position based on expected ball position 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) + 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) + self.game_state['player2_position'] = max(player2_position - (50 * self.speed), 0) - async def predict_ball_trajectory(self, steps=60): + def predict_ball_trajectory(self, steps=60): + future_x = self.game_state['ball_position']['x'] future_y = self.game_state['ball_position']['y'] velocity_x = self.game_state['ball_velocity']['x'] velocity_y = self.game_state['ball_velocity']['y'] + for _ in range(steps): future_x += velocity_x - 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 <= 10 or future_y >= 390: - velocity_y = -velocity_y # Reverse the direction of vertical movement + future_y += velocity_y + + # Dealing with bounces off walls + if future_y <= 0 or future_y >= 300: + velocity_y = -velocity_y # Reverse the direction of vertical movement + return {'x': future_x, 'y': future_y} async def update_game_state(self): @@ -221,10 +210,10 @@ class Game: self.ended = True if self.game_loop_task: self.game_loop_task.cancel() - print(f"- Game #{self.game_id} ENDED --- ({self})") + print(f"- Game #{self.game_id} ENDED") end_time = datetime.now() - duration = (end_time - self.start_time).total_seconds() / 60 + duration = (end_time - self.start_time).total_seconds() / 60 # Notify that one player left the game if disconnected_player: @@ -246,17 +235,6 @@ class Game: if not self.botgame: if not self.localgame: await self.player2.send(end_message) - while (Game.USING_DB): - await asyncio.sleep(1) - Game.USING_DB = True - if hasattr(self, 'tournament'): - print(f"*** Game #{self.game_id} from tournament: {self.tournament.tournoi_reg.name} ENDED ***") - 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, self.tournament.tournoi_reg) - print(f"*** Game #{self.game_id} from tournament: {self.tournament.tournoi_reg.name} is REGISTERED ***") - else: - await sync_to_async(handle_game_data)(self.game_state['player1_name'], self.game_state['player2_name'], + 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) - Game.USING_DB = False diff --git a/pong/game/matchmaking.py b/pong/game/matchmaking.py index 6382b95..989a1dd 100644 --- a/pong/game/matchmaking.py +++ b/pong/game/matchmaking.py @@ -26,8 +26,7 @@ class MatchMaker: for game in self.active_games.values(): if player in [game.player1, game.player2]: await game.end_game(disconnected_player=player) - if game.game_id in self.active_games: - del self.active_games[game.game_id] + del self.active_games[game.game_id] break async def match_loop(self): @@ -103,5 +102,11 @@ 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/migrations/0001_initial.py:Zone.Identifier b/pong/game/migrations/0001_initial.py:Zone.Identifier new file mode 100644 index 0000000..e69de29 diff --git a/pong/game/migrations/0002_alter_match_winner.py b/pong/game/migrations/0002_alter_match_winner.py deleted file mode 100644 index fe0f120..0000000 --- a/pong/game/migrations/0002_alter_match_winner.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.0.7 on 2024-07-31 16:04 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('game', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='match', - name='winner', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='won_matches', to='game.player'), - ), - ] diff --git a/pong/game/migrations/0002_alter_match_winner.py:Zone.Identifier b/pong/game/migrations/0002_alter_match_winner.py:Zone.Identifier new file mode 100644 index 0000000..e69de29 diff --git a/pong/game/migrations/0003_alter_tournoi_date.py b/pong/game/migrations/0003_alter_tournoi_date.py deleted file mode 100644 index 6bfda6c..0000000 --- a/pong/game/migrations/0003_alter_tournoi_date.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.1 on 2024-09-10 14:17 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('game', '0002_alter_match_winner'), - ] - - operations = [ - migrations.AlterField( - model_name='tournoi', - name='date', - field=models.DateField(auto_now_add=True), - ), - ] diff --git a/pong/game/models.py b/pong/game/models.py index 4f63102..efdb580 100644 --- a/pong/game/models.py +++ b/pong/game/models.py @@ -1,9 +1,13 @@ -# /pong/game/models.py - from django.db import models from django.contrib.auth.models import User +from django.core.exceptions import ValidationError +import logging +logger = logging.getLogger(__name__) + +# Ajout d'un champ auth_token à la classe User User.add_to_class('auth_token', models.CharField(max_length=100, null=True, blank=True, unique=True)) +logger.debug("Ajout du champ auth_token à la classe User") class Player(models.Model): name = models.CharField(max_length=100) @@ -20,23 +24,33 @@ class Player(models.Model): num_won_tournaments = models.PositiveSmallIntegerField(default=0) def __str__(self): - return self.name + return self.name + + def save(self, *args, **kwargs): + logger.info(f"Saving player: {self.name}") + super().save(*args, **kwargs) + logger.info(f"Player {self.name} saved successfully") class Tournoi(models.Model): name = models.CharField(max_length=200) nbr_player = models.PositiveSmallIntegerField() - date = models.DateField(auto_now_add=True) + date = models.DateField() winner = models.ForeignKey('Player', on_delete=models.SET_NULL, null=True) def __str__(self): return self.name + def save(self, *args, **kwargs): + logger.info(f"Saving tournament: {self.name}") + super().save(*args, **kwargs) + logger.info(f"Tournament {self.name} saved successfully") + class Match(models.Model): player1 = models.ForeignKey('Player', related_name='match_as_player1', on_delete=models.CASCADE) player2 = models.ForeignKey('Player', related_name='match_as_player2', on_delete=models.CASCADE) score_player1 = models.PositiveSmallIntegerField() score_player2 = models.PositiveSmallIntegerField() - winner = models.ForeignKey('Player', related_name='won_matches',on_delete=models.CASCADE, null=True) + winner = models.ForeignKey('Player', related_name='won_matches', on_delete=models.CASCADE, null=True) nbr_ball_touch_p1 = models.PositiveIntegerField() nbr_ball_touch_p2 = models.PositiveIntegerField() duration = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) @@ -45,6 +59,7 @@ class Match(models.Model): tournoi = models.ForeignKey('Tournoi', related_name='matches', on_delete=models.SET_NULL, null=True) def clean(self): + logger.debug(f"Cleaning match: {self.player1.name} vs {self.player2.name}") if self.score_player1 < 0 or self.score_player2 < 0: raise ValidationError('Les scores doivent être positifs.') if self.score_player1 > self.score_player2 and self.winner != self.player1: @@ -54,8 +69,10 @@ class Match(models.Model): super().clean() def save(self, *args, **kwargs): + logger.info(f"Saving match: {self.player1.name} vs {self.player2.name}") self.clean() super().save(*args, **kwargs) + logger.info(f"Match {self.player1.name} vs {self.player2.name} saved successfully") def __str__(self): - return f"{self.player1.name} vs {self.player2.name}" \ No newline at end of file + return f"{self.player1.name} vs {self.player2.name}" diff --git a/pong/game/routing.py b/pong/game/routing.py index 8b421b9..3c631ff 100644 --- a/pong/game/routing.py +++ b/pong/game/routing.py @@ -1,8 +1,15 @@ # /pong/game/routing.py - from django.urls import re_path from . import consumers +import logging + +logger = logging.getLogger(__name__) + +logger.debug("Configuring WebSocket routing patterns") websocket_urlpatterns = [ - re_path(r'ws/game/$', consumers.GameConsumer.as_asgi()), + re_path(r'ws/game/$', consumers.GameConsumer.as_asgi(), name='game_ws'), + re_path(r'ws/chat/(?P\w+)/$', consumers.ChatConsumer.as_asgi()), ] + +logger.info("WebSocket routing patterns configured successfully") diff --git a/pong/game/templates/pong/tournament_brackets.html b/pong/game/templates/pong/tournament_brackets.html deleted file mode 100644 index ca65553..0000000 --- a/pong/game/templates/pong/tournament_brackets.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - Tournament Brackets - - - -
- {% for round in tournament_rounds %} -
- {% for match in round %} -
-
- {{ match.player1 }} -
-
- {{ match.player2|default:"BYE" }} -
-
- {% endfor %} -
- {% endfor %} -
- - \ No newline at end of file diff --git a/pong/game/templates/pong/tournament_waiting_room.html b/pong/game/templates/pong/tournament_waiting_room.html index cb77338..dc09859 100644 --- a/pong/game/templates/pong/tournament_waiting_room.html +++ b/pong/game/templates/pong/tournament_waiting_room.html @@ -1,20 +1,10 @@ - +

Tournament Waiting Room

- -

Players currently waiting: {{ players_count }}

-

Minimum players needed to start: {{ min_players_to_start }}

- -

Players:

+

Players currently waiting: {{ players|length }}

    - {% for player in players %} -
  • {{ player }}
  • - {% endfor %} + {% for player in players %} +
  • {{ player }}
  • + {% endfor %}
- - {% if players_count >= min_players_to_start %} - - {% else %} -

Waiting for more players to join...

- {% endif %}
\ No newline at end of file diff --git a/pong/game/tournament.py b/pong/game/tournament.py index c2903a1..4faeca0 100644 --- a/pong/game/tournament.py +++ b/pong/game/tournament.py @@ -1,224 +1,49 @@ # /pong/game/tournament.py - import json -import asyncio from django.template.loader import render_to_string -import random -from .matchmaking import match_maker -from .game import Game -from .models import Tournoi -from .utils import create_tournament, update_tournament, getlen -from asgiref.sync import sync_to_async +import logging -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 - # Store the tournament instance - self.tournament = tournament - - async def end_game(self, disconnected_player=None): - # Call the parent class's end_game method - 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] +logger = logging.getLogger(__name__) class TournamentMatchMaker: def __init__(self): self.waiting_players = [] - self.matches = [] - self.rounds = [] - self.current_round = 0 - self.games = 0 - self.tournament_state = "waiting" #Can be "waiting", "in_progress", or "ended" - self.name = random.choice(TOURNAMENT_NAMES) - self.final_name = "" - self.tournoi_reg = None + logger.debug("TournamentMatchMaker initialized with an empty waiting_players list") async def add_player(self, player): - if self.tournament_state == "waiting" and player not in self.waiting_players: + if player not in self.waiting_players: self.waiting_players.append(player) - if player: - print(f"User {player.user.username} joins the TOURNAMENT WAITING ROOM") - else: - print("BOT joins the TOURNAMENT WAITING ROOM") + logger.info(f"User {player.user.username} added to the TOURNAMENT WAITING ROOM") await self.update_waiting_room() - - async def update_waiting_room(self): - html = self.generate_waiting_room_html() - for player in self.waiting_players: - await self.send_to_player(player, { - 'type': 'update_tournament_waiting_room', - 'html': html - }) - - def generate_waiting_room_html(self): - context = { - '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': 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)) + else: + logger.warning(f"User {player.user.username} is already in the TOURNAMENT WAITING ROOM") async def remove_player(self, player): if player in self.waiting_players: self.waiting_players.remove(player) + logger.info(f"User {player.user.username} removed from the TOURNAMENT WAITING ROOM") await self.update_waiting_room() + else: + logger.warning(f"Attempt to remove non-existent user {player.user.username} from the TOURNAMENT WAITING ROOM") - # Tournament start method - async def start_tournament(self): - if len(self.waiting_players) < 3: - return False - random.shuffle(self.waiting_players) - '''if (len(self.waiting_players) % 2) != 0: - print("Adding a BYE to the tournament..") - await self.add_player(None)''' - self.tournament_state = "in_progress" - self.current_round = 0 - len_tournament = await sync_to_async(getlen)() - self.final_name = self.name + " #" + str(len_tournament + 1) - self.tournoi_reg = await sync_to_async(create_tournament)(self.final_name, len(self.waiting_players)) - 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") - 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): - # Create a new instance of TournamentMatch for this round - match = TournamentMatch(self.games, players[i], players[i + 1], self) - matches.append(match) - else: - # 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) - - async def update_brackets(self): - html = self.generate_bracket_html() + async def update_waiting_room(self): + logger.debug("Updating TOURNAMENT WAITING ROOM") + html = self.generate_waiting_room_html() for player in self.waiting_players: - await self.send_to_player(player, { - 'type': 'update_brackets', + await player.send(json.dumps({ + 'type': 'update_waiting_room', 'html': html - }) + })) + logger.info("TOURNAMENT WAITING ROOM updated and sent to all players") - def generate_bracket_html(self): + def generate_waiting_room_html(self): + logger.debug("Generating TOURNAMENT WAITING ROOM HTML") context = { - 'tournament_rounds': self.get_tournament_data() + 'players': [player.user.username for player in self.waiting_players] } - return render_to_string('pong/tournament_brackets.html', context) - - def get_tournament_data(self): - return [ - [ - { - 'player1': match.player1.user.username if match.player1 else 'BYE', - 'player2': match.player2.user.username if match.player2 else 'BYE', - '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'] - } - for match in round_matches - ] - for round_matches in self.rounds - ] - - 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 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() - await self.send_game_text(match.player1, "You lucky bastard! You got an auto-win!") - - async def send_game_text(self, player, text): - message = json.dumps({ - 'type': 'game_text_update', - 'game_text': text - }) - await player.send(message) - - 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_username - }) - - await sync_to_async(update_tournament)(self.final_name, winner_username) - # Reset tournament state - self.waiting_players = [] - self.matches = [] - self.rounds = [] - self.current_round = 0 - self.games = 0 - self.tournament_state = "waiting" - - async def handle_match_end(self, match): - await self.update_brackets() + html = render_to_string('pong/tournament_waiting_room.html', context) + logger.info("TOURNAMENT WAITING ROOM HTML generated successfully") + return html # Instance of the class -tournament_match_maker = TournamentMatchMaker() \ No newline at end of file +tournament_match_maker = TournamentMatchMaker() diff --git a/pong/game/urls.py b/pong/game/urls.py index df150f0..f4bf6e2 100644 --- a/pong/game/urls.py +++ b/pong/game/urls.py @@ -2,10 +2,9 @@ from django.urls import path, include from . import views -from rest_framework.routers import DefaultRouter +#from rest_framework.routers import DefaultRouter from .views import match_list_json, player_list_json, tournoi_list_json - urlpatterns = [ path('', views.index, name='index'), path('check_user_exists/', views.check_user_exists, name='check_user_exists'), diff --git a/pong/game/utils.py b/pong/game/utils.py index 0f193d1..6b0f565 100644 --- a/pong/game/utils.py +++ b/pong/game/utils.py @@ -1,3 +1,4 @@ +import logging from .models import Player, Tournoi, Match from django.core.exceptions import ValidationError from django.shortcuts import get_object_or_404 @@ -5,55 +6,62 @@ from django.db.models import Max, Sum, F from datetime import timedelta from channels.db import database_sync_to_async +logger = logging.getLogger(__name__) + def handle_game_data(p1, p2, s_p1, s_p2, bt_p1, bt_2, dur, is_tournoi, name_tournament): + logger.info(f"Handling game data for players: {p1} vs {p2}") try: player_1 = get_or_create_player(p1) player_2 = get_or_create_player(p2) + logger.debug(f"Players created or retrieved: {player_1}, {player_2}") - print("CHAKU & THEOUCHE are the BEST") create_match(player_1, player_2, s_p1, s_p2, bt_p1, bt_2, dur, is_tournoi, name_tournament) - print("and ADRIANO is the PEST") + logger.info("Match created successfully") update_player_statistics(p1) update_player_statistics(p2) - + logger.info("Player statistics updated") + except Exception as e: - print(f"Error in endfortheouche: {e}") + logger.error(f"Error in handle_game_data: {e}") def get_player_by_name(name): + logger.debug(f"Checking if player exists with name: {name}") exists = Player.objects.filter(name=name).exists() + logger.debug(f"Player exists: {exists}") return exists - def get_player(name): + logger.debug(f"Retrieving player with name: {name}") return Player.objects.get(name=name) - def get_or_create_player(name): + logger.info(f"Getting or creating player with name: {name}") player_exists = get_player_by_name(name) if not player_exists: player = create_player(name) + logger.info(f"Player created: {player}") return player else: player = get_player(name) - return player - + logger.info(f"Player retrieved: {player}") + return player def create_player( - name, - total_match=0, - total_win=0, - p_win= None, - m_score_match= None, - m_score_adv_match= None, - best_score=0, - m_nbr_ball_touch= None, - total_duration= None, - m_duration= None, - num_participated_tournaments=0, + name, + total_match=0, + total_win=0, + p_win=None, + m_score_match=None, + m_score_adv_match=None, + best_score=0, + m_nbr_ball_touch=None, + total_duration=None, + m_duration=None, + num_participated_tournaments=0, num_won_tournaments=0 ): - + logger.info(f"Creating player: {name}") player = Player( name=name, total_match=total_match, @@ -69,14 +77,18 @@ def create_player( num_won_tournaments=num_won_tournaments ) player.save() + logger.info(f"Player {name} saved successfully") return player def create_tournoi(name, nbr_player, date, winner): + logger.info(f"Creating tournament: {name}") tournoi = Tournoi(name=name, nbr_player=nbr_player, date=date, winner=winner) tournoi.save() + logger.info(f"Tournament {name} saved successfully") return tournoi def create_match(player1, player2, score_player1, score_player2, nbr_ball_touch_p1, nbr_ball_touch_p2, duration, is_tournoi, tournoi): + logger.info(f"Creating match between {player1.name} and {player2.name}") match = Match( player1=player1, player2=player2, @@ -95,20 +107,22 @@ def create_match(player1, player2, score_player1, score_player2, nbr_ball_touch_ match.winner = match.player2 else: match.winner = None - + match.save() + logger.info(f"Match saved successfully with winner: {match.winner.name if match.winner else 'None'}") return match def update_player_statistics(player_name): + logger.info(f"Updating statistics for player: {player_name}") player = get_object_or_404(Player, name=player_name) matches_as_player1 = Match.objects.filter(player1=player) matches_as_player2 = Match.objects.filter(player2=player) total_match = matches_as_player1.count() + matches_as_player2.count() - - # avoid dividing by 0 + if total_match == 0: + logger.warning(f"No matches found for player: {player_name}") player.total_match = total_match player.total_win = 0 player.p_win = 0 @@ -121,22 +135,23 @@ def update_player_statistics(player_name): player.num_participated_tournaments = 0 player.num_won_tournaments = 0 player.save() + logger.info(f"Player statistics reset for {player_name}") return - + won_matches = Match.objects.filter(winner=player) #part_tourn_as_p1 = Tournoi.objects.filter(matches__is_tournoi=True, matches__matches_as_player1=player) #part_tourn_as_p2 = Tournoi.objects.filter(matches__is_tournoi=True, matches__matches_as_player2=player) - #won_tourn = Tournoi.objects.filter(winner=player) + #won_tourn = Tournoi.objects.filter(winner=player) total_score = matches_as_player1.aggregate(Sum('score_player1'))['score_player1__sum'] or 0 total_score += matches_as_player2.aggregate(Sum('score_player2'))['score_player2__sum'] or 0 - + total_score_adv = matches_as_player1.aggregate(Sum('score_player2'))['score_player2__sum'] or 0 total_score_adv += matches_as_player2.aggregate(Sum('score_player1'))['score_player1__sum'] or 0 total_win = won_matches.count() p_win = (total_win / total_match) * 100 - + m_score_match = total_score / total_match m_score_adv_match = total_score_adv / total_match @@ -151,7 +166,7 @@ def update_player_statistics(player_name): #total_tourn_p = part_tourn_as_p1.count() + part_tourn_as_p2.count() #total_win_tourn = won_tourn.count() #p_win_tourn = (total_win_tourn / total_tourn_p) * 100 if total_tourn_p else 0 - + best_score_as_player1 = matches_as_player1.aggregate(Max('score_player1'))['score_player1__max'] or 0 best_score_as_player2 = matches_as_player2.aggregate(Max('score_player2'))['score_player2__max'] or 0 best_score = max(best_score_as_player1, best_score_as_player2) @@ -166,33 +181,12 @@ def update_player_statistics(player_name): player.total_duration = total_duration player.m_duration = m_duration # player.num_participated_tournaments = total_tourn_p - #player.num_won_tournaments = total_win_tourn + #player.num_won_tournaments = total_win_tourn player.save() + logger.info(f"Statistics updated for player: {player_name}") def get_player_p_win(player_name): + logger.debug(f"Getting win percentage for player: {player_name}") player = get_object_or_404(Player, name=player_name) return player.p_win - -def create_tournament(name, nbr_player): - print("tournoi created!!!") - tournoi=Tournoi(name=name, nbr_player=nbr_player, winner=None) - tournoi.save() - print(f"tournoi name : {tournoi.name} *******!*!*!*!**!*!**!*!*!*!*!*!*!*!*!*") - return tournoi - -def update_tournament(name_tournoi, winner_name): - tournoi = get_object_or_404(Tournoi, name=name_tournoi) - winner_p = get_object_or_404(Player, name=winner_name) - print(f"in update tourna - tournoi name : {tournoi.name} *******!*!*!*!**!*!**!*!*!*!*!*!*!*!*!*") - print(f"in update tourna - winner is : {winner_p.name} *******!*!*!*!**!*!**!*!*!*!*!*!*!*!*!*") - - tournoi.winner = winner_p - print(f"in update tourna - TOURNOI winner is : {tournoi.winner.name} *******!*!*!*!**!*!**!*!*!*!*!*!*!*!*!*") - tournoi.save() - - - - -def getlen(): - return Tournoi.objects.count() diff --git a/pong/game/views.py b/pong/game/views.py index 304cf1d..22d057a 100644 --- a/pong/game/views.py +++ b/pong/game/views.py @@ -9,65 +9,111 @@ from django.contrib.auth.models import User from django.contrib.auth import authenticate from django.views.decorators.csrf import csrf_exempt from rest_framework import viewsets +from django.db import transaction, IntegrityError import json import uuid +import logging + +logger = logging.getLogger(__name__) def index(request): + logger.info("Accessing index view") return render(request, 'index.html') @csrf_exempt def check_user_exists(request): if request.method == 'POST': + logger.info("POST request received for checking user existence") data = json.loads(request.body) username = data.get('username') if User.objects.filter(username=username).exists(): + logger.info(f"User {username} exists") return JsonResponse({'exists': True}) + logger.info(f"User {username} does not exist") return JsonResponse({'exists': False}) + logger.warning("Invalid request method for check_user_exists") return JsonResponse({'error': 'Invalid request method'}, status=400) @csrf_exempt def register_user(request): - if request.method == 'POST': - data = json.loads(request.body) - username = data.get('username') - password = data.get('password') - if not User.objects.filter(username=username).exists(): - user = User.objects.create_user(username=username, password=password) - token = get_or_create_token(user) - return JsonResponse({'registered': True, 'token': token}) - return JsonResponse({'registered': False, 'error': 'User already exists'}) - return JsonResponse({'error': 'Invalid request method'}, status=400) - -@csrf_exempt -def authenticate_user(request): if request.method == 'POST': try: + logger.info("Received POST request for user registration") data = json.loads(request.body) - username = data.get('username', '') - password = data.get('password', '') - user = authenticate(username=username, password=password) - if user is not None: + username = data.get('username') + password = data.get('password') + + logger.info(f"Attempting to register user: {username}") + + if not username or not password: + logger.warning("Username or password not provided") + return JsonResponse({'registered': False, 'error': 'Username and password are required'}, status=400) + + with transaction.atomic(): + user_exists = User.objects.select_for_update().filter(username=username).exists() + + if user_exists: + logger.warning(f"User {username} already exists") + return JsonResponse({'registered': False, 'error': 'User already exists'}, status=409) + + user = User.objects.create_user(username=username, password=password) token = get_or_create_token(user) - return JsonResponse({'authenticated': True, 'token': token, 'user_id': user.id}) - else: - return JsonResponse({'authenticated': False}, status=401) + logger.info(f"User {username} registered successfully") + return JsonResponse({'registered': True, 'token': token}) + + except json.JSONDecodeError as e: + logger.error(f"JSON decode error: {str(e)}") + return JsonResponse({'error': 'Invalid JSON data'}, status=400) + + except IntegrityError: + logger.error(f"IntegrityError: User {username} already exists") + return JsonResponse({'registered': False, 'error': 'User already exists'}, status=409) + except Exception as e: - return JsonResponse({'error': str(e)}, status=500) - else: - return JsonResponse({'error': 'Method not allowed'}, status=405) + logger.error(f"Error in register_user: {str(e)}") + return JsonResponse({'error': f'Internal Server Error: {str(e)}'}, status=500) + + logger.warning("Invalid request method for register_user") + return JsonResponse({'error': 'Invalid request method'}, status=405) def get_or_create_token(user): + logger.info(f"Generating or retrieving token for user: {user.username}") if not user.auth_token: while True: token = str(uuid.uuid4()) if not User.objects.filter(auth_token=token).exists(): user.auth_token = token user.save() + logger.info(f"Token generated for user {user.username}: {token}") break return user.auth_token +@csrf_exempt +def authenticate_user(request): + if request.method == 'POST': + try: + logger.info("Received POST request for user authentication") + data = json.loads(request.body) + username = data.get('username', '') + password = data.get('password', '') + user = authenticate(username=username, password=password) + if user is not None: + token = get_or_create_token(user) + logger.info(f"User {username} authenticated successfully") + return JsonResponse({'authenticated': True, 'token': token, 'user_id': user.id}) + else: + logger.warning(f"Authentication failed for user {username}") + return JsonResponse({'authenticated': False}, status=401) + except Exception as e: + logger.error(f"Error in authenticate_user: {str(e)}") + return JsonResponse({'error': str(e)}, status=500) + else: + logger.warning("Invalid request method for authenticate_user") + return JsonResponse({'error': 'Method not allowed'}, status=405) + def match_list_json(request): + logger.info("Fetching match list") matches = Match.objects.all() data = { 'matches': list(matches.values( @@ -76,11 +122,13 @@ def match_list_json(request): 'is_tournoi', 'tournoi__name' )) } + logger.info(f"Match list fetched successfully: {len(data['matches'])} matches found") return JsonResponse(data) def player_list_json(request): + logger.info("Fetching player list") players = Player.objects.all() - + data = { 'players': list(players.values( 'id', 'name', 'total_match', 'total_win', 'p_win', @@ -89,25 +137,30 @@ def player_list_json(request): 'num_participated_tournaments', 'num_won_tournaments' )) } + logger.info(f"Player list fetched successfully: {len(data['players'])} players found") return JsonResponse(data) -def get_tournoi_data(tournoi): - return { - "id": tournoi.id, - "name": tournoi.name, - "nbr_player": tournoi.nbr_player, - "date": tournoi.date, - "winner": { - "id": tournoi.winner.id, - "name": tournoi.winner.name - } if tournoi.winner else None +def tournoi_list_json(request): + logger.info("Fetching tournoi list") + tournois = Tournoi.objects.all() + + data = { + 'tournois': list(tournois.values( + 'id', 'name', 'nbr_player', 'date', 'winner' + )) } + logger.info(f"Tournoi list fetched successfully: {len(data['tournois'])} tournois found") + return JsonResponse(data) def tournoi_list_json(request): - tournois = Tournoi.objects.select_related('winner').all() # Charge les données du gagnant - tournois_data = [get_tournoi_data(tournoi) for tournoi in tournois] - return JsonResponse({"tournois": tournois_data}) + tournois = Tournoi.objects.all() + data = { + 'tournois': list(tournois.values( + 'id', 'name', 'nbr_player', 'date', 'winner' + )) + } + return JsonResponse(data) from web3 import Web3 @@ -145,7 +198,7 @@ def read_data(request): # print(f"Final Order: {', '.join(tournament[5])}") print("-----------------------------") return JsonResponse(json_data, safe=False) - + def write_data(request): # addTournament(string,uint256,uint256,string[],string[]) @@ -173,4 +226,3 @@ def write_data(request): # tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash) # print("Transaction receipt:", tx_receipt) print("-----------------------------") - diff --git a/pong/settings.py b/pong/settings.py index 446ec08..af52863 100644 --- a/pong/settings.py +++ b/pong/settings.py @@ -29,43 +29,43 @@ 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' + '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', + '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', - ], - }, - }, + { + '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' @@ -74,32 +74,32 @@ ASGI_APPLICATION = 'pong.asgi.application' # 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', - } + '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', - }, + { + '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 @@ -115,10 +115,6 @@ 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') @@ -130,43 +126,87 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # Channels # Define the channel layers for WebSockets +# Define the channel layers for WebSockets CHANNEL_LAYERS = { - 'default': { - 'BACKEND': 'channels.layers.InMemoryChannelLayer', - }, + '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 - }, - }, -}''' +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'json': { + '()': 'pythonjsonlogger.jsonlogger.JsonFormatter', + 'format': '%(asctime)s %(levelname)s %(name)s %(module)s %(process)d %(thread)d %(message)s', + }, + 'default': { + 'format': '[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s', + }, + 'verbose': { + 'format': '[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s [%(process)d:%(thread)d]', + }, + 'simple': { + 'format': '%(levelname)s %(message)s', + }, + }, + 'filters': { + 'require_debug_true': { + '()': 'django.utils.log.RequireDebugTrue', + }, + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse', + }, + }, + 'handlers': { + 'file': { + 'level': 'INFO', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(BASE_DIR, 'logs/django.log'), + 'formatter': 'json', + 'maxBytes': 1024 * 1024 * 5, # 5 MB + 'backupCount': 5, + }, + 'error_file': { + 'level': 'ERROR', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(BASE_DIR, 'logs/django_errors.log'), + 'formatter': 'json', + 'maxBytes': 1024 * 1024 * 5, # 5 MB + 'backupCount': 5, + }, + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'verbose', + 'filters': ['require_debug_true'], + }, + 'mail_admins': { + 'level': 'ERROR', + 'class': 'django.utils.log.AdminEmailHandler', + 'filters': ['require_debug_false'], + }, + }, + 'loggers': { + 'django': { + 'handlers': ['file', 'console', 'mail_admins'], + 'level': 'DEBUG', + 'propagate': True, + }, + 'django.request': { + 'handlers': ['error_file', 'mail_admins'], + 'level': 'ERROR', + 'propagate': False, + }, + 'django.security': { + 'handlers': ['error_file', 'mail_admins'], + 'level': 'ERROR', + 'propagate': False, + }, + }, + 'root': { + 'handlers': ['console', 'file'], + 'level': 'INFO', + }, +} diff --git a/pong/static/burger.js b/pong/static/burger.js index ca45088..a98343c 100644 --- a/pong/static/burger.js +++ b/pong/static/burger.js @@ -103,7 +103,6 @@ document.addEventListener('DOMContentLoaded', () => { console.log('Displaying matches:'); const matchListBody = document.querySelector('#match-list tbody'); matchListBody.innerHTML = ''; - if (matches.length != 0) { matches.forEach(match => { @@ -125,7 +124,6 @@ document.addEventListener('DOMContentLoaded', () => { matchListBody.appendChild(row); }); } else { - const row = document.createElement('tr'); row.innerHTML = ` No matches found. `; @@ -137,7 +135,6 @@ document.addEventListener('DOMContentLoaded', () => { console.log('Displaying players:'); const playersListBody = document.querySelector('#player-list tbody'); playersListBody.innerHTML = ''; - if (players.length != 0) { players.forEach(player => { @@ -160,9 +157,8 @@ document.addEventListener('DOMContentLoaded', () => { playersListBody.appendChild(row); }); } else { - const row = document.createElement('tr'); row.innerHTML = ` - No player found. + No matches found. ` playersListBody.appendChild(row); } @@ -175,22 +171,19 @@ document.addEventListener('DOMContentLoaded', () => { if (tournois.length != 0) { tournois.forEach(tournoi => { - console.log('Winner:', tournoi.winner); //debug !!!!!!!!!!!!!!!!! - console.log('Winner:', tournoi.winner__name); //debug !!!!!!!!!!!!!!!!! const row = document.createElement('tr'); row.innerHTML = ` ${tournoi.id} ${tournoi.name} ${tournoi.nbr_player} ${tournoi.date} - ${tournoi.winner ? tournoi.winner.name : 'No one yet ...'} + ${tournoi.winner.name} `; tournoisListBody.appendChild(row); }); } else { - const row = document.createElement('tr'); row.innerHTML = ` - No tournoi found. + No matches found. ` tournoisListBody.appendChild(row); } diff --git a/pong/static/flags/de.svg b/pong/static/flags/de.svg old mode 100755 new mode 100644 diff --git a/pong/static/flags/es.svg b/pong/static/flags/es.svg old mode 100755 new mode 100644 diff --git a/pong/static/flags/fr.svg b/pong/static/flags/fr.svg old mode 100755 new mode 100644 diff --git a/pong/static/flags/it.svg b/pong/static/flags/it.svg old mode 100755 new mode 100644 diff --git a/pong/static/flags/us.svg b/pong/static/flags/us.svg old mode 100755 new mode 100644 diff --git a/pong/static/game.js b/pong/static/game.js index b5a0b9d..20d291b 100644 --- a/pong/static/game.js +++ b/pong/static/game.js @@ -1,73 +1,91 @@ document.addEventListener('DOMContentLoaded', () => { - const formBlock = document.getElementById('block-form'); + console.log("DOM fully loaded and parsed"); + const formBlock = document.getElementById('block-form'); - const authForm = document.getElementById('auth-form'); - const nicknameInput = document.getElementById('nickname'); - const checkNicknameButton = document.getElementById('check-nickname'); + const authForm = document.getElementById('auth-form'); + const nicknameInput = document.getElementById('nickname'); + const checkNicknameButton = document.getElementById('check-nickname'); - const registerForm = document.getElementById('register-form'); - const passwordInput = document.getElementById('password'); - const confirmPasswordInput = document.getElementById('confirm-password'); - const registerButton = document.getElementById('register'); + const registerForm = document.getElementById('register-form'); + const passwordInput = document.getElementById('password'); + const confirmPasswordInput = document.getElementById('confirm-password'); + const registerButton = document.getElementById('register'); - const loginForm = document.getElementById('login-form'); - const loginPasswordInput = document.getElementById('login-password'); - const loginButton = document.getElementById('login'); + const loginForm = document.getElementById('login-form'); + const loginPasswordInput = document.getElementById('login-password'); + const loginButton = document.getElementById('login'); - const authForm2 = document.getElementById('auth-form2'); - const nicknameInput2 = document.getElementById('nickname2'); - const checkNicknameButton2 = document.getElementById('check-nickname2'); + const authForm2 = document.getElementById('auth-form2'); + const nicknameInput2 = document.getElementById('nickname2'); + const checkNicknameButton2 = document.getElementById('check-nickname2'); - const registerForm2 = document.getElementById('register-form2'); - const passwordInput2 = document.getElementById('password2'); - const confirmPasswordInput2 = document.getElementById('confirm-password2'); - const registerButton2 = document.getElementById('register2'); + const registerForm2 = document.getElementById('register-form2'); + const passwordInput2 = document.getElementById('password2'); + const confirmPasswordInput2 = document.getElementById('confirm-password2'); + const registerButton2 = document.getElementById('register2'); - const loginForm2 = document.getElementById('login-form2'); - const loginPasswordInput2 = document.getElementById('login-password2'); - const loginButton2 = document.getElementById('login2'); + const loginForm2 = document.getElementById('login-form2'); + const loginPasswordInput2 = document.getElementById('login-password2'); + const loginButton2 = document.getElementById('login2'); - const gameContainer = document.getElementById('game1'); - const tournamentContainer = document.getElementById('tournament-bracket'); + const gameContainer = document.getElementById('game1'); + const tournamentContainer = document.getElementById('tournament-bracket'); - const pongElements = document.getElementById('pong-elements'); - const logo = document.querySelector('.logo'); + const pongElements = document.getElementById('pong-elements'); + const logo = document.querySelector('.logo'); - const postFormButtons = document.getElementById('post-form-buttons'); - const localGameButton = document.getElementById('local-game'); - const quickMatchButton = document.getElementById('quick-match'); - const tournamentButton = document.getElementById('tournament'); + const localGameButton = document.getElementById('local-game'); + const quickMatchButton = document.getElementById('quick-match'); + const tournamentButton = document.getElementById('tournament'); - let socket; - let token; - let gameState; - let saveData = null; + let socket; + let token; + let gameState; + let activeRoom = null; // Stocker la room active + let roomSockets = {}; // Stocker les connexions WebSocket par room + let username; // Ajouter cette variable pour stocker le nom d'utilisateur - // Auto-focus and key handling for AUTH-FORM - nicknameInput.focus(); - nicknameInput.addEventListener('keypress', function (event) { - if (event.key === 'Enter') { - event.preventDefault(); - checkNicknameButton.click(); - } - }); + console.log("DOM elements initialized"); - checkNicknameButton.addEventListener('click', handleCheckNickname); - registerButton.addEventListener('click', handleRegister); - loginButton.addEventListener('click', handleLogin); + // Auto-focus and key handling for AUTH-FORM + nicknameInput.focus(); + nicknameInput.addEventListener('keypress', function (event) { + if (event.key === 'Enter') { + event.preventDefault(); + handleCheckNickname(); // Appeler directement la fonction au lieu de simuler un clic + } + }); - checkNicknameButton2.addEventListener('click', handleCheckNickname2); - registerButton2.addEventListener('click', handleRegister2); - loginButton2.addEventListener('click', handleLogin2); + checkNicknameButton.addEventListener('click', handleCheckNickname); + registerButton.addEventListener('click', handleRegister); + loginButton.addEventListener('click', handleLogin); - localGameButton.addEventListener('click', startLocalGame); - quickMatchButton.addEventListener('click', startQuickMatch); - tournamentButton.addEventListener('click', startTournament); + checkNicknameButton2.addEventListener('click', handleCheckNickname2); + registerButton2.addEventListener('click', handleRegister2); + loginButton2.addEventListener('click', handleLogin2); - async function handleCheckNickname() { + localGameButton.addEventListener('click', startLocalGame); + quickMatchButton.addEventListener('click', startQuickMatch); + tournamentButton.addEventListener('click', startTournament); + + console.log("Event listeners added"); + + // Function to get the CSRF token from cookies + function getCSRFToken() { + let csrfToken = null; + const cookies = document.cookie.split(';'); + cookies.forEach(cookie => { + const [name, value] = cookie.trim().split('='); + if (name === 'csrftoken') { + csrfToken = value; + } + }); + return csrfToken; + } + + async function handleCheckNickname() { const nickname = nicknameInput.value.trim(); if (nickname) { - window.firstPlayerName = nickname; try { const exists = await checkUserExists(nickname); if (exists) { @@ -104,421 +122,874 @@ document.addEventListener('DOMContentLoaded', () => { } } - async function checkUserExists(username) { - const response = await fetch('/check_user_exists/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ username }) - }); - const data = await response.json(); - return data.exists; - } + async function handleRegister() { + console.log("handleRegister called"); + const nickname = nicknameInput.value.trim(); + const password = passwordInput.value.trim(); + const confirmPassword = confirmPasswordInput.value.trim(); - async function handleRegister() { - const nickname = nicknameInput.value.trim(); - const password = passwordInput.value.trim(); - const confirmPassword = confirmPasswordInput.value.trim(); + // Validation du nom d'utilisateur + if (!nickname || nickname.length < 3) { + console.error("Invalid username. It must be at least 3 characters long."); + alert("Invalid username. It must be at least 3 characters long."); + return; + } - if (password === confirmPassword) { - try { - const result = await registerUser(nickname, password); - if (result) { - registerForm.style.display = 'none'; - document.getElementById("post-form-buttons").style.display = 'block'; - } else { - alert('Registration failed. Please try again.'); - } - } catch (error) { - console.error('Error registering user:', error); - } - } else { - alert('Passwords do not match.'); - } - } + // Validation des mots de passe + if (password !== confirmPassword) { + alert('Passwords do not match.'); + console.error('Passwords do not match.'); + return; + } - async function registerUser(username, password) { - const response = await fetch('/register_user/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ username, password }) - }); - const data = await response.json(); - if (data.registered) { - token = data.token; - } - return data.registered; - } + // Tentative d'inscription + try { + console.log("Attempting to register user:", nickname); + const result = await registerUser(nickname, password); - async function handleLogin() { - const nickname = nicknameInput.value.trim(); - const password = loginPasswordInput.value.trim(); - try { - const result = await authenticateUser(nickname, password); - if (result) { - loginForm.style.display = 'none'; - document.getElementById("post-form-buttons").style.display = 'block'; - } else { - alert('Authentication failed. Please try again.'); - } - } catch (error) { - console.error('Error authenticating user:', error); - } - } + // Vérification du résultat de l'inscription + if (result && result.token) { + token = result.token; // Stocker le token de l'utilisateur + console.log("Token stored successfully:", token); + console.log("User registered successfully"); - async function authenticateUser(username, password) { - const response = await fetch('/authenticate_user/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ username, password }) - }); - const data = await response.json(); - if (data.authenticated) { - token = data.token; - } - return data.authenticated; - } + // Mise à jour de l'interface après inscription réussie + registerForm.style.display = 'none'; + document.getElementById("post-form-buttons").style.display = 'block'; + username = nickname; // Stocker le nom d'utilisateur - async function handleCheckNickname2() { - const nickname2 = nicknameInput2.value.trim(); - if (nickname2) { - try { - const exists = await checkUserExists2(nickname2); - if (exists) { - authForm2.style.display = 'none'; - loginForm2.style.display = 'block'; - loginPasswordInput2.focus(); - loginPasswordInput2.addEventListener('keypress', function (event) { - if (event.key === 'Enter') { - event.preventDefault(); - loginButton2.click(); - } - }); - } else { - authForm2.style.display = 'none'; - registerForm2.style.display = 'block'; - passwordInput2.focus(); - passwordInput2.addEventListener('keypress', function (event) { - if (event.key === 'Enter') { - confirmPasswordInput2.focus(); - confirmPasswordInput2.addEventListener('keypress', function (event) { - if (event.key === 'Enter') { - event.preventDefault(); - registerButton2.click(); - } - }); - } - }); - } - } catch (error) { - console.error('Error checking user existence:', error); - } - } else { - alert('Please enter a nickname.'); - } - } + // Définir le nom de la room principale + roomName = "main_room"; - async function checkUserExists2(username) { - const response = await fetch('/check_user_exists/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ username }) - }); - const data = await response.json(); - return data.exists; - } + // Sécuriser l'appel à `joinRoom` (initialiser le chat WebSocket) + if (token && roomName) { + console.log(`Joining room: ${roomName} with token: ${token}`); + joinRoom(token, roomName, username); // Initialiser le chat WebSocket + } else { + console.error("Token or roomName is undefined. Cannot join room."); + alert("Error joining the chat room. Please try again."); + } - async function handleRegister2() { - const nickname2 = nicknameInput2.value.trim(); - const password2 = passwordInput2.value.trim(); - const confirmPassword2 = confirmPasswordInput2.value.trim(); + } else { + console.error('Registration failed. Invalid response from server.'); + alert('Registration failed. Please try again.'); + } - if (password2 === confirmPassword2) { - try { - const result = await registerUser2(nickname2, password2); - if (result) { - registerForm2.style.display = 'none'; - startLocalGame2(); - } else { - alert('Registration failed. Please try again.'); - } - } catch (error) { - console.error('Error registering user:', error); - } - } else { - alert('Passwords do not match.'); - } - } + } catch (error) { + console.error('Error registering user:', error); + alert('An error occurred during registration. Please try again.'); + } + } - async function registerUser2(username, password) { - const response = await fetch('/register_user/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ username, password }) - }); - const data = await response.json(); - if (data.registered) { - token2 = data.token; - } - return data.registered; - } - async function handleLogin2() { - const nickname2 = nicknameInput2.value.trim(); - const password2 = loginPasswordInput2.value.trim(); - try { - const result = await authenticateUser2(nickname2, password2); - if (result) { - loginForm2.style.display = 'none'; - startLocalGame2(); - } else { - alert('Authentication failed. Please try again.'); - } - } catch (error) { - console.error('Error authenticating user:', error); - } - } + async function checkUserExists(username) { + console.log("checkUserExists called with username:", username); + try { + const response = await fetch('/check_user_exists/', { + method: 'POST', + headers: { + 'X-CSRFToken': getCSRFToken(), + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username }) + }); - async function authenticateUser2(username, password) { - const response = await fetch('/authenticate_user/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ username, password }) - }); - const data = await response.json(); - if (data.authenticated) { - token2 = data.token; - } - return data.authenticated; - } + if (!response.ok) { + console.error(`HTTP error! Status: ${response.status}`); + return false; + } - function startLocalGame() { - console.log("starting a Local Game.."); - document.getElementById("post-form-buttons").style.display = 'none'; - authForm2.style.display = 'block'; - nicknameInput2.focus(); - nicknameInput2.addEventListener('keypress', function (event) { - if (event.key === 'Enter') { - event.preventDefault(); - checkNicknameButton2.click(); - } - }); - } + const data = await response.json(); + console.log("User existence check response:", data); + return data.exists; + } catch (error) { + console.error('Error during user existence check:', error); + return false; + } + } - function startLocalGame2() { - nickname = nicknameInput.value.trim(); - nickname2 = nicknameInput2.value.trim(); - saveData = { - type: 'local', - player1_name: nickname, - player2_name: nickname2 - }; - gameContainer.style.display = 'flex'; - logo.style.display = 'none'; - pongElements.style.display = 'none'; - formBlock.style.display = 'none'; - startWebSocketConnection(token, 2); - } + async function handleRegister() { + console.log("handleRegister called"); + const nickname = nicknameInput.value.trim(); + const password = passwordInput.value.trim(); + const confirmPassword = confirmPasswordInput.value.trim(); - function startQuickMatch() { - saveData = { - type: 'quick' - } - gameContainer.style.display = 'flex'; - logo.style.display = 'none'; - pongElements.style.display = 'none'; - formBlock.style.display = 'none'; - document.getElementById('player1-name').textContent = "player 1"; - document.getElementById('player2-name').textContent = "player 2"; - document.getElementById('game-text').textContent = ""; - document.getElementById('player1-score').textContent = 0; - document.getElementById('player2-score').textContent = 0; + if (!nickname || nickname.length < 3) { + console.error("Invalid username. It must be at least 3 characters long."); + alert("Invalid username. It must be at least 3 characters long."); + return; + } + + if (password === confirmPassword) { + try { + console.log("Attempting to register user:", nickname); + const result = await registerUser(nickname, password); + if (result) { + token = result; // Assurez-vous que le token est bien stocké ici + console.log("Token stored:", token); + console.log("User registered successfully"); + registerForm.style.display = 'none'; + document.getElementById("post-form-buttons").style.display = 'block'; + username = nickname; // Stocker le nom d'utilisateur après l'inscription + roomName = "main_room"; // Nom de la room principale + joinRoom(token,roomName, username); // Initialiser le chat WebSocket + } else { + console.error('Registration failed.'); + alert('Registration failed. Please try again.'); + } + } catch (error) { + console.error('Error registering user:', error); + } + } else { + alert('Passwords do not match.'); + console.error('Passwords do not match.'); + } + } + + async function registerUser(username, password) { + console.log("registerUser called with username:", username); + try { + const response = await fetch('/register_user/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username, password }) + }); + + if (!response.ok) { + console.error(`HTTP error! Status: ${response.status}`); + return null; // Retournez null en cas d'erreur HTTP + } + + let data; + try { + data = await response.json(); + } catch (error) { + console.error('Invalid JSON response:', error); + return null; + } + + console.log("Registration response data:", data); + if (data.registered) { + console.log('User registered successfully:', data); + return data.token; + } else { + console.error('Registration failed:', data.error); + return null; // Retournez null si l'enregistrement échoue + } + } catch (error) { + console.error('Error during registration request:', error); + return null; + } + } + + async function handleLogin() { + console.log("handleLogin called"); + const nickname = nicknameInput.value.trim(); + const password = loginPasswordInput.value.trim(); + try { + console.log("Attempting to authenticate user:", nickname); + const result = await authenticateUser(nickname, password); + if (result) { + console.log("User authenticated successfully"); + loginForm.style.display = 'none'; + document.getElementById("post-form-buttons").style.display = 'block'; + } else { + console.error('Authentication failed.'); + alert('Authentication failed. Please try again.'); + } + } catch (error) { + console.error('Error authenticating user:', error); + } + } + + async function authenticateUser(username, password) { + console.log("authenticateUser called with username:", username); + try { + const response = await fetch('/authenticate_user/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username, password }) + }); + + if (!response.ok) { + console.error(`HTTP error! Status: ${response.status}`); + return false; + } + + const data = await response.json(); + console.log("Authentication response data:", data); + if (data.authenticated) { + token = data.token; + } + return data.authenticated; + } catch (error) { + console.error('Error during authentication request:', error); + return false; + } + } + + + async function handleCheckNickname2() { + const nickname2 = nicknameInput2.value.trim(); + if (nickname2) { + try { + const exists = await checkUserExists2(nickname2); + if (exists) { + authForm2.style.display = 'none'; + loginForm2.style.display = 'block'; + loginPasswordInput2.focus(); + loginPasswordInput2.addEventListener('keypress', function (event) { + if (event.key === 'Enter') { + event.preventDefault(); + loginButton2.click(); + } + }); + } else { + authForm2.style.display = 'none'; + registerForm2.style.display = 'block'; + passwordInput2.focus(); + passwordInput2.addEventListener('keypress', function (event) { + if (event.key === 'Enter') { + confirmPasswordInput2.focus(); + confirmPasswordInput2.addEventListener('keypress', function (event) { + if (event.key === 'Enter') { + event.preventDefault(); + registerButton2.click(); + } + }); + } + }); + } + } catch (error) { + console.error('Error checking user existence:', error); + } + } else { + alert('Please enter a nickname.'); + } + } + + async function checkUserExists2(username) { + console.log("checkUserExists2 called with username:", username); + try { + const response = await fetch('/check_user_exists/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username }) + }); + + if (!response.ok) { + console.error(`HTTP error! Status: ${response.status}`); + return false; + } + + const data = await response.json(); + console.log("User existence check response (checkUserExists2):", data); + return data.exists; + } catch (error) { + console.error('Error during user existence check (checkUserExists2):', error); + return false; + } + } + + async function handleRegister2() { + console.log("handleRegister2 called"); + const nickname2 = nicknameInput2.value.trim(); + const password2 = passwordInput2.value.trim(); + const confirmPassword2 = confirmPasswordInput2.value.trim(); + + if (!nickname2 || nickname2.length < 3) { + console.error("Invalid username (handleRegister2). It must be at least 3 characters long."); + alert("Invalid username. It must be at least 3 characters long."); + return; + } + + if (password2 === confirmPassword2) { + try { + console.log("Attempting to register user (handleRegister2):", nickname2); + const result = await registerUser2(nickname2, password2); + if (result) { + console.log("User registered successfully (handleRegister2)"); + registerForm2.style.display = 'none'; + startLocalGame2(); + } else { + console.error('Registration failed (handleRegister2).'); + alert('Registration failed. Please try again.'); + } + } catch (error) { + console.error('Error registering user (handleRegister2):', error); + } + } else { + alert('Passwords do not match.'); + console.error('Passwords do not match (handleRegister2).'); + } + } + + async function registerUser2(username, password) { + console.log("registerUser2 called with username:", username); + try { + const response = await fetch('/register_user/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username, password }) + }); + + if (!response.ok) { + console.error(`HTTP error (registerUser2)! Status: ${response.status}`); + return null; + } + + let data; + try { + data = await response.json(); + } catch (error) { + console.error('Invalid JSON response (registerUser2):', error); + return null; + } + + console.log("Registration response data (registerUser2):", data); + if (data.registered) { + console.log('User registered successfully (registerUser2):', data); + return data.token; + } else { + console.error('Registration failed (registerUser2):', data.error); + return null; + } + } catch (error) { + console.error('Error during registration request (registerUser2):', error); + return null; + } + } + + async function handleLogin2() { + console.log("handleLogin2 called"); + const nickname2 = nicknameInput2.value.trim(); + const password2 = loginPasswordInput2.value.trim(); + try { + console.log("Attempting to authenticate user (handleLogin2):", nickname2); + const result = await authenticateUser2(nickname2, password2); + if (result) { + console.log("User authenticated successfully (handleLogin2)"); + loginForm2.style.display = 'none'; + startLocalGame2(); + } else { + console.error('Authentication failed (handleLogin2).'); + alert('Authentication failed. Please try again.'); + } + } catch (error) { + console.error('Error authenticating user (handleLogin2):', error); + } + } + + async function authenticateUser2(username, password) { + console.log("authenticateUser2 called with username:", username); + try { + const response = await fetch('/authenticate_user/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username, password }) + }); + + if (!response.ok) { + console.error(`HTTP error (authenticateUser2)! Status: ${response.status}`); + return false; + } + + const data = await response.json(); + console.log("Authentication response data (authenticateUser2):", data); + if (data.authenticated) { + token2 = data.token; + } + return data.authenticated; + } catch (error) { + console.error('Error during authentication request (authenticateUser2):', error); + return false; + } + } + + function startLocalGame() { + console.log("Starting a Local Game..."); + + document.getElementById("post-form-buttons").style.display = 'none'; + console.log("Hid post-form-buttons."); + + authForm2.style.display = 'block'; + console.log("Displayed authForm2."); + + nicknameInput2.focus(); + console.log("Focused on nicknameInput2."); + + nicknameInput2.addEventListener('keypress', function (event) { + if (event.key === 'Enter') { + event.preventDefault(); + console.log("Enter key pressed on nicknameInput2."); + checkNicknameButton2.click(); + } + }); + } + + function startLocalGame2() { + console.log("Starting the local game (2 players)..."); + + // Display the game container + gameContainer.style.display = 'flex'; + console.log("Game container set to 'flex'"); + + // Hide the logo, pong elements, and form block + logo.style.display = 'none'; + console.log("Logo hidden"); + + pongElements.style.display = 'none'; + console.log("Pong elements hidden"); + + formBlock.style.display = 'none'; + console.log("Form block hidden"); + + // Log the token before starting the WebSocket connection + console.log("Token before WebSocket authentication:", token); + + // Check if token is defined + if (!token) { + console.error("Token is not defined or is null."); + return; + } + + // Start the WebSocket connection + startWebSocketConnection(token, 2); + console.log("WebSocket connection initiated for a local game."); + } + + function startQuickMatch() { + // Masquer les éléments inutiles et afficher le conteneur de jeu + gameContainer.style.display = 'flex'; + logo.style.display = 'none'; + pongElements.style.display = 'none'; + formBlock.style.display = 'none'; + + // Log pour vérifier le token avant l'authentification WebSocket + console.log("Token before WebSocket authentication:", token); + + if (!token) { + console.error("Token is not defined or is null. WebSocket connection aborted."); + return; + } + + // Vérification si une connexion WebSocket est déjà active avant d'initialiser + if (roomSockets["quick_match"] && roomSockets["quick_match"].readyState === WebSocket.OPEN) { + console.warn("WebSocket for quick_match already open."); + return; + } + // Rejoindre la room "quick_match" + roomName = 'quick_match'; + joinRoom(token, roomName, username); + console.log("Starting WebSocket connection for quick match..."); + startWebSocketConnection(token, 1); // Le "1" pourrait être un identifiant pour le mode Quick Match + } - startWebSocketConnection(token, 1); - } function startTournament() { - saveData = { - type: 'tournoi' - } - tournamentContainer.style.display = 'flex'; - logo.style.display = 'none'; - pongElements.style.display = 'none'; - formBlock.style.display = 'none'; - startWebSocketConnection(token, 42); - } + // Masquer les éléments inutiles et afficher le conteneur du tournoi + tournamentContainer.style.display = 'flex'; + logo.style.display = 'none'; + pongElements.style.display = 'none'; + formBlock.style.display = 'none'; - function startWebSocketConnection(token, players) { - socket = new WebSocket(`ws://${window.location.host}/ws/game/`); + // Log pour vérifier le token avant l'authentification WebSocket + console.log("Token before WebSocket authentication:", token); - socket.onopen = function (event) { - console.log('WebSocket connection established'); - if (players === 1) { - console.log("Sending token for a quick match game"); - socket.send(JSON.stringify({ type: 'authenticate', token: token })); - } else if (players === 2) { - console.log("Sending tokens for a local game"); - socket.send(JSON.stringify({ type: 'authenticate2', token_1: token, token_2: token2 })); - } else { - console.log("Sending token for a tournament game") - socket.send(JSON.stringify({ type: 'authenticate3', token: token })); - } - }; + if (!token) { + console.error("Token is not defined or is null. WebSocket connection aborted."); + return; + } - socket.onmessage = function (event) { - const data = JSON.parse(event.data); - if (data.type === 'authenticated') { - console.log('Authentication successful'); - } else if (data.type === 'waiting_room') { - console.log('Entered the WAITING ROOM'); - } else if (data.type === 'game_start') { - console.log('Game started:', data.game_id, '(', data.player1, 'vs', data.player2, ')'); - gameContainer.style.display = 'flex'; - document.addEventListener('keydown', handleKeyDown); - } else if (data.type === 'game_state_update') { - updateGameState(data.game_state); - } else if (data.type === 'game_text_update') { - updateGameText(data.game_text); - } else if (data.type === 'player_disconnected') { - console.log('Player disconnected:', data.player); - } else if (data.type === 'game_ended') { - console.log('Game ended:', data.game_id); - } else if (data.type === 'error') { - console.error(data.message); - } 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..'); - socket.send(JSON.stringify({type: 'start_tournament'})); - } else { - console.error('WebSocket is not open or undefined'); - } - }); - } - } 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); - } - }; + // Vérification si une connexion WebSocket est déjà active avant d'initialiser + if (roomSockets["tournament"] && roomSockets["tournament"].readyState === WebSocket.OPEN) { + console.warn("WebSocket for tournament already open."); + return; + } + // Rejoindre la room "tournament" + roomName = 'tournament'; + joinRoom(token, roomName, username); + console.log("Starting WebSocket connection for tournament..."); + startWebSocketConnection(token, 42); // Le "42" pourrait être un identifiant pour le mode tournoi + } - socket.onclose = function (event) { - console.log('WebSocket connection closed'); - }; - socket.onerror = function (error) { - console.error('WebSocket error:', error); - }; - } + function startWebSocketConnection(token, players) { + if (socket && socket.readyState === WebSocket.OPEN) { + console.warn('WebSocket connection already open.'); + return; + } + socket = new WebSocket(`ws://${window.location.host}/ws/game/`); - function handleKeyDown(event) { - if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'w' || event.key === 's') { - sendKeyPress(event.key.toLowerCase()); - } - } + socket.onopen = function (event) { + console.log('WebSocket connection established'); + if (players === 1) { + console.log("Sending token for a quick match game"); + socket.send(JSON.stringify({ type: 'authenticate', token: token })); + } else if (players === 2) { + console.log("Sending tokens for a local game"); + socket.send(JSON.stringify({ type: 'authenticate2', token_1: token, token_2: token2 })); + } else { + console.log("Sending token for a tournament game"); + socket.send(JSON.stringify({ type: 'authenticate3', token: token })); + } + }; - function sendKeyPress(key) { - if (socket.readyState === WebSocket.OPEN) { - socket.send(JSON.stringify({ type: 'key_press', key })); - } - } + // Gestion des messages reçus + socket.onmessage = function (event) { + const data = JSON.parse(event.data); + if (data.type === 'authenticated') { + console.log('Authentication successful'); + } else if (data.type === 'waiting_room') { + console.log('Entered the WAITING ROOM'); + } else if (data.type === 'game_start') { + console.log('Game started:', data.game_id, '(', data.player1, 'vs', data.player2, ')'); + document.addEventListener('keydown', handleKeyDown); + } else if (data.type === 'game_state_update') { + updateGameState(data.game_state); + } else if (data.type === 'player_disconnected') { + console.log("Player disconnected:", data.player); + } else if (data.type === 'game_ended') { + console.log("Game ended:", data.game_id); + } else if (data.type === 'error') { + console.error(data.message); + } else if (data.type === 'update_waiting_room') { + document.getElementById('tournament-bracket').innerHTML = data.html; + } else { + console.log('Message from server:', data.type, data.message); + } + }; - function updateGameState(newState) { - gameState = newState; - renderGame(); - checkForWinner(); - } + // Gestion des fermetures de connexion + socket.onclose = function (event) { + console.log('WebSocket connection closed'); + }; - function renderGame() { - document.getElementById('player1-name').textContent = `${gameState.player1_name}`; - document.getElementById('player2-name').textContent = `${gameState.player2_name}`; + socket.onerror = function (error) { + console.error('WebSocket error:', error); + }; + } - document.getElementById('player1-pad').style.top = `${gameState.player1_position}px`; - document.getElementById('player2-pad').style.top = `${gameState.player2_position}px`; + // Gestion des événements de touche + function handleKeyDown(event) { + console.log("Key pressed:", event.key); + if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'w' || event.key === 's') { + sendKeyPress(event.key.toLowerCase()); + } + } - document.getElementById('ball').style.left = `${gameState.ball_position.x}px`; - document.getElementById('ball').style.top = `${gameState.ball_position.y}px`; + function sendKeyPress(key) { + console.log("Sending key press:", key); + if (socket.readyState === WebSocket.OPEN) { + socket.send(JSON.stringify({ type: 'key_press', key })); + } else { + console.warn("WebSocket is not open. Key press not sent."); + } + } - document.getElementById('player1-score').textContent = gameState.player1_score; - document.getElementById('player2-score').textContent = gameState.player2_score; + // Fonction pour mettre à jour l'état du jeu + function updateGameState(newState) { + console.log("Updating game state..."); + gameState = newState; + renderGame(); + } - document.getElementById('game-text').textContent = gameState.game_text; - } + // Fonction pour rendre l'état du jeu à l'écran + function renderGame() { + console.log("Rendering game state..."); + document.getElementById('player1-name').textContent = `${gameState.player1_name}`; + document.getElementById('player2-name').textContent = `${gameState.player2_name}`; - function updateGameText(gameText) { - document.getElementById('game-text').textContent = gameText; - } + document.getElementById('player1-pad').style.top = `${gameState.player1_position}px`; + document.getElementById('player2-pad').style.top = `${gameState.player2_position}px`; - const starsContainer = document.getElementById('stars'); - for (let i = 0; i < 500; i++) { - const star = document.createElement('div'); - star.className = 'star'; - star.style.width = `${Math.random() * 3}px`; - star.style.height = star.style.width; - star.style.left = `${Math.random() * 100}%`; - star.style.top = `${Math.random() * 100}%`; - star.style.animationDuration = `${Math.random() * 2 + 1}s`; - starsContainer.appendChild(star); - } + document.getElementById('ball').style.left = `${gameState.ball_position.x}px`; + document.getElementById('ball').style.top = `${gameState.ball_position.y}px`; - const homeButton = document.getElementById('home'); - const replayButton = document.getElementById('retry'); - const gameControls = document.getElementById('game-controls'); + document.getElementById('player1-score').textContent = gameState.player1_score; + document.getElementById('player2-score').textContent = gameState.player2_score; - homeButton.addEventListener('click', () => { - gameContainer.style.display = 'none'; - gameControls.style.display = 'none'; + document.getElementById('game-text').textContent = gameState.game_text; + } - logo.style.display = 'block' + ////////////////////////////CHAT//////////////////////////////////// + class ChatInput { + constructor(roomName, username, chatSocket) { + this.roomName = roomName; + this.username = username.toLowerCase().trim(); // Normalisation du nom d'utilisateur + this.chatSocket = chatSocket; + this.messageInput = document.querySelector(`#chat-input-${roomName} input`); + this.chatButton = document.querySelector(`#chat-input-${roomName} button`); + + console.log(`ChatInput initialized for room: ${roomName}, username: ${this.username}`); + this.initEventListeners(); + } + + initEventListeners() { + // Envoi de message en appuyant sur "Entrée" + this.messageInput.addEventListener('keypress', (event) => { + if (event.key === 'Enter') { + console.log("Enter key pressed, attempting to send message..."); + this.sendMessage(); + } + }); + + // Envoi de message en cliquant sur le bouton d'envoi + this.chatButton.addEventListener('click', () => { + console.log("Send button clicked, attempting to send message..."); + this.sendMessage(); + }); + } + + sendMessage() { + const message = this.messageInput.value.trim(); + console.log(`Attempting to send message: ${message}`); + + if (message) { + if (message.startsWith('/b ')) { + const targetUser = message.slice(3).trim().toLowerCase() + console.log(`Detected block command for user: ${targetUser}`); + this.sendBlockCommand(targetUser); + } else if (message.startsWith('/i ')) { + const targetUser = message.slice(3).trim().toLowerCase(); + console.log(`Detected invite command for user: ${targetUser}`); + this.sendInviteCommand(targetUser); + } else { + console.log("Sending chat message to WebSocket..."); + this.chatSocket.send(JSON.stringify({ + 'type': 'chat_message', + 'message': message, + 'username': this.username, + 'room': this.roomName + })); + } + this.messageInput.value = ''; // Effacer le champ de saisie + console.log("Message input cleared."); + } else { + console.warn('Cannot send an empty message.'); + } + } + + sendBlockCommand(targetUser) { + console.log(`Sending block command to WebSocket for user: ${targetUser}`); + this.chatSocket.send(JSON.stringify({ + 'type': 'block_user', + 'target_user': targetUser, + 'username': this.username, + 'room': this.roomName + })); + } + + sendInviteCommand(targetUser) { + console.log(`Sending invite command to WebSocket for user: ${targetUser}`); + this.chatSocket.send(JSON.stringify({ + 'type': 'invite_user', + 'target_user': targetUser, + 'username': this.username, + 'room': this.roomName + })); + } + } + + function startChatWebSocket(token, roomName, username) { + // Vérification de la validité du username + if (!username || username.trim() === '') { + console.error("Username is not defined or empty. WebSocket connection aborted."); + alert("Username is required to join the chat. Please log in."); + return; + } - formBlock.style.display = 'block'; - postFormButtons.style.display = 'flex'; - - setupFirstPlayer(); - }); + username = username.toLowerCase().trim(); // Normalisation du nom d'utilisateur + + // Vérification si un WebSocket est déjà ouvert pour la room + if (roomSockets[roomName] && roomSockets[roomName].readyState === WebSocket.OPEN) { + console.warn(`WebSocket for room ${roomName} already open.`); + return; + } + + console.log("Initializing chat WebSocket..."); + console.log(`Initializing chat WebSocket for room: ${roomName} with username: ${username}`); + + try { + console.log(`startChatWebSocket: ${roomName} with username: ${username} and token: ${token}`); + const chatSocket = new WebSocket(`ws://${window.location.host}/ws/chat/${roomName}/`); + + // Événement déclenché lorsque la connexion WebSocket est ouverte + chatSocket.onopen = function () { + console.log(`Chat WebSocket connection established in room: ${roomName}`); + + // Envoi d'un message d'authentification avec le token et le username + chatSocket.send(JSON.stringify({ + 'type': 'authenticate', + 'token': token, + 'room': roomName, + 'username': username + })); + console.log(`Authentication message sent for room: ${roomName} with username: ${username}`); + }; + + // Gestion des messages reçus du serveur WebSocket + chatSocket.onmessage = function (event) { + const data = JSON.parse(event.data); + console.log(`Message received from server in room ${roomName}:`, data); + + switch (data.type) { + case 'authenticated': + console.log(`User authenticated successfully in room: ${roomName}`); + break; + case 'chat_message': + const message = data.message; + const chatLog = document.getElementById(`chat-log-${roomName}`); + const receivedUsername = data.username || 'Anonymous'; // Utiliser un fallback si le username est manquant + + if (chatLog) { + const messageElement = document.createElement('div'); + messageElement.textContent = `${receivedUsername}: ${message}`; + chatLog.appendChild(messageElement); + console.log(`Message displayed in chat log for room: ${roomName}`); + } else { + console.error('Chat log element not found'); + } + break; + case 'success': // Nouveau cas pour traiter les messages de succès + console.log(`Success message received: ${data.message}`); + alert(`Success: ${data.message}`); // Vous pouvez remplacer l'alerte par une notification visuelle plus adaptée + break; + case 'error': + console.error(`Error message received: ${data.message}`); + alert(`Error: ${data.message}`); // Afficher une alerte ou un message d'erreur à l'utilisateur + break; + default: + console.warn('Unhandled message type:', data); + } + }; + + // Gestion de la fermeture du WebSocket + chatSocket.onclose = function (event) { + if (event.wasClean) { + console.log(`Chat WebSocket closed cleanly for room ${roomName}, code=${event.code}, reason=${event.reason}`); + } else { + console.error(`Chat WebSocket closed unexpectedly for room ${roomName}`); + } + }; + + // Gestion des erreurs WebSocket + chatSocket.onerror = function (error) { + console.error(`Chat WebSocket error in room ${roomName}:`, error); + }; + + // Stocker la connexion WebSocket dans roomSockets + roomSockets[roomName] = chatSocket; + console.log(`WebSocket connection stored for room: ${roomName}`); + + // Initialiser l'interface de chat pour cette room + new ChatInput(roomName, username, chatSocket); + + } catch (error) { + console.error(`Error initializing chat WebSocket for room ${roomName}:`, error); + } + } + + function createRoomTab(token, roomName, username) { + console.log(`createRoomTab: ${roomName} with username: ${username} and token: ${token}`); - function setupFirstPlayer() { - const firstPlayerName = window.firstPlayerName; - document.getElementById('player1-name').textContent = firstPlayerName; - } + const tabContainer = document.getElementById('room-tabs-container'); + if (!tabContainer) { + console.error('Room tabs container not found.'); + return; + } - replayButton.addEventListener('click', () => { - document.getElementById('player1-name').textContent = saveData.player1_name; - document.getElementById('player2-name').textContent = saveData.player2_name; - startLocalGame2(); - }); + const existingTab = Array.from(tabContainer.children).find(tab => tab.dataset.room === roomName); + if (existingTab) { + console.log(`Tab for room ${roomName} already exists.`); + // Vous pouvez ajouter une classe pour indiquer que l'onglet est inactif + existingTab.classList.remove('active'); + } else { + console.warn(`Tab for room ${roomName} not found in the HTML.`); + } + } - function checkForWinner() { - if (gameState.player1_score === 3 || gameState.player2_score === 3) { - if (saveData.type != "tournoi"){ - gameControls.style.display = 'flex'; - homeButton.style.display = 'block'; - replayButton.style.display = 'none'; - console.log(saveData.type); - if (saveData.type === 'local'){ - replayButton.style.display = 'block'; - } - } - } - } + function showRoomTab(roomName) { + const tabContainer = document.getElementById('room-tabs-container'); + const tab = Array.from(tabContainer.children).find(tab => tab.dataset.room === roomName); + if (tab) { + // Vous pouvez ajouter une classe pour indiquer que l'onglet est actif + tab.classList.add('active'); + console.log(`Showing tab for room: ${roomName}`); + } else { + console.warn(`Tab for room ${roomName} not found.`); + } + } + function switchRoom(token, roomName, username) { + console.log(`switchRoom: ${roomName} with username: ${username} and token: ${token}`); + + if (!roomName) { + console.error('Room name is undefined.'); + return; + } + console.log(`Attempting to switch to room: ${roomName}`); + if (activeRoom === roomName) { + console.log(`Already in room: ${roomName}`); + return; + } + + console.log(`Switching from room ${activeRoom} to room ${roomName}`); + const previousRoom = activeRoom; + activeRoom = roomName; + + if (previousRoom && document.getElementById(`chat-log-${previousRoom}`)) { + console.log(`Hiding chat log for previous room: ${previousRoom}`); + document.getElementById(`chat-log-${previousRoom}`).style.display = 'none'; + } + + const chatLog = document.getElementById(`chat-log-${roomName}`); + if (chatLog) { + chatLog.style.display = 'block'; + } else { + console.warn(`No chat log found for room: ${roomName}`); + } + + // Mettre à jour l'affichage des inputs + document.querySelectorAll('.chat-input').forEach(input => { + input.style.display = 'none'; + }); + document.getElementById(`chat-input-${roomName}`).style.display = 'block'; + + // Mettre à jour l'onglet actif + const tabs = document.querySelectorAll('.room-tab'); + tabs.forEach(t => t.classList.remove('active')); + const activeTab = Array.from(tabs).find(tab => tab.dataset.room === roomName); + if (activeTab) { + activeTab.classList.add('active'); + } + } + + function joinRoom(token, roomName, username) { + // Vérifier si la room est déjà active + console.log(`Joining room: ${roomName} with username: ${username} and token: ${token}`); + if (activeRoom === roomName) { + console.log(`Already in room: ${roomName}`); + return; + } + + if (!roomSockets[roomName]) { + console.log(`Joining new room: ${roomName}`); + createRoomTab(token, roomName, username); + showRoomTab(roomName); + startChatWebSocket(token, roomName, username); + } + + switchRoom(token, roomName, username); + } }); diff --git a/pong/static/index.html b/pong/static/index.html index aa89490..b762ee1 100644 --- a/pong/static/index.html +++ b/pong/static/index.html @@ -3,205 +3,279 @@ - - - Pong Game - - + + + Pong Game + + -
- Français - English - Italiano - Español - Deutsch -
-
-
-
-
-
-
-
-
+
+ Français + English + Italiano + Español + Deutsch +
+
+
+
+
+
+
+
+
- - + + -
-

BIENVENUE DANS LE PONG 42

-
-
- - - -
- - - - - - -
-
+
+

BIENVENUE DANS LE PONG 42

+
+
+ + + +
+ + + + + + +
+
- + - + - + - - - - - - - - - - - + + + + - - - + + + + + + + + + + + + +
+ +
+
+ + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
Main Room
+
Tournament
+
Quick Match
+
+
+ + + + + + - + \ No newline at end of file diff --git a/pong/static/styles.css b/pong/static/styles.css index 38d78f0..29e7fbd 100644 --- a/pong/static/styles.css +++ b/pong/static/styles.css @@ -1,472 +1,601 @@ /* General styles */ html { - font-family: Arial, sans-serif; - color: #00ffff; - background-color: #0a0a2a; - margin: 0; - padding: 0; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100%; - overflow: auto; + font-family: Arial, sans-serif; + color: #00ffff; + background-color: #0a0a2a; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + overflow: auto; } label { - margin: 10px 0 5px; + margin: 10px 0 5px; } input { - padding: 10px; - margin: 5px 0 20px; - width: 200px; + padding: 10px; + margin: 5px 0 20px; + width: 200px; } button { - background-color: #00ffff; - color: #000033; - border: none; - padding: 1rem 2rem; - font-size: 1.5rem; - cursor: pointer; - transition: all 0.3s ease; - border-radius: 10px; - margin-top: 1rem; - text-transform: uppercase; - letter-spacing: 2px; + background-color: #00ffff; + color: #000033; + border: none; + padding: 1rem 2rem; + font-size: 1.5rem; + cursor: pointer; + transition: all 0.3s ease; + border-radius: 10px; + margin-top: 1rem; + text-transform: uppercase; + letter-spacing: 2px; } button:hover { - background-color: #000033; - color: #00ffff; - box-shadow: 0 0 20px #00ffff; + background-color: #000033; + color: #00ffff; + box-shadow: 0 0 20px #00ffff; } #game1 { - width: 810px; - height: 500px; - position: relative; - background-color: #000; - border: 3px solid #00ffff; - box-shadow: 0 0 30px #00ffff, inset 0 0 20px #00ffff; - overflow: hidden; - display: flex; - justify-content: center; - align-items: center; + width: 810px; + height: 500px; + position: relative; + background-color: #000; + border: 3px solid #00ffff; + box-shadow: 0 0 30px #00ffff, inset 0 0 20px #00ffff; + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; } .name { - font-size: 24px; - position: absolute; - top: 20px; + font-size: 24px; + position: absolute; + top: 20px; } #player1-name { - left: 10px; - /* Adjust player score position */ + left: 10px; + /* Adjust player score position */ } #player2-name { - right: 10px; - /* Adjust bot score position */ + right: 10px; + /* Adjust bot score position */ } #game2 { - top: 60px; - width: 800px; - height: 400px; - position: absolute; - background-color: #000; - overflow: hidden; - border: 2px solid white; - /* Add red border */ - display: flex; - justify-content: center; - align-items: center; + top: 60px; + width: 800px; + height: 400px; + position: absolute; + background-color: #000; + overflow: hidden; + border: 2px solid white; + /* Add red border */ + display: flex; + justify-content: center; + align-items: center; } .score { - font-size: 24px; - position: absolute; - top: 10px; + font-size: 24px; + position: absolute; + top: 10px; } #player1-score { - left: 50px; - /* Adjust player score position */ + left: 50px; + /* Adjust player score position */ } #player2-score { - right: 50px; - /* Adjust bot score position */ + right: 50px; + /* Adjust bot score position */ } .pad { - width: 10px; - height: 100px; - background-color: #ffffff; - position: absolute; + width: 10px; + height: 100px; + background-color: #ffffff; + position: absolute; } #player1-pad { - left: 10px; - background-color: #00ffff; - border-radius: 10px; - box-shadow: 0 0 15px #00ffff; + left: 10px; + background-color: #00ffff; + border-radius: 10px; + box-shadow: 0 0 15px #00ffff; } #player2-pad { - right: 10px; - background-color: #00ffff; - border-radius: 10px; - box-shadow: 0 0 15px #00ffff; + right: 10px; + background-color: #00ffff; + border-radius: 10px; + box-shadow: 0 0 15px #00ffff; } #ball { - width: 20px; - height: 20px; - background-color: #00ffff; - border-radius: 50%; - position: absolute; + width: 20px; + height: 20px; + background-color: #00ffff; + border-radius: 50%; + position: absolute; } #game-text { - font-size: 64px; - color: #00ffff; - position: absolute; - top: 150px; + font-size: 64px; + color: #00ffff; + position: absolute; + top: 150px; } .logo { - position: absolute; - top: 20px; - left: 20px; - z-index: 20; + position: absolute; + top: 20px; + left: 20px; + z-index: 20; } .logo img { - max-width: 100%; - height: auto; + max-width: 100%; + height: auto; } .stars { - position: absolute; - width: 100%; - height: 100%; + position: absolute; + width: 100%; + height: 100%; } .star { - position: absolute; - background-color: #ffffff; - border-radius: 50%; - animation: twinkle 2s infinite alternate; + position: absolute; + background-color: #ffffff; + border-radius: 50%; + animation: twinkle 2s infinite alternate; } .background { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 0; - pointer-events: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; + pointer-events: none; } .pong-elements { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 5; - pointer-events: none; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 5; + pointer-events: none; } .paddle { - position: absolute; - width: 20px; - height: 100px; - background-color: #00ffff; - border-radius: 10px; - box-shadow: 0 0 15px #00ffff; + position: absolute; + width: 20px; + height: 100px; + background-color: #00ffff; + border-radius: 10px; + box-shadow: 0 0 15px #00ffff; } .paddle-left { - left: 50px; - animation: paddleMove 5s infinite alternate ease-in-out; + left: 50px; + animation: paddleMove 5s infinite alternate ease-in-out; } .paddle-right { - right: 50px; - animation: paddleMove 4s infinite alternate-reverse ease-in-out; + right: 50px; + animation: paddleMove 4s infinite alternate-reverse ease-in-out; } .ball_anim { - position: absolute; - width: 30px; - height: 30px; - background-color: #00ffff; - border-radius: 50%; - box-shadow: 0 0 20px #00ffff; - left: 80px; - top: 50%; - transform-style: preserve-3d; - animation: ballMove 3s linear infinite; + position: absolute; + width: 30px; + height: 30px; + background-color: #00ffff; + border-radius: 50%; + box-shadow: 0 0 20px #00ffff; + left: 80px; + top: 50%; + transform-style: preserve-3d; + animation: ballMove 3s linear infinite; } @keyframes paddleMove { - 0% { - transform: translateY(10vh); - } + 0% { + transform: translateY(10vh); + } - 100% { - transform: translateY(70vh); - } + 100% { + transform: translateY(70vh); + } } @keyframes ballMove { - 0% { - transform: translateZ(0) scale(1); - } + 0% { + transform: translateZ(0) scale(1); + } - 50% { - transform: translateZ(-500px) scale(0.5); - } + 50% { + transform: translateZ(-500px) scale(0.5); + } - 100% { - transform: translateZ(0) scale(1); - } + 100% { + transform: translateZ(0) scale(1); + } } .input-container { - margin-bottom: 2rem; + margin-bottom: 2rem; } .container { - text-align: center; - background-color: rgba(0, 0, 40, 0.8); - padding: 3rem; - border-radius: 15px; - border: 3px solid #00ffff; - box-shadow: 0 0 30px #00ffff, inset 0 0 20px #00ffff; - position: relative; - z-index: 10; - max-width: 80%; - overflow: auto; + text-align: center; + background-color: rgba(0, 0, 40, 0.8); + padding: 3rem; + border-radius: 15px; + border: 3px solid #00ffff; + box-shadow: 0 0 30px #00ffff, inset 0 0 20px #00ffff; + position: relative; + z-index: 10; + max-width: 80%; + overflow: auto; } .navbar { - position: absolute; - top: 30px; - right: 10px; - padding: 10px; + position: absolute; + top: 30px; + right: 10px; + padding: 10px; } .burger-menu { - font-size: 24px; - background: none; - border: none; - color: #00ffff; - cursor: pointer; - transition: color 0.3s ease; + font-size: 24px; + background: none; + border: none; + color: #00ffff; + cursor: pointer; + transition: color 0.3s ease; } .burger-menu:hover { - color: #ffffff; + color: #ffffff; } + .dropdown-content { - display: none; - position: absolute; - top: 100%; - right: 0; - margin-top: 10px; - background-color: #1a1a2e; - color: #ffffff; - box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.3); - border-radius: 5px; - z-index: 1; - width: max-content; + display: none; + position: absolute; + top: 100%; + right: 0; + margin-top: 10px; + background-color: #1a1a2e; + color: #ffffff; + box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.3); + border-radius: 5px; + z-index: 1; + width: max-content; } .dropdown-content.show { - display: block; + display: block; } + .dropdown-content a { - color: #ffffff; - padding: 12px 16px; - text-decoration: none; - display: block; - border-bottom: 1px solid rgba(255, 255, 255, 0.2); - transition: background-color 0.3s ease; + color: #ffffff; + padding: 12px 16px; + text-decoration: none; + display: block; + border-bottom: 1px solid rgba(255, 255, 255, 0.2); + transition: background-color 0.3s ease; } + .dropdown-content a:hover { - background-color: #333; - color: #00ffff; + background-color: #333; + color: #00ffff; } .language-switcher { - position: absolute; - top: 10px; - right: 10px; - display: flex; - gap: 10px; + position: absolute; + top: 10px; + right: 10px; + display: flex; + gap: 10px; } .language-switcher img { - width: 30px; - height: 20px; - cursor: pointer; + width: 30px; + height: 20px; + cursor: pointer; } #settings-btn { - position: fixed; - bottom: 10px; - right: 10px; - cursor: pointer; - z-index: 1000; - border: none; - border-radius: 50%; - padding: 10px; - font-size: 18px; + position: fixed; + bottom: 10px; + right: 10px; + cursor: pointer; + z-index: 1000; + border: none; + border-radius: 50%; + padding: 10px; + font-size: 18px; } #settings-menu { - position: fixed; - bottom: 50px; - right: 10px; - padding: 20px; - background-color: rgb(66, 63, 63); - border: 1px solid #ccc; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); - width: 250px; - z-index: 1000; + position: fixed; + bottom: 50px; + right: 10px; + padding: 20px; + background-color: rgb(66, 63, 63); + border: 1px solid #ccc; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + width: 250px; + z-index: 1000; } #close-settings { - position: absolute; - top: 10px; - right: 10px; - background: none; - border: none; - cursor: pointer; - font-size: 16px; + position: absolute; + top: 10px; + right: 10px; + background: none; + border: none; + cursor: pointer; + font-size: 16px; } .modal { - display: none; - position: fixed; - z-index: 1; - left: 0; - top: 0; - width: 100%; - height: 100%; - overflow: auto; - background-color: rgb(0, 0, 0); - background-color: rgba(0, 0, 0, 0.4); - padding-top: 60px; + display: none; + position: fixed; + z-index: 1; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgb(0, 0, 0); + background-color: rgba(0, 0, 0, 0.4); + padding-top: 60px; } .modal-content { - background-color: #fefefe; - margin: 5% auto; - padding: 20px; - border: 5px solid #888; - width: 80%; - max-height: 70vh; - overflow-y: auto; + background-color: #fefefe; + margin: 5% auto; + padding: 20px; + border: 5px solid #888; + width: 80%; + max-height: 70vh; + overflow-y: auto; } .close { - color: #aaa; - float: right; - font-size: 28px; - font-weight: bold; + color: #aaa; + float: right; + font-size: 28px; + font-weight: bold; } .close:hover, .close:focus { - color: black; - text-decoration: none; - cursor: pointer; + color: black; + text-decoration: none; + cursor: pointer; } pre { - white-space: pre-wrap; - word-wrap: break-word; + white-space: pre-wrap; + word-wrap: break-word; } -/* + .tournament-waiting-room { - background-color: rgba(0, 0, 0, 0.6); - padding: 20px; - border-radius: 15px; - color: #00ffff; - width: 50%; - margin: auto; - text-align: center; - box-shadow: 0 0 30px #00ffff, inset 0 0 20px #00ffff; + background-color: rgba(0, 0, 0, 0.6); + padding: 20px; + border-radius: 15px; + color: #00ffff; + width: 50%; + margin: auto; + text-align: center; + box-shadow: 0 0 30px #00ffff, inset 0 0 20px #00ffff; } .tournament-waiting-room h2 { - font-family: 'Arial', sans-serif; - font-size: 2em; - margin-bottom: 15px; - text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7); + font-family: 'Arial', sans-serif; + font-size: 2em; + margin-bottom: 15px; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7); } .tournament-waiting-room p { - font-family: 'Verdana', sans-serif; - font-size: 1.2em; - margin-bottom: 20px; - text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.6); + font-family: 'Verdana', sans-serif; + font-size: 1.2em; + margin-bottom: 20px; + text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.6); } .tournament-waiting-room ul { - list-style-type: none; - padding: 0; + list-style-type: none; + padding: 0; } -*/ + body { - color: rgb(0, 255, 255); /* Valeur par défaut */ - font-family: Arial, sans-serif; - font-size: 16px; + color: rgb(0, 255, 255); /* Valeur par défaut */ + font-family: Arial, sans-serif; + font-size: 16px; } canvas { - max-width: 100%; - height: auto; - margin-top: 20px; + max-width: 100%; + height: auto; + margin-top: 20px; } #game-controls { - display: flex; - justify-content: center; - gap: 20px; - margin-bottom: 10px; + display: flex; + justify-content: center; + gap: 20px; + margin-bottom: 10px; } #game-controls button { - background-color: #00ffff; - color: #000033; - border: none; - padding: 1rem 2rem; - font-size: 1rem; - cursor: pointer; - transition: all 0.3s ease; - border-radius: 10px; - text-transform: uppercase; - letter-spacing: 2px; + background-color: #00ffff; + color: #000033; + border: none; + padding: 1rem 2rem; + font-size: 1rem; + cursor: pointer; + transition: all 0.3s ease; + border-radius: 10px; + text-transform: uppercase; + letter-spacing: 2px; } #game-controls button:hover { - background-color: #000033; - color: #00ffff; - box-shadow: 0 0 20px #00ffff; -} \ No newline at end of file + background-color: #000033; + color: #00ffff; + box-shadow: 0 0 20px #00ffff; +} + +/* Styles pour le conteneur principal du chat */ +#chat-container { + position: fixed; + bottom: 10px; + left: 10px; + width: 300px; + background-color: rgba(0, 0, 0, 0.7); + border-radius: 10px; + padding: 10px; + box-shadow: 0 0 30px #00ffff, inset 0 0 20px #00ffff; + z-index: 1000; + box-sizing: border-box; /* Ajouté */ + display: flex; + flex-direction: column; +} + +/* Styles pour le conteneur du chat log */ +#chat-log-container { + width: 100%; + background-color: transparent; /* Fond transparent */ + padding: 10px; + border-radius: 10px; + color: #fff; + font-size: 14px; + max-height: 150px; + margin-bottom: 10px; + box-sizing: border-box; /* Ajouté */ + flex: 1; /* Permet au conteneur de prendre tout l'espace disponible */ +} + +.chat-log { + max-height: 150px; + overflow-y: auto; + margin-bottom: 10px; + box-sizing: border-box; /* Ajouté */ + background-color: transparent; /* Fond transparent */ +} + +/* Styles pour l'entrée de texte du chat */ +#chat-input-container { + width: 100%; + height: 25%; + display: flex; + flex-direction: column; /* Aligner les chat-inputs verticalement */ + background-color: transparent; /* Fond transparent */ + padding: 5px; + border-radius: 10px; + box-sizing: border-box; /* Ajouté */ +} + +.chat-input { + flex: 1; + padding: 5px; + font-size: 13px; + border: none; + border-radius: 5px; + margin-right: 5px; + height: 28px; + box-sizing: border-box; /* Ajouté */ + display: none; /* Masquer par défaut */ +} + +.chat-input.active { + display: flex; /* Afficher lorsque actif */ +} + +.chat-input input[type="text"] { + flex: 1; /* Permet à l'input de prendre tout l'espace disponible */ + padding: 5px; + font-size: 13px; + border: none; + border-radius: 5px; + box-sizing: border-box; /* Ajouté */ + height: 28px; /* Augmenter la hauteur de l'input */ +} + +.chat-button { + height: 28px; + width: 150px; /* Réduire la largeur du bouton */ + border: none; + border-radius: 5px; + background-color: #00ffff; + color: #000033; + cursor: pointer; + margin-left: 100px; /* Espace entre l'input et le bouton */ + display: flex; /* Utiliser flexbox pour centrer le contenu */ + align-items: center; /* Centrer verticalement */ + justify-content: center; /* Centrer horizontalement */ + text-align: center; /* Assurer que le texte est centré */ + font-size: 13px; /* Ajuster la taille de la police */ + padding: 0; /* Supprimer le padding interne */ +} + +/* Styles pour les onglets de room */ +#room-tabs-container { + display: flex; + justify-content: space-between; + margin-top: 10px; +} + +/* Styles pour les onglets de room */ +.room-tab { + font-size: 14px; + padding: 5px 10px; + margin-right: 5px; + cursor: pointer; + border: 1px solid #00ffff; + background-color: #000033; + color: #00ffff; + border-radius: 5px; + box-sizing: border-box; /* Ajouté */ + opacity: 0.1; /* Onglets inactifs grisés */ +} + +.room-tab:hover { + background-color: #00ffff; + color: #000033; +} + +.room-tab.active { + background-color: #00ffff; + color: #000033; + opacity: 1; /* Onglets actifs non grisés */ +} +