从一个游戏情怀说起

接触的第一款多人对战游戏是帝国时代,依稀记得那时候上学每周最期待的就是冲到电脑课撸一把罗马复兴,高中开始接触《魔兽争霸3》,一款真正让我迷恋十多年的游戏,怀念那时候的《魔兽争霸十大经典战役》还有到图书馆翻 《大众软件》找各种电子游戏相关的新闻的日子,之后和很多人的经历一样,有了 Dota 有了王者荣耀,打一款MOBA游戏几乎成家常便饭,最近也没忍住撸到王者六十多星 ╮(╯_╰)╭。

帝国时代

魔兽争霸3

阴差阳错成为了一名码农,但不幸的是从来没有机会真正去涉足游戏开发者行业。去年魔兽3重制版出来,没忍住交出了一笔情怀税,算是弥补这么多年对暴雪的亏欠,然后转念一想,码农快十载了难道还任由自己继续堕落下去吗?对战类游戏最大的乐趣就是 “与人斗主宰一切的感觉”,“Triple Kill” “Monster Kill” 缭绕于耳,然而再想想那个真正在虚拟世界主宰一切的其实是制定游戏规则的人,也就是游戏创作者,那种当作者的感觉不是2.5D视角的而是真正的上帝视角,所以去年年中开始决定转行求变,从零起步了解下游戏设计,先从技术入手,啰嗦很多,当然不是为了给自己沉迷网络游戏找借口啦。

一个简单的聊天室

若要问一个能集合多人互动又需要实时同步的简单场景是什么?答案就是聊天室,这也是很多游戏框架的入门demo,不例外,我也是从聊天室开始学习的,很快,写这篇文章的现在我大概花了那么丁点时间快速撸了一个,顺带凭着这么多年积累的前端美感对界面稍微加了点样式,代码地址。

聊天室

如果你是一个前端从业者,相信你很快会想到使用 socket.io, 如果你不是,相信你也听过 Websocket。是的,因为简单,我们不用花时间去理解 TCP 的三次握手,拿来即用。为什么聊天室需要Websocket,答案是长连接,在聊天室里,一个房间的任何消息变化都要通过服务端实时广播推送给各个客户端,如下,client1 发送一条消息,其他的 client 都需要收到服务端的消息,而这个的前提是服务端需要知道有多少客户端连接着。

C/S

对比下 http 请求,client1 发送完消息 (Request) 服务端接收后并返回 (Response) 即断开,如此服务端是无法获得其他客户端的连接状态并推送,不过 http 可以使用轮询 (Polling),每个客户端隔一段时间发送一个请求到服务端,如果有发现别的客户端的发送聊天室消息就返回数据,消息延迟跟轮询时间间隔有关, 如此也能做一个聊天室,想想任务也就完成了,但是如果这个聊天室是马化腾发起的呢,目标是做成微信呢?

性能,才是一款优秀的游戏服务器追寻的目标,一条消息服务端广播的数量和客户端数量成正比,n 条消息就是 n * n, 如果再配上轮询,想想王者荣耀 460ms 的延迟是一个玩家能忍受的吗。回到 Websocket 同样会带来性能瓶颈,早期的网络游戏服务器大多是单台服务器单进程架构,所有逻辑都写在一起,同时长连接也需要比短连接带来更多的内存开销,如存储所有客户端Session信息,且内部其实也是通过某种轮询去实现的,这些总总,当我们想去打造一个 “企业级的游戏框架” (这个说法来自 eggjs =。=) 的时候,简单的使用 socket.io, 在面临大量的在线客户端时候,我们可能就到此就止步了,这也是这篇文章的一个背景和初衷,我想聊聊游戏服务器为了性能到底能做什么,可能经验不足,但至少搞下来收获满满。

分布式多进程模型设计

我在 Github 搜了很多游戏框架并对比,最终映入眼帘的就是网易的 pomelo。一是网易的大厂背景,想想当年的梦幻西游,二是它的文档架构的完备性,所以我花了很多时间把它的代码几乎都看完,但是由于它的代码年久失修几乎不维护,同时秉承前端造论圈的坏风气,我重新参考它的代码以更现代化的方式写了一个游戏框架 Regax,并美化了下架构图:

我们回顾上节所讲的性能瓶颈:

  • 单进程单服务器无法承载更多的客户端。
  • 长连接广播带来的开销巨大,特别是游戏场景很频繁需要推送消息。

再看下上边的图到底做了什么:

  • 第一点,所有业务逻辑都以进程服务器粒度拆分,拆分越细越好,提升伸缩性,进程间通过RPC调用,如此可保证进程可跨集群服务器调用,这是分布式架构的基本。
  • 第二点,Socket连接服务器单独拆分,这是最关键的,它只负责连接及广播,不负责任何其他的业务逻辑,保证其性能最大化。

