Ginkgo06
本帖最后由 Ginkgo06 于 2018-12-24 16:59 编辑

复制代码
Minecraft基岩版 MOD的安装以及编写
我的博客有排版更加完善的版本 http://blog.haojie06.me/bedrock_mod/ 欢迎来访
转载请注明出处,有问题请留下评论,也希望大佬能够指出文章中的问题


文章有点长,如果感兴趣请耐心看完,我参考的教程是Mod Loader的作者MCMrARM编写的Wiki https://github.com/minecraft-lin ... embly-&-Hooking
https://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&-Hooking, 原版是英文版,我翻译了一下主要的步骤,总结并解释了一些要点,水平渣,不会cpp,仅作参考

下载了SDK后把SDK添加到环境变量中去
  1. nano /etc/profile
  2. #在最后一行添加
  3. export MODLOADER_SDK=$MODLOADER_SDK/home/bedrock/ModDev
  4. #之前在$MODLOADER_SDK后面加了引号可把自己坑惨了...注意
  5. #SDK(解压的文件夹)所在的路径
  6. #保存后执行
  7. source  /etc/profile
  8. echo $MODLOADER_SDK
  9. #此时便可以看见我们SDK的路径了
复制代码



<span id="3">

尝试编写Mod

这里我采用的开发方式是直接在服务器上创建mod文件,并利用VSCODE的ssh插件在电脑上编辑这个mod,此外g++等等环境需要配置好。 Mod Loader属于第三方的扩展,Mod SDK可以在Loader的下载页中获得,SDK提供了 C和C++的API 尽管只学过C,但这次还是采用C++吧... 基岩版官方服务端使用C++编写,Mod也相应的采用了C/C++来编写,浏览作者MCMrARM的Wiki,首先我们可以看到对Mod SDK中 Hooking API的介绍,看到Hook(钩子)这个词,我们可以了解到,Mod采取的机制是对诸如命中、移动、跳跃等等游戏中的事件进行Hook,当这些事件发生时,Mod会进行处理。

<span id="4">

Mod SDK 以C++ API为例

以SDK中提供的C++Api为例,SDK提供了C++的 作用域基础上的钩子(Hook),在这些钩子之上建立了基于宏的静态Hook API 自己翻译得好烂啊...看原文吧 The library provides a simple C++ scoped-based hook (modloader::AutoHook), and a macro-based static hook API is built upon it:

  1. //代码如下
  2. // #include <modloader/statichook.h>

  3. #define THook2(iname, ret, sym, args...) ...
  4. #define THook(ret, sym, args...) ...
  5. #define TClasslessInstanceHook2(iname, ret, sym, args...) ...
  6. #define TClasslessInstanceHook(ret, sym, args...) ...
  7. #define TInstanceHook2(iname, ret, sym, type, args...) ...
  8. #define TInstanceHook(ret, sym, type, args...) ...
  9. #define TStaticHook2(iname, ret, sym, type, args...) ...
  10. #define TStaticHook(ret, sym, type, args...) ...
复制代码

这里需要提到的一点就是C++中的 name mangling规则(命名粉碎规则),这个规则会给函数不同的签名,以明确具体调用的是哪一个函数,避免调用重载的函数时会出现二义性 具体一些的例子

可以看见这里定义了一些static Hook 的写法,宏中的钩子会hook签名为sym,返回类型为ret且参数为args的函数,而TInstanceHook 和 TStaticHook两个宏因为没有iname字段,所以必须要给出有效的类名

作者对不同宏作用的介绍以及我的理解:

  • You should use THook for hooking global functions, that are not part of any class. 当要hook的函数为全局函数而不是某一个类的一部分时使用THook 例如 printf()
  • You should use TInstanceHook for hooking member functions. Requires a full class declaration使用TInstanceHook hook成员函数(不是静态),需要完整地声明类 例如DedicatedServer::run()
  • Use TClasslessInstanceHook if you do not want to use a full class declaration如果不想使用完整的类的声明时使用
  • You should use TStaticHook for hooking static (class) functions. Requires a full class declaration想要hook 静态函数时使用,需要完整的类的声明 例如Common::getServerVersionString()
  • Use TClasslessInstanceHook if you do not want to use a full class declaration反之

对于full class declaration的解释 Requires a full class declaration means that to use it you need to declare the class somewhere and include it (class DedicatedServer { }; in an included header is OK, but a forward declaration like class DedicatedServer; is not enough).

意思是如果我要使用需要full class declaration的Hook时,需要在代码中包含这个类的声明(或者是包含包含了这个类的声明的头文件,如果只在代码中创建一个引用(不知道C++中怎么说的)是不够的)

如果在一个Mod中需要多次hook一个函数,需要使用后缀为2的宏并且传递不同的第一个参数(iname)

<span id="5">

HelloMinecraft MOD

创建HelloMinecraftMod 这里我们会用到ModLoader的Loger工具 Loger有以下几个等级的输出

  • verbose
  • debug
  • info
  • warn
  • error
    编辑 testmod.cpp

  1. #include填入头文件路径,如果不使用绝对路径需要在编译时指明
  2. #include </home/bedrock/ModDev/include/modloader/log.h>
  3. using namespace modloader;

  4. extern "C" void modloader_on_server_start(void* serverInstance) {
  5.     Log::verbose("BeginMod", "Hello Minecraft!");
  6.     Log::info("BeginMod","BeginServer");
  7.     Log::debug("BeginMod","CreateByhaojie")
  8. }
复制代码

编译生成.so mod文件 /home/bedrock/ModDev# g++ testmod.cpp -std=c++11 -I ${MODLOADER_SDK}/include/ -L ${MODLOADER_SDK}/lib/ -lserver_modloader -shared -fPIC -o BeginMod.so

注意

  • -L用于指明连接的库所在的文件夹
  • -l用于指明具体的库
  • -I指明#include的文件所在的文件夹

没有报错就说明成功了 ls 在当前文件夹出现了 BeginMod.so 文件,这个就是我们的MOD了,将它移动到服务端文件夹中的mods文件夹中并启动服务端 请使用 ./start_modloader 启动服务器 在启动的过程中我们便可以在命令行中看见输出了,第一个MOD成功(虽然还没有实际作用)

<span id="6">

创建一个具有实际作用的MOD (爆炸箭矢)

参考作者的wiki https://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&-Hooking 为了实现具体的功能我们需要将服务端解包,这里还需要掌握一定的汇编知识 我们这里使用到IDA解包 下载 IDA有一款插件HexRays CodeXplorer似乎可以把汇编语言转化为更好懂的形式?不过这次还没有使用。

开始制作

使用IDA->new->选择从官网下载的服务端压缩包解压后的 bedrock_server 文件 (注意这个文件没有扩展名,所以在选择是要将文件类型改为所有文件*)

然后便开始解包 耗时十来分钟



