热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

用Swoole来写个联机对战游戏呀!(七)异步匹配玩家

联机逻辑开发进度:■■■■■□□□□□□□本章结束开发进度:■■■■■■■□□□□□

联机逻辑开发进度:■■■■■□□□□□□□

本章结束开发进度:■■■■■■■□□□□□

上一章的答案:

DataCenter 类:

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_idplayer_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
  • 服务端连接时保存玩家信息
  • 前端发送 code600 的指令
  • 服务端将 player_id 放入匹配队列

剩下的操作就是:

  • 检测匹配队列长度,当长度大于等于2时,创建游戏房间

异步检测匹配队列

我们大部分游戏逻辑都是运行在 worker 里的,异步的玩家匹配可以减轻主程序 worker 的负担。关于 Task 机制不了解的童鞋,请先熟悉一下官方文档。

  • Swoole Task 文档: https://wiki.swoole.com/wiki/...
  1. 根据官方文档,在 Server 类中完成 Task 机制的初始化。
 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 类:

task(xxx);
    }
}

Server 类:

 
 

可以发现, onTask 方法只是接收传递的 $data ,当我们有多种 Task 任务 (匹配玩家、在线检测、游戏状态检查) 时,我们的 worker 怎么区分每一个 Task 任务呢?其实就和客户端与服务端通信一样,我们可以根据一个 code 来区分。

Logic 类:

task(['code'=>'xxx']);
    }
}

Server 类:

xxx();
                break;
            case 'yyy':
                //task->yyy();
                break;
        }
    }
    ...
}
...

从代码可以看出,我们现在缺了两种机制:

  • 全局获取Server对象:在 Logic 中获取 swoole_server 从而调用 task() 方法。
  • 增加Task管理类:需要一个类管理 TaskcodeTask 需要执行的逻辑方法。

全局获取Server对象

第一个比较好处理,我们在 onWorkerStart 的时候就能获取到 swoole_server

  • 有童鞋可以会问,为什么不在 onStart 的时候获取?这是因为 onStart 回调的是 Master 进程,而 onWorkerStart 回调的是 Worker 进程,只有 Worker 进程才可以发起 Task 任务。有兴趣的童鞋请查阅文档: https://wiki.swoole.com/wiki/...
  1. DataCenter 中新增静态变量 $server
  2. onWorkerStart 回调函数中,将 $server 保存到 DataCenter 中。

DataCenter 类:

 
 

Server 类:

 
 

这样就解决了第一种问题,下面轮到第二个问题。

增加Task管理类

在项目 Manager 文件夹下,创建 TaskManager 类文件。

TaskManager 类:

 
 

后续所有跟 task 有关的常量、方法都归于这个类来管理。

  1. 设置一个常量 TASK_CODE_FIND_PLAYER ,用于发起寻找玩家 task 任务。
  2. 新增静态方法 findPlayer() ,当匹配队列长度大于等于 2 时,弹出队列前两个玩家的 player_id 并返回。

TaskManager 类:

= 2) {
            $redPlayer = DataCenter::popPlayerFromWaitList();
            $bluePlayer = DataCenter::popPlayerFromWaitList();
            return [
                'red_player' => $redPlayer,
                'blue_player' => $bluePlayer
            ];
        }
        return false;
    }
}

现在前置准备就绪,可以将上面写过的伪代码改成真实代码啦~

  1. Logic 类的 matchPlayer() 方法中,发起一个 Task 任务尝试匹配。
  2. Server 类中根据传入的 code ,执行 TaskManagerfindPlayer() 方法。
  3. findPlayer() 方法有值返回的时候,返回执行结果并携带上 codeworker 进程。

本章就到这里结束了,这次留的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来写个联机对战游戏呀!(七)异步匹配玩家


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 我们


推荐阅读
author-avatar
用户rsvowi2rvt
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有