Logic 类:

...

class Logic

{

public function matchPlayer($playerId)

{

...

//发起一个Task尝试匹配

DataCenter::$server->task(['code' => TaskManager::TASK_CODE_FIND_PLAYER]);

}

}

复制代码

Server 类:

...

class Server

{

...

public function onTask($server, $taskId, $srcWorkerId, $data)

{

DataCenter::log("onTask", $data);

$result = [];

switch ($data['code']) {

case TaskManager::TASK_CODE_FIND_PLAYER:

$ret = TaskManager::findPlayer();

if (!empty($ret)) {

$result['data'] = $ret;

}

break;

}

if (!empty($result)) {

$result['code'] = $data['code'];

return $result;

}

}

...

}

...

复制代码

童鞋们的作业完成情况如何呢?

我们来再次梳理一下目前的匹配功能进度:

前端连接时发送 player_id

服务端连接时保存玩家信息

前端发送 code 为 600 的指令

服务端将 player_id 放入匹配队列

服务端发起一个 task 进行玩家匹配,当寻找到两个玩家时返回两个 player_id 到 worker 进程

那下一步就很明显了,就是创建游戏房间。

创建房间分析

在 Server 类的 onFinish() 方法中,根据传入的 code ,执行 Logic 的 createRoom() 方法。

Server 类:

...

class Server

{

...

public function onFinish($server, $taskId, $data)

{

DataCenter::log("onFinish", $data);

switch ($data['code']) {

case TaskManager::TASK_CODE_FIND_PLAYER:

$this->logic->createRoom($data['data']['red_player'],

$data['data']['blue_player']);

break;

}

}

}

...

复制代码

显然,下一步就是完成这个 createRoom() 方法匹配机制就大功告成了。但是真的这么简单吗?下面我们要思考一件事情。

我们的匹配队列是存放在 Redis 中的,无论哪个 worker 都可以读取,但游戏数据是存放在内存中的,在启动 Swoole Worker 时设置了 'worker_num' => 4 , worker 是多进程的,这会产生什么效果呢?就是进程内存隔离。

比如, A玩家 进入了 worker_1 ,数据保存在 worker_1 进程内存中,而 B玩家 进入了 worker_2 ,数据保存在 worker_2 进程内存中。他们的匹配队列用的却是同一个 Redis List ,假如我们选择了 worker_1 进行游戏数据存放,那么 B玩家 将会读取不到内存中的游戏数据。

要解决这个问题有几个容易的方法:

Redis

worker

显然, A 方法过于粗暴,没想到竟说出如此 粗鄙之语 !而 B 方法扩展性不好,当有成千上万玩家的时候,我们的 Redis 分分钟就挂给你看。这样下来只能选择 C 方法来实践。

Swoole 为我们提供了一个 bind() 方法,就可将连接绑定到固定的一个 worker 来处理。不了解 bind() 方法的童鞋请先阅读一下官方文档,尤其是 时序问题 。

那么我们创建房间的流程就是:

生成一个房间 room_id

将 task 寻找到的两位玩家连接的 fd 绑定到 room_id 算出的同一个 int 值

通知玩家 room_id

前端获取到 room_id 后,发起开始游戏请求

绑定玩家连接

想要使用 bind() 方法,需先将 dispatch_mode 设置为 5 。

完成 Logic 的 createRoom() 方法,生成一个 room_id ,绑定连接 fd 。

获取 $server 对象,向两个玩家分别发送房间 room_id 。

Server 类:

...

class Server

{

...

const CONFIG = [

...

'dispatch_mode' => 5,

...

];

...

}

...

复制代码

Logic 类:

...

class Logic

{

...

public function createRoom($redPlayer, $bluePlayer)

{

$roomId = uniqid('room_');

$this->bindRoomWorker($redPlayer, $roomId);

$this->bindRoomWorker($bluePlayer, $roomId);

}

private function bindRoomWorker($playerId, $roomId)

{

$playerFd = DataCenter::getPlayerFd($playerId);

DataCenter::$server->bind($playerFd, crc32($roomId));

DataCenter::$server->push($playerFd, $roomId);

}

}

复制代码

童鞋们发现问题了吗?

没错,我们的 push() 方法直接就把 room_id 发过去了。又是这种问题:接收方无法识别该消息是何种消息。那么我们要如何处理呢?还是老套路,加 code 协议码。一个更好的办法是,找一个类来专门管理发送相关的变量和方法。

在 Manager 文件夹下,新建 Sender 类文件。

Sender 类:

namespace App\Manager;

class Sender

{

}

复制代码

在 Sender 类中新增 MSG_ROOM_ID 常量,作为发送 room_id 的 code 。

新增方法 sendMessage($playerId, $code, $data = []) ,通过传入的 $playerId 发送固定格式的消息到客户端。比较常规的内容需要有: code 、 msg 、 data 。

将 bindRoomWorker() 中发送房间 room_id 的代码改为使用 Sender 发送。

Sender 类:

...

class Sender

