From e54c870f0526f54a7134cd9d253383f5c3cc1170 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 8 Jan 2026 11:41:46 +0100 Subject: [PATCH 1/4] Post return messages --- assets/game1.js | 19 +++++++++++++++++++ assets/styles/game1.css | 10 +++++++--- src/Game/Service/GameResponseService.php | 1 + 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/assets/game1.js b/assets/game1.js index 4141f60..6ec1a34 100644 --- a/assets/game1.js +++ b/assets/game1.js @@ -24,6 +24,7 @@ function subscribeToMercure(mercurePublicUrl, topic) { msgEl.style.color = '#0F0'; // Green for incoming messages msgEl.style.marginBottom = '10px'; messageContainer.appendChild(msgEl); + window.scrollTo(0, document.body.scrollHeight); if(stillPlayingSound) playSound(); console.log('[Mercure][game1] sequenceFinished status:', sequenceFinished); @@ -181,6 +182,7 @@ document.addEventListener('DOMContentLoaded', async () => { msgEl.textContent = msg[0]; msgEl.style.marginBottom = '10px'; messageContainer.appendChild(msgEl); + window.scrollTo(0, document.body.scrollHeight); playSound(); @@ -203,6 +205,13 @@ document.addEventListener('DOMContentLoaded', async () => { stillPlayingSound = false; sequenceFinished = false; const message = inputField.value.trim(); + + const msgEl = document.createElement('div'); + msgEl.className = 'message'; + msgEl.textContent = message; + msgEl.style.marginBottom = '10px'; + messageContainer.appendChild(msgEl); + if (message && apiEchoUrl) { inputField.value = ''; try { @@ -211,6 +220,16 @@ document.addEventListener('DOMContentLoaded', async () => { body: { message, ts: new Date().toISOString() }, }); console.log('[API][game1] message sent →', response); + if (response && response.result && Array.isArray(response.result.result)) { + response.result.result.forEach(text => { + const msgEl = document.createElement('div'); + msgEl.className = 'message'; + msgEl.textContent = text; + msgEl.style.marginBottom = '10px'; + messageContainer.appendChild(msgEl); + }); + window.scrollTo(0, document.body.scrollHeight); + } } catch (err) { console.error('[API][game1] Failed to send message:', err); } diff --git a/assets/styles/game1.css b/assets/styles/game1.css index 83fa243..cf1a6bf 100644 --- a/assets/styles/game1.css +++ b/assets/styles/game1.css @@ -29,8 +29,11 @@ body { div#game-timer { position: fixed; - top: 20px; - left: 20px; + top: 0; + left: 0; + width: 100%; + padding: 20px; + background-color: #000; color: #F00; font-size: 28px; z-index: 100; @@ -38,7 +41,7 @@ div#game-timer { div#message-container { padding: 20px; - padding-top: 60px; /* Space for fixed timer */ + padding-top: 80px; /* Space for fixed timer */ display: flex; flex-direction: column; justify-content: flex-end; @@ -49,6 +52,7 @@ div#message-container { div.message { color: #C0C0C0; + white-space: pre-wrap; } div#input { diff --git a/src/Game/Service/GameResponseService.php b/src/Game/Service/GameResponseService.php index 707b3b8..2d546ff 100644 --- a/src/Game/Service/GameResponseService.php +++ b/src/Game/Service/GameResponseService.php @@ -189,6 +189,7 @@ class GameResponseService $messages[] = ' If you want to send a message specifically to one other agent, use the id of the agent after /chat, like /chat 6 {message}'; $messages[] = ' This will send the message only to agent with id 6.'; $messages[] = ' USAGE: /chat {message}'; + $messages[] = ' USAGE: /chat 6 {message}'; $messages[] = ''; break; case 'help': -- 2.49.1 From 65070688ec315d7bdded590c58750ca0d28cd179 Mon Sep 17 00:00:00 2001 From: Frank van den Berg Date: Thu, 8 Jan 2026 13:05:40 +0100 Subject: [PATCH 2/4] Make mercure work correctly --- compose.yaml | 6 +++--- config/packages/mercure.yaml | 4 +++- docker/compose.yaml | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compose.yaml b/compose.yaml index c4fa3b1..21181f3 100644 --- a/compose.yaml +++ b/compose.yaml @@ -7,11 +7,11 @@ services: environment: # Uncomment the following line to disable HTTPS, #SERVER_NAME: ':80' - MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!' - MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!' + MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureJWTSignedBySymfonySecretKey!' + MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureJWTSignedBySymfonySecretKey!' # Set the URL of your Symfony project (without trailing slash!) as value of the cors_origins directive MERCURE_EXTRA_DIRECTIVES: | - cors_origins http://127.0.0.1:8000 + cors_origins http://localhost:8080 # Comment the following line to disable the development mode command: /usr/bin/caddy run --config /etc/caddy/dev.Caddyfile healthcheck: diff --git a/config/packages/mercure.yaml b/config/packages/mercure.yaml index 11dbcda..a4c87b7 100644 --- a/config/packages/mercure.yaml +++ b/config/packages/mercure.yaml @@ -3,4 +3,6 @@ mercure: default: url: '%env(MERCURE_URL)%' public_url: '%env(MERCURE_PUBLIC_URL)%' - jwt: '%env(MERCURE_JWT_SECRET)%' + jwt: + secret: '%env(MERCURE_JWT_SECRET)%' + publish: ['*'] diff --git a/docker/compose.yaml b/docker/compose.yaml index b37ede0..23b5f35 100644 --- a/docker/compose.yaml +++ b/docker/compose.yaml @@ -63,8 +63,8 @@ services: container_name: escapepage-mercure environment: SERVER_NAME: ":80" - MERCURE_PUBLISHER_JWT_KEY: ${MERCURE_JWT_SECRET:-!ChangeThisMercureJWT!} - MERCURE_SUBSCRIBER_JWT_KEY: ${MERCURE_JWT_SECRET:-!ChangeThisMercureJWT!} + MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureJWTSignedBySymfonySecretKey!' + MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureJWTSignedBySymfonySecretKey!' MERCURE_CORS_ALLOWED_ORIGINS: http://localhost:8080 MERCURE_PUBLISH_ALLOWED_ORIGINS: http://localhost:8080 MERCURE_EXTRA_DIRECTIVES: | -- 2.49.1 From e4976e51fa25dffea8aee8ba4a15a0f6b00b9c6e Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 8 Jan 2026 14:40:51 +0100 Subject: [PATCH 3/4] JWT token in env file --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index e39ec66..eafd2a9 100644 --- a/.env +++ b/.env @@ -57,7 +57,7 @@ MERCURE_URL=http://mercure/.well-known/mercure # Public hub URL used by browsers MERCURE_PUBLIC_URL=http://localhost:8090/.well-known/mercure # Shared secret for signing JWTs (dev only). In prod, set via real env/secrets. -MERCURE_JWT_SECRET=!ChangeThisMercureJWT! +MERCURE_JWT_SECRET=!ChangeThisMercureJWTSignedBySymfonySecretKey! # Base URL for Mercure topics. Use .dev in development; override to .com in prod via .env.prod or real env. MERCURE_TOPIC_BASE=https://escapepage.dev ###< mercure ### -- 2.49.1 From e42966e61852e1d016c9e32ec1a03af60e91a7bd Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 8 Jan 2026 15:34:43 +0100 Subject: [PATCH 4/4] Message when everyone is verified --- assets/game1.js | 12 +++++-- src/Game/Controller/GameController.php | 14 ++++++-- src/Game/Enum/SessionSettingType.php | 1 + src/Game/Service/GameResponseService.php | 46 ++++++++++++++++++++++++ templates/game/index.html.twig | 1 + 5 files changed, 70 insertions(+), 4 deletions(-) diff --git a/assets/game1.js b/assets/game1.js index 6ec1a34..a69635a 100644 --- a/assets/game1.js +++ b/assets/game1.js @@ -4,7 +4,7 @@ import './styles/game1.css'; let sequenceFinished = false; let stillPlayingSound = true; -function subscribeToMercure(mercurePublicUrl, topic) { +function subscribeToMercure(mercurePublicUrl, topic, myScreen) { try { const url = mercurePublicUrl + '?topic=' + encodeURIComponent(topic); const es = new EventSource(url); @@ -16,6 +16,13 @@ function subscribeToMercure(mercurePublicUrl, topic) { // data is [sendTo, message] if (Array.isArray(data) && data.length >= 2) { + const sendTo = parseInt(data[0]); + // Filter: 0 means everyone, otherwise must match myScreen + if (sendTo !== 0 && sendTo !== parseInt(myScreen)) { + console.log('[Mercure][game1] Message not for this player, skipping.'); + return; + } + const messageContainer = document.getElementById('message-container'); if (messageContainer) { const msgEl = document.createElement('div'); @@ -121,11 +128,12 @@ document.addEventListener('DOMContentLoaded', async () => { const mercurePublicUrl = cfgEl.dataset.mercurePublicUrl; const topic = cfgEl.dataset.topic; + const screen = cfgEl.dataset.screen; const apiPingUrl = cfgEl.dataset.apiPingUrl; const apiEchoUrl = cfgEl.dataset.apiEchoUrl; if (mercurePublicUrl && topic) { - subscribeToMercure(mercurePublicUrl, topic); + subscribeToMercure(mercurePublicUrl, topic, screen); } else { console.warn('[Mercure][game1] Missing data attributes on #mercure-config'); } diff --git a/src/Game/Controller/GameController.php b/src/Game/Controller/GameController.php index 6c46080..6060be0 100644 --- a/src/Game/Controller/GameController.php +++ b/src/Game/Controller/GameController.php @@ -85,8 +85,18 @@ final class GameController extends AbstractController #[IsGranted(new Expression("is_granted('ROLE_PLAYER') or is_granted('ROLE_ADMIN')"))] #[IsGranted('SESSION_VIEW', subject: 'session')] public function index( - Session $session): Response + Session $session, + Security $security, + \App\Game\Repository\PlayerRepository $playerRepository + ): Response { - return $this->render('game/index.html.twig', ['session' => $session]); + $user = $security->getUser(); + $player = $playerRepository->findOneBy(['session' => $session, 'user' => $user]); + $screen = $player ? $player->getScreen() : 0; + + return $this->render('game/index.html.twig', [ + 'session' => $session, + 'screen' => $screen, + ]); } } diff --git a/src/Game/Enum/SessionSettingType.php b/src/Game/Enum/SessionSettingType.php index 66123c0..17abbdf 100644 --- a/src/Game/Enum/SessionSettingType.php +++ b/src/Game/Enum/SessionSettingType.php @@ -21,4 +21,5 @@ enum SessionSettingType: string case VERIFICATION_PROGRESS_FOR_PLAYER1 = 'VerificationProgressForPlayer1'; case VERIFICATION_PROGRESS_FOR_PLAYER2 = 'VerificationProgressForPlayer2'; case VERIFICATION_PROGRESS_FOR_PLAYER3 = 'VerificationProgressForPlayer3'; + case EVERYONE_VERIFIED = 'EveryoneVerified'; } diff --git a/src/Game/Service/GameResponseService.php b/src/Game/Service/GameResponseService.php index 2d546ff..bf33f8d 100644 --- a/src/Game/Service/GameResponseService.php +++ b/src/Game/Service/GameResponseService.php @@ -585,6 +585,52 @@ class GameResponseService $setting->setValue(json_encode($rights)); $this->entityManager->persist($setting); $this->entityManager->flush(); + + $this->checkIfAllPlayersVerified($player); + } + } + + private function checkIfAllPlayersVerified(Player $player): void + { + $session = $player->getSession(); + $everyoneVerifiedSetting = $this->sessionSettingRepository->getSetting($session, SessionSettingType::EVERYONE_VERIFIED, $player); + + if ($everyoneVerifiedSetting && $everyoneVerifiedSetting->getValue() === 'true') { + return; + } + + $allVerified = true; + foreach ([1, 2, 3] as $screen) { + $progressSettingName = match ($screen) { + 1 => SessionSettingType::VERIFICATION_PROGRESS_FOR_PLAYER1, + 2 => SessionSettingType::VERIFICATION_PROGRESS_FOR_PLAYER2, + 3 => SessionSettingType::VERIFICATION_PROGRESS_FOR_PLAYER3, + default => null, + }; + + $progressSetting = $this->sessionSettingRepository->getSetting($session, $progressSettingName, $player); + $progress = json_decode($progressSetting?->getValue() ?? '[]', true) ?? []; + + if (count($progress) < 2) { + $allVerified = false; + break; + } + } + + if ($allVerified) { + if (!$everyoneVerifiedSetting) { + $everyoneVerifiedSetting = new SessionSetting(); + $everyoneVerifiedSetting->setSession($session); + $everyoneVerifiedSetting->setPlayer($player); + $everyoneVerifiedSetting->setName(SessionSettingType::EVERYONE_VERIFIED); + } + $everyoneVerifiedSetting->setValue('true'); + $this->entityManager->persist($everyoneVerifiedSetting); + $this->entityManager->flush(); + + $topic = $_ENV['MERCURE_TOPIC_BASE'] . '/game/hub-' . $session->getId(); + $message = "Mainframe Help Modus: Agents Doyle, Vega and Lennox rapports have been updated with coded messages."; + $this->hub->publish(new Update($topic, json_encode([0, $message]))); } } diff --git a/templates/game/index.html.twig b/templates/game/index.html.twig index b3c67f4..6c098cb 100644 --- a/templates/game/index.html.twig +++ b/templates/game/index.html.twig @@ -18,6 +18,7 @@ data-topic="{{ (mercure_topic_base ~ '/game/hub-' ~ session.id)|e('html_attr') }}" data-api-ping-url="{{ path('game_api_ping')|e('html_attr') }}" data-api-echo-url="{{ path('game_api_message')|e('html_attr') }}" + data-screen="{{ screen|e('html_attr') }}" style="display:none"> -- 2.49.1