Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP Ratchet Wamp Broadcast to subscribers on publish event

I am developing a web application where I need real time behavior for the following scenario,

Application will have two type of users Player and Spectator. Players can join an going game while Spectators can mere spectate.

A game will be initialized by an admin user.

A spectator is basically someone who can see the list of people who have joined a game. Of course this needs to be real-time meaning when a player disconnects or a new player joins a game spectator sees the real time list.

To summarize, consider the below example

Spectator_1 joins Clan_101 
Spectator_2 joins Clan_201

Player_1 joins Clan_101 // Need to broadcast this event to Spectator_1
Player_2 joins Clan_101 // Need to broadcast this event to Spectator_1
Player_1 disconnects Clan_101 // // Need to broadcast this event to Spectator_1

Player_11 joins Clan_201 // Need to broadcast this event to Spectator_2
Player_12 joins Clan_201 // // Need to broadcast this event to Spectator_2

Considering an on-going game as a topic/channel (Ratchet\Wamp\Topic), I need to broadcast to spectators on the following events player join and player left to the game/topic on which spectators have subscribed.

I am using Ratchet WebSockets for PHP on server side and autobahn js on client side

Below is the code. So far where I am able to send information to server(from client) when a player joins/disconnects a game. But How do I broadcast this information to spectators(client end) when a player joins or disconnects.

player.html

<script src="scripts/autobahn.js" type="text/javascript"></script>
<script src="scripts/jquery-1.11.2.min.js" type="text/javascript"></script>
<script>
ab.connect(
    'ws://localhost:8080',
     function (session) {
         appSession = session;
         $('#btnJoinGame').on('click',function(){
         session.publish('joingame', ['data','GAME_ID']);
     });                   
 });
</script>

spectator.html

<script>
var conn = new ab.Session(
    'ws://localhost:8080', 
    function() {            
         conn.subscribe('spectator_GAME_ID', function(topic, data) {
            console.log(topic);
            console.log(data);
         });
     },
     function() {            
        console.warn('WebSocket connection closed');
     }    
 );  
 /* OR Using the legacy syntax */
 /*
     ab.connect(
         'ws://localhost:8080',
          function (session) {
              session.subscribe("t1011", function (topic, event) {
                  console.log(event);
              });
          }                       
      );
 */
</script>

Server.php

require __DIR__ . '/vendor/autoload.php';

use Ratchet\Wamp\WampServerInterface;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface as Conn;


class EventHandler implements WampServerInterface, MessageComponentInterface{
     public function __construct(React\EventLoop\StreamSelectLoop $loop){
        $this->loop = $loop;
     }
     public function onSubscribe(Conn $conn, $subscription, $params = array()){   
        $subscription->broadcast($this->data);
     }

     public function onPublish(Conn $conn, $topic, $params, array $exclude, array $eligible) {
        if($topic->getId() === 'joingame'){
            if(!isset($this->data[$params[1]])){
                $this->data[$params[1]] = array($params[0]);
            }else{
                array_push($this->data[$params[1]], $params[0]);
            }            
        }
        /** DOES'NT WORKS **/
        $newtopic = new Ratchet\Wamp\Topic('spectator_GAME_ID');
        $this->onSubscribe($conn,$newtopic);
    }
    /*Omitting other methods for brevity*/
}

$loop   = React\EventLoop\Factory::create();

$webSock = new React\Socket\Server($loop);
$webSock->listen(8080, '0.0.0.0'); // Binding to 0.0.0.0 means remotes can connect
new Ratchet\Server\IoServer(
    new Ratchet\Http\HttpServer(    
        new Ratchet\WebSocket\WsServer(
            new Ratchet\Wamp\WampServer(
                new EventHandler($loop) // This is my class. Pass in the loop!
            )
        )
    ),
    $webSock
);

$loop->run();
like image 267
hitesh israni Avatar asked Nov 03 '15 08:11

hitesh israni


1 Answers

First of all this answer is probably too late for you already although I will answer it for the record.

Once you have established multiple channels in your application: spectator_GAME_ID

You want to be able to see who is playing the game that you are watching. And the reason you are using a WebSocket is so that you can see realtime changes.

You first of all have to understand that the Topics are all different channels/gameId's.

Once you realize this and use the code that is provided on the example page of ratchet itself.

    $entryData = json_decode($entry, true);

    // If the lookup topic object isn't set there is no one to publish to
    if (!array_key_exists($entryData['category'], $this->subscribedTopics)) {
        return;
    }

    $topic = $this->subscribedTopics[$entryData['category']];

    // re-send the data to all the clients subscribed to that category
    $topic->broadcast($entryData);

In their example they use categories in their JSON string you will probably change this to gameId.

Once you have this in place you are able to send data to only the people listening for a certain gameId.


Second part of your question was to send updates to them and how to know what the update is.

The easiest way is by adding a string to the JSON obejct that is being sent

{
     "action": "join",
     "gameId": "123",                  //so that you know to which game to publish it
     "userIdThatJoined": "123456789",  //whatever other data you need
     "userNameThatJoined": "Foo Bar"
}

Once this is sent you should receive it on clientside and check for the action, if the action is 'join' then add that user his name to some list. If the action is 'leave' then remove that user his/her name from the list.

You could update your display in the list of active players with some function that is called after an update or use a simple ng-repeat from angular and then just apply the new values to it.

like image 110
mitchken Avatar answered Oct 15 '22 01:10

mitchken