全息浮空標籤与数量閾值 (MC 26.2)
本文件详细介绍 Minecraft 26.2 下 Item Clumps 的全息浮空计数系统、label_min_count 閾值門控及纯原版客户端名牌渲染機制。
📊 特性資訊框
| 属性 | 技术规格 |
|---|---|
| 系统名称 | 服务端全息名牌提供器 |
| 渲染策略 | 原版原生自定义實體名牌 (setCustomName / setCustomNameVisible) |
| 需要客户端模组? | 否(100% 由原版未装模组客户端直接渲染) |
| 標籤格式規範 | <物品名称> x<数量>(如 鑽石 x128、橡木原木 x500) |
| 閾值游戏規則 | item_clumps:label_min_count(默认:-1,使用 DataComponents.MAX_STACK_SIZE) |
| 显示开关游戏規則 | item_clumps:render_labels(默认:true) |
| Tick 额外開銷 | $0.00\text{ ms}$(仅在 setItem() 变更时触发) |
🏷️ 浮空標籤在原版客户端上的渲染機制
Item Clumps 无需客户端安装任何模组,也不发送自定义 OpenGL 渲染資料包,而是直接借用原版 Minecraft 自帶的實體自定义名牌系统:
┌───────────────────────────────┐
│ Ground Item Stack Size Changes│
│ (ItemEntity.setItem) │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Is render_labels Enabled? │
│ Does count > maxStack? │
└───────────────┬───────────────┘
│
┌────────────────┴────────────────┐
│ (Yes) │ (No)
▼ ▼
┌───────────────────────────┐ ┌───────────────────────────┐
│ Set Entity Custom Name │ │ Clear Entity Custom Name │
│ setCustomName(name xCount)│ │ setCustomName(null) │
│ setCustomNameVisible(true)│ │ setCustomNameVisible(false│
└─────────────┬─────────────┘ └───────────────────────────┘
│
▼
┌───────────────────────────┐
│ SyncedEntityData Packet │
│ Sent to all nearby clients│
└─────────────┬─────────────┘
│
▼
┌───────────────────────────┐
│ Vanilla Client Renders │
│ Clean Floating Name Tag! │
└───────────────────────────┘⚙️ 数量閾值自定义 (label_min_count)
在 Minecraft 26.2 中,管理员可通过 item_clumps:label_min_count 游戏規則精确自定义標籤出现的閾值:
label_min_count 设定值 | 閾值触发行为 |
|---|---|
-1 (默认) | 原版堆疊大小:仅当聚合数量超过该物品原版最大堆疊时才显示標籤(鵝卵石 $> 64$、珍珠 $> 16$、剑 $> 1$)。 |
0 | 始终显示:世界上所有掉落的物品實體均显示数量標籤(如 泥土 x1)。 |
100 | 巨型聚合过滤:在聚合体达到 $101+$ 个物品之前隐藏標籤。 |
1000 | 超高产模式:仅巨型工业儲存庫级聚合体显示標籤。 |
💻 真實源码实现参考
源自 Minecraft 26.2 的 ItemEntityMixin.java:
java
private void item_clumps$updateVanillaNameTag() {
if (this.level() == null || this.level().isClientSide()) return;
ItemStack stack = this.getItem();
if (stack.isEmpty()) {
this.setCustomName(null);
this.setCustomNameVisible(false);
return;
}
int count = stack.getCount();
int labelMin = DynamicGameRuleManager.getInt(this.level(), ItemClumpsFabric.LABEL_MIN_COUNT);
int maxStack = (labelMin == -1) ? stack.getOrDefault(DataComponents.MAX_STACK_SIZE, 1) : labelMin;
if (DynamicGameRuleManager.getBoolean(this.level(), ItemClumpsFabric.RENDER_LABELS) && count > maxStack) {
Component name = stack.getItemName().copy().append(" x" + count);
if (!this.hasCustomName() || !this.getCustomName().getString().equals(name.getString())) {
this.setCustomName(name);
this.setCustomNameVisible(true);
}
} else {
if (this.hasCustomName()) {
this.setCustomName(null);
this.setCustomNameVisible(false);
}
}
}
@Inject(method = "setItem", at = @At("TAIL"))
private void item_clumps$onSetItem(ItemStack itemStack, CallbackInfo ci) {
if (!this.level().isClientSide()) {
this.item_clumps$updateVanillaNameTag();
}
}