有人说, 从游戏行业诞生的那一刻, 也是游戏反作弊诞生的那一刻.
客户端给的数据不能轻易相信. 在开发插件时一定要注意, 如果你的插件要接收某个信息, 这个信息不是你自己指定的, 而是服主设置配置文件自己设定的或者是客户端发来的, 那你一定要仔细思考, 多加留意.
虽然不是服务器插件开发圈的事情, 但是这件事情为我们敲响了警钟.
2020年2月, MCBBS网站以及线下圈子内突然爆发一股针对Minecraft 1.12版本MOD漏洞的争议.
https://www.mcbbs.net/thread-968903-1-1.html
早前在GitHub上, 一个来自俄罗斯的账户发布了一个作弊MOD.
这款作弊MOD的作者研究了各大主流MOD的协议包漏洞, 发现这些知名的大MOD的协议包或多或少在接收到玩家客户端数据时, 对玩家客户端发来的数据的合理性判定极度缺失, 甚至是根本没有.
(不恰当的比方)玩家客户端说自己有一百万个钻石, 服务端真的就认为玩家客户端有一百万个钻石. 因此, 这款作弊MOD针对主流的大MOD的这些协议包问题, 有针对性的对每个MOD都开发了作弊方案.
作弊MOD事件过后, VexView的作者这样回应这一问题(以下节选有删改):
“没错, VexView很幸运, 同样存在这个BUG, 而且存在于 1.7.10-1.14 全版本. 但是不用担心, 我们当初在设计的时候就已经考虑到这个问题. 同时我们在开发文档中也明确指出要保持谨慎. 如果附属插件能够正常处理, 应该不会出现这些问题的. 当然不排除某些附属插件逻辑就不会存在此类刷物品的漏洞. 这完全取决于附属开发者.”
可见开发者对客户端的警惕意识有多么重要.
我们要编写的是服务端插件.
服务端插件根本上面向于这一服务器的玩家, 最直接的使用者是这一服务器的服主. 那么, 服主能不能正确配置配置文件? 玩家的数据信息真的正确吗?
也许你说, 你写的又不是反作弊插件, 不可能做到处处提防玩家. 诚然如此, 但是警惕意识绝对不能丢.
先虑忧患, 享于安乐.
如果你有经验你应该知道, 装上Resdience插件后, 创建一个领地, 关闭领地所有玩家的移动权限, 把一个玩家扔进去, 玩家在里面试图移动的话, 效果并不是完全动不了, 而是玩家仍然可以运动, 但是在一个间隔时间之后会被“弹回来”. 这是为什么?
为什么最终的效果不是"玩家一点都动不了"呢?
事实上, 我们无法在服务端取消玩家一点也不能移动. 客户端移动玩家时, 会在客户端显示出移动后的样子, 然后才会传递给服务器玩家移动的信号, 服务端收到客户端的信号后, 服务器才会做出响应.
也就是说, 客户端与服务端之间, 客户端往往都是"先斩后奏"的. 客户端不管你服务端想干什么, 先那么显示出来再说. 因为毕竟玩家在服务器里完全动不了不是MC原版的设定之一.
Minecraft在1.8版本的更新中隆重引入了UUID, 以便正版玩家修改自己的游戏ID且游戏内数据不变.
这意味着从此以后服务端区分玩家的判据不再是玩家的ID, 而是UUID.
正版服务器不必多说, 通过Mojang提供的API, 服务端可以得知一个进入服务器的玩家的UUID, 数据按照玩家的UUID存储即可.
但是离线服务器怎么办呢? 答案是根据玩家名计算.
//下面的代码出自OBC的CraftPlayerProfile类
if (isOnlineMode) {
profile = lookupName ? userCache.getProfile(name) : userCache.getProfileIfCached(name);
} else {
profile = new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name);
} 可见盗版玩家的UUID计算的是OfflinePlayer:玩家名对应的UUID.