Sodium Config API 使用指南:为 Minecraft 视频设置界面注册你自己的 Mod 配置页
图形学游戏开发【免费下载链接】sodium-fabricA high-performance rendering engine replacement for Minecraft, which greatly improves frame rates and reduces micro-stutter项目地址https://gitcode.com/gh_mirrors/so/sodium-fabric点击查看免费下载本指南完整讲解 Sodiumsodium-fabric 项目为第三方模组提供的Sodium Config API如何在构建脚本中引入 API 依赖、通过元数据或注解声明 Config Entrypoint、用声明式 Builder 注册布尔 / 整数 / 枚举 / 外部按钮选项以及利用动态依赖值、选项覆写Replacement / Overlay、自定义 Flags 与 Flag Hook 等高级能力把 Mod 自己的视频设置页面无缝接入 Sodium 替换后的视频设置屏幕。读完本文你将掌握从零接入到深度定制的完整实现方案并能对照仓库源码理解每个 API 背后的底层行为。适用范围与设计定位Sodium Config API 是专为视频设置类模组设计的展示层 API而非通用配置库。它的定位非常明确不做配置文件的读写加载、解析、序列化配置数据完全由你的 Mod 自己负责可以自行实现也可以借助第三方配置库不做通用配置管理如果你的需求是平台通用的 Mod 配置管理而非视频设置项请使用对应平台的 Mod 列表与配置库方案只负责呈现它负责把 Mod 的视频设置选项以规范化的结构注册进 Sodium 的视频设置屏幕中。在 API 出现之前第三方 Mod 往往通过 Mixin 侵入 Sodium 内部代码来追加设置按钮有了这套 API 之后Mod 不再需要触碰 Sodium 的实现细节即可独立于 GUI 实现方式注册自己的设置页。API 虽不能覆盖所有历史 Mixin 用例但足以覆盖绝大多数常见需求。核心能力一览API 提供了如下能力对应 USAGE.md 的 Features 清单每个 Mod 一组选项页页拥有 ID、名称、版本、主题色以及可选的方形单色图标每页一组选项分组可命名分组也支持外部页——点击后直接打开 Mod 自定义的Screen每组的选项列表选项 ID 使用Identifier命名空间 路径标识多种选项类型整数滑块、枚举、布尔复选框、外部按钮打开新Screen回调式值绑定通过save/load回调或OptionBinding绑定值存储动态值启用状态、默认值、允许值都可以动态计算且可依赖其他选项的值系统自动做循环依赖与已声明依赖的一致性检查值格式化器整数滑块与枚举值以Component形式呈现值相关的动态 TooltipFlags 机制触发渲染器重载、资源包重载等图形事件全部用户可见字符串可翻译translatable存储处理器变更应用后统一刷新flush一次早期注册供需要在窗口创建前就解析选项的 Mod 使用基于 Entrypoint 的配置注册声明式 Builder 风格选项覆写 / 替换可部分修改Overlay或整体替换Replacement既有选项自定义 Flags在特定变更发生后执行自定义动作。两阶段注册机制注册分为Early早期与Late晚期两个独立阶段见 ConfigEntryPoint.java早期注册registerConfigEarly发生在 Sodium 初始化自己的早期选项、窗口创建之前只有少数需要在非常早的加载阶段解析选项的 Mod 才需要晚期注册registerConfigLate发生在游戏启动之后绝大多数 Mod 只需使用晚期注册只有晚期注册的选项才会出现在 GUI 中。从源码看两个阶段通过 ConfigManager.java 中的registerConfigsEarly()与registerConfigsLate()分别驱动Sodium 会对每个 Entrypoint 构造一个实例再在正确时机依次调用其早期与晚期方法。注册期间发生的任何异常都会以崩溃报告的形式终止游戏crashWithMessage因此 Builder 链中所有必填属性都必须设置完整。构建依赖配置Maven 仓库声明Sodium 将 API 包发布在 CaffeineMC 的 Maven 仓库你需要在构建脚本中声明该仓库maven { name CaffeineMC url https://maven.caffeinemc.net/releases // 或 /snapshots }Kotlin DSL 写法maven { name CaffeineMC url uri(https://maven.caffeinemc.net/releases) // 或 /snapshots }仓库带有 Web 前端可浏览所有可用版本。依赖坐标与装载方式依赖坐标因平台与 Minecraft 版本而异Fabric 1.21.11 使用modImplementationFabric 1.21.12 与 NeoForge 1.21.11 使用implementationFabricdependencies { // ... other dependencies // 使用打标签的 API 构建 modImplementation net.caffeinemc:sodium-fabric-api:0.8.0mc1.21.11 // 或使用快照构建 modImplementation net.caffeinemc:sodium-fabric-api:0.8.0-SNAPSHOTmc1.21.11-pre3-build.773 }NeoForgedependencies { // ... other dependencies // 使用打标签的 API 构建 implementation net.caffeinemc:sodium-neoforge-api:0.8.0mc1.21.11 // 或使用快照构建 implementation net.caffeinemc:sodium-neoforge-api:0.8.0-SNAPSHOTmc1.21.11-pre3-build.773 }注意上述版本号来自文档原文实际应使用与你所用 Sodium / Minecraft 版本匹配的坐标。声明 Config EntrypointEntrypoint 类负责承载选项注册代码既可以在 Mod 元数据中声明也可以在 NeoForge 上用注解声明。方式一元数据声明元数据声明使用键sodium:config_api_user其值为实现net.caffeinemc.mods.sodium.api.config.ConfigEntryPoint接口的类的完整引用。该键名常量在 ConfigManager.java 中定义为CONFIG_ENTRY_POINT_KEY。Fabricfabric.mod.json{ entrypoints: { // ... other entrypoints sodium:config_api_user: [ com.example.examplemod.ExampleModConfigBuilder ] } }NeoForgeneoforge.mods.toml[modproperties.examplemod] sodium:config_api_user com.example.examplemod.ExampleModConfigBuilder方式二NeoForge 注解声明NeoForge 习惯用注解标记 Entrypoint因此提供了ConfigEntryPointForge(examplemod)作为替代方案。注解必须传入 Mod ID用于确定该配置通过ConfigBuilder.registerOwnModOptions()注册时归属的默认 Mod否则无法唯一确定类与 Mod 的对应关系。注解定义见 ConfigEntryPointForge.java其收集逻辑见 ConfigLoaderForge.java——Sodium 会扫描所有 Mod 的 ASM 注解数据校验value指定的 Mod ID 确实存在后注册。import net.caffeinemc.mods.sodium.api.config.ConfigEntryPoint; import net.caffeinemc.mods.sodium.api.config.ConfigEntryPointForge; ConfigEntryPointForge(examplemod) public class ExampleConfigUser implements ConfigEntryPoint { // 类体与上文完全相同 }Entrypoint 的收集与实例化在 Fabric 侧ConfigLoaderFabric.java 通过FabricLoader.getInstance().getEntrypointContainers(CONFIG_ENTRY_POINT_KEY, ConfigEntryPoint.class)收集所有声明了该入口的 Mod并将每个容器注册为ConfigUserSodium 自身的SodiumConfigBuilder也会被注册modId 为sodium。在 ConfigManager.java 中通过类名加载的 Entrypoint 会被反射构造要求存在无参构造器若类缺失、类型不符或构造失败只记录警告日志并跳过不会崩溃。编写一个最小可用的配置 Entrypoint以下完整示例文档原文示例含注释补充展示了晚期注册的完整链路package com.example.examplemod; import net.caffeinemc.mods.sodium.api.config.ConfigEntryPoint; import net.caffeinemc.mods.sodium.api.config.structure.ConfigBuilder; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; public class ExampleConfigUser implements ConfigEntryPoint { private final OptionStorage storage new OptionStorage(); private final Runnable handler this.storage::flush; // 通常会被多处引用 Override public void registerConfigLate(ConfigBuilder builder) { builder.registerOwnModOptions() .setIcon(Identifier.parse(examplemod:textures/gui/icon.png)) .addPage(builder.createOptionPage() .setName(Component.literal(Example Page)) .addOptionGroup(builder.createOptionGroup() .setName(Component.literal(Example Group)) // 仅当为清晰起见才有必要 .addOption(builder.createBooleanOption(Identifier.parse(examplemod:example_option)) .setName(Component.literal(Example Option)) // 实际请使用翻译键 .setTooltip(Component.literal(Example tooltip)) .setStorageHandler(this.handler) .setBinding(this.storage::setExampleOption, this.storage::getExampleOption) .setDefaultValue(true) ) ) ); } } // OptionStorage.java class OptionStorage { private boolean exampleOption true; public boolean getExampleOption() { return this.exampleOption; } public void setExampleOption(boolean value) { this.exampleOption value; } public void flush() { // flush options to config file } }Sodium 会为每个 Entrypoint 构造一个实例并在合适的时机调用早期与晚期注册方法。ConfigBuilder 与 ModOptions注册你的配置结构三种注册入口ConfigBuilder是传入 Entrypoint 方法的根 Builder 接口见 ConfigBuilder.java提供三类注册入口registerOwnModOptions()为当前 Entrypoint 所属的 Mod 注册配置Mod 名称与版本会自动从元数据查询registerModOptions(String configId)为指定 Mod ID 注册配置名称版本自动查询registerModOptions(String configId, String name, String version)显式指定配置 ID、名称与版本。在 ConfigBuilderImpl.java 中registerOwnModOptions()实际以 Entrypoint 关联的defaultConfigId为键调用registerModOptionsmodInfoFunction 由平台侧注入Fabric 读取FabricLoader元数据NeoForge 读取ModList。此外ConfigBuilder还提供一系列创建方法createColorTheme()、createOptionPage()、createExternalPage()、createOptionGroup()、createBooleanOption(Identifier)、createIntegerOption(Identifier)、createEnumOption(Identifier, ClassE)以及createExternalButtonOption(Identifier)。ModOptionsBuilder见 ModOptionsBuilder.java允许配置 Mod 级元数据setName(String)显示名称setVersion(String)版本字符串通常由 Mod 加载器自动填充formatVersion(FunctionString, String)原始版本到显示版本的格式化函数例如 Sodium 自身在 SodiumConfigBuilder.java 中用splitWithDelimiters(\\, 2)截取分隔的构建元数据setColorTheme(ColorThemeBuilder)设置主题色可选setIcon(Identifier)/setNonTintedIcon(Identifier)设置单色着色 / 原色图标addPage(PageBuilder)追加配置页registerOptionReplacement(...)、registerOptionOverlay(...)、registerFlagHook(...)高级能力下文详解。页面与分组每个 Mod 在页面列表中拥有独立的头部。OptionPageBuilder见 OptionPageBuilder.java除setName(Component)与addOptionGroup(...)外还支持addOption(OptionBuilder)——直接添加到页面的选项会被归入页面底部的一个隐式未命名分组。OptionGroupBuilder见 OptionGroupBuilder.java只用于在 UI 中视觉分组setName是可选的、未设置则标题不显示官方建议通常不需要命名分组选项名称已足够表达分类。外部页与外部按钮若希望点击页面列表中的某一项时直接切换到自定义Screen用ConfigBuilder.createExternalPage()创建外部页再配置名称与ConsumerScreen接收当前屏幕并切换到你的自定义屏幕即可接口见 ExternalPageBuilder.java。同理createExternalButtonOption(Identifier)创建的选项在点击时也会通过setScreenConsumer(ConsumerScreen)打开自定义屏幕见 ExternalButtonOptionBuilder.java。选项类型与 OptionBuilder 详解选项的通用属性每个选项具有IDIdentifier、名称、Tooltip、存储处理器、绑定binding与默认值。共有三种状态型选项类型布尔复选框、整数滑块、枚举。所有类型的选项都可以被禁用disabled整数与枚举选项还可以限制允许值的范围后两种类型还必须设置为每个选定值分配标签的函数。基础接口 OptionBuilder.java 提供setName、setTooltip、setEnabled与setEnabledProvider带状态的选项继承自 StatefulOptionBuilder.java额外提供方法说明setStorageHandler(StorageEventHandler)选项变更通过绑定应用后调用的处理器典型用途是一次性把变更 flush 到配置文件而不是每次变更都写盘setTooltip(Component)/setTooltip(FunctionV, Component)静态 Tooltip或根据选项当前值动态生成 Tooltip对枚举这类描述过长的选项尤其有用setImpact(OptionImpact)性能影响级别从低到高或因情况而异setFlags(OptionFlag... / Identifier...)控制该选项应用时重置什么重载区块、重载资源包等setDefaultValue(V)/setDefaultProvider(...)默认值当绑定返回值不满足选项约束整数 / 枚举时使用setControlHiddenWhenDisabled(boolean)禁用时是否隐藏控件默认隐藏仅当用户即便无法交互也应知晓选项状态时才设为 falsesetBinding(ConsumerV, SupplierV)/setBinding(OptionBindingV)变更应用、值不再满足约束而重置为默认值、以及初始化时加载值时调用setApplyHook(ConsumerConfigState)选项保存后值有变化时运行的回调可直接访问当前ConfigState无需声明依赖因为钩子不返回值其中setApplyHook在实现上等价于为该选项创建一个单例自定义 Flag 并注册对应 Flag Hook——源码 Config.java 中collectApplyHooks()确实把每个 apply hook 包装成ApplyHookFlagHook再注册。性能影响 OptionImpactOptionImpact.java 定义了四个等级每个等级都附带翻译键与颜色样式等级语义LOW绿色更改选项不会带来可测量或可察觉的性能影响MEDIUM黄色在部分场景、部分系统上可能有明显影响HIGH金色在多数场景下很可能有显著影响VARIES白色影响高度依赖具体场景与系统内置 Flag 与自定义 FlagOptionFlag.java 定义了五个内置 Flag每个 Flag 对应一个以sodium:builtin_option_flag.name形式生成的IdentifierFlag触发的行为REQUIRES_RENDERER_RELOAD丢弃并重建全部网格破坏性较强仅在必要时使用源码中调用levelExtractor.allChanged()REQUIRES_RENDERER_UPDATE剔除状态可能变化重新计算遮挡剔除对用户无可见扰动调用clearVisibleSections()REQUIRES_ASSET_RELOAD重载资源包然后重载渲染器导致所有网格被丢弃重建REQUIRES_VIDEOMODE_RELOAD更新视频模式切换分辨率 / 全屏模式有扰动REQUIRES_GAME_RESTART需要重启游戏才能生效在控制台输出警告消息Flag 的处理集中在 Config.java 的processFlagsSodium 通过applyAllOptions()/applyOption(id)应用变更后统一触发。内置 Flag 之外你可以把任意Identifier作为自定义 Flag 通过setFlags(Identifier...)挂到选项上再用registerFlagHook监听。各类型选项的专属方法布尔选项BooleanOptionBuilder.java布尔值类型无额外必填方法。整数选项IntegerOptionBuilder.javasetRange(int min, int max, int step)/setRange(Range)必填定义滑块的起点、终点与步长setRangeProvider(FunctionConfigState, ? extends SteppedValidator, Identifier...)动态范围setValidator(SteppedValidator)/setValidatorProvider(...)设置校验器Range即一种 stepped validatorsetValueFormatter(ControlValueFormatter)必填把整数值格式化为Component显示。Range见 Range.java是一个 record构造时会校验min max、step 0SteppedValidator.isValueValid要求值落在[min, max]内且(value - min) % step 0见 SteppedValidator.java不合法时返回默认值。Sodium 自带大量现成格式化器ControlValueFormatterImpls位于 ControlValueFormatterImpls.java不属于 API 包例如brightness()、fpsLimit()、guiScale()、percentage()、multiplier()、translateVariable(key)、number()等可直接在实现类中参考其写法。枚举选项EnumOptionBuilder.javasetElementNameProvider(FunctionE, Component)必填为每个枚举常量提供显示名可用静态辅助nameProviderFrom(Component... names)按 ordinal 顺序映射setAllowedValues(SetE)/setAllowedValuesProvider(...)限制用户可选的枚举元素集合枚举选项同样支持动态 TooltipsetTooltip(FunctionE, Component)。必填校验在 StatefulOptionBuilderImpl.java 中集中完成存储处理器、Tooltip provider、默认值、绑定、名称等缺失都会导致注册失败整数的setValueFormatter与枚举的setElementNameProvider缺失同样会失败。动态值Dependent Values与依赖检查动态值提供器形如set___Provider的动态方法接收一个 provider该 provider 被调用时会拿到当前选项存储状态的引用ConfigState见 ConfigState.java。要读取其他选项的值你必须把这些选项的 ID 作为依赖参数传给该方法读取未声明为依赖的选项会抛出异常。ConfigState提供了三种读取方法readBooleanOption(Identifier)、readIntOption(Identifier)、readEnumOption(Identifier, ClassE)。可动态化的属性包括默认值setDefaultProvider(FunctionConfigState, V, Identifier...)启用状态setEnabledProvider(FunctionConfigState, Boolean, Identifier...)允许值枚举的setAllowedValuesProvider、整数的setRangeProvider/setValidatorProvider。实现上动态值被封装为 DynamicValue.java 并缓存计算结果依赖变化时通过invalidateCache()失效。依赖关系会在 Config.java 的validateDependencies()中校验依赖指向不存在的选项会抛异常随后对所有选项做 DFS 检测发现环Cycle detected in dependency graph starting from option ...会直接抛异常——由于动态属性可能改变选项状态循环依赖会导致注册失败并使游戏崩溃。另外 Option.java 在构造时也禁止选项依赖自身。特殊依赖ConfigState提供了两个特殊 ID均为__meta__命名空间ConfigState.UPDATE_ON_REBUILD声明依赖它的 provider 会在 UI 重建时被刷新ConfigState.UPDATE_ON_APPLY声明依赖它的 provider 会在其所属选项的值被应用时刷新这是唯一允许动态属性依赖自身选项值的情况仍不允许读取其他未声明的选项。源码 Config.java 将这两个 ID 列为SPECIAL_DEPENDENCIES并在校验中豁免依赖UPDATE_ON_REBUILD的动态值被收集进globalRebuildDependents依赖UPDATE_ON_APPLY的动态值被注册为所属选项的 apply-dependent 并获准读取父选项allowReadingParentOption。官方明确提示这两个特殊依赖通常并不需要使用它们多半是误用——Sodium 只在处理行为极其特殊的GUI Scale界面缩放选项时使用它们。例如 SodiumConfigBuilder.java 中GUI Scale 的 validator provider 依赖UPDATE_ON_REBUILD与UPDATE_ON_APPLY根据保存值与窗口实际可计算比例动态得出GUIScaleRange。选项覆写与替换Overlay / Replacement修改 Sodium 或其他 Mod 已有选项的范围或行为是常见需求有两种方式Replacement替换通过ModOptionsBuilder.registerOptionReplacement(Identifier target, OptionBuilder replacement)提供一个目标 ID 与一个按常规方式构建的新选项新选项的 ID 会替换旧选项的 ID你也可以选择让新选项沿用旧 ID从而继续作为其他 Overlay 的目标。源码 Config.java 会收集所有 override按优先级排序registerOptionReplacement(..., int priority)可指定优先级默认 0高优先级胜出同一目标同优先级出现多个 override 会报错再替换原选项并更新索引exchangeOption。注意override / overlay 不能指向自己所属 Mod 的选项Config.applyOptionChanges()会检查目标命名空间与modConfig.configId()是否相同并抛异常。Overlay覆写通过ModOptionsBuilder.registerOptionOverlay(Identifier target, OptionBuilder overlay)提供部分选项——用一个平时构建选项时用的 Builder 只设置你想改写的属性。目标选项的对应属性会被新值替换未设置的部分保持不变。实现上overlay 构建器会基于目标选项base option调用buildWithBaseOption生成新选项见 Config.java。覆写目标必须匹配一个已存在的选项 ID要么由其他 Mod 定义要么由本 Mod 的 Replacement 定义。若目标选项已被替换overlay 必须瞄准替换后选项的 ID可能与原 ID 相同也可能不同。overlay 同样支持优先级参数规则与 replacement 一致。自定义 Flags 与 Flag Hook通过ModOptionsBuilder.registerFlagHook(BiConsumerCollectionIdentifier, ConfigState hook, Identifier... triggers)或FlagHook接口见 FlagHook.java你可以在指定集合中的任一 Flag 被触发后运行代码。触发集合既可以包含自定义 Flag即任意Identifier也可以包含元 Flag例如OptionFlag.REQUIRES_RENDERER_RELOAD.getId()。流程是选项被应用且值已保存后其所有挂载 Flag 一起触发applyAllOptions收集所有变更选项的 Flag见 Config.java。hook 收到的是全部触发的 Flag 集合——可能大于它感兴趣的集合这是刻意设计避免为每个 hook 生成不同的 Flag 集合属于性能优化。同一 Flag 上注册的多个 hook 在一次处理中只会触发一次triggeredHooks去重。图标与主题色设计规范视频设置页的 UI 面向单色、二值透明度binary-alpha图标设计图标会被着色为各 Mod 的主题色。建议遵守以下规范完整摘要见 ModOptionsBuilder.java图标应为方形纹理如 64×64、128×128除非主体填满整个方块否则背景应透明采用二值透明度完全不透明或完全透明细节克制——图标最终以小尺寸渲染使用setIcon设置时图标会以主题色单色着色因此纹理本身应为纯白色若想展示原色图标用setNonTintedIcon。不设置图标时 UI 布局会自动调整。主题色是可选的不设置时系统会从预定义色集中确定性地随机选择。若自定义主题色应遵循以下约束ColorThemeBuilderImpl.java 中实现了MIN_THEME_SATURATION 0.2f、MIN_THEME_BRIGHTNESS 0.55f的 HSV 约束饱和度不能太低——它作为强调色accent color必须保持本色明度 / 亮度不能太暗——考虑美观与可访问性基础色也不能太亮——否则更亮的 highlight 色将与其失去对比。ColorThemeBuilder见 ColorThemeBuilder.java提供两种配置方式setBaseThemeRGB(int)只给基础色亮色与暗色自动派生或setFullThemeRGB(int theme, int highlight, int disabled)完整指定三个 RGB 整数Alpha 位被忽略。Sodium 自身在 SodiumConfigBuilder.java 用Colors.THEME / THEME_LIGHTER / THEME_DARKER设置了完整主题。存储处理器与绑定写入策略StorageEventHandler见 StorageEventHandler.java是一个函数式接口afterSave()在选项通过绑定保存后被调用。它的设计意图是只 flush 一次你把 flush 逻辑挂到这个处理器上而不是在每个选项的 save 回调里各自写盘。源码 Config.java 中变更的选项会把各自的存储处理器收集到pendingStorageHandlers集合去重随后flushStorageHandlers()统一调用afterSave()并清空。Sodium 自身的实现可作参考Fabric / NeoForge 的SodiumConfigBuilder分别用vanillaStorage调用vanillaOpts.save()与sodiumStorage调用SodiumOptions.writeToDisk作为存储处理器见 SodiumConfigBuilder.java。绑定binding在以下时机被调用变更被应用时save 方向值不再满足约束被重置为默认值时初始化时——Config.java 在构造 Config 后对每个选项调用loadValueInitial()与resetAllOptionsFromBindings()从绑定初值加载load 方向。若绑定加载的值不满足约束例如越界或步长不符则该值会被默认值静态setDefaultValue或动态setDefaultProvider替代。参考Sodium 自身的注册实现USAGE.md 指出Sodium 自己的选项注册是理解 API 的最佳深度示例——即 SodiumConfigBuilder.java它实现了ConfigEntryPoint并在registerConfigEarly中注册了性能页的早期选项、在registerConfigLate中构建了完整配置通用 / 画质 / 性能三个页面。该文件展示了大量实战写法动态 TooltipsetTooltip(i - ...)依据当前枚举值切换翻译键如图形 API 选项区分 Vulkan 与 OpenGL 提示组合绑定全屏模式选项同时修改原版的 fullscreen 与 exclusiveFullscreen 两个选项动态启用全屏分辨率选项通过setEnabledProvider读取sodium:general.fullscreen_mode仅在独占全屏模式下启用并声明该依赖见 SodiumConfigBuilder.javaapply hook云距离选项用setApplyHook((_) - levelRenderer.cloudRenderer().markForRebuild())在保存后重建云渲染器枚举名称映射EnumOptionBuilder.nameProviderFrom(...)按 ordinal 顺序映射枚举常量显示名。这些模式可直接迁移到你自己的 Mod 实现中。常见问题与注意事项注册失败即崩溃必填属性缺失、重复的 configId、依赖不存在的选项、依赖环、对自身选项做 override/overlay都会导致ConfigManager以崩溃报告终止游戏ConfigManager.java。请在开发阶段充分测试只有晚期注册的选项出现在 GUI早期阶段主要用于窗口创建前必须解析的少量选项图标是单色设计setIcon的纹理请使用纯白色 二值透明度原色图标请用setNonTintedIcon动态 provider 只读已声明依赖读取未声明依赖的选项会抛异常循环依赖会导致崩溃UPDATE_ON_REBUILD/UPDATE_ON_APPLY慎用它们是为 GUI Scale 这类特殊选项准备的一般场景请勿使用API 不负责持久化配置文件的加载与保存需要你自己实现或借助第三方配置库存储处理器只负责变更后 flush这一时机。至此你已经掌握了从构建依赖、Entrypoint 声明、选项注册到动态值、覆写替换、自定义 Flag 的完整 Sodium Config API 使用链路对照 USAGE.md 与仓库中common/src/api/java/net/caffeinemc/mods/sodium/api/config/下的接口、common/src/main/java/net/caffeinemc/mods/sodium/client/config/下的实现即可开始为你的 Mod 接入这套视频设置界面。赞分享图形学游戏开发【免费下载链接】sodium-fabricA high-performance rendering engine replacement for Minecraft, which greatly improves frame rates and reduces micro-stutter项目地址https://gitcode.com/gh_mirrors/so/sodium-fabric点击查看免费下载相关推荐daisyUI设置页面应用配置界面完整指南daisyUI设置页面应用配置界面完整指南 daisyUI作为最流行的免费开源Tailwind CSS组件库提供了强大的配置系统来满足不同项目的需求。通过d前端UI组件SukiUI 设置页布局指南使用 SettingsLayout 构建自适应分栏设置界面SukiUI 设置页布局指南使用 SettingsLayout 构建自适应分栏设置界面 本指南以 SukiUIAvaloniaUI 的 UI 主题库中的UI组件桌面应用BetterDiscord插件设置界面使用Settings组件构建配置页BetterDiscord插件设置界面使用Settings组件构建配置页 你是否还在为Discord插件配置页开发繁琐而头疼从设计UI元素到状态管理每个环桌面应用UI组件上一篇BerryIO完整功能解析GPIO控制、摄像头监控、LCD显示一站式解决方案下一篇Disque网络通信协议解析节点间数据同步机制创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