感觉这个IDA查找起来非常耗CPU啊,低压i5占用100%????... ps:卡顿是因为还在建立索引,当索引建立完毕之后就不卡了。

  • 找到需要Hook的函数
    我们要创建一个箭矢碰撞后爆炸的MOD,首先我们要找到箭矢碰撞的函数,::onHit (其实找到对应的函数是非常困难的一件事情,毕竟这里面的函数太多了。而且官方并没有给出说明...)
    你是不是以为我们的目标是 Throwable::onHit ?但经过对Arrow的继承树的查看我们可以发现(作者发现的),Throwable并没有出现过,实际上我们应该使用的是 ProjectileComponent::onHit
  • Hooking的函数名
    找到事件发生的函数之后怎么对其进行Hook呢?
    首先我们要获得这个函数的 mangled name(关于mangled name前面有介绍),在IDA中双击FunctionName在IDA VIEW里面展示具体的函数。


在这里我们可以看见一串以_ZN 开头的字符(_ZN是name mangling 的 symbol) eg:_ZN19ProjectileComponent5onHitERK9HitResult

我们Hook时使用的就是这个名字,在IDA里面无法查看函数的返回值以及函数是否是静态的,IDA可以对这个返回值进行猜测,不过据作者MCMrARM所说,猜测的一般会失败...注释中所写的也不是完全正确的。(难道只能自己猜了嘛??)

公布结果:ProjectileComponent::onHit的返回值为void

  • 编写MOD,检测击中
    看注释吧,这里还没有爆炸,只是当事件发生的时候在控制台输出

编写完成之后像之前一样进行编译

  1. g++ testmod.cpp -std=c++11 -I ${MODLOADER_SDK}/include/ -L ${MODLOADER_SDK}/lib/ -lserver_modloader -shared -fPIC -o explodeArrow.so
复制代码

满怀期待地打开服务器,捡起弓箭,shot,然后发现控制台并没有输出????....

问题解决了,真是个低级的错误,看上面的指令就能发现问题,我忘了修改源代码文件的名字了!!将 testmod.cpp 改为 explodeArrow.cpp 编译,成功之后将.so移动到../mods下,启动游戏,掏出弓箭,射击,控制台成功显示!

  • 编写MOD,处理击中之后的爆炸
    添加爆炸效果
    已经能检测到击中事件了,接下来我们要做的就是在击中的坐标处引发一次爆炸

首先我们要在IDA中找到负责爆炸的函数,搜索 ::explode,这一次我们使用的是Level(ModLoader作者在wiki中指明,说实话我也不知道怎么选择) Level::explode 现在我们面临的问题是如何获得level的指针以及爆炸的方块的源以及位置。

因为如前面所说 TInstanceHook hook成员函数(不是静态),需要完整地声明类 因此我们要像下面这样声明需要使用到的方法

  • 声明获得Actor指针的方法
    使用ProjectileComponent::getEntity() 我们可以获得一个额 Actor* 指针(实体的指针,根据wiki所说,之所以不是getActor是因为几个版本前的更新Mojang还没有彻底将Entity转变为Actor)

  1. //添加
  2. class Actor;
  3. class ProjectileComponent {
  4. public:
  5.   Actor& getEntity();
  6. };
复制代码

  • 声明获得Level and BlockSource指针的方法

    1. //添加
    2. class BlockSource;
    3. class Level;
    4. class Actor {
    5. public:
    6. BlockSource* getRegion() const;
    7. Level* getLevel();
    8. };
    复制代码

  • 最后我们要从HITRESULT中获得击中的坐标(即爆炸坐标),声明获得坐标的方法 HitResult::getPos() const 会返回一个 Vec3 const& 因此我们先声明 Vec3,再声明这个方法

    1. class Vec3;
    2. class HitResult {
    3. public:
    4. Vec3 const& getPos() const;
    5. };·
    复制代码

  • 声明爆炸的方法,各个参数的意思我们也不知道,只能不断地修改,然后在游戏中测试

    1. class Level {
    2. public:
    3. void explode(BlockSource&, Actor*, Vec3 const&, float, bool, bool, float, bool);
    4. };
    复制代码


最后 ,在TInstanceHook中添加,当弓箭击中物体时,这个hook便会调用其中的方法做出响应

  1. TInstanceHook(void, _ZN19ProjectileComponent5onHitERK9HitResult, ProjectileComponent, HitResult const& hitResult) {
  2.   Log::verbose(TAG, "ProjectileComponent::onHit");
  3.   original(this, hitResult);
  4.   Actor& entity = this->getEntity();
  5.   entity.getLevel()->explode(*entity.getRegion(), nullptr, hitResult.getPos(), 5.f, /* 是否产生燃烧 */ false, /*是否破坏方块 */ true, 1.f, true);
  6. }
复制代码

如果编译出错请查看作者的完整代码https://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&-Hooking

完成了这些,我们的爆炸箭矢MOD的源代码就完成了,最后编译之后将.so移动到../mods文件夹下,开启server 弓箭射击..BOOM!

<span id="7">

总结

一开始我以为官方服务器是没有MOD了的,没想到能在github上发现这个MODLoader,而且作者MCMrARM大大还热心的写了wiki教程,一步一步的写出了一个基岩版官方服务端的MOD,不过要说做BDS的MOD开发还是会面临一些很大的问题

  • 官方没有很好的API支持,写一个MOD还得解包
  • 解包后函数过多,很难确定究竟应该Hook哪一个函数
  • 资料非常的少

在这个框架下MOD的潜力有多大?之后基岩版服务器的MOD还会有什么发展,这都是值得我们拭目以待的。
转自我的博客 blog.haojie06.me
Minecraft基岩版 MOD的安装以及编写

转载请注明出处,有问题请留下评论,也希望大佬能够指出文章中的问题 文章有点长,如果感兴趣请耐心看完,我参考的教程是Mod Loader的作者MCMrARM编写的Wikihttps://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&-Hooking, 原版是英文版,我翻译了一下主要的步骤,总结并解释了一些要点,水平渣,不会cpp,仅作参考

目录




昨天安装好了微软推出的Minecraft基岩版官方服务端,这个服务端虽然非常的接近原版,但相对于其它的第三方版本,它少了一个很重要的功能,"MOD",没有了MOD也就少了许多趣味,当然,我怎么会甘心?

今天,在万能的Github上搜索Bedrock Dedicate Server的时候,搜索结果中出现的bedrock-modloader 吸引了我的眼球,不是说没有MOD嘛,这里怎么又有一个loader了呢?



<span id="1">

安装ModLoader

参看作者的wiki,安装这个loader很简单,已经有了现成的脚本。在服务器的目录中执行 wget http://mrarm.io/u/setup_server_modloader.sh 这个脚本没有指定脚本的解释器,于是我在脚本开始处添加#!/binbash,之后再使用chmod +x setup_server_modloader.sh 以及 ./setup_server_modloader.sh 便可以实现一键安装,之后再查看目录,便可以看见Mods文件夹以及 start_modloader.sh 启动脚本。

