#nullable enable using System; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Cysharp.Threading.Tasks; using Godot; using ShrinkApp; using ShrinkNetwork; using ShrinkSDK.Runtime; namespace ShrinkSDK.Godot; public partial class ShrinkGodotHost : Node, IShrinkMainThreadDispatcher, IShrinkDeferredMainThreadDispatcher, IShrinkLogger, IShrinkClock, IShrinkPathProvider, IShrinkScreenshotProvider, IShrinkApplicationLifecycle { private readonly ShrinkGodotRuntime _runtime = new(); private SceneTree? _tree; private ulong _lastTickFrame = ulong.MaxValue; private bool _signalsConnected; public ShrinkGodotRuntime Runtime => _runtime; [Export] public int DispatchBudgetPerFrame { get => _runtime.DispatchBudgetPerFrame; set => _runtime.DispatchBudgetPerFrame = value; } [Export] public int NetworkDispatchBudgetPerFrame { get => _runtime.NetworkDispatchBudgetPerFrame; set => _runtime.NetworkDispatchBudgetPerFrame = value; } public event Action? Paused { add => _runtime.Paused += value; remove => _runtime.Paused -= value; } public event Action? Resumed { add => _runtime.Resumed += value; remove => _runtime.Resumed -= value; } public event Action? Exiting { add => _runtime.Exiting += value; remove => _runtime.Exiting -= value; } public bool IsMainThread => _runtime.IsMainThread; public bool IsInitialized => _runtime.IsInitialized; public bool IsShutdown => _runtime.IsShutdown; public DateTimeOffset UtcNow => _runtime.UtcNow; public double UnscaledTimeSeconds => _runtime.UnscaledTimeSeconds; public string PersistentDataPath => _runtime.PersistentDataPath; public string TemporaryDataPath => _runtime.TemporaryDataPath; public override void _Ready() { _runtime.Initialize(this); SetProcess(true); if (_signalsConnected || !IsInsideTree()) return; _tree = GetTree(); _tree.ProcessFrame += OnProcessFrame; TreeExiting += OnTreeExiting; TreeExited += OnTreeExited; _signalsConnected = true; } public override void _Process(double delta) => Tick(delta); public override void _Notification(int what) => _runtime.Notify(what); public override void _ExitTree() { if (_runtime.IsInitialized && !_runtime.IsShutdown) _runtime.ShutdownAsync().Forget(); } private void OnProcessFrame() => Tick(GetProcessDeltaTime()); private void Tick(double delta) { var frame = Engine.GetProcessFrames(); if (frame == _lastTickFrame) return; _lastTickFrame = frame; _runtime.Tick(delta); } private void OnTreeExiting() { if (_runtime.IsInitialized && !_runtime.IsShutdown) _runtime.ShutdownAsync().Forget(); } private void OnTreeExited() { if (_tree != null) { _tree.ProcessFrame -= OnProcessFrame; _tree = null; } TreeExiting -= OnTreeExiting; TreeExited -= OnTreeExited; _signalsConnected = false; if (_runtime.IsInitialized && !_runtime.IsShutdown) _runtime.ShutdownAsync().Forget(); } public UniTask StartAppAsync(ShrinkAppSettings? settings = null, ShrinkAppServices? services = null) => _runtime.StartAppAsync(settings, services); public UniTask ShutdownAsync() => _runtime.ShutdownAsync(); public UniTask ShutdownAppAsync() => _runtime.StopAppAsync(); public bool TryPost(Action action) => _runtime.TryPost(action); public bool TryPostDeferred(Action action) => _runtime.TryPostDeferred(action); public void AddNetworkService(ShrinkNetworkService service) => _runtime.AddNetworkService(service); public bool RemoveNetworkService(ShrinkNetworkService service) => _runtime.RemoveNetworkService(service); public static ShrinkCodeGenWovenAttribute RequireWoven(Assembly assembly) => ShrinkGodotRuntime.RequireWoven(assembly); public void Log(ShrinkLogLevel level, string message, Exception? exception = null) => _runtime.Log(level, message, exception); public ValueTask CapturePngAsync(CancellationToken cancellationToken = default) => _runtime.CapturePngAsync(cancellationToken); }