From 824ad52f9873117aa132d23f94ea394cb8f9a941 Mon Sep 17 00:00:00 2001 From: Markus Reinhold Date: Tue, 23 Dec 2025 00:25:59 +0100 Subject: [PATCH 1/2] Expose GetOpenGames via messaging --- config/connect-four/services/game.yml | 2 +- .../Game/Query/Model/OpenGames/OpenGame.php | 2 ++ .../Query/Model/OpenGames/OpenGameStore.php | 2 +- .../Game/Query/OpenGamesHandler.php | 10 +++--- .../Application/Game/Query/OpenGamesQuery.php | 4 +++ .../Port/Adapter/Http/FragmentController.php | 2 +- .../Adapter/Messaging/RpcMessageHandler.php | 32 +++++++++++++++++++ .../Projection/OpenGamesProjection.php | 23 ++++++------- .../Repository/PredisOpenGameStore.php | 22 +++++++++---- 9 files changed, 70 insertions(+), 29 deletions(-) diff --git a/config/connect-four/services/game.yml b/config/connect-four/services/game.yml index 98a097a2e..c4bff8954 100644 --- a/config/connect-four/services/game.yml +++ b/config/connect-four/services/game.yml @@ -17,7 +17,7 @@ services: connect-four.open-game-store: class: Gaming\ConnectFour\Port\Adapter\Persistence\Repository\PredisOpenGameStore - arguments: ['@connect-four.predis', 'open-games'] + arguments: ['@connect-four.predis', 'open-games', '@connect-four.normalizer'] connect-four.running-games-store: class: Gaming\ConnectFour\Port\Adapter\Persistence\Repository\PredisRunningGameStore diff --git a/src/ConnectFour/Application/Game/Query/Model/OpenGames/OpenGame.php b/src/ConnectFour/Application/Game/Query/Model/OpenGames/OpenGame.php index 5ca84725d..a993fa05a 100644 --- a/src/ConnectFour/Application/Game/Query/Model/OpenGames/OpenGame.php +++ b/src/ConnectFour/Application/Game/Query/Model/OpenGames/OpenGame.php @@ -8,6 +8,8 @@ final class OpenGame { public function __construct( public readonly string $gameId, + public readonly int $width, + public readonly int $height, public readonly string $playerId ) { } diff --git a/src/ConnectFour/Application/Game/Query/Model/OpenGames/OpenGameStore.php b/src/ConnectFour/Application/Game/Query/Model/OpenGames/OpenGameStore.php index 005fb9f0d..1b12f757e 100644 --- a/src/ConnectFour/Application/Game/Query/Model/OpenGames/OpenGameStore.php +++ b/src/ConnectFour/Application/Game/Query/Model/OpenGames/OpenGameStore.php @@ -10,5 +10,5 @@ public function save(OpenGame $openGame): void; public function remove(string $gameId): void; - public function all(): OpenGames; + public function all(int $limit): OpenGames; } diff --git a/src/ConnectFour/Application/Game/Query/OpenGamesHandler.php b/src/ConnectFour/Application/Game/Query/OpenGamesHandler.php index ab1650f28..75748f250 100644 --- a/src/ConnectFour/Application/Game/Query/OpenGamesHandler.php +++ b/src/ConnectFour/Application/Game/Query/OpenGamesHandler.php @@ -9,15 +9,13 @@ final class OpenGamesHandler { - private OpenGameStore $openGameStore; - - public function __construct(OpenGameStore $openGameStore) - { - $this->openGameStore = $openGameStore; + public function __construct( + private readonly OpenGameStore $openGameStore + ) { } public function __invoke(OpenGamesQuery $query): OpenGames { - return $this->openGameStore->all(); + return $this->openGameStore->all($query->limit); } } diff --git a/src/ConnectFour/Application/Game/Query/OpenGamesQuery.php b/src/ConnectFour/Application/Game/Query/OpenGamesQuery.php index 0270035de..710f88e2a 100644 --- a/src/ConnectFour/Application/Game/Query/OpenGamesQuery.php +++ b/src/ConnectFour/Application/Game/Query/OpenGamesQuery.php @@ -12,4 +12,8 @@ */ final class OpenGamesQuery implements Request { + public function __construct( + public readonly int $limit + ) { + } } diff --git a/src/ConnectFour/Port/Adapter/Http/FragmentController.php b/src/ConnectFour/Port/Adapter/Http/FragmentController.php index 89fd87d51..053ec3111 100644 --- a/src/ConnectFour/Port/Adapter/Http/FragmentController.php +++ b/src/ConnectFour/Port/Adapter/Http/FragmentController.php @@ -36,7 +36,7 @@ public function statisticsAction(): Response public function openGamesAction(): Response { return $this->render('@connect-four/open-games.html.twig', [ - 'openGames' => $openGames = $this->queryBus->handle(new OpenGamesQuery()), + 'openGames' => $openGames = $this->queryBus->handle(new OpenGamesQuery(100)), 'usernames' => $this->usernames->byIds( array_map( static fn(OpenGame $openGame) => $openGame->playerId, diff --git a/src/ConnectFour/Port/Adapter/Messaging/RpcMessageHandler.php b/src/ConnectFour/Port/Adapter/Messaging/RpcMessageHandler.php index 69a08000d..1c8ae255c 100644 --- a/src/ConnectFour/Port/Adapter/Messaging/RpcMessageHandler.php +++ b/src/ConnectFour/Port/Adapter/Messaging/RpcMessageHandler.php @@ -15,6 +15,8 @@ use Gaming\ConnectFour\Application\Game\Query\Model\Game\Game; use Gaming\ConnectFour\Application\Game\Query\Model\Game\Move; use Gaming\ConnectFour\Application\Game\Query\Model\GamesByPlayer\State; +use Gaming\ConnectFour\Application\Game\Query\Model\OpenGames\OpenGame; +use Gaming\ConnectFour\Application\Game\Query\OpenGamesQuery; use GamingPlatform\Api\ConnectFour\V1\ConnectFourV1; use GamingPlatform\Api\ConnectFour\V1\Game as ProtoV1Game; use GamingPlatform\Api\ConnectFour\V1\GetGamesByPlayer\State as ProtoV1State; @@ -33,6 +35,7 @@ public function handle(Message $message, Context $context): void ConnectFourV1::OpenGameType => $this->handleOpenGame($message, $context), ConnectFourV1::JoinGameType => $this->handleJoinGame($message, $context), ConnectFourV1::MakeMoveType => $this->handleMakeMove($message, $context), +// ConnectFourV1::GetOpenGamesType => $this->handleGetOpenGames($message, $context), ConnectFourV1::GetGamesByPlayerType => $this->handleGetGamesByPlayer($message, $context), default => true }; @@ -99,6 +102,35 @@ private function handleMakeMove(Message $message, Context $context): void ); } +// private function handleGetOpenGames(Message $message, Context $context): void +// { +// $request = ConnectFourV1::createGetOpenGames($message->body()); +// +// $response = $this->queryBus->handle( +// new OpenGamesQuery( +// $request->getLimit() +// ) +// ); +// +// $context->reply( +// new Message( +// ConnectFourV1::GetOpenGamesResponseType, +// ConnectFourV1::createGetOpenGamesResponse() +// ->setGames( +// array_map( +// static fn(OpenGame $game) => ConnectFourV1::createGetOpenGamesResponse_Game() +// ->setGameId($game->gameId) +// ->setWidth($game->width) +// ->setHeight($game->height) +// ->setPlayerId($game->playerId), +// $response->games() +// ) +// ) +// ->serializeToString() +// ) +// ); +// } + private function handleGetGamesByPlayer(Message $message, Context $context): void { $request = ConnectFourV1::createGetGamesByPlayer($message->body()); diff --git a/src/ConnectFour/Port/Adapter/Persistence/Projection/OpenGamesProjection.php b/src/ConnectFour/Port/Adapter/Persistence/Projection/OpenGamesProjection.php index 550b26262..84dd9a9d9 100644 --- a/src/ConnectFour/Port/Adapter/Persistence/Projection/OpenGamesProjection.php +++ b/src/ConnectFour/Port/Adapter/Persistence/Projection/OpenGamesProjection.php @@ -27,22 +27,17 @@ public function handle(DomainEvent $domainEvent): void $content = $domainEvent->content; match ($content::class) { - GameOpened::class => $this->saveGame($content->aggregateId(), $content->playerId()), + GameOpened::class => $this->openGameStore->save( + new OpenGame( + $content->aggregateId(), + $content->width(), + $content->height(), + $content->playerId() + ) + ), GameAborted::class, - PlayerJoined::class => $this->removeGame($content->aggregateId()), + PlayerJoined::class => $this->openGameStore->remove($content->aggregateId()), default => true }; } - - private function saveGame(string $gameId, string $playerId): void - { - $this->openGameStore->save( - new OpenGame($gameId, $playerId) - ); - } - - private function removeGame(string $gameId): void - { - $this->openGameStore->remove($gameId); - } } diff --git a/src/ConnectFour/Port/Adapter/Persistence/Repository/PredisOpenGameStore.php b/src/ConnectFour/Port/Adapter/Persistence/Repository/PredisOpenGameStore.php index 5bc1d6d4e..be8bf9f25 100644 --- a/src/ConnectFour/Port/Adapter/Persistence/Repository/PredisOpenGameStore.php +++ b/src/ConnectFour/Port/Adapter/Persistence/Repository/PredisOpenGameStore.php @@ -4,6 +4,7 @@ namespace Gaming\ConnectFour\Port\Adapter\Persistence\Repository; +use Gaming\Common\Normalizer\Normalizer; use Gaming\ConnectFour\Application\Game\Query\Model\OpenGames\OpenGame; use Gaming\ConnectFour\Application\Game\Query\Model\OpenGames\OpenGames; use Gaming\ConnectFour\Application\Game\Query\Model\OpenGames\OpenGameStore; @@ -16,14 +17,21 @@ final class PredisOpenGameStore implements OpenGameStore public function __construct( private readonly Client $predis, - private readonly string $storageKey + private readonly string $storageKey, + private readonly Normalizer $normalizer ) { } public function save(OpenGame $openGame): void { $this->predis->pipeline(function (ClientContextInterface $pipeline) use ($openGame): void { - $pipeline->set($this->storageKeyForGameInfo($openGame->gameId), $openGame->playerId); + $pipeline->set( + $this->storageKeyForGameInfo($openGame->gameId), + json_encode( + $this->normalizer->normalize($openGame, OpenGame::class), + JSON_THROW_ON_ERROR + ) + ); $pipeline->zadd( $this->storageKey, @@ -41,17 +49,19 @@ public function remove(string $gameId): void }); } - public function all(): OpenGames + public function all(int $limit): OpenGames { - $gameIds = $this->predis->zrange($this->storageKey, 0, 10000); + $gameIds = $this->predis->zrange($this->storageKey, 0, $limit - 1); if (count($gameIds) === 0) { return new OpenGames([]); } return new OpenGames( array_map( - static fn(string $gameId, string $playerId): OpenGame => new OpenGame($gameId, $playerId), - $gameIds, + fn(string $openGame): OpenGame => $this->normalizer->denormalize( + json_decode($openGame, true, flags: JSON_THROW_ON_ERROR), + OpenGame::class + ), $this->predis->mget(array_map($this->storageKeyForGameInfo(...), $gameIds)) ) ); From 42d8a6518790848b65a10b23c8aa5d3aff758d7e Mon Sep 17 00:00:00 2001 From: Markus Reinhold Date: Tue, 23 Dec 2025 14:19:13 +0100 Subject: [PATCH 2/2] Enable messaging endpoint --- composer.json | 2 +- composer.lock | 14 ++--- .../Adapter/Messaging/RpcMessageHandler.php | 58 +++++++++---------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/composer.json b/composer.json index 9305e013e..5a6cd7991 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "doctrine/doctrine-migrations-bundle": "^3.3", "doctrine/migrations": "^3.7", "doctrine/orm": "^3.0", - "gaming-platform/api": "^1.8", + "gaming-platform/api": "^1.9", "jms/serializer": "^3.17", "marein/php-nchan-client": "^3.1", "marein/symfony-lock-doctrine-migrations-bundle": "^1.0", diff --git a/composer.lock b/composer.lock index 69d2e6c4c..b7d574fd5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "86ca91a82aa1ec08b99cee28a7b7c274", + "content-hash": "a4949b098697de8bcf03e6ae15b1de67", "packages": [ { "name": "amphp/amp", @@ -2201,16 +2201,16 @@ }, { "name": "gaming-platform/api", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/gaming-platform/api.git", - "reference": "fca26b34d7e878087508eb5145062a51d118f243" + "reference": "1956d082e12f696f6c9443c461640d4969916191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/gaming-platform/api/zipball/fca26b34d7e878087508eb5145062a51d118f243", - "reference": "fca26b34d7e878087508eb5145062a51d118f243", + "url": "https://api.github.com/repos/gaming-platform/api/zipball/1956d082e12f696f6c9443c461640d4969916191", + "reference": "1956d082e12f696f6c9443c461640d4969916191", "shasum": "" }, "require": { @@ -2234,10 +2234,10 @@ ], "description": "API definitions for the platform with generated code in various languages.", "support": { - "source": "https://github.com/gaming-platform/api/tree/v1.8.0", + "source": "https://github.com/gaming-platform/api/tree/v1.9.0", "issues": "https://github.com/gaming-platform/api/issues" }, - "time": "2025-12-20T23:55:46+00:00" + "time": "2025-12-23T12:50:08+00:00" }, { "name": "jms/metadata", diff --git a/src/ConnectFour/Port/Adapter/Messaging/RpcMessageHandler.php b/src/ConnectFour/Port/Adapter/Messaging/RpcMessageHandler.php index 1c8ae255c..d36b15f7d 100644 --- a/src/ConnectFour/Port/Adapter/Messaging/RpcMessageHandler.php +++ b/src/ConnectFour/Port/Adapter/Messaging/RpcMessageHandler.php @@ -35,7 +35,7 @@ public function handle(Message $message, Context $context): void ConnectFourV1::OpenGameType => $this->handleOpenGame($message, $context), ConnectFourV1::JoinGameType => $this->handleJoinGame($message, $context), ConnectFourV1::MakeMoveType => $this->handleMakeMove($message, $context), -// ConnectFourV1::GetOpenGamesType => $this->handleGetOpenGames($message, $context), + ConnectFourV1::GetOpenGamesType => $this->handleGetOpenGames($message, $context), ConnectFourV1::GetGamesByPlayerType => $this->handleGetGamesByPlayer($message, $context), default => true }; @@ -102,34 +102,34 @@ private function handleMakeMove(Message $message, Context $context): void ); } -// private function handleGetOpenGames(Message $message, Context $context): void -// { -// $request = ConnectFourV1::createGetOpenGames($message->body()); -// -// $response = $this->queryBus->handle( -// new OpenGamesQuery( -// $request->getLimit() -// ) -// ); -// -// $context->reply( -// new Message( -// ConnectFourV1::GetOpenGamesResponseType, -// ConnectFourV1::createGetOpenGamesResponse() -// ->setGames( -// array_map( -// static fn(OpenGame $game) => ConnectFourV1::createGetOpenGamesResponse_Game() -// ->setGameId($game->gameId) -// ->setWidth($game->width) -// ->setHeight($game->height) -// ->setPlayerId($game->playerId), -// $response->games() -// ) -// ) -// ->serializeToString() -// ) -// ); -// } + private function handleGetOpenGames(Message $message, Context $context): void + { + $request = ConnectFourV1::createGetOpenGames($message->body()); + + $response = $this->queryBus->handle( + new OpenGamesQuery( + $request->getLimit() + ) + ); + + $context->reply( + new Message( + ConnectFourV1::GetOpenGamesResponseType, + ConnectFourV1::createGetOpenGamesResponse() + ->setGames( + array_map( + static fn(OpenGame $game) => ConnectFourV1::createGetOpenGamesResponse_Game() + ->setGameId($game->gameId) + ->setWidth($game->width) + ->setHeight($game->height) + ->setPlayerId($game->playerId), + $response->games() + ) + ) + ->serializeToString() + ) + ); + } private function handleGetGamesByPlayer(Message $message, Context $context): void {