1 Star 1 Fork 0

熊若晗 / football1922

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Unlicense

从零开始写多人足球游戏

说明

本文是选修 Linux 操作系统后,完成结课大作业时编写的报告。

在完成结课大作业的过程中,我编写了一个游戏服务端程序、一个对应的游戏客户端程序。

在编写服务端程序的过程中,我使用 Linux 系统调用实现了全部游戏逻辑、交互协议、多用户并发访问和基本的状态/故障指示。

该服务端程序运行于 Linux 操作系统上,部署在位于新加坡的 CentOS 服务器上,可在互联网上直接访问。该服务器配备了 10Gbps 网卡,但由于服务器的地理位置在海外,网络连接会受到主干网出口的影响。

vi server.c
cc -lm -lpthread server.c # 在x86机器上编译需要“-std=gnu99”

该服务端程序使用 C 语言编写,源代码采用 C99 标准,使用了 math 库和 pthread 库,在编译时需要额外指明连接这两个库;使用 aarch64 架构 Linux 系统接口,具体系统内核版本为5.14.0-214.el9。

游戏进行中的客户端截图
图1:客户端截图

该客户端程序使用 Java 编写,源代码采用 Java 1.8 SE,使用 Java Swing 图形化接口。客户端截图展示了10个玩家同时游戏时,其中一位白方玩家的客户端状态。

成果介绍

我编写的是一款多人足球游戏,给它的名字是 football 1922,代表该游戏一百年以前,象征这是一款画质和玩法上古的游戏。游戏默认支持最多 10 人同时在线游玩,可以通过调整源代码中的宏定义来修改最多游戏人数。游戏服务端按照玩家的进入顺序,将他们分为两队相互对抗。

由于 COVID-19 肆虐全国,本人也不幸中招,我在完成大作业时,不得不遵循非必要不设计的原则,以在本就不富裕的清醒时间中完成任务。双端从设计到编写完成一共花费了 16hr,仅持续工作了不到两天,论其速度,也算得上是我个人历史上的工程奇迹了。

玩法介绍

玩家可以在球场上移动、转向、碰撞、抢球、传球和冲刺。

首先需要用鼠标点击聚焦客户端左下角的文本输入框,使 Java Swing 能够读取玩家的键盘信息。

移动

使用 W、S、A、D 来前后左右移动画面中的角色。你会看到角色在画面中的绝对位置并未改变,而是角色脚下的地图在移动。这是由于客户端在现实画面的时候进行了坐标系变换,玩家看到的是自己角色视角下的画面。

玩家的默认移动速度可以在服务端代码的宏定义中调节。

转向

使用 J 向左转,使用 L 向右转。同角色移动一样,旋转会改变画面的视角,因为玩家相对于地图在旋转。

玩家的转向速度可以在服务端代码的宏定义中调节。

碰撞

当两个玩家相互运动直到接触时,玩家之间的距离将不能再减小。当一位玩家静止不动时,其他玩家可借助这种碰撞来推动静止的玩家。

同样的,由于存在碰撞,当玩家运动到地图边缘时,将不能继续运动离开地图。

抢球

当玩家和球相遇之后,球将会自动被转移到玩家正前方紧贴玩家处,此过程被称为抢球/持球。

不同队伍的玩家可以通过运动到对方玩家正前方,来获得球的控制权。

传球

当玩家持球时按下 K 键,球将会被以大于玩家的速度被向前弹出。

同一队伍的玩家可以借此将球向前快速传递,你也可以用这个动作来射门或躲避对方的抢球。

传球时,球会被赋予向前一定范围内随机方向的速度,用来模拟玩家传球时的误差,该散射角度可以在服务端的宏定义中调节。

冲刺

当玩家希望以更快的速度移动时,可以按下 I 键。这将消耗球员的体力来获得部分加速。

球员的体力由球员的半径所体现,当球员的体力(半径)减少到一定程度后,玩家将不能再加速。体力(半径)会在玩家没有加速的时候缓慢恢复。

以上所有参数:加速比率、体力消耗速率、体力恢复速率、最大半径和最小半径,都可以在服务端的宏定义中调节。

架构视图

本节将介绍服务端软件的设计结构,其基本框架如图\ref{pic:架构视图}所示。

Football1922 架构视图
图2:Football1922 架构视图

代理线程

当收到来自客户端的请求时,主线程(父线程)会创建一个子线程来与之对接,翻译客户端端的请求,并代理客户端修改游戏模型中由客户端控制的部分,从而保证玩家的意图能被正确传递到服务端。子线程会在创建后,立即断开与父线程的连接,防止僵尸线程。

模型步进

服务端在启动时,先初始化程序状态和游戏模型,同时向操作系统内核注册一个真实时间的定时器,用来按时计算模型的演化。

游戏模型中的玩家部分被来自客户端的请求不断修改,当主线程收到来自内核的定时器信号时,立即快开始推算游戏的下一帧,判断并调整游戏内各个元素的状态;同时维护程序状态,定时在标准输出流中写入游戏状态信息。

服务端程序已经预先编写好了,能输出游戏模型大部分状态的函数,可以简单修改代码实现定时展示系统状态。默认系统每 10sec 输出一次 heartbeat,用来表示自己正常运行。

技术选型

并发

本程序使用多线程技术来实现并发。

子线程在创建后立即与父线程断开连接。

通信

本程序使用全局变量来进行通信。

由于程序在运行时只有一个进程,因此不需要使用进程间通信。由于需要通信交换数据的部分即为游戏模型,而游戏模型的生命周期即为服务端进程的生命周期,因而可以直接采用全局变量来实现线程间通信。

同步

本程序不使用同步技术。