源码7k-PHP人才机遇桥梁招聘求职全能系统小程序源码

源码7k-PHP人才机遇桥梁招聘求职全能系统小程序源码

PHP 人才机遇桥梁招聘求职全能系统小程序源码 PHP 人才机遇桥梁招聘求职全能系统,是一套面向人力资源场景的小程序源码方案,依托 PHP 后端架构,搭配微信小程序前端,打通求职者、企业、平台管理员三方业务链路,适配普通…

2026/10/15 6:43:49 阅读更多 →
虚拟机搭建Hadoop大数据环境:从JDK到伪分布式保姆级教程

虚拟机搭建Hadoop大数据环境:从JDK到伪分布式保姆级教程

先说个背景。平时遇到很多朋友学大数据,第一反应就是把自己工作电脑当实验场,直接在物理机上装 JDK、Hadoop。装完就后悔:系统里其他 Java 项目依赖被环境变量搞乱,HDFS 的临时文件把磁盘塞满,想模拟多节点集群又找不出…

2026/10/15 6:42:48 阅读更多 →
context-mode:微服务运行时上下文管理与全链路透传实践

context-mode:微服务运行时上下文管理与全链路透传实践

context-mode:让程序随时知道"自己在哪、在为谁服务"深夜十一点半,手机连着响了五声,值班群里有人发了一连串告警截图:订单服务把测试环境的优惠策略下发到了生产网关,短短三分钟产生了上百笔异常订单。我一…

