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

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

从一个游戏情怀说起接触的第一款多人对战游戏是帝国时代,依稀记得那时候上学每周最期待的就是冲到电脑课撸一把罗马复兴,高中开始接触《魔兽争霸3》ÿ

从一个游戏情怀说起

接触的第一款多人对战游戏是帝国时代,依稀记得那时候上学每周最期待的就是冲到电脑课撸一把罗马复兴,高中开始接触《魔兽争霸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


推荐阅读
  • Asynchronous JavaScript and XML (AJAX) 的流行很大程度上得益于 Google 在其产品如 Google Suggest 和 Google Maps 中的应用。本文将深入探讨 AJAX 在 .NET 环境下的工作原理及其实现方法。 ... [详细]
  • 深入解析Unity3D游戏开发中的音频播放技术
    在游戏开发中,音频播放是提升玩家沉浸感的关键因素之一。本文将探讨如何在Unity3D中高效地管理和播放不同类型的游戏音频,包括背景音乐和效果音效,并介绍实现这些功能的具体步骤。 ... [详细]
  • ArcBlock 发布 ABT 节点 1.0.31 版本更新
    2020年11月9日,ArcBlock 区块链基础平台发布了 ABT 节点开发平台的1.0.31版本更新,此次更新带来了多项功能增强与性能优化。 ... [详细]
  • Spring Security基础配置详解
    本文详细介绍了Spring Security的基础配置方法,包括如何搭建Maven多模块工程以及具体的安全配置步骤,帮助开发者更好地理解和应用这一强大的安全框架。 ... [详细]
  • 视觉Transformer综述
    本文综述了视觉Transformer在计算机视觉领域的应用,从原始Transformer出发,详细介绍了其在图像分类、目标检测和图像分割等任务中的最新进展。文章不仅涵盖了基础的Transformer架构,还深入探讨了各类增强版Transformer模型的设计思路和技术细节。 ... [详细]
  • Python3爬虫入门:pyspider的基本使用[python爬虫入门]
    Python学习网有大量免费的Python入门教程,欢迎大家来学习。本文主要通过爬取去哪儿网的旅游攻略来给大家介绍pyspid ... [详细]
  • 探讨了在HTML表单中使用元素代替进行表单提交的方法。 ... [详细]
  • JavaScript 跨域解决方案详解
    本文详细介绍了JavaScript在不同域之间进行数据传输或通信的技术,包括使用JSONP、修改document.domain、利用window.name以及HTML5的postMessage方法等跨域解决方案。 ... [详细]
  • 本文探讨了异步编程的发展历程,从最初的AJAX异步回调到现代的Promise、Generator+Co以及Async/Await等技术。文章详细分析了Promise的工作原理及其源码实现,帮助开发者更好地理解和使用这一重要工具。 ... [详细]
  • 尽管在WPF中工作了一段时间,但在菜单控件的样式设置上遇到了一些基础问题,特别是关于如何正确配置前景色和背景色。 ... [详细]
  • MITM(中间人攻击)原理及防范初探(二)
    上一篇文章MITM(中间人攻击)原理及防范初探(一)给大家介绍了利用ettercap进行arp欺骗及劫持明文口令,后来我发现好友rootoorotor的文章介绍比我写的更透彻,所以基础利用大家可以参看 ... [详细]
  • 本文探讨了如何利用RxJS库在AngularJS应用中实现对用户单击和拖动操作的精确区分,特别是在调整区域大小的场景下。 ... [详细]
  • 在测试软件或进行系统维护时,有时会遇到电脑蓝屏的情况,即便使用了沙盒环境也无法完全避免。本文将详细介绍常见的蓝屏错误代码及其解决方案,帮助用户快速定位并解决问题。 ... [详细]
  • Fiddler 安装与配置指南
    本文详细介绍了Fiddler的安装步骤及配置方法,旨在帮助用户顺利抓取用户Token。文章还涵盖了一些常见问题的解决方案,以确保安装过程顺利。 ... [详细]
  • 本文详细探讨了在Java TCP编程中,如何理解和测量并发连接数、请求数及并发用户数,并提供了实际应用中的测试方法和优化建议。 ... [详细]
author-avatar
_Terr1鄭x宜_F
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有