教程:制作一个指令
专家级教程,需要Java知识:基础语法,面向对象,异常处理
版本:1.12.2 Forge
就像绝大多数MC的内容,写一个指令只需要两步:
1,写出它的类;
2,注册给Forge。
正如物品需要继承Item,指令需要继承CommandBase,具体一点的话是net.minecraft.command.CommandBase。
首先示范一下我理想境mod里的跨维度传送指令。
这里面每个是干什么的,我们后面再说。如果你复制后发现execute函数里有若干报错,那也不用急,因为那函数里面的内容只是示例,大可以都删了。
如果你想参考一下原版的其他指令,只要看ICommand接口的继承列表就行了。
先说注册指令的方式。由于我的这个指令是跨维度传送,是一个实际影响数据的操作,所以他要在服务端执行。我把它在FMLServerStartingEvent里注册,具体来说,就是在我的注册管理类里加入:
当然了,光这么写没有用,因为这段代码不监听事件。我需要在主类里加入
这样就注册完成了。
那么,为什么注册一个指令我给分在两个地方呢?因为我不希望主类的内容过多。随着指令种类的增多,要注册的指令的代码会一排排地变多,而我不希望全都堆到主类里,所以主类只调用一个接口,具体的指令列表我都放到注册管理类里。任何一个等效的写法都是可以的,不必像我这样拆分。这是规范上的问题,不是语法或逻辑上的问题。
有了上面的本体类,和注册它的代码,这个指令就能用了。

说回本体类的书写。继承CommandBase后,语法会要求你至少覆写三个东西:getName、getUsage、execute。
getName,就是这个指令的名字。比如time指令的time,gamerule指令的gamerule。
getUsage,就是这个指令在你打错了,或者打help的时候出现的那个用法。一般是告诉你这个指令有几个参数用的,具体的格式可以参考原版的指令。
execute,就是实际执行指令。这里面也要处理报错。
我们都听过那个关于程序酒吧的笑话(测试员进酒吧,买了一杯酒,然后不付钱,然后进去又出来,然后把老板打了一顿,满意地出来了。结果顾客进来点了一份炒饭,酒吧炸了。),这里正是体现它的地方。
Server参数给了你服务器引用,sender是指令发送者,args是参数列表(arguments)的意思。
sender这个参数要注意,它并不一定是玩家。我们都知道,红石可以导通命令方块触发指令。因此,如果你想把sender转化为玩家或者生物,务必在转化前用 instanceof 检查类型。如果你的指令不适用于方块,记得给玩家提示,以免玩家对着毫无反应的指令一脸懵逼,然后骂这mod有bug。
你要清楚地告诉玩家,这是玩家输入错误了,而不是mod出bug了。
args也要注意。比如你想制作一个升级指令,参数是等级的值,那么你需要把String转化为数字。一切参数都是字符串String,玩家输入 level 3 那也是作为字符串的"3",而不是int类型的3。要想获得int,那就得Integer.parseInt。然后,既然有parse了,你就要考虑玩家输入的不合规范的情况。比如一个“level <整数>”的指令,玩家可以输入“level asdasd”,这时候你就要告诉玩家,你输入错了,这个参数接受的是整数。我写的时候一般是和HarryTalks一样,直接给玩家发送消息,原版的方式更多是抛出异常,总之都是能用的。

上面的都搞定,一个合格的指令就诞生了。顺便讲几个其他需要注意的地方,从合格走向优秀。

getAliases。返回本指令的缩写列表。举例来说,创世神的schematic指令,可以缩写为schem。我做得更加过分,我的tpdim指令也接受“chuansong”,这个拼音写法。我见过太多英语不好的玩家朋友了,他们记不住这些英语的缩写的。它的示例用法可以参考上面我给出的例子。
getRequiredPermissionLevel 这个返回所需要的权限等级。如果你的指令效果很大,一般都不会允许玩家随意使用,尤其是在服务器上更要注意,不要让玩家因为滥用指令对服务器造成破坏。具体这个数字定为几合适,可以参考原版的各个指令的权限值。除了在代码里慢慢找之外,也可以翻阅wiki:
https://minecraft.fandom.com/zh/wiki/%E5%91%BD%E4%BB%A4#%E6%9D%83%E9%99%90%E7%AD%89%E7%BA%A7
我希望这个链接能活下来。如果有一天wiki都没了,也就别做什么mc了,去干点别的吧。