Players open a no-login link and see live cursors and dragged gear pieces synced in real time, backed by a small Workerman WebSocket relay (src/Command/PuzzleRelayCommand.php) run alongside symfony serve - no Docker/Mercure hub needed. The board renders the wall artwork with a spare-gears tray, and a "Reset puzzle" button (visible only to logged-in users, verified server-side via a signed token) lets the DM wipe the shared state for everyone. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
39 lines
1.4 KiB
PHP
39 lines
1.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Controller;
|
|
|
|
use App\Puzzle\PuzzleResetTokenSigner;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\Routing\Attribute\Route;
|
|
|
|
/**
|
|
* Shared, no-login puzzle screens: everyone with the link sees the same
|
|
* board, the same pieces, and each other's cursors, synced over the
|
|
* WebSocket relay in {@see \App\Command\PuzzleRelayCommand}.
|
|
*/
|
|
final class PuzzleController extends AbstractController
|
|
{
|
|
public function __construct(
|
|
#[Autowire(env: 'PUZZLE_RELAY_PUBLIC_URL')]
|
|
private readonly string $relayPublicUrl,
|
|
private readonly PuzzleResetTokenSigner $resetTokenSigner,
|
|
) {
|
|
}
|
|
|
|
#[Route('/puzzle/{room}', name: 'app_puzzle_room', requirements: ['room' => '[a-z0-9-]+'], methods: ['GET'])]
|
|
public function room(string $room): Response
|
|
{
|
|
return $this->render('puzzle/room.html.twig', [
|
|
'room' => $room,
|
|
'relayPublicUrl' => $this->relayPublicUrl,
|
|
// Only a logged-in user gets a token that lets them reset the shared
|
|
// board; anonymous players can play but not wipe it for everyone.
|
|
'resetToken' => $this->getUser() ? $this->resetTokenSigner->sign($room) : null,
|
|
]);
|
|
}
|
|
}
|