上手 Minestom: 新时代 Minecraft 服务端实现
笔者观望 Minestom 已经有一段时间了. 以前他的线程模型还不够成熟, 曾经联系 @TheMode 想帮他翻译那块的文档结果鸽了.
正好, 今天摸鱼的时候看了一眼 Minestom 官方, 发觉现在或许是时候上手尝试一下了.
简中圈子里吹 Minestom 的很多, 然而真正上手 / 普及 Minestom 开发的很少. 官方文档已经足够详尽, 因此本篇博文只作引路贴, 希望对 Minestom 感兴趣的你自己上手尝试.
配置环境
Minestom 主要托管在 JitPack 上.
build.gradle:
1 | repositories { |
静静等待依赖图下载完成, 下载的过程中, 我们不难发现一些老熟人.
启动
Minestom 启动实现
很简单, 只需要两行就搞定了.
1 | public static void main(String[] args) { |
一个空的 Minestom 实现
在一秒钟内就能初始化完毕.
打开 Minecraft, 添加一个 localhost
到服务器列表中就能看到你的新 Minestom 服务器.
注意: Minestom 语境下的
实例
和实现
可能和你想象中的不一样
例如:实现/Implementation
是基于 Minestom 开发的服务端软件, 而实例/Instance
不只指对象.
你很快就会发现你卡在登入中. 先别急着去 Issues 找骂, 看看日志怎么说:
You need to specify a spawning instance in the PlayerLoginEvent
嗯… 有意思.
如果想加入我们刚刚创建的新鲜 Minestom 实现, 我们首先要设置玩家加入的 实例
. 那么, 实例是什么?
实例
Instances are what replace “worlds” from Minecraft vanilla, those are lightweight and should offer similar properties. There are multiple instances implementation, currently InstanceContainer and SharedInstance (both are explained below)
简单的说, 在 Minestom 的世界里, 实例
和我们先前在 Bukkit / Forge 上开发时的 世界
是相同的概念. 不同的是, 比起世界来说, 一个 实例
通常更加轻量一些.
那么怎么创建实例呢? 你可能会发现你刚刚得到的 MinecraftServer
对象除了能监听端口什么都不会干, 这是因为 Minestom 的大部分功能…
都被 MinecraftServer
的静态方法包装起来了. 我觉得这样做的意图可能是模拟其他 JVM 语言上 “object
“ 的做法, object
类型的 “类” 默认就是单例, 因此这种类的静态方法不复存, 所有的方法和字段实际上都直接指向那个单例.
虽然在 Java 的语境下这样的做法难免令人我感觉奇怪, 但是这毕竟不是重点.
我们拿到 InstanceManager
, 然后创建一个新的实例:
1 | var manager = MinecraftServer.getInstanceManager(); |
噢! 不要忘记设置默认的 世界生成器
, 不然你会一直掉下去虚空的.
1 | instanceContainer.setGenerator(unit -> |
然后要注册一个事件监听器, 用于告诉 Minestom 我们想让玩家出生在什么地方.
1 | MinecraftServer.getGlobalEventHandler().addListener(PlayerLoginEvent.class, evt ->{ |
但是! 虽然现在已经可以进入服务器了, 我们会出生在 (0,0,0)
, 然后无尽掉虚空.
所以, 还需要额外补几行防止这种情况.
1 | MinecraftServer.getGlobalEventHandler().addListener(PlayerSpawnEvent.class, evt ->{ |
启动服务器, 进去将会发现一大片草方块.
或者, 你也可以稍微更换一些参数…
Minestom 生成世界的速度很快(可能是懒加载而已), 你几乎感受不到平时在 Notchian 服上最常见的世界生成卡顿. (可能是因为空 Minestom 处理的数据比较少, Minestom 就算直接加载 Minecraft 地图速度也是远超 Notchian.)
虽然 Minestom 支持直接加载 Anvil 格式的存档, 但是官方文档没有提到要怎么做.
加载 Anvil 格式的地图
注: 以下使用的主要 API 被官方标记为不稳定
不难发现, createInstanceContainer
其实有支持传入 IChunkLoader
的重载方法. 只需要搜索片刻…
你就能找到 AnvilLoader
.
1 | var manager = MinecraftServer.getInstanceManager(); |
只需要这样, 就可以加载你的 Minecraft 地图了.
聊天与命令
Minestom 似乎内置一个简单的聊天功能实现 (连聊天格式都和原版一样), 处理信息的方法应该和在 Bukkit 上的相差不大, 只不过 Minestom 大量运用了 Kyori 的 Adventure API我有点反胃. 写代码时最好留个心眼在返回值上.
所以比较想提一嘴的是命令, 毕竟其他教程也有自古以来从命令入手写功能的习俗.
注册一个新的命令很简单:
1 | var commandNew = new Command("new"); |
接着你就可以在你的实现里用 /new
了, 正如你想象的那样运行. 这个命令框架看起来并不新奇, 笔者甚至觉得有些奇怪.
不过, 上面给的例子只是为了你三行快速上手, 官方推崇的写法是这样的:
1 | package demo.commands; |
之后一样的套路: MinecraftServer.getCommandManager().register(new TestCommand())
接着是, 有参数的情况:
1 | package demo.commands; |
在埋头苦读上方源码之前, 不如看看官方文档是怎么解释的.
All auto-completable commands should extend Command, each command is composed of zero or multiple syntaxes, and each syntax is composed of arguments
If you find it confusing, here are a few examples:
/health
一条指令/health set 50;
一条指令和他的语法set
一小段字面量型的参数~ ~ ~
一个坐标参数
一条命令由零个或多个 语法
构成, 每个 语法
又由一个或多个参数构成. 如果感到无法理解, 不如这样想:
- 所谓的
语法
就是命令的一个基本样子.
比如:/effect xxx give xxx
是一条语法, 而/effect xxx give xxx 30 24
因为后面多了两个参数就是一个新的语法. - 所以语法就规定了应该有哪些参数, 以及它们对应的类型.
更多内容, 请转向 官方文档 / Minestom Wiki
一些别的
虽然它还是高度实验性的服务端, 但是它很有潜力一举代替 Spigot 成为支撑 RPG, 小游戏服务器的主流服务端, 这也说明这真的不怪Java, 别再说什么 C++ 重写性能翻3000%了 因此, 现在开始学习如何使用是完全可取的, 因为本文所述的, 官方 Wiki 中包含的, 以及本身 API 架构不太可能再发生巨大变更.
Minestom 不仅提供了一个更加模块化的 Minecraft 服务端事件, 而且也兼顾了性能和 API 的良好设计, 开放程度远超 Spigot. 但是在使用 Minestom 开发你的实现之前, 你要花更多精力在维持好程序的良好架构上, 不然就会 go die.有人今天用minestom写东西写的一团糟我不说是谁 所以, 我觉得如果不是很必要, 可以使用 Minestom 的 扩展(插件) API 和 高度实验性的脚本 API.
这样做或许更加有利于 Minestom 的生态发展, 而最坏的情况就是大家都喜欢自己 hold 一个 Minestom, 谁也不服谁, 就好像那帮 Mod Loader 一样.
但是 Mod Loader 也各自多多少少有一些 Mod, 要是世界上光有 Mod Loader 没有 Mod 就真的成灾难了…
End
感谢你的观看, 欢迎在评论区留言.
上手 Minestom: 新时代 Minecraft 服务端实现
https://blog.0w0.ing/2022/12/19/Getting-Started-With-Minestom/