原生模組快速開始
Android,而不是 LeviLamina
本部分介紹 Android 上 Minecraft 基岩版的原生模組。它們由
Android 版 LeviLauncher
通過 Preloader SDK(LiteLDev/preloader-android)加載,而不是由
LeviLamina 加載,並使用本部分記錄的獨立 pl:: API。
本頁面描述 Android 版 LeviLauncher 原生模組的受支持開發路徑。公開 SDK 位於 LiteLDev/preloader-android。
獨立第三方模組建議從 Android 模組模板 開始。
推薦以 examples/full-cpp-mod 作為參考實現。它包含生命週期註冊、類型化
配置、Mod Menu 集成、Android 打包和 .levipack 輸出。
構建示例
在倉庫根目錄運行:
.\examples\full-cpp-mod\build.ps1 -Clean這個示例可以在當前倉庫直接構建。獨立模組工程建議從模板開始,並把 SDK 作為外部依賴引入。
輸出:
examples\full-cpp-mod\dist\arm64-v8a\full-cpp-mod\
examples\full-cpp-mod\dist\arm64-v8a\full-cpp-mod.levipack可以導入 .levipack,也可以把解包後的模組目錄複製到啟動器的原生模組位置。
包結構
full-cpp-mod/
├── manifest.json
├── libfull_cpp_mod.so
└── config/
├── config.json
└── config.schema.json目錄名是運行期模組 id。發佈後保持穩定,因為它會用於路徑、Mod Menu 歸屬和用戶持久化狀態。
manifest.json
{
"type": "preload-native",
"name": "Full C++ Lifecycle Mod Example",
"author": "LiteLDev",
"version": "1.0.0",
"entry": "libfull_cpp_mod.so",
"minecraft_versions": [],
"overwrite_files": [],
"overwrite_folders": []
}| 字段 | 說明 |
|---|---|
type | 必須是 preload-native。 |
entry | 模組目錄內 Android .so 的相對路徑。 |
name | 啟動器顯示名。 |
author | 作者信息。 |
version | 模組版本。 |
icon | 可選圖標相對路徑。 |
minecraft_versions | 支持精確版本和 * 前綴通配;缺失或為空表示全部版本。 |
overwrite_files | 可選的相對文件列表;重複導入已安裝模組時,這些已存在文件允許被替換。缺失或為空時不額外覆蓋文件。 |
overwrite_folders | 可選的相對目錄列表;重複導入已安裝模組時,這些目錄內已存在的文件允許被替換。缺失或為空時,除 manifest.json 和 entry 外不覆蓋已有文件。 |
生命週期形態
#include <pl/Mod.hpp>
class FullCppMod {
public:
static FullCppMod &instance();
FullCppMod();
[[nodiscard]] ll::mod::NativeMod &getSelf() const { return mSelf; }
bool load();
bool enable();
bool disable();
bool unload();
private:
ll::mod::NativeMod &mSelf;
};
PL_REGISTER_MOD(FullCppMod, FullCppMod::instance())load() 是必需的。其它生命週期函數可選,缺失時默認成功。
在構造函數中保存 loader 提供的原生模組對象:
FullCppMod::FullCppMod() : mSelf(*ll::mod::NativeMod::current()) {}
bool FullCppMod::load() {
auto &self = getSelf();
std::filesystem::create_directories(self.getConfigDir());
self.getLogger().info("Loaded {}", self.getName());
return true;
}SDK 依賴
獨立 Android 原生模組工程使用 FetchContent 引入 preloader-android,
並把自己的模組庫鏈接到 preloader target:
include(FetchContent)
FetchContent_Declare(
preloader_android
GIT_REPOSITORY https://github.com/LiteLDev/preloader-android.git
GIT_TAG 0.2.2)
FetchContent_MakeAvailable(preloader_android)
target_link_libraries(my_mod PRIVATE preloader)實際項目應把 GIT_TAG 固定到 release tag,避免構建結果漂移。
常用 SDK 頭文件:
#include <pl/Mod.hpp>
#include <pl/ModMenu.hpp>
#include <pl/Input.hpp>
#include <pl/Config.hpp>
#include <pl/memory/Hook.hpp>
#include <pl/memory/Patch.hpp>
#include <pl/memory/Signature.hpp>只使用 include/pl 下的公開 SDK 頭文件。
類型化配置
配置應保存在模組自有狀態中。生命週期調用期間,ConfigFile 默認會使用當前
原生模組的 config/config.json 和 config/config.schema.json 路徑:
struct ExampleConfig {
int version = 1;
bool showOverlay = true;
int opacity = 80;
};
class FullCppMod {
private:
std::optional<pl::config::ConfigFile<ExampleConfig>> mConfig;
};
bool FullCppMod::load() {
mConfig.emplace();
return mConfig->load();
}把 config.json 和 config.schema.json 隨模組一起打包。完整示例的構建腳本會在
生成 .levipack 前產出這兩個文件。
Mod Menu
bool FullCppMod::enable() {
const auto &config = mConfig->value();
return pl::modmenu::ModuleBuilder("full_cpp_mod.hud",
"Full C++ Config Demo")
.modId(getSelf().getId())
.description("Pure C++ lifecycle module with persistent typed config.")
.defaultEnabled(config.showOverlay)
.config("opacity", "Opacity", pl::modmenu::ConfigType::SliderInt,
std::to_string(config.opacity), "0", "100")
.registerModule();
}浮動按鈕使用 ButtonBuilder:
bool registerQuickButton(ll::mod::NativeMod &self) {
return pl::modmenu::ButtonBuilder("full_cpp_mod.quick_drop",
"Full C++ Quick Drop")
.moduleId("full_cpp_mod.hud")
.modId(self.getId())
.label("Q")
.androidKeyCode(45)
.behavior(pl::modmenu::ButtonBehavior::Click)
.registerButton();
}要在 HUD 中繪製已註冊圖片,請先註冊原始 RGBA 像素,再提交帶
DrawCommand::imageId 的 Image 繪製命令:
bool registerLogoImage(std::span<const unsigned char> rgbaPixels) {
return pl::modmenu::registerImage("full_cpp_mod.logo", rgbaPixels, 64, 64);
}
void submitLogoOverlay() {
const std::vector<pl::modmenu::DrawCommand> commands = {
{
.type = pl::modmenu::DrawCommandType::Image,
.x = 16.0f,
.y = 16.0f,
.w = 32.0f,
.h = 32.0f,
.imageId = "full_cpp_mod.logo",
},
};
pl::modmenu::submitDrawCommands("full_cpp_mod.hud", commands);
}如果模塊或按鈕是臨時 UI,請在 disable() 中註銷。
完整 API 參考見 Mod Menu API。
構建選項
.\examples\full-cpp-mod\build.ps1
.\examples\full-cpp-mod\build.ps1 -Clean
.\examples\full-cpp-mod\build.ps1 -Ndk <path-to-android-ndk>省略 -Ndk 時,腳本會從 ANDROID_NDK_HOME、ANDROID_NDK_ROOT、ANDROID_HOME
或 ANDROID_SDK_ROOT 解析 NDK。
檢查清單
- 原生模組目標 ABI 為
arm64-v8a。 - 鏈接
preloadertarget,並只使用include/pl下的公開 SDK 頭文件。 - 使用
PL_REGISTER_MOD註冊一個長期存活對象。 - 從
ll::mod::NativeMod::current()保存ll::mod::NativeMod &mSelf。 - 註冊運行期 UI 前先加載配置。
- hook 和 patch handle 放在模組自有狀態中。
disable()中註銷臨時 Mod Menu 項。- callback 在註銷或 unload 前必須保持有效。
完整 API 繼續閱讀 Mod API 參考 和 Config API 參考。