我试图实现脉冲到一个PHP棘轮Websocket应用程序



Hello Stack Overflow,

我正在构建一个基于浏览器的文本只多人RPG写在PHP棘轮为骨干。

我目前所拥有的:它工作得很好。我已经实现了一个简单而有效的命令解释器,它可以很好地在客户机和服务器之间传输数据。我可以很容易地执行数据库操作,并在我的服务器类中实例化外部类,以便将信息传递回客户端。

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

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

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

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

我试过使用onPeriodicTimer函数,与反应,但我不知道如何实现它。

我尝试过一些疯狂的事情,比如使用node js每45秒向我的服务器发送一条消息,我的解释器捕捉到这个特定的消息并启动tick进程。这是我最接近成功实现的,但我真的希望能够做到这一点,而不需要客户端连接并与服务器通信,这似乎很糟糕。

我已经尝试过ZeroMQ来实现与上面相同的目标(一个客户端发送消息到我的服务器,触发更新),但再次,我不想有一个客户端监听器不断连接游戏运行,而且,ZeroMQ是很多处理这么小的事情。我运气不好。

一定有更好的方法来实现这一点。如有任何帮助,不胜感激。

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

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

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

下面是Server类:
    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 disconnectedn";
    }
    public function onError(ConnectionInterface $conn, Exception $e) {
    echo "An error has occurred: {$e->getMessage()}n";
        $conn->close();
    }

也许一个onTick函数添加到这个类得到调用每X秒?这可能吗?

要以45秒的间隔(或任何其他数字)向每个人广播消息,您必须控制Ratchet使用的事件循环。

你需要添加一个定时事件,不同的供应商称其为定时事件,计时器事件,可重复事件,但它的行为总是相同的-一个函数在X时间后触发。

你要找的类在这个链接

或者,你可以用冰柱代替棘轮。我个人更喜欢它,我没有什么特别的理由——在我看来,这两个库都很出色,有一个选择总是好的。

有趣的是,你尝试使用ZeroMQ——它是一个传输层,绝对是我用过的最好的库/项目之一。它与事件循环配合得很好,对于开发分布式系统、作业队列和类似的东西来说,它绝对很有趣。

祝你游戏顺利!如果您对WS、扩展到多台机器或类似的问题有任何其他问题,请随时在此答案下方的评论中与我联系。


谢谢你,N.B.!

对于任何可能陷入类似情况的人,我希望这能帮助到别人。我甚至不知道应该搜索哪些术语来找到问题的根源,正如我最初问题下面的评论所证明的那样,我因为不够"具体"而受到批评。有时候,如果你不完全确定自己在找什么,就很难提出问题!

这是游戏的启动脚本现在的样子,我已经测试了一个实现的"tick"循环。

<?php 
use RatchetMessageComponentInterface;
use RatchetConnectionInterface;
use RatchetServerIoServer;
use RatchetHttpHttpServer;
use RatchetWebSocketWsServer;
use ReactSocketServer as Reactor;
use ReactEventLoopFactory 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(ReactEventLoopLoopInterface $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 disconnectedn";
    }
    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();

相关内容

  • 没有找到相关文章