2026/10/15 6:42:48 阅读更多 →

最新新闻

专家PID控制MATLAB程序代码:规则表与增量式PID融合实现

专家PID控制MATLAB程序代码:规则表与增量式PID融合实现

简介:这份资源是面向自动化、控制工程方向学习者与科研人员的专家PID控制MATLAB程序代码文档,适合已掌握常规PID、希望进一步理解智能控制策略的中高级读者。资源以PDF形式呈现,共1个文件,压缩包约85KB,内容围绕专家PI…

2026/10/15 11:00:08 阅读更多 →
深度学习如何提升视觉SLAM性能:从特征提取到不确定性建模

深度学习如何提升视觉SLAM性能:从特征提取到不确定性建模

1. 视觉SLAM的“眼睛”与“脑子”:为什么传统方法卡在精度和鲁棒性的死循环里 视觉SLAM(Simultaneous Localization and Mapping)不是个新概念,但直到今天,很多刚接触ROS小车建图、PX4无人机自主导航的朋友&#xff0c…

2026/10/15 11:00:08 阅读更多 →
Matlab实现车辆路径规划(VRP)算法详解

Matlab实现车辆路径规划(VRP)算法详解

1. 车辆路径规划问题概述 车辆路径规划(Vehicle Routing Problem, VRP)是运筹学中一个经典且实用的研究方向,主要解决如何在满足各种约束条件下,为车队设计最优的配送路线。这个问题最早由Dantzig和Ramser在1959年提出&#xff0c…

