客户端护甲渲染替换
[!INFO] 实现类:
lain.mods.cos.client.PlayerRenderHandler(仅客户端,@SideOnly通过InventoryManagerClient间接保证)
基本信息
| 属性 | 值 |
|---|---|
| 挂载点 | MinecraftForge.EVENT_BUS(客户端事件总线) |
| 事件 | RenderPlayerEvent.Pre / .Post、RenderHandEvent、WorldEvent.Unload |
| 全局开关 | InventoryManagerClient.isRenderActive,默认 true |
| 切换方式 | 成品界面 5×5 按钮 ID 77,或见 界面与按钮 |
| 服务端可见性 | 否,纯客户端视觉行为 |
功能
不改玩家数据,而是在渲染前把 player.inventory.armorInventory 临时替换成时装护甲,渲染后原样还原。核心是「换入 → 渲染 → 换回」三段式,缓存 Map<EntityPlayer, ItemStack[]> realArmorsCache 保存真实护甲快照。
事件与优先级
| 事件 | 优先级 | receiveCanceled |
动作 |
|---|---|---|---|
RenderPlayerEvent.Pre |
HIGH |
否 | restorePlayersRealArmor() 后 swapForCosmeticArmor() |
RenderPlayerEvent.Post |
LOW |
否 | restorePlayersRealArmor() |
RenderHandEvent(本地玩家) |
HIGH |
否 | restorePlayersRealArmor() 后 swapForCosmeticArmor() |
RenderHandEvent(本地玩家) |
LOWEST |
是 | 仅 restorePlayersRealArmor() |
RenderPlayerEvent.Pre |
LOWEST |
是 | 事件被取消时补一次 restorePlayersRealArmor() |
WorldEvent.Unload |
— | — | world.isRemote 时清空 realArmorsCache |
后两个 LOWEST + receiveCanceled 订阅是安全网:如果别的 mod 在 Pre 阶段 setCanceled(),本 mod 的换入可能没走完 Post,因此补一次还原,避免玩家护甲被永久留在 null 状态。
换入逻辑
ItemStack[] armor = player.inventory.armorInventory;
realArmorsCache.put(player, Arrays.copyOf(armor, armor.length)); // 深拷贝快照
for (int i = 0; i < cosArmor.length; i++) {
if (invCosArmor.isSkinArmor(i)) armor[i] = null;
else if (cosArmor[i] != null) armor[i] = cosArmor[i];
}
要点:
- 用
Arrays.copyOf做快照,因此渲染期间原版护甲数据仍在,只是数组引用被临时改写。 - 时装槽为空时不覆盖原版护甲——
armor[i]保持原值,实现「逐件替换」。 - 判定 皮肤模式开关 时优先于时装物品。
- 缓存是
HashMap,每次渲染后即被remove清空(restorePlayersRealArmor首行即realArmorsCache.remove(player)),因此WorldEvent.Unload的清理实际是冗余的(源码注释也这么说明)。
还原逻辑
ItemStack[] cachedArmor = realArmorsCache.remove(player);
if (cachedArmor != null && armor != null && cachedArmor.length == armor.length)
System.arraycopy(cachedArmor, 0, armor, 0, cachedArmor.length);
长度不匹配时放弃还原(静默),这是防御性写法:若中途有其他 mod 改动了 armorInventory 的长度,原数组会被丢弃而不是写入越界。
开关的注册与状态
InventoryManagerClient.init() 里直接调用一次 toggleRenderer(),把 isRenderActive 从 false 翻成 true——默认开启。toggleRenderer() 同时负责向 MinecraftForge.EVENT_BUS 注册/反注册整个 PlayerRenderHandler,因此关闭后所有渲染钩子彻底停摆(不只是 if 判定跳过)。
ClientDisconnectionFromServerEvent 中:若 !isRenderActive 则补一次 toggleRenderer(),并 cacheClient.clear() 清空客户端护甲影子缓存——避免重连服务器后沿用旧玩家数据。
限制
- 完全客户端,服务端玩家看不到自己以外的任何差异之外的东西——其他玩家的皮肤模式可见性依赖 网络封包 下发的
isSkinArmor标志。 - 不改变任何护甲数值、伤害减免或摔落判定,仅影响渲染。
- 不渲染到手持物(
RenderHandEvent只还原本地玩家护甲,不替换手上物品)。