我正在尝试将脉冲实现到 PHP Ratchet Websocket 应用程序中
I am trying to implement a pulse into a PHP Ratchet Websocket Application
你好堆栈溢出,
我正在构建一个基于浏览器的纯文本多人角色扮演游戏,用 PHP 编写,棘轮作为 backbone。
我目前所拥有的:它工作得很好。我实现了一个简单有效的命令解释器,它可以很好地在客户端和服务器之间传输数据。我能够轻松地执行数据库操作并在我的服务器 class 内部的 classes 之外实例化以用于将信息传递回客户端。
我被卡住的地方: 出于某种原因,我的大脑在尝试实现滴答时崩溃了,在我的游戏中,滴答是一组事件,每次都会发生45 秒。它基本上是游戏的心跳,如果没有可靠而优雅的实现,我就无法前进。 tick 需要做很多事情,包括(但不限于):向玩家发送消息、更新玩家再生、内存处理等。通常,所有这些操作都可以编码并放在更新 class.
中
但我不知道如何让滴答声真正发生。 tick 本身,只是我的反应循环中每 45 秒发生一次的函数,它应该在服务器启动时启动。它绝对需要在服务器端。我可以在技术上在客户端实现它并与数据库中的值同步,但我不想走那条路。
我觉得这应该比我的大脑做起来容易。
我试过的:
我已经尝试 运行ning 一个简单的递归函数,它使用 sleep(45) 在计时器上构造我的更新 class,但是同样,这需要在服务器启动时启动,如果我抛出一个无限我的服务器构造中的循环函数 class,启动脚本永远不会通过,游戏也永远不会开始。
我尝试过使用react自带的onPeriodicTimer函数,但是我不知道如何实现它..
我尝试过一些疯狂的事情,比如使用 node js 每 45 秒向我的服务器发送一条消息,我的解释器捕获该特定消息并启动 tick 进程。这是我最接近成功实施的一次,但我真的希望能够在客户端不必连接服务器并与服务器对话的情况下完成它,这似乎是 hackkey。
我已经尝试使用 ZeroMQ 来实现与上述相同的目标(客户端向我的服务器发送消息以触发更新)但是我不想让客户端侦听器不断连接运行 的游戏,而且,对于这么小的东西,zeroMQ 需要处理很多事情。我运气不好。
必须有更好的方法来实现这一点。任何帮助将不胜感激。
作为参考,这里是我的套接字应用程序正在运行的基本概述。首先,我使用了 Ratchet 网站上的 "Hello World" 教程。
所以我有一个 startup.php 脚本,我 运行 初始化服务器 class,它接受来自已连接客户端的消息。 onMessage,解释器 class 被实例化,它解析消息并查找客户端在数据库 table 中传递的命令,数据库 table 加载相应的 Class 和该命令的方法,该数据是基于返回 onMessage 函数,调用命令的 class 和方法,并将结果传回客户端。
TLDR:如何向 Ratchet websocket 服务器添加重复功能,使其每 45 秒向连接的客户端发送一次消息?
这是服务器 class:
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();
}
也许向此 class 添加了一个每隔 X 秒调用一次的 onTick 函数?这可能吗?
要以 45 秒(或任何其他数字)为间隔向所有人广播消息,您必须控制 Ratchet 使用的事件循环。
您需要添加一个定时事件,各种供应商将此称为定时事件、计时器事件、可重复事件,但它的行为始终相同 - 函数在 X 时间后触发。
Class 你要找的是 documented at this link
或者,您可以使用 icicle 而不是 Ratchet。我个人更喜欢它,我没有任何特别的偏好理由 - 在我看来这两个库都很棒,而且有一个替代品总是很好。
有趣的是,您尝试使用 ZeroMQ - 它是一个传输层,它绝对是我用过的最好的库/项目之一。它与事件循环配合得很好,对于开发分布式系统、作业队列和类似系统来说绝对很有趣。
祝你游戏顺利!如果您对 WS、扩展到多台机器或类似问题有任何其他问题 - 请随时在此答案下方的评论中联系我。
谢谢N.B.!
对于任何可能陷入类似情况的人,我希望这能帮助别人。我什至搞不清楚我应该用谷歌搜索什么术语来弄清我的问题的根源,正如我最初问题下面的评论所证明的那样,我因为 "specific" 不够用而受到指责。如果您不完全确定自己在寻找什么,有时很难提出问题!
这是游戏的启动脚本现在的样子,其中包含一个我测试过的 "tick" 循环。
<?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 编写,棘轮作为 backbone。
我目前所拥有的:它工作得很好。我实现了一个简单有效的命令解释器,它可以很好地在客户端和服务器之间传输数据。我能够轻松地执行数据库操作并在我的服务器 class 内部的 classes 之外实例化以用于将信息传递回客户端。
我被卡住的地方: 出于某种原因,我的大脑在尝试实现滴答时崩溃了,在我的游戏中,滴答是一组事件,每次都会发生45 秒。它基本上是游戏的心跳,如果没有可靠而优雅的实现,我就无法前进。 tick 需要做很多事情,包括(但不限于):向玩家发送消息、更新玩家再生、内存处理等。通常,所有这些操作都可以编码并放在更新 class.
中但我不知道如何让滴答声真正发生。 tick 本身,只是我的反应循环中每 45 秒发生一次的函数,它应该在服务器启动时启动。它绝对需要在服务器端。我可以在技术上在客户端实现它并与数据库中的值同步,但我不想走那条路。
我觉得这应该比我的大脑做起来容易。
我试过的: 我已经尝试 运行ning 一个简单的递归函数,它使用 sleep(45) 在计时器上构造我的更新 class,但是同样,这需要在服务器启动时启动,如果我抛出一个无限我的服务器构造中的循环函数 class,启动脚本永远不会通过,游戏也永远不会开始。
我尝试过使用react自带的onPeriodicTimer函数,但是我不知道如何实现它..
我尝试过一些疯狂的事情,比如使用 node js 每 45 秒向我的服务器发送一条消息,我的解释器捕获该特定消息并启动 tick 进程。这是我最接近成功实施的一次,但我真的希望能够在客户端不必连接服务器并与服务器对话的情况下完成它,这似乎是 hackkey。
我已经尝试使用 ZeroMQ 来实现与上述相同的目标(客户端向我的服务器发送消息以触发更新)但是我不想让客户端侦听器不断连接运行 的游戏,而且,对于这么小的东西,zeroMQ 需要处理很多事情。我运气不好。
必须有更好的方法来实现这一点。任何帮助将不胜感激。
作为参考,这里是我的套接字应用程序正在运行的基本概述。首先,我使用了 Ratchet 网站上的 "Hello World" 教程。
所以我有一个 startup.php 脚本,我 运行 初始化服务器 class,它接受来自已连接客户端的消息。 onMessage,解释器 class 被实例化,它解析消息并查找客户端在数据库 table 中传递的命令,数据库 table 加载相应的 Class 和该命令的方法,该数据是基于返回 onMessage 函数,调用命令的 class 和方法,并将结果传回客户端。
TLDR:如何向 Ratchet websocket 服务器添加重复功能,使其每 45 秒向连接的客户端发送一次消息?
这是服务器 class:
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();
}
也许向此 class 添加了一个每隔 X 秒调用一次的 onTick 函数?这可能吗?
要以 45 秒(或任何其他数字)为间隔向所有人广播消息,您必须控制 Ratchet 使用的事件循环。
您需要添加一个定时事件,各种供应商将此称为定时事件、计时器事件、可重复事件,但它的行为始终相同 - 函数在 X 时间后触发。
Class 你要找的是 documented at this link
或者,您可以使用 icicle 而不是 Ratchet。我个人更喜欢它,我没有任何特别的偏好理由 - 在我看来这两个库都很棒,而且有一个替代品总是很好。
有趣的是,您尝试使用 ZeroMQ - 它是一个传输层,它绝对是我用过的最好的库/项目之一。它与事件循环配合得很好,对于开发分布式系统、作业队列和类似系统来说绝对很有趣。
祝你游戏顺利!如果您对 WS、扩展到多台机器或类似问题有任何其他问题 - 请随时在此答案下方的评论中联系我。
谢谢N.B.!
对于任何可能陷入类似情况的人,我希望这能帮助别人。我什至搞不清楚我应该用谷歌搜索什么术语来弄清我的问题的根源,正如我最初问题下面的评论所证明的那样,我因为 "specific" 不够用而受到指责。如果您不完全确定自己在寻找什么,有时很难提出问题!
这是游戏的启动脚本现在的样子,其中包含一个我测试过的 "tick" 循环。
<?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();