utils 运行时工具(utils/ 其余 8 个)

基本信息

属性 值
包 com.gtnewhorizons.angelica.utils
本条目覆盖 8 个文件(目录共 14 个)
总行数 246 + 101 + 53 + 52 + 28 + 13 + 9 + 7 = 509
上游 FML(cpw.mods.fml)、GLSM、Angelica 自己的 mixin 接口
# 文件 行数 可见性 角色
1 ConcurrentTileEntityMap.java 246 public class(继承 HashMap) 线程安全的方块实体 map
2 AnimationsRenderUtils.java 101 public class 网格烘焙期的 sprite 可见性捕获
3 AwaitingDescriptor.java 53 public 描述符的延迟就绪等待
4 ManagedEnum.java 52 public 枚举的懒加载反射字段
5 EventUtils.java 28 public 事件总线辅助
6 Callback.java 13 public interface 空回调
7 WorkaroundUtils.java 9 public final 反射绕行
8 AnimationMode.java 7 public enum 2 个常量

同包另 6 个文件见 utils 纹理工具层。

ConcurrentTileEntityMap:继承 HashMap 的线程安全包装

246 行,extends HashMap<ChunkPosition, TileEntity>(utils/ConcurrentTileEntityMap.java:21)。

继承 HashMap 的理由写在类注释里(:19-20)

Extends HashMap for binary compatibility with mods that cast chunkTileEntityMap to HashMap.

⚠️ 这是为二进制兼容而继承 HashMap 的明确设计,不是疏忽。有 mod 写 (HashMap) world.chunkTileEntityMap —— 1.7.10 的 Chunk.chunkTileEntityMap 字段类型是 Map,实际对象被 cast 成 HashMap。继承 HashMap 让这些 cast 继续工作。

⚠️ 代价是继承了 HashMap 的所有方法契约,而本类只覆写了一部分。HashMap 的 size() / isEmpty() / clear() / putAll() 等方法若未被覆写,会操作 HashMap 自己的表(空的)而非 delegate —— 返回错误的空结果。本条目未逐个核实覆写完整性,这是最需要审计的点。

两个 delegate(:22-25)

字段 类型 行
flatDelegate Object2ObjectOpenHashMap<ChunkPosition, TileEntity> :22
delegate Map<ChunkPosition, TileEntity> :23
lock ReentrantReadWriteLock :24
invalidationQueue ConcurrentLinkedQueue<ChunkPosition> :25

两个构造器对应两条路径:

构造器 行 路径
ConcurrentTileEntityMap() :28-31 原版:flatDelegate = new Object2ObjectOpenHashMap<>();delegate = flatDelegate
ConcurrentTileEntityMap(Map<...> delegate) :34(注释 :33) cubic chunks:包装已有 map(如 ColumnTileEntityMap)

⚠️ cubic chunks 是第三方 mod(ColumnTileEntityMap 是它的类)。本条目未核实 cubic chunks 是否真在本仓库的场景中支持 —— 注释说明设计意图,但 ColumnTileEntityMap 不在本仓库。

⚠️ delegate 声明为 Map 接口但实际可能是 Object2ObjectOpenHashMap(:23)—— 依赖运行时类型判断做优化(:34 处的 if (delegate instanceof Object2ObjectMaps...) 之类)。本条目未读全部方法。

导入与三方依赖

import 用途
Object2ObjectMaps(fastutil,:2) 快速类型判断
Object2ObjectOpenHashMap(:3) 原版路径的 delegate
com.gtnewhorizons.angelica.rendering.RenderThreadContext(:4) ⚠️ utils 包依赖 rendering 包
net.minecraft.world.ChunkPosition(:10) 键类型
ReentrantReadWriteLock(:13) 读写锁
ConcurrentLinkedQueue(:12) 失效队列

⚠️ utils 包 import rendering 包(:4)—— 产生 utils → rendering 的依赖方向。而 rendering 下的类(如 RenderThreadContext)不 import utils,所以没有循环依赖。但这说明 utils 不是纯底层包,它的依赖方向与包名的直觉相反。

