[MD]
# 很遗憾,此插件于此归档,后续将会移动到HologramLib为后续库使用
## HologramLib为协议层完成的就虚假方块/悬浮字/原生渲染图形框 等服务端渲染内容使用
# DebugShape-Protocol 技术文档
## 一、项目定位
DebugShape-Protocol 是一个**纯协议层实现**的 Minecraft Bedrock Edition DebugShape 服务端插件。
与传统的 DebugShape 插件不同,本插件**完全自主实现数据包的序列化**,仅借用 BDS 底层的 `NetworkPeer::sendPacket` 通道发送原始字节流,不依赖 BDS 的 `DebugDrawerPacket`、`ShapeDataPayload`、`cereal` 反射系统等任何上层协议封装。
本插件已集成 **MeowPAPI** 静态库,FloatingText 文本支持 `%name%` 和 `{name}` 两种格式的 PAPI 占位符翻译(通过 RemoteCall 调用 MeowSidebar 的 PAPI 中心)。
### 核心设计准则
> **极致化的优化非必要的跳转,达到性能优化到极致。**
- 序列化层:完全自主实现,零 BDS 依赖
- 数据结构:完全自主定义,零 BDS 依赖
- 发送层:仅借用 `NetworkPeer::sendPacket` 原始字节流发送
- 占位符翻译:集成 MeowPAPI 静态库,复用 MeowSidebar 的 PAPI 注册中心
前方案** |
## 二、架构设计
### 整体架构
```
┌─────────────────────────────────────────────────────────┐
│ 应用层 (LSE API) │
│ FloatingTextManager / GradientLineManager / Exporters │
├─────────────────────────────────────────────────────────┤
│ 占位符翻译层 (MeowPAPI 静态库) │
│ PlaceholderApi → RemoteCall → MeowSidebar PAPI 中心 │
│ (仅 FloatingText 文本路径经过此层) │
├─────────────────────────────────────────────────────────┤
│ 形状管理层 │
│ PacketDebugRenderer │
│ (ShapeData 持有 ProtoShape) │
├─────────────────────────────────────────────────────────┤
│ 协议层 (纯自主实现) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ProtocolStream│ │ProtocolShape │ │ProtocolPackets │ │
│ │ (二进制流) │ │ (形状结构) │ │ (包构造+发送) │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ BDS 底层网络通道 (仅借用) │
│ NetworkPeer::sendPacket() │
└─────────────────────────────────────────────────────────┘
```
### 文件结构
```
src/
├── ProtocolStream.h # 纯协议层二进制写入流
├── ProtocolShape.h # 纯协议层形状数据结构 + 序列化
├── ProtocolPackets.h # 协议层数据包构造与发送声明
├── ProtocolPackets.cpp # 协议层数据包构造与发送实现
├── PacketDebugRenderer.h # 形状管理器
├── PacketDebugRenderer.cpp # 形状管理器实现
├── FloatingTextManager.h # 悬浮文字管理器
├── FloatingTextManager.cpp # 悬浮文字管理器实现 (集成 MeowPAPI 翻译)
├── GradientLineManager.h # 渐变线段管理器
├── GradientLineManager.cpp # 渐变线段管理器实现
├── RemoteCallExporter.cpp # LSE RemoteCall 导出 (DebugShape 命名空间)
├── FloatingTextExporter.cpp # LSE RemoteCall 导出 (FloatingText 命名空间)
├── GradientLineExporter.cpp # LSE RemoteCall 导出 (GradientLine 命名空间)
├── ModEntry.cpp # 插件入口 (初始化 MeowPAPI 客户端模式)
└── MemoryOperators.cpp # 内存操作符
../MeowPAPI/ # MeowPAPI 静态库 (通过 add_deps 链接)
├── include/meowpapi/ # 公开头文件
│ ├── PlaceholderApi.h # 统一 PAPI 入口 (自动路由服务端/客户端)
│ ├── PlaceholderRegistry.h # 占位符注册表核心
│ ├── Builtins.h # 内置原生占位符
│ ├── RemoteCallBridge.h # RemoteCall 桥接层
│ └── BepApiBridge.h # BEPlaceholderAPI 双向兼容层
└── src/ # 实现文件 (随主插件一起编译)
```
## 三、协议层实现
### 3.1 ProtocolStream - 二进制写入流
`ProtocolStream` 是完全自主实现的 Minecraft Bedrock 协议二进制写入流,提供基础数据类型的写入能力。
**核心方法:**
| 方法 | 说明 | 编码格式 |
|------|------|---------|
| `writeVarUInt(uint64_t)` | 变长无符号整数 | 7bit + 1bit continuation |
| `writeVarInt(int64_t)` | 变长有符号整数 | ZigZag + VarUInt |
| `writeFloat(float)` | 单精度浮点 | 4字节小端 |
| `writeByte(uint8_t)` | 单字节 | 原始字节 |
| `writeString(string)` | 字符串 | VarUInt(长度) + 字节 |
| `writeBytes(void*, size_t)` | 原始字节 | 原始字节 |
**辅助类型:**
- `ProtoVec3` - 三维向量 (x, y, z float)
- `ProtoColor` - RGBA 颜色 (4字节)
### 3.2 ProtocolShape - 形状数据结构
#### 形状类型枚举
```cpp
enum class ProtoShapeType : uint8_t {
Box = 1,
Sphere = 2,
Circle = 3,
Text = 4,
Arrow = 5,
};
```
> **注意:** MC 没有 Line 类型,Line 使用 Arrow 类型实现。
#### 额外数据载荷 (variant)
```cpp
using ProtoExtraData = std::variant<
ProtoNullPayload, // index 0 - NullType (Sphere/Circle 使用)
ProtoArrowPayload, // index 1 - ArrowData
ProtoTextPayload, // index 2 - TextData
ProtoBoxPayload, // index 3 - BoxData
ProtoLinePayload, // index 4 - LineData
ProtoSpherePayload // index 5 - SphereData (未使用)
>;
```
#### ProtoShape 结构
```cpp
struct ProtoShape {
uint64_t mNetworkId; // 网络ID
std::optional<ProtoShapeType> mShapeType; // 形状类型
std::optional<ProtoVec3> mLocation; // 位置
std::optional<ProtoVec3> mRotation; // 旋转
std::optional<float> mScale; // 缩放
std::optional<ProtoColor> mColor; // 颜色
std::optional<float> mTimeLeftTotalSec;// 剩余时间
std::optional<int32_t> mDimensionId; // 维度ID (VarInt)
std::optional<uint64_t> mAttachedToId; // 附加实体ID
ProtoExtraData mExtraData; // 额外数据
};
```
### 3.3 序列化格式
#### 完整数据包格式
```
┌──────────────────────────────────────────────────────────┐
│ VarUInt(packetId=328) # 包头:数据包ID │
│ VarUInt(shapeCount) # 形状数量 │
│ [Shape] * shapeCount # 形状数据 │
└──────────────────────────────────────────────────────────┘
```
#### 单个 Shape 序列化格式
```
┌──────────────────────────────────────────────────────────┐
│ VarUInt(mNetworkId) # 网络ID │
│ # 注意:无object start│
│ Optional(mShapeType) # 形状类型 │
│ Optional(mLocation) # 位置 │
│ Optional(mRotation) # 旋转 │
│ Optional(mScale) # 缩放 │
│ Optional(mColor) # 颜色 │
│ Optional(mTimeLeftTotalSec) # 剩余时间 │
│ Optional(mDimensionId, VarInt) # 维度ID │
│ Optional(mAttachedToId, VarUInt) # 附加实体ID │
│ VarUInt(variantIndex) # variant索引 │
│ [ExtraData] # 额外数据 │
└──────────────────────────────────────────────────────────┘
```
#### Optional<T> 编码
```
absent: 0x00
present: 0x01 + value
```
#### 各类型 ExtraData 编码
| variant index | 类型 | 数据格式 |
|---------------|------|---------|
| 0 | NullType | 无数据 |
| 1 | Arrow | Optional(mEndLocation) + Optional(mArrowHeadLength) + Optional(mArrowHeadRadius) + Optional(mNumSegments) |
| 2 | Text | String(mText) |
| 3 | Box | Vec3(mBoxBound) |
| 4 | Line | Vec3(mEndLocation) |
| 5 | Sphere | uint8(mNumSegments) |
### 3.4 ProtocolPackets - 数据包发送
**发送链路:**
```
ProtoShape[] → ProtocolStream 序列化 → std::string 字节流
→ ll::service::getNetworkSystem()
→ getPeerForUser(networkIdentifier)
→ NetworkPeer::sendPacket(data, ReliableOrdered, Compressible)
```
**发送方法:**
| 方法 | 说明 |
|------|------|
| `sendToPlayer(player, shapes)` | 发送到指定玩家 |
| `sendToAll(shapes)` | 发送到所有玩家 |
| `sendToDimension(dimId, shapes)` | 发送到指定维度 |
## 四、LSE API 文档
本插件通过 LegacyRemoteCall 导出三个命名空间的 API 供 LSE 脚本调用。
### 4.1 导入方式
```javascript
// 导入函数
const func = ll.import("命名空间", "函数名");
```
### 4.2 DebugShape 命名空间 - 基础形状 API
#### 创建形状
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `createText` | (float x, float y, float z, string text) | int64 (shapeId) | 创建文本形状 |
| `createLine` | (float x1, float y1, float z1, float x2, float y2, float z2) | int64 | 创建线段(使用 Arrow 类型) |
| `createBox` | (float x1, float y1, float z1, float x2, float y2, float z2) | int64 | 创建方块 |
| `createCircle` | (float x, float y, float z, float scale) | int64 | 创建圆圈 |
| `createSphere` | (float x, float y, float z, float scale) | int64 | 创建球体 |
| `createArrow` | (float x1, float y1, float z1, float x2, float y2, float z2) | int64 | 创建箭头 |
| `createFilledQuad` | (float x, float y, float z, float width, float height, int plane) | int64 | 创建填充面(plane: 0=XY, 1=XZ, 2=YZ) |
| `createFilledQuadBatch` | (float[] positions, float[] colors, float width, float height, int plane) | int64[] | 批量创建填充面 |
#### 属性设置
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `setText` | (int64 id, string text) | bool | 设置文本内容 |
| `getText` | (int64 id) | string | 获取文本内容 |
| `setLocation` | (int64 id, float x, float y, float z) | bool | 设置位置 |
| `getLocation` | (int64 id) | float[3] | 获取位置 [x, y, z] |
| `setColor` | (int64 id, float r, float g, float b, float a) | bool | 设置颜色 (0.0~1.0) |
| `getColor` | (int64 id) | float[4] | 获取颜色 [r, g, b, a] |
| `setScale` | (int64 id, float scale) | bool | 设置缩放 |
| `setDuration` | (int64 id, float seconds) | bool | 设置持续时间 |
| `setRotation` | (int64 id, float pitch, float yaw, float roll) | bool | 设置固定朝向(弧度) |
| `clearRotation` | (int64 id) | bool | 清除旋转,恢复 billboard 模式 |
| `getRotation` | (int64 id) | float[3] | 获取旋转 [pitch, yaw, roll],billboard 模式返回空数组 |
| `getShapeType` | (int64 id) | int | 获取形状类型 |
#### 显示控制
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `draw` | (int64 id) | bool | 绘制到所有玩家 |
| `drawToPlayer` | (int64 id, string playerName) | bool | 绘制到指定玩家 |
| `drawToDimension` | (int64 id, int dimId) | bool | 绘制到指定维度 |
| `remove` | (int64 id) | bool | 移除显示(所有玩家) |
| `removeToPlayer` | (int64 id, string playerName) | bool | 移除指定玩家的显示 |
| `removeToDimension` | (int64 id, int dimId) | bool | 移除指定维度的显示 |
| `update` | (int64 id) | bool | 更新显示(属性变更后调用) |
| `updateToPlayer` | (int64 id, string playerName) | bool | 更新指定玩家的显示 |
| `updateToDimension` | (int64 id, int dimId) | bool | 更新指定维度的显示 |
| `drawBatch` | (int64[] ids) | bool | 批量绘制 |
#### 生命周期
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `destroy` | (int64 id) | bool | 销毁形状 |
| `destroyAll` | () | void | 销毁所有形状 |
| `destroyBatch` | (int64[] ids) | bool | 批量销毁 |
#### 查询
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `findTextByLocation` | (float x, float y, float z, float radius) | int64[] | 根据位置查找文本形状 |
| `findTextByLocationAndContent` | (float x, float y, float z, float radius, string text) | int64 | 根据位置和内容查找 |
| `getAllShapeIds` | () | int64[] | 获取所有形状ID |
| `exists` | (int64 id) | bool | 检查形状是否存在 |
### 5.3 FloatingText 命名空间 - 悬浮文字 API
> **占位符支持**:FloatingText 的所有文本参数(`addLine`、`setLineText`)均支持 PAPI 占位符翻译。
> 文本中的 `%name%` 和 `{name}` 会被 MeowPAPI 自动替换为对应占位符的值(需 MeowSidebar 加载并注册占位符)。
> 同时保留内置变量 `{time}`/`{online}`/`{player}`/`{tps}` 作为兜底,PAPI 未注册时仍可用。
> 详见 [六、MeowPAPI 占位符翻译](#六meowpapi-占位符翻译)。
#### 创建与销毁
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `create` | (float x, float y, float z) | int64 | 创建悬浮文字 |
| `destroy` | (int64 id) | bool | 销毁悬浮文字 |
| `destroyAll` | () | void | 销毁所有悬浮文字 |
#### 行管理
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `addLine` | (int64 id, string text) | bool | 添加一行 |
| `setLineText` | (int64 id, int lineIndex, string text) | bool | 设置指定行文本 |
| `setLineScale` | (int64 id, int lineIndex, float scale) | bool | 设置指定行缩放 |
| `removeLine` | (int64 id, int lineIndex) | bool | 移除指定行 |
| `clearLines` | (int64 id) | bool | 清空所有行 |
| `getLineCount` | (int64 id) | int | 获取行数 |
#### 颜色设置
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `setColor` | (int64 id, float r, float g, float b, float a) | bool | 设置整体颜色 (0.0~1.0) |
| `setLineColor` | (int64 id, int lineIndex, float r, float g, float b, float a) | bool | 设置单行纯色 |
| `setLineGradient` | (int64 id, int lineIndex, float r1, float g1, float b1, float r2, float g2, float b2) | bool | 设置单行渐变 |
| `setLineRainbow` | (int64 id, int lineIndex, float speed) | bool | 设置单行彩虹效果 |
#### 动画
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `setLineScroll` | (int64 id, int lineIndex, int direction, float speed) | bool | 设置行滚动 (direction: 0=无, 1=左, 2=右) |
| `setVerticalAnimation` | (int64 id, int type, float speed, float range) | bool | 设置垂直动画 (type: 0=无, 1=弹跳, 2=滚动) |
| `setLineSpacing` | (int64 id, float spacing) | bool | 设置行间距 |
| `setLocation` | (int64 id, float x, float y, float z) | bool | 设置位置 |
| `setFollowPlayer` | (int64 id, string playerName, float offsetY) | bool | 跟随玩家 |
| `clearFollowPlayer` | (int64 id) | bool | 取消跟随 |
| `tick` | (float deltaTime) | void | 更新动画(应在游戏tick中调用) |
#### 显示控制
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `draw` | (int64 id) | bool | 绘制到所有玩家 |
| `drawToDimension` | (int64 id, int dimId) | bool | 绘制到指定维度 |
| `drawToPlayer` | (int64 id, string playerName) | bool | 绘制到指定玩家 |
| `remove` | (int64 id) | bool | 移除显示 |
| `refresh` | (int64 id) | bool | 刷新显示 |
### 5.4 GradientLine 命名空间 - 渐变线段 API
#### 创建与销毁
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `create` | (float x1, float y1, float z1, float x2, float y2, float z2, int segments) | int64 | 创建渐变线段 |
| `destroy` | (int64 id) | bool | 销毁线段 |
| `destroyAll` | () | void | 销毁所有线段 |
#### 属性设置
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `setGradient` | (int64 id, float r1, float g1, float b1, float r2, float g2, float b2) | bool | 设置渐变色 (0.0~1.0) |
| `setRainbow` | (int64 id, float speed) | bool | 设置彩虹效果 |
| `setColor` | (int64 id, float r, float g, float b, float a) | bool | 设置纯色 |
| `setEndpoints` | (int64 id, float x1, float y1, float z1, float x2, float y2, float z2) | bool | 设置端点 |
#### 显示控制
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `draw` | (int64 id) | bool | 绘制到所有玩家 |
| `drawToDimension` | (int64 id, int dimId) | bool | 绘制到指定维度 |
| `remove` | (int64 id) | bool | 移除显示 |
#### 动画
| 函数名 | 参数 | 返回值 | 说明 |
|--------|------|--------|------|
| `tick` | (float deltaTime) | void | 更新动画(应在游戏tick中调用) |
### 4.5 LSE 调用示例
```javascript
// DebugShape 命名空间
const createText = ll.import("DebugShape", "createText");
const createBox = ll.import("DebugShape", "createBox");
const createSphere = ll.import("DebugShape", "createSphere");
const setColor = ll.import("DebugShape", "setColor");
const draw = ll.import("DebugShape", "draw");
const destroy = ll.import("DebugShape", "destroy");
// 创建文本
const textId = createText(100, 64, 0, "Hello World");
draw(textId);
// 创建红色方块
const boxId = createBox(100, 64, 0, 102, 66, 2);
setColor(boxId, 1.0, 0.0, 0.0, 1.0);
draw(boxId);
// 创建绿色球体
const sphereId = createSphere(100, 70, 0, 1.0);
setColor(sphereId, 0.0, 1.0, 0.0, 1.0);
draw(sphereId);
// 销毁
destroy(textId);
destroy(boxId);
destroy(sphereId);
// FloatingText 命名空间
const ftCreate = ll.import("FloatingText", "create");
const ftAddLine = ll.import("FloatingText", "addLine");
const ftSetLineColor = ll.import("FloatingText", "setLineColor");
const ftDraw = ll.import("FloatingText", "draw");
const ftDestroy = ll.import("FloatingText", "destroy");
// 创建多行悬浮文字
const ftId = ftCreate(100, 80, 0);
ftAddLine(ftId, "第一行");
ftAddLine(ftId, "第二行");
ftSetLineColor(ftId, 0, 1.0, 0.0, 0.0, 1.0); // 第一行红色
ftSetLineColor(ftId, 1, 0.0, 1.0, 0.0, 1.0); // 第二行绿色
ftDraw(ftId);
// 销毁
ftDestroy(ftId);
// GradientLine 命名空间
const glCreate = ll.import("GradientLine", "create");
const glSetGradient = ll.import("GradientLine", "setGradient");
const glDraw = ll.import("GradientLine", "draw");
const glDestroy = ll.import("GradientLine", "destroy");
// 创建渐变线段
const glId = glCreate(100, 64, 0, 110, 70, 0, 16);
glSetGradient(glId, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0); // 红到蓝
glDraw(glId);
// 销毁
glDestroy(glId);
// FloatingText + MeowPAPI 占位符示例
// 需 MeowSidebar 加载以提供完整 PAPI 服务;未加载时 {time}/{online} 等内置变量仍可兜底
const ftId2 = ftCreate(100, 90, 0);
ftAddLine(ftId2, "欢迎 %player_name% 来到服务器"); // %name% BEPAPI 格式
ftAddLine(ftId2, "当前在线: {online} 人"); // {name} BetterSidebar 格式 (内置兜底)
ftAddLine(ftId2, "时间: {time}"); // 内置兜底变量
ftAddLine(ftId2, "TPS: {%server_tps%} MSPT: {%server_mspt%}"); // MeowSidebar 内置 PAPI
ftSetFollowPlayer(ftId2, "Steve", 2.0); // 跟随玩家(提供 Player 上下文)
ftDraw(ftId2);
```
## 五、MeowPAPI 占位符翻译
### 5.1 集成方式
DebugShape-Protocol 通过 xmake.lua 静态链接 MeowPAPI 静态库:
```lua
-- xmake.lua
includes("../MeowPAPI/xmake.lua")
target("DebugShape-Protocol")
add_deps("MeowPAPI") -- 链接静态库
add_includedirs("../MeowPAPI/include") -- 暴露 meowpapi/ 头文件
```
插件入口 `ModEntry::enable()` 中初始化为**客户端模式**:
```cpp
// ModEntry.cpp
#include "meowpapi/PlaceholderApi.h"
bool ModEntry::enable() {
// 客户端模式:通过 RemoteCall 调用 MeowSidebar 的 PAPI 中心
meowpapi::PlaceholderApi::getInstance().initAsClient();
// ...
}
```
### 5.2 工作原理
```
FloatingTextManager::processVariables(text, playerName)
│
├─ 1. 通过 Level 查找 Player* (若 followPlayer 非空)
│
├─ 2. meowpapi::PlaceholderApi::translateStringWithPlayer(text, player)
│ │
│ ├─ 客户端模式 → RemoteCall 调用 MeowSidebar
│ │ ├─ MeowSidebar 本地注册表
│ │ └─ BEPAPI 回退解析器 (若加载)
│ │
│ └─ 未注册的占位符保留原样 {%name%} / {{name}}
│
└─ 3. 内置变量兜底替换: {time} / {online} / {player} / {tps}
(仅当 PAPI 未注册这些占位符时生效)
```
### 5.3 支持的占位符格式
| 格式 | 示例 | 说明 |
|------|------|------|
| `%name%` | `%server_name%` | BEPAPI 经典格式,纯占位符名(不含空格/特殊字符) |
| `{name}` | `{player_name}` | BetterSidebar 格式,不含冒号或空格 |
| `{js:expr}` | `{js:Date.now()}` | **不处理**,保留原样(含冒号) |
| `{E:name}` | `{E:money}` | **不处理**,保留原样(含冒号) |
### 5.4 内置兜底变量
以下变量由 FloatingTextManager 自身提供,当 MeowSidebar 未加载或未注册同名占位符时生效:
| 变量名 | 格式 | 示例值 | 说明 |
|--------|------|--------|------|
| `time` | `{time}` | `14:25:36` | 当前本地时间 (HH:MM:SS) |
| `online` | `{online}` | `12` | 当前在线玩家数 |
| `player` | `{player}` | `Steve` | 玩家名(取自 followPlayer 上下文) |
| `tps` | `{tps}` | `20.0` | TPS(简化实现,固定 20.0) |
### 5.5 占位符来源
通过 MeowPAPI 客户端模式可访问的占位符来源:
| 来源 | 说明 |
|------|------|
| **MeowSidebar 内置** | 由 `meowpapi::registerBuiltinPlaceholders()` 注册(MSPT/TPS/时间/玩家等) |
| **LSE 插件注册** | LSE 脚本通过 `ll.import("MeowPAPI", "registerPlayerPlaceholder")` 等注册 |
| **C++ 插件注册** | 其他 C++ 插件通过 `meowpapi::PlaceholderApi::registerXxxPlaceholder` 注册 |
| **BEPAPI 兼容** | 若 MeowSidebar 检测到 BEPAPI 加载,会安装回退解析器访问 BEPAPI 占位符 |
### 5.6 依赖关系
| 依赖 | 类型 | 说明 |
|------|------|------|
| `MeowPAPI` | 静态库(编译期) | 通过 `add_deps` 链接,随主插件一起编译 |
| `MeowSidebar` | 软依赖(运行时) | PAPI 中心,未加载时占位符翻译降级为内置变量兜底 |
| `LegacyRemoteCall` | 必需依赖 | MeowPAPI 客户端模式通过 RemoteCall 调用 MeowSidebar |
> **注意**:MeowSidebar 未加载时,`isRemoteAvailable()` 返回 false,PAPI 翻译返回原字符串,
> FloatingText 的内置变量兜底仍可正常工作,不影响基本功能。
[/MD]