feat: add Godot C# host package
This commit is contained in:
@@ -0,0 +1,44 @@
|
|||||||
|
name: Publish NuGet package
|
||||||
|
|
||||||
|
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
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
tag="${GITEA_REF#refs/tags/}"
|
||||||
|
version="$(dotnet msbuild ShrinkSDK.Godot.csproj -getProperty:Version -nologo)"
|
||||||
|
test "$tag" = "v$version"
|
||||||
|
dotnet restore ShrinkSDK.Godot.csproj --configfile NuGet.Config
|
||||||
|
dotnet pack ShrinkSDK.Godot.csproj --configuration Release --no-restore --output packages --include-symbols --include-source
|
||||||
|
|
||||||
|
- name: Publish package
|
||||||
|
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
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
**/bin/
|
||||||
|
**/obj/
|
||||||
|
artifacts/
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<clear />
|
||||||
|
<add key="ShrinkSDK" value="https://git.crash.work/api/packages/ShrinkSDK/nuget/index.json" />
|
||||||
|
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||||
|
</packageSources>
|
||||||
|
</configuration>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# ShrinkSDK.Godot
|
||||||
|
|
||||||
|
Godot 4.6 C# 宿主与平台服务适配包。
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
dotnet add package ShrinkSDK.Godot --version 0.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
主场景根节点继承 `ShrinkGodotHost`,在 `_Ready` 中先调用 `base._Ready()`,再调用 `StartAppAsync()`。CodeGen 由依赖模块的 `buildTransitive` 自动启用。
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Godot;
|
||||||
|
using ShrinkEventBus;
|
||||||
|
using ShrinkDataSaver;
|
||||||
|
using ShrinkApp;
|
||||||
|
using ShrinkNetwork;
|
||||||
|
using ShrinkSDK.Runtime;
|
||||||
|
|
||||||
|
namespace ShrinkSDK.Godot;
|
||||||
|
|
||||||
|
public partial class ShrinkGodotHost : Node, IShrinkMainThreadDispatcher,
|
||||||
|
IShrinkLogger, IShrinkClock, IShrinkPathProvider, IShrinkScreenshotProvider,
|
||||||
|
IShrinkApplicationLifecycle
|
||||||
|
{
|
||||||
|
private readonly ConcurrentQueue<Action> _dispatchQueue = new();
|
||||||
|
private readonly Dictionary<ShrinkNetworkService, ShrinkNetworkDispatchQueue> _networkServices = new();
|
||||||
|
private int _mainThreadId;
|
||||||
|
private bool _networkPumpRunning;
|
||||||
|
private ShrinkAppRuntime? _appRuntime;
|
||||||
|
|
||||||
|
public event Action? Paused;
|
||||||
|
public event Action? Resumed;
|
||||||
|
public event Action? Exiting;
|
||||||
|
|
||||||
|
[Export] public int DispatchBudgetPerFrame { get; set; } = 256;
|
||||||
|
[Export] public int NetworkDispatchBudgetPerFrame { get; set; } = 256;
|
||||||
|
|
||||||
|
public bool IsMainThread => Thread.CurrentThread.ManagedThreadId == _mainThreadId;
|
||||||
|
public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
|
||||||
|
public double UnscaledTimeSeconds => Time.GetTicksMsec() / 1000d;
|
||||||
|
public string PersistentDataPath => ProjectSettings.GlobalizePath("user://");
|
||||||
|
public string TemporaryDataPath => OS.GetCacheDir();
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
_mainThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
ShrinkRuntimeServices.Configure(this, this, this, this, this, this);
|
||||||
|
ShrinkEventBusRuntime.ConfigureMainThreadDispatcher(this);
|
||||||
|
ShrinkDataSaverPlatform.Configure(this, this, this, this);
|
||||||
|
SetProcess(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Process(double delta)
|
||||||
|
{
|
||||||
|
var budget = Math.Max(1, DispatchBudgetPerFrame);
|
||||||
|
while (budget-- > 0 && _dispatchQueue.TryDequeue(out var action))
|
||||||
|
action();
|
||||||
|
PumpNetworkAsync().Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Notification(int what)
|
||||||
|
{
|
||||||
|
switch ((long)what)
|
||||||
|
{
|
||||||
|
case NotificationApplicationPaused:
|
||||||
|
case NotificationApplicationFocusOut:
|
||||||
|
Paused?.Invoke();
|
||||||
|
break;
|
||||||
|
case NotificationApplicationResumed:
|
||||||
|
case NotificationApplicationFocusIn:
|
||||||
|
Resumed?.Invoke();
|
||||||
|
break;
|
||||||
|
case NotificationWMCloseRequest:
|
||||||
|
case NotificationPredelete:
|
||||||
|
Exiting?.Invoke();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async UniTask<ShrinkAppRuntime> StartAppAsync(ShrinkAppSettings? settings = null, ShrinkAppServices? services = null)
|
||||||
|
{
|
||||||
|
if (_appRuntime?.IsRunning == true) return _appRuntime;
|
||||||
|
_appRuntime = new ShrinkAppRuntime(this, settings, services);
|
||||||
|
await _appRuntime.StartAsync();
|
||||||
|
return _appRuntime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async UniTask ShutdownAppAsync()
|
||||||
|
{
|
||||||
|
if (_appRuntime == null) return;
|
||||||
|
await _appRuntime.ShutdownAsync();
|
||||||
|
_appRuntime = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryPost(Action action)
|
||||||
|
{
|
||||||
|
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||||
|
_dispatchQueue.Enqueue(action);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddNetworkService(ShrinkNetworkService service)
|
||||||
|
{
|
||||||
|
if (service == null) throw new ArgumentNullException(nameof(service));
|
||||||
|
if (_networkServices.ContainsKey(service)) return;
|
||||||
|
var queue = new ShrinkNetworkDispatchQueue(Math.Max(1, NetworkDispatchBudgetPerFrame * 4));
|
||||||
|
service.DispatchScheduler = queue;
|
||||||
|
_networkServices.Add(service, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveNetworkService(ShrinkNetworkService service)
|
||||||
|
{
|
||||||
|
if (!_networkServices.Remove(service, out var queue)) return false;
|
||||||
|
service.DispatchScheduler = ShrinkNetworkDispatchSchedulers.Inline;
|
||||||
|
queue.Dispose();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShrinkCodeGenWovenAttribute RequireWoven(Assembly assembly)
|
||||||
|
{
|
||||||
|
if (assembly == null) throw new ArgumentNullException(nameof(assembly));
|
||||||
|
return assembly.GetCustomAttribute<ShrinkCodeGenWovenAttribute>() ??
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Assembly '{assembly.GetName().Name}' was not woven by ShrinkSDK.CodeGen. Ensure the package is installed and ShrinkCodeGenEnabled is not false.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log(ShrinkLogLevel level, string message, Exception? exception = null)
|
||||||
|
{
|
||||||
|
var text = exception == null ? message : $"{message}{System.Environment.NewLine}{exception}";
|
||||||
|
if (level >= ShrinkLogLevel.Error) GD.PushError(text);
|
||||||
|
else if (level >= ShrinkLogLevel.Warning) GD.PushWarning(text);
|
||||||
|
else GD.Print(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask<byte[]> CapturePngAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
var image = GetViewport().GetTexture().GetImage();
|
||||||
|
return ValueTask.FromResult(image.SavePngToBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async UniTaskVoid PumpNetworkAsync()
|
||||||
|
{
|
||||||
|
if (_networkPumpRunning) return;
|
||||||
|
_networkPumpRunning = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var queue in _networkServices.Values)
|
||||||
|
await queue.PumpAsync(Math.Max(1, NetworkDispatchBudgetPerFrame));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_networkPumpRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<AssemblyName>ShrinkSDK.Godot</AssemblyName>
|
||||||
|
<RootNamespace>ShrinkSDK.Godot</RootNamespace>
|
||||||
|
<PackageId>ShrinkSDK.Godot</PackageId>
|
||||||
|
<Version>0.1.0</Version>
|
||||||
|
<Description>Godot 4.6 host and platform services for ShrinkSDK.</Description>
|
||||||
|
<ShrinkCodeGenEnabled>false</ShrinkCodeGenEnabled>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="GodotSharp" Version="4.6.3" />
|
||||||
|
<PackageReference Include="UniTask" Version="2.5.10" />
|
||||||
|
<PackageReference Include="ShrinkSDK.Runtime.Abstractions" Version="0.1.0" />
|
||||||
|
<PackageReference Include="ShrinkSDK.EventBus" Version="2.1.0" />
|
||||||
|
<PackageReference Include="ShrinkSDK.Network" Version="0.3.0" />
|
||||||
|
<PackageReference Include="ShrinkSDK.DataSaver" Version="2.3.0" />
|
||||||
|
<PackageReference Include="ShrinkSDK.App.Core" Version="0.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user