我几乎完成了一款小型独立式多人游戏的开发。虽然我打算允许人们在单人游戏中作弊,但在多人游戏中这显然是不可接受的。有谁知道有什么方法可以阻止普通的Joe使用Cheat-Engine之类的东西来修改游戏的某些部分?我目前计划让客户端每隔几秒钟将游戏使用的每个设置文件的MD5哈希值(存储为XML)上载到游戏服务器进行验证,但是我能做些什么来阻止内存编辑器等操作。 ?
我几乎完成了一款小型独立式多人游戏的开发。虽然我打算允许人们在单人游戏中作弊,但在多人游戏中这显然是不可接受的。有谁知道有什么方法可以阻止普通的Joe使用Cheat-Engine之类的东西来修改游戏的某些部分?我目前计划让客户端每隔几秒钟将游戏使用的每个设置文件的MD5哈希值(存储为XML)上载到游戏服务器进行验证,但是我能做些什么来阻止内存编辑器等操作。 ?
Answers:
如果您担心本地修改的代码,那么如何确定有人没有简单地修改您的通知代码以发送与您期望的相同的MD5哈希的静态列表?实际上,您甚至不需要修改代码就能做到这一点,您只需要一个相当基本的代理即可(假设没有SSL,但即使付出更多的努力也可以伪造)。
做您想要的事情的唯一方法是拥有一台服务器,而根本不信任客户端。所有计算都应在服务器上进行,所有动作至少在某种程度上应得到验证。例如,当您知道他们刚才在另一侧时,请勿让客户说他们想移至地图的一侧。没有中间的服务器去做对游戏至关重要的一切,就不可能没有作弊,因为聪明的人会一直寻找一种方法解决您可以构建到客户端中的所有内容。即使只有其中一种,如果您不考虑通过所有不同的方式来轻微地操纵情况,仍然是可能的。例如,我之前提到的运动验证:如果进行简单的点对点计算,人们仍然可以穿越墙壁。
所以问题是,“平均乔”的平均水平是多少?您已经提到了代码编辑和内存编辑器。这高于我个人认为的平均水平,如果您确实要担心该水平,则需要一个单独的受信任的服务器来完成所有繁重的工作,并且客户端将最终沦为显示和输入设备。
客户在敌人的手中。(在线世界设计法则)
确实,唯一能击败大多数骗子的方法是让客户端成为“瘦客户端”,即:仅充当输入和输出设备,并且从不提供超出其实际需要的更多信息。
这不会停止自动化,也不会停止被动信息的收集和分析,但是您可以设计游戏,使它们不会产生重大影响。
这不会阻止人们通过服务器依赖的格式不正确的消息互相攻击-您的客户端需要防范服务器发出的消息,就像您保护网站免受SQL注入攻击一样。
由于您还希望在单人游戏中使用相同的客户端,因此一种解决方案是在单独的进程/线程中运行本地服务器,并将其在内部作为客户端/服务器设置。然后,“作弊”将在服务器端起作用。我的世界(Minecraft)做了类似的事情,但这绝不是第一个以这种方式从界面分离游戏引擎的游戏。
建议阅读:
在基本的多人游戏中,作弊引擎这种作弊根本行不通。客户端仅发送数据以及您设计的数据要执行的操作。通常,这并不比玩家“做”的要多,例如,他朝哪个方向奔跑,如果他在射击等。因此,他对内存所做的更改只会影响自己的游戏,而其他玩家将看不到任何变化,即所谓的不同步。大多数游戏会像这样检测不同步,并从游戏中删除不同步的玩家。
现在还有其他作弊方式,但这些都与游戏的设计方式有关。
为了防止伪造网络消息的最简单的黑客攻击,服务器应检查这些软件包的完整性。“那个家伙刚刚跳了5个屏幕?那不可能,拒绝包裹。” (请注意,您还可以走完全同步的路线-这意味着所有信息都已同步,并且仅“播放器按下了哪些按钮。”这类消息被发送-这使伪造的网络消息在设计上毫无用处。)
在多人游戏中作弊的最后一种方法是可见性破解,那就是修改客户端,以便向玩家显示他不知道的数据。这样的例子没有显示出战争的迷雾,无法看穿墙壁,显示小地图或其他东西。这些是不可能防止的。
为了防止基本的骗子引擎操纵变量值,那么您必须隐藏这些值。通常,作弊引擎通过搜索已知变量的已知值,玩更多游戏并使该值变为更改后,Cheat Engine将从先前搜索新值的结果中进行新搜索。这使作弊者可以放大该值的存储位置,现在他们可以使用作弊引擎更改该存储位置的值。
例如,我有245 GOLD ...使用作弊引擎,我搜索245并找到许多内存位置。然后,我再玩一些,将黄金提高到314,然后在先前的搜索输出中搜索值314,并轻松找到存储金的存储位置。
防止这种情况的方法是永远不要将实际值存储在存储位置中。例如,我将值存储在一个对象中,该对象必须在需要时按需计算实际值。假设玩家拥有245 GOLD。如果他们搜索值为245的内存位置,他们可能会发现很多,但没有一个将是实际存储黄金值的存储位置,这是因为您没有存储黄金的值245。当游戏需要知道多少黄金时,它将询问持有该黄金价值的对象,该对象将根据需要进行计算。
因此,现在的问题是:如何以一种不会泄露价值的方式准确地存储价值?这有点棘手和丑陋,我敢肯定有很多方法可以做到。我想做的是存储一个布尔数组(或字节数组)。数组的长度可以是任何长度,但是可以说是13。然后您有一个计数器,表示13变为该实际值的次数。因此,如果我们要表示245,则计数器的值将为18。现在,对于其余的245/13 ...基本上是模数,该数组会将所有布尔值都设置为true。在本例中为11,因此数组中的前11个布尔值将设置为true,其余设置为false。要检索该值,只需将计数器乘以数组长度,然后为每个设置为true的布尔值加1(在第一个false处停止)。现在,数字245永远不会存储在任何地方,并且很难找到需要操纵以更改金币数量的存储位置。创建该对象时,您可能希望将数组长度设置为不同的大小(可能会在某个合理范围内随机选择一个数字)。
编辑:这对于多人游戏和单人游戏很有用。在多人游戏中也有作弊行为,其中数据包中的值可能会更改。这将需要不同的技术来防止,例如对每个数据包进行签名。