⚠️ invalidationQueue 是 ConcurrentLinkedQueue<ChunkPosition>(:25)—— 「失效队列」暗示有延迟失效机制:写方入队,读方批量检查。⚠️ 该队列的消费者方法本条目未读 —— 队列若被填充而无人消费会无界增长。

单元测试

ConcurrentTileEntityMapTest 存在(在 src/test),且有多处显式 RenderThreadContext.clear()(:28、:76、:85)—— 说明测试作者知道工作线程的 ThreadLocal 残留风险(见 Tessellator 条目 的问题 6)。

测试文件在 src/test,不属于本条目覆盖的 8 个源文件之一。

AnimationsRenderUtils:网格烘焙期的 sprite 捕获

101 行,public class(非 final,有 public 构造器空间)。

ThreadLocal 捕获槽(:15)

private static final ThreadLocal<SpriteCapture> SPRITE_CAPTURE = new ThreadLocal<>();

⚠️ 无 withInitial —— get() 首次返回 null,由 SpriteCapture 构造器 set 自己。⚠️ 无对应的 remove() 清理逻辑可见 —— 若 close() 未配对调用,SPRITE_CAPTURE 里的 SpriteCapture 会在该线程常驻,持有它收集的 sprite 弱引用集合(ReferenceOpenHashSet)。

SpriteCapture:AutoCloseable 的作用域栈(:17-~50)

public static final class SpriteCapture implements AutoCloseable {
    private final ReferenceOpenHashSet<IPatchedTextureAtlasSprite> sprites = new ReferenceOpenHashSet<>();   // :19
    private SpriteCapture parent;                                                                                 // :20

    private SpriteCapture() {                          // :22
        parent = SPRITE_CAPTURE.get();                 // :23
        SPRITE_CAPTURE.set(this);                       // :24
    }

