I am trying to build a multiplayer game using only WebSockets for the chat and the game moves.
My version is laravel 11, I started this before v12 came out. I am also using Laravel Reverb for the WebSocket handling.
A side note: this is not monolithic; the Laravel is an api, and I have a nuxt frontend app.
I created an event called MessageSent
class MessageSent implements ShouldBroadcastNow
{
use InteractsWithSockets, SerializesModels;
public function __construct(protected Game $game, protected string $message)
{ }
public function broadcastOn(): array
{
return [
new PrivateChannel("game.{$this->game->id}.chat"),
];
}
public function broadcastAs(): string
{
return 'message.sent';
}
public function broadcastWith(): array
{
Log::info('Broadcasting message', [
'game' => $this->game->id,
'user' => auth()->user()->name,
'message' => $this->message,
]);
return [
'game' => $this->game->id,
'user' => auth()->user()->name,
'message' => $this->message,
];
}
}
Then created the listener HandleChatMessages
class HandleChatMessages
{
public function handle($event): void
{
Log::info('HandleChatMessages listener triggered', ['event' => $event]);
if (!isset($event->data)) {
Log::error('Event data is not set', ['event' => $event]);
return;
}
$data = $event->data;
if (!is_array($data)) {
Log::error('Event data is not an array', ['data' => $data]);
return;
}
$game = Game::find($data['gameId']);
$message = $data['message'];
if (!$game) {
Log::warning('Game not found', ['gameId' => $data['gameId']]);
return;
}
Log::info('Broadcasting message', ['gameId' => $game->id, 'message' => $message]);
broadcast(new MessageSent($game, $message));
}
}
I created a new provider called EventServiceProvider:
class EventServiceProvider extends ServiceProvider
{
public function register()
{
//
}
public function boot()
{
Event::listen('client-message.sent', HandleChatMessages::class);
Event::listen('client-message.sent', function ($event) {
\Log::info('Event received in EventServiceProvider', ['event' => $event]);
});
}
}
I then added this to the AppServiceProvider:
public function register(): void
{
$this->app->register(EventServiceProvider::class);
}
I then make sure the default broadcasting is reverb in the broadcasting.php:
'default' => env('BROADCAST_CONNECTION', 'reverb'),
In the reverb config file I then make sure the event is being mapped:
'events' => [
'client-message.sent' => 'client-message.sent',
],
I removed the channels.php from the withRouting in the bootstrap app and added:
->withBroadcasting(
__DIR__.'/../routes/channels.php',
['prefix' => 'api/v1', 'middleware' => ['api', 'auth:sanctum']],
)
The reason for this is because the docks state if we are usingprivate channels with sanctum It is needed to put is withBroadcasting().
Finally setup the channel.php, I have not done any logic, I just want it to work at the moment.
Broadcast::channel('game.{gameId}.chat', function ($user, $gameId) {
return true;
});
When I run the:
php artisan reverb:start --debug
Send a message from the nuxt app I get this in the terminal:
Message Received .... 323444322.234444442
1▕ {
2▕ "event": "client-message.sent",
3▕ "data": {
4▕ "gameId": "2",
5▕ "message": "This is a test"
6▕ },
7▕ "channel": "private-game.2.chat"
8▕ }
Message Handled ........ 23423433.342344444
So the nuxt app is connecting and sending a message. But the event is not being triggered to broadcast back.
I know this because I am getting no logging within the laravel.log file. Also, I am not getting a broadcast out to the nuxt app.
I am new to WebSockets on Laravel. Can anyone shed some light on what I am doing incorrectly?