DSPCore 是戴森球计划模组的新通用底层标准。
- 为常见模组基础设施提供统一 API。
- 逐步替代分散的 LDBTool、DSPModSave、CommonAPI、BuildBarTool、ErrorAnalyzer 式诊断、成就策略聚合、图标注册和旧 API 兼容需求。
- 保留旧 API 命名空间作为
[Obsolete]兼容层,让已有模组可以先运行,再逐步迁移。
- Thunderstore 包名:
MengLei-DSPCore - DLL 名称:
DSPCore.dll - 新命名空间:
DSPCore - 英文说明:
README-EN.md
DSPCore/:主 BepInEx 插件项目,内部拆成Authoring/作者能力和Systems/系统集成两片。DSPCore/Authoring/:模组作者直接调用的能力,例如 Core、DataPhases、ProtoAccess、Items、Recipes、Techs、Tutorials、Tabs、BuildBar、Resources、Icons、GameEnums、KeyBinds、Saves、Achievements、Diagnostics、Components、Planets、Blueprints、Models、Options、Multiplayer、Networks、Galaxy 和 UI。DSPCore/Systems/:DSPCore 对作者声明的运行时处理,例如生命周期、Proto pipeline、分页投射、选择器 surface、快捷栏投射、资源加载、存档桥、成就策略、作者声明诊断和错误窗口。DSPCore.Preloader/:BepInEx patchers 项目,用于游戏程序集加载前 patch。DSPCore.Packaging/:Thunderstore 打包项目。
- 提供 P0/P1 的作者可见能力:功能生命周期、数据阶段、原型访问、物品/配方/科技/指引注册、建造栏位置、资源、图标、本地化、分页、原版游戏枚举扩展、按键、存档、成就策略和通用 UI 框架。
- 提供
xiaoye97.LDBTool、crecheng.DSPModSave、CommonAPI和BuildBarTool的旧 API 兼容层;兼容代码按所属作者能力放入Authoring/<Capability>/Compat/,不再使用集中式Legacy/目录。 - 公开 API 提供中英文 XML summary。
当前版本已接入 P0/P1 和第一批 P2/P3 运行时桥接:BepInEx/Harmony 启动、Preloader 字段和枚举预留槽注入、Proto 写入、多行建造栏绑定、玩家覆盖、RebindBuildBar 配置导入、资源/图标加载、物品/配方/制造器/信号/标签图标分页、选择器弹窗和实时过滤、自定义配方类型选择前过滤、自定义物品类型标记入口、按键回调、DSPCore 独立存档、旧 DSPModSave 处理器桥接、实体组件生命周期、星球/恒星/银河系统生命周期、蓝图参数块、模型和预制体克隆、配置项绑定、作者声明诊断、可选联机软桥、网络查询适配器、补丁平台、成就异常检查和竞争性上传策略补丁、可带游戏/实体上下文的错误报告和可复制诊断文本、错误窗口复制/关闭按钮、本地化条目和通用 UI 窗口生命周期转发。
P0/P1 是当前实现目标。
- 功能生命周期:声明能力块、依赖、优先级和初始化,并可通过
Lifecycle注册 DSPCore 启动、更新、销毁和常见存档链路回调。 - 数据阶段:
Data、DataUpdates和DataFinalFixes。 - 原型相关能力:DataPhases 提供三阶段;ProtoAccess 通过
ProtoPhaseContext.FindItem(...)/FindRecipe(...)和data.Access承接第二/第三阶段读取和修改他人注册数据;Items、Recipes、Techs、Tutorials 分别负责对应 proto 注册;ItemProto/RecipeProto可用对象短入口设置GridIndex、绑定图标并注册,TechProto/TutorialProto可用对象短入口直接注册;ProtoRegistration 保留低层聚合和兼容入口。 - 建造栏位置:将
ItemProto或物品 ID 绑定到 tab/row/index 槽位;第 1 行写入原版建造栏,第 2 行及以后使用 DSPCore 扩展按钮,并保留 BuildBarTool 兼容入口。其他作者能力,例如物品注册,首选在拿到ItemProto后调用ItemProto.SetBuildBar(...);BuildBar 不承担 Proto 创建职责。 - 资源、图标和本地化:通过
ModResources登记资源根和翻译条目;同一模组可用ModResources.Pack(...)复用 owner、资源根和 assembly;通过Icons.FromResources(...)、Icons.FromFile(...)、Icons.FromEmbedded(...)、Icons.FromAssetBundle(...)或Icons.BindToProto(...)注册图标,目标类型明确时优先使用pack.ItemIcon(...)、RecipeIcon(...)等 typed helper。 - 分页:作者可以声明自定义页面并取得
TabSlot,再用TabSlot生成物品/配方GridIndex。选择器 surface 属于 DSPCore 系统实现。 - 游戏枚举:
GameEnums.RegisterRecipeType(...)声明自定义配方类型限制,ItemProto.SetCustomItemType()标记 DSPCore 预留的自定义物品类型;运行时代码使用GameEnums.CustomRecipeTypeValue/CustomItemTypeValue避免直接编译期依赖 Preloader 注入字段。 - 存档:
Saves.Auto<TState>(...)自动创建无参状态对象并注册 schema,Saves.Auto(modGuid, state)支持已有实例,另有委托式简单存档处理器、原始BinaryReader/BinaryWriter处理器和 tagged block 工具。 - 成就策略:声明是否影响银河系/排行榜上传等策略。错误窗口和错误收集属于 DSPCore 系统实现。
- 作者声明诊断:
Diagnostics.Warn(...)/Error(...)可把模组自己的声明问题写入统一诊断;DSPCore 启动期也会检查 Proto ID、GridIndex、Tab 图标、Option 页面和本地化基础语言配对。 - UI 框架:窗口生命周期、标签页窗口、基础控件、声明式网格布局、主题卡片辅助,以及标准表单、列表、详情区和状态页脚脚手架;不包含具体业务页面。
- 实体组件:用
Components.Register<TComponent>(...)按 item id、model index 或PrefabDesc条件给实体挂自定义组件,转发移除、tick 和存档;复杂构造再使用 descriptor。 - 星球/恒星/银河系统:用
PlanetSystems.Register<TSystem>(...)、GalaxySystems.RegisterStar<TSystem>(...)或RegisterGalaxy<TSystem>(...)注册系统,按PlanetFactory、StarData或GalaxyData创建实例并转发生命周期;复杂构造再使用 descriptor。 - 蓝图参数:用
Blueprints.Register(blockId, ownerModGuid, copy, paste, ...)注册整数负载 tagged block,避免多个模组抢BuildingParameters.parameters固定槽位;复杂 block 处理再使用 descriptor。 - 模型和预制体:从已有
ModelProto克隆新模型,配置独立PrefabDesc,并重建模型派生缓存;调用方已有来源对象时优先使用ModelProto.CloneAsModel(...),只有来源 model index 时再使用Models.CloneModel(...)。 - 配置、联机和网络:提供
Options.Page(...).Section(...)页面上下文和Options.String/Bool/Int/Float/Enum/IntRange/FloatRange短入口,支持同名方法加OptionUi设置显示名、同页排序和 Reset 按钮,提供 BepInEx 配置绑定、DSPCore 统一设置窗口、设置页面、设置版本描述、配置导入/导出、Nebula 软检测、packet/host relay/planet data/client save 声明、adapter snapshot/query 入口、联机 descriptor 高级重载,以及Networks.Register(...)工厂网络查询适配器短入口。 - 补丁平台:通过
Patches.Register(...)/RegisterForPlugin(...)集中声明条件补丁、必需插件 GUID/version、禁用原因和应用失败报告;descriptor 保留为高级路径。
已接入运行时桥接:
DSPCorePlugin通过 BepInEx 启动并应用 Harmony 补丁。Lifecycle会在 DSPCore 运行时桥接装配后触发OnStarted,在插件更新和销毁时触发OnUpdate/OnDestroyed,并从 SaveRuntime 转发OnNewGame、OnBeforeSave、OnBeforeLoad、OnAfterLoad和OnSaveDeleted。- 原型注册会按类似 Factorio 的
Data、DataUpdates、DataFinalFixes三阶段执行回调;阶段上下文提供当前可见 Proto 查询和修改入口;运行时在VFPreload.InvokeOnLoadWorkEnded前后写入对应 Proto,并在最终修正后重建ProtoSet索引和关键派生缓存。 BuildBarRegistry.BindQuickBar会把物品 ID 或ItemProto映射到建造栏 tab/row/index 槽位;第 1 行写入原版UIBuildMenu.protos,第 2 行及以后使用 DSPCore 扩展按钮。BuildBar.SetPlayerOverride(...)会写入玩家覆盖层并保存到.dspcore,运行时总是用作者默认绑定叠加玩家覆盖后的有效绑定。没有 DSPCore BuildBar 存档数据时,DSPCore 会从 RebindBuildBar 的CustomBarBind.cfg导入第 1 行玩家配置。IconSetRegistry可以加载 UnityResourcessprite、本地 PNG 文件、已加载 assembly 中的嵌入 PNG 或 AssetBundle 中的Sprite/Texture2D,缓存后写入目标 Proto;作者侧短入口是Icons.FromResources(...)、Icons.FromFile(...)、Icons.FromEmbedded(...)、Icons.FromAssetBundle(...)和Icons.BindToProto(...)。同一模组有统一资源根时,可先创建ModResources.Pack(...),再用 pack 上的图标方法减少重复参数;常见物品/配方/科技/指引/信号图标绑定应优先使用pack.ItemIcon(...)、RecipeIcon(...)、TechIcon(...)、TutorialIcon(...)和SignalIcon(...)。TabRegistry会为稳定页面 ID 分配TabSlot,并通过现有 GridIndex 分类流程把自定义页面投射到物品选择器、配方选择器、制造器界面、信号选择器和标签图标选择器。PickerSurfaces会处理物品、配方和信号选择器 surface,实时网格会应用过滤、重复GridIndex兜底和动态行列扩容。GameEnums.RegisterRecipeType(...)当前会把声明的配方标记为自定义配方类型,并在制作器配方列表打开前隐藏当前机器不能使用的配方;ItemProto.SetCustomItemType()会把已有物品标记为 DSPCore 预留的自定义物品类型;RecipeTypes保留为旧别名,AssemblerComponent.SetRecipe仍保留最终保护。KeyBindRegistry会轮询已注册按键并调用回调;作者侧短入口是KeyBinds.Register(id, ownerModGuid, displayName, defaultKey, callback, ...),支持简单的Ctrl/Alt/Shift修饰键组合;CanOverride=true的按键会进入 DSPCore 统一设置窗口,玩家可用 Capture 捕获按键或直接编辑文本,同一ConflictGroup内的同键配置会显示冲突提示;运行时优先读取玩家配置,配置为空或非法时回落默认键。SaveRegistry会写入.dspcore独立存档,并按CoreLoadOrder导入处理器。AchievementPolicyRegistry汇总每个模组的竞争性上传阻断声明;DSPCore 始终屏蔽原版异常检查并保持本地/平台成就可用,任意模组调用Achievements.BlockCompetitiveUpload(...)时只阻断 Milky Way / Steam 排行榜上传。disableAchievements旧参数保留为兼容名,当前语义是阻止竞争性上传。ErrorWindow会接收 Unity fatal/error 日志和错误窗口事件;作者可用Errors.ReportException(..., ErrorDiagnosticContext)把明确的星球/实体上下文存进错误报告,诊断文本会展示报告自带上下文,并可额外加入本次复制的上下文、当前游戏状态、最近错误、候选插件文本命中、DSPCore 声明和 Harmony patch map 概览。Diagnostics会在启动期运行作者声明检查,发现 warning/error 时写入 BepInEx 日志并进入Errors.BuildDiagnosticText(...)的 Diagnostics 段;作者也可以用Diagnostics.Warn(...)、Error(...)或Info(...)手动加入模组自己的声明问题。ResourceRegistry.RegisterLocalization会写入 DSP 本地化 key 和语言字符串;作者侧短入口是ModResources.Root(...)、ModResources.Text(...)和ModResources.Pack(...)。UiWindowManager会在UIRoot打开、更新和销毁时转发 DSPCore 窗口生命周期;具体窗口由模组自己创建和打开。Components会在PlanetFactory.CreateEntityLogicComponents后按描述创建组件;无参构造组件可用Components.Register<TComponent>(...)短入口注册。运行时会在实体移除、电力 tick、工厂 tick 和后置阶段转发回调;组件数据写入.dspcore,未加载星球的数据会延迟到GameData.GetOrCreateFactory后恢复。Planets会在GameData.GetOrCreateFactory后为每个PlanetFactory创建星球系统;无参构造系统可用PlanetSystems.Register<TSystem>(...)短入口注册。运行时转发本地星球绘制、电力 tick、工厂 tick 和后置阶段。Blueprints会把作者参数块编码到BuildingParameters参数数组尾部,在复制、蓝图、粘贴和预建筑落地链路中保持 block ID;简单参数块可用Blueprints.Register(...)直接处理int[]负载。Models会在最终 Proto 派生缓存重建前克隆ModelProto和PrefabDesc,然后重建ModelProto索引和PlanetFactory.PrefabDescByModelIndex。Options会把作者声明的字符串配置项绑定到 DSPCore 的 BepInEx 配置文件,并保存设置页面和设置版本描述;Options.Page(...).Section(...)可先固定设置页和配置分区,String、Bool、Int、Float、Enum、IntRange和FloatRange会注册配置并返回当前值,需要显示名、排序或 Reset 按钮时可给同名短入口传入OptionUi;ExportValues/ExportText与ImportValues/ImportText提供配置快照导入导出;Options.OpenWindow()会打开 DSPCore 自有统一设置窗口。Multiplayer当前检测 Nebula 是否加载,保存 packet、host relay、planet data request 和 client missing-save 声明,并提供 adapter snapshot/query 入口和 descriptor 高级重载;真实 Nebula 发送由专门适配器接入。Networks提供Register(...)适配器短入口,以及TryGetCommonNetwork(...)和IsConnectedToNetwork(...)查询表面;具体网络扫描由注册适配器提供。Galaxy会在银河数据存在后创建恒星/银河系统;无参构造系统可用GalaxySystems.RegisterStar<TSystem>(...)/RegisterGalaxy<TSystem>(...)短入口注册。运行时在SpaceSector.GameTick转发更新和 sidecar 存档。PatchRuntime会应用Patches.Register(...)或PatchDescriptor声明的条件补丁,记录禁用原因和失败异常。
当前运行时限制:
- RebindBuildBar 的
BuildBarBinds配置会导入到 DSPCore 第 1 行玩家覆盖层;DSPCore 不接管 RebindBuildBar 自己的重绑 UI、快捷键或后续配置写回。 - 分页投射当前覆盖原版
UIItemPicker、UIRecipePicker、UIReplicatorWindow、UISignalPicker和UISignalTagPicker。蓝图图标、蓝图说明图标、智能输入框图标等使用这些原版 picker 的界面会受益;DSPCore 不按 GenesisBook、OrbitalRing、FE 等插件 GUID 跳过注入。真正自建且不复用原版 picker 的第三方界面需要专门 adapter。 - 选择器行列数由运行时汇总
GridIndex后计算;DSPCore 会以当前 UI surface 的实际尺寸为基准,统计所有相关物品、配方或信号格子需要的最大行列,并同步扩展数组、材质、鼠标命中和显示尺寸。模组不需要也不能单独声明 picker 长宽。 - UI 框架只提供通用架子,不注册具体页面、业务导航、解锁条件或存档状态。
- 当前 Proto 阶段挂点是保守的第一版桥接,不是最终 VFPreload 中段生命周期。
- 可选联机桥当前不直接发送 Nebula packet;需要专门 Nebula 适配器读取 DSPCore multiplayer registry。
- 网络模块当前是查询适配平台,不内置所有原版或第三方网络扫描。
- 玩家便利模块,例如 RecipeFinder、FreeMechaCustom、AssemblerUI 风格功能,仍未并入核心。
using DSPCore;
ProtoRegistration.Data("com.example.my-mod", data =>
{
data.RegisterItem(
itemProto.SetGridIndex(tab: 3, row: 1, index: 5),
"Declare base item")
.SetBuildBar(tab: 3, row: 1, index: 5);
});
ProtoRegistration.DataUpdates("com.example.my-mod", data =>
{
data.RegisterRecipe(
recipeProto.SetGridIndex(tab: 3, row: 1, index: 6),
"Attach recipe after item declarations");
ItemProto baseItem = data.FindItem(1001);
if (baseItem != null)
baseItem.GridIndex = GridIndexes.From(tab: 3, row: 1, index: 5);
});
ProtoRegistration.DataFinalFixes("com.example.my-mod", data =>
{
data.RegisterTutorial(tutorialProto, "Final tutorial chain fix");
});using DSPCore;
Achievements.BlockCompetitiveUpload("com.example.my-mod");
bool blockUpload = Achievements.ShouldBlockCompetitiveUpload();不调用表示该模组不要求阻止竞争性上传;多个模组同时声明时,任意 true 胜出。本地成就、平台成就和平台元数据调用保持可用。详细边界见 DSPCore/Authoring/Achievements/README.md。
using DSPCore;
myItemProto.SetBuildBar(tab: 3, row: 2, index: 5);
BuildBar.BindQuickBar(tab: 3, row: 2, index: 4, itemId: 9554);using DSPCore;
OptionSection settings = Options
.Page("com.example.settings", "com.example.my-mod", "Example Settings")
.Section("Example");
bool enabled = settings.Bool("Enabled", true, "Enable example feature.");
int rows = settings.Int("Rows", 2, "Example row count.");
int maxRows = settings.IntRange("MaxRows", 3, "Maximum rows.", minimum: 1, maximum: 6);
// 在按钮、快捷键或自定义 UI 回调中打开,调用时 UIRoot 必须已经初始化。
// Open from a button, key bind, or custom UI callback after UIRoot is ready.
Options.OpenWindow();
var pack = ModResources.Pack(
ownerModGuid: "com.example.my-mod",
rootPath: "assets/icons",
assembly: typeof(MyPlugin).Assembly);
pack.Text("ExampleMachines", "zhCN", "示例机器");
pack.IconFromEmbedded("example-embedded", "ExampleMod.Assets.example.png");
pack.IconFromAssetBundle("example-bundle", "example-icons", "example-machine");
pack.ItemIcon("example-machine", "example.png", itemId: 9554);using DSPCore;
private sealed class ExampleState
{
[CoreSaveField("counter")]
public int Counter { get; set; }
}
private static readonly ExampleState State = Saves.Auto<ExampleState>("com.example.auto-mod");
Saves.Register(
modGuid: "com.example.my-mod",
export: writer => writer.Write(counter),
import: reader => counter = reader.ReadInt32(),
intoOtherSave: () => counter = 0);
Lifecycle.OnStarted(InitializeAfterDspCore);
Lifecycle.OnBeforeSave(saveName => FlushTransientCache(saveName));
Lifecycle.OnAfterLoad(RebuildTransientCache);using DSPCore;
Patches.Register(
id: "example.core-patch",
ownerModGuid: "com.example.my-mod",
apply: ApplyCorePatch,
description: "Apply example runtime patch.",
isEnabled: IsFeatureEnabled,
getDisabledReason: () => "example feature is disabled");
Patches.RegisterForPlugin(
id: "example.target-plugin-integration",
ownerModGuid: "com.example.my-mod",
requiredPluginGuid: "com.example.target-plugin",
apply: ApplyTargetPluginIntegration,
description: "Enable integration when the target plugin is loaded.",
minimumPluginVersion: "1.2.0");using DSPCore;
TabSlot machinesTab = Tabs.AddTab(
id: "example-machines",
ownerModGuid: "com.example.my-mod",
title: "Example Machines",
iconId: "example-machine-tab",
order: 100);
itemProto.GridIndex = ProtoRegistration.GetGridIndex(machinesTab, row: 1, index: 5);
recipeProto.GridIndex = ProtoRegistration.GetGridIndex(machinesTab, row: 1, index: 5);#pragma warning disable CS0618
BuildBarTool.BuildBarTool.SetBuildBar(3, 4, 9554, true);
#pragma warning restore CS0618旧调用会被接受,但会标记为 obsolete。新模组应首选使用 ItemProto.SetBuildBar(...),只有手上只有物品 ID 时再使用 DSPCore.BuildBar.BindQuickBar(...)。
README-EN.md- 能力示例采用
Examples/<Scenario>.md+Examples/<Scenario>Example.cs成对文件;.cs示例只作为文档产物,不参与编译。 DSPCore/Authoring/Core/Examples/LifecycleExample.csDSPCore/Authoring/Core/Examples/Lifecycle.mdDSPCore/Authoring/Core/Examples/ModuleDeclarationExample.csDSPCore/Authoring/Core/Examples/ModuleDeclaration.mdDSPCore/Authoring/Core/Examples/PatchPlatformExample.csDSPCore/Authoring/Core/Examples/PatchPlatform.mdDSPCore/Authoring/Achievements/Examples/AchievementPolicyExample.csDSPCore/Authoring/Achievements/Examples/AchievementPolicy.mdDSPCore/Authoring/Diagnostics/Examples/DeclarationDiagnosticsExample.csDSPCore/Authoring/Diagnostics/Examples/DeclarationDiagnostics.mdDSPCore/Authoring/BuildBar/Examples/QuickBarBindingExample.csDSPCore/Authoring/BuildBar/Examples/QuickBarBinding.mdDSPCore/Authoring/Saves/Examples/SaveHandlerExample.csDSPCore/Authoring/Saves/Examples/SaveHandler.mdDSPCore/Authoring/Saves/Examples/SaveBlocksExample.csDSPCore/Authoring/Saves/Examples/SaveBlocks.mdDSPCore/Authoring/Icons/Examples/IconSetRegistrationExample.csDSPCore/Authoring/Icons/Examples/IconSetRegistration.mdDSPCore/Authoring/Resources/Examples/ResourceRegistrationExample.csDSPCore/Authoring/Resources/Examples/ResourceRegistration.mdDSPCore/Authoring/Items/Examples/ItemAuthoringChainExample.csDSPCore/Authoring/Items/Examples/ItemAuthoringChain.mdDSPCore/Authoring/Recipes/Examples/RecipeAuthoringChainExample.csDSPCore/Authoring/Recipes/Examples/RecipeAuthoringChain.mdDSPCore/Authoring/Tabs/Examples/TabRegistrationExample.csDSPCore/Authoring/Tabs/Examples/TabRegistration.mdDSPCore/Systems/PickerSurfaces/Examples/PickerRequestExample.csDSPCore/Systems/PickerSurfaces/Examples/PickerRequest.mdDSPCore/Authoring/GameEnums/Examples/RecipeTypeRegistrationExample.csDSPCore/Authoring/GameEnums/Examples/RecipeTypeRegistration.mdDSPCore/Authoring/DataPhases/Examples/ProtoPhasesExample.csDSPCore/Authoring/DataPhases/Examples/ProtoPhases.mdDSPCore/Authoring/KeyBinds/Examples/KeyBindRegistrationExample.csDSPCore/Authoring/KeyBinds/Examples/KeyBindRegistration.mdDSPCore/Authoring/UI/Examples/WindowScaffoldExample.csDSPCore/Authoring/UI/Examples/WindowScaffold.mdDSPCore/Authoring/Components/Examples/EntityComponentExample.csDSPCore/Authoring/Components/Examples/EntityComponent.mdDSPCore/Authoring/Planets/Examples/PlanetSystemExample.csDSPCore/Authoring/Planets/Examples/PlanetSystem.mdDSPCore/Authoring/Blueprints/Examples/BuildingParametersExample.csDSPCore/Authoring/Blueprints/Examples/BuildingParameters.mdDSPCore/Authoring/Models/Examples/CloneModelExample.csDSPCore/Authoring/Models/Examples/CloneModel.mdDSPCore/Authoring/Options/Examples/OptionRegistrationExample.csDSPCore/Authoring/Options/Examples/OptionRegistration.mdDSPCore/Authoring/Multiplayer/Examples/SoftPacketExample.csDSPCore/Authoring/Multiplayer/Examples/SoftPacket.mdDSPCore/Authoring/Networks/Examples/CommonNetworkExample.csDSPCore/Authoring/Networks/Examples/CommonNetwork.mdDSPCore/Authoring/Galaxy/Examples/GalaxyLifecycleExample.csDSPCore/Authoring/Galaxy/Examples/GalaxyLifecycle.md