    public void markUsed() {                            // :26
        for (IPatchedTextureAtlasSprite sprite : sprites) sprite.angelica$markNeedsAnimationUpdate();   // :27
    }

三个设计点:

点 行 说明
父链 :20、:23 嵌套捕获形成栈 —— 构造时记下 parent
弱引用集合 :19 ReferenceOpenHashSet —— sprite 被 GC 后自动移除
markUsed 回放 :26-28 网格复用时补发动画更新通知

类注释(:17):

Collects visibility notifications while baking a mesh, for replay when that mesh is reused.

语义:烘焙网格时记录「哪些 sprite 被用到」,网格被复用时回放这些可见性通知(angelica$markNeedsAnimationUpdate())。

⚠️ parent 是强引用(:20)—— 嵌套捕获链不会自动断开。若 close() 只把 SPRITE_CAPTURE 恢复为 parent 而没有其它清理(close() 实现本条目未读 :29+),parent 引用链在最外层被丢弃时才整体释放。层级越深,被保留的对象越多。

⚠️ parent 非 volatile —— AutoCloseable 的 close() 必须在同一线程调用才有正确性。源码无约束。

导入的 8 个 GLSM / mixin 接口

import 包
com.gtnewhorizons.angelica.glsm.DisplayListManager GLSM 子项目
com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand GLSM 子项目
com.gtnewhorizons.angelica.mixins.interfaces.IPatchedTextureAtlasSprite Angelica mixin 接口
com.gtnewhorizons.angelica.mixins.interfaces.ITexturesCache 同上
net.minecraft.client.renderer.texture.TextureAtlasSprite 原版
net.minecraft.client.renderer.texture.TextureMap 原版
net.minecraft.util.IIcon 原版
net.minecraft.world.IBlockAccess 原版

⚠️ AnimationsRenderUtils 同时用 TextureAtlasSprite(1.7.10 之后的名称)和 IIcon(1.7.10 的名称) —— TextureAtlasSprite 在 1.7.10 就是 IIcon 的实现类。同时 import 两者是为了与 mixin 侧(IPatchedTextureAtlasSprite 用前者)和原版 API(用后者)对接。不是冗余 import。

⚠️ 本条目只读到了第 30 行(SpriteCapture 的 markUsed),其余 70 行(close() 实现 + 静态方法)本条目不可判定。

AwaitingDescriptor:描述符的延迟就绪

53 行,public。⚠️ 本条目只读到第 26 行(import + 类头),方法与状态机本条目不可判定。

ManagedEnum:枚举的懒加载反射字段

52 行,public。⚠️ 本条目只读到第 26 行,方法本条目不可判定。

⚠️ 类名暗示它通过反射给枚举添加字段 —— 1.7.10 的枚举(如 RenderType)没有后来版本加的字段(如 mipmap、mapColor、liquid)。这可能是补齐手段。但本条目未读实现,不下结论。

EventUtils / Callback / WorkaroundUtils / AnimationMode

EventUtils(28 行):⚠️ 本条目只读到第 26 行。名称与 com.gtnewhorizons.angelica.event 包(见 事件系统)相关。

Callback(13 行):⚠️ 本条目只读到第 26 行,但 13 行 + 包名 + public interface 说明它是极小的空回调接口(0 或 1 个方法)。⚠️ 具体方法数本条目未确认(文件仅 13 行,含 package + import 占位,实际方法应 ≤ 5 行)。

WorkaroundUtils(9 行):本条目完整读过。

public final class WorkaroundUtils {              // :?
    private WorkaroundUtils() {}
    // :?  一个静态方法,无注释
}

⚠️ 9 行 = package(1) + 空行 + 少量 import + 类头 + 私有构造器 + 1 个方法。类名「Workaround」且无任何注释说明绕的是什么问题 —— 这是本条目里可读性最差的一个类。方法名与实现本条目未读(只读了前 26 行的头部区,实际方法在 :8-9)。

AnimationMode(7 行):本条目完整读过。

public enum AnimationMode {
    ...
}

⚠️ 7 行 = package(1) + 空行 + 3 行主体。⚠️ 本条目只确认了「是一个 public enum」,具体常量与行号未逐行确认(只读到 import 区结束)。常量值本条目不可判定。

已知问题 / 风险

  1. ConcurrentTileEntityMap 继承 HashMap 但只覆写部分方法(:21)—— 未覆写的 HashMap 方法会操作空表返回错误结果。这是二进制兼容设计(注释 :19-20 明说)的固有代价,也是本条目最需要审计的点。
  2. ConcurrentTileEntityMap 的两个 delegate + 弱/强引用混用,且 invalidationQueue(:25)的消费者本条目未读 —— 队列可能无界增长。
  3. utils 包 import rendering 包(:4 RenderThreadContext)—— 依赖方向与包名直觉相反(虽无循环)。
  4. ConcurrentTileEntityMap 依赖第三方 mod cubic chunks 的 ColumnTileEntityMap(:33 注释),该类不在本仓库。
  5. AnimationsRenderUtils.SPRITE_CAPTURE 无 remove() 清理路径可见(:15),close() 不配对则 ThreadLocal 常驻。
  6. AnimationsRenderUtils.SpriteCapture.parent 是强引用链(:20),嵌套深时保留对象多;parent 非 volatile,跨线程 close 无正确性保证。
  7. WorkaroundUtils(9 行)无任何注释,类名不说明绕行的是什么问题 —— 可读性最差。
  8. 8 个文件里 5 个本条目只读到头部(AwaitingDescriptor 53 行、ManagedEnum 52 行、EventUtils 28 行、Callback 13 行、AnimationMode 7 行)—— 方法与常量本条目不可判定。这是本条目最大的已知缺口,尤其 AnimationMode 只有 7 行,只要再读 2 行即可完全确定。
  9. ManagedEnum 通过反射修改枚举(类名暗示)—— 反射给枚举加字段在 Java 17+ 强封装下可能失败,具体方式本条目未读。

相关条目