LuaInMinecraftBukkit II
English | 中文
[luajava-jasonsantos]: https://github.com/jasonsantos/luajava [luajava-smileyik]: https://github.com/SmileYik/luajava [lua-pool-example]: https://github.com/SmileYik/LuaInMinecraftBukkitII/blob/gh-page/docs/en/GlobalVariable.md#pooledcallable---transform-a-lua-closure-into-a-closure-that-can-be-run-in-the-lua-pool [bstats]: https://bstats.org/signatures/bukkit/LuaInMinecraftBukkitII.svg ## 介绍 LuaInMinecraftBukkit II 是一个 Minecraft Bukkit 服务器插件,旨在利用 Lua 脚本与 Bukkit 服务器进行交互。 Lua 是一种小型脚本语言,具有非常简单的语法和良好的执行速度。 想象一下,使用一个轻量级的脚本来编写 Bukkit 插件,无需编译即可运行是多么美好。 如果需要修改,只需更改并重新加载脚本——这简直就像一个梦想。 ## 与上一代相比 与上一代相比,此版本更加注重本机 Lua 虚拟机。 同样,此版本也基于 [luajava][luajava-jasonsantos] 项目。 但是,此版本使用了一个 [克隆的 luajava][luajava-smileyik] 仓库。 与原始仓库相比,克隆的 luajava 仓库从根本上重写了部分反射功能,进一步简化了 Lua 调用 Java 方法的过程,并在 C 语言端提供了非常友好的异常消息。 ## 它能做什么? 主要功能: + **命令注册**: 注册任何你想要的命令,它将自动生成帮助信息和命令层级结构。 + **事件监听**: 监听任何你想要的 Bukkit 事件,即使它来自另一个插件的自定义事件。 + **Lua 池化**: 通过池化 Lua,你可以将 Lua 闭包提交到其他状态机上以实现真正的多线程并行执行:[示例][lua-pool-example]。 其他功能包括: + **快速反射**: Lua 中用于反射的组件现在使用 MethodHandle 快速反射,速度比标准反射快得多。 + **自动重新加载**: 该插件可以监控 Lua 脚本文件的更改并自动重新加载它们。 然而,依赖于 Java 的反射和动态代理机制,目前可以继承 Java 接口并在 Lua 脚本中调用任何 Java 类型的公共方法或公共属性。 这意味着这个插件可以动态加载脚本并享受 Java 功能的一个子集。 当然,反射不是万能的,仍然会有许多情况无法在 Lua 侧处理。 在这种情况下,Java 需要弥补 Lua 的不足。 但是,在开发过程中,我将尝试简化 Lua 和 Java 之间的交互。 除了上述内容外,类似于第一代,它还可以加载用 C/C++ 编写的动态链接库。 当然,这些是 Lua 语言本身支持的功能。 ## Lua 池化 得益于 [luajava][luajava-smileyik] 不懈的努力, 现在可以在多个线程上执行不同的 Lua 代码。 在此插件中,每个 Lua 环境配置都具有一个 **主 Lua 虚拟机**。 在没有池化的 Lua 虚拟机的情况下,当 **主 Lua 虚拟机** 正在运行时, 如果另一个线程 B 需要获取 **主 Lua 虚拟机** 的执行权限(例如,当触发 Bukkit 事件或 Bukkit 异步任务时)来运行 Lua 代码 (例如 Lua 闭包函数),线程 B 必须 **阻塞并等待主 Lua 虚拟机**。 只有在 **主 Lua 虚拟机** 完成当前任务后,线程 B 才能获取 执行权限来运行 Lua 代码。 Lua 代码完成后,它必须释放 **主 Lua 虚拟机** 的执行权限。 如果线程 B 执行的代码耗时很长, **主 Lua 虚拟机** 将一直被线程 B 占用,从而阻塞其他 Lua 代码的执行,直到线程 B 释放执行权限。 使用 Lua 池化,在相同的场景中,线程 B 获取 **主 Lua 虚拟机** 的执行权限后, 它会立即将要执行的 Lua 代码以及所有相关数据转移到 **另一个 Lua 虚拟机**。 一旦转移完成, 它会立即释放 **主 Lua 虚拟机** 的执行权限并获取 **另一个 Lua 虚拟机的** 执行权限来运行传输的代码。 这使得线程 B 能够在非常短的时间内释放 **主 Lua 虚拟机** 的执行权限,无需等待 Lua 代码完成,确保 **主 Lua 虚拟机** 可以快速响应其他调用。 这是一个更具体的例子。 下面的代码调用了两个 Bukkit 异步任务,并且两个任务都是无限循环。 ```lua local import = require "import" local Thread = import "java.lang.Thread" for i = 1, 2 do luaBukkit.helper:asyncCall(luaBukkit.env:pooledCallable( function () while true do luaBukkit.log:info(Thread:currentThread():getName() .. " async call " .. i .. "!") Thread:sleep(1000) end end )) end ``` 当启用 Lua 池化并且容量为 2 时,上面的代码会输出以下内容。 你可以看到来自多个线程的输出,并且可以通过 `stop` 命令关闭服务器: ``` [13:14:41 INFO]: [LuaInMinecraftBukkitII] Craft Scheduler Thread - 3 - LuaInMinecraftBukkitII async call 1! [13:14:41 INFO]: [LuaInMinecraftBukkitII] Craft Scheduler Thread - 1 - LuaInMinecraftBukkitII async call 2! [13:14:42 INFO]: [LuaInMinecraftBukkitII] Craft Scheduler Thread - 1 - LuaInMinecraftBukkitII async call 2! [13:14:42 INFO]: [LuaInMinecraftBukkitII] Craft Scheduler Thread - 3 - LuaInMinecraftBukkitII async call 1! ``` 当禁用 Lua 池化时,上面的代码会输出以下内容。 你可以看到只有一个线程的输出,并且虽然 `stop` 命令被确认, 但由于无限循环导致无法获取释放 Lua VM 资源的执行权限,服务器无法关闭: ``` [13:17:49 INFO]: [LuaInMinecraftBukkitII] Craft Scheduler Thread - 2 - LuaInMinecraftBukkitII async call 2! [13:17:52 INFO]: [LuaInMinecraftBukkitII] Craft Scheduler Thread - 2 - LuaInMinecraftBukkitII async call 2! stop [13:17:53 INFO]: [LuaInMinecraftBukkitII] Craft Scheduler Thread - 2 - LuaInMinecraftBukkitII async call 2! [13:17:53 INFO]: Stopping the server [13:17:53 INFO]: Stopping server [13:17:53 INFO]: [LuaInMinecraftBukkitII] Disabling LuaInMinecraftBukkitII vmaster-59748d7+luajava-master-a3ddaf6+java-21 [13:17:53 INFO]: [LuaInMinecraftBukkitII] [LuaEnv default] Shutdown auto-reload. [13:17:54 INFO]: [LuaInMinecraftBukkitII] Craft Scheduler Thread - 2 - LuaInMinecraftBukkitII async call 2! [13:17:55 INFO]: [LuaInMinecraftBukkitII] Craft Scheduler Thread - 2 - LuaInMinecraftBukkitII async call 2! [13:17:56 INFO]: [LuaInMinecraftBukkitII] Craft Scheduler Thread - 2 - LuaInMinecraftBukkitII async call 2! ``` ## JNIBridge 得益于 cffi 的支持,JNIBridge 横空出世\! 现在可以与动态链接库进行交互。 JNIBridge 仍在完善中,但目前支持: + [x] Java 方法调用 + [x] 基本类型转换 下一阶段计划的功能包括: - [ ] 字段访问和赋值 - [ ] 数组访问和赋值 下图显示了一个简单的玩家加入事件,但事件处理逻辑由 Cpp 侧管理。  ![bstats]