{

const MSG_ROOM_ID = 1001;

const CODE_MSG = [

self::MSG_ROOM_ID => '房间ID',

];

public static function sendMessage($playerId, $code, $data = [])

{

$message = [

'code' => $code,

'msg' => self::CODE_MSG[$code] ?? '',

'data' => $data

];

$playerFd = DataCenter::getPlayerFd($playerId);

if (empty($playerFd)) {

return;

}

DataCenter::$server->push($playerFd, json_encode($message));

}

}

复制代码

Logic 类:

...

class Logic

{

...

private function bindRoomWorker($playerId, $roomId)

{

$playerFd = DataCenter::getPlayerFd($playerId);

DataCenter::$server->bind($playerFd, crc32($roomId));

Sender::sendMessage($playerId, Sender::MSG_ROOM_ID, ['room_id' => $roomId]);

}

}

复制代码

这下我们的前端就能通过接收的 code 来判断,究竟这条 message 是 房间ID 或者是 游戏数据 。

我们来测试一下目前为止的代码有没有问题。重启 Server 服务器,在浏览器打开两个游戏前端页面并点击匹配按钮。

[root@localhost app]# php Server.php

master start (listening on 0.0.0.0:8811)

server: onWorkStart,worker_id:4

server: onWorkStart,worker_id:5

server: onWorkStart,worker_id:6

server: onWorkStart,worker_id:7

server: onWorkStart,worker_id:0

server: onWorkStart,worker_id:1

server: onWorkStart,worker_id:2

server: onWorkStart,worker_id:3

[2019-04-21 15:59:46][INFO]: client open fd:3

[2019-04-21 15:59:50][INFO]: client open fd:3,message:{"code":600}

[2019-04-21 15:59:50][INFO]: onTask {"code":1}

[2019-04-21 15:59:50][INFO]: onFinish {"data":{"red_player":"player_177","blue_player":"player_181"},"code":1}

PHP Warning: Swoole\WebSocket\Server::push(): the connected client of connection[9] is not a websocket client or closed. in /mnt/htdocs/HideAndSeek_teach/app/Manager/Sender.php on line 31

复制代码

显然,程序报错了。这是因为我们启动 服务器 时,没有清除之前的残余玩家信息,所以 push() 时报错了。

初始化玩家数据

在 DataCenter 中新增 initDataCenter() 方法清除 Redis 中的残余数据。

在 onStart 的时候调用 initDataCenter() 方法。

DataCenter 类:

...

class DataCenter

{

...

public static function initDataCenter()

{

//清空匹配队列

$key = self::PREFIX_KEY . ':player_wait_list';

self::redis()->del($key);

//清空玩家ID

$key = self::PREFIX_KEY . ':player_id*';

$values = self::redis()->keys($key);

foreach ($values as $value) {

self::redis()->del($value);

}

//清空玩家FD

$key = self::PREFIX_KEY . ':player_fd*';

$values = self::redis()->keys($key);

foreach ($values as $value) {

self::redis()->del($value);

}

}

...

}

复制代码

Server 类:

...

class Server

{

...

public function onStart($server)

{

...

DataCenter::initDataCenter();

}

...

}

...

复制代码

现在再来一次,重启 Server 服务器,在浏览器打开两个游戏前端页面并点击匹配按钮。

可以看到,服务端成功发送 room_id 。

发送开始游戏指令

在 Vue 的数据属性中新增 roomId ,用于保存服务端发送的 room_id 。

新增方法 startRoom() ,当服务端发来 room_id 消息时,发送 code 以及 room_id 到服务端开始游戏。

本章留的Homework是前端功能,但是比较简单,请童鞋们尽力完成哦。

当前目录结构:

HideAndSeek

├── app

│ ├── Lib

│ │ └── Redis.php

│ ├── Manager

│ │ ├── DataCenter.php

│ │ ├── Game.php

│ │ ├── Logic.php

│ │ ├── Sender.php

│ │ └── TaskManager.php

│ ├── Model

│ │ ├── Map.php

│ │ └── Player.php

│ └── Server.php

├── composer.json

├── composer.lock

├── frontend

│ └── index.html

├── test.php

└── vendor

├── autoload.php

└── composer

复制代码

