我试图在PHP Ratchet Websocket应用程序中实现pulse

我试图在PHP Ratchet Websocket应用程序中实现pulse,php,websocket,ratchet,Php,Websocket,Ratchet,你好,堆栈溢出 我正在构建一个基于浏览器的纯文本多玩家RPG,用PHP编写,Ratchet作为主干 到目前为止我所拥有的:它运行得非常好。我已经实现了一个简单有效的命令解释程序,它很好地完成了客户机和服务器之间的数据传输。我能够轻松地执行数据库操作,并在我的服务器类内实例化外部类,以用于将信息传递回客户端 我陷入困境的地方:由于某种原因,我的大脑在尝试执行滴答声时崩溃了,在我的游戏中,滴答声是一组每45秒发生一次的事件。它基本上是游戏的心跳,如果没有一个可靠而优雅的实现,我就无法前进。tick需

你好,堆栈溢出

我正在构建一个基于浏览器的纯文本多玩家RPG,用PHP编写,Ratchet作为主干

到目前为止我所拥有的:它运行得非常好。我已经实现了一个简单有效的命令解释程序,它很好地完成了客户机和服务器之间的数据传输。我能够轻松地执行数据库操作,并在我的服务器类内实例化外部类,以用于将信息传递回客户端

我陷入困境的地方:由于某种原因,我的大脑在尝试执行滴答声时崩溃了,在我的游戏中,滴答声是一组每45秒发生一次的事件。它基本上是游戏的心跳,如果没有一个可靠而优雅的实现,我就无法前进。tick需要做很多事情,包括(但不限于):向玩家发送消息、更新玩家重新生成、内存处理等等。通常,所有这些操作都可以编码并放置在更新类中

但我不知道怎样才能让滴答声真正发生。滴答声本身就是一个在我的react循环中每隔45秒发生一次的函数,它应该在服务器启动时启动。它绝对需要是服务器端的。从技术上讲,我可以在客户端实现它,并与数据库中的值同步,但我不想走这条路

我觉得这比我的大脑想象的要容易

我所尝试的: 我试过运行一个简单的递归函数,它使用sleep(45)在计时器上构造我的更新类,但同样,这需要在服务器启动时启动,如果我在服务器类的构造中抛出一个无限循环函数,启动脚本永远不会通过,游戏也永远不会启动

我尝试过使用react附带的onPeriodicTimer函数,但我不知道如何实现它

我曾经尝试过一些疯狂的事情,比如使用NodeJS每45秒向我的服务器发送一条消息,我的解释器捕捉到这条特定的消息,并启动勾选过程。这是我最接近成功实现的一次,但我真的希望能够做到这一点,而无需客户端连接并与服务器通信,这似乎是黑客行为

我已经尝试过ZeroMQ实现与上面相同的目标(一个向我的服务器发送消息以触发更新的客户端),但是,我不想让客户端侦听器一直连接到游戏才能运行,而且,ZeroMQ对于这么小的东西来说需要处理很多事情。。我运气不好

必须有更好的方法来实现这一点。任何帮助都将不胜感激

作为参考,这里是我的套接字应用程序工作的基本概要。首先,我使用了Ratchet网站上的“Hello World”教程

因此,我有一个startup.php脚本,运行该脚本来初始化服务器类,它接受来自连接客户端的消息。onMessage,实例化一个解释器类,它解析消息并在数据库表中查找客户端传递的命令,该数据库表加载该命令的相应类和方法,该数据基于onMessage函数,调用该命令的类和方法,并将结果传回客户端

TLDR:如何向Ratchet websocket服务器添加一个重复功能,该服务器可以每45秒向连接的客户端发送消息

以下是服务器类:

    class Server implements MessageComponentInterface

{
    public $clients;

    public function __construct() 
    {
        $this->clients = new \SplObjectStorage;
        //exec("nodejs ../bin/java.js", $output);
    }