2026/10/15 11:00:08 阅读更多 →
专家PID控制MATLAB程序代码:规则表到闭环实现

专家PID控制MATLAB程序代码:规则表到闭环实现

简介:这份资源是面向自动化、控制工程方向学习者与科研人员的专家PID控制MATLAB程序代码文档,适合已掌握常规PID、希望进一步理解智能控制策略的中高级读者。资源以PDF形式呈现,共1个文件,压缩包约85KB,内容围绕专家PI…

2026/10/15 11:00:08 阅读更多 →
社区健康管理系统毕业设计实战:Vue+Spring Boot+AI风险预测

社区健康管理系统毕业设计实战:Vue+Spring Boot+AI风险预测

如果一个社区的居民健康管理还停留在纸质档案和Excel表格的阶段,光是整理高血压随访记录就能让人崩溃。青湖社区健康管理系统就是为这种场景做的:面向社区基层的健康管理平台,前端用Vue搭建,后端提供稳定接口服务,同时…

2026/10/15 11:00:08 阅读更多 →
GC2145 datasheet实战:从寄存器手册到图像链路调试

GC2145 datasheet实战:从寄存器手册到图像链路调试

简介:GC2145数据手册面向嵌入式视觉、摄像头模组与图像采集方向的硬件工程师及驱动开发者,用于快速掌握这款1/5英寸UXGA CMOS图像传感器的规格与设计要点。资源包共1个PDF文件,约814KB,即Galaxy Core发布的GC2145 CSP Datasheet V…

2026/10/15 10:59:04 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →