联机初始化

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

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

Swoole开发环境

教程使用Swoole 4.3.1版本开发,但并没有使用协程等功能,只是使用了Swoole WebSocket Server,理论上安装旧版也是没问题的。环境需要大家自行安装,这个也是学习的一个过程,详情可以查看小册的附录一:Swoole入门篇(上)

以下是一些有可能有帮助的资料:

CentOS中一键安装PHP开发环境:lnmp.org/install.htm…

Swoole安装教程:wiki.swoole.com/wiki/page/6…

Redis安装教程:redis.io/download

php-redis扩展包:pecl.php.net/package/red…

童鞋们也可以像我一样在Windows使用PHPStorm进行开发,再通过一些方法如共享文件夹WinSCP等工具将项目在CentOS中运行起来。

安装swoole-ide-helper扩展

使用PHPStorm来开发Swoole项目的童鞋,如果不想面向运气编程的话,最好安装一个swoole-ide-Helper扩展来协作开发。

在项目根目录运行以下命令:

  1. composer require --dev "eaglewu/swoole-ide-helper:dev-master"

安装完毕我们就可以愉快地编写Swoole代码了。

服务端基本架构

5 联机初始化 - 图1

赵童鞋设想的基本架构特别简单,只有三个层,他们分别是:

  • 网络层:管理Swoole WebSocket对象,主要负责接收前端消息,传递到逻辑层进行处理。
  • 逻辑层:接收网络层传递的消息,主要负责游戏房间创建、玩家移动、结束检测等游戏逻辑。
  • 数据层:用于管理玩家ID、房间ID、匹配队列等数据。

管理这三个层分别需要三个类:ServerLogicDataCenter

其中最重要的就是Server类,它的作用就是作为服务端和客户端的消息交换中心,我们先来写一个初始化的Server类,在项目app目录新建Server.php

做题时间

  1. 使用面向对象的方式实现一个WebSocket Server类。
  2. 分别绑定startworkerStartopenmessageclose回调方法。
  3. 在类中引入composer自动加载机制。
  4. WebSocket Server对象配置4worker进程。

Swoole Websocket Server:wiki.swoole.com/wiki/page/3…

Server类:

  1. <?php
  2. require_once __DIR__ . '/../vendor/autoload.php';
  3. class Server
  4. {
  5. const HOST = '0.0.0.0';
  6. const PORT = 8811;
  7. const CONFIG = [
  8. 'worker_num' => 4,
  9. ];
  10. private $ws;
  11. public function __construct()
  12. {
  13. $this->ws = new \Swoole\WebSocket\Server(self::HOST, self::PORT);
  14. $this->ws->set(self::CONFIG);
  15. $this->ws->on('start', [$this, 'onStart']);
  16. $this->ws->on('workerStart', [$this, 'onWorkerStart']);
  17. $this->ws->on('open', [$this, 'onOpen']);
  18. $this->ws->on('message', [$this, 'onMessage']);
  19. $this->ws->on('close', [$this, 'onClose']);
  20. $this->ws->start();
  21. }
  22. public function onStart($server)
  23. {
  24. swoole_set_process_name('hide-and-seek');
  25. echo sprintf("master start (listening on %s:%d)\n",
  26. self::HOST, self::PORT);
  27. }
  28. public function onWorkerStart($server, $workerId)
  29. {
  30. echo "server: onWorkStart,worker_id:{$server->worker_id}\n";
  31. }
  32. public function onOpen($server, $request)
  33. {
  34. }
  35. public function onClose($server, $fd)
  36. {
  37. }
  38. public function onMessage($server, $request)
  39. {
  40. }
  41. }
  42. new Server();

我们来运行一下Server.php文件,顺便检验一下Swoole扩展是否运行正常,在项目app目录下,运行php Server

后面所有启动Server操作都是基于CentOS环境

  1. [root@localhost app]# php Server.php
  2. master start (listening on 0.0.0.0:8811)
  3. server: onWorkStart,worker_id:0
  4. server: onWorkStart,worker_id:1
  5. server: onWorkStart,worker_id:2
  6. server: onWorkStart,worker_id:3

输出以上信息就代表Server运行成功。

Logic类和DataCenter类属于游戏管理类,在Manager文件夹下新建Logic.phpDataCenter.php文件,并在DataCenter类中增加一个格式化输出日志的log()方法。

Logic类:

  1. <?php
  2. namespace App\Manager;
  3. class Logic
  4. {
  5. }

DataCenter类:

  1. <?php
  2. namespace App\Manager;
  3. class DataCenter
  4. {
  5. public static function log($info, $context = [], $level = 'INFO')
  6. {
  7. if ($context) {
  8. echo sprintf("[%s][%s]: %s %s\n", date('Y-m-d H:i:s'), $level, $info,
  9. json_encode($context, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
  10. } else {
  11. echo sprintf("[%s][%s]: %s\n", date('Y-m-d H:i:s'), $level, $info);
  12. }
  13. }
  14. }

前端初始化

5 联机初始化 - 图2

前端我们将会使用Vue框架来开发,项目只使用到了简单的v-ifv-for<template>WebSocket。如果没有接触过前端框架的童鞋,可以尝试阅读官方文档,或者阅读小册的附录三:Vue入门篇

Vue官方文档:cn.vuejs.org/v2/guide/

v-if:cn.vuejs.org/v2/guide/co…

v-for:cn.vuejs.org/v2/guide/li…

<template>cn.vuejs.org/v2/guide/li…

在项目根目录创建frontend文件夹,并在其中创建index.html文件,进行前端初始化。

做题时间

  1. 使用CDN的形式引入Vue框架。
  2. 根据Vue官方文档,编写一个HelloWorld页面。

index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>HideAndSeek</title>
  6. <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
  7. <link rel="icon" href="data:;base64,=">
  8. </head>
  9. <body>
  10. <div id="app">
  11. {{ message }}
  12. </div>
  13. <script>
  14. var app = new Vue({
  15. el: '#app',
  16. data: {
  17. message: 'Hello Vue!'
  18. }
  19. })
  20. </script>
  21. </body>
  22. </html>

前端的HelloWorld页面文件我们有了,但是我们要怎么通过HTTP请求获取到这个页面呢?这时候就需要使用SwooleStatic Handler功能。

Swoole Static Handler:wiki.swoole.com/wiki/page/7…

做题时间

  1. 根据官方文档添加Swoole Static Handler配置。
  2. Server新增一个监听端口8812,用于处理HTTP请求。

Server类:

  1. <?php
  2. ...
  3. class Server
  4. {
  5. ...
  6. const FRONT_PORT = 8812;
  7. const CONFIG = [
  8. ...
  9. 'enable_static_handler' => true,
  10. 'document_root' =>
  11. '/mnt/htdocs/HideAndSeek/frontend',
  12. ];
  13. public function __construct()
  14. {
  15. ...
  16. $this->ws->listen(self::HOST, self::FRONT_PORT, SWOOLE_SOCK_TCP);
  17. ...
  18. }
  19. ...
  20. }
  21. new Server();

记得将代码中的`document_root`改为自己项目的前端目录哦。

添加完以上代码后,在虚拟机中运行Server,并尝试通过浏览器访问http://【虚拟机ip】:8812/index.html浏览前端页面。

5 联机初始化 - 图3

看到以上画面就代表请求成功。

WebSocket初始化

现在项目的服务端有了,前端页面有了,就差一个桥梁把他们连接起来,这个桥梁就是WebSocket通信机制,项目使用到了四个简单的WebSocket方法:

  • onmessage
  • onopen
  • onerror
  • onclose

做题时间

  1. Vue实例新增data属性websock,在Vue实例创建完毕后,初始化WebSocket连接服务端并绑定上述四个方法。
  2. WebSocket对象建立连接后,在onopen回调方法中发送消息到服务端。
  3. 服务端接收到客户端发送的消息后,通过客户端的fd返回一条消息。
  4. 服务端增加客户端连接日志输出。
  5. 在页面关闭时,断开WebSocket连接。

以下是一些有可能有帮助的资料。

阮一峰WebSocket教程:www.ruanyifeng.com/blog/2017/0…

Vue生命周期:cn.vuejs.org/v2/guide/in…

Swoole WebSocket onMessage: wiki.swoole.com/wiki/page/4…

Swoole Client fd:wiki.swoole.com/wiki/page/5…

本章项目初始化就到这里啦,请童鞋们尽量完成自己的Homework后,再进入下一章的学习。

本章对应Github Commit第五章结束

当前项目结构:

  1. HideAndSeek
  2. ├── app
  3. ├── Manager
  4. ├── DataCenter.php
  5. ├── Game.php
  6. └── Logic.php
  7. ├── Model
  8. ├── Map.php
  9. └── Player.php
  10. └── Server.php
  11. ├── composer.json
  12. ├── composer.lock
  13. ├── frontend
  14. └── index.html
  15. ├── test.php
  16. └── vendor
  17. ├── autoload.php
  18. └── composer