diff --git a/config/services.yaml b/config/services.yaml index 6bbad87..5913030 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -16,5 +16,9 @@ services: App\: resource: '../src/' + App\Game\Service\GameResponseService: + arguments: + $projectDir: '%kernel.project_dir%' + # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/src/Game/Controller/GameAdminController.php b/src/Game/Controller/GameAdminController.php new file mode 100644 index 0000000..3081f6e --- /dev/null +++ b/src/Game/Controller/GameAdminController.php @@ -0,0 +1,30 @@ +findByRole('ROLE_PLAYER'); + $sessions = $sessionRepository->findAll(); + + return $this->render('game/admin/index.html.twig', [ + 'players' => $players, + 'sessions' => $sessions, + ]); + } +} diff --git a/src/Game/Service/GameResponseService.php b/src/Game/Service/GameResponseService.php index cacee9d..b298e27 100644 --- a/src/Game/Service/GameResponseService.php +++ b/src/Game/Service/GameResponseService.php @@ -21,6 +21,7 @@ class GameResponseService private SessionSettingRepository $sessionSettingRepository, private HubInterface $hub, private EntityManagerInterface $entityManager, + private string $projectDir, ) { } @@ -44,7 +45,7 @@ class GameResponseService if(!$player) return ['error' => 'You are not in a game.']; - // TODO: Here i need to add a message handler to save the message in a big log. + $this->logSessionActivity($player, 'PLAYER: ' . $message); $data = []; @@ -54,9 +55,43 @@ class GameResponseService $data = $this->checkConsoleCommando($message, $player); } + $responseLog = ''; + if (isset($data['result']) && is_array($data['result'])) { + foreach ($data['result'] as $line) { + if (is_array($line)) { + $responseLog .= json_encode($line) . "\n"; + } elseif (is_string($line) || is_numeric($line)) { + $responseLog .= (string)$line . "\n"; + } + } + } elseif (isset($data['error'])) { + $responseLog = 'ERROR: ' . $data['error']; + } + + if ($responseLog !== '') { + $this->logSessionActivity($player, 'SERVER: ' . trim($responseLog)); + } + return $data; } + private function logSessionActivity(Player $player, string $content): void + { + $sessionId = $player->getSession()->getId(); + $username = $player->getUser()->getUsername(); + $logDir = $this->projectDir . '/var/log/sessions/' . $sessionId; + + if (!is_dir($logDir)) { + mkdir($logDir, 0777, true); + } + + $logFile = $logDir . '/' . $username . '.txt'; + $timestamp = date('Y-m-d H:i:s'); + $logMessage = sprintf("[%s] %s\n", $timestamp, $content); + + file_put_contents($logFile, $logMessage, FILE_APPEND); + } + private function getRechten(Player $player): array { $settingName = SessionSettingType::tryFrom('RightsForPlayer' . $player->getScreen()); diff --git a/src/Tech/Repository/UserRepository.php b/src/Tech/Repository/UserRepository.php index 3417362..7742d1e 100644 --- a/src/Tech/Repository/UserRepository.php +++ b/src/Tech/Repository/UserRepository.php @@ -32,4 +32,16 @@ class UserRepository extends ServiceEntityRepository implements PasswordUpgrader $this->getEntityManager()->persist($user); $this->getEntityManager()->flush(); } + + /** + * @return User[] + */ + public function findByRole(string $role): array + { + return $this->createQueryBuilder('u') + ->andWhere('u.roles LIKE :role') + ->setParameter('role', '%"' . $role . '"%') + ->getQuery() + ->getResult(); + } } diff --git a/templates/game/admin/index.html.twig b/templates/game/admin/index.html.twig new file mode 100644 index 0000000..4b96283 --- /dev/null +++ b/templates/game/admin/index.html.twig @@ -0,0 +1,82 @@ +{% extends 'base.html.twig' %} + +{% block title %}Game Admin Dashboard{% endblock %} + +{% block body %} +

Game Admin Dashboard

+ +
+
+

All Players ({{ players|length }})

+ + + + + + + + + + + + {% for player in players %} + + + + + + + + {% else %} + + + + {% endfor %} + +
IDUsernameEmailRolesVerified
{{ player.id }}{{ player.username }}{{ player.email }}{{ player.roles|join(', ') }}{{ player.isVerified ? 'Yes' : 'No' }}
No players found.
+
+ +
+

All Sessions ({{ sessions|length }})

+ + + + + + + + + + + + + {% for session in sessions %} + + + + + + + + + {% else %} + + + + {% endfor %} + +
IDGameStatusPlayers JoinedCreated AtActions
{{ session.id }}{{ session.game.name }}{{ session.status.value }} +
    + {% for p in session.players %} +
  • {{ p.user.username }} (Screen: {{ p.screen ?? 'N/A' }})
  • + {% else %} +
  • No players
  • + {% endfor %} +
