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 区结束)。常量值本条目不可判定。
已知问题 / 风险
ConcurrentTileEntityMap继承HashMap但只覆写部分方法(:21)—— 未覆写的HashMap方法会操作空表返回错误结果。这是二进制兼容设计(注释:19-20明说)的固有代价,也是本条目最需要审计的点。ConcurrentTileEntityMap的两个 delegate + 弱/强引用混用,且invalidationQueue(:25)的消费者本条目未读 —— 队列可能无界增长。utils包 importrendering包(:4RenderThreadContext)—— 依赖方向与包名直觉相反(虽无循环)。ConcurrentTileEntityMap依赖第三方 mod cubic chunks 的ColumnTileEntityMap(:33注释),该类不在本仓库。AnimationsRenderUtils.SPRITE_CAPTURE无remove()清理路径可见(:15),close()不配对则 ThreadLocal 常驻。AnimationsRenderUtils.SpriteCapture.parent是强引用链(:20),嵌套深时保留对象多;parent非 volatile,跨线程 close 无正确性保证。WorkaroundUtils(9 行)无任何注释,类名不说明绕行的是什么问题 —— 可读性最差。- 8 个文件里 5 个本条目只读到头部(
AwaitingDescriptor53 行、ManagedEnum52 行、EventUtils28 行、Callback13 行、AnimationMode7 行)—— 方法与常量本条目不可判定。这是本条目最大的已知缺口,尤其AnimationMode只有 7 行,只要再读 2 行即可完全确定。 ManagedEnum通过反射修改枚举(类名暗示)—— 反射给枚举加字段在 Java 17+ 强封装下可能失败,具体方式本条目未读。
相关条目
- utils 纹理工具层 - 同包的另 6 个文件
- Tessellator / 线程世界访问 / 渲染队列 -
RenderThreadContext的定义(被本包依赖) - 事件系统 -
EventUtils的同族语义 - HUDCaching - 缓存失效的另一个实现
- Celeritas(内嵌地形渲染引擎) - 网格烘焙与复用的调用方
- Subprojects(内嵌子项目) - GLSM 的
DisplayListManager/DisplayListCommand - Angelica mixin 分组 -
IPatchedTextureAtlasSprite/ITexturesCache的定义