安装启动器是很简单,但是哪里有Mod呢?我找了挺久,反正是没有找到写好的Mod(当然和之前的Java以及PHP的Mode不兼容),只在作者的Wiki发现了Making Mods 的介绍,看来在服务端的初期,这些东西都得自己动手啊。



<span id="1">

安装MODSDK

查看wiki 首先需要前往作者的github下载SDK并解压。

在服务器上进行操作

  1. mkdir /home/moddev
  2. cd /home/moddev
  3. wget https://github.com/minecraft-linux/server-modloader/releases/download/v0.0.1-alpha1/mod_sdk.zip
  4. unzip mod_sdk.zip
  5. #创建一个mod
  6. test testmod.cpp
  7. #之后我采取的方式是在VSCODE上编辑(当然你也可以直接在服务器上使用Vim或者Nano)
复制代码

下载了SDK后把SDK添加到环境变量中去

  1. nano /etc/profile
  2. #在最后一行添加
  3. export MODLOADER_SDK=$MODLOADER_SDK/home/bedrock/ModDev
  4. #之前在$MODLOADER_SDK后面加了引号可把自己坑惨了...注意
  5. #SDK(解压的文件夹)所在的路径
  6. #保存后执行
  7. source /etc/profile
  8. echo $MODLOADER_SDK
  9. #此时便可以看见我们SDK的路径了
复制代码



<span id="3">

尝试编写Mod

这里我采用的开发方式是直接在服务器上创建mod文件,并利用VSCODE的ssh插件在电脑上编辑这个mod,此外g++等等环境需要配置好。 Mod Loader属于第三方的扩展,Mod SDK可以在Loader的下载页中获得,SDK提供了 C和C++的API 尽管只学过C,但这次还是采用C++吧... 基岩版官方服务端使用C++编写,Mod也相应的采用了C/C++来编写,浏览作者MCMrARM的Wiki,首先我们可以看到对Mod SDK中 Hooking API的介绍,看到Hook(钩子)这个词,我们可以了解到,Mod采取的机制是对诸如命中、移动、跳跃等等游戏中的事件进行Hook,当这些事件发生时,Mod会进行处理。

<span id="4">

Mod SDK 以C++ API为例

以SDK中提供的C++Api为例,SDK提供了C++的 作用域基础上的钩子(Hook),在这些钩子之上建立了基于宏的静态Hook API 自己翻译得好烂啊...看原文吧 The library provides a simple C++ scoped-based hook (modloader::AutoHook), and a macro-based static hook API is built upon it:

  1. //代码如下
  2. // #include <modloader/statichook.h>

  3. #define THook2(iname, ret, sym, args...) ...
  4. #define THook(ret, sym, args...) ...
  5. #define TClasslessInstanceHook2(iname, ret, sym, args...) ...
  6. #define TClasslessInstanceHook(ret, sym, args...) ...
  7. #define TInstanceHook2(iname, ret, sym, type, args...) ...
  8. #define TInstanceHook(ret, sym, type, args...) ...
  9. #define TStaticHook2(iname, ret, sym, type, args...) ...
  10. #define TStaticHook(ret, sym, type, args...) ...
复制代码

这里需要提到的一点就是C++中的 name mangling规则(命名粉碎规则),这个规则会给函数不同的签名,以明确具体调用的是哪一个函数,避免调用重载的函数时会出现二义性 具体一些的例子

可以看见这里定义了一些static Hook 的写法,宏中的钩子会hook签名为sym,返回类型为ret且参数为args的函数,而TInstanceHook 和 TStaticHook两个宏因为没有iname字段,所以必须要给出有效的类名

作者对不同宏作用的介绍以及我的理解:

  • You should use THook for hooking global functions, that are not part of any class. 当要hook的函数为全局函数而不是某一个类的一部分时使用THook 例如 printf()
  • You should use TInstanceHook for hooking member functions. Requires a full class declaration使用TInstanceHook hook成员函数(不是静态),需要完整地声明类 例如DedicatedServer::run()
  • Use TClasslessInstanceHook if you do not want to use a full class declaration如果不想使用完整的类的声明时使用
  • You should use TStaticHook for hooking static (class) functions. Requires a full class declaration想要hook 静态函数时使用,需要完整的类的声明 例如Common::getServerVersionString()
  • Use TClasslessInstanceHook if you do not want to use a full class declaration反之

对于full class declaration的解释 Requires a full class declaration means that to use it you need to declare the class somewhere and include it (class DedicatedServer { }; in an included header is OK, but a forward declaration like class DedicatedServer; is not enough).

意思是如果我要使用需要full class declaration的Hook时,需要在代码中包含这个类的声明(或者是包含包含了这个类的声明的头文件,如果只在代码中创建一个引用(不知道C++中怎么说的)是不够的)

如果在一个Mod中需要多次hook一个函数,需要使用后缀为2的宏并且传递不同的第一个参数(iname)

<span id="5">

HelloMinecraft MOD

创建HelloMinecraftMod 这里我们会用到ModLoader的Loger工具 Loger有以下几个等级的输出

  • verbose
  • debug
  • info
  • warn
  • error
    编辑 testmod.cpp

  1. #include填入头文件路径,如果不使用绝对路径需要在编译时指明
  2. #include </home/bedrock/ModDev/include/modloader/log.h>
  3. using namespace modloader;

  4. extern "C" void modloader_on_server_start(void* serverInstance) {
  5. Log::verbose("BeginMod", "Hello Minecraft!");
  6. Log::info("BeginMod","BeginServer");
  7. Log::debug("BeginMod","CreateByhaojie")
  8. }
复制代码

编译生成.so mod文件 /home/bedrock/ModDev# g++ testmod.cpp -std=c++11 -I ${MODLOADER_SDK}/include/ -L ${MODLOADER_SDK}/lib/ -lserver_modloader -shared -fPIC -o BeginMod.so

注意

  • -L用于指明连接的库所在的文件夹
  • -l用于指明具体的库
  • -I指明#include的文件所在的文件夹

没有报错就说明成功了 ls 在当前文件夹出现了 BeginMod.so 文件,这个就是我们的MOD了,将它移动到服务端文件夹中的mods文件夹中并启动服务端 请使用 ./start_modloader 启动服务器 在启动的过程中我们便可以在命令行中看见输出了,第一个MOD成功(虽然还没有实际作用)

<span id="6">

创建一个具有实际作用的MOD (爆炸箭矢)

参考作者的wiki https://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&-Hooking 为了实现具体的功能我们需要将服务端解包,这里还需要掌握一定的汇编知识 我们这里使用到IDA解包 下载 IDA有一款插件HexRays CodeXplorer似乎可以把汇编语言转化为更好懂的形式?不过这次还没有使用。

