php - 我正在尝试将脉冲实现到 PHP Ratchet Websocket 应用程序中

标签 php websocket ratchet

你好堆栈溢出,

我正在构建一个基于浏览器的纯文本多人 RPG,用 PHP 编写,以 Ratchet 为 Backbone 。

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

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

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

我觉得这应该比我的大脑制作更容易。

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

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

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

我已经尝试使用 ZeroMQ 来实现与上述相同的目标(客户端向我的服务器发送消息以触发更新),但同样,我不想让客户端监听器不断连接以运行游戏,而且,zeroMQ 对于这么小的事情来说有很多处理。我没有运气。

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

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

所以我有一个 startup.php 脚本,我运行它来初始化 Server 类,它接受来自连接客户端的消息。 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();
    }

也许添加到此类的 onTick 函数每 X 秒调用一次?那可能吗?

最佳答案

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

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

您所追求的类(class)是 documented at this link

或者,您可以使用 icicle而不是 Ratchet 。我个人更喜欢它,我没有任何特别的偏好理由 - 在我看来这两个库都很棒,而且有一个替代方案总是很好。

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

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

谢谢你,NB!

对于任何可能陷入类似情况的人,我希望这对某人有所帮助。我什至无法弄清楚我应该使用哪些术语来搜索问题的根源,并且正如我原始问题下方的评论所证明的那样,我因不够“具体”而受到抨击。有时,如果您不完全确定自己在寻找什么,就很难提出问题!

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

<?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();

关于php - 我正在尝试将脉冲实现到 PHP Ratchet Websocket 应用程序中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40512954/

相关文章:

PHP-Build - 构建 PHP 5.3.28 错误

python - 从Python中无限循环的异步函数返回结果

websocket - 如何在 GO 中存储 websocket 连接

php - 用于树莓派的 Ratchet Websocket

javascript - JavaScript websocket可以从服务器获取返回值吗?

php - 推进中的 Concat_ws

php - 为具有不同时区用户的 codeigniter 应用程序将日期存储到 Mysql 中的最佳方法

PHP 类::为什么声明为新的?

java - Tomcat 8 和 Websocket

symfony + Ratchet : how to send msg to user from controller