Files
Workspace/Godot/Packages/ShrinkSDK.EntJoy.Godot/ShrinkEntJoyHost.cs
T

97 lines
2.3 KiB
C#

#nullable enable
using System;
using System.Threading;
using EntJoy.ECS;
using EntJoy.JobSystem;
using Godot;
using ShrinkSDK.Godot;
namespace ShrinkSDK.EntJoy.Godot;
/// <summary>Godot node that owns an EntJoy World and advances its frame boundary.</summary>
public partial class ShrinkEntJoyHost : ShrinkGodotHost
{
private static readonly object SchedulerGate = new();
private static int SchedulerUsers;
private bool _schedulerAcquired;
private bool _worldDisposed;
private ulong _lastFrame = ulong.MaxValue;
private SceneTree? _tree;
[Export] public string EntJoyWorldName { get; set; } = "GodotWorld";
public World? EntJoyWorld { get; private set; }
public override void _Ready()
{
base._Ready();
AcquireScheduler();
EntJoyWorld = new World(EntJoyWorldName);
World.DefaultWorld = EntJoyWorld;
_tree = GetTree();
_tree.ProcessFrame += OnProcessFrame;
TreeExited += OnTreeExited;
}
public override void _Process(double delta)
{
base._Process(delta);
Advance();
}
private void OnProcessFrame() => Advance();
private void Advance()
{
if (EntJoyWorld == null || _worldDisposed) return;
var frame = Engine.GetProcessFrames();
if (frame == _lastFrame) return;
_lastFrame = frame;
EntJoyWorld.NextFrameEvents();
NativeJobScheduler.EndFrame();
EntJoyWorld.CurrentFrame++;
}
public override void _ExitTree()
{
DisposeWorld();
base._ExitTree();
}
private void OnTreeExited()
{
if (_tree != null)
_tree.ProcessFrame -= OnProcessFrame;
_tree = null;
TreeExited -= OnTreeExited;
DisposeWorld();
}
private void AcquireScheduler()
{
lock (SchedulerGate)
{
if (SchedulerUsers++ == 0)
NativeJobScheduler.Initialize();
_schedulerAcquired = true;
}
}
private void DisposeWorld()
{
if (_worldDisposed) return;
_worldDisposed = true;
EntJoyWorld?.Dispose();
EntJoyWorld = null;
if (!_schedulerAcquired) return;
lock (SchedulerGate)
{
_schedulerAcquired = false;
if (--SchedulerUsers == 0)
NativeJobScheduler.Shutdown();
}
}
}