在我们继续讲述命令处理器前,我们要注意一件事情。哦,不用写代码,大家放松,放松~
这是有关插件安全性的问题。后门插件和漏洞插件屡见不鲜,虽然我相信各位都不会去编写后门插件,但漏洞毕竟有可能存在,本节大致说一说从哪几个方面增加插件安全。
客户端按道理是 Minecraft 的原版客户端,但作弊客户端也是有的,客户端发送来的信息不能全部相信,否则……我们举一个例子看看。
在原版的 Minecraft 服务器中,服务端不仅向玩家同步数据(下行),还会依据客户端数据处理该玩家的信息(上行)。
在著名的 WWE 作弊端中有一个功能:Timer。这个功能能够加快客户端的 TPS。还记得吗,我们说客户端和服务端的世界相对独立地运行。于是,客户端的时间非常快。可能每 1 分钟(1200 刻)太阳就会转一圈。
这对于下行数据没什么影响,但对于上行数据是致命的。
服务端将太阳的位置同步给客户端,因此客户端的太阳往上窜了之后就会回到原位(回弹),不会有很大的影响。
但客户端也会报告玩家的移动信息。WWE 向服务端发送了大量正常的数据包(其中的每个刻都对的上),于是服务端极快地更新玩家位置,玩家便实现了超高速移动。
在使用该作弊方法时会出现回弹的现象,是因为客户端发包速度实在太快,有些数据包服务端没来得及处理,因为服务端也要向客户端推送数据,就会造成玩家向后退的现象。但尽管如此,玩家的有效移动速度仍然很快。
解决这个问题的方法很多,一种方法就是在服务端内完成计算,玩家如果移动超出设定的速度就强行拽回来。
但总而言之,你对从客户端接收的数据不能不加验证就应用。
不要明文存储密码——这是插件的规则。即使服主对服务器有完整的控制权,也不应当将这些敏感信息泄露。
还有另一点——必须验证配置文件的完整性。服主能否正确配置文件?最简单的方法就是设置默认值。而不要粗暴地丢出一个错误信息。要知道,大多数服主的 Java 水平为零!
其它插件中或许含有潜在的后门或者漏洞,有的也许和你的功能有所冲突。
你可以通过各个插件的行为特征来鉴别服务端是否安装了冲突的插件并进行相应的调整。
插件内部的逻辑同样重要,请妥善地处理插件错误,而不要进行一些可能造成严重后果的尝试!
我们举刚刚的一个例子:配置文件的 get
和 getString
。同样的情况下,使用 getString
可以避免强制类型转换发生错误的可能。仅仅多加上这 6 个字母,就可以减少抛出错误的可能,那为什么不这样做呢?
如果错误发生了,那最好就在插件内部解决掉。
使用 try
和 catch
可以进行试错并捕获错误,用法是这样的:
try {
// 可能出错的语句
} catch (错误类型 e){
// 处理错误
} finally {
// 无论是否有错,都执行,finally 是可选的
}
一个 try
可以跟多个 catch
用来捕获不同类型的错误。
至于错误类型有哪些,你可以看看可能出错的方法的签名:
public void MightThrowError() throws IOException, SQLException
throws
关键字后面的错误都是有可能抛出的错误。Java 对于错误捕捉的要求很严格:可能发生的错误如果无法通过 try
和 catch
进行捕捉,就把它写在调用方法的签名中。换句话说:要么处理,要么报告,打不过就加入。系统错误(ClassCastException
、NullPointerException
等)可以不用指出。
但是,还是建议你将可能发生的错误都 catch
出来。
有时候即便我们用尽全力,也不能保证插件正常运行,因此我们要做好万全的准备。例如:为了防止数据丢失,要定期保存;对于小型的错误,应当能够进行自动纠错;发生错误时,努力将其记录进日志中等等。这些虽然不能阻止错误的发生,但能够在不幸发生后将损失减到最低,并且降低排查错误的难度。引用 DiskGenius 的 Slogan:数据无价,谨慎操作!
我们会尽可能做到公开、诚实和相信他人,希望您也是如此。
如果你拿着从我这里(或者别的地方)学来的知识去编写后门插件,那真是太糟糕了。不仅令开发社区失望,本小马也白白为你操碎了心……记住:钱有别的方法赚,出卖的诚实是回不来的!