开始制作

使用IDA->new->选择从官网下载的服务端压缩包解压后的 bedrock_server 文件 (注意这个文件没有扩展名,所以在选择是要将文件类型改为所有文件*)

然后便开始解包 耗时十来分钟



感觉这个IDA查找起来非常耗CPU啊,低压i5占用100%????... ps:卡顿是因为还在建立索引,当索引建立完毕之后就不卡了。

  • 找到需要Hook的函数
    我们要创建一个箭矢碰撞后爆炸的MOD,首先我们要找到箭矢碰撞的函数,::onHit (其实找到对应的函数是非常困难的一件事情,毕竟这里面的函数太多了。而且官方并没有给出说明...)
    你是不是以为我们的目标是 Throwable::onHit ?但经过对Arrow的继承树的查看我们可以发现(作者发现的),Throwable并没有出现过,实际上我们应该使用的是 ProjectileComponent::onHit
  • Hooking的函数名
    找到事件发生的函数之后怎么对其进行Hook呢?
    首先我们要获得这个函数的 mangled name(关于mangled name前面有介绍),在IDA中双击FunctionName在IDA VIEW里面展示具体的函数。


在这里我们可以看见一串以_ZN 开头的字符(_ZN是name mangling 的 symbol) eg:_ZN19ProjectileComponent5onHitERK9HitResult

我们Hook时使用的就是这个名字,在IDA里面无法查看函数的返回值以及函数是否是静态的,IDA可以对这个返回值进行猜测,不过据作者MCMrARM所说,猜测的一般会失败...注释中所写的也不是完全正确的。(难道只能自己猜了嘛??)

公布结果:ProjectileComponent::onHit的返回值为void

  • 编写MOD,检测击中
    看注释吧,这里还没有爆炸,只是当事件发生的时候在控制台输出

编写完成之后像之前一样进行编译

  1. g++ testmod.cpp -std=c++11 -I ${MODLOADER_SDK}/include/ -L ${MODLOADER_SDK}/lib/ -lserver_modloader -shared -fPIC -o explodeArrow.so
复制代码

满怀期待地打开服务器,捡起弓箭,shot,然后发现控制台并没有输出????....

问题解决了,真是个低级的错误,看上面的指令就能发现问题,我忘了修改源代码文件的名字了!!将 testmod.cpp 改为 explodeArrow.cpp 编译,成功之后将.so移动到../mods下,启动游戏,掏出弓箭,射击,控制台成功显示!

  • 编写MOD,处理击中之后的爆炸
    添加爆炸效果
    已经能检测到击中事件了,接下来我们要做的就是在击中的坐标处引发一次爆炸

首先我们要在IDA中找到负责爆炸的函数,搜索 ::explode,这一次我们使用的是Level(ModLoader作者在wiki中指明,说实话我也不知道怎么选择) Level::explode 现在我们面临的问题是如何获得level的指针以及爆炸的方块的源以及位置。

因为如前面所说 TInstanceHook hook成员函数(不是静态),需要完整地声明类 因此我们要像下面这样声明需要使用到的方法

  • 声明获得Actor指针的方法
    使用ProjectileComponent::getEntity() 我们可以获得一个额 Actor* 指针(实体的指针,根据wiki所说,之所以不是getActor是因为几个版本前的更新Mojang还没有彻底将Entity转变为Actor)

  1. //添加
  2. class Actor;
  3. class ProjectileComponent {
  4. public:
  5. Actor& getEntity();
  6. };
复制代码

  • 声明获得Level and BlockSource指针的方法

    1. //添加
    2. class BlockSource;
    3. class Level;
    4. class Actor {
    5. public:
    6. BlockSource* getRegion() const;
    7. Level* getLevel();
    8. };
    复制代码

  • 最后我们要从HITRESULT中获得击中的坐标(即爆炸坐标),声明获得坐标的方法 HitResult::getPos() const 会返回一个 Vec3 const& 因此我们先声明 Vec3,再声明这个方法

    1. class Vec3;
    2. class HitResult {
    3. public:
    4. Vec3 const& getPos() const;
    5. };·
    复制代码

  • 声明爆炸的方法,各个参数的意思我们也不知道,只能不断地修改,然后在游戏中测试

    1. class Level {
    2. public:
    3. void explode(BlockSource&, Actor*, Vec3 const&, float, bool, bool, float, bool);
    4. };
    复制代码


最后 ,在TInstanceHook中添加,当弓箭击中物体时,这个hook便会调用其中的方法做出响应

  1. TInstanceHook(void, _ZN19ProjectileComponent5onHitERK9HitResult, ProjectileComponent, HitResult const& hitResult) {
  2. Log::verbose(TAG, "ProjectileComponent::onHit");
  3. original(this, hitResult);
  4. Actor& entity = this->getEntity();
  5. entity.getLevel()->explode(*entity.getRegion(), nullptr, hitResult.getPos(), 5.f, /* 是否产生燃烧 */ false, /*是否破坏方块 */ true, 1.f, true);
  6. }
复制代码

如果编译出错请查看作者的完整代码https://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&-Hooking

完成了这些,我们的爆炸箭矢MOD的源代码就完成了,最后编译之后将.so移动到../mods文件夹下,开启server 弓箭射击..BOOM!

<span id="7">

总结

一开始我以为官方服务器是没有MOD了的,没想到能在github上发现这个MODLoader,而且作者MCMrARM大大还热心的写了wiki教程,一步一步的写出了一个基岩版官方服务端的MOD,不过要说做BDS的MOD开发还是会面临一些很大的问题

  • 官方没有很好的API支持,写一个MOD还得解包
  • 解包后函数过多,很难确定究竟应该Hook哪一个函数
  • 资料非常的少

在这个框架下MOD的潜力有多大?之后基岩版服务器的MOD还会有什么发展,这都是值得我们拭目以待的。
[/code]



2021.12 数据,可能有更多内容 本帖最后由 Ginkgo06 于 2018-12-24 16:59 编辑

代码:


Minecraft基岩版 MOD的安装以及编写
我的博客有排版更加完善的版本 http://blog.haojie06.me/bedrock_mod/ 欢迎来访
转载请注明出处,有问题请留下评论,也希望大佬能够指出文章中的问题


文章有点长,如果感兴趣请耐心看完,我参考的教程是Mod Loader的作者MCMrARM编写的Wiki https://github.com/minecraft-lin ... y-&amp;-Hooking
https://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&amp;-Hooking, 原版是英文版,我翻译了一下主要的步骤,总结并解释了一些要点,水平渣,不会cpp,仅作参考


下载了SDK后把SDK添加到环境变量中去

