Hello Stack Overflow,
我正在构建一个基于浏览器的文本,只有用Python编写的多人游戏RPG,以Ratchet为骨干。
到目前为止我所拥有的一切:它运作良好。我已经实现了一个简单而有效的命令解释器,它可以很好地在客户端和服务器之间传输数据。我能够轻松地执行数据库操作并在我的Server类中实例化外部类,以用于将信息传递回客户端。
我遇到困难的地方:出于某种原因,我的大脑试图实施滴答声,在我的游戏环境中,这是一组每45秒发生一次的事件。它基本上是游戏的心跳,如果没有可靠和优雅的实现,我无法前进。 tick需要做很多事情,包括(但不限于):向玩家发送消息,更新玩家重生,内存处理等等。通常,所有这些操作都可以编码并放在Update类中。
但我无法弄清楚如何让蜱实际发生。 tick本身,只是我的react循环中每45秒发生一次的函数,它应该在服务器启动时启动。它绝对需要服务器端。我可以在技术上实现它在客户端并与数据库中的值同步,但我不想走这条路。
我觉得这应该比我的大脑更容易。
我尝试了什么: 我尝试运行一个简单的递归函数,使用sleep(45)在一个计时器上构造我的更新类,但同样,这需要在服务器启动时启动,如果我在服务器类的构造中抛出一个无限循环函数,启动脚本永远不会通过,游戏永远不会启动。
我尝试过使用带有反应的onPeriodicTimer函数,但我无法弄清楚如何实现它..
我尝试了一些疯狂的事情,比如使用节点js每45秒向我的服务器发送一条消息,我的解释器捕获该特定消息并启动滴答过程。这是我最接近成功实现的,但我真的希望能够在没有客户端连接和与服务器通信的情况下做到这一点,这似乎是hackey。
我已经尝试过ZeroMQ来实现与上面相同的目标(一个向我的服务器发送消息以触发更新的客户端)但同样,我不想让一个客户端监听器不断为游戏连接运行,而且,对于那么小的东西来说,零MM是很多的事情。我没有运气。
必须有更好的方法来实现这一目标。任何帮助将不胜感激。
供参考,这是我的套接字应用程序正在运行的基本概要。首先,我在Ratchet网站上使用了“Hello World”教程。
所以我有一个startup.php脚本,我运行它来初始化Server类,它接受来自连接客户端的消息。 onMessage,实例化一个解释器类,它解析消息并查找客户端在数据库表中传递的命令,该数据库表为该命令加载相应的Class和Method,该数据基于onMessage函数,类和方法为调用该命令,结果将传递回客户端。
TLDR:如何向Ratchet 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 disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
也许每隔X秒调用一次onTick函数添加到这个类中?这可能吗?
答案 0 :(得分:3)
要以45秒(或任何其他数字)的间隔向所有人广播消息,您必须控制Ratchet使用的事件循环。
你需要添加一个定时事件,各个供应商调用这个定时事件,定时器事件,可重复事件,但它总是表现相同 - 一个函数在X时间后触发。
您所关注的课程是documented at this link
或者,您可以使用icicle代替Ratchet。我个人更喜欢它,我没有任何特别的偏好 - 我认为这两个图书馆都很优秀,并且总是很乐意有另一种选择。
有趣的是,你尝试使用ZeroMQ - 它是一个传输层,它绝对是我用过的最好的图书馆/项目之一。它与事件循环很好地配合,对于开发分布式系统,作业队列和类似工具非常有趣。
祝你好运!如果您对WS有任何其他问题,请扩展到多台机器或类似机器 - 请随时在下面的评论中给我发这个答案。
谢谢你,N.B。!
对于任何可能陷入类似情况的人,我希望这可以帮助某人。我甚至无法弄清楚我应该用谷歌搜索哪些术语来解决问题的根源,正如我原来的问题下面的评论所证明的那样,我因为没有&#34;特定的&#34;而感到沮丧。足够。如果您不完全确定自己在寻找什么,有时候很难提出问题!
以下是游戏的启动脚本现在的样子,实现了&#34; tick&#34;我测试过的循环。
<?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();