Skip to content

Mod Config Framework

XiaoColorful edited this page Nov 13, 2025 · 7 revisions

模组配置框架

这是我第一个自己做的我的世界模组(同时也是我第一个“像样的项目”),而我不知道怎么使用Forge提供的配置API😅,看其他模组(开源代码)的配置一下子也学不来,算了先把别的功能写了……

在完成早期的游戏框架后,模组配置管理器也使用类似的设计(不过还不支持子配置管理器热插拔),然后就自然地使用纯Java进行配置文件读写

因此,没有游戏内GUI修改配置文件的方式,同时,本模组的配置文件实际只需要服主提前准备好预设即可,全部大逃杀配置均支持热加载,也有设置大逃杀游戏步长以快速测试复杂的区域配置并在JourneyMap上绘制,何必再支持GUI修改……

模组在多个版本更新(1.20.1forge,1.20.2f&n,1.20.4f&n,1.21.1f&n,1.21.4neoforge,1.21.6neoforge,1.21.10neoforge)几乎全部配置文件均通用(只依赖Java,从而通用语Forge/NeoForge/其他平台移植),仅有个别配置如NBT字符串需改为组件系统写法、生物实体NBT格式更改等MC因素而不通用

本文档写于版本0.4.6-dev1

模组配置框架设计为由模组配置管理器主配置管理器子配置管理器构成

  • 模组配置管理器:包含若干主配置管理器子配置管理器
  • 主配置管理器:本身不包含配置文件读写,但包含若干子配置管理器,本质是进行分类
  • 子配置管理器:有且仅有1种类型的配置文件及其专属的文件夹,在文件夹下直接存放若干该类型配置文件,或者在文件夹下分不同文件夹但仍使用相同类型的配置文件(如通用刷新配置

该结构实际体现为:重载所有游戏配置指令为 /cbr reload game(模组配置管理器-主配置管理器);重载区域配置指令为 /cbr reload game zone(模组配置管理器-主配置管理器-子配置管理器);重载物资刷新配置指令为 /cbr reload loot(模组配置管理器-子配置管理器)

配置管理器特殊机制

package xiao.battleroyale.api.common;

public interface ISideOnly {
	default boolean clientSideOnly() {
		return false;
	}
	default boolean serverSideOnly() {
		return false;
	}
	default boolean inProperSide(McSide mcSide) {
		if (clientSideOnly() && mcSide == McSide.DEDICATED_SERVER) {
			return false;
		} else return !serverSideOnly() || mcSide != McSide.CLIENT;
	}
}
  • 在未读取到有效配置时(即使已有配置文件),生成默认配置(写入操作)并自动重新读取一次
  • 读取配置后选取一个配置进行应用配置(执行一些额外操作),当游戏管理器正在进行游戏时应避免影响游戏
  • 客户端管理器需要重载ISideOnly,避免在专用服务器上注册到模组配置管理器
  • 各配置类型的Json读写均使用 fromJson()toJson() 而未使用Gson装饰器,使得代码自文档并提供更精细的默认值替换和无效配置判定
package xiao.battleroyale.api.config;

public interface IManagerName {
    String getNameKey();
}
  • 将配置指令输入的字符串作为getNameKey,用于获取主配置管理器子配置管理器
  • 如需用自制配置管理器替换已有配置管理器,应具有相同的getNameKey

模组配置管理器

  • 作为模组全局静态API及所有配置管理操作的入口
package xiao.battleroyale.api.config;

public interface IModConfigManager {
	// 添加/移除配置管理器
	boolean registerConfigManager(IConfigManager manager);
	boolean registerConfigSubManager(IConfigSubManager<?> subManager);
	boolean unregisterConfigManager(IConfigManager manager);
	boolean unregisterConfigSubManager(IConfigSubManager<?> subManager);
	// 获取配置管理器
	List<IConfigManager> getConfigManagers();
	List<IConfigSubManager<?>> getConfigSubManagers();
	@Nullable IConfigManager getConfigManager(String managerNameKey);
	@Nullable IConfigSubManager<?> getConfigSubManager(String subManagerNameKey);
	@Nullable IConfigSubManager<?> getConfigSubManager(String managerNameKey, String subManagerNameKey);
	// 重载配置
	int reloadAllConfigs();
	// 生成配置
	int generateAllDefaultConfigs();
	// 保存配置
	int saveAllConfigs();
	// 备份配置
	String getDefaultBackupRoot();
	int backupAllConfigs(String backupRoot);
}

注册配置管理器

  • 注册时检查该配置管理器是否在正确的端侧(专用服务器/客户端)下,如专用服务器上不应注册客户端配置管理器
public interface IModConfigManager {
	boolean registerConfigManager(IConfigManager manager);
	boolean registerConfigSubManager(IConfigSubManager<?> subManager);
	boolean unregisterConfigManager(IConfigManager manager);
	boolean unregisterConfigSubManager(IConfigSubManager<?> subManager);
}

配置管理

所有操作均为调用自身持有的主配置管理器子配置管理器执行相应功能

  • 返回值为成功执行的管理器数量

读写操作

重载配置
  • 当配置管理器未读取到有效配置时,才执行一次写入操作(生成配置),并自动再重载一次
public interface IModConfigManager {
	// 重载配置
	default int reloadAllConfigs() {
		return reloadAllConfigManagers() + reloadAllConfigSubManagers();
	}
	default int reloadAllConfigManagers() {
		int reloadCount = 0;
		for (IConfigManager configManager : getConfigManagers()) {
			reloadCount += configManager.reloadAllConfigs();
		}
		return reloadCount;
	}
	default int reloadAllConfigSubManagers() {
		int reloadCount = 0;
		for (IConfigSubManager<?> configSubManager : getConfigSubManagers()) {
			reloadCount += configSubManager.reloadAllConfigs();
		}
		return reloadCount;
	}
}

写入操作

生成配置
public interface IModConfigManager {
	default int generateAllDefaultConfigs() {  
		int generateCount = 0;  
		for (IConfigManager configManager : getConfigManagers()) {  
			generateCount += configManager.generateAllDefaultConfigs();  
		}  
		for (IConfigSubManager<?> configSubManager : getConfigSubManagers()) {  
			generateCount += configSubManager.generateAllDefaultConfigs();  
		}  
		return generateCount;  
	}
}
保存配置
public interface IModConfigManager {
	int saveAllConfigs();
	int saveAllConfigManagers();
	int saveAllConfigSubManagers();
}
备份配置
public interface IModConfigManager {
	String getDefaultBackupRoot();
	int backupAllConfigs(String backupRoot);
	int backupAllConfigManagers(String backupRoot);
	int backupAllConfigSubManagers(String backupRoot);
}

主配置管理器

  • 持有相同类别的子配置管理器主配置管理器自身没有配置文件类型
  • 子配置管理器无文件夹分类,可调用无 folderId 的重载
package xiao.battleroyale.api.config;

public interface IConfigManager extends IManagerName, ISideOnly {  
	boolean registerSubManager(IConfigSubManager<?> subManager);
	boolean unregisterSubManager(IConfigSubManager<?> subManager);
	@Nullable IConfigSubManager<?> getConfigSubManager(String subManagerNameKey);
	List<IConfigSubManager<?>> getConfigSubManagers();
	// 配置管理
	int generateAllDefaultConfigs();
	int reloadAllConfigs();
	int saveAllConfigs();
	int backupAllConfigs();
	// 获取单个文件内配置词条
	@Nullable IConfigSingleEntry getConfigEntry(String subManagerNameKey, int folderId, int id);  
	@Nullable List<IConfigSingleEntry> getConfigEntryList(String subManagerNameKey, int folderId);
	// 当前选中配置的文件名
	@Nullable String getCurrentSelectedFileName(String subManagerNameKey, int folderId);
	// 文件夹下配置文件类型
	@Nullable String getFolderType(String subManagerNameKey, int folderId);
	// 获取子配置管理器配置文件目录
	@Nullable String getConfigDirPath(String subManagerNameKey, int folderId);
	// 切换配置文件
	boolean switchConfigFile(String subManagerNameKey, int folderId);
	boolean switchConfigFile(String subManagerNameKey, int folderId, String fileName);
}

子配置管理器

package xiao.battleroyale.api.config;

public interface IConfigSubManager<T extends IConfigSingleEntry> extends IManagerName, ISideOnly,
		IConfigSwitchable, IConfigLoadable<T>, IConfigDefaultable<T>, IConfigSaveable,
		IConfigSubReadApi<T> {
		// 配置管理
		@Override int generateAllDefaultConfigs();
		@Override int reloadAllConfigs();
		@Override int saveAllConfigs();
		@Override int backupAllConfigs(String backupRoot);
		// 获取单个文件内配置词条
		@Nullable T getConfigEntry(int folderId, int id);
		List<T> getConfigEntryList(int folderId);
		// 当前选中配置的文件名
		String getCurrentSelectedFileName(int folderId);
		// 文件夹下配置文件类型
		String getFolderType(int folderId);
		// 获取配置文件目录
		@Override default Path getConfigDirPath(int folderId) {
			return Paths.get(getConfigPath(folderId)).resolve(getConfigSubPath(folderId));
		}
}

配置文件目录

  • getConfigPath:获取getConfigSubPath父文件夹路径
  • getConfigSubPath:配置文件所在文件名
package xiao.battleroyale.api.config.sub;

public interface IConfigLoadable<T> {
	String getConfigPath(int folderId);
	String getConfigSubPath(int folderId);
}

单个配置文件

  • 使用copy复制配置
  • 使用applyDefault应用默认配置
package xiao.battleroyale.api.config.sub;

public interface IConfigEntry {
	/**
	 * 获取当前 Entry 的类型,用于 JSON 反序列化
	 * @return Entry 的类型名称
	 */
	String getType();
	/**
	 * 将当前 Entry 序列化为 JSON 对象,用于配置存储和编辑
	 * @return 包含 Entry 配置的 JSON 对象
	 */
	JsonObject toJson();
	@NotNull IConfigEntry copy();
}
package xiao.battleroyale.api.config.sub;

public interface IConfigSingleEntry extends IConfigEntry, IConfigAppliable {
	int getConfigId();
	String getName();
	boolean isDefaultSelect();
	@Override @NotNull IConfigSingleEntry copy();
}
package xiao.battleroyale.api.config.sub;

public interface IConfigAppliable {
    void applyDefault();
}

English

🌐 Language / 语言


Clone this wiki locally