• 欢迎加入MineBBS QQ讨论群:点击查看所有的官方讨论群
  • 我们将于近期对服务器进行迁移,服务可能中断至多2日。请各位安排好自己的访问计划,造成不便敬请谅解!
  • MineBBS入站考试已经上线!想要成为【正式会员】解锁更多功能吗?快来参与吧!【点我去看】
MeowPAPI静态PAPI库

原生 MeowPAPI静态PAPI库 26.10.1

完成注册/登录后下载
版权类型
原创
版权链接
https://github.com/Ieckseyr/MeowPAPI/tree/main
适配版本(基岩)
  1. 最新版本
[MD] # MeowPAPI > **Minecraft Bedrock 服务器 PlaceholderAPI 中心** —— 统一的占位符注册 / 翻译 / 跨插件共享方案 > 适用于 LeviLamina (BDS) 插件生态,同时支持 C++ 原生插件与 LSE (JS) 脚本插件。 **当前版本:v1.1.0 (ABI 0x010100)** --- ## 目录 - [特性](#特性) - [游戏内指令](#游戏内指令) - [安装](#安装) - [快速开始](#快速开始) - [版本与 ABI](#版本与-abi) - [文档](#文档) - [已知集成插件](#已知集成插件) --- ## 特性 - **双目标架构**:`MeowPAPI.dll` 独立插件承载全部逻辑;`MeowPAPI` 静态库供 C++ 消费者嵌入集成 - **自动释放**:静态库消费者首次加载时自动把内嵌的 `MeowPAPI.dll` 释放到 `plugins/MeowPAPI/` 并加载——无需手动分发前置 DLL(`std::call_once` 防多消费者冲突) - **三格式翻译**:`%name%`(BEPAPI 经典)、`{name}`(BetterSidebar)、`${papi:...}`(GMLIB 带参格式)同串共存 - **带参占位符**:GMLIB PAPI 兼容的 `<param>` 槽位模板注册(如 `title_score_rank_<score>_<title>_<number>`),支持显式参数与槽位参数混用、嵌套 `${...}` 递归翻译 - **LSE 运行时桥接**:`LegacyRemoteCall (lrca)` 软依赖——运行时挂载,精确符号 + PE 导出表前缀模糊匹配两级解析,**跨 lrca 编译版本二进制兼容**;lrca 缺席时自动降级(原生 C++ PAPI 不受影响) - **ABI 版本管理**:单一来源宏体系(`MEOWPAPI_ABI_VERSION`),消费者可运行时查询 `loadedAbiVersion()` / `paramPapiSupported()` / `loadedBuildTimestamp()` 判定部署版本 - **启动自动广播**:`ServerStartedEvent` 时自动输出版本 / ABI / 构建时间 / 占位符统计 / lrca 挂载状态——远端秒判部署版本 - **BEPAPI 兼容层**:内置 BEPAPI 占位符兼容导出,旧插件无感迁移 - **线程安全**:注册表全程持锁;JS 回调统一回主线程调度 ## 游戏内指令 | 指令 | 权限 | 说明 | |------|------|------| | `/meowpapi` | 所有玩家 | 显示版本摘要 + 帮助 | | `/meowpapi version` | 所有玩家 | 版本 / ABI / 构建时间 / 功能位 / lrca 挂载详情 / 占位符统计 | | `/meowpapi list [页码]` | OP(execute 内自检) | 分页列出全部已注册占位符(名称 / 类型 / 归属插件 / 带参标记) | > 指令注册为 `CommandPermissionLevel::Any` + execute 内自检,规避 Bedrock 客户端对高权限命令的本地隐藏问题(跨服/代理场景 OP 状态同步异常时 `GameDirectors` 命令会显示"未知命令")。 > 控制台 / 后台来源同样可执行(输出自动去色)。 ## 安装 ### 方式一:独立前置插件(LSE 脚本 / 不编译 C++ 的用户) ``` plugins/ └── MeowPAPI/ ├── manifest.json └── MeowPAPI.dll ``` 直接放置即可。lrca(LegacyRemoteCall)为**可选依赖**——存在时自动挂载 LSE 桥,缺席时仅 LSE 脚本通道不可用。 ### 方式二:静态库集成(C++ 插件开发者,推荐) 在你的插件 `xmake.lua` 中: ```lua includes("../MeowPAPI/xmake.lua") target("MyPlugin") -- ... 其他配置 ... add_deps("MeowPAPI") -- 静态库(内嵌 MeowPAPI.dll) add_includedirs("../MeowPAPI/include") add_includedirs("../MeowPAPI/src-wrapper") -- wrapper 层头文件 ``` ```cpp #include "meowpapi/EnsureLoaded.h" #include "meowpapi/PlaceholderApi.h" bool MyPlugin::load() { // 部署 + 加载 MeowPAPI.dll(首次自动释放到 plugins/MeowPAPI/) // 注意:manifest.json 中 MeowPAPI 必须声明为 optionalDependencies // 或不声明——硬依赖会在首次部署前拒载消费者(鸡生蛋问题) return meowpapi::ensureLoaded(); } ``` 详见 [INTEGRATION_GUIDE.md](INTEGRATION_GUIDE.md)。 ## 快速开始 ### C++ 消费者 ```cpp auto& papi = meowpapi::PlaceholderApi::getInstance(); // 服务器级占位符 papi.registerServerPlaceholder("MyPlugin", "my_online", []() { return std::to_string(getOnlineCount()); }); // 翻译 std::string text = papi.translateStringWithPlayer( "§a{pl.realName} §7TPS: {server_tps}", player); ``` ### LSE (JS) 插件 ```js // 1. 导出回调 ll.export((player) => "我的称号", "MyScript", "getTitle"); // 2. 注册占位符(hasExported 预检 + 重试,勿直接 ll.import) function tryRegister() { if (!ll.hasExported("MeowPAPI", "registerPlayerPlaceholder")) { setTimeout(tryRegister, 3000); return; } ll.import("MeowPAPI", "registerPlayerPlaceholder")( "MyScript", "my_title", "MyScript", "getTitle"); } tryRegister(); ``` > **LSE 时序铁律**:MeowPAPI 的 RemoteCall 导出由后台线程在 lrca 加载后立即完成(抢在 `ServerStartedEvent` 之前),但 LSE 插件加载仍可能先于导出——**必须 `ll.hasExported` 预检 + `setTimeout` 重试**,直接 `ll.import` 未导出函数会报 "Fail to import!" 且本次启动注册永久跳过。 ## 版本与 ABI | 版本 | ABI | 功能位 | 说明 | |------|-----|--------|------| | 1.1.0 | 0x010100 | bit0 = 带参占位符 | 参数化 PAPI、`/meowpapi` 指令、启动版本广播、运行时 ABI 查询、LSE 桥模糊符号匹配 | | 1.0.0 | 0x010000 | — | 初始版本:注册表 + 三格式翻译 + RemoteCall 桥 | 消费者侧运行时检查: ```cpp if (meowpapi::loadedAbiVersion() < MEOWPAPI_ABI_VERSION) { logger.warn("MeowPAPI 运行时版本过低: 0x{:06x} < 编译期 0x{:06x}", meowpapi::loadedAbiVersion(), MEOWPAPI_ABI_VERSION); } ``` API 遵循 **add-only 策略**:现有接口永不修改或删除,仅追加。 ## 文档 | 文档 | 内容 | |------|------| | [API.md](API.md) | 全部调用通道参考:C++ / LSE (JS) / C ABI / RemoteCall、带参占位符、内置占位符全表、线程模型、故障排查 | | [INTEGRATION_GUIDE.md](INTEGRATION_GUIDE.md) | 静态库集成步骤:目录结构、xmake 配置、生命周期、manifest 依赖声明规范 | ## 已知集成插件 | 插件 | 集成方式 | |------|----------| | MeowSidebar | 静态库(自动释放)+ 运行时 ABI 诊断 | | MeowMenu | 静态库 | | MeowHolographicRenderer | 静态库 | | MeowAchievement | 静态库(自动释放) | | MeowTombstone | 静态库(自动释放) | | MeowItemFilter | 静态库(自动释放) | | MeowMemeTriggers | 静态库(自动释放) | | MeowTemperature | 静态库(自动释放) | | FastMiner | 静态库 | | ZXDash (ZXPanel) | 静态库 + RemoteCall 双通道 | --- *MeowPAPI © 2026 — Built with LeviLamina* [/MD]
[MD] # MeowPAPI API 调用文档 > 适用版本:MeowPAPI 1.1.0(ABI 0x010100) > 本文档覆盖全部调用通道:C++ 静态库、LSE (JS)、C ABI、RemoteCall 代理。 --- ## 目录 1. [架构概述](#1-架构概述) 2. [快速开始](#2-快速开始) 3. [翻译格式](#3-翻译格式) 4. [C++ API(PlaceholderApi)](#4-c-apiplaceholderapi) 5. [C++ API(PlaceholderRegistry 直用)](#5-c-apiplaceholderregistry-直用) 6. [LSE (JS) API](#6-lse-js-api) 7. [带参占位符(GMLIB PAPI 兼容)](#7-带参占位符gmlib-papi-兼容) 8. [ABI 版本与功能位](#8-abi-版本与功能位) 9. [内置占位符全表](#9-内置占位符全表) 10. [线程模型](#10-线程模型) 11. [C ABI 参考(DllExports)](#11-c-abi-参考dllexports) 12. [RemoteCall 代理(RemoteCallAPI.h)](#12-remotecall-代理remotecallapih) 13. [错误处理与常见问题](#13-错误处理与常见问题) 14. [游戏内指令(/meowpapi)](#14-游戏内指令meowpapi) --- ## 1. 架构概述 MeowPAPI 采用 **双目标架构**: | 目标 | 类型 | 职责 | |------|------|------| | `MeowPAPI_DLL` | LeviLamina 插件 DLL | 全部实际逻辑:注册表、内置占位符、RemoteCall 桥、BEPAPI 兼容 | | `MeowPAPI` | 静态库(wrapper) | 嵌入 DLL 二进制 → 检测/部署/加载 → 转发调用 | 消费者插件只需链接静态库。首次加载时,静态库把 `MeowPAPI.dll` 释放到 `plugins/MeowPAPI/` 并 `LoadLibraryW` 加载,随后自动完成初始化 (`initAsServer` + 注册内置占位符 + 导出 RemoteCall API + 安装 BEPAPI 兼容层)。 多个消费者插件防冲突:`std::call_once` 保证只部署一次,第一个加载的是"部署者"。 ``` ┌─────────────┐ 静态库(wrapper) ┌──────────────────┐ │ MeowSidebar │ ─┐ │ │ │ MeowMenu │ ─┼──add_deps────> │ MeowPAPI.dll │ <── LeviLamina 独立插件 │ ZXPanel ... │ ─┘ │ (注册表+内置PAPI) │ └─────────────┘ └────────┬─────────┘ │ RemoteCall (MeowPAPI 命名空间) ┌──────┴──────┐ │ LSE/JS 插件 │ └─────────────┘ ``` 集成方式(xmake 依赖、load/enable/disable 生命周期)见 [INTEGRATION_GUIDE.md](INTEGRATION_GUIDE.md)。核心三步: ```lua -- xmake.lua includes("../MeowPAPI/xmake.lua") target("MyPlugin") add_deps("MeowPAPI") add_includedirs("../MeowPAPI/include") add_includedirs("../MeowPAPI/src-wrapper") ``` ```cpp // load() 阶段:触发部署 + 自动初始化(勿在 enable() 重复 init) if (!meowpapi::ensureLoaded()) return false; ``` --- ## 2. 快速开始 ### C++ 消费者 ```cpp #include "meowpapi/EnsureLoaded.h" #include "meowpapi/PlaceholderApi.h" bool MyPlugin::load() { return meowpapi::ensureLoaded(); // 部署 + 自动初始化 } bool MyPlugin::enable() { auto& papi = meowpapi::PlaceholderApi::getInstance(); // 注册服务器级占位符:{my_online} → "12" papi.registerServerPlaceholder("MyPlugin", "my_online", [](Player* /*player*/) { return std::to_string(/* ... */ 12); }); // 翻译 std::string text = papi.translateStringWithPlayer( "§a{pl.realName} §7TPS: {server_tps}", player); return true; } ``` ### LSE (JS) 插件 ```js // 1. 导出回调 ll.export((player) => { return "我的称号"; }, "MyScript", "getTitle"); // 2. 注册玩家级占位符 {my_title} ll.import("MeowPAPI", "registerPlayerPlaceholder")( "MyScript", // pluginName —— 注册归属(卸载时按此清理) "my_title", // papiName —— 占位符名 "MyScript", // callbackPlugin —— 回调所属 RemoteCall 命名空间 "getTitle" // callbackFunc —— 回调函数名 ); // 3. 翻译(也可交给 MeowSidebar 等消费者在显示时翻译) let text = ll.import("MeowPAPI", "translateStringWithPlayer")( "§d{my_title} §7{pl.realName}", "Steve" ); ``` --- ## 3. 翻译格式 `translateString*` 系列同时支持三种格式: | 格式 | 示例 | 说明 | |------|------|------| | `%name%` | `%server_tps%` | BEPAPI 经典格式 | | `{name}` | `{pl.realName}` | BetterSidebar 格式 | | `${papi:...}` | `${papi:rank_<n>,<n>=10}` | GMLIB PAPI 带参格式,见 §7 | 规则: - **`{}` 格式含冒号时跳过不处理**(如 `{js:expr}`、`{E:name}`),保留原文。 - **未命中**:占位符保留为 `{name}` 原样返回(便于第二遍翻译再次匹配), `%name%` 格式未命中同样回退为 `{name}` 形式。 - **回调内部错误 / 回调缺失**:输出 `{ERR}`。 - `${papi:...}` 嵌套感知配对大括号,值内可嵌套任意 `${...}` 表达式。 --- ## 4. C++ API(PlaceholderApi) 头文件:`meowpapi/PlaceholderApi.h`。单例自动路由——服务端模式走本地注册表, 客户端模式走 RemoteCall 调远端。 ### 4.1 初始化 ```cpp meowpapi::PlaceholderApi& papi = meowpapi::PlaceholderApi::getInstance(); papi.initAsServer(); // 服务端模式(ensureLoaded 的 autoInit 已调用,勿重复) papi.initAsClient(); // 客户端模式:翻译经 RemoteCall 转发远端注册表 ``` > 消费者插件经 `ensureLoaded()` 加载后默认已是服务端模式; > 仅当插件明确要作为"远程客户端"时才覆盖为 `initAsClient()`。 ### 4.2 翻译与取值 ```cpp // 翻译整串(替换全部 %name% / {name} / ${papi:...}) std::string translateString(std::string const& str); std::string translateStringWithPlayer(std::string const& str, Player* player); // skipRemote=true:跳过 mainThreadOnly 占位符(保留原文,留主线程二遍翻译) // 仅服务端模式生效;客户端模式忽略该参数 std::string translateStringWithPlayer(std::string const& str, Player* player, bool skipRemote); // 取单个占位符值(不存在时返回 "{name}" 形式) std::string getValue(std::string const& name); std::string getValueWithPlayer(std::string const& name, Player* player); // 检查存在性 bool hasPlaceholder(std::string const& name); ``` ### 4.3 注册(普通占位符) 回调统一签名为 `std::function<std::string(Player*)>`,服务器级回调收到 `nullptr`: ```cpp using PlaceholderCallback = std::function<std::string(Player* player)>; bool registerServerPlaceholder(pluginName, name, cb); // 每次求值实时调用 bool registerPlayerPlaceholder(pluginName, name, cb); // 每次求值实时调用 bool registerStaticPlaceholder(pluginName, name, cb, updateIntervalMs); // 结果缓存 ``` - `pluginName`:注册归属,`unregisterByPlugin` 按此批量注销。 - `name`:占位符名。重复注册返回 `false`。 - `updateIntervalMs`:静态缓存刷新间隔(毫秒),0 表示不缓存。 - 客户端模式下注册经 RemoteCall 转发到远端注册表。 ### 4.4 注册(带参占位符,GMLIB 兼容) ```cpp using PlaceholderParamCallback = std::function<std::string(Player* player, std::string const& paramsJson)>; bool registerServerPlaceholderWithParams(pluginName, name, cb); bool registerPlayerPlaceholderWithParams(pluginName, name, cb); ``` `name` 可含 `<param>` 槽位模板;`paramsJson` 为参数 JSON 对象字符串, 每个参数**双键注入**(`{"score":"zxsc","<score>":"zxsc",...}`, 兼容 `params.score` 与 GMLIB 原版 `params['<score>']` 两种取值)。详见 §7。 ### 4.5 ABI 查询 ```cpp uint32_t getAbiVersion(); // DLL 侧=编译期宏;wrapper 侧=运行时 DLL 报告值(旧 DLL 返回 0) bool isParamPapiSupported(); // 是否支持带参占位符(wrapper 侧查功能位,旧 DLL 返回 false) ``` --- ## 5. C++ API(PlaceholderRegistry 直用) 头文件:`meowpapi/PlaceholderRegistry.h`。需要更细控制(线程标记、按插件注销、 列表查询)时直接使用注册表单例: ```cpp auto& reg = meowpapi::PlaceholderRegistry::getInstance(); ``` ### 5.1 Remote 版本注册(mainThreadOnly 标记) 回调访问非线程安全 API(Level / Player 内部状态等)时,必须用 `*Remote` 版本注册, 后台线程翻译时自动跳过,留给主线程二遍翻译(见 §10): ```cpp bool registerServerPlaceholderRemote(pluginName, name, cb); bool registerPlayerPlaceholderRemote(pluginName, name, cb); bool registerStaticPlaceholderRemote(pluginName, name, cb, updateIntervalMs); // 带参 Remote 版本 bool registerServerPlaceholderWithParamsRemote(pluginName, name, cb); bool registerPlayerPlaceholderWithParamsRemote(pluginName, name, cb); ``` ### 5.2 注销与列表 ```cpp bool unregisterPlaceholder(std::string const& name); void unregisterByPlugin(std::string const& pluginName); void clear(); // 清空全部(慎用) std::vector<std::string> listPlaceholders(); std::vector<std::string> listPlaceholdersByPlugin(std::string const& pluginName); ``` ### 5.3 翻译 / 取值(与 PlaceholderApi 等价) ```cpp std::string translateString(str); std::string translateStringWithPlayer(str, player); std::string translateStringWithPlayer(str, player, skipRemote); std::string getValue(name); std::string getValueWithPlayer(name, player); bool hasPlaceholder(name); ``` ### 5.4 EnsureLoaded(meowpapi/EnsureLoaded.h) ```cpp bool meowpapi::ensureLoaded(); // 确保 DLL 已部署+初始化;线程安全,可多次调用 bool meowpapi::isDeployer(); // 本插件是否为部署者(第一个触发部署的插件) //===== 运行时 ABI 查询(须在 ensureLoaded() 之后调用)===== uint32_t meowpapi::loadedAbiVersion(); // 运行时 DLL 的 ABI 版本(旧 DLL 返回 0) uint32_t meowpapi::loadedAbiFeatures(); // 运行时 DLL 的功能位掩码(旧 DLL 返回 0) bool meowpapi::paramPapiSupported(); // 便捷判断:是否支持带参占位符 uint64_t meowpapi::loadedBuildTimestamp(); // 运行时 DLL 构建时间戳(PE TimeDateStamp) ``` 运行时 ABI 诊断示例(MeowSidebar 同款,enable 阶段打印一行即可秒判部署版本): ```cpp uint32_t rtAbi = meowpapi::loadedAbiVersion(); logger.info("MeowPAPI 运行时: ABI 0x{:06x}{} (编译期 0x{:06x})", rtAbi, meowpapi::paramPapiSupported() ? " 带参PAPI" : "", MEOWPAPI_ABI_VERSION); if (rtAbi > 0 && rtAbi < MEOWPAPI_ABI_VERSION) { logger.warn("运行中的 MeowPAPI 低于编译期版本,部分功能降级"); } ``` 生命周期约定: - `load()`:调 `ensureLoaded()`。 - `enable()`:直接使用 API。 - `disable()`:只清理自己的 RemoteCall 命名空间 (`RemoteCall::removeNameSpace("MyPlugin")`);仅部署者额外调用 `removeRemoteCallApi()` / `removeBepApiFallback()`。 --- ## 6. LSE (JS) API MeowPAPI 通过 RemoteCall 导出命名空间 `"MeowPAPI"`,全部 14 个函数: ### 6.1 注册占位符 注册流程固定两步:先 `ll.export` 导出回调,再 `ll.import` 注册。 ```js // ===== 服务器级(回调签名:() -> string)===== ll.export(() => { return "Hello"; }, "MyScript", "getHello"); ll.import("MeowPAPI", "registerServerPlaceholder")("MyScript", "my_hello", "MyScript", "getHello"); // ===== 玩家级(回调签名:(player) -> string)===== ll.export((player) => { return player.name + " 的称号"; }, "MyScript", "getTitle"); ll.import("MeowPAPI", "registerPlayerPlaceholder")("MyScript", "my_title", "MyScript", "getTitle"); // ===== 静态缓存级(回调签名:() -> string,结果按间隔缓存)===== ll.import("MeowPAPI", "registerStaticPlaceholder")( "MyScript", "my_stat", "MyScript", "getStat", 5000); // updateIntervalMs=5000 // ===== 带参服务器级(回调签名:(paramsJson: string) -> string)===== ll.export((paramsJson) => { const params = JSON.parse(paramsJson); // {"score":"zxsc","number":"10"} return "查询 " + params.score + " 榜前 " + params.number; }, "MyScript", "getRank"); ll.import("MeowPAPI", "registerServerPlaceholderWithParams")( "MyScript", "title_score_rank_<score>_<title>_<number>", "MyScript", "getRank"); // ===== 带参玩家级(回调签名:(player, paramsJson: string) -> string)===== ll.export((player, paramsJson) => { const params = JSON.parse(paramsJson); return player.name + ": " + params.kind; }, "MyScript", "getInfo"); ll.import("MeowPAPI", "registerPlayerPlaceholderWithParams")( "MyScript", "player_info_<kind>", "MyScript", "getInfo"); ``` 注册参数(四个注册函数通用): | 参数 | 含义 | |------|------| | `pluginName` | 注册归属插件名(按插件批量注销的依据) | | `papiName` | 占位符名,带参版可含 `<param>` 槽位模板 | | `callbackPluginName` | 回调所属 RemoteCall 命名空间(即 `ll.export` 的第二参数) | | `callbackFuncName` | 回调函数名(即 `ll.export` 的第三参数) | > LSE 回调目标函数被卸载(如脚本插件热重载/关服)时静默返回空串, > 目标恢复后占位符自动继续工作,不产生报错日志。 ### 6.2 查询与翻译 ```js // 翻译 ll.import("MeowPAPI", "translateString")("TPS: {server_tps}"); ll.import("MeowPAPI", "translateStringWithPlayer")("{pl.realName} 血量 {pl.health}", "Steve"); // 取值 ll.import("MeowPAPI", "GetValue")("server_tps"); ll.import("MeowPAPI", "GetValueWithPlayer")("pl.ping", "Steve"); // 检查 / 列表 ll.import("MeowPAPI", "hasPlaceholder")("server_tps"); // -> bool ll.import("MeowPAPI", "listPlaceholders")(); // -> JSON 数组字符串 // 注销 ll.import("MeowPAPI", "unRegisterPlaceholder")("my_title"); // -> bool ``` ### 6.3 ABI 与版本查询 ```js const abi = ll.import("MeowPAPI", "getAbiVersion")(); // 0x010100 = 65792 const feat = ll.import("MeowPAPI", "getAbiFeatures")(); // 功能位掩码 if (feat & 1) { /* 支持带参占位符(bit0 = MEOWPAPI_ABI_FEATURE_PARAMS) */ } const ver = ll.import("MeowPAPI", "getVersion")(); // "1.1.0"(版本字符串) ``` --- ## 7. 带参占位符(GMLIB PAPI 兼容) ### 7.1 翻译语法 ``` ${papi:<占位符名>[,key=value[,key=value...]]} ``` 完整示例(对应 §6.1 注册的 `title_score_rank_<score>_<title>_<number>`): ``` ${papi:title_score_rank_<score>_<title>_<number>,<score>=zxsc,<title>=在线榜,<number>=10} ``` 回调收到 `paramsJson`(**键双格式注入**——每个参数同时以带尖括号键与剥尖括号键存在): ```json {"score":"zxsc","title":"在线榜","number":"10","<score>":"zxsc","<title>":"在线榜","<number>":"10"} ``` > 双键设计兼容两种取值写法:`params.score`(剥尖括号)与 `params['<score>']` > (GMLIB 原版语义)。从 GMLIB 迁移的 JS 插件(回调里 `params['<number>']`) > 无需改动即可工作。 ### 7.2 槽位模板匹配 注册名含 `<param>` 时,翻译按 `_` 分段匹配实际占位符名提取参数: ``` 注册: title_score_rank_<score>_<title>_<number> 使用: ${papi:title_score_rank_zxsc_在线榜_10} → 槽位提取 score=zxsc, title=在线榜, number=10 ``` **参数合并优先级:显式参数 > 槽位参数**。两者同时存在时同名键以显式参数为准: ``` ${papi:title_score_rank_zxsc_在线榜_10,<number>=5} → score/title 来自槽位,number=5 来自显式参数 ``` ### 7.3 转义与嵌套 | 能力 | 写法 | 说明 | |------|------|------| | 逗号转义 | `\,` | 参数值中包含字面逗号 | | 等号转义 | `\=` | 参数值中包含字面等号 | | 值嵌套 | `${papi:a,<k>=${papi:b,...}}` | 值内嵌套表达式先递归翻译再作为参数值 | | 嵌套简单占位符 | `${papi:a,<k>={server_tps}}` | 嵌套非 papi 表达式按普通占位符解析 | | key 规范化 | `<score>` → `score` | 参数键自动去除 `<` `>` | 嵌套未命中时该嵌套表达式保留原文。非 `papi:` 前缀的 `${...}` 不被拦截, 按原有 `{name}` 逻辑处理。 ### 7.4 无参引用带参占位符 `%name%` / `{name}` 直接引用带参注册的占位符时走**空参数对象** `{}`, 槽位模板匹配照常提取参数(无需显式传参即可用槽位值): ``` 注册: player_info_<kind> 使用: {player_info_gold} → 槽位提取 kind=gold,回调收到 {"kind":"gold"} ``` > 勿在回调里假设 paramsJson 一定含特定键——`%name%` 无槽位引用时是 `{}`。 ### 7.5 通道汇总 带参注册共四个通道,行为一致: | 通道 | 入口 | |------|------| | C++ 静态库 | `PlaceholderApi::registerServer/PlayerPlaceholderWithParams` | | LSE | `ll.import("MeowPAPI", "registerServer/PlayerPlaceholderWithParams")` | | C ABI | `MeowPAPI_RegisterPlaceholderWithParams`(type: 0=Server / 1=Player) | | DLL 内部 | `PlaceholderRegistry::register*WithParams[Remote]` | --- ## 8. ABI 版本与功能位 ### 8.1 单一来源宏(include/meowpapi/DllExports.h) ```cpp #define MEOWPAPI_V_MAJOR 1 #define MEOWPAPI_V_MINOR 1 #define MEOWPAPI_V_PATCH 0 #define MEOWPAPI_ABI_VERSION ((MAJOR<<16)|(MINOR<<8)|PATCH) // 0x010100 #define MEOWPAPI_VERSION_STRING ... // "1.1.0" // 功能位掩码(add-only:只增位不改值) #define MEOWPAPI_ABI_FEATURE_PARAMS 0x00000001 // 带参占位符 ``` ### 8.2 查询通道 | 通道 | 调用 | 返回 | |------|------|------| | 编译期宏 | `MEOWPAPI_ABI_VERSION` | 消费者编译时的版本 | | C API | `MeowPAPI_GetAbiVersion()` / `MeowPAPI_GetAbiFeatures()` | DLL 运行时值 | | C++ | `PlaceholderApi::getAbiVersion()` / `isParamPapiSupported()` | 见 §4.5 | | C++ | `meowpapi::loadedAbiVersion()` 等(EnsureLoaded.h) | 见 §5.4 | | LSE | `ll.import("MeowPAPI","getAbiVersion")()` / `getAbiFeatures` / `getVersion` | 同 C API | | 游戏内 | `/meowpapi version` | 见 §14 | 另有 `MeowPAPI_GetBuildTimestamp()`:返回 PE TimeDateStamp(Unix 秒), 用于检测已部署 DLL 是否比嵌入版本旧。 ### 8.3 服务器启动时的自动版本广播 MeowPAPI 监听 `ServerStartedEvent`,开服完成时在日志自动广播一行版本摘要: ``` [MeowPAPI] MeowPAPI v1.1.0 (ABI 0x010100, build 2026-08-24 18:20) 已就绪: 42 个占位符, lrca已挂载(LSE可用) ``` 无需任何配置。与磁盘 DLL 的 LastWriteTime 对比即可秒判实际部署版本 (同 HologramLib 构建时间戳模式)。 ### 8.4 兼容性判断(推荐写法) ```cpp // 运行时 DLL 是否支持带参功能:功能位 + 导出符号双重判定(旧 DLL 自动降级) meowpapi::PlaceholderApi::getInstance().isParamPapiSupported(); ``` ```js const feat = ll.import("MeowPAPI", "getAbiFeatures")(); const ok = (feat & 1) !== 0; // bit0 = 带参 ``` ### 8.4 版本历史 | ABI | 版本 | 功能 | |-----|------|------| | 0x010000 | 1.0.0 | 注册 / 翻译 / 静态缓存 / RemoteCall 桥 | | 0x010100 | 1.1.0 | 带参占位符(GMLIB PAPI 兼容)、ABI 查询导出 | 旧版 DLL(<1.1.0)无 `GetAbiVersion` 等导出,`GetProcAddress` 返回空 → 视为版本 0 / 无功能位,消费者据此降级。 --- ## 9. 内置占位符全表 内置占位符在 `ensureLoaded()` 自动初始化时注册(移植自 CoralFans / BetterSidebar), 注册归属插件名为 `MeowSidebar`(历史原因)。**MT** = 仅主线程(mainThreadOnly)。 ### 9.1 服务器级(Static 缓存) | 占位符 | 含义 | 缓存 | 线程 | |--------|------|------|------| | `server_tps` | 服务器 TPS | 500ms | MT | | `server_mspt` | 每刻毫秒数 | 500ms | MT | | `server_online` | 当前在线人数 | 500ms | MT | | `server_max_players` | 最大玩家数 | 5000ms | MT | | `server_version` | 游戏版本 | 5000ms | 任意 | | `server_protocol_version` | 协议版本号 | 5000ms | 任意 | | `server_world_name` | 世界名 | 5000ms | MT | | `server_difficulty` | 难度 | 5000ms | MT | | `server_uptime` | 运行时长 | 1000ms | 任意 | | `server_ram_used` | 已用内存 | 1000ms | 任意 | | `server_ram_free` | 可用内存 | 1000ms | 任意 | | `server_ram_max` | 总内存 | 5000ms | 任意 | | `server_ram_bds_used` | BDS 进程内存 | 1000ms | 任意 | | `server_total_entities` | 已加载实体总数(玩家/生物/掉落物/弹射物等) | 1000ms | MT | | `server_total_chunks` | 已加载区块数 | 5000ms | 任意 | | `server_name` | 服务器名(server.properties) | 5000ms | 任意 | | `server_port` | IPv4 端口 | 5000ms | MT | | `server_port_v6` | IPv6 端口 | 5000ms | MT | | `server_on_allowlist` | 是否启用白名单 | 5000ms | MT | | `server_has_whitelist` | 是否存在白名单文件 | 5000ms | MT | ### 9.2 玩家级(全部 MT) | 占位符 | 含义 | |--------|------| | `pl.realName` | 玩家名 | | `pl.health` / `pl.maxHealth` | 当前 / 最大生命值 | | `pl.hunger` | 饥饿值 | | `pl.pos` | 坐标(x,y,z) | | `pl.posX` / `pl.posY` / `pl.posZ` | 单轴坐标 | | `pl.dimId` / `pl.dimName` | 维度 ID / 名称 | | `pl.expLevel` | 经验等级 | | `pl.gamemode` | 游戏模式 | | `pl.flying` | 是否飞行 | | `pl.isOp` | 是否 OP | | `pl.device` | 设备类型 | | `pl.ping` | 延迟(毫秒) | | `pl.ip` | IP 地址 | | `pl.uuid` / `pl.xuid` | UUID / XUID | | `pl.handItem` | 手持物品名 | | `pl.speed` | 移动速度 | | `pl.direction` | 朝向 | | `pl.money` | LLMoney 余额(未装返回 0) | | `pl.money_formatted` | LLMoney 余额(千分位格式) | ### 9.3 时间级(Static 缓存 1000ms,任意线程) | 占位符 | 含义 | |--------|------| | `date.h` / `date.m` / `date.s` | 时 / 分 / 秒 | | `date.D` / `date.M` / `date.Y` | 日 / 月 / 年 | | `date.W` | 星期 | --- ## 10. 线程模型 ### 10.1 mainThreadOnly 机制 访问非线程安全 API(Level / ProfilerLite / NetworkHandler / Player 内部状态)的 占位符回调必须以 `*Remote` 版本注册(`mainThreadOnly=true`)。后台线程翻译时 这些占位符被跳过并保留原文。 ### 10.2 两遍翻译(skipRemote) ``` 后台线程: translateStringWithPlayer(str, player, /*skipRemote=*/true) → mainThreadOnly 占位符保留原文 主线程: translateStringWithPlayer(str, player, /*skipRemote=*/false) → 全量翻译(含 mainThreadOnly 占位符) ``` 典型消费者(MeowSidebar):后台线程定期刷新非敏感占位符,主线程 flush 时 补全玩家级 / TPS 等敏感占位符——既不阻塞又避免数据竞争拖慢 TPS。 ### 10.3 线程安全总结 | 操作 | 线程安全性 | |------|-----------| | `ensureLoaded()` / `isDeployer()` | 线程安全,可并发 | | 注册 / 注销 | 应在插件生命周期回调(主线程)执行 | | 翻译 / 取值 | 可跨线程;`mainThreadOnly` 占位符需主线程补翻 | | LSE 注册的占位符 | 一律 mainThreadOnly(LSE 引擎单线程) | --- ## 11. C ABI 参考(DllExports) `MeowPAPI.dll` 导出的 C 接口。**通常只有静态库 wrapper 使用**;消费者一律走 C++ / LSE 层。完整列表见 `include/meowpapi/DllExports.h`,速查: ```c // 版本 const char* MeowPAPI_GetVersion(void); uint32_t MeowPAPI_GetAbiVersion(void); uint32_t MeowPAPI_GetAbiFeatures(void); uint64_t MeowPAPI_GetBuildTimestamp(void); // 初始化(autoInit 已含,勿手动调用) void MeowPAPI_InitAsServer(void); void MeowPAPI_InitAsClient(void); // 回调调用器(wrapper 内部使用) void MeowPAPI_SetCallbackInvoker(MeowPAPI_CallbackFn fn); void MeowPAPI_SetCallbackInvokerWithParams(MeowPAPI_CallbackWithParamsFn fn); // 翻译 / 取值 / 检查(返回结果长度;out=NULL 时返回所需长度) int MeowPAPI_TranslateString(const char* str, char* out, int outSize); int MeowPAPI_TranslateStringWithPlayer(const char* str, void* player, char* out, int outSize); int MeowPAPI_TranslateStringWithPlayerSkip(const char* str, void* player, int skipRemote, char* out, int outSize); int MeowPAPI_GetValue(const char* name, char* out, int outSize); int MeowPAPI_GetValueWithPlayer(const char* name, void* player, char* out, int outSize); int MeowPAPI_HasPlaceholder(const char* name); // 注册(type: 0=Server 1=Player 2=Static;返回 1=成功) int MeowPAPI_RegisterPlaceholder(const char* pluginName, const char* name, int type, uint64_t callbackId, int updateIntervalMs); int MeowPAPI_RegisterPlaceholderWithParams(const char* pluginName, const char* name, int type, uint64_t callbackId, int updateIntervalMs); int MeowPAPI_UnregisterPlaceholder(const char* name); void MeowPAPI_UnregisterByPlugin(const char* pluginName); void MeowPAPI_Clear(void); // 列表(JSON 数组字符串) int MeowPAPI_ListPlaceholders(char* out, int outSize); int MeowPAPI_ListPlaceholdersByPlugin(const char* pluginName, char* out, int outSize); // RemoteCall / 内置 / BEPAPI(autoInit 已含) void MeowPAPI_ExportRemoteCallApi(void); void MeowPAPI_RemoveRemoteCallApi(void); void MeowPAPI_RegisterBuiltinPlaceholders(void); int MeowPAPI_IsBepApiAvailable(void); void MeowPAPI_InstallBepApiFallback(void); void MeowPAPI_RemoveBepApiFallback(void); // 远程客户端桥接 int MeowPAPI_IsRemoteAvailable(void); int MeowPAPI_RemoteTranslateString(const char* str, char* out, int outSize); int MeowPAPI_RemoteTranslateStringWithPlayer(const char* str, const char* playerName, char* out, int outSize); int MeowPAPI_RemoteGetValue(const char* name, char* out, int outSize); int MeowPAPI_RemoteGetValueWithPlayer(const char* name, const char* playerName, char* out, int outSize); int MeowPAPI_RemoteHasPlaceholder(const char* name); ``` 约束:单次结果最大 `MEOWPAPI_MAX_RESULT`(8192)字节。 --- ## 12. RemoteCall 代理(RemoteCallAPI.h) 消费者插件需要 `RemoteCall::exportAs / importAs` 能力但**不想链接 legacyremotecall** 时,用 MeowPAPI 头文件替代原版: ```cpp // 替代 #include <RemoteCallAPI.h> #include "meowpapi/RemoteCallAPI.h" // 用法与原版完全一致: RemoteCall::exportAs("MyPlugin", "myFunc", []() -> bool { return true; }); auto fn = RemoteCall::importAs<int(std::string const&)>("NS", "func"); ``` 机制:编译消费者插件时(未定义 `MEOWPAPI_DLL_EXPORTS`)走**代理模式**—— `exportAs / importAs` 等经函数指针转发到 MeowPAPI.dll 内的 `RC_*` 代理, 由 MeowPAPI.dll 统一与 lrca 通信。函数指针在 `ensureLoaded()` 时经 `GetProcAddress` 初始化。调用顺序要求:先 `ensureLoaded()` 再使用 RemoteCall 代理。 --- ## 13. 错误处理与常见问题 ### 13.1 输出标记 | 输出 | 含义 | 处理建议 | |------|------|----------| | `{name}` 原样保留 | 占位符未注册,或 mainThreadOnly 被后台线程跳过 | 主线程二遍翻译;检查名字拼写 | | `{ERR}` | 回调抛异常 / 回调调用器缺失 / 无参引用带参 entry 走了空回调路径 | 检查回调实现;带参注册勿假设必有参数 | | 空串 | 远程回调目标已被卸载(热重载/关服) | 属正常静默降级,目标恢复后自愈 | ### 13.2 常见问题 **Q:`registerServerPlaceholder` 返回 false?** 同名占位符已存在(内置占位符归属 `MeowSidebar`,见 §9)。换名或先 `unregisterPlaceholder`。 **Q:注册的占位符在侧边栏不刷新?** 静态占位符按 `updateIntervalMs` 缓存;LSE 注册的回调一律 mainThreadOnly,后台线程刷新周期内保留原文属正常现象。 **Q:`%name%` 引用带参占位符输出 `{ERR}`?** 1.1.0 已修复(无参引用自动传 `{}` 走带参路径)。若仍出现,说明运行的是 旧版 DLL——用 `getAbiVersion()` 核实(应 ≥ 0x010100),并重新部署 `plugins/MeowPAPI/MeowPAPI.dll`(构建产物在 `build/windows/x64/release/`, 须复制到插件根目录才算部署)。 **Q:多消费者插件会重复部署 DLL 吗?** 不会。`std::call_once` 保证首个消费者部署一次,其余直接连接已加载实例。 **Q:消费者 disable 时该清理什么?** 只清理自己的 RemoteCall 命名空间(`RemoteCall::removeNameSpace("MyPlugin")`) 和按插件注销占位符(`unregisterByPlugin`);全局清理仅部署者执行 (`isDeployer()` 判断)。 **Q:消费者只 include 了 RemoteCallAPI.h、没调 ensureLoaded,程序行为异常?** 静态库可能被 `/OPT:REF` 丢弃导致代理指针为空。头文件已用 `#pragma comment(linker, "/include:...")` 强制拉入,但仍应在 `load()` 里 显式调用 `meowpapi::ensureLoaded()` 完成同步初始化。 --- ## 14. 游戏内指令(/meowpapi) MeowPAPI 作为独立插件注册游戏内指令,全部子命令游戏内聊天栏输出: | 命令 | 权限 | 说明 | |------|------|------| | `/meowpapi` | 所有玩家 | 帮助(含版本摘要) | | `/meowpapi version` | 所有玩家 | 版本信息:版本字符串、ABI、构建时间、功能、lrca 挂载状态、占位符总数 | | `/meowpapi list [页码]` | 仅 OP(execute 内自检) | 分页浏览占位符列表(每页 8 条,含类型/来源插件/带参标记) | 示例输出: ``` ===== MeowPAPI ===== 版本: 1.1.0 (ABI 0x010100) 构建: 2026-08-24 18:20 功能: 带参占位符(GMLIB PAPI 兼容) lrca: 已挂载 (LSE 可用) 占位符: 42 个 (/meowpapi list 查看) ``` ``` ===== 占位符列表 [1/6]===== 42 个 #1 mspt [Static] MeowSidebar #2 pl.health [Player] MeowSidebar ... 下一页: /meowpapi list 2 ``` > 命令注册为 `CommandPermissionLevel::Any` + execute 内 > `getCommandPermissionLevel() >= 1` 自检 OP(避免 Bedrock 客户端对 > GameDirectors 命令的本地隐藏问题,MeowMenu 同款方案)。 > `version` 对所有玩家开放(版本信息无害),`list` 仅 OP。 [/MD]
作者
伊希娅
下载
8
查看
158
首次发布
最后更新

评分

0.00 星 0 次评分

伊希娅 的其他资源

最新更新

  1. 架构更新

    独立出来了,并且您可无需从此处下载,直接下载MeowSidebar,DebugShape-Protocol等依赖MeowPapi的插件,他们会自动释放最新版本帮你进行更新
  2. 修复MeowPAPI的生物总数获取bug

    Meowsidebar上写明了原因了
后退
顶部 底部