代码:

  1. nano /etc/profile
  2. #在最后一行添加
  3. export MODLOADER_SDK=$MODLOADER_SDK/home/bedrock/ModDev
  4. #之前在$MODLOADER_SDK后面加了引号可把自己坑惨了...注意
  5. #SDK(解压的文件夹)所在的路径
  6. #保存后执行
  7. source/etc/profile
  8. echo $MODLOADER_SDK
  9. #此时便可以看见我们SDK的路径了



&lt;span id=&quot;3&quot;&gt;


尝试编写Mod


这里我采用的开发方式是直接在服务器上创建mod文件,并利用VSCODE的ssh插件在电脑上编辑这个mod,此外g++等等环境需要配置好。 Mod Loader属于第三方的扩展,Mod SDK可以在Loader的下载页中获得,SDK提供了 C和C++的API 尽管只学过C,但这次还是采用C++吧... 基岩版官方服务端使用C++编写,Mod也相应的采用了C/C++来编写,浏览作者MCMrARM的Wiki,首先我们可以看到对Mod SDK中 Hooking API的介绍,看到Hook(钩子)这个词,我们可以了解到,Mod采取的机制是对诸如命中、移动、跳跃等等游戏中的事件进行Hook,当这些事件发生时,Mod会进行处理。


&lt;span id=&quot;4&quot;&gt;


Mod SDK 以C++ API为例


以SDK中提供的C++Api为例,SDK提供了C++的 作用域基础上的钩子(Hook),在这些钩子之上建立了基于宏的静态Hook API 自己翻译得好烂啊...看原文吧 The library provides a simple C++ scoped-based hook (modloader::AutoHook), and a macro-based static hook API is built upon it:


代码:

  1. //代码如下
  2. // #include <modloader/statichook.h>

  3. #define THook2(iname, ret, sym, args...) ...
  4. #define THook(ret, sym, args...) ...
  5. #define TClasslessInstanceHook2(iname, ret, sym, args...) ...
  6. #define TClasslessInstanceHook(ret, sym, args...) ...
  7. #define TInstanceHook2(iname, ret, sym, type, args...) ...
  8. #define TInstanceHook(ret, sym, type, args...) ...
  9. #define TStaticHook2(iname, ret, sym, type, args...) ...
  10. #define TStaticHook(ret, sym, type, args...) ...

这里需要提到的一点就是C++中的 name mangling规则(命名粉碎规则),这个规则会给函数不同的签名,以明确具体调用的是哪一个函数,避免调用重载的函数时会出现二义性 具体一些的例子


可以看见这里定义了一些static Hook 的写法,宏中的钩子会hook签名为sym,返回类型为ret且参数为args的函数,而TInstanceHook 和 TStaticHook两个宏因为没有iname字段,所以必须要给出有效的类名


作者对不同宏作用的介绍以及我的理解:


  • You should use THook for hooking global functions, that are not part of any class. 当要hook的函数为全局函数而不是某一个类的一部分时使用THook 例如 printf()
  • You should use TInstanceHook for hooking member functions. Requires a full class declaration使用TInstanceHook hook成员函数(不是静态),需要完整地声明类 例如DedicatedServer::run()
  • Use TClasslessInstanceHook if you do not want to use a full class declaration如果不想使用完整的类的声明时使用
  • You should use TStaticHook for hooking static (class) functions. Requires a full class declaration想要hook 静态函数时使用,需要完整的类的声明 例如Common::getServerVersionString()
  • Use TClasslessInstanceHook if you do not want to use a full class declaration反之

对于full class declaration的解释 Requires a full class declaration means that to use it you need to declare the class somewhere and include it (class DedicatedServer { }; in an included header is OK, but a forward declaration like class DedicatedServer; is not enough).


意思是如果我要使用需要full class declaration的Hook时,需要在代码中包含这个类的声明(或者是包含包含了这个类的声明的头文件,如果只在代码中创建一个引用(不知道C++中怎么说的)是不够的)


如果在一个Mod中需要多次hook一个函数,需要使用后缀为2的宏并且传递不同的第一个参数(iname)


&lt;span id=&quot;5&quot;&gt;


HelloMinecraft MOD


创建HelloMinecraftMod 这里我们会用到ModLoader的Loger工具 Loger有以下几个等级的输出


  • verbose
  • debug
  • info
  • warn
  • error
    编辑 testmod.cpp

代码:

  1. #include填入头文件路径,如果不使用绝对路径需要在编译时指明
  2. #include </home/bedrock/ModDev/include/modloader/log.h>
  3. using namespace modloader;

  4. extern "C" void modloader_on_server_start(void* serverInstance) {
  5.     Log::verbose("BeginMod", "Hello Minecraft!");
  6.     Log::info("BeginMod","BeginServer");
  7.     Log::debug("BeginMod","CreateByhaojie")
  8. }

编译生成.so mod文件 /home/bedrock/ModDev# g++ testmod.cpp -std=c++11 -I ${MODLOADER_SDK}/include/ -L ${MODLOADER_SDK}/lib/ -lserver_modloader -shared -fPIC -o BeginMod.so


注意


  • -L用于指明连接的库所在的文件夹
  • -l用于指明具体的库
  • -I指明#include的文件所在的文件夹

没有报错就说明成功了 ls 在当前文件夹出现了 BeginMod.so 文件,这个就是我们的MOD了,将它移动到服务端文件夹中的mods文件夹中并启动服务端 请使用 ./start_modloader 启动服务器 在启动的过程中我们便可以在命令行中看见输出了,第一个MOD成功(虽然还没有实际作用)



&lt;span id=&quot;6&quot;&gt;


创建一个具有实际作用的MOD (爆炸箭矢)


参考作者的wiki https://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&amp;-Hooking 为了实现具体的功能我们需要将服务端解包,这里还需要掌握一定的汇编知识 我们这里使用到IDA解包 下载 IDA有一款插件HexRays CodeXplorer似乎可以把汇编语言转化为更好懂的形式?不过这次还没有使用。


开始制作


使用IDA-&gt;new-&gt;选择从官网下载的服务端压缩包解压后的 bedrock_server 文件 (注意这个文件没有扩展名,所以在选择是要将文件类型改为所有文件*)



然后便开始解包 耗时十来分钟





感觉这个IDA查找起来非常耗CPU啊,低压i5占用100%????... ps:卡顿是因为还在建立索引,当索引建立完毕之后就不卡了。


  • 找到需要Hook的函数
    我们要创建一个箭矢碰撞后爆炸的MOD,首先我们要找到箭矢碰撞的函数,::onHit (其实找到对应的函数是非常困难的一件事情,毕竟这里面的函数太多了。而且官方并没有给出说明...)
    你是不是以为我们的目标是 Throwable::onHit ?但经过对Arrow的继承树的查看我们可以发现(作者发现的),Throwable并没有出现过,实际上我们应该使用的是 ProjectileComponent::onHit
  • Hooking的函数名
    找到事件发生的函数之后怎么对其进行Hook呢?
    首先我们要获得这个函数的 mangled name(关于mangled name前面有介绍),在IDA中双击FunctionName在IDA VIEW里面展示具体的函数。


