mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-13 20:10:09 +08:00
update docs [skip ci]
This commit is contained in:
parent
9a8b696132
commit
034e256f2f
@ -18,6 +18,7 @@
|
||||
* [新建事件](#新建事件)
|
||||
* [广播自定义事件](#广播自定义事件)
|
||||
* [监听自定义事件](#监听自定义事件)
|
||||
* [在 Console 中自定义事件](../mirai-console/docs/plugin/JVMPlugin-DataExchange.md)
|
||||
- [工具函数(Kotlin)](#工具函数kotlin)
|
||||
- [线性同步(`syncFromEvent`)](#线性同步syncfromevent)
|
||||
- [线性同步(`nextEvent`)](#线性同步nextevent)
|
||||
@ -498,6 +499,10 @@ GlobalEventChannel.INSTANCE.subscribeAlways(ExampleEvent.class, event -> {
|
||||
});
|
||||
```
|
||||
|
||||
### 在 Console 中自定义事件
|
||||
|
||||
请参考 [Console - JVMPlugin - Data Exchange](../mirai-console/docs/plugin/JVMPlugin-DataExchange.md)
|
||||
|
||||
> 回到 [目录](#目录)
|
||||
|
||||
## 工具函数(Kotlin)
|
||||
|
@ -40,6 +40,8 @@ module.exports = {
|
||||
{text: '内置命令', link: '/BuiltInCommands.html'},
|
||||
{text: 'Mirai Console 附录', link: '/Appendix.html'},
|
||||
{text: "插件 - JVM Plugin - 附录", link: "/plugin/JVMPlugin-Appendix.html"},
|
||||
{text: "插件 - JVM Plugin - 多插件数据交换", link: "/plugin/JVMPlugin-DataExchange.html"},
|
||||
{text: "插件 - JVM Plugin - 排错", link: "/plugin/JVMPlugin-Debug.html"},
|
||||
]
|
||||
},
|
||||
]
|
||||
|
88
mirai-console/docs/plugin/JVMPlugin-DataExchange.md
Normal file
88
mirai-console/docs/plugin/JVMPlugin-DataExchange.md
Normal file
@ -0,0 +1,88 @@
|
||||
# Mirai Console Backend - JVM Plugins - Data Exchange
|
||||
|
||||
> 本章主要介绍如何在多个插件内直接交换数据
|
||||
>
|
||||
> 注: 多插件之间广播事件也是直接交换数据的一种方式
|
||||
|
||||
--------------------------
|
||||
|
||||
|
||||
如果需要在插件之间直接交换数据, 则插件之间必须存在直接或间接的依赖关系。
|
||||
|
||||
在 [Console - JvmPlugin](JVMPlugin.md#类加载隔离) 中有提到类加载隔离,没有依赖关系的插件之间是不能直接交换数据的。
|
||||
|
||||
> 关于如何建立依赖关系, 见 [JVMPlugin - 有关插件依赖的说明](JVMPlugin.md#有关插件依赖的说明)
|
||||
|
||||
## 在有依赖关系的插件中广播事件
|
||||
|
||||
现在系统中存在有两个插件 `com.example.guide.baseplugin` 和 `com.example.guide.extplugin`, 其中 `extplugin` 依赖 `baseplugin`。
|
||||
|
||||
在 `baseplugin` 中定义的类都可以在 `extplugin` 中访问
|
||||
|
||||
## 在无依赖关系的两个插件中广播事件
|
||||
|
||||
在两个没有依赖关系的插件中, 是不能直接交换数据的, 即使使用了相同的类名也是不能进行数据交换的。
|
||||
|
||||
如果需要在两个没有任何关系的插件中交换数据, 需要最少三个模块: `data-typedef`, `plugin-a`, `plugin-b`, `plugin-....`
|
||||
|
||||
### 多插件数据交换核心思路
|
||||
|
||||
在多个插件间交换数据, 必须存在直接或者间接的关系, 只有建立了关系才能解析到相同的类。
|
||||
|
||||
两个没有关系的插件 `A` 和 `B` 之间, 必须通过另外的模块 `data-typedef` 建立起间接的关系。
|
||||
|
||||
比如在 `data-typedef` 中定义的事件 `MyEvent`, 即使该事件是在 A 广播的也可以在 B 监听到。
|
||||
|
||||
|
||||
### 通过修改插件全局类路径链接插件
|
||||
|
||||
> 不推荐此方法, 仅适合于高度自定义
|
||||
>
|
||||
> - 使用此方法很可能会干扰到其他插件的执行导致其他插件报错。
|
||||
> - 使用此方法很可能会遇到库冲突的情况。
|
||||
|
||||
将 `data-typedef.jar` 放入 `plugin-shared-libraries` 即可。
|
||||
|
||||
模块间的关系如下图
|
||||
```text
|
||||
plugin-a plugin-b others.... ......
|
||||
| | | ......
|
||||
| | | ......
|
||||
| | | ......
|
||||
=================================================================
|
||||
## data-typedef
|
||||
## ........
|
||||
=================================================================
|
||||
|
|
||||
=================================================================
|
||||
##
|
||||
## MIRAI CONSOLE PLUGIN SYSTEM
|
||||
##
|
||||
```
|
||||
|
||||
### 将 data-typedef 打包成中转插件
|
||||
|
||||
将 `data-typedef` 也编写为一个 mirai-console 插件,
|
||||
并在 `plugin-a`, `plugin-b` 中定义对 `data-typedef` 的依赖定义即可。
|
||||
|
||||
|
||||
模块间的关系如下图
|
||||
```text
|
||||
plugin-a plugin-b others.... ......
|
||||
| | | ......
|
||||
+--- data-typedef ---+ | ......
|
||||
| | ......
|
||||
=================================================================
|
||||
## MIRAI CONSOLE PLUGIN SYSTEM
|
||||
##
|
||||
```
|
||||
|
||||
## 排错
|
||||
|
||||
见 [JVMPlugin Debug](JVMPlugin-Debug.md)
|
||||
|
||||
----------------------
|
||||
|
||||
> 返回 [JVMPlugin](JVMPlugin.md)
|
||||
>
|
||||
> 返回 [开发文档索引](../README.md#mirai-console)
|
105
mirai-console/docs/plugin/JVMPlugin-Debug.md
Normal file
105
mirai-console/docs/plugin/JVMPlugin-Debug.md
Normal file
@ -0,0 +1,105 @@
|
||||
# Mirai Console Backend - JVM Plugins - Debug
|
||||
|
||||
> 建议在 Java 9+ 的环境中进行排错, mirai-console 在 java 9+ 中的错误堆栈中报告了错误类来自哪个类加载器
|
||||
|
||||
## 错误堆栈基本解析
|
||||
|
||||
```log
|
||||
java.lang.Exception: Thread stack dump
|
||||
at java.base/java.lang.Thread.dumpStack(Thread.java)
|
||||
at example-plugin.mirai2.jar[shared]//com.example.exmapleplugin.sharedlib.SharedLib.handle(shared.kt:6)
|
||||
at example-plugin.mirai2.jar[private]//com.example.exmapleplugin.privatelib.PrivLib.cmd(priv.kt:5)
|
||||
at example-plugin.mirai2.jar//com.example.exmapleplugin.MyCommand.cmd(MyCommand.kt:63)
|
||||
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
|
||||
at ......
|
||||
at net.mamoe.mirai.console.command.CommandManager.executeCommand$default(CommandManager.kt:125)
|
||||
at chat-command-0.5.1.jar//net.mamoe.mirai.console.plugins.chat.command.PluginMain.handleCommand(PluginMain.kt:86)
|
||||
at chat-command-0.5.1.jar//net.mamoe.mirai.console.plugins.chat.command.PluginMain$onEnable$2$1.invokeSuspend(PluginMain.kt:69)
|
||||
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
|
||||
at ......
|
||||
```
|
||||
|
||||
来自 plugin 本身的类加载器的堆栈会以 插件文件名 开头, 其中 `...[private]` `....[shared]` 都是该插件使用的类库.
|
||||
|
||||
- `[shared]` 代表是共享库, 其中的类可以被依赖此插件的其他插件解析到
|
||||
- `[private]` 代表是私有库, 仅该插件自己内部使用, 依赖此插件的其他插件将不能解析到此类加载器的类
|
||||
|
||||
## 多插件间数据交换结果和预期不符合
|
||||
|
||||
多插件间数据结果不一致 90% 是因为缺少依赖关系导致的未解析到相同的类导致结果不一致
|
||||
|
||||
关于如何建立关系, 见 [JVMPlugin - Data Exchange](./JVMPlugin-DataExchange.md)
|
||||
|
||||
可以使用以下代码确定是否是因为类链接错误导致的数据不一致
|
||||
|
||||
```kotlin
|
||||
fun MiraiLogger.dumpClass(klass: Class<*>) {
|
||||
info { "Class name: $klass" }
|
||||
info { " |- loader: ${klass.classLoader}" }
|
||||
info { " |- module: ${klass.module}" }
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public static void dumpClass(MiraiLogger logger, Class<?> klass) {
|
||||
logger.info("Class name: " + klass);
|
||||
logger.info(" |- loader: " + klass.getClassLoader());
|
||||
logger.info(" |- module: " + klass.getModule());
|
||||
}
|
||||
```
|
||||
|
||||
## 使用的第三方库报错没有模块实现
|
||||
|
||||
在插件初始化的时候, 线程上下文类加载器依然还是 console 的系统类加载器 (`AppClassLoader`), 需要手动将其切换到插件的类加载器
|
||||
|
||||
详见 [Issue Comment](https://github.com/mamoe/mirai/issues/2138#issuecomment-1179673302)
|
||||
|
||||
```kotlin
|
||||
fun onEnable() {
|
||||
val oThreadCtxLoader = Thread.currentThread().contextClassLoader
|
||||
try {
|
||||
Thread.currentThread().contextClassLoader = javaClass.classLoader
|
||||
// .......
|
||||
} finally {
|
||||
Thread.currentThread().contextClassLoader = oThreadCtxLoader
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## java.lang.LinkageError: loader constraint violation
|
||||
|
||||
```log
|
||||
java.lang.LinkageError: loader constraint violation: when resolving method 'void io.ktor.client.request.HttpRequestBuilder.setMethod(io.ktor.http.HttpMethod)' the class loader 'test-ktor-dev.mirai2.jar' @61dde151 of the current class, com/kasukusakura/testktor/TestKtor$getTailrec$$inlined$get$2, and the class loader 'app' for the method's defining class, io/ktor/client/request/HttpRequestBuilder, have different Class objects for the type io/ktor/http/HttpMethod used in the signature (com/kasukusakura/testktor/TestKtor$getTailrec$$inlined$get$2 is in unnamed module of loader 'test-ktor-dev.mirai2.jar' @61dde151, parent loader 'global-shared' @32b9bd12; io.ktor.client.request.HttpRequestBuilder is in unnamed module of loader 'app')
|
||||
at .................
|
||||
|
||||
java.lang.LinkageError: loader constraint violation:
|
||||
when resolving method 'void io.ktor.client.request.HttpRequestBuilder.setMethod(io.ktor.http.HttpMethod)'
|
||||
the class loader 'test-ktor-dev.mirai2.jar' @61dde151 of the current class, com/kasukusakura/testktor/TestKtor$getTailrec$$inlined$get$2,
|
||||
and
|
||||
the class loader 'app' for the method's defining class, io/ktor/client/request/HttpRequestBuilder,
|
||||
|
||||
have different Class objects for the type io/ktor/http/HttpMethod used in the signature
|
||||
|
||||
(
|
||||
com/kasukusakura/testktor/TestKtor$getTailrec$$inlined$get$2 is in unnamed module of loader 'test-ktor-dev.mirai2.jar' @61dde151,
|
||||
parent loader 'global-shared' @32b9bd12;
|
||||
|
||||
io.ktor.client.request.HttpRequestBuilder is in unnamed module of loader 'app'
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
翻译
|
||||
|
||||
```log
|
||||
JVM 无法解析 com/kasukusakura/testktor/TestKtor$getTailrec$$inlined$get$2 中的方法引用
|
||||
'void io.ktor.client.request.HttpRequestBuilder.setMethod(io.ktor.http.HttpMethod)'
|
||||
|
||||
搜索到的 'io.ktor.client.request.HttpRequestBuilder' 位于系统类加载器 'app' 中
|
||||
|
||||
HttpRequestBuilder 中的 HttpMethod 引用和
|
||||
TestKtor$getTailrec$$inlined$get$2 得到的 HttpMethod 引用
|
||||
不一致, 无法连接
|
||||
```
|
||||
|
||||
结论: 插件没有附带完整的 ktor 依赖导致部分类解析至插件库加载器, 部分类解析至系统类加载器
|
@ -575,10 +575,18 @@ Central
|
||||
> 如果没法输入命令, 请确认 Gradle 任务视图没有被聚焦至 `:runConsole`,
|
||||
> 必须选择整个 Gradle 任务视图才可执行命令。
|
||||
|
||||
### 排错
|
||||
|
||||
详见 [JVMPlugin Debug](JVMPlugin-Debug.md)
|
||||
|
||||
## 发布插件到 mirai-console-loader
|
||||
|
||||
插件中心仍在开发中。
|
||||
|
||||
## 多插件间数据交换
|
||||
|
||||
见 [JVMPlugin - Data Exchange](JVMPlugin-DataExchange.md)
|
||||
|
||||
> 下一步,[Commands](../Commands.md#mirai-console-backend---commands)
|
||||
>
|
||||
> 返回 [开发文档索引](../README.md#mirai-console)
|
||||
|
Loading…
Reference in New Issue
Block a user