程序在设计时按照代理模式,一个客户端只更新内存中对应自己的数据,不存在写入竞争,故不需要使用同步技术。

对于脏读的问题,由于程序默认的模型刷新时间间隔为 10ms,部分写入的客户端状态数据只会影响很短的时间,在下一次模型刷新时被修正,这种误差对用户来说是很难感知的。

更新

本程序使用真实时间的定时器来实现稳定更新。

游戏服务端在初始化时即向操作系统注册了一个定时器,采用较短时间间隔的真实时间定时。通过信号处理函数来实现定时更新模型。

协议

我设计了定长的字节协议,方便两端读取信道中的消息,也为未来优化通信时,建立持续信道做准备。

请求定义

请求头部为 4 个字节的标识,标识请求的类型。合法的请求类型已在宏定义中列出。

请求体为一个联合体,包含了三种可能的消息内容:加入时的姓名、要退出游戏的 id 和玩家的指令。

玩家的指令由四个字节的 id 和四个字节的具体指令组成。

合法的指令已由宏定义给出,将需要的指令按位或连接即可得到传送时的指令。

#define GET_STATUS  0
#define UPDATE_SELF 1
#define JOIN_GAME   2
#define QUIT_GAME   3

#define TURN_LEFT     0x01
#define TURN_RIGHT    0x02
#define MOVE_LEFT     0x04
#define MOVE_RIGHT    0x08
#define MOVE_FORWARD  0x10
#define MOVE_BACKWARD 0x20
#define SHOOT         0x40
#define SPEED_UP      0x80

typedef struct player_instruction {
    int  id;        // 要更新的玩家id
    int  move;      // 要更新的玩家数据,由上方的宏定义连接得到
} ORDER;

typedef union req_data {
    char   join_name[64];
    int    quit_id;
    ORDER  order;
} req_data;

typedef struct request {
    int      flag;
    req_data data;
} req;

响应定义

按照不同的请求类型,服务端直接响应对应的结构体,仅当遇到无法解析的请求时,会发送“bad request”字符串。

困难与解决

在设计与实现服务端和客户端程序的过程中,我也遇到了些许问题。

协议设计

在程序设计早期,我有考虑过采用类似 HTTP 的字符请求响应协议,但由于这样做涉及到大量字符串操作,可能会影响性能,以及为编程和 debug 带来困难,我最后采用了统一请求格式的字节协议。

通过请求消息结尾的联合体,可以解析为不同请求的具体消息,这样也方便了消息的统一读取。

并发处理

在程序设计早期,我有考虑过使用 epoll 的 I/O 多路复用来实现并发,但马上由于其设计和实现的困难程度而放弃了 I/O 多路复用,转而使用传统的多线程/多进程方案。

使用 epoll 最难的部分是非阻塞信息处理。由于需要设置非阻塞的信道,这将导致一条消息可能会被拆分成多部分分开被读取。处理这种情况需要将不完整的消息先缓存,等待所有消息被读取,能组合成完整的协议消息时,再来解析该信息。这样会导致程序的设计难度大大提高。

数据同步

使用多进程需要做到不同进程间的数据源统一,需要使用消息队列,管道或共享内存;还需要使用信号量或者锁来进行同步,这为程序的编写和 debug 带来了不小难度。

我最后采用了多线程,并使用全局变量来通信,当有同步需要时,可以简单的通过锁协议来实现。

特性

本章将介绍我在完成大作业过程中所设计的双端程序的特性。

模型更新

使用定时器信号实现真实时间下的模型更新,模型演化的时间误差不到 0.5%。

应用层协议

  • 采用类 HTTP 的无状态协议,不需要建立持续连接,实现了单一职责。

  • 采用统一请求结构,使得消息的读取写入变得方便。

  • 模仿 fcntl 中的 flag,采用按位或连接多个操作,压缩并简化了表示消息的结构。

性能和负载

内存占用维持在 1M 以内,CPU 占用在 5% 以内。基本不受负载影响。

由于使用了高效的字节协议,在每秒 50 次请求时,网络负载为上行 66kB/s,下行 33kB/s。网络负载与请求量基本维持线性关系。

每秒50次请求
图1:每秒50次请求
尝试1000次/s
图1:尝试1000次/s

负载

图\ref{pic:50pps}展示了软件在每秒50次请求下的负载,在一次消息结构优化后,响应消息的长度减短了 85%,该图也展示了在每秒 50 次请求时,消息结构优化前后的网络负载变化。

压力测试

我尝试使用定时器发送每秒一千次(实际 600+ 次每秒)获取游戏状态的压力测试,在该负载下,CPU 占用为 12.6%。

由于请求消息长度较短,每次请求上行只会传递 1076 字节,所以瓶颈基本出现在请求的响应频率上(创建 TCP 连接的频率)。

经过几轮测试,我得到的最大网络占用为 2.2M 字节每秒,约合请求响应 2000 次每秒。

游戏设计为 10 人同时在线,目标刷新率为 50Hz;每秒 2000 次请求能让每位玩家流畅游玩。

This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to <http://unlicense.org>

简介

football1922,代表该游戏来自一百年以前,象征这是一款画质和玩法上古的游戏。 游戏客户端使用原生Java开发,服务端使用C语言,运行在CentOS上 游戏默认支持最多 10 人同时在线游玩,可以通过调整源代码中的宏定义来修改最多游戏人数。 游戏服务端按照玩家的进入顺序,将他们分为两队相互对抗。 展开 收起
C 等 4 种语言
Unlicense
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
C
1
https://gitee.com/Dar-Xs/football1922.git
git@gitee.com:Dar-Xs/football1922.git
Dar-Xs
football1922
football1922
master

搜索帮助