在这里我们可以看见一串以_ZN 开头的字符(_ZN是name mangling 的 symbol) eg:_ZN19ProjectileComponent5onHitERK9HitResult


我们Hook时使用的就是这个名字,在IDA里面无法查看函数的返回值以及函数是否是静态的,IDA可以对这个返回值进行猜测,不过据作者MCMrARM所说,猜测的一般会失败...注释中所写的也不是完全正确的。(难道只能自己猜了嘛??)


公布结果:ProjectileComponent::onHit的返回值为void


  • 编写MOD,检测击中
    看注释吧,这里还没有爆炸,只是当事件发生的时候在控制台输出

编写完成之后像之前一样进行编译



代码:

  1. g++ testmod.cpp -std=c++11 -I ${MODLOADER_SDK}/include/ -L ${MODLOADER_SDK}/lib/ -lserver_modloader -shared -fPIC -o explodeArrow.so

满怀期待地打开服务器,捡起弓箭,shot,然后发现控制台并没有输出????....


问题解决了,真是个低级的错误,看上面的指令就能发现问题,我忘了修改源代码文件的名字了!!将 testmod.cpp 改为 explodeArrow.cpp 编译,成功之后将.so移动到../mods下,启动游戏,掏出弓箭,射击,控制台成功显示!



  • 编写MOD,处理击中之后的爆炸
    添加爆炸效果
    已经能检测到击中事件了,接下来我们要做的就是在击中的坐标处引发一次爆炸

首先我们要在IDA中找到负责爆炸的函数,搜索 ::explode,这一次我们使用的是Level(ModLoader作者在wiki中指明,说实话我也不知道怎么选择) Level::explode 现在我们面临的问题是如何获得level的指针以及爆炸的方块的源以及位置。


因为如前面所说 TInstanceHook hook成员函数(不是静态),需要完整地声明类 因此我们要像下面这样声明需要使用到的方法


  • 声明获得Actor指针的方法
    使用ProjectileComponent::getEntity() 我们可以获得一个额 Actor* 指针(实体的指针,根据wiki所说,之所以不是getActor是因为几个版本前的更新Mojang还没有彻底将Entity转变为Actor)

代码:

  1. //添加
  2. class Actor;
  3. class ProjectileComponent {
  4. public:
  5. Actor& getEntity();
  6. };

  • 声明获得Level and BlockSource指针的方法


    代码:

    1. //添加
    2. class BlockSource;
    3. class Level;
    4. class Actor {
    5. public:
    6. BlockSource* getRegion() const;
    7. Level* getLevel();
    8. };

  • 最后我们要从HITRESULT中获得击中的坐标(即爆炸坐标),声明获得坐标的方法 HitResult::getPos() const 会返回一个 Vec3 const&amp; 因此我们先声明 Vec3,再声明这个方法


    代码:

    1. class Vec3;
    2. class HitResult {
    3. public:
    4. Vec3 const& getPos() const;
    5. };·

  • 声明爆炸的方法,各个参数的意思我们也不知道,只能不断地修改,然后在游戏中测试


    代码:

    1. class Level {
    2. public:
    3. void explode(BlockSource&, Actor*, Vec3 const&, float, bool, bool, float, bool);
    4. };


最后 ,在TInstanceHook中添加,当弓箭击中物体时,这个hook便会调用其中的方法做出响应


代码:

  1. TInstanceHook(void, _ZN19ProjectileComponent5onHitERK9HitResult, ProjectileComponent, HitResult const& hitResult) {
  2. Log::verbose(TAG, "ProjectileComponent::onHit");
  3. original(this, hitResult);
  4. Actor& entity = this->getEntity();
  5. entity.getLevel()->explode(*entity.getRegion(), nullptr, hitResult.getPos(), 5.f, /* 是否产生燃烧 */ false, /*是否破坏方块 */ true, 1.f, true);
  6. }

如果编译出错请查看作者的完整代码https://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&amp;-Hooking


完成了这些,我们的爆炸箭矢MOD的源代码就完成了,最后编译之后将.so移动到../mods文件夹下,开启server 弓箭射击..BOOM!


&lt;span id=&quot;7&quot;&gt;


总结


一开始我以为官方服务器是没有MOD了的,没想到能在github上发现这个MODLoader,而且作者MCMrARM大大还热心的写了wiki教程,一步一步的写出了一个基岩版官方服务端的MOD,不过要说做BDS的MOD开发还是会面临一些很大的问题


  • 官方没有很好的API支持,写一个MOD还得解包
  • 解包后函数过多,很难确定究竟应该Hook哪一个函数
  • 资料非常的少

在这个框架下MOD的潜力有多大?之后基岩版服务器的MOD还会有什么发展,这都是值得我们拭目以待的。
转自我的博客 blog.haojie06.me
Minecraft基岩版 MOD的安装以及编写


转载请注明出处,有问题请留下评论,也希望大佬能够指出文章中的问题 文章有点长,如果感兴趣请耐心看完,我参考的教程是Mod Loader的作者MCMrARM编写的Wikihttps://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&amp;-Hooking, 原版是英文版,我翻译了一下主要的步骤,总结并解释了一些要点,水平渣,不会cpp,仅作参考


目录





昨天安装好了微软推出的Minecraft基岩版官方服务端,这个服务端虽然非常的接近原版,但相对于其它的第三方版本,它少了一个很重要的功能,&quot;MOD&quot;,没有了MOD也就少了许多趣味,当然,我怎么会甘心?


今天,在万能的Github上搜索Bedrock Dedicate Server的时候,搜索结果中出现的bedrock-modloader 吸引了我的眼球,不是说没有MOD嘛,这里怎么又有一个loader了呢?




&lt;span id=&quot;1&quot;&gt;


安装ModLoader


参看作者的wiki,安装这个loader很简单,已经有了现成的脚本。在服务器的目录中执行 wget http://mrarm.io/u/setup_server_modloader.sh 这个脚本没有指定脚本的解释器,于是我在脚本开始处添加#!/binbash,之后再使用chmod +x setup_server_modloader.sh 以及 ./setup_server_modloader.sh 便可以实现一键安装,之后再查看目录,便可以看见Mods文件夹以及 start_modloader.sh 启动脚本。


安装启动器是很简单,但是哪里有Mod呢?我找了挺久,反正是没有找到写好的Mod(当然和之前的Java以及PHP的Mode不兼容),只在作者的Wiki发现了Making Mods 的介绍,看来在服务端的初期,这些东西都得自己动手啊。




&lt;span id=&quot;1&quot;&gt;


安装MODSDK


查看wiki 首先需要前往作者的github下载SDK并解压。


在服务器上进行操作