除了解决上节问题再进一步优化:

  • 第三点,协议层更加灵活,不再只是Websocket,由于连接服务器的隔离加纯粹性,服务器可支持多种连接方式共存,如此我们能承载的客户端更多,还可支持灵活切换,如真正的业务场景tcp和udp可根据客户端支持情况自动切换。
  • 第四点,引入网关层,网关层用来控制连接的路由算法,想想农药里的服务器分区策略,再比如地理位置就近原则,分配就近的服务器,进一步提升网络传输效率。
  • 第五点,进程支持权重,权重越高,分配的进程越多机会越大,这也是伸缩性的一种提升。

其他模块就是大众服务器所通用的扩展,如监控及存储等,这里不赘述,真正去理解专研一款优秀的框架设计时候,真的会爱不释手。

一切准备就绪,设计完框架后急需一个业务场景去试炼一番,以此来反哺框架,想想现在能做的太多了,撸一个页游传奇Online渣渣灰绰绰有余,在我所在的支付宝小程序团队也很需要创新场景,框架本身也能给业务带来更多的可能性更多的玩法,最终敲定做了一款简单的多人实时对战贪吃蛇, 可支持和好友一起玩,这时候才是体会开发游戏的乐趣所在。

多人实时对战贪吃蛇

我们参照了王者荣耀的好友匹配+对战的模式设计了下贪吃蛇,如下:

贪吃蛇房间匹配页面

贪吃蛇对战页面

贪吃蛇游戏结束排名

首先按上节的架构,我对服务器做了拆分:

  1. 连接服务器 (ConnectorServer):负责和客户端的Websocket连接及通知,同时校验登陆Token,如果Token不合法直接关闭连接,连接后通过token再去数据库拿用户的昵称等信息。
class ConnectorServer {enter({ token }){// 1. 校验 Token// 2. 通过 Token从数据库获取用户信息, 并创建 Session// 3. 监听 Socket关闭this.ctx.session.on('disconnect', () => {// 4. 如果当前用户在某个房间,发送RPC通知房间服务器踢掉用户this.ctx.rpc.room.kickUser(this.ctx.session.uid)})}
}

2. 房间服务器 (RoomServer): 负责房间的创建及加入,并通知房间里所有的用户信息

class RoomServer {kickUser() {// 1. 踢掉用户// 2. 发送 RPC 给 ConnectorServer 广播给客户端房间信息, 这里channel内部封装了rpcthis.ctx.channel.room.pushMessage('onRoomChange', roomData)}joinUser() {// 1. 加入用户// 2. 发送 RPC 给 ConnectorServer 广播给客户端房间信息, 这里channel内部封装了rpcthis.ctx.channel.room.pushMessage('onRoomChange', roomData)}startGame() {// 1. 发送RPC给 BattleServer 开始游戏this.ctx.rpc.battle.start(roomMembers)}
}

3. 对战服务器 (BattleServer): 贪吃蛇开始游戏后,会在服务端建立 帧同步 模式,并定时推送消息, 帧同步会再之后介绍:

class BattleServer {start() {// 模拟帧同步,真正实现会比这个复杂setInterval(() => {// 按每秒三十帧的频率发送帧数据给所有客户端this.ctx.channel.battle.pushMessage('onBattleFrame', currentFrame)},1000 / 30)},syncFrameAction() {// 从客户端接收到贪吃蛇的操作动作并插入到当前帧数据里}
}

而在客户端:

import { Client } from '@regax/client-websocket'const client = new Client({ url: 'ws://localhost:8002', reconnect: true })// 监听服务端断线
client.on('disconnect', () => {})// 1. 创建 WebSocket 连接
await client.connect()// 2. 监听房间成员变化,这里会通过服务端广播接收到
client.on('onRoomChange', ( roomData) => {} )// 3. 监听游戏开始后的帧数据变化
client.on('onBattleFrame', ( frame) => {// 每接收到一帧,就驱动贪吃蛇渲染引擎渲染一次
})// 4. 登陆并校验token
await client.request('connector.enter', { token })
// 5. 加入房间
await client.request('room.joinUser')
// 6. 点击开始游戏
await client.request('room.startGame')
// 7. 操作贪吃蛇时候发送操作行为
await client.request('battle.syncFrameAction', { action })

这样一款多人对战版的贪吃蛇算是基本完成了,但是真正实现的时候遇到不少的坑,如卡顿严重,另外为什么要使用帧同步,帧同步和状态同步的区别在哪,再下一章我会聊一聊这个话题。

最后

如果大家想体验可以到支付宝搜下 `福利贪吃蛇`, 目前集群机器还比较少请轻虐,最后,不忘记招聘,如果你有兴趣,可以私信我, 阿里系能给你的自由度及想象空间挺大。

转自https://zhuanlan.zhihu.com/p/114150098

NodeJS 开发多人实时对战游戏服务器 (一)相关推荐

  1. TS 也能开发多人实时对战?

    大厂技术  高级前端  Node进阶 点击上方 程序员成长指北,关注公众号 回复1,加入高级Node交流群 ❓ 帧同步和状态同步可以并用? ❓ 200ms 毫秒延迟也能实现单机游戏般的丝滑流畅? ❓ ...

