2016-11-09 87 views
1

您好堆栈溢出,我想实现一个脉冲到PHP的WebSocket棘轮应用

我建立PHP编写与棘轮为骨干基于浏览器的文本只多玩家角色扮演。

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

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

但我无法弄清楚如何让蜱实际发生。滴答本身,就是我的反应循环中每45秒发生一次的函数,它应该在服务器启动时启动。它绝对需要在服务器端。我可以在技术上实现它在客户端并与数据库中的值同步,但我不想走这条路。

我觉得这不是我的大脑是使它应该更容易。

我已经试过: 我试着运行使用睡眠(45)构建一个计时器我更新类简单的递归函数,但同样,这需要在服务器启动时启动,如果我在我的服务器类的构造中抛弃了一个无限循环函数,启动脚本永远不会通过,并且游戏永远不会启动。

我使用随反应的onPeriodicTimer功能试过,但我不能想出如何实现它..

我已经尝试一些疯狂喜欢使用节点JS来发送消息给我服务器每45秒一次,我的解释器会捕获该特定消息并启动滴答过程。这是我成功实现的最接近的,但我真的希望能够做到这一点,而无需客户端连接并与服务器通话,这看起来很怪异。

我试过ZeroMQ实现上述相同的目标(一个客户端发送消息到我的服务器,触发更新),但我不想有一个客户端监听器不断连接游戏运行,而且,zeroMQ对于处理这么小的事情有很大的帮助。我没有运气。

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

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

所以我有我运行初始化服务器类,它接受来自连接的客户端消息的startup.php脚本。onMessage,实例化解释器类,解析消息并查找客户端在数据库表中传递的命令,该数据库表为该命令加载相应的Class和Method,该数据基于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秒?那可能吗?

+1

请仔细阅读[我可以问什么议题有关(http://stackoverflow.com/help/on-topic) 和[如何问一个很好的问题(http://stackoverflow.com/帮助/怎么问) 和[完美的问题](http://codeblog.jonskeet.uk/2010/08/29/writing-the-perfect-question/) 以及如何创建一个[最小,完整和可验证的例子](http://stackoverflow.com/help/mcve) – RiggsFolly

+0

有太多可能的答案,或者对于这种格式,好的答案太长。请添加详细信息以缩小答案集或隔离可以用几个段落回答的问题。我建议你找一个开发论坛(也许[Quora](http://www.quora.com/Computer-Programming?))来解决一般问题。然后,当/如果您有特定的编码问题,请回到StackOverflow,我们很乐意提供帮助。 –

+1

我不明白这个问题不够具体,如果你有Websockets或棘轮的经验,它不能在几个段落或更少的回答。我尽可能的明确和清晰,因为我可以... – Lynne

回答

2

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

您需要添加一个定时事件,各个供应商调用此定时事件,定时器事件,可重复事件,但它总是表现相同 - 一个函数在X时间后触发。

类,你追求的是documented at this link

或者,你可以使用icicle代替棘轮。我个人更喜欢它,我没有任何特别的理由 - 我认为这两个图书馆都非常出色,并且拥有替代品总是很好的。有趣的是,您尝试使用ZeroMQ--它是一个传输层,它绝对是我曾经使用过的最好的库/项目之一。它与事件循环很好地结合在一起,对于开发分布式系统,工作队列和类似的事物来说,这非常有趣。

祝您好运与您的游戏!如果您有任何关于WS的其他问题,扩展到多台机器或类似的问题 - 请随时在本答案下面的评论中给我打电话。


谢谢,N.B.!

对于任何可能陷入类似情况的人,我希望这可以帮助别人。我甚至不知道我应该用什么术语来找到我的问题的底部,正如我原来的问题下面的评论所证明的那样,我因为没有足够的“特定”而得到了一些答案。如果你不完全确定你在找什么,有时候很难问一个问题!

下面是游戏的启动脚本现在看起来像一个已实施的“滴答”循环,我已经测试。

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