代码:

  1. mkdir /home/moddev
  2. cd /home/moddev
  3. wget https://github.com/minecraft-linux/server-modloader/releases/download/v0.0.1-alpha1/mod_sdk.zip
  4. unzip mod_sdk.zip
  5. #创建一个mod
  6. test testmod.cpp
  7. #之后我采取的方式是在VSCODE上编辑(当然你也可以直接在服务器上使用Vim或者Nano)

下载了SDK后把SDK添加到环境变量中去


代码:

  1. nano /etc/profile
  2. #在最后一行添加
  3. export MODLOADER_SDK=$MODLOADER_SDK/home/bedrock/ModDev
  4. #之前在$MODLOADER_SDK后面加了引号可把自己坑惨了...注意
  5. #SDK(解压的文件夹)所在的路径
  6. #保存后执行
  7. source /etc/profile
  8. echo $MODLOADER_SDK
  9. #此时便可以看见我们SDK的路径了



&lt;span id=&quot;3&quot;&gt;


尝试编写Mod


这里我采用的开发方式是直接在服务器上创建mod文件,并利用VSCODE的ssh插件在电脑上编辑这个mod,此外g++等等环境需要配置好。 Mod Loader属于第三方的扩展,Mod SDK可以在Loader的下载页中获得,SDK提供了 C和C++的API 尽管只学过C,但这次还是采用C++吧... 基岩版官方服务端使用C++编写,Mod也相应的采用了C/C++来编写,浏览作者MCMrARM的Wiki,首先我们可以看到对Mod SDK中 Hooking API的介绍,看到Hook(钩子)这个词,我们可以了解到,Mod采取的机制是对诸如命中、移动、跳跃等等游戏中的事件进行Hook,当这些事件发生时,Mod会进行处理。


&lt;span id=&quot;4&quot;&gt;


Mod SDK 以C++ API为例


以SDK中提供的C++Api为例,SDK提供了C++的 作用域基础上的钩子(Hook),在这些钩子之上建立了基于宏的静态Hook API 自己翻译得好烂啊...看原文吧 The library provides a simple C++ scoped-based hook (modloader::AutoHook), and a macro-based static hook API is built upon it:


代码:

  1. //代码如下
  2. // #include <modloader/statichook.h>

  3. #define THook2(iname, ret, sym, args...) ...
  4. #define THook(ret, sym, args...) ...
  5. #define TClasslessInstanceHook2(iname, ret, sym, args...) ...
  6. #define TClasslessInstanceHook(ret, sym, args...) ...
  7. #define TInstanceHook2(iname, ret, sym, type, args...) ...
  8. #define TInstanceHook(ret, sym, type, args...) ...
  9. #define TStaticHook2(iname, ret, sym, type, args...) ...
  10. #define TStaticHook(ret, sym, type, args...) ...

这里需要提到的一点就是C++中的 name mangling规则(命名粉碎规则),这个规则会给函数不同的签名,以明确具体调用的是哪一个函数,避免调用重载的函数时会出现二义性 具体一些的例子


可以看见这里定义了一些static Hook 的写法,宏中的钩子会hook签名为sym,返回类型为ret且参数为args的函数,而TInstanceHook 和 TStaticHook两个宏因为没有iname字段,所以必须要给出有效的类名


作者对不同宏作用的介绍以及我的理解:


  • You should use THook for hooking global functions, that are not part of any class. 当要hook的函数为全局函数而不是某一个类的一部分时使用THook 例如 printf()
  • You should use TInstanceHook for hooking member functions. Requires a full class declaration使用TInstanceHook hook成员函数(不是静态),需要完整地声明类 例如DedicatedServer::run()
  • Use TClasslessInstanceHook if you do not want to use a full class declaration如果不想使用完整的类的声明时使用
  • You should use TStaticHook for hooking static (class) functions. Requires a full class declaration想要hook 静态函数时使用,需要完整的类的声明 例如Common::getServerVersionString()
  • Use TClasslessInstanceHook if you do not want to use a full class declaration反之

对于full class declaration的解释 Requires a full class declaration means that to use it you need to declare the class somewhere and include it (class DedicatedServer { }; in an included header is OK, but a forward declaration like class DedicatedServer; is not enough).


意思是如果我要使用需要full class declaration的Hook时,需要在代码中包含这个类的声明(或者是包含包含了这个类的声明的头文件,如果只在代码中创建一个引用(不知道C++中怎么说的)是不够的)


如果在一个Mod中需要多次hook一个函数,需要使用后缀为2的宏并且传递不同的第一个参数(iname)


&lt;span id=&quot;5&quot;&gt;


HelloMinecraft MOD


创建HelloMinecraftMod 这里我们会用到ModLoader的Loger工具 Loger有以下几个等级的输出


  • verbose
  • debug
  • info
  • warn
  • error
    编辑 testmod.cpp

代码:

  1. #include填入头文件路径,如果不使用绝对路径需要在编译时指明
  2. #include </home/bedrock/ModDev/include/modloader/log.h>
  3. using namespace modloader;

  4. extern "C" void modloader_on_server_start(void* serverInstance) {
  5. Log::verbose("BeginMod", "Hello Minecraft!");
  6. Log::info("BeginMod","BeginServer");
  7. Log::debug("BeginMod","CreateByhaojie")
  8. }

编译生成.so mod文件 /home/bedrock/ModDev# g++ testmod.cpp -std=c++11 -I ${MODLOADER_SDK}/include/ -L ${MODLOADER_SDK}/lib/ -lserver_modloader -shared -fPIC -o BeginMod.so


注意


  • -L用于指明连接的库所在的文件夹
  • -l用于指明具体的库
  • -I指明#include的文件所在的文件夹

没有报错就说明成功了 ls 在当前文件夹出现了 BeginMod.so 文件,这个就是我们的MOD了,将它移动到服务端文件夹中的mods文件夹中并启动服务端 请使用 ./start_modloader 启动服务器 在启动的过程中我们便可以在命令行中看见输出了,第一个MOD成功(虽然还没有实际作用)



&lt;span id=&quot;6&quot;&gt;


创建一个具有实际作用的MOD (爆炸箭矢)


参考作者的wiki https://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&amp;-Hooking 为了实现具体的功能我们需要将服务端解包,这里还需要掌握一定的汇编知识 我们这里使用到IDA解包 下载 IDA有一款插件HexRays CodeXplorer似乎可以把汇编语言转化为更好懂的形式?不过这次还没有使用。


开始制作


使用IDA-&gt;new-&gt;选择从官网下载的服务端压缩包解压后的 bedrock_server 文件 (注意这个文件没有扩展名,所以在选择是要将文件类型改为所有文件*)



然后便开始解包 耗时十来分钟





