RTS 客户端-服务器网络
Stone Monarch 从一开始就支持多人游戏,但随着时间的推移,网络模型经历了多次迭代。我最初基于这篇著名的帝国时代文章实现了点对点锁步模型。
点对点锁定步骤有一些众所周知的问题。点对点方面使玩家很难相互连接,并增加了每个新玩家的网络负载。锁定步骤方面很容易出现棘手的错误,导致玩家之间的游戏状态不同步。我当前的架构引入了服务器,并且还放宽了锁步的一些确定性要求。它仍然使用“回合”的锁步概念来确保每个客户端运行相同的模拟,并且在没有收到所有玩家的命令的情况下不会继续进行。
客户端-服务器锁定步骤

锁步转弯示例
游戏分为一系列固定持续时间的“回合”。游戏开始时的第一步是确定 1 回合的长度。这是通过测量消息从每个客户端到服务器的往返时间来完成的。服务器选择最长的时间作为回合长度。在游戏过程中,可以根据观察到的网络性能调整回合长度。
每当玩家想要执行某个操作时,请求的操作就会立即发送到服务器。服务器聚合它收到的所有操作,直到到达下一个回合边界。此时,服务器向所有客户端发送轮次消息,其中包含未来 1 轮要执行的操作。当客户准备好执行下一个回合时,他们应该已经收到模拟回合所需的所有信息。
在上面的示例中,转弯长度已设置为 100ms。服务器在第 1 轮期间接收操作,并在第 2 轮开始时发送包含第 3 轮操作的轮次消息。该消息应及时到达客户端,以便客户端执行第 3 轮。
处理延误

客户端延迟
如果其中一个客户端在准备好执行该轮次时没有收到轮次消息,则它必须暂停模拟,直到收到来自服务器的轮次。一旦它收到回合,它就可以立即开始再次执行。此时客户端将稍微落后于服务器的模拟,因此更有可能及时接收轮流。然而,玩家的命令发出和执行之间的延迟将会增加。

