From 4930099af7c215217bc0c422ee2dc7a5634942e2 Mon Sep 17 00:00:00 2001 From: cneicy Date: Wed, 26 Aug 2026 02:49:29 +0800 Subject: [PATCH] chore: initialize standalone UPM package --- .gitea/workflows/publish.yml | 49 ++++ .gitea/workflows/unity-verify.yml | 39 +++ .gitignore | 10 + .npmignore | 8 + CHANGELOG.md | 15 ++ CHANGELOG.md.meta | 7 + Development~/UnityProject/.gitignore | 6 + Development~/UnityProject/Assets/.gitkeep | 0 .../UnityProject/Packages/manifest.json | 15 ++ .../ProjectSettings/ProjectVersion.txt | 2 + Editor.meta | 8 + Editor/ShrinkApp.Starter.Basic.Editor.asmdef | 23 ++ ...ShrinkApp.Starter.Basic.Editor.asmdef.meta | 7 + Editor/ShrinkAppBasicStarterGenerator.cs | 248 ++++++++++++++++++ Editor/ShrinkAppBasicStarterGenerator.cs.meta | 11 + README.md | 47 ++++ README.md.meta | 7 + Runtime.meta | 8 + .../ShrinkApp.Starter.Basic.Runtime.asmdef | 29 ++ ...hrinkApp.Starter.Basic.Runtime.asmdef.meta | 7 + Runtime/ShrinkAppBasicContextComposition.cs | 78 ++++++ .../ShrinkAppBasicContextComposition.cs.meta | 11 + Runtime/ShrinkAppBasicDemoRuntime.cs | 214 +++++++++++++++ Runtime/ShrinkAppBasicDemoRuntime.cs.meta | 11 + Runtime/ShrinkAppBasicSampleController.cs | 173 ++++++++++++ .../ShrinkAppBasicSampleController.cs.meta | 11 + Runtime/ShrinkAppBasicStarterSettings.cs | 17 ++ Runtime/ShrinkAppBasicStarterSettings.cs.meta | 11 + package.json | 36 +++ package.json.meta | 7 + 30 files changed, 1115 insertions(+) create mode 100644 .gitea/workflows/publish.yml create mode 100644 .gitea/workflows/unity-verify.yml create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 CHANGELOG.md create mode 100644 CHANGELOG.md.meta create mode 100644 Development~/UnityProject/.gitignore create mode 100644 Development~/UnityProject/Assets/.gitkeep create mode 100644 Development~/UnityProject/Packages/manifest.json create mode 100644 Development~/UnityProject/ProjectSettings/ProjectVersion.txt create mode 100644 Editor.meta create mode 100644 Editor/ShrinkApp.Starter.Basic.Editor.asmdef create mode 100644 Editor/ShrinkApp.Starter.Basic.Editor.asmdef.meta create mode 100644 Editor/ShrinkAppBasicStarterGenerator.cs create mode 100644 Editor/ShrinkAppBasicStarterGenerator.cs.meta create mode 100644 README.md create mode 100644 README.md.meta create mode 100644 Runtime.meta create mode 100644 Runtime/ShrinkApp.Starter.Basic.Runtime.asmdef create mode 100644 Runtime/ShrinkApp.Starter.Basic.Runtime.asmdef.meta create mode 100644 Runtime/ShrinkAppBasicContextComposition.cs create mode 100644 Runtime/ShrinkAppBasicContextComposition.cs.meta create mode 100644 Runtime/ShrinkAppBasicDemoRuntime.cs create mode 100644 Runtime/ShrinkAppBasicDemoRuntime.cs.meta create mode 100644 Runtime/ShrinkAppBasicSampleController.cs create mode 100644 Runtime/ShrinkAppBasicSampleController.cs.meta create mode 100644 Runtime/ShrinkAppBasicStarterSettings.cs create mode 100644 Runtime/ShrinkAppBasicStarterSettings.cs.meta create mode 100644 package.json create mode 100644 package.json.meta diff --git a/.gitea/workflows/publish.yml b/.gitea/workflows/publish.yml new file mode 100644 index 0000000..56028aa --- /dev/null +++ b/.gitea/workflows/publish.yml @@ -0,0 +1,49 @@ +name: Publish UPM package + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + env: + NODE_AUTH_TOKEN: ${{ secrets.SHRINKSDK_PACKAGE_TOKEN }} + steps: + - name: Fetch tagged revision + shell: bash + run: | + set -eu + ref="${{ gitea.sha }}" + test -n "$ref" + git init . + git remote add origin "https://git.crash.work/ShrinkSDK/ShrinkApp.Starter.Basic.git" + git fetch --depth=1 origin "$ref" + git checkout --detach FETCH_HEAD + + - name: Validate immutable release version + shell: bash + run: | + set -eu + tag="$(git describe --exact-match --tags HEAD)" + version="$(node -p "require('./package.json').version")" + test "$tag" = "v$version" + npm pack --dry-run + + - name: Publish to ShrinkSDK registry + shell: bash + run: | + set -eu + : "${NODE_AUTH_TOKEN:?SHRINKSDK_PACKAGE_TOKEN is required}" + npmrc="$HOME/.npmrc" + cleanup() { rm -f "$npmrc"; } + trap cleanup EXIT + printf '%s\n' \ + 'registry=https://git.crash.work/api/packages/ShrinkSDK/npm/' \ + '//git.crash.work/api/packages/ShrinkSDK/npm/:_authToken=${NODE_AUTH_TOKEN}' > "$npmrc" + npm publish --registry=https://git.crash.work/api/packages/ShrinkSDK/npm/ \ No newline at end of file diff --git a/.gitea/workflows/unity-verify.yml b/.gitea/workflows/unity-verify.yml new file mode 100644 index 0000000..d14a95a --- /dev/null +++ b/.gitea/workflows/unity-verify.yml @@ -0,0 +1,39 @@ +name: Verify standalone Unity package + +on: + workflow_dispatch: + +jobs: + editmode: + runs-on: unity-2022.3.62f3 + container: + image: docker.1panel.live/unityci/editor:ubuntu-2022.3.62f3-windows-mono-3 + volumes: + - /www/dk_project/bt-recovery/gitea/runner/seedskey-unity-license:/root/.local/share/unity3d/Unity + - /www/dk_project/bt-recovery/gitea/runner/seedskey-unity-entitlements:/root/.config/unity3d/Unity/licenses + steps: + - name: Fetch selected revision + shell: bash + run: | + set -eu + ref="${{ gitea.sha }}" + git init . + git remote add origin "https://git.crash.work/ShrinkSDK/ShrinkApp.Starter.Basic.git" + git fetch --depth=1 origin "$ref" + git checkout --detach FETCH_HEAD + + - name: Run package EditMode tests + shell: bash + run: | + set -eu + unity_bin="$(command -v unity-editor || command -v unity || command -v Unity || true)" + test -n "$unity_bin" + "$unity_bin" \ + -batchmode \ + -nographics \ + -quit \ + -projectPath "$PWD/Development~/UnityProject" \ + -runTests \ + -testPlatform EditMode \ + -testResults "$PWD/TestResults/editmode.xml" \ + -logFile "$PWD/TestResults/unity.log" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..246935d --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/Development~/UnityProject/[Ll]ibrary/ +/Development~/UnityProject/[Tt]emp/ +/Development~/UnityProject/[Oo]bj/ +/Development~/UnityProject/[Ll]ogs/ +/Development~/UnityProject/[Uu]ser[Ss]ettings/ +/Development~/UnityProject/TestResults/ +/Tools~/**/[Bb]in/ +/Tools~/**/[Oo]bj/ +*.user +*.DotSettings.user diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..700f03d --- /dev/null +++ b/.npmignore @@ -0,0 +1,8 @@ +.git/ +.gitea/ +Development~/ +Tools~/ +*.csproj +*.sln +*.user +*.DotSettings.user diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0b1bc05 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +本文件记录 `ShrinkApp.Starter.Basic` 的包内变更。 + +## [0.1.0] - 2026-05-18 + +### Added + +- 新增最小起盘 Starter 包。 +- 新增场景生成器 `ShrinkAppBasicStarterGenerator`。 +- 新增示例控制器 `ShrinkAppBasicSampleController`。 +- 新增 `ShrinkAppBasicDemoRuntime`,补齐默认命令源、状态命令与最小 loopback 网络示例。 +- 新增 `ShrinkAppBasicStarterSettings`,提供最小 Starter 级别的默认配置资产。 +- 示例场景继续扩展为小型调试台,补齐命令输入、输出日志、模块/服务/存档/命令/网络状态面板。 +- 状态面板进一步细化为已启动模块、已注册服务、存档槽位、命令列表、网络诊断五块只读视图。 diff --git a/CHANGELOG.md.meta b/CHANGELOG.md.meta new file mode 100644 index 0000000..eba6b24 --- /dev/null +++ b/CHANGELOG.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e3e78d4720b16b84fbf0d766b0a0122b +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Development~/UnityProject/.gitignore b/Development~/UnityProject/.gitignore new file mode 100644 index 0000000..aa2eb00 --- /dev/null +++ b/Development~/UnityProject/.gitignore @@ -0,0 +1,6 @@ +[Ll]ibrary/ +[Tt]emp/ +[Oo]bj/ +[Ll]ogs/ +[Uu]ser[Ss]ettings/ +TestResults/ \ No newline at end of file diff --git a/Development~/UnityProject/Assets/.gitkeep b/Development~/UnityProject/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Development~/UnityProject/Packages/manifest.json b/Development~/UnityProject/Packages/manifest.json new file mode 100644 index 0000000..6254dbd --- /dev/null +++ b/Development~/UnityProject/Packages/manifest.json @@ -0,0 +1,15 @@ +{ + "scopedRegistries": [ + { + "name": "ShrinkSDK", + "url": "https://git.crash.work/api/packages/ShrinkSDK/npm/", + "scopes": [ + "com.cneicy" + ] + } + ], + "dependencies": { + "com.unity.test-framework": "1.1.33", + "com.cneicy.shrink-app-starter-basic": "file:../../.." + } +} diff --git a/Development~/UnityProject/ProjectSettings/ProjectVersion.txt b/Development~/UnityProject/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..587f809 --- /dev/null +++ b/Development~/UnityProject/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2022.3.62f3 +m_EditorVersionWithRevision: 2022.3.62f3 (96770f904ca7) diff --git a/Editor.meta b/Editor.meta new file mode 100644 index 0000000..ffcd571 --- /dev/null +++ b/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f6be676789111fd4a9afd67b011a12c9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/ShrinkApp.Starter.Basic.Editor.asmdef b/Editor/ShrinkApp.Starter.Basic.Editor.asmdef new file mode 100644 index 0000000..73fa11f --- /dev/null +++ b/Editor/ShrinkApp.Starter.Basic.Editor.asmdef @@ -0,0 +1,23 @@ +{ + "name": "ShrinkApp.Starter.Basic.Editor", + "rootNamespace": "ShrinkApp.Starter.Basic.Editor", + "references": [ + "ShrinkApp.Starter.Basic.Runtime", + "ShrinkApp.Core.Runtime", + "ShrinkApp.Core.Editor", + "ShrinkContext.AppAdapter.Runtime", + "ShrinkDataSaver.Runtime", + "ShrinkDataSaver.Integration.App", + "UnityEngine.UI" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} diff --git a/Editor/ShrinkApp.Starter.Basic.Editor.asmdef.meta b/Editor/ShrinkApp.Starter.Basic.Editor.asmdef.meta new file mode 100644 index 0000000..0b63870 --- /dev/null +++ b/Editor/ShrinkApp.Starter.Basic.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: aa988409913004043b6cbe3d19de32bd +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/ShrinkAppBasicStarterGenerator.cs b/Editor/ShrinkAppBasicStarterGenerator.cs new file mode 100644 index 0000000..a6394bb --- /dev/null +++ b/Editor/ShrinkAppBasicStarterGenerator.cs @@ -0,0 +1,248 @@ +#if UNITY_EDITOR +using ShrinkApp.Editor; +using ShrinkContext.AppAdapter; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.UI; + +namespace ShrinkApp.Starter.Basic.Editor +{ + public static class ShrinkAppBasicStarterGenerator + { + private const string MenuPath = "ShrinkApp/Starter/生成 Basic Entry 场景"; + private const string CompositionMenuPath = "ShrinkApp/Starter/创建默认 Cordis Composition"; + private const string CompositionPath = "Assets/Resources/ShrinkAppComposition.asset"; + + [MenuItem(MenuPath)] + public static void Generate() + { + EnsureAssets(); + BuildScene(); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + Debug.Log("[ShrinkApp.Starter.Basic] 已生成 Basic Entry 场景与配置。"); + } + + [MenuItem(CompositionMenuPath)] + public static void CreateDefaultCompositionAsset() + { + EnsureResourcesDirectory(); + var existing = AssetDatabase.LoadAssetAtPath(CompositionPath); + if (existing != null) + { + Selection.activeObject = existing; + return; + } + + // 修复脚本/asmdef 变更期间可能留下的无主 .asset,而不是在同一路径叠加创建。 + if (!string.IsNullOrEmpty(AssetDatabase.AssetPathToGUID(CompositionPath))) + AssetDatabase.DeleteAsset(CompositionPath); + + var composition = ScriptableObject.CreateInstance(); + composition.SetDocument(ShrinkAppBasicContextComposition.CreateDefaultDocument()); + AssetDatabase.CreateAsset(composition, CompositionPath); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + Selection.activeObject = composition; + } + + private static void EnsureAssets() + { + ShrinkAppEditorMenu.CreateSettingsAsset(); + + EnsureResourcesDirectory(); + + const string dataSaverSettingsPath = "Assets/Resources/ShrinkDataSaverSettings.asset"; + if (AssetDatabase.LoadAssetAtPath(dataSaverSettingsPath) == null) + { + var asset = ScriptableObject.CreateInstance("ShrinkDataSaverSettings"); + if (asset == null) + throw new System.InvalidOperationException( + "Cannot create ShrinkDataSaverSettings. Ensure ShrinkDataSaver.Runtime is imported and compiled."); + + AssetDatabase.CreateAsset(asset, dataSaverSettingsPath); + } + + const string starterSettingsPath = "Assets/Resources/ShrinkAppBasicStarterSettings.asset"; + if (AssetDatabase.LoadAssetAtPath(starterSettingsPath) == null) + { + var starterAsset = ScriptableObject.CreateInstance(); + starterAsset.autoBindLoopbackTransport = true; + starterAsset.defaultCommandText = "starter status"; + starterAsset.defaultLoopbackSessionId = 1; + starterAsset.logCommandOutputToConsole = true; + starterAsset.maxCommandOutputLines = 12; + AssetDatabase.CreateAsset(starterAsset, starterSettingsPath); + } + + CreateDefaultCompositionAsset(); + } + + private static void EnsureResourcesDirectory() + { + const string resourcesDir = "Assets/Resources"; + if (!AssetDatabase.IsValidFolder(resourcesDir)) + AssetDatabase.CreateFolder("Assets", "Resources"); + } + + private static void BuildScene() + { + const string scenesDir = "Assets/Scenes"; + if (!AssetDatabase.IsValidFolder(scenesDir)) + AssetDatabase.CreateFolder("Assets", "Scenes"); + + var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); + + new GameObject("Main Camera", typeof(Camera)); + new GameObject("EventSystem", typeof(UnityEngine.EventSystems.EventSystem), + typeof(UnityEngine.EventSystems.StandaloneInputModule)); + + var canvasGo = new GameObject("Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster)); + var canvas = canvasGo.GetComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + var scaler = canvasGo.GetComponent(); + scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + scaler.referenceResolution = new Vector2(1920, 1080); + + var panelGo = new GameObject("Panel", typeof(RectTransform), typeof(Image)); + panelGo.transform.SetParent(canvasGo.transform, false); + var panelRect = panelGo.GetComponent(); + panelRect.sizeDelta = new Vector2(980f, 720f); + panelRect.anchoredPosition = Vector2.zero; + panelGo.GetComponent().color = new Color(0.12f, 0.15f, 0.2f, 0.92f); + + var statusText = CreateText("StatusText", panelGo.transform, new Vector2(0f, 240f), new Vector2(840f, 120f), + "ShrinkApp Basic starter ready."); + statusText.alignment = TextAnchor.MiddleCenter; + var detailText = CreateText("DetailText", panelGo.transform, new Vector2(0f, 135f), new Vector2(840f, 140f), + "app=False, save=False, cmd=False, net=False"); + detailText.alignment = TextAnchor.UpperLeft; + detailText.fontSize = 20; + var commandInputField = CreateInputField("CommandInput", panelGo.transform, new Vector2(0f, 20f), + new Vector2(520f, 54f), "starter status"); + var outputText = CreateText("OutputText", panelGo.transform, new Vector2(0f, -250f), new Vector2(840f, 180f), + string.Empty); + outputText.alignment = TextAnchor.UpperLeft; + outputText.fontSize = 20; + outputText.horizontalOverflow = HorizontalWrapMode.Wrap; + outputText.verticalOverflow = VerticalWrapMode.Overflow; + var saveMetaText = CreateText("SaveMetaText", panelGo.transform, new Vector2(-280f, -430f), new Vector2(240f, 150f), + "No save slots found."); + saveMetaText.alignment = TextAnchor.UpperLeft; + saveMetaText.fontSize = 18; + var moduleText = CreateText("ModuleText", panelGo.transform, new Vector2(0f, -430f), new Vector2(240f, 150f), + "No started modules."); + moduleText.alignment = TextAnchor.UpperLeft; + moduleText.fontSize = 18; + var serviceText = CreateText("ServiceText", panelGo.transform, new Vector2(280f, -430f), new Vector2(240f, 150f), + "No registered services."); + serviceText.alignment = TextAnchor.UpperLeft; + serviceText.fontSize = 18; + var commandListText = CreateText("CommandListText", panelGo.transform, new Vector2(-280f, -600f), new Vector2(240f, 150f), + "Command service not ready."); + commandListText.alignment = TextAnchor.UpperLeft; + commandListText.fontSize = 18; + var networkText = CreateText("NetworkText", panelGo.transform, new Vector2(280f, -600f), new Vector2(240f, 150f), + "Network service not ready."); + networkText.alignment = TextAnchor.UpperLeft; + networkText.fontSize = 18; + + var saveButton = CreateButton("SaveButton", panelGo.transform, new Vector2(-250f, -70f), "Save Slot 0"); + var loadButton = CreateButton("LoadButton", panelGo.transform, new Vector2(0f, -70f), "Load Slot 0"); + var commandButton = CreateButton("CommandButton", panelGo.transform, new Vector2(250f, -70f), "Run Command"); + var loopbackButton = CreateButton("LoopbackButton", panelGo.transform, new Vector2(-250f, -165f), "Bind Loopback"); + var disconnectButton = CreateButton("DisconnectLoopbackButton", panelGo.transform, new Vector2(0f, -165f), "Disconnect Loopback"); + var refreshButton = CreateButton("RefreshButton", panelGo.transform, new Vector2(250f, -165f), "Refresh Status"); + var clearOutputButton = CreateButton("ClearOutputButton", panelGo.transform, new Vector2(0f, -345f), "Clear Output"); + + var controller = panelGo.AddComponent(); + var serializedObject = new SerializedObject(controller); + serializedObject.FindProperty("statusText").objectReferenceValue = statusText; + serializedObject.FindProperty("detailText").objectReferenceValue = detailText; + serializedObject.FindProperty("outputText").objectReferenceValue = outputText; + serializedObject.FindProperty("moduleText").objectReferenceValue = moduleText; + serializedObject.FindProperty("serviceText").objectReferenceValue = serviceText; + serializedObject.FindProperty("saveMetaText").objectReferenceValue = saveMetaText; + serializedObject.FindProperty("commandListText").objectReferenceValue = commandListText; + serializedObject.FindProperty("networkText").objectReferenceValue = networkText; + serializedObject.FindProperty("saveButton").objectReferenceValue = saveButton; + serializedObject.FindProperty("loadButton").objectReferenceValue = loadButton; + serializedObject.FindProperty("commandButton").objectReferenceValue = commandButton; + serializedObject.FindProperty("loopbackButton").objectReferenceValue = loopbackButton; + serializedObject.FindProperty("refreshButton").objectReferenceValue = refreshButton; + serializedObject.FindProperty("clearOutputButton").objectReferenceValue = clearOutputButton; + serializedObject.FindProperty("disconnectLoopbackButton").objectReferenceValue = disconnectButton; + serializedObject.FindProperty("commandInputField").objectReferenceValue = commandInputField; + serializedObject.ApplyModifiedPropertiesWithoutUndo(); + + EditorSceneManager.SaveScene(scene, "Assets/Scenes/ShrinkAppEntry.unity"); + } + + private static Text CreateText(string name, Transform parent, Vector2 anchoredPosition, Vector2 size, string text) + { + var go = new GameObject(name, typeof(RectTransform), typeof(Text)); + go.transform.SetParent(parent, false); + var rect = go.GetComponent(); + rect.sizeDelta = size; + rect.anchoredPosition = anchoredPosition; + var label = go.GetComponent(); + label.text = text; + label.font = Resources.GetBuiltinResource("LegacyRuntime.ttf"); + label.fontSize = 28; + label.color = Color.white; + return label; + } + + private static Button CreateButton(string name, Transform parent, Vector2 anchoredPosition, string labelText) + { + var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(Button)); + go.transform.SetParent(parent, false); + var rect = go.GetComponent(); + rect.sizeDelta = new Vector2(220f, 72f); + rect.anchoredPosition = anchoredPosition; + go.GetComponent().color = new Color(0.24f, 0.45f, 0.75f, 1f); + + var label = CreateText("Label", go.transform, Vector2.zero, new Vector2(220f, 72f), labelText); + label.alignment = TextAnchor.MiddleCenter; + label.resizeTextForBestFit = true; + label.resizeTextMinSize = 18; + label.resizeTextMaxSize = 28; + + return go.GetComponent