diff --git a/.gitea/workflows/publish-nuget.yml b/.gitea/workflows/publish-nuget.yml
new file mode 100644
index 0000000..06dc7b7
--- /dev/null
+++ b/.gitea/workflows/publish-nuget.yml
@@ -0,0 +1,52 @@
+name: Publish NuGet packages
+
+on:
+ push:
+ tags:
+ - 'v*'
+ workflow_dispatch:
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+ env:
+ NUGET_AUTH_TOKEN: ${{ secrets.SHRINKSDK_PACKAGE_TOKEN }}
+ steps:
+ - name: Checkout tagged source
+ uses: actions/checkout@v4
+
+ - name: Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Validate tag and pack projects
+ env:
+ GITEA_REF: ${{ gitea.ref }}
+ shell: bash
+ run: |
+ set -euo pipefail
+ tag="${GITEA_REF#refs/tags/}"
+ version="$(node -p "require('./package.json').version")"
+ test "$tag" = "v$version"
+ mkdir -p packages
+ mapfile -d '' projects < <(find DotNet~ Godot~ -type f -name '*.csproj' -print0 2>/dev/null || true)
+ test "${#projects[@]}" -gt 0
+ for project in "${projects[@]}"; do
+ dotnet restore "$project" --configfile NuGet.Config
+ dotnet pack "$project" --configuration Release --no-restore --output "$PWD/packages" --include-symbols --include-source
+ done
+ find packages -maxdepth 1 -name '*.nupkg' -type f | grep -q .
+
+ - name: Publish packages
+ shell: bash
+ run: |
+ set -euo pipefail
+ : "${NUGET_AUTH_TOKEN:?SHRINKSDK_PACKAGE_TOKEN is required}"
+ dotnet nuget push 'packages/*.nupkg' --api-key "$NUGET_AUTH_TOKEN" --source https://git.crash.work/api/packages/ShrinkSDK/nuget/index.json --skip-duplicate
+ if compgen -G 'packages/*.snupkg' > /dev/null; then
+ dotnet nuget push 'packages/*.snupkg' --api-key "$NUGET_AUTH_TOKEN" --source https://git.crash.work/api/packages/ShrinkSDK/nuget/index.json --skip-duplicate
+ fi
diff --git a/.gitignore b/.gitignore
index 246935d..c3b8764 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,11 @@
/Tools~/**/[Oo]bj/
*.user
*.DotSettings.user
+/DotNet~/**/[Bb]in/
+/DotNet~/**/[Oo]bj/
+/Godot~/**/[Bb]in/
+/Godot~/**/[Oo]bj/
+/artifacts/
+/packages/
+!DotNet~/**/*.csproj
+!Godot~/**/*.csproj
diff --git a/.npmignore b/.npmignore
index 700f03d..6a59c77 100644
--- a/.npmignore
+++ b/.npmignore
@@ -6,3 +6,7 @@ Tools~/
*.sln
*.user
*.DotSettings.user
+DotNet~/
+Godot~/
+NuGet.Config
+Directory.Build.props
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..43b188b
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,13 @@
+
+
+ latest
+ enable
+ true
+ true
+ ShrinkSDK
+ ShrinkSDK
+ https://git.crash.work/ShrinkSDK
+ true
+ snupkg
+
+
diff --git a/DotNet~/Runtime/ShrinkDataSaverRuntime.cs b/DotNet~/Runtime/ShrinkDataSaverRuntime.cs
new file mode 100644
index 0000000..8cc5d94
--- /dev/null
+++ b/DotNet~/Runtime/ShrinkDataSaverRuntime.cs
@@ -0,0 +1,43 @@
+#nullable enable
+
+using System.IO;
+using Cysharp.Threading.Tasks;
+using ShrinkSDK.Runtime;
+
+namespace ShrinkDataSaver;
+
+public sealed class ShrinkDataSaverRuntimeConfig
+{
+ public string? RootPath { get; set; }
+ public string SaveFileExtension { get; set; } = ".sav";
+ public string SettingsFileName { get; set; } = "settings.json";
+ public int CurrentSaveVersion { get; set; } = 1;
+ public int MaxSlots { get; set; } = 3;
+ public float SettingsWriteDebounceSeconds { get; set; } = 0.35f;
+ public bool DontDestroyOnLoadDriver { get; set; }
+}
+
+public static class ShrinkDataSaverRuntime
+{
+ public static bool IsInitialized { get; private set; }
+
+ public static void Initialize(ShrinkDataSaverRuntimeConfig? config = null)
+ {
+ if (IsInitialized) return;
+ config ??= new ShrinkDataSaverRuntimeConfig();
+ var root = string.IsNullOrWhiteSpace(config.RootPath)
+ ? ShrinkRuntimeServices.Paths.PersistentDataPath
+ : config.RootPath!;
+ Directory.CreateDirectory(root);
+ var storage = new LocalStorageProvider(root);
+ ShrinkDataSaverPlatform.SettingsWriteDebounceSeconds = config.SettingsWriteDebounceSeconds;
+ ShrinkDataSaverPlatform.MaxSlots = config.MaxSlots;
+ ShrinkSettings.Initialize(storage, Path.Combine(root, config.SettingsFileName));
+ ShrinkSave.Initialize(storage, Path.Combine(root, "saves"), config.SaveFileExtension,
+ config.CurrentSaveVersion);
+ ShrinkSettings.LoadAsync().Forget();
+ IsInitialized = true;
+ ShrinkRuntimeServices.Logger.Log(ShrinkLogLevel.Information,
+ $"[ShrinkDataSaver] Runtime initialized. Root: {root}");
+ }
+}
diff --git a/DotNet~/ShrinkSDK.DataSaver.csproj b/DotNet~/ShrinkSDK.DataSaver.csproj
new file mode 100644
index 0000000..95585ca
--- /dev/null
+++ b/DotNet~/ShrinkSDK.DataSaver.csproj
@@ -0,0 +1,29 @@
+
+
+ netstandard2.1
+ false
+ ShrinkDataSaver.Runtime
+ ShrinkDataSaver
+ ShrinkSDK.DataSaver
+ 2.3.0
+ ShrinkSDK engine-neutral versioned save and settings runtime.
+ false
+ disable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NuGet.Config b/NuGet.Config
new file mode 100644
index 0000000..43e134b
--- /dev/null
+++ b/NuGet.Config
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index 3aa07cd..ab84cad 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# ShrinkDataSaver
-一个为 Unity C# 项目设计的模块化存档与设置管理系统。支持多存档槽、链式版本迁移、可选 AES-256 加密、关键模块保护、跨模块只读查询,以及完整的事件驱动架构。
+一个可用于 Unity、Godot 和普通 .NET 宿主的模块化存档与设置管理系统。支持多存档槽、链式版本迁移、可选 AES-256 加密、关键模块保护、跨模块只读查询,以及完整的事件驱动架构。
+
+Godot 和普通 .NET 项目安装 `ShrinkSDK.DataSaver`;可打包源码位于 `DotNet~`,持久化路径与生命周期由宿主平台服务注入。
## ✨ 特性概览