UUID 身份模型

基本信息

属性 值
核心类 User、Group、CapeConfig
管理器 UserManager、GroupManager(均为单例)
标识方式 玩家 UUID 字符串(不是玩家名)
配置 ID 容量 BitSet(256),ID 从 1 起

README 说明为何用 UUID:可以用 MCUUID.NET 手工查,或用 MCAPI 从 JSON 批量取。

User

public class User {
    public List<ICape> capes;
    public final String userUUID;
}
  • userUUID 是 36 位带连字符的 UUID 字符串。
  • capes 是列表而非单个引用——一个玩家可以同时拥有多个披风(例如同时命中一个组和一条单人规则)。
  • UserManager 是全局单例,按 UUID 索引;解析与渲染都经由 UserManager.getInstance().getUser(uuid) 查找。

Group

public class Group {
    protected HashMap<String, User> users;
    protected ICape cape;
    public final String name;
}

一个组 = 一个共享披风 + 一组 UUID。关键行为在 addUser:

public void addUser(User user) {
    if (!this.users.containsValue(user)) {
        user.capes.add(this.cape);   // 把组披风挂到玩家身上
        this.users.put(user.userUUID, user);
    }
}

注意守卫用的是 containsValue(按 equals 查已存在的 User 对象)而写入用的是 userUUID。由于 User 没有覆写 equals/hashCode,同一 UUID 解析出的两个 User 实例会被判定为不同,从而在 capes 列表里出现重复条目。

setCape 会先把旧披风从所有成员身上移除再替换,但它没有把新披风加回去——只做 user.capes.remove(this.cape),随后设 this.cape = cape,成员身上不会自动获得新披风。

一个玩家的多披风与渲染取舍

渲染管线 里的 RenderEventHandler 只取 user.capes.get(0):

ICape cape = user.capes.get(0);

因此即使配置让一个玩家有多个披风,实际渲染的永远只有列表里的第一个。而 Group.addUser 是在解析期间按组出现的顺序 add 的,组在 JSON 里的先后决定哪个披风胜出(单人规则走 UserManager.parse,同样是 capes.add)。

配置 ID 分配

CapeConfigManager 用 HashBiMap<Integer, CapeConfig> 双向映射配置与整数 ID,ID 池是 BitSet availableIds = new BitSet(256):

规则 实现 结果
取新 ID getUniqueId() = availableIds.nextClearBit(1) 从 1 开始,跳过 0
占用校验 claimId(id) id <= 0 抛 InvalidCapeConfigIdException
重复占用 claimId 检查 availableIds.get(id) 抛 The config ID %d is already claimed.
越界 UnsignedBytes.checkedCast(id)(字节范围 0–255) 异常被 printStackTrace() 吞掉,不向上抛

[!NOTE] claimId 里 UnsignedBytes.checkedCast 的 catch 只 printStackTrace() 不重抛,所以传入 ≥256 的 ID 会打印栈但继续正常占用该位。这是源码里的一个宽松处理。

相关条目