chg: dev: replace the legacy gos/web-socket-bundle & replace it with Mercure protocol #4
This commit is contained in:
@@ -1,79 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the SplendidBear Websites' projects.
|
||||
*
|
||||
* Copyright (c) 2026 @ www.splendidbear.org
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use Gos\Bundle\WebSocketBundle\Server\App\Registry\ServerRegistry;
|
||||
use Gos\Bundle\WebSocketBundle\Server\ServerLauncherInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Replaces gos WebsocketServerCommand to avoid the --profile option conflict
|
||||
* introduced when Symfony 6.4 added --profile as a global console option.
|
||||
*/
|
||||
#[AsCommand(name: 'gos:websocket:server', description: 'Starts the websocket server')]
|
||||
final class WebsocketServerCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ServerLauncherInterface $serverLauncher,
|
||||
private readonly string $host,
|
||||
private readonly int $port,
|
||||
private readonly ?ServerRegistry $serverRegistry = null,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'Name of the server to start, launches the first registered server if not specified')
|
||||
->addOption('ws-profile', 'm', InputOption::VALUE_NONE, 'Enable profiling of the websocket server')
|
||||
->addOption('host', 'a', InputOption::VALUE_OPTIONAL, 'The hostname of the websocket server')
|
||||
->addOption('port', 'p', InputOption::VALUE_OPTIONAL, 'The port of the websocket server');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
/** @var string $name */
|
||||
$name = $input->getArgument('name');
|
||||
|
||||
/** @var string $host */
|
||||
$host = null === $input->getOption('host') ? $this->host : $input->getOption('host');
|
||||
|
||||
/** @var int|string $port */
|
||||
$port = null === $input->getOption('port') ? $this->port : $input->getOption('port');
|
||||
|
||||
if (!is_numeric($port)) {
|
||||
throw new InvalidArgumentException('The port option must be a numeric value.');
|
||||
}
|
||||
|
||||
/** @var bool $profile */
|
||||
$profile = (bool) $input->getOption('ws-profile');
|
||||
|
||||
$this->serverLauncher->launch($name, $host, (int) $port, $profile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('name') && null !== $this->serverRegistry) {
|
||||
$suggestions->suggestValues(array_keys($this->serverRegistry->getServers()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
@@ -29,8 +28,11 @@ class GameController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
#[Autowire(env: 'APP_ENV')]
|
||||
private readonly string $env,
|
||||
private readonly RequestStack $request,
|
||||
private readonly string $env,
|
||||
#[Autowire(env: 'MERCURE_PUBLIC_URL')]
|
||||
private readonly string $mercurePublicUrl,
|
||||
#[Autowire(env: 'MERCURE_SUBSCRIBER_JWT')]
|
||||
private readonly string $mercureSubscriberJwt,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -42,8 +44,9 @@ class GameController extends AbstractController
|
||||
public function play(): Response
|
||||
{
|
||||
return $this->render('Game/play.html.twig', [
|
||||
'env' => $this->env,
|
||||
'ssl' => $this->request->getCurrentRequest()->isSecure() ? 'true' : 'false',
|
||||
'env' => $this->env,
|
||||
'mercure_hub_url' => $this->mercurePublicUrl,
|
||||
'mercure_subscriber_jwt' => $this->mercureSubscriberJwt,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
98
src/Controller/MercureController.php
Normal file
98
src/Controller/MercureController.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of the SplendidBear Websites' projects.
|
||||
*
|
||||
* Copyright (c) 2019 @ www.splendidbear.org
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Util\RpcManager;
|
||||
use App\Util\TopicManager;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Class MercureController
|
||||
*
|
||||
* Handles HTTP API endpoints that replace the former WebSocket RPC and Topic handlers.
|
||||
* Client → Server communication is via HTTP POST/GET.
|
||||
* Server → Client broadcasting is via Mercure (SSE).
|
||||
*
|
||||
* @package App\Controller
|
||||
* @author Lang <https://www.splendidbear.org>
|
||||
* @category Class
|
||||
* @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
|
||||
* @link www.splendidbear.org
|
||||
* @since 2026. 04. 09.
|
||||
*/
|
||||
class MercureController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TopicManager $topicManager,
|
||||
private readonly RpcManager $rpcManager,
|
||||
) {
|
||||
}
|
||||
|
||||
/** POST /api/game/start — save the grid and create the PlayedGame record */
|
||||
public function start(Request $request): JsonResponse
|
||||
{
|
||||
$data = $request->toArray();
|
||||
$result = $this->rpcManager->saveGrid([$data['grid'], $data['gameAssoc']]);
|
||||
|
||||
return $this->json(['success' => $result]);
|
||||
}
|
||||
|
||||
/** GET /api/game/connect/{gameAssoc} — return grid + current user info (base64 JSON) */
|
||||
public function connect(string $gameAssoc): Response
|
||||
{
|
||||
$payload = $this->rpcManager->getConnectInformation($gameAssoc);
|
||||
|
||||
return new Response($payload, Response::HTTP_OK, ['Content-Type' => 'text/plain']);
|
||||
}
|
||||
|
||||
/** POST /api/game/join/{gameAssoc} — register the player, broadcast subscription event via Mercure */
|
||||
public function join(string $gameAssoc, Request $request): JsonResponse
|
||||
{
|
||||
$this->topicManager->subscribe($gameAssoc, $this->resolveUserName($request), $this->getUser());
|
||||
|
||||
return $this->json(['success' => true]);
|
||||
}
|
||||
|
||||
/** POST /api/game/step/{gameAssoc} — persist the step and broadcast game event via Mercure */
|
||||
public function step(string $gameAssoc, Request $request): JsonResponse
|
||||
{
|
||||
$this->topicManager->publish($gameAssoc, $this->resolveUserName($request), $request->toArray());
|
||||
|
||||
return $this->json(['success' => true]);
|
||||
}
|
||||
|
||||
/** POST /api/game/leave/{gameAssoc} — broadcast disconnect event via Mercure */
|
||||
public function leave(string $gameAssoc, Request $request): JsonResponse
|
||||
{
|
||||
$this->topicManager->unSubscribe($gameAssoc, $this->resolveUserName($request));
|
||||
|
||||
return $this->json(['success' => true]);
|
||||
}
|
||||
|
||||
private function resolveUserName(Request $request): string
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
if (null !== $user) {
|
||||
return $user->getUserIdentifier();
|
||||
}
|
||||
|
||||
$sessionId = $request->getSession()->getId();
|
||||
if (empty($sessionId)) {
|
||||
$sessionId = bin2hex(random_bytes(16));
|
||||
}
|
||||
|
||||
return 'anon_' . $sessionId;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ namespace App\Entity;
|
||||
|
||||
use App\Repository\PlayedGameRepository;
|
||||
use DateTime;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
@@ -19,6 +21,7 @@ use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Mapping\JoinColumn;
|
||||
use Doctrine\ORM\Mapping\ManyToOne;
|
||||
use Doctrine\ORM\Mapping\OneToMany;
|
||||
use Doctrine\ORM\Mapping\OneToOne;
|
||||
|
||||
/**
|
||||
@@ -80,107 +83,28 @@ class PlayedGame
|
||||
#[JoinColumn(name: 'blue_anon', referencedColumnName: 'id', nullable: true)]
|
||||
private ?Gamer $blueAnon = null;
|
||||
|
||||
#[OneToOne(mappedBy: 'playedGame')]
|
||||
private ?Step $step = null;
|
||||
#[OneToMany(mappedBy: 'playedGame', targetEntity: Step::class)]
|
||||
private Collection $steps;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->steps = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(?int $id): self
|
||||
{
|
||||
$this->id = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGameAssoc(): ?string
|
||||
{
|
||||
return $this->gameAssoc;
|
||||
}
|
||||
|
||||
public function setGameAssoc(?string $gameAssoc): self
|
||||
public function setGameAssoc(?string $gameAssoc): void
|
||||
{
|
||||
$this->gameAssoc = $gameAssoc;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRedPoints(): ?int
|
||||
{
|
||||
return $this->redPoints;
|
||||
}
|
||||
|
||||
public function setRedPoints(?int $redPoints): self
|
||||
{
|
||||
$this->redPoints = $redPoints;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBluePoints(): ?int
|
||||
{
|
||||
return $this->bluePoints;
|
||||
}
|
||||
|
||||
public function setBluePoints(?int $bluePoints): self
|
||||
{
|
||||
$this->bluePoints = $bluePoints;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRedExplodedBomb(): ?bool
|
||||
{
|
||||
return $this->redExplodedBomb;
|
||||
}
|
||||
|
||||
public function setRedExplodedBomb(?bool $redExplodedBomb): self
|
||||
{
|
||||
$this->redExplodedBomb = $redExplodedBomb;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBlueExplodedBomb(): ?bool
|
||||
{
|
||||
return $this->blueExplodedBomb;
|
||||
}
|
||||
|
||||
public function setBlueExplodedBomb(?bool $blueExplodedBomb): self
|
||||
{
|
||||
$this->blueExplodedBomb = $blueExplodedBomb;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getResign(): ?string
|
||||
{
|
||||
return $this->resign;
|
||||
}
|
||||
|
||||
public function setResign(?string $resign): self
|
||||
{
|
||||
$this->resign = $resign;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreated(): ?DateTime
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
public function setCreated(?DateTime $created): self
|
||||
{
|
||||
$this->created = $created;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdated(): ?DateTime
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
|
||||
public function setUpdated(?DateTime $updated): self
|
||||
{
|
||||
$this->updated = $updated;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGrid(): ?Grid
|
||||
@@ -188,10 +112,9 @@ class PlayedGame
|
||||
return $this->grid;
|
||||
}
|
||||
|
||||
public function setGrid(?Grid $grid): self
|
||||
public function setGrid(?Grid $grid): void
|
||||
{
|
||||
$this->grid = $grid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRed(): ?User
|
||||
@@ -199,10 +122,9 @@ class PlayedGame
|
||||
return $this->red;
|
||||
}
|
||||
|
||||
public function setRed(?User $red): self
|
||||
public function setRed(?User $red): void
|
||||
{
|
||||
$this->red = $red;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRedAnon(): ?Gamer
|
||||
@@ -210,10 +132,9 @@ class PlayedGame
|
||||
return $this->redAnon;
|
||||
}
|
||||
|
||||
public function setRedAnon(?Gamer $redAnon): self
|
||||
public function setRedAnon(?Gamer $redAnon): void
|
||||
{
|
||||
$this->redAnon = $redAnon;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBlue(): ?User
|
||||
@@ -221,10 +142,9 @@ class PlayedGame
|
||||
return $this->blue;
|
||||
}
|
||||
|
||||
public function setBlue(?User $blue): self
|
||||
public function setBlue(?User $blue): void
|
||||
{
|
||||
$this->blue = $blue;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBlueAnon(): ?Gamer
|
||||
@@ -232,20 +152,83 @@ class PlayedGame
|
||||
return $this->blueAnon;
|
||||
}
|
||||
|
||||
public function setBlueAnon(?Gamer $blueAnon): self
|
||||
public function setBlueAnon(?Gamer $blueAnon): void
|
||||
{
|
||||
$this->blueAnon = $blueAnon;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStep(): ?Step
|
||||
public function getRedPoints(): ?int
|
||||
{
|
||||
return $this->step;
|
||||
return $this->redPoints;
|
||||
}
|
||||
|
||||
public function setStep(?Step $step): self
|
||||
public function setRedPoints(?int $redPoints): void
|
||||
{
|
||||
$this->step = $step;
|
||||
return $this;
|
||||
$this->redPoints = $redPoints;
|
||||
}
|
||||
|
||||
public function getBluePoints(): ?int
|
||||
{
|
||||
return $this->bluePoints;
|
||||
}
|
||||
|
||||
public function setBluePoints(?int $bluePoints): void
|
||||
{
|
||||
$this->bluePoints = $bluePoints;
|
||||
}
|
||||
|
||||
public function getRedExplodedBomb(): ?bool
|
||||
{
|
||||
return $this->redExplodedBomb;
|
||||
}
|
||||
|
||||
public function setRedExplodedBomb(?bool $redExplodedBomb): void
|
||||
{
|
||||
$this->redExplodedBomb = $redExplodedBomb;
|
||||
}
|
||||
|
||||
public function getBlueExplodedBomb(): ?bool
|
||||
{
|
||||
return $this->blueExplodedBomb;
|
||||
}
|
||||
|
||||
public function setBlueExplodedBomb(?bool $blueExplodedBomb): void
|
||||
{
|
||||
$this->blueExplodedBomb = $blueExplodedBomb;
|
||||
}
|
||||
|
||||
public function getResign(): ?string
|
||||
{
|
||||
return $this->resign;
|
||||
}
|
||||
|
||||
public function setResign(?string $resign): void
|
||||
{
|
||||
$this->resign = $resign;
|
||||
}
|
||||
|
||||
public function getCreated(): ?DateTime
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
public function setCreated(?DateTime $created): void
|
||||
{
|
||||
$this->created = $created;
|
||||
}
|
||||
|
||||
public function getUpdated(): ?DateTime
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
|
||||
public function setUpdated(?DateTime $updated): void
|
||||
{
|
||||
$this->updated = $updated;
|
||||
}
|
||||
|
||||
public function getSteps(): Collection
|
||||
{
|
||||
return $this->steps;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class Step
|
||||
#[Column(nullable: true)]
|
||||
private ?bool $wBomb = null;
|
||||
|
||||
#[ManyToOne(inversedBy: 'step')]
|
||||
#[ManyToOne(inversedBy: 'steps')]
|
||||
private ?PlayedGame $playedGame = null;
|
||||
|
||||
#[Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of the SplendidBear Websites' projects.
|
||||
*
|
||||
* Copyright (c) 2019 @ www.splendidbear.org
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\EventListener;
|
||||
|
||||
use Gos\Bundle\WebSocketBundle\Event\ClientConnectedEvent;
|
||||
use Gos\Bundle\WebSocketBundle\Event\ClientDisconnectedEvent;
|
||||
use Gos\Bundle\WebSocketBundle\Event\ClientErrorEvent;
|
||||
use Gos\Bundle\WebSocketBundle\Event\ClientRejectedEvent;
|
||||
use Gos\Bundle\WebSocketBundle\Event\ServerLaunchedEvent;
|
||||
|
||||
/**
|
||||
* Class MineseekerClientEventListener
|
||||
*
|
||||
* @package App\EventListener
|
||||
* @author Lang <https://www.splendidbear.org>
|
||||
* @category Class
|
||||
* @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
|
||||
* @link www.splendidbear.org
|
||||
* @since 2026. 04. 09.
|
||||
*/
|
||||
class MineseekerClientEventListener
|
||||
{
|
||||
public function onClientConnect(ClientConnectedEvent $event): void
|
||||
{
|
||||
$conn = $event->getConnection();
|
||||
echo $conn->resourceId . ' connected' . PHP_EOL;
|
||||
}
|
||||
|
||||
public function onClientDisconnect(ClientDisconnectedEvent $event): void
|
||||
{
|
||||
$conn = $event->getConnection();
|
||||
echo $conn->resourceId . ' disconnected' . PHP_EOL;
|
||||
}
|
||||
|
||||
public function onClientError(ClientErrorEvent $event): void
|
||||
{
|
||||
$conn = $event->getConnection();
|
||||
$e = $event->getException();
|
||||
echo 'connection error occurred: ' . $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
|
||||
public function onServerStart(ServerLaunchedEvent $event): void
|
||||
{
|
||||
echo 'Server was successfully started !' . PHP_EOL;
|
||||
}
|
||||
|
||||
public function onClientRejected(ClientRejectedEvent $event): void
|
||||
{
|
||||
$origin = $event->getOrigin();
|
||||
echo 'connection rejected from ' . $origin . PHP_EOL;
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,7 @@
|
||||
|
||||
namespace App\Interfaces;
|
||||
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\Wamp\Topic;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* Interface TopicManagerInterface
|
||||
@@ -25,9 +24,9 @@ use Ratchet\Wamp\Topic;
|
||||
*/
|
||||
interface TopicManagerInterface
|
||||
{
|
||||
public function subscribe(Topic $topic, ConnectionInterface $connection): void;
|
||||
public function subscribe(string $gameAssoc, string $userName, ?UserInterface $user): void;
|
||||
|
||||
public function unSubscribe(Topic $topic, ConnectionInterface $connection): void;
|
||||
public function unSubscribe(string $gameAssoc, string $userName): void;
|
||||
|
||||
public function publish(Topic $topic, ConnectionInterface $connection, $event): void;
|
||||
public function publish(string $gameAssoc, string $userName, array $event): void;
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of the SplendidBear Websites' projects.
|
||||
*
|
||||
* Copyright (c) 2019 @ www.splendidbear.org
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Interfaces;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* Interface WebsocketManagerInterface
|
||||
*
|
||||
* @package App\Interfaces
|
||||
* @author Lang <https://www.splendidbear.org>
|
||||
* @category Interface
|
||||
* @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
|
||||
* @link www.splendidbear.org
|
||||
* @since 2026. 04. 09.
|
||||
*/
|
||||
interface WebsocketManagerInterface
|
||||
{
|
||||
public function reConnect(EntityManagerInterface $entityManager): ?EntityManagerInterface;
|
||||
}
|
||||
0
src/Migrations/.gitignore
vendored
0
src/Migrations/.gitignore
vendored
42
src/Migrations/2026/04/Version20260409194708.php
Normal file
42
src/Migrations/2026/04/Version20260409194708.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the SplendidBear Websites' projects.
|
||||
*
|
||||
* Copyright (c) 2026 @ www.splendidbear.org
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Class Version20260409194708
|
||||
*
|
||||
* @package App\Migrations
|
||||
* @author Lang <https://www.splendidbear.org>
|
||||
* @category Class
|
||||
* @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
|
||||
* @link www.splendidbear.org
|
||||
* @since 2026. 04. 09.
|
||||
*/
|
||||
final class Version20260409194708 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Refactor entities';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE step RENAME COLUMN wbomb TO w_bomb');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE step RENAME COLUMN w_bomb TO wbomb');
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of the SplendidBear Websites' projects.
|
||||
*
|
||||
* Copyright (c) 2019 @ www.splendidbear.org
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Periodic;
|
||||
|
||||
use Gos\Bundle\WebSocketBundle\Periodic\PdoPeriodicPing;
|
||||
use Gos\Bundle\WebSocketBundle\Periodic\PeriodicInterface;
|
||||
|
||||
/**
|
||||
* Class MinePeriodic
|
||||
*
|
||||
* @package App\Periodic
|
||||
* @author Lang <https://www.splendidbear.org>
|
||||
* @category Class
|
||||
* @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
|
||||
* @link www.splendidbear.org
|
||||
* @since 2026. 04. 09.
|
||||
*
|
||||
* @method MinePeriodic|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method MinePeriodic|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method MinePeriodic[] findAll()
|
||||
* @method MinePeriodic[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class MinePeriodic implements PeriodicInterface
|
||||
{
|
||||
public function __construct(private PdoPeriodicPing $ping) { }
|
||||
|
||||
/**
|
||||
* This function is executed every 5 seconds.
|
||||
*
|
||||
* For more advanced functionality, try injecting
|
||||
* a Topic Service to perform actions on your
|
||||
* connections every x seconds.
|
||||
*/
|
||||
public function tick(): void
|
||||
{
|
||||
$this->ping->tick();
|
||||
}
|
||||
|
||||
public function getTimeout(): int
|
||||
{
|
||||
return 300;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of the SplendidBear Websites' projects.
|
||||
*
|
||||
* Copyright (c) 2019 @ www.splendidbear.org
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Rpc;
|
||||
|
||||
use App\Util\RpcManager;
|
||||
use Gos\Bundle\WebSocketBundle\Router\WampRequest;
|
||||
use Gos\Bundle\WebSocketBundle\RPC\RpcInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* Class MineseekerRpc
|
||||
*
|
||||
* @package App\Rpc
|
||||
* @author Lang <https://www.splendidbear.org>
|
||||
* @category Class
|
||||
* @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
|
||||
* @link www.splendidbear.org
|
||||
* @since 2026. 04. 09.
|
||||
*/
|
||||
class MineseekerRpc implements RpcInterface
|
||||
{
|
||||
public function __construct(private RpcManager $manager) { }
|
||||
|
||||
/**
|
||||
* Name of RPC, use for pubsub router (see step3)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'mineseeker.rpc';
|
||||
}
|
||||
|
||||
/**
|
||||
* It handles the game starting processes
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
* @param WampRequest $request
|
||||
* @param array $params
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function startGame(ConnectionInterface $connection, WampRequest $request, array $params): bool
|
||||
{
|
||||
return $this->manager->saveGrid($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* It handles when somebody trying to connect to the party
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
* @param WampRequest $request
|
||||
* @param array $params
|
||||
*
|
||||
* @return string Json string for frontend w/ numbering consideration. (=> a number is not string)
|
||||
*/
|
||||
public function connectGame(ConnectionInterface $connection, WampRequest $request, array $params): string
|
||||
{
|
||||
return $this->manager->getConnectInformation($params);
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of the SplendidBear Websites' projects.
|
||||
*
|
||||
* Copyright (c) 2019 @ www.splendidbear.org
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Topic;
|
||||
|
||||
use App\Util\TopicManager;
|
||||
use Gos\Bundle\WebSocketBundle\Router\WampRequest;
|
||||
use Gos\Bundle\WebSocketBundle\Topic\TopicInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\Wamp\Topic;
|
||||
|
||||
/**
|
||||
* Class MineseekerTopic
|
||||
*
|
||||
* @package App\Topic
|
||||
* @author Lang <https://www.splendidbear.org>
|
||||
* @category Class
|
||||
* @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
|
||||
* @link www.splendidbear.org
|
||||
* @since 2026. 04. 09.
|
||||
*/
|
||||
class MineseekerTopic implements TopicInterface
|
||||
{
|
||||
public function __construct(private TopicManager $manager) { }
|
||||
|
||||
/**
|
||||
* Like RPC is will use to prefix the channel
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'mineseeker.topic';
|
||||
}
|
||||
|
||||
/**
|
||||
* This will receive any Subscription requests for this topic.
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
* @param Topic $topic
|
||||
* @param WampRequest $request
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request): void
|
||||
{
|
||||
$this->manager->subscribe($topic, $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will receive any UnSubscription requests for this topic.
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
* @param Topic $topic
|
||||
* @param WampRequest $request
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onUnSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request): void
|
||||
{
|
||||
$this->manager->unSubscribe($topic, $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will receive any Publish requests for this topic.
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
* @param Topic $topic
|
||||
* @param WampRequest $request
|
||||
* @param $event
|
||||
* @param array $exclude
|
||||
* @param array $eligible
|
||||
*
|
||||
* @return mixed|void
|
||||
* @internal param Topic $Topic
|
||||
* @internal param array $eligibles
|
||||
*/
|
||||
public function onPublish(
|
||||
ConnectionInterface $connection,
|
||||
Topic $topic,
|
||||
WampRequest $request,
|
||||
$event,
|
||||
array $exclude,
|
||||
array $eligible
|
||||
) {
|
||||
$this->manager->publish($topic, $connection, $event);
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,10 @@ use App\Entity\PlayedGame;
|
||||
use App\Interfaces\RpcManagerInterface;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\ORMException;
|
||||
use Exception;
|
||||
use JsonException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class RpcManager
|
||||
@@ -30,13 +31,12 @@ use Psr\Log\LoggerInterface;
|
||||
* @link www.splendidbear.org
|
||||
* @since 2026. 04. 09.
|
||||
*/
|
||||
class RpcManager extends WebsocketManager implements RpcManagerInterface
|
||||
class RpcManager implements RpcManagerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManagerInterface $entityManager,
|
||||
private LoggerInterface $logger,
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly LoggerInterface $logger,
|
||||
) {
|
||||
parent::__construct($logger);
|
||||
}
|
||||
|
||||
public function getConnectInformation($params): string
|
||||
@@ -45,17 +45,33 @@ class RpcManager extends WebsocketManager implements RpcManagerInterface
|
||||
$grid = $this->getGrid($gameAssoc);
|
||||
$users = null !== $grid ? $this->getUsers($gameAssoc) : null;
|
||||
|
||||
return base64_encode(json_encode([
|
||||
'grid' => $grid,
|
||||
'users' => $users,
|
||||
], JSON_THROW_ON_ERROR, 512));
|
||||
try {
|
||||
return base64_encode(json_encode([
|
||||
'grid' => $grid,
|
||||
'users' => $users,
|
||||
], JSON_THROW_ON_ERROR, 512));
|
||||
} catch (JsonException $e) {
|
||||
throw new RuntimeException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function saveGrid($data): bool
|
||||
{
|
||||
$existingGame = $this->entityManager
|
||||
->getRepository(PlayedGame::class)
|
||||
->findOneByGameAssoc($data[1]);
|
||||
|
||||
if (null !== $existingGame) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$playedGame = new PlayedGame();
|
||||
$grid = new Grid();
|
||||
$rows = json_decode(base64_decode($data[0]), true, 512, JSON_THROW_ON_ERROR);
|
||||
try {
|
||||
$rows = json_decode(base64_decode($data[0]), true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (JsonException $e) {
|
||||
throw new RuntimeException($e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
foreach ($rows as $row) {
|
||||
@@ -66,7 +82,6 @@ class RpcManager extends WebsocketManager implements RpcManagerInterface
|
||||
/** Save Row */
|
||||
$gridRow->setGrid($grid);
|
||||
$this->entityManager->persist($gridRow);
|
||||
|
||||
}
|
||||
|
||||
/** Save Grid */
|
||||
@@ -81,8 +96,6 @@ class RpcManager extends WebsocketManager implements RpcManagerInterface
|
||||
$this->entityManager->persist($playedGame);
|
||||
|
||||
$this->entityManager->flush();
|
||||
} catch (ORMException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
|
||||
@@ -10,20 +10,20 @@
|
||||
|
||||
namespace App\Util;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Entity\Gamer;
|
||||
use App\Entity\PlayedGame;
|
||||
use App\Entity\Step;
|
||||
use App\Entity\User;
|
||||
use App\Interfaces\TopicManagerInterface;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Exception;
|
||||
use Gos\Bundle\WebSocketBundle\Client\ClientManipulatorInterface;
|
||||
use RuntimeException;
|
||||
use JsonException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\Wamp\Topic;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Mercure\HubInterface;
|
||||
use Symfony\Component\Mercure\Update;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* Class TopicManager
|
||||
@@ -35,79 +35,110 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
* @link www.splendidbear.org
|
||||
* @since 2026. 04. 09.
|
||||
*/
|
||||
class TopicManager extends WebsocketManager implements TopicManagerInterface
|
||||
class TopicManager implements TopicManagerInterface
|
||||
{
|
||||
public function __construct(
|
||||
protected ClientManipulatorInterface $clientManipulator,
|
||||
protected EntityManagerInterface $entityManager,
|
||||
protected RequestStack $requestStack,
|
||||
protected LoggerInterface $logger
|
||||
)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
private readonly HubInterface $hub,
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly LoggerInterface $logger
|
||||
) {
|
||||
}
|
||||
|
||||
public function subscribe(Topic $topic, ConnectionInterface $connection): void
|
||||
public function subscribe(string $gameAssoc, string $userName, ?UserInterface $user): void
|
||||
{
|
||||
/** this will broadcast the message to ALL subscribers of this topic. */
|
||||
$user = $this->clientManipulator->getClient($connection);
|
||||
$userName = $user->getUserIdentifier() ?: 'anon_' . $connection->resourceId;
|
||||
$playedGame = $this->getPlayedGame($gameAssoc);
|
||||
if (null === $playedGame) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** if more user wants to connect than 2 to one channel */
|
||||
if ($topic->count() > 2) {
|
||||
$topic->remove($connection);
|
||||
} else {
|
||||
$users = $this->controlUsers($topic, $userName, $user);
|
||||
$users = $this->getUserCollection($playedGame);
|
||||
$count = $this->getPlayerCount($users);
|
||||
$isKnown = in_array($userName, array_filter(array_values($users)), true);
|
||||
|
||||
$topic->broadcast([
|
||||
'userTopicId' => $connection->resourceId,
|
||||
'channel' => $topic->getId(),
|
||||
'user' => $userName,
|
||||
'userCnt' => $topic->count(),
|
||||
'users' => $users
|
||||
]);
|
||||
/** Reject a third player who is not a reconnecting player */
|
||||
if ($count >= 2 && !$isKnown) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** Save the player to the database on a fresh join */
|
||||
if (!$isKnown && $count < 2) {
|
||||
$users = $this->saveUserToDb($gameAssoc, $userName, $user, $count + 1);
|
||||
$count = $this->getPlayerCount($users);
|
||||
}
|
||||
|
||||
$topic = 'mineseeker/channel/' . $gameAssoc;
|
||||
|
||||
try {
|
||||
$this->hub->publish(new Update(
|
||||
$topic,
|
||||
json_encode([
|
||||
'userTopicId' => $userName,
|
||||
'channel' => $topic,
|
||||
'user' => $userName,
|
||||
'userCnt' => $count,
|
||||
'users' => $users,
|
||||
], JSON_THROW_ON_ERROR)
|
||||
));
|
||||
} catch (JsonException $e) {
|
||||
throw new RuntimeException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function unSubscribe(Topic $topic, ConnectionInterface $connection): void
|
||||
public function unSubscribe(string $gameAssoc, string $userName): void
|
||||
{
|
||||
/** This will broadcasts the message to ALL subscribers of this topic. */
|
||||
$topic->broadcast(['msg' => $connection->resourceId . ' has left ' . $topic->getId()]);
|
||||
$topic = 'mineseeker/channel/' . $gameAssoc;
|
||||
|
||||
$this->hub->publish(new Update(
|
||||
$topic,
|
||||
json_encode(['msg' => $userName . ' has left ' . $topic])
|
||||
));
|
||||
}
|
||||
|
||||
public function publish(Topic $topic, ConnectionInterface $connection, $event): void
|
||||
public function publish(string $gameAssoc, string $userName, array $event): void
|
||||
{
|
||||
$user = $this->clientManipulator->getClient($connection);
|
||||
$userName = $user->getUserIdentifier();
|
||||
|
||||
/** Save every step by user to db */
|
||||
null === $event['resign']
|
||||
? $this->saveStepToDb($topic, $event)
|
||||
: $this->saveResignToDb($topic, $event['resign']);
|
||||
? $this->saveStepToDb($gameAssoc, $event)
|
||||
: $this->saveResignToDb($gameAssoc, $event['resign']);
|
||||
|
||||
$topic->broadcast([
|
||||
'userTopicId' => $connection->resourceId,
|
||||
'channel' => $topic->getId(),
|
||||
'user' => $userName,
|
||||
'userCnt' => $topic->count(),
|
||||
'data' => $event
|
||||
]);
|
||||
$playedGame = $this->getPlayedGame($gameAssoc);
|
||||
$users = $this->getUserCollection($playedGame);
|
||||
$count = $this->getPlayerCount($users);
|
||||
$topic = 'mineseeker/channel/' . $gameAssoc;
|
||||
|
||||
try {
|
||||
$this->hub->publish(new Update(
|
||||
$topic,
|
||||
json_encode([
|
||||
'userTopicId' => $userName,
|
||||
'channel' => $topic,
|
||||
'user' => $userName,
|
||||
'userCnt' => $count,
|
||||
'data' => $event,
|
||||
], JSON_THROW_ON_ERROR)
|
||||
));
|
||||
} catch (JsonException $e) {
|
||||
throw new RuntimeException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Resign event to database
|
||||
*
|
||||
* @param $topic
|
||||
* @param $color
|
||||
*/
|
||||
private function saveResignToDb(Topic $topic, $color): void
|
||||
private function getPlayedGame(string $gameAssoc): ?PlayedGame
|
||||
{
|
||||
$gameAssoc = explode('/', $topic->getId())[2];
|
||||
|
||||
/** @var PlayedGame $playedGame */
|
||||
$playedGame = $this->entityManager
|
||||
return $this->entityManager
|
||||
->getRepository(PlayedGame::class)
|
||||
->findOneByGameAssoc($gameAssoc);
|
||||
}
|
||||
|
||||
private function getPlayerCount(array $users): int
|
||||
{
|
||||
$red = '' !== $users['red'] || '' !== $users['redAnon'] ? 1 : 0;
|
||||
$blue = '' !== $users['blue'] || '' !== $users['blueAnon'] ? 1 : 0;
|
||||
|
||||
return $red + $blue;
|
||||
}
|
||||
|
||||
private function saveResignToDb(string $gameAssoc, string $color): void
|
||||
{
|
||||
$playedGame = $this->getPlayedGame($gameAssoc);
|
||||
|
||||
$playedGame->setResign($color);
|
||||
|
||||
@@ -115,30 +146,17 @@ class TopicManager extends WebsocketManager implements TopicManagerInterface
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save steps and point information to database
|
||||
*
|
||||
* @param $topic
|
||||
* @param $event
|
||||
*/
|
||||
private function saveStepToDb(Topic $topic, $event): void
|
||||
private function saveStepToDb(string $gameAssoc, array $event): void
|
||||
{
|
||||
try {
|
||||
$gameAssoc = explode('/', $topic->getId())[2];
|
||||
|
||||
/** @var PlayedGame $playedGame */
|
||||
$playedGame = $this->entityManager
|
||||
->getRepository(PlayedGame::class)
|
||||
->findOneByGameAssoc($gameAssoc);
|
||||
$playedGame = $this->getPlayedGame($gameAssoc);
|
||||
|
||||
$step = new Step();
|
||||
|
||||
$step->setRow($event['coords'][0]);
|
||||
$step->setCol($event['coords'][1]);
|
||||
$step->setWBomb($event['bomb']);
|
||||
$step->setPlayedGame($playedGame);
|
||||
$step->setCreated(new DateTime());
|
||||
|
||||
$this->entityManager->persist($step);
|
||||
|
||||
$playedGame->setBluePoints($event['bluePoints']);
|
||||
@@ -146,7 +164,6 @@ class TopicManager extends WebsocketManager implements TopicManagerInterface
|
||||
$playedGame->setBlueExplodedBomb($event['blueExplodedBomb'] ? true : null);
|
||||
$playedGame->setRedExplodedBomb($event['redExplodedBomb'] ? true : null);
|
||||
$playedGame->setUpdated(new DateTime());
|
||||
|
||||
$this->entityManager->persist($playedGame);
|
||||
|
||||
$this->entityManager->flush();
|
||||
@@ -155,62 +172,11 @@ class TopicManager extends WebsocketManager implements TopicManagerInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Control all users in a channel
|
||||
*
|
||||
* @param Topic $topic
|
||||
* @param string $userName
|
||||
* @param $user
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function controlUsers(Topic $topic, string $userName, TokenInterface $user): array
|
||||
private function saveUserToDb(string $gameAssoc, string $userName, ?UserInterface $user, int $count): array
|
||||
{
|
||||
$gameAssoc = explode('/', $topic->getId())[2];
|
||||
$playedGame = $this->getPlayedGame($gameAssoc);
|
||||
|
||||
/** @var PlayedGame $playedGame */
|
||||
$playedGame = $this->entityManager
|
||||
->getRepository(PlayedGame::class)
|
||||
->findOneByGameAssoc($gameAssoc);
|
||||
|
||||
/** @var $users {array} */
|
||||
$users = $this->getUserCollection($playedGame);
|
||||
|
||||
$red = '' !== $users['red'] || '' !== $users['redAnon'] ? 1 : 0;
|
||||
$blue = '' !== $users['blue'] || '' !== $users['blueAnon'] ? 1 : 0;
|
||||
$one = $topic->count() === 1;
|
||||
$two = $topic->count() === 2;
|
||||
|
||||
/** This checks it is a reconnection */
|
||||
if (($one && ($red + $blue === 0)) || ($two && ($red + $blue === 1))) {
|
||||
/** @var $users {array} w/ save users to database */
|
||||
$users = $this->saveUserToDb($topic, $userName, $user, $topic->count());
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save user data to database
|
||||
*
|
||||
* @param $topic
|
||||
* @param $userName
|
||||
* @param $user
|
||||
* @param $count
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function saveUserToDb(Topic $topic, string $userName, TokenInterface $user, $count)
|
||||
{
|
||||
$gameAssoc = explode('/', $topic->getId())[2];
|
||||
|
||||
/** @var PlayedGame $playedGame */
|
||||
$playedGame = $this->entityManager
|
||||
->getRepository(PlayedGame::class)
|
||||
->findOneByGameAssoc($gameAssoc);
|
||||
|
||||
/** when the user is not anonym */
|
||||
null !== $user->getUser()
|
||||
null !== $user
|
||||
? $this->saveRegisteredUser($userName, $count, $playedGame)
|
||||
: $this->saveAnonUser($userName, $count, $playedGame);
|
||||
|
||||
@@ -220,23 +186,15 @@ class TopicManager extends WebsocketManager implements TopicManagerInterface
|
||||
return $this->getUserCollection($playedGame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the registered user to the database
|
||||
*
|
||||
* @param string $userName
|
||||
* @param int $count
|
||||
* @param PlayedGame $playedGame
|
||||
*/
|
||||
private function saveRegisteredUser(string $userName, int $count, PlayedGame $playedGame)
|
||||
private function saveRegisteredUser(string $userName, int $count, PlayedGame $playedGame): void
|
||||
{
|
||||
/** @var User $FOSUser */
|
||||
/** @var User $user */
|
||||
$user = $this->entityManager
|
||||
->getRepository(User::class)
|
||||
->findOneByUsername($userName);
|
||||
|
||||
try {
|
||||
if ($count === 1) {
|
||||
/** @var $random {integer} Active player: red: 0, blue: 1 */
|
||||
$random = random_int(0, 1);
|
||||
!$random ? $playedGame->setRed($user) : $playedGame->setBlue($user);
|
||||
} else {
|
||||
@@ -249,17 +207,8 @@ class TopicManager extends WebsocketManager implements TopicManagerInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save anonymous Gamer to database
|
||||
*
|
||||
* @param string $userName
|
||||
* @param int $count
|
||||
* @param PlayedGame $playedGame
|
||||
*/
|
||||
private function saveAnonUser(string $userName, int $count, PlayedGame $playedGame)
|
||||
private function saveAnonUser(string $userName, int $count, PlayedGame $playedGame): void
|
||||
{
|
||||
// $request = $this->requestStack->getCurrentRequest(); // TODO nem megy...
|
||||
|
||||
try {
|
||||
$anon = new Gamer();
|
||||
$anon->setUsername($userName);
|
||||
@@ -267,7 +216,6 @@ class TopicManager extends WebsocketManager implements TopicManagerInterface
|
||||
$this->entityManager->persist($anon);
|
||||
|
||||
if ($count === 1) {
|
||||
/** @var $random {integer} Active player: red: 0, blue: 1 */
|
||||
$random = random_int(0, 1);
|
||||
!$random ? $playedGame->setRedAnon($anon) : $playedGame->setBlueAnon($anon);
|
||||
} else {
|
||||
@@ -280,20 +228,13 @@ class TopicManager extends WebsocketManager implements TopicManagerInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user collection from PlayedGame entity
|
||||
*
|
||||
* @param $playedGame
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getUserCollection(PlayedGame $playedGame): array
|
||||
{
|
||||
return [
|
||||
'red' => null !== $playedGame->getRed() ? $playedGame->getRed()->getUsername() : '',
|
||||
'blue' => null !== $playedGame->getBlue() ? $playedGame->getBlue()->getUsername() : '',
|
||||
'redAnon' => null !== $playedGame->getRedAnon() ? $playedGame->getRedAnon()->getUserName() : '',
|
||||
'blueAnon' => null !== $playedGame->getBlueAnon() ? $playedGame->getBlueAnon()->getUserName() : ''
|
||||
'red' => null !== $playedGame->getRed() ? $playedGame->getRed()->getUsername() : '',
|
||||
'blue' => null !== $playedGame->getBlue() ? $playedGame->getBlue()->getUsername() : '',
|
||||
'redAnon' => null !== $playedGame->getRedAnon() ? $playedGame->getRedAnon()->getUserName() : '',
|
||||
'blueAnon' => null !== $playedGame->getBlueAnon() ? $playedGame->getBlueAnon()->getUserName() : '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of the SplendidBear Websites' projects.
|
||||
*
|
||||
* Copyright (c) 2019 @ www.splendidbear.org
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Util;
|
||||
|
||||
use App\Interfaces\WebsocketManagerInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class WebsocketManager
|
||||
*
|
||||
* @package App\Util
|
||||
* @author Lang <https://www.splendidbear.org>
|
||||
* @category Class
|
||||
* @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
|
||||
* @link www.splendidbear.org
|
||||
* @since 2026. 04. 09.
|
||||
*/
|
||||
class WebsocketManager implements WebsocketManagerInterface
|
||||
{
|
||||
public function __construct(private LoggerInterface $logger) { }
|
||||
|
||||
public function reConnect(EntityManagerInterface $entityManager): ?EntityManagerInterface
|
||||
{
|
||||
try {
|
||||
$connection = $entityManager->getConnection();
|
||||
|
||||
if (false === $connection->ping()) {
|
||||
$connection->close();
|
||||
$connection->connect();
|
||||
}
|
||||
} catch (RuntimeException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
|
||||
return $entityManager;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user