服务器端延迟
为了防止客户端远远落后于服务器的模拟(并遭受高命令延迟),服务器可以尝试检测到这一点并暂停其自己的模拟。为此,客户端可以在发送到服务器的每个操作中包含当前游戏时间。然后服务器将该时间与自己模拟中的当前游戏时间进行比较。如果差异大于 1 圈的长度(或任何任意长度),服务器可以暂停以允许客户端赶上。如果任何其他客户端在模拟中进一步领先,这可能会导致它们也暂停,直到所有客户端彼此更加同步。
在我最初的实现中,我使每次暂停的长度等于 1 圈的长度,这似乎是合乎逻辑的,可以防止客户在模拟中向前或向后滑动。然而,当前的方法极大地减少了暂停发生时的持续时间。通常,玩家甚至不会注意到它们。现在可以允许慢速客户端稍微落后于其他客户端,理论上他们可能处于劣势,因为他们的操作将需要更长的时间来执行。然而,这可以被服务器限制,所以我总是可以调整它以找到合适的平衡。
调整转弯长度
如果服务器观察到太多的暂停,它可以通过在轮次消息中包含新的轮次长度来增加轮次长度。所有客户在开始执行新回合时都将应用此规则。这将同等地增加所有玩家的命令延迟。
相反,如果服务器在一段时间内没有观察到任何暂停,它可以减少回合长度,从而为所有玩家提供更低的命令延迟。
非确定性事件
在传统的锁步网络中,每回合仅发出玩家命令,其余模拟预计将在客户端之间确定性地进行。然而,某些游戏逻辑很难保持确定性,尤其是在 Unity 中,游戏引擎不提供任何此类保证。
在这种情况下,我确保非确定性游戏逻辑仅执行一次,并将结果作为其自己的操作以及玩家请求的操作一起发布。
例如,假设玩家攻击了一枚炸弹,导致其爆炸。玩家发出所有客户端执行的攻击动作。在炸弹爆炸的地方,需要检查周围区域,看看哪些单位会被击中。这是使用不确定的 Unity API 完成的。因此,只有一个客户端(可能是拥有炸弹的客户端)将进行此模拟并将结果(应该受到伤害的单位列表)作为新操作发送到服务器。通过这种方式,每个客户端的模拟仍然是相同的
如果存在作弊问题,服务器可以代替客户端来计算这些操作。这将要求服务器运行与客户端相同的模拟。这是我正在努力的方向,尽管目前客户仍然做出一些不确定的决定。
这种方法的优点是,仅当某些特定的游戏逻辑预计是不确定的时才需要发送额外的数据。任何确定性的事情(例如村民继续收集直到他们的资源已满)都不会使用任何额外的网络资源。它也比重写 Unity 功能以使其具有确定性更容易实现,尤其是在物理方面。
缺点是,如果我错了,认为某些事情是确定性的,但事实并非如此,它仍然会导致游戏不同步。
相关文章:
RTS 客户端-服务器网络
Stone Monarch 从一开始就支持多人游戏,但随着时间的推移,网络模型经历了多次迭代。我最初基于这篇著名的帝国时代文章实现了点对点锁步模型。 点对点锁定步骤有一些众所周知的问题。点对点方面使玩家很难相互连接,并增加了每个新玩家的网络…...
python连接数据库的方式
python连接数据库的方式 pyzenith.connect()函数就是连接数据库; exception.ScriptException()这一句是自定义异常,可以不用我这个; finally里面还有一个try finally是有必要的,防止…...
【腾讯云云上实验室-向量数据库】探索腾讯云向量数据库:全方位管理与高效利用多维向量数据的引领者
目录 前言1 腾讯云向量数据库介绍2 向量数据库信息及设置2.1 向量数据库实例信息2.2 实例监控2.3 密钥管理2.4 安全组2.5 Embedding2.6 可视化界面 3 可视化界面4 Embedding4.1 embedding_coll精确查询4.2 unenabled_embedding_coll精确查询 5 数据库5.1 创建数据库5.2 插入数据…...
二、sql手工注入
一、SQL注入的本质 解释:想要进行sql注入,肯定要发现注入点,一般简单的sql注入通过下面两种方式判断就能发现是否存在sql注入漏洞 1.字符型 注意:字符型注入可能为或" 查询语句: select * from student where…...
day61 layui和分页原理
昨日内容回顾 choices参数的使用 一般用在什么场景:当被存储的字段数据可能被列举完毕的时候一般会使用choices参数 性别 学历 来源 工作经验等 一般情况下不在数据表中直接存储中文,存数字、存字母来做映射 # 怎么使用 gender_choices ((1, 男),(2…...
Rust开发——变量、静态变量与常量
1.变量 在 Rust 中,类型安全是通过静态类型系统来实现的。变量绑定默认情况下是不可变的(immutable)。 在 Rust 中声明一个变量时,默认情况下它是不可变的。例如: fn main() {let x :i32 5; // 这是一个…...
javascript Math相关计算取值属性方法
*向上取整【只要有小数就+1】 Math.ceil(3.14); // 4 *向下取整【有小数就舍弃】 Math.floor(3.14); // 3 parseInt(3.14); // 3 // 常用于字符串类型的数字转为十进制的数据 四舍五入【小数点后部分】 Math.round(11.5)); //12 Math.round(-11.5)); //-11 取两数…...
git reset hard,mixed,soft
首先,我们得了解git reset命令的形式之一: git reset [<mode>] [<commit>] 此命令的作用是恢复HEAD分支到<commit>位置,并根据<mode>决定是否恢复index file和working tree。恢复是指将staging area和working tree…...
Cookie与Session知识
目录 一.Cookie与Session的发展史 1.Cookie的发展史 2.Session的发展史 3.Cookie和Session的关系 4.总结 二.Cookie与Session详解 1.Cookie 2.Session 3.token 4.总结 三.Django操作Cookie 1.设置Cookie 2.获取Cookie 3.设置超时时间 4.注销Cookie 5.登录功能实…...
Vue批量全局处理undefined和null转为““ 空字符串
我们在处理后台返回的信息,有的时候返回的是undefined或者null,这种字符串容易引起用户的误解,所以需要我们把这些字符串处理一下。 如果每个页面都单独处理,那么页面会很冗余,并且后期如果有修改容易遗漏,…...
【2023年APMCM亚太杯C题】完整数据与解题思路
2023年亚太杯C题 数据下载与搜集重点数据其余数据第一问第二问第三问第四问第五问第六问 数据与思路获取 数据下载与搜集 该题并没有提供数据集,对所需数据进行收集整理是对题目进行求解的基础。在本题中,主要需要以下数据:新能源汽车历史销…...
嵌入式单片机方向和Linux驱动开发方向哪个发展前景好?
嵌入式单片机方向和Linux驱动开发方向哪个发展前景好? 在某些平台上看到很多人鼓吹嵌入式Linux开发比单片机开发要好,让所有人都去做嵌入式Linux开发。说这种话的人大多数是嵌入式Linux的培训机构,或者是一开始就以嵌入式Linux入门的那一批人…...
如何搭建Zblog网站并通过内网穿透将个人博客发布到公网
文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 想要成为一个合格的技术宅或程序员,自己搭建网站制作网页是绕…...
2:kotlin集合(Collections)
集合有助于数据分组,方便后续操作 集合类型说明Lists有序的可重复的集合Sets无序的不可重复的集合Maps键值对映射集合,键唯一,且一个键只能映射到一个值 每个集合类型都可以是可变的或者只读的 List List按照添加的顺序存储内容ÿ…...
小诺2.0开源版工程启动
小诺是一款开源的前后端开发框架,同若依、SpringBladex一样可作为私活、外包脚手架。 开源地址:Snowy: 最新:💖国内首个国密前后分离快速开发平台💖,采用Vue3AntDesignVue3 ViteSpringBootMpHuToolSaToke…...
idea手动导入maven包
当maven仓库中没有包时,我们需要手动导入jar到maven项目中 1.这里的maven设置成你自己安装的maven 2.查看pom.xml文件中maven,以下面为例 <dependency><groupId>com.jdd.pay</groupId><artifactId>mapi-sdk-v3</artifactId&…...
2、单片机及开发板介绍
单片机介绍 单片机,英文Micro Controller Unit,简称:MCU 内部集成:CPU、RAM(随机存储器)、ROM(只读存储器)、定时器、中断系统、通讯接口等 作用:信息采集(传感器)、处理࿰…...
Leetcode 第 372 场周赛题解
Leetcode 第 372 场周赛题解 Leetcode 第 372 场周赛题解题目1:2937. 使三个字符串相等思路代码复杂度分析 题目2:2938. 区分黑球与白球思路代码复杂度分析 题目3:2939. 最大异或乘积思路代码复杂度分析 题目4:2940. 找到 Alice 和…...
mysql查询统计最近12个月的数据
项目场景: mysql查询统计最近12个月的数据,按每个月纵向展示,效果图 sql语句 注意:count( v.uuid ) 这里的是被统计那张表的id SELECT m.month,count( v.uuid ) AS total FROM (SELECT DATE_FORMAT(( CURDATE()), %Y-%m ) AS mon…...
14.Python 模块
目录 1. 使用模块2. 使用包3. 常用模块3.1 日期和时间3.2 伪随机数3.3 摘要算法3.4 JSON 处理3.5 图像处理 模块是Python用来组织代码的一种方法,包是Python用来组织模块的一种方法。 常用基本语法如下: Windows 按住winR 输入 cmd,Mac 打开…...
赛力斯第一季营收257亿:计入政府补助6亿 扣非后净利1亿同比降74%
雷递网 雷建平 4月29日赛力斯集团股份有限公司(简称:“赛力斯”,证券代码:601127)今日发布2026年第一季度的财报。财报显示,赛力斯2026年第一季度营收为257.46亿元,较上年同期的191.47亿元增长3…...
别再折腾了!Windows 11下STM32开发环境一站式搭建指南(MDK5.38 + DAP/ST-Link + CH340)
Windows 11下零痛感STM32开发环境全栈配置手册 刚拿到STM32开发板的新手开发者,往往会在环境搭建阶段经历各种"玄学问题":MDK版本兼容性报错、仿真器驱动冲突、串口识别异常...这些看似简单的准备工作,实际可能消耗数天时间。本文将…...
ArcGIS Server 切片服务发布实战:从ArcMap预处理到JavaScript加载的完整避坑指南
ArcGIS Server切片服务发布实战:从预处理到前端加载的全链路避坑指南 当遥感影像数据需要从本地TIF文件转变为可被全球访问的Web地图服务时,ArcGIS Server的切片服务发布流程往往成为GIS工程师的必经之路。这个看似标准化的技术路径中,却隐藏…...
RimSort终极指南:如何轻松管理《环世界》模组,告别加载冲突烦恼
RimSort终极指南:如何轻松管理《环世界》模组,告别加载冲突烦恼 【免费下载链接】RimSort RimSort is an open source mod manager for the video game RimWorld. There is support for Linux, Mac, and Windows, built from the ground up to be a reli…...
royalrover
1. 哑铃图是什么? 哑铃图(Dumbbell Plot),有时也称为DNA图或杠铃图,是一种用于比较两个相关数据点的可视化图表。 它源于人们对更有效数据比较方式的持续探索。 在传统的时间序列比较中,我们通常使用两条折…...
别再死记硬背了!我用这10个Python高频面试题,帮你拆解背后的设计思想
10个Python高频面试题背后的设计哲学与工程智慧 1. GIL全局解释器锁的取舍之道 Python最受争议的设计莫过于GIL(全局解释器锁)。这个看似简单的机制背后,隐藏着语言设计者对单线程性能与多核利用的深刻权衡: 性能优先的设计哲学&a…...
流浪动物救助小程序(文档+源码)_kaic
第5章 系统实现进入到这个环节,也就可以及时检查出前面设计的需求是否可靠了。一个设计良好的方案在运用于系统实现中,是会帮助系统编制人员节省时间,并提升开发效率的。所以在系统的编程阶段,也就是系统实现阶段,对于…...
【仅限前500名】R 4.5专属微生物组分析包清单(含6个未公开CRAN镜像源+3个GitHub高星私有工具链)
更多请点击: https://intelliparadigm.com 第一章:R 4.5微生物组多组学分析环境构建与兼容性验证 在微生物组多组学研究中,R 4.5 版本提供了更稳健的 Bioconductor 3.19 生态支持,但需特别注意其与常用多组学包(如 p…...
保姆级教程:手把手教你用STM32F103自制一个带串口调试的STM32程序下载/复位控制板
从零打造STM32下载调试器:硬件设计到固件开发的完整实战指南 在嵌入式开发中,频繁地手动按压复位和BOOT按钮进行程序下载调试,不仅效率低下,还容易因操作失误导致芯片进入错误状态。本文将带你用STM32F103打造一款智能下载调试器&…...
从sysfs目录看Linux设备模型:一个‘ls /sys’命令背后的kobject、kset与ktype故事
从/sys目录透视Linux设备模型:kobject、kset与ktype的实战解析 当你第一次在Linux终端中输入ls /sys时,那些层次分明的目录结构可能看起来像普通的文件夹。但事实上,这个看似简单的文件系统背后隐藏着Linux设备模型的核心机制——kobject、ks…...