php创建游戏房间思路,用Swoole来写个联机对战游戏呀!(八)创建游戏房间相关推荐

  1. 一个新游戏的思路;大家来说说看,觉得好的话,我做成游戏

    在一个40x40的棋盘上,分布7种颜色或图形的标记 每次3秒中随机选出其中其中4个颜色标记到 n,m 的空白位置,用鼠标选择某个位置的颜色标记,可以移动到鼠标选择的目标位:当目标位的横或竖的排列达到4 ...

  2. Cocos Creator 3D使用腾讯云游戏联机对战引擎(MGOBE)

    腾讯云在2019年2月上线新品--游戏联机对战引擎MGOBE,为游戏提供房间管理.在线匹配.网络通信.云开发等服务,帮助开发者快速搭建多人交互游戏. 在2021年2月,实现兼容 Cocos 引擎 v3 ...

  3. 腾讯云为小游戏开发者升级工具箱 小游戏联机对战引擎免费用

    由微信小游戏举办的"微信小游戏创意大赛"正在火热进行中.12月23日,腾讯云宣布,除了给参赛者提供基础云资源,还将提供更多工具支持.开发者在通过初赛后,可免费使用腾讯云" ...

  4. 怎么开发联机小游戏_微信小游戏创意大赛火热进行中,小游戏联机对战引擎免费用...

    腾讯云为小游戏开发者升级工具箱 小游戏联机对战引擎免费用 由微信小游戏举办的"微信小游戏创意大赛"正在火热进行中.12月23日,腾讯云宣布,除了给创意大赛的参赛者提供基础云资源,还 ...

  5. 战双帕弥什登入显示服务器错误,战双帕弥什游戏进不去怎么办 卡在初始界面解决方法...

    战双帕弥什游戏进不去怎么办.<战双帕弥什>是一款末世科幻题材的3D动作手游,游戏马上要公测了,不过在之前的测试很多玩家都或多或少的出现过进不去游戏的情况,这个该怎么办呢?下面小编就为大家带 ...

  6. iOS简易蓝牙对战五子棋游戏设计思路之一——核心蓝牙通讯类的设计

    iOS简易蓝牙对战五子棋游戏设计思路之一--核心蓝牙通讯类的设计 一.引言 本系列博客将系统的介绍一款蓝牙对战五子棋的开发思路与过程,其中的核心部分有两个,一部分是蓝牙通讯中对战双方信息交互框架的设计 ...

  7. 在通知栏上玩游戏,Steve iOS 游戏实现思路

    在通知栏上玩游戏,Steve iOS 游戏实现思路 最近有一款游戏特别的火爆,叫做Steve ,一种可以在通知中心直接玩的游戏.作者的脑洞也是非常的大,实在让人佩服.其实实现起来也简单,就是用到了iO ...

  8. python逢7跳过_python实现逢七拍腿小游戏的思路详解

    逢七拍腿游戏 几个小朋友在一起玩逢七拍腿的游戏,从1开始数数,当数到7的倍数或者尾号是7时,拍一下腿.现在从1数到99,假设每个人都没有错,计算一下共要拍腿几次? 第一种实现思路:通过在for循环语句 ...

  9. 【有趣的Python小程序】Python多个简单上手的库制作WalkLattice 走格子游戏 (思路篇)上

    篇写上一个思路篇,那么今天我们就来完成这一项工作 源代码和配套文件 链接: https://caiyun.139.com/m/i?135ClY1yWrSKX 提取码:e4pq 复制内容打开中国移动云盘 ...

  10. iOS简易蓝牙对战五子棋游戏设计思路之二——核心棋盘逻辑与胜负判定算法

    2019独角兽企业重金招聘Python工程师标准>>> iOS简易蓝牙对战五子棋游戏设计思路之二--核心棋盘逻辑与胜负判定算法 一.引言 上一篇博客我们介绍了在开发一款蓝牙对战五子棋 ...

最新文章

  1. python 如何自学-如何系统地自学 Python?
  2. 【Oracle】触发器最系统入门学习指导
  3. Python3 编程示例:斐波纳契数列
  4. 下拉推广系统立择火星推荐_下拉词优化不仅仅优化百度,其实还可以优化抖音、京东和阿里巴巴...
  5. oracle删除orcl库_oracle删除数据文件
  6. 报错, org.hibernate.PropertyAccessException
  7. 使用c 对mysql数据库_c对mysql数据库的操作
  8. mysql in varchar_MySQL中char和varchar有啥区别?优缺点是啥?
  9. linux rm 命令删除文件恢复_rm删除文件空间就释放了吗?天真!
  10. Arduino框架下合宙ESP32C3 +1.8“TFT液晶屏通过TFT_eSPI库驱动显示
  11. 3种夸克有多少组合?
  12. iOS - 常用的iOS Mac框架和库以及常用的中文开发博客
  13. 积累的VC编程小技巧之打印相关
  14. MATLAB初学教程(一)
  15. String是基本数据类型吗?
  16. js正则匹配以{开头,以}结尾怎么写?
  17. 有什么靠谱的Python培训机构
  18. 用Go语言编程的利与弊
  19. 按键精灵官网版 v9.63.12960 绿色版
  20. ICC配置文件与photoshop颜色管理

热门文章

  1. 利用css实现返回顶部按钮
  2. java实验原理_java实验报告实验原理.doc
  3. mysql带参数游标_mysql游标的使用
  4. python学习手册教程_python学习手册视频教程
  5. xshell7,xftp7个人免费版官方下载,无需破解,免激活,下载即可使用
  6. 汉诺塔五层C语言程序,汉诺塔的c语言程序
  7. 七种滤波方法测试matlab
  8. linux系统下JDK的下载安装和配置教程
  9. Think PHP(TP)框架基础知识
  10. 数字电路课程设计--电子钟实验报告