+ ({{ session.players|length }} / {{ session.game.numberOfPlayers }}) +
{{ session.created|date('Y-m-d H:i') }} + View Game +
No sessions found.
+
+
+{% endblock %} diff --git a/templates/game/dashboard.html.twig b/templates/game/dashboard.html.twig index 4836688..62b196c 100644 --- a/templates/game/dashboard.html.twig +++ b/templates/game/dashboard.html.twig @@ -5,6 +5,10 @@ {% block body %}

Game Dashboard

+ {% if is_granted('ROLE_ADMIN') %} +

Go to Game Admin Dashboard

+ {% endif %} +

Create New Session

{% if availableGames is not empty %}
diff --git a/tests/Game/GameResponseServiceChatVerifyCodeTest.php b/tests/Game/GameResponseServiceChatVerifyCodeTest.php index dbdaea0..1b4b0f0 100644 --- a/tests/Game/GameResponseServiceChatVerifyCodeTest.php +++ b/tests/Game/GameResponseServiceChatVerifyCodeTest.php @@ -3,6 +3,7 @@ declare(strict_types=1); namespace App\Tests\Game; +use App\Game\Entity\Game; use App\Game\Entity\Player; use App\Game\Entity\Session; use App\Game\Entity\SessionSetting; @@ -39,7 +40,8 @@ class GameResponseServiceChatVerifyCodeTest extends TestCase $this->playerService, $this->sessionSettingRepository, $this->hub, - $this->entityManager + $this->entityManager, + 'H:\escapepage' ); $_ENV['MERCURE_TOPIC_BASE'] = 'http://test'; @@ -50,8 +52,12 @@ class GameResponseServiceChatVerifyCodeTest extends TestCase $user = new User(); $user->setUsername('testuser'); + $game = $this->createMock(Game::class); + $game->method('getNumberOfPlayers')->willReturn(4); + $session = $this->createMock(Session::class); $session->method('getId')->willReturn(123); + $session->method('getGame')->willReturn($game); $player = $this->createMock(Player::class); $player->method('getUser')->willReturn($user); diff --git a/tests/Game/SessionLoggingTest.php b/tests/Game/SessionLoggingTest.php new file mode 100644 index 0000000..5b52ee9 --- /dev/null +++ b/tests/Game/SessionLoggingTest.php @@ -0,0 +1,102 @@ +tempDir = sys_get_temp_dir() . '/escapepage_test_' . uniqid(); + mkdir($this->tempDir, 0777, true); + + $this->security = $this->createMock(Security::class); + $this->playerService = $this->createMock(PlayerService::class); + $this->sessionSettingRepository = $this->createMock(SessionSettingRepository::class); + $this->hub = $this->createMock(HubInterface::class); + $this->entityManager = $this->createMock(EntityManagerInterface::class); + + $this->service = new GameResponseService( + $this->security, + $this->playerService, + $this->sessionSettingRepository, + $this->hub, + $this->entityManager, + $this->tempDir + ); + } + + protected function tearDown(): void + { + $this->removeDir($this->tempDir); + } + + private function removeDir(string $dir): void + { + if (!is_dir($dir)) return; + $files = array_diff(scandir($dir), ['.', '..']); + foreach ($files as $file) { + (is_dir("$dir/$file")) ? $this->removeDir("$dir/$file") : unlink("$dir/$file"); + } + rmdir($dir); + } + + public function testLogging(): void + { + $user = new User(); + $user->setUsername('player1'); + + $session = $this->createMock(Session::class); + $session->method('getId')->willReturn(456); + + $player = $this->createMock(Player::class); + $player->method('getUser')->willReturn($user); + $player->method('getSession')->willReturn($session); + $player->method('getScreen')->willReturn(1); + + $this->security->method('getUser')->willReturn($user); + $this->playerService->method('GetCurrentlyActiveAsPlayer')->willReturn($player); + + // Mock rights + $rightsSetting = new SessionSetting(); + $rightsSetting->setValue(json_encode(['chat'])); + $this->sessionSettingRepository->method('getSetting') + ->willReturnMap([ + [$session, SessionSettingType::RIGHTS_FOR_PLAYER1, $player, $rightsSetting], + ]); + + // Simulate 'help' command (always returns something) + $raw = json_encode(['message' => 'help', 'ts' => '123']); + $result = $this->service->getGameResponse($raw); + + $this->assertNotEmpty($result); + + $logFilePath = $this->tempDir . '/var/log/sessions/456/player1.txt'; + $this->assertFileExists($logFilePath); + + $logContent = file_get_contents($logFilePath); + $this->assertStringContainsString('PLAYER: help', $logContent); + $this->assertStringContainsString('SERVER:', $logContent); + } +}