视图覆盖 · LSE¶
view* 只改客户端视图:某个玩家(或所有玩家)客户端上的某只实体、某个方块换个样子,
而服务端世界、存档、碰撞、其他玩家看到的都是原样。sensing* 用来查玩家当前的输入设备
(键鼠 / 触屏 / 手柄 / 体感),最常见的用法是按设备分流界面:触屏给虚拟容器,键鼠给交易菜单。
覆盖只存在服务器的内存里:重启就没了,但按玩家名记的覆盖在该玩家重进后仍然有效。
view* 与 sensing* 都没有事件、也没有轮询队列,直接调用直接拿结果。
快速开始¶
const viewEntity = ll.import("HologramLib", "viewEntity");
const viewBlock = ll.import("HologramLib", "viewBlock");
const viewSelfType = ll.import("HologramLib", "viewSelfType");
const viewUniqueIdOf = ll.import("HologramLib", "viewUniqueIdOf");
const viewClearAll = ll.import("HologramLib", "viewClearAll");
// Steve 看到的 12345 号实体变成一头牛,名字牌「§c奶牛」并一直显示
viewEntity("Steve", 12345, "type=minecraft:cow;name=§c奶牛;always=1");
// Steve 客户端的 (100,64,100) 显示成钻石块
viewBlock("Steve", 100, 64, 100, "minecraft:diamond_block");
// 让所有人看 Steve 都是一只僵尸
viewSelfType("Steve", "minecraft:zombie");
// 拿别人的 uniqueId:判在线只认 -1(uniqueId 本身可能是负数,别写 < 0)
const uid = viewUniqueIdOf("Alex");
if (uid !== -1) viewEntity("", uid, "type=minecraft:cow"); // 所有人都看到 Alex 是一头牛
viewClearAll(""); // 收工:清掉所有玩家的覆盖
spec 串¶
viewEntity / viewSelf 的 spec 用分号分隔,每项写成 键=值;键不分大小写,值两边的空白会被去掉。
| 键 | 值 | 说明 |
|---|---|---|
type= |
实体类型名 | 换成该类型(cow 会自动补成 minecraft:cow);对玩家实体 = 给他造一个同 id 的生物替身 |
as= |
在线玩家名 | 换成那名玩家的样子(皮肤取自他);只对玩家实体有效 |
skin= |
已注册的皮肤 id | 换成注册表里的皮肤(由 playerNpcRegisterSkin / playerNpcCaptureSkin 注册);只对非玩家实体有效,客户端会用玩家模型渲染它,服务端那边还是原来那只生物 |
hidden= |
1 / true / yes / on |
对这名玩家隐藏这只实体 |
name= |
名字牌文字 | 出现这个键就改名字牌;值给空串 = 清空名字 |
always= |
1 / true / yes / on |
名字牌一直显示(要和 name= 一起用) |
pose= |
0..13 |
姿态编号,越界会被钳进范围;客户端只对盔甲架与玩家按它摆姿势 |
scale= |
0.0625~10 |
元数据缩放(客户端实体缩放 + 碰撞箱),越界整项忽略;与 skin= 那档的模型缩放不是一回事 |
variant= |
整数 | 变种:羊的颜色档 / 河豚的鼓胀档(0 常态 1 半鼓 2 全鼓)/ 热带鱼体型… |
mark= |
整数 | 二级变种(也可写 markvariant=):热带鱼花纹 0..5、马的斑纹等 |
color= |
0..15 |
主色(也可写 colorindex=),越界整项忽略 |
color2= |
0..15 |
花纹色(热带鱼用),越界整项忽略 |
pose=/scale=/variant=/mark=/color=/color2=是附加项,要和主项 (type=/as=/skin=/hidden=/name=)一起给:只给附加项的 spec 会被当成「没有任何生效项」, 效果等于把这条覆盖撤掉。
主入口¶
覆盖玩家看到的某只实体¶
viewEntity(playerName, entityUniqueId, spec)
- 参数:
playerName:String
玩家名;空串 = 对所有玩家生效entityUniqueId:Integer
实体的唯一 id —— 玩家的用viewUniqueIdOf取,其它实体可取自Entity.uniqueId(用Number(e.uniqueId)转成数字)spec:String
覆盖内容,见上文「spec 串」:换类型、换皮肤、隐藏、改名字牌- 返回值:
true= 已登记;false=spec里没有任何生效项(这次调用等于撤销覆盖,原本没有覆盖就是false) - 返回值类型:
Boolean -
示例:
覆盖玩家看到的某个方块¶
viewBlock(playerName, x, y, z, type)
- 参数:
playerName:String
玩家名;空串 = 对所有玩家生效x:Integer
方块坐标 X(整数)y:Integer
方块坐标 Y(整数)z:Integer
方块坐标 Z(整数)type:String
方块 id,如"minecraft:diamond_block";空串 = 撤销这个坐标的覆盖- 返回值:
true= 已登记;false= 类型名不在客户端注册表里(type传空时,false= 这个坐标本来就没有覆盖) - 返回值类型:
Boolean -
示例:
撤销单只实体的覆盖¶
viewClearEntity(playerName, entityUniqueId)
- 参数:
playerName:String
玩家名;空串 = 对所有玩家生效entityUniqueId:Integer
实体的唯一 id- 返回值:
true= 已撤销并恢复(这名玩家重新看到真身);false= 没有这条覆盖 - 返回值类型:
Boolean -
示例:
撤销单个方块的覆盖¶
viewClearBlock(playerName, x, y, z)
- 参数:
playerName:String
玩家名;空串 = 对所有玩家生效x:Integer
方块坐标 X(整数)y:Integer
方块坐标 Y(整数)z:Integer
方块坐标 Z(整数)- 返回值:
true= 已撤销并推回真实方块;false= 这个坐标本来就没有覆盖 - 返回值类型:
Boolean -
示例:
撤销这名玩家的全部覆盖¶
viewClearAll(playerName)
- 参数:
playerName:String
玩家名;空串 = 全部玩家- 返回值:没有返回值
- 返回值类型:
Null -
示例:
「自己」一族¶
这一组都作用在调用者自己身上,并且是给所有玩家看的(管理员自我伪装、活动变身这类用途)。
给自己套一层覆盖¶
viewSelf(playerName, spec)
- 参数:
playerName:String
玩家名,必须在线spec:String
覆盖内容(同viewEntity,见上文「spec 串」;等价于viewEntity("", viewUniqueIdOf(playerName), spec),登记后所有玩家都看得见)- 返回值:
true= 已登记;false= 玩家不在线,或spec里没有任何生效项 - 返回值类型:
Boolean -
示例:
撤销套在自己身上的覆盖¶
viewSelfClear(playerName)
- 参数:
playerName:String
玩家名- 返回值:
true= 已撤销;false= 玩家不在线或本来就没有覆盖 - 返回值类型:
Boolean -
示例:
让自己在所有玩家眼里变身¶
viewSelfType(playerName, type)
- 参数:
playerName:String
玩家名type:String
实体类型名,如"minecraft:zombie"(玩家/生物类型都可以)- 返回值:
true= 已登记;false= 玩家不在线 - 返回值类型:
Boolean -
示例:
借别人的皮肤显示自己¶
viewSelfSkin(playerName, skinOfPlayer)
- 参数:
playerName:String
玩家名skinOfPlayer:String
皮肤来源的在线玩家名(换肤这一族是唯一连本人一起换的覆盖)- 返回值:
true= 已登记;false= 玩家不在线 - 返回值类型:
Boolean -
示例:
改自己头顶的名字牌¶
viewSelfName(playerName, nametag, alwaysShow)
- 参数:
playerName:String
玩家名nametag:String
新名字(支持§颜色码);空串 = 清空名字alwaysShow:Booleantrue= 名字牌一直显示- 返回值:
true= 已登记(所有玩家都看得到);false= 玩家不在线 - 返回值类型:
Boolean -
示例:
对所有人隐藏自己¶
viewSelfHide(playerName, hidden)
- 参数:
playerName:String
玩家名hidden:Booleantrue= 其他人都看不到你;false= 恢复可见(等于撤销这条覆盖)- 返回值:
true= 已登记;false= 玩家不在线 - 返回值类型:
Boolean -
示例:
逐字段与按 id 操作¶
拿不到别人 uniqueId 的时候,用 viewTargetType;要一次清掉某只实体的全部覆盖,用 viewClearId。
让所有玩家看到它是该类型¶
viewTargetType(entityUniqueId, type)
- 参数:
entityUniqueId:Integer
实体的唯一 idtype:String
实体类型名,如"minecraft:cow"(不限于玩家实体)- 返回值:
true= 已登记;false=type传空并且没有可撤销的覆盖 - 返回值类型:
Boolean -
示例:
撤销这只实体的全部覆盖¶
viewClearId(entityUniqueId)
- 参数:
entityUniqueId:Integer
实体的唯一 id- 返回值:
true= 已撤销;false= 没有这条覆盖 - 返回值类型:
Boolean -
示例:
查询与诊断¶
取走「刚出生的实体」事件(1.30.0 / 26.51.4 起)¶
viewPollSpawnEvents()
- 参数:无
- 返回值:字符串数组,每条是一只要么刚出生的实体,读一次清空(取走语义,与
ghost*/containerPollClicks一致) - 掉落物:
drop|uid|dim|x|y|z| - 其它实体:
actor|uid|dim|x|y|z|identifier - 返回值类型:
Array<String> - 说明:出生包在出站钩子里就记一条(每只实体只记一次),所以脚本不需要再靠定时扫描(0.5~1s 一轮)去找"刚刷出来的东西",
最晚一拍就能拿到。典型用法:掉落物一出生就
itemDisplayEmitForEntity(玩家, uid, ...)让这名玩家看到模型;生物一出生就套伪装规则。 队列有上限(512 条),脚本停摆太久会丢掉最早的几条。 -
示例:
const pollSpawn = ll.import("HologramLib", "viewPollSpawnEvents"); const emitOne = ll.import("HologramLib", "itemDisplayEmitForEntity"); function onTick() { for (const ev of pollSpawn()) { const [kind, uid, dim, x, y, z, identifier] = ev.split("|"); if (kind === "drop") attach(Number(uid), "-0.4", "0", "0", "0.375", 180, 2, "", true); } }
按玩家名取他的 uniqueId¶
viewUniqueIdOf(playerName)
- 参数:
playerName:String
玩家名(脚本平时拿不到实体的这个 id,用它来取)- 返回值:该玩家实体的 uniqueId;
-1= 不在线(uniqueId 本身可能是负数,判断只认-1,不要写< 0) - 返回值类型:
Integer -
示例:
查这名玩家挂了多少条覆盖¶
viewDescribe(playerName)
- 参数:
playerName:String
玩家名- 返回值:以
entities=N blocks=N开头的摘要(诊断用),后面还跟着若干钩子计数(排查覆盖有没有生效时看它) - 返回值类型:
String -
示例:
把字符串原样回显¶
viewEcho(text)
- 参数:
text:String
任意字符串(用来确认这一串到底有没有传进来)- 返回值:形如
len=12 hex=7479... text=[...];hex是最多前 16 个字节 - 返回值类型:
String -
示例:
离线验 spec 折出的元数据¶
viewMetaProbe(spec)
- 参数:
spec:String
覆盖内容(同viewEntity);不需要客户端就能验它写对没有- 返回值:形如
items=N bytes=N hex=...;spec里没有任何生效项时是items=0 plan=empty - 返回值类型:
String -
示例:
查掉落物出生包有没有被拦¶
viewItemSpawnHooked()
- 返回值:本版本恒为
true(掉落物换类型 / 隐藏一次到位);老库里没有这个函数名(ll.hasExported判为false)→ 视为不支持,脚本需要周期性重套覆盖 - 返回值类型:
Boolean -
示例:
注意事项¶
- 只改客户端视图:服务端世界、存档、碰撞、其他玩家看到的都是原样。把某个玩家显示成一头牛时, 他的 id 没变 —— 打他就是打他本人,伤害与掉落都按真身结算。
- 覆盖只存在内存里:重启服务器就没了。按玩家名记的覆盖在该玩家重进后仍然有效 (实体重新出生时自动套用,方块随区块送达自动补发)。
- 自己的客户端不参与自己实体的覆盖:换类型、隐藏都是给别人看的(否则本人客户端会出问题);
只有换肤那一类(
viewSelfSkin/as=)连本人一起换。 - 实体还没出生也能先设覆盖,库会在它出生时套用;反过来,本来就不发给这名玩家的实体或方块 (视野外等)不会因为覆盖而凭空出现。
- 撤销时离得太远(> 64 格)或已经消失的实体不会重建 —— 客户端上本来也没有它,等它下次出生即可。
- 逐玩家的发光 / 隐身做不了:基岩版的隐身是效果而不是外观,这一版 API 里没有这两项。
逐玩家的实体缩放要用
scale=(0.0625~10),不要和skin=那档的模型缩放混用。 sensing*不用轮询:换设备后下一个输入包就更新,玩家离线即清零。- 界面类域(容器 / 对话 / 交易)的事件才需要轮询,见界面与容器。