工具类 Utilities

基本信息

属性 值
文件 src/main/java/net/darkhax/wawla/util/Utilities.java(338 行,全 mod 最长的文件)
静态方法数 15
实例成员 无(纯静态工具类,无构造器实例化)
第三方依赖 org.apache.commons.lang3.SystemUtils / WordUtils、net.minecraftforge.common.UsernameCache
@SideOnly 只有 getBlockDamage()(Utilities.java:318)

15 个方法一览

方法 签名要点 用途 调用方
prepareStackCompound (ItemStack) → ItemStack 没有 tag 就补一个,便于后续读写 NBT 内部 getEnchantmentsFromStack
wrapStringToList (String, int, boolean, List) → List 按行宽折行后追加进 tooltip 物品悬浮提示、未接线的 addon
getEnchantmentsFromStack (ItemStack, boolean) → Enchantment[] stored=true 读 StoredEnchantments(附魔书),否则读 ench 物品悬浮提示
round (double, int) → double BigDecimal + HALF_UP 四舍五入 7 处
generateElementArray (Class) → String[] 把枚举常量转成名字数组 Pixelmon
getInventoryStacks (NBTTagCompound, int) → ItemStack[] 从 "Items" 列表还原背包 4 个 addon
compareByClass (Class, Class) → boolean 忽略大小写比类名,两边都判 null 5 个 addon
compareTileEntityByClass (TileEntity, Class) → boolean compareByClass(entity.getClass(), teClass) 3 个 addon
getProgression (float, float) → float cur / max × 100 Tinkers、Pixelmon
upperCase (String) → String 首字母大写 神秘时代
getBlockLightLevel (World, int, int, int, boolean) → int 读上方一格的方块光照;day=true 传遮蔽值 0,false 传 16(忽略日光) 通用方块
getClass (String) → Class Class.forName 包装,失败打 WARN + 返回 null 全 mod 无调用点
getUsernameByUUID (String) → String 查 UsernameCache,查不到显示 tooltip.wawla.unknownplayer 通用实体
canPlayerSleep (EntityPlayer) → boolean 未在睡 + 存活 + 世界时间 ∈ (12541, 23458) 通用方块
getDeepTagCompound (NBTTagCompound, String[]) → NBTTagCompound 逐层下钻 NBT,遇不到就停在当前层 未接线的 addon
getBlockDamage () → float(@SideOnly(CLIENT)) 反射读 PlayerControllerMP 的挖掘进度 通用方块

另有 1 个静态字段 currentBlockDamage(Field,Utilities.java:308),在 ProxyClient.preInit 里用 ReflectionHelper.findField(PlayerControllerMP.class, "g", "field_78770_f", "curBlockDamageMP") 初始化。

⚠️ 源码缺陷

缺陷 1:round(value, 0) 是空操作

Utilities.java:105 的条件是 if (value >= 0 && places > 0),places == 0 时直接落到 return value 原样返回。Javadoc 却说"round to a certain amount of places"。

AddonPixelmonTiles.java:113 正是这样调的:

Utilities.round(Utilities.getProgression(meta, 5), 0) + "%"

即栗子树成长进度完全不取整,HUD 上会显示类似 83.333336%(float 精度误差)的浮点数。AddonGenericTiles.java:143 调的是 round(..., 2),正常。

⚠️ 附带问题:value >= 0 意味着负数永远不取整(round(-1.23456, 2) 返回 -1.23456)。当前 7 个调用点的值都是非负,暂未触发。

缺陷 2:getInventoryStacks 可能返回 null,但 5 个调用点全部直接下标访问

Utilities.java:145 声明 ItemStack[] inventory = null;,只有在 tag.hasKey("Items") 为真时才 new ItemStack[invSize](:150),否则保持 null 返回。

但所有调用点都假设它非 null:

位置 代码
AddonVanillaTiles.java:79 ItemStack[] furnaceStacks = Utilities.getInventoryStacks(data.getNBTData(), 3); 随后 furnaceStacks[0]
AddonThaumcraftTiles.java:125 ItemStack pedestalStack = Utilities.getInventoryStacks(data.getNBTData(), 1)[0];
AddonThaumcraftTiles.java:138 ItemStack pedestalStack = Utilities.getInventoryStacks(data.getNBTData(), 1)[0];
AddonTinkersTiles.java:69 ItemStack cover = Utilities.getInventoryStacks(data.getNBTData(), 4)[3];
AddonTinkersTiles.java:96 ItemStack item = Utilities.getInventoryStacks(data.getNBTData(), 1)[0];
AddonTinkersTiles.java:133 ItemStack[] furnaceStacks = Utilities.getInventoryStacks(data.getNBTData(), 3);

⚠️ 熔炉/基座/晾干架在刚放置、还没放入任何物品时 NBT 里可能没有 Items 键(TileEntityFurnace.writeToNBT 只在 isSpaceInInventory(inventory) 为假时才写),此时 getInventoryStacks 返回 null,[0] 立即 NPE。这是一个可复现的 HUD 崩溃点。

缺陷 3:getInventoryStacks 的越界保护写错了对象

Utilities.java:154:

if (!(i > list.tagCount())) {
  • 意图显然是 i < inventory.length(防 inventory[Slot] 越界);
  • 写成 !(i > tagCount()) 即 i <= tagCount(),而 i 的循环上界就是 tagCount(),恒为真,是死代码;
  • 就算改成 i < inventory.length 也不解决问题——真正越界的是 currentTag.getByte("Slot") 这个值(Utilities.java:157),不是循环下标 i。当 invSize 小于 NBT 里实际的槽位数时(例如按 1 读取却读到 Slot: 0..2 的基座)就会 AIOOBE。

AddonThaumcraftTiles.java:125 / :138 用 getInventoryStacks(tag, 1) 读基座与法杖基座,恰好是这种"小 invSize 读大背包"的高危组合。

缺陷 4:getUsernameByUUID 对非法 UUID 字符串抛异常

Utilities.java:264 直接 UUID.fromString(uuid)。AddonGenericEntities.java:64-65 只校验了 hasKey("OwnerUUID") 与 length() > 0,不校验格式。被破坏的存档或被刻意写入的 NBT 会让 UUID.fromString 抛 IllegalArgumentException,而 getWailaBody 里没有任何 try/catch——一次刷怪笼、一次宠物 HUD 刷新就能炸掉 tooltip 渲染。

缺陷 5:upperCase("") 抛 StringIndexOutOfBoundsException

Utilities.java:209 的 string.charAt(0) 对空串直接越界。当前两处调用(AddonThaumcraftTiles.java:78 与 :117)恰好都有 !aspect.isEmpty() / hasKey("Aspect") 守卫,暂未触发,但该方法本身不安全。

缺陷 6:getBlockDamage 吞掉异常后返回 0

Utilities.java:328-334 两个空 catch 块吞掉 IllegalArgumentException 与 IllegalAccessException。结果是反射失效时挖掘进度静默变成 0%(AddonGenericTiles.java:145 的 progress > 0 判断会直接跳过该行),无任何日志。

无调用点的方法

Utilities.getClass(String)(Utilities.java:239)全 mod 零调用——13 个 addon 都直接用 Class.forName 加 try/catch,而不是走这个已经封装了 WARN 日志的包装。它失败时打的那句 "A class could not be found! This will cause issues, please report to Darkhax!" 因此永远不会出现。

相关条目