感觉这个IDA查找起来非常耗CPU啊,低压i5占用100%????... ps:卡顿是因为还在建立索引,当索引建立完毕之后就不卡了。


  • 找到需要Hook的函数
    我们要创建一个箭矢碰撞后爆炸的MOD,首先我们要找到箭矢碰撞的函数,::onHit (其实找到对应的函数是非常困难的一件事情,毕竟这里面的函数太多了。而且官方并没有给出说明...)
    你是不是以为我们的目标是 Throwable::onHit ?但经过对Arrow的继承树的查看我们可以发现(作者发现的),Throwable并没有出现过,实际上我们应该使用的是 ProjectileComponent::onHit
  • Hooking的函数名
    找到事件发生的函数之后怎么对其进行Hook呢?
    首先我们要获得这个函数的 mangled name(关于mangled name前面有介绍),在IDA中双击FunctionName在IDA VIEW里面展示具体的函数。


在这里我们可以看见一串以_ZN 开头的字符(_ZN是name mangling 的 symbol) eg:_ZN19ProjectileComponent5onHitERK9HitResult


我们Hook时使用的就是这个名字,在IDA里面无法查看函数的返回值以及函数是否是静态的,IDA可以对这个返回值进行猜测,不过据作者MCMrARM所说,猜测的一般会失败...注释中所写的也不是完全正确的。(难道只能自己猜了嘛??)


公布结果:ProjectileComponent::onHit的返回值为void


  • 编写MOD,检测击中
    看注释吧,这里还没有爆炸,只是当事件发生的时候在控制台输出

编写完成之后像之前一样进行编译



代码:

  1. g++ testmod.cpp -std=c++11 -I ${MODLOADER_SDK}/include/ -L ${MODLOADER_SDK}/lib/ -lserver_modloader -shared -fPIC -o explodeArrow.so

满怀期待地打开服务器,捡起弓箭,shot,然后发现控制台并没有输出????....


问题解决了,真是个低级的错误,看上面的指令就能发现问题,我忘了修改源代码文件的名字了!!将 testmod.cpp 改为 explodeArrow.cpp 编译,成功之后将.so移动到../mods下,启动游戏,掏出弓箭,射击,控制台成功显示!



  • 编写MOD,处理击中之后的爆炸
    添加爆炸效果
    已经能检测到击中事件了,接下来我们要做的就是在击中的坐标处引发一次爆炸

首先我们要在IDA中找到负责爆炸的函数,搜索 ::explode,这一次我们使用的是Level(ModLoader作者在wiki中指明,说实话我也不知道怎么选择) Level::explode 现在我们面临的问题是如何获得level的指针以及爆炸的方块的源以及位置。


因为如前面所说 TInstanceHook hook成员函数(不是静态),需要完整地声明类 因此我们要像下面这样声明需要使用到的方法


  • 声明获得Actor指针的方法
    使用ProjectileComponent::getEntity() 我们可以获得一个额 Actor* 指针(实体的指针,根据wiki所说,之所以不是getActor是因为几个版本前的更新Mojang还没有彻底将Entity转变为Actor)

代码:

  1. //添加
  2. class Actor;
  3. class ProjectileComponent {
  4. public:
  5. Actor& getEntity();
  6. };

  • 声明获得Level and BlockSource指针的方法


    代码:

    1. //添加
    2. class BlockSource;
    3. class Level;
    4. class Actor {
    5. public:
    6. BlockSource* getRegion() const;
    7. Level* getLevel();
    8. };

  • 最后我们要从HITRESULT中获得击中的坐标(即爆炸坐标),声明获得坐标的方法 HitResult::getPos() const 会返回一个 Vec3 const&amp; 因此我们先声明 Vec3,再声明这个方法


    代码:

    1. class Vec3;
    2. class HitResult {
    3. public:
    4. Vec3 const& getPos() const;
    5. };·

  • 声明爆炸的方法,各个参数的意思我们也不知道,只能不断地修改,然后在游戏中测试


    代码:

    1. class Level {
    2. public:
    3. void explode(BlockSource&, Actor*, Vec3 const&, float, bool, bool, float, bool);
    4. };


最后 ,在TInstanceHook中添加,当弓箭击中物体时,这个hook便会调用其中的方法做出响应


代码:

  1. TInstanceHook(void, _ZN19ProjectileComponent5onHitERK9HitResult, ProjectileComponent, HitResult const& hitResult) {
  2. Log::verbose(TAG, "ProjectileComponent::onHit");
  3. original(this, hitResult);
  4. Actor& entity = this->getEntity();
  5. entity.getLevel()->explode(*entity.getRegion(), nullptr, hitResult.getPos(), 5.f, /* 是否产生燃烧 */ false, /*是否破坏方块 */ true, 1.f, true);
  6. }

如果编译出错请查看作者的完整代码https://github.com/minecraft-linux/server-modloader/wiki/Making-mods-%232:-Disassembly-&amp;-Hooking


完成了这些,我们的爆炸箭矢MOD的源代码就完成了,最后编译之后将.so移动到../mods文件夹下,开启server 弓箭射击..BOOM!


&lt;span id=&quot;7&quot;&gt;


总结


一开始我以为官方服务器是没有MOD了的,没想到能在github上发现这个MODLoader,而且作者MCMrARM大大还热心的写了wiki教程,一步一步的写出了一个基岩版官方服务端的MOD,不过要说做BDS的MOD开发还是会面临一些很大的问题


  • 官方没有很好的API支持,写一个MOD还得解包
  • 解包后函数过多,很难确定究竟应该Hook哪一个函数
  • 资料非常的少

在这个框架下MOD的潜力有多大?之后基岩版服务器的MOD还会有什么发展,这都是值得我们拭目以待的。
[/code]



Ginkgo06
MARKDOWN转换出问题了,尴尬..

641844114
期待官方插件的完善!

lein317
用hook的方式?我以为,官方提供扩展呢。hook多高级的方式啊。太难了。

lein317
我看有些群,提到js,意思不是可以用js?

mohist
教程不错,正在学习,谢谢啦

Ginkgo06
lein317 发表于 2019-9-18 19:23
我看有些群,提到js,意思不是可以用js?

目前官方的服务端移除了脚本引擎,而且光依靠官方的脚本引擎也很难实现插件的功能,如果想用脚本制作插件可以尝试使用stoneserver,我写了两篇文章介绍
https://blog.haojie06.com/setup-stoneserver/
https://blog.haojie06.com/stoneserver_plugin_dev/

Ginkgo06
结果stoneserver弃坑了,又回到了最初的起点。。

ff947944101
很好,支持一下

Ginkgo06

如果现在还想用这样的方法做插件的话,可以查看bdlauncher这个项目 https://github.com/Sysca11/bdlauncher 原理类似,然后他已经有一些插件的代码了

dooos
所以说基岩版官方服务端mod怎么添加呢

北斗七星Chk
希望您能把该教程移至基岩版插件&服务端里,并把其类型改为教程,我已发布加载插件开服教程,希望您能与我配合!(把JS的教程也搬运过来,行吗?)