- 版权类型
- 原创
- 版权链接
- #
- 前置组件
- Hologramlib https://github.com/Ieckseyr/HologramLib
- 适配版本(基岩)
- 全版本
[MD]
# Disguise(管理员伪装面板)
独立的 LSE(qjs)脚本包:基于 HologramLib 的客户端视图覆盖,管理员可以把玩家、生物在**客户端上**显示成另一种样子。
一条铁律:**只改客户端看到的东西**。服务端那边实体还是原来的实体 —— 血量、伤害、掉落、交互、权限、背包、位置全部照旧,变的只是"别人屏幕上看到什么"。
- 入口:`/disguise`(权限:GameMasters)
- 存档:`plugins/Disguise/rules.json`
- 依赖:HologramLib(带逐字段导出的版本)、legacy-script-engine-quickjs
## 一、装法
1. 整个 `Disguise` 文件夹放进服务器的 `plugins/` 下(和别的 LSE 脚本并列)。
2. 确认服务器上的 HologramLib 是带逐字段导出的版本(26.40.7 分支的修复提交 ba6a248 之后)。
3. 起服;已经在跑的话 `Reload mod Disguise` 即可(脚本改动热重载就够,**换库必须重启服务器**,DLL 是进程内加载的)。
4. 进服敲 `/disguise`。
启动时日志会打两行:
```
[Disguise] v2.6 启动: PERSIST=true, 存档已读到: 规则 1 条 / 自己 0 个 / 指定实体 0 个
[Disguise] 扫描器已启动: 类型规则 1 条, 每 1000ms 扫一遍
```
如果打出 `HologramLib 未就绪, 缺导出: ...`,说明挂的还是旧库;缺哪些导出会列出来。这种情况下面板仍能打开,但设置伪装会提示"没就绪",换库重启即可。
## 二、面板六个功能
### 1. 变成生物
选自己变成哪种生物,**所有人**都看到你是那只生物。
服务端仍然认你是玩家:别人打"那头牛"其实是打你,按你的真身结算伤害;你的背包、权限、移动、视角都是你自己的(第一人称看自己也还是你自己)。
别人看到的那只生物没有名字牌(原版的玩家名字牌不会跟过来)。
### 2. 伪装成某个玩家
从在线玩家列表里选一个人,**借他的皮肤**:所有人看到你的皮肤变成他的样子。
名字牌还是你自己的名字(库发的皮肤更新包用的是你自己的 UUID 和名字,只换皮肤数据)。这条可随时撤销,撤销是立刻生效的。
### 3. 让别人变成生物
先选一名在线玩家,再填生物类型:所有人看到他就是那只生物。
做法是把他的真身从客户端上移除、用**同一个 runtimeId / uniqueId** 发一只该类型的实体,所以服务端一切照旧 —— 打他仍然按真身结算,他照常掉血、照常死亡掉落,只是打的时候客户端上看到的是那只生物。
### 4. 查看当前伪装
一次给你三行:
- 你自己的伪装(变成什么 / 伪装成谁 / 无)
- 类型规则几条、指定实体几个、库内计数(`entities=N blocks=M`,库自己记账的覆盖条数)
- 规则套用情况:定时扫描套了几只、生成事件(自然生成)瞬时套了几只、上次扫描拿到多少实体
最后这一行是排查"规则为什么没生效"用的,见第六节。
### 5. 卸下伪装
- **卸下我自己的**:清掉自己的伪装(生物形态 / 借的皮肤)。
- **卸下别人的**:从在线列表里选人,清掉他身上的伪装。
皮肤类的撤销立刻生效;生物形态类的,库会立刻尝试把真身推回去,但玩家模型的客户端缓存有时要**重进服务器或换维度**才彻底恢复 —— 面板里也有这句提示。
### 6. 类型替换规则
一条规则 = 一个源类型 → 一个目标类型。加完之后,世界里**所有**该类型的实体都显示成目标类型(包括之后新生成的)。
- 添加规则:先填源类型(要被替换掉的),再填目标类型。
- 删除规则 / 清空全部规则:规则本身立刻失效,但**已经被套过的实体**要重进服务器或换维度才恢复真实样子。
- 规则、你自己的伪装、"让别人变成生物"这三样都会写进存档,重启服务器后自动重套。
## 三、类型名怎么写
填类型名的输入框里可以直接写短名,不带冒号会自动补成 `minecraft:` 前缀:
- `cow` 等于 `minecraft:cow`
- `zombie` / `creeper` / `skeleton` / `spider` / `enderman` / `pig` / `sheep` / `chicken` / `wolf` / `cat` / `fox` / `panda` / `bee` / `iron_golem` / `villager_v2` / `wither` / `ender_dragon` ...
带命名空间的要写全(比如自定义实体 `meow:xxx`),写了冒号的不会被改动。
类型名必须是**客户端认识的原版实体名**。乱填的后果是那个客户端上看不到东西,严重时客户端报错 —— 卸下伪装或重进即可恢复。不确定的话先用原版实体名试。
## 四、生效与撤销时机
| 操作 | 生效 | 撤销 |
|------|------|------|
| 变成生物(自己) | 立刻(所有在线玩家) | 要重进 / 换维度才彻底恢复 |
| 伪装成某个玩家(借皮肤) | 立刻 | 立刻恢复 |
| 让别人变成生物 | 立刻 | 要重进 / 换维度才彻底恢复 |
| 类型规则:世界里已有的实体 | 加规则那一刻立刻套 | 要重进 / 换维度才恢复 |
| 类型规则:之后自然生成的实体 | 生成时瞬时(`onMobSpawned`) | 同上 |
| 类型规则:生成蛋 / 指令 / 繁殖 生成的实体 | 1 秒内(定时扫描兜底) | 同上 |
| 重启服务器之后 | 开局自动重套;当时不在线的玩家,进服 3 秒后补上 | 同上 |
"进服 3 秒后补"这个延迟是故意的:玩家刚进服时客户端的出生流程还没走完,包发早了会被丢掉。
## 五、存档文件
`plugins/Disguise/rules.json`:
```json
{
"version": 2,
"rules": [{ "from": "minecraft:cow", "to": "minecraft:zombie" }],
"self": [{ "name": "Steve", "type": "minecraft:zombie", "skin": "" }],
"targets": [{ "id": -25769803777, "type": "minecraft:zombie" }]
}
```
- `self` 的 `type` 和 `skin` **二选一**:库那边玩家实体的皮肤优先,这两条路只能走一条,所以面板里"伪装成某个玩家"会自动取消之前的生物形态(界面会提示)。
- `targets` 按实体**唯一 id**(uniqueId,int64,可能是负数)记账,所以重启后还能认出同一只实体。注意 LSE 的 `Entity.id` 是运行时 id,和它不是一回事。
- 旧版存档(整个文件是一个规则数组)能直接读入,下次存盘自动升级成上面的结构。
- 想临时关掉存档:把脚本顶部的 `PERSIST` 改成 `false`(只放内存,重启即清空)。
- 备份 / 迁移:直接拷这个文件。
## 六、排查
**设置伪装提示"失败: 你自己不在线?"** — 库按名字查不到实体。确认名字是 `player.realName`(面板用的就是这个);同时确认服务器上的库是修复过的那版(首版库把"不在线"判定成"id 是负数",而玩家 id 本身就是负数,会误判)。
**规则加了但牛没变** — 打 `/disguise` → 4. 查看:
- "扫描情况: 实体列表取不到" → `mc.getAllEntities()` 在这套 LSE 上没拿到东西,规则套不上去,需要排查环境。
- "定时扫描 0 只" 但世界里确实有该类型的实体 → 检查规则里源类型写对没有(必须是完整的 `minecraft:cow` 这种形式,面板里已经自动补前缀)。
- 计数在涨但客户端没变化 → 库那边没接收到覆盖:看查看面板里的"库内计数",如果一直是 `entities=0`,说明调度没落到库里,检查库版本与加载顺序。
**生成蛋生的牛要等 1 秒才变** — 这是已知的:`onMobSpawned` 只覆盖"自然生成",其余路径靠 1 秒一轮的扫描。自然生成的是瞬时的(看"生成事件瞬时"那个计数)。要让所有路径都零延迟,需要库侧提供"按类型替换"的 API(出生包里带着类型字符串,不需要 uniqueId),目前没有。
**启动日志说缺导出** — 服务器上挂的是旧库。换库之后必须重启服务器。
**`/disguise` 能看到但执行提示未知的命令** — 这是 LSE 的命令注册限制:空 overload 的指令客户端会认成未知命令。本包注册了一个可选的字符串参数来规避,如果还是这样,确认脚本是整份加载的(`logs/` 里的 LSE 日志会显示 `Reload mod Disguise successfully`)。
## 七、已知限制
- 变的只有**外观**:服务端那边的一切(伤害、掉落、AI、音效、粒子)都还是真身的;客户端上的模型和判定盒按目标类型算,服务端按真身算,所以体型差异大时(比如人变鸡)打点会对不上,需要打真身的位置才能打中。
- 皮肤只对玩家实体有效;皮肤源必须在线,当时不在线的话,等他进服会自动补一次。
- 规则只会套在**已经加载**的实体上;远处未加载区块里的生物,等区块加载、它的出生包发出来时才套(那一刻是零延迟的,出生包直接被换成目标类型)。
- 同一个玩家身上,"变成生物"和"借皮肤"同时只能有一个生效(皮肤优先)。
- 变的都是别人看到的样子;你看自己、以及服务端逻辑,全都不变。
## 八、在别的脚本里直接调库
面板只是调用库的导出,这些导出你自己脚本里也能用:
```js
const viewSelfType = ll.import("HologramLib", "viewSelfType"); // 自己变生物
const viewSelfSkin = ll.import("HologramLib", "viewSelfSkin"); // 自己借皮肤
const viewTargetType = ll.import("HologramLib", "viewTargetType"); // 让别人变生物
const viewClearId = ll.import("HologramLib", "viewClearId"); // 按 id 撤销
const viewSelfClear = ll.import("HologramLib", "viewSelfClear"); // 撤销自己的
const viewUniqueIdOf = ll.import("HologramLib", "viewUniqueIdOf"); // 名字 -> id
viewSelfType("Steve", "minecraft:zombie"); // 所有人看 Steve 是僵尸
const id = viewUniqueIdOf("Alex");
if (id !== -1) viewTargetType(id, "minecraft:cow"); // 所有人看 Alex 是牛
```
判定"在不在线"只认 `-1`(库的约定):**uniqueId 本身可以是负数**,别写 `id < 0`。
面板各功能对应的导出:
| 面板功能 | 用到的导出 |
|----------|-----------|
| 变成生物(自己) | `viewSelfType(name, type)` |
| 伪装成某个玩家 | `viewSelfSkin(name, skinOfPlayer)` |
| 让别人变成生物 | `viewTargetType(uniqueId, type)` |
| 卸下我自己的 | `viewSelfClear(name)` |
| 卸下别人的 | `viewClearId(uniqueId)` |
| 查看 | `viewDescribe(name)`、`viewUniqueIdOf(name)` |
| 类型规则(已有实体) | `viewTargetType(uniqueId, type)` |
| 类型规则(新生成) | `viewTargetType(uniqueId, type)` |
需要"一次改多个字段"(换类型 + 改名字牌一起)时,用整串 spec 的版本:`viewSelf(name, "type=minecraft:cow;name=§c奶牛;always=1")`、`viewEntity(viewerName, uniqueId, spec)`。完整签名见 HologramLib 仓库的 `API.md` 第 2.16 节。
[/MD]