    public function onOpen(ConnectionInterface $conn) 
    {
        $conn->connected_state = 0;
        $this->clients->attach($conn);

        // Initiate login
        $login = new Login('CONN_GETNAME');

        if($login->success)
        {
            $conn->send($login->output);
            $conn->connected_state = $login->new_state;
            $conn->chData = new Character();
        }

        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) 
    {
        if($msg == 'do_tick')
        {
            echo "a tick happened <br>";

        }
        else 
        {
            if($from->connected_state == 'CONN_CONNECTED' || $msg == 'chardump')
            {
                $interpretor = new Interpret($msg);

                if($interpretor->success)
                {
                    $action_class_var = $interpretor->class;
                    $action_method_var = $interpretor->function;

                    $action_class = new $action_class_var($this->clients, $from, $interpretor->msg);
                    $action = $action_class->{$action_method_var}();

                    foreach($this->clients as $client)
                    {
                    if($action->to_room)
                    {
                        if($from != $client)
                        {
                            $client->send($action->to_room);
                        }
                    }

                    if($action->to_global)
                    {
                        if($from != $client)
                        {
                            $client->send($action->to_global);
                        }
                    }

                    if($action->to_char)
                    {

                        $client->send($action->to_char);
                    }
                    }
                }
                else
                {
                    $from->send('Huh?');
                }
            }
            else
            {
                $login = new Login($from->connected_state, $msg, $from);

                $from->connected_state = $login->new_state;

                if($login->char_data && count($login->char_data)>0)
                {
                foreach($login->char_data as $key=>$val)
                {
                    $from->chData->{$key} = $val;
                }
                }

                $from->send($login->output);
            }
        }
    }

    public function onClose(ConnectionInterface $conn) {
    $this->clients->detach($conn);
        echo "Connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
    echo "An error has occurred: {$e->getMessage()}\n";
        $conn->close();
    }
类服务器实现MessageComponentInterface
{
公众客户;
公共函数构造()
{
$this->clients=new\SplObjectStorage;
//exec(“nodejs../bin/java.js”,$output);
}
公共功能onOpen(连接接口$conn)
{
$conn->connected_state=0;
$this->clients->attach($conn);
//启动登录
$login=新登录名('CONN_GETNAME');
如果($login->success)
{
$conn->send($login->output);
$conn->connected_state=$login->new_state;
$conn->chData=新字符();
}
echo“新建连接!({$conn->resourceId})\n”;
}
消息上的公共函数(ConnectionInterface$from,$msg)
{
如果($msg=='do_tick')
{
回声“滴答声发生
”; } 其他的 { 如果($from->connected_state=='CONN_connected'.\124;$msg=='chardump') { $EXPLATOR=新解释($msg); 如果($interpreator->success) { $action\u class\u var=$interpreator->class; $action\u method\u var=$interpreator->function; $action\u class=new$action\u class\u var($this->clients,$from,$interpreator->msg); $action=$action_类->{$action_方法_var}(); foreach($this->clients as$client) { 如果($action->to_room) { 如果($from!=$client) { $client->send($action->to_room); } } 如果($action->to_global) { 如果($from!=$client) { $client->send($action->to_global); } } 如果($action->to_char) { $client->send($action->to_char); } } } 其他的 { $from->send('Huh?'); } } 其他的 { $login=新登录($from->connected_state,$msg,$from); $from->connected_state=$login->new_state; 如果($login)
<?php 
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use React\Socket\Server as Reactor;
use React\EventLoop\Factory as LoopFactory;;
require dirname(__DIR__) . '/vendor/autoload.php';

foreach(new DirectoryIterator(dirname(__DIR__) .'/src/') as $fileInfo)
{
    if($fileInfo->isDot() || $fileInfo->isDir())
    {
        continue;
    }

    require_once(dirname(__DIR__) . '/src/' . $fileInfo->getFilename());
}

$clients = null;

class Server implements MessageComponentInterface
{   
    public function __construct(React\EventLoop\LoopInterface $loop) 
    {
        global $clients;
        $clients = new \SplObjectStorage;

        // Breathe life into the game
        $loop->addPeriodicTimer(40, function() 
        {
            $this->doTick();
        });
    }

    public function onOpen(ConnectionInterface $ch) 
    {
        global $clients;
        $clients->attach($ch);

        $controller = new Controller($ch);
        $controller->login();
    }

    public function onMessage(ConnectionInterface $ch, $args) 
    {
        $controller = new Controller($ch, $args);

        if($controller->isLoggedIn())
        {
            $controller->interpret();
        }
        else
        {
            $controller->login();
        }

    }

    public function onClose(ConnectionInterface $conn) 
    {
        global $clients;
        $clients->detach($conn);
        echo "Connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e) 
    {
        echo "An error has occurred: {$e->getMessage()}\n";
        $conn->close();
    }

    public function doTick()
    {
        global $clients;
        $update = new Update($clients);
    }
}

$loop = LoopFactory::create();
$socket = new Reactor($loop);
$socket->listen(9000, 'xx.xx.xx.xxx');
$server = new IoServer(new HttpServer(new WsServer(new Server($loop))), $socket, $loop);
$server->run();