内容简介:联机逻辑开发进度:■■■■■□□□□□□□本章结束开发进度:■■■■■■■□□□□□
联机逻辑开发进度:■■■■■□□□□□□□
本章结束开发进度:■■■■■■■□□□□□
上一章的答案:
DataCenter 类:
<?php
...
class DataCenter
{
const PREFIX_KEY = "game";
...
public static function getPlayerWaitListLen()
{
$key = self::PREFIX_KEY . ":player_wait_list";
return self::redis()->lLen($key);
}
public static function pushPlayerToWaitList($playerId)
{
$key = self::PREFIX_KEY . ":player_wait_list";
self::redis()->lPush($key, $playerId);
}
public static function popPlayerFromWaitList()
{
$key = self::PREFIX_KEY . ":player_wait_list";
return self::redis()->rPop($key);
}
public static function getPlayerFd($playerId)
{
$key = self::PREFIX_KEY . ":player_fd:" . $playerId;
return self::redis()->get($key);
}
public static function setPlayerFd($playerId, $playerFd)
{
$key = self::PREFIX_KEY . ":player_fd:" . $playerId;
self::redis()->set($key, $playerFd);
}
public static function delPlayerFd($playerId)
{
$key = self::PREFIX_KEY . ":player_fd:" . $playerId;
self::redis()->del($key);
}
public static function getPlayerId($playerFd)
{
$key = self::PREFIX_KEY . ":player_id:" . $playerFd;
return self::redis()->get($key);
}
public static function setPlayerId($playerFd, $playerId)
{
$key = self::PREFIX_KEY . ":player_id:" . $playerFd;
self::redis()->set($key, $playerId);
}
public static function delPlayerId($playerFd)
{
$key = self::PREFIX_KEY . ":player_id:" . $playerFd;
self::redis()->del($key);
}
public static function setPlayerInfo($playerId, $playerFd)
{
self::setPlayerId($playerFd, $playerId);
self::setPlayerFd($playerId, $playerFd);
}
}
我们先来测试一下,前面所写的代码有没有问题,重新运行 Server.php ,并在浏览器打开游戏前端页面。
查看 Redis 中的键值:
127.0.0.1:6379> keys * 1) "game:player_fd:player_177" 2) "game:player_id:9"
可以看到, player_id 和 player_fd 都已经保存下来了。
发送一个 匹配 请求,并查看 Redis 中的键值:
127.0.0.1:6379> keys * 1) "game:player_fd:player_177" 2) "game:player_wait_list" 3) "game:player_id:9" 127.0.0.1:6379> lrange game:player_wait_list 0 -1 1) "player_177"
可以看到,匹配队列 game:player_wait_list 中已经成功存入了 player_177 。
目前我们的匹配机制已经完成了:
- 前端连接时发送
player_id - 服务端连接时保存玩家信息
- 前端发送
code为600的指令 - 服务端将
player_id放入匹配队列
剩下的操作就是:
- 检测匹配队列长度,当长度大于等于2时,创建游戏房间
异步检测匹配队列
我们大部分游戏逻辑都是运行在 worker 里的,异步的玩家匹配可以减轻主程序 worker 的负担。关于 Task 机制不了解的童鞋,请先熟悉一下官方文档。
-
Swoole Task文档: https://wiki.swoole.com/wiki/...
- 根据官方文档,在
Server类中完成Task机制的初始化。
<?php
...
class Server
{
...
const CONFIG = [
...
'task_worker_num' => 4,
...
];
public function __construct()
{
...
$this->ws->on('task', [$this, 'onTask']);
$this->ws->on('finish', [$this, 'onFinish']);
...
}
...
public function onTask($server, $taskId, $srcWorkerId, $data)
{
}
public function onFinish($server, $taskId, $data)
{
}
}
...
我们什么时候会用 Task 进行匹配队列检测呢?其实就是把玩家放入匹配队列后。
Logic 类:
<?php
...
class Logic
{
public function matchPlayer($playerId)
{
//将用户放入队列中
DataCenter::pushPlayerToWaitList($playerId);
//发起一个Task尝试匹配
//swoole_server->task(xxx);
}
}
Server 类:
<?php
...
class Server
{
...
public function onTask($server, $taskId, $srcWorkerId, $data)
{
DataCenter::log("onTask", $data);
//执行某些逻辑
}
...
}
...
可以发现, onTask 方法只是接收传递的 $data ,当我们有多种 Task 任务 (匹配玩家、在线检测、游戏状态检查) 时,我们的 worker 怎么区分每一个 Task 任务呢?其实就和客户端与服务端通信一样,我们可以根据一个 code 来区分。
Logic 类:
<?php
...
class Logic
{
public function matchPlayer($playerId)
{
//将用户放入队列中
DataCenter::pushPlayerToWaitList($playerId);
//发起一个Task尝试匹配
//swoole_server->task(['code'=>'xxx']);
}
}
Server 类:
<?php
...
class Server
{
...
public function onTask($server, $taskId, $srcWorkerId, $data)
{
DataCenter::log("onTask", $data);
switch ($data['code']) {
//执行task方法
case 'xxx':
//task->xxx();
break;
case 'yyy':
//task->yyy();
break;
}
}
...
}
...
从代码可以看出,我们现在缺了两种机制:
- 全局获取Server对象:在
Logic中获取swoole_server从而调用task()方法。 - 增加Task管理类:需要一个类管理
Task的code和Task需要执行的逻辑方法。
全局获取Server对象
第一个比较好处理,我们在 onWorkerStart 的时候就能获取到 swoole_server 。
- 有童鞋可以会问,为什么不在
onStart的时候获取?这是因为onStart回调的是Master进程,而onWorkerStart回调的是Worker进程,只有Worker进程才可以发起Task任务。有兴趣的童鞋请查阅文档: https://wiki.swoole.com/wiki/...
- 在
DataCenter中新增静态变量$server。 - 在
onWorkerStart回调函数中,将$server保存到DataCenter中。
DataCenter 类:
<?php
...
class DataCenter
{
...
public static $server;
...
}
Server 类:
<?php
...
class Server
{
...
public function onWorkerStart($server, $workerId)
{
...
DataCenter::$server = $server;
}
...
}
...
这样就解决了第一种问题,下面轮到第二个问题。
增加Task管理类
在项目 Manager 文件夹下,创建 TaskManager 类文件。
TaskManager 类:
<?php
namespace App\Manager;
class TaskManager
{
}
后续所有跟 task 有关的常量、方法都归于这个类来管理。
- 设置一个常量
TASK_CODE_FIND_PLAYER,用于发起寻找玩家task任务。 - 新增静态方法
findPlayer(),当匹配队列长度大于等于2时,弹出队列前两个玩家的player_id并返回。
TaskManager 类:
<?php
namespace App\Manager;
class TaskManager
{
const TASK_CODE_FIND_PLAYER = 1;
public static function findPlayer()
{
$playerListLen = DataCenter::getPlayerWaitListLen();
if ($playerListLen >= 2) {
$redPlayer = DataCenter::popPlayerFromWaitList();
$bluePlayer = DataCenter::popPlayerFromWaitList();
return [
'red_player' => $redPlayer,
'blue_player' => $bluePlayer
];
}
return false;
}
}
现在前置准备就绪,可以将上面写过的伪代码改成真实代码啦~
- 在
Logic类的matchPlayer()方法中,发起一个Task任务尝试匹配。 - 在
Server类中根据传入的code,执行TaskManager的findPlayer()方法。 - 当
findPlayer()方法有值返回的时候,返回执行结果并携带上code到worker进程。
本章就到这里结束了,这次留的Homework可能有点难度,请童鞋们尽力完成。
当前目录结构:
HideAndSeek
├── app
│ ├── Lib
│ │ └── Redis.php
│ ├── Manager
│ │ ├── DataCenter.php
│ │ ├── Game.php
│ │ ├── Logic.php
│ │ └── TaskManager.php
│ ├── Model
│ │ ├── Map.php
│ │ └── Player.php
│ └── Server.php
├── composer.json
├── composer.lock
├── frontend
│ └── index.html
├── test.php
└── vendor
├── autoload.php
└── composer
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 用Swoole来写个联机对战游戏呀!(五)联机初始化
- 重命名与迁移联机数据文件
- 从动物森友会聊主机游戏联机原理
- 滥用word联机视频特征执行恶意代码POC
- 用Swoole来写个联机对战游戏呀!(一)前言
- 用Swoole来写个联机对战游戏呀!(三)完善游戏功能
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
一本书读懂大数据
黄颖 / 吉林出版集团有限责任公司 / 2014-12-1
进入大数据时代,让数据开口说话将成为司空见惯的事情,书中将从大数据时代的前因后果讲起,全面分析大数据时代的特征、企业实践的案例、大数据的发展方向、未来的机遇和挑战等内容,展现一个客观立体、自由开放的大数据时代。一起来看看 《一本书读懂大数据》 这本书的介绍吧!
Markdown 在线编辑器
Markdown 在线编辑器
HEX CMYK 转换工具
HEX CMYK 互转工具