内容简介:联机逻辑开发进度:■■■■■□□□□□□□本章结束开发进度:■■■■■■■□□□□□
联机逻辑开发进度:■■■■■□□□□□□□
本章结束开发进度:■■■■■■■□□□□□
上一章的答案:
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来写个联机对战游戏呀!(三)完善游戏功能
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据结构与算法分析
维斯 / 人民邮电 / 2006-10 / 59.00元
《数据结构与算法分析:C++描述》秉承Weiss著全一贯的严谨风格,同时又突出了实践。书中充分应用了现代C++语言特性,透彻地讲述了数据结构的原理和应用,不仅使学生具备算法分析能力,能够开发高效的程序,而且让学生掌握良好的程序设计技巧。一起来看看 《数据结构与算法分析》 这本书的介绍吧!