  2. 从零学习游戏服务器开发(一) 从一款多人联机实时对战游戏开始

    写在前面的话 经常有学生或者初学者问我如何去阅读和学习一个开源软件的代码,也有不少朋友在工作岗位时面对前同事留下的项目,由于文档不完善.代码注释少.工程数量大,而无从下手.本文将来通过一个多人联机实时 ...

  3. .net 实时通信_【WebSocket】实时多人答题对战游戏

    本文公众号来源:后端技术漫谈 作者:蛮三刀把刀 前言 前两章教程,我们使用WebSocket的基础特性打造了一个小小聊天室,并在第二章对其进行了集群化改造. 系列教程回顾: 手把手搭建WebSocke ...

  4. 答题对战方案java_使用WebSocket实现实时多人答题对战游戏

    前言 前两章教程,我们使用WebSocket的基础特性打造了一个小小聊天室,并在第二章对其进行了集群化改造. 系列教程回顾: 在本文中,我将介绍如何使用WebSocket向实时多人答题对战游戏提供服务 ...

  5. [WebSocket]使用WebSocket实现实时多人答题对战游戏

    前言 前两章教程,我们使用WebSocket的基础特性打造了一个小小聊天室,并在第二章对其进行了集群化改造. 系列教程回顾: [WebSocket]第一章:手把手搭建WebSocket多人在线聊天室( ...

  6. 基于DevCloud进行黑白棋实时对战游戏开发实践【华为云至简致远】

    [摘要] 本次实践让我体验到了全程在云上创建弹性云服务器ECS,配置云服务器环境,在DevCloud平台上一站式进行项目管理.代码托管.代码检查.流水线.编译.构建.部署.测试.发布的流程.基本做到了 ...

  7. 分享一套仿英雄联盟大型多人联机实时对战游戏源码(包含完整服务器和客户端源码)...

    源码介绍 这是一款类似英雄联盟的 5V5 实时对战游戏,服务器端使用 C/C++ 开发,同时支持 Linux 和  Windows 系统部署:客户端使用 C# + Unity3D 开发. 我们先来看下 ...

  8. 一套仿英雄联盟大型多人联机实时对战游戏源码(包含完整服务器和客户端源码)...

    源码介绍 这是一款类似英雄联盟的 5V5 实时对战游戏,同时支持 Linux 和  Windows 系统部署. 我们先来看下这个游戏的内容吧,这是在我机器上编译后运行的效果图: 登录界面 进入后台配置 ...

  9. 手把手教你实战开发黑白棋实时对战游戏

    摘要:本次实践可以体验到全程在云上创建弹性云服务器ECS,配置云服务器环境,在DevCloud平台上一站式进行项目管理.代码托管.代码检查.流水线.编译.构建.部署.测试.发布的流程. 本文分享自华为 ...

最新文章

  1. 攻防世界-crypto-OldDriver(RSA低加密指数广播攻击) 方法总结
  2. spring mvc静态资源访问的配置
  3. python matplotlib:figure,add_subplot,subplot,subplots讲解实现
  4. c语言中分号存在的意义,问什么C程序里总是提示缺少分号;,而明明有分号?
  5. 骨骼动画编辑器Spine的纹理打包器(texture packer)
  6. File类的一些方法测试
  7. GA-T1400协议--人脸数据
  8. ffmpeg 反复推流_FFmpeg 推流问题记录
  9. 高端物理学名词_物理名词大全
  10. word复制某些公式无响应处理方法
  11. Galaxy Note20 Ultra 开发者选项
  12. asp企业建站系统 最新推出的 免费下载
  13. Python工程师面试汇总
  14. 模型并行,数据并行,参数平均,ASGD
  15. SECS连接模式中active与passive
  16. 使用Fiddler抓取夜神模拟器上的包
  17. linux安装chrome浏览器
  18. 全志A10/A20 Bootloader加载过程分析
  19. HG30-3交直流校准源
  20. 深入理解黑客攻击-键盘记录器

热门文章

  1. 南开计算机考研真题,(NEW)南开大学《812计算机综合基础》历年考研真题汇编(83页)-原创力文档...
  2. 关于全球时间的一点理解
  3. OS学习笔记-17(清华大学慕课)进程的同步和互斥
  4. torch.mul、matmul、mm、bmm的区别
  5. 靠一颗火锅丸子弯道超车三全,安井到底凭什么?
  6. 从估值、稀释和倍数的角度来看 Yuga labs 、Opensea 等明星 NFT 项目
  7. F1 Score详解 查准率较高 召回率较低怎末处理?
  8. 鼠标悬浮显示禁止图标
  9. CSS选择器(id选择器,包含选择器,标签名选择器,类选择器,属性选择器,通配符选择器,伪类选择器,相邻选择器,选择器的优先级,子选择器)
  10. 德国汉诺威地面材料展览会搭建就找上海宽创国际