Skip to content

Config loading is missing null-safety checks and can silently corrupt or delete user settings #166

Description

@benjamin920102

Issue 1: 加载配置时模块的 enabled/key 字段缺失会导致 NPE,且错误信息不够精确

文件: src/main/java/top/fpsmaster/modules/config/ConfigManager.java,第 284-285 行

问题描述:
loadConfig 在读取每个模块的配置时,只检查了 moduleJson 是否包含 "settings" 字段:

if (moduleJson == null || !moduleJson.has("settings")) {
    continue;
}
module.set(moduleJson.get("enabled").getAsBoolean());
module.key = moduleJson.get("key").getAsInt();

如果配置文件中某个模块对象缺少 "enabled""key" 字段(手工编辑、外部工具生成、部分写入损坏等情况下均可能发生),这里会抛出 NullPointerException。虽然外层有 catch (Throwable) 兜底,但用户只会看到一条模糊日志 "Failed to load module from config: xxx",无法定位到具体是哪个字段缺失。

建议修复:

if (moduleJson == null || !moduleJson.has("settings")) {
    continue;
}
if (moduleJson.has("enabled")) {
    module.set(moduleJson.get("enabled").getAsBoolean());
}
if (moduleJson.has("key")) {
    module.key = moduleJson.get("key").getAsInt();
}

Issue 2: 高版本配置在低版本客户端上会被直接删除,无备份

文件: ConfigManager.java,resolveMigrationPath(第409-428行)与 migrateConfigIfNeeded(第380-385行)

问题描述:
fromVersion > targetVersion(即配置文件的 schemaVersion 高于当前客户端支持的版本,常见于用户从新版本降级回旧版本时),resolveMigrationPath 返回空列表,随后 migrateConfigIfNeeded 会直接调用 deleteConfigFile 永久删除用户的配置文件,且没有任何备份步骤:

if (migrationPath.isEmpty()) {
    ClientLogger.warn("No config migration path from schema " + currentVersion + " to " + SCHEMA_VERSION + ", deleting " + name + ".json");
    deleteConfigFile(name);
    return null;
}

对用户来说,只是切换/回退了一次客户端版本,就会丢失全部自定义设置(模块状态、按键绑定、UI 布局等),体验很差,也没有恢复手段。

建议修复:在删除前先将原文件重命名/复制为 name.json.bak-schemaV{currentVersion},而不是直接删除;或至少提示用户"检测到更高版本的配置,将重置为默认设置,原文件已备份为 xxx"。


Issue 3: MultipleItemSetting 反序列化时未处理 Item.getItemById 返回 null 的情况

文件: ConfigManager.java 第 325 行,配合 ItemsUtil.getItemStackWithMetadata

问题描述:

int id = item.get("id").getAsInt();
int metadata = item.get("meta").getAsInt();
multipleItemSetting.addItem(ItemsUtil.getItemStackWithMetadata(Item.getItemById(id), metadata));

若配置中记录的物品 ID 在当前游戏环境下已不存在(比如卸载了某个添加该物品的模组、或 Minecraft/Forge 版本变化导致 ID 映射改变),Item.getItemById(id) 会返回 null。而 ItemsUtil.getItemStackWithMetadata

public static ItemStack getItemStackWithMetadata(Item item, int metadata) {
    ItemStack itemStack = new ItemStack(item);
    itemStack.setItemDamage(metadata);
    return itemStack;
}

item 参数没有做 null 检查,直接构造 new ItemStack(null)。这会生成一个语义上无效的 ItemStack,后续任何依赖 itemStack.getItem() 非空的渲染或逻辑代码都有出错风险。

建议修复:

Item resolvedItem = Item.getItemById(id);
if (resolvedItem != null) {
    multipleItemSetting.addItem(ItemsUtil.getItemStackWithMetadata(resolvedItem, metadata));
} else {
    ClientLogger.warn("Skipping unknown item id " + id + " in setting " + setting.name);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions