Code/General/SingletonComponent.cs
using Sandbox;
using System.Runtime.CompilerServices;
using System.Threading;
namespace CubeCore.Core.SandboxExtensions;
public interface ISingleton<T> where T : ISingleton<T>
{
static abstract T? Instance { get; }
}
public abstract class SingletonComponent<T> : Component, ISingleton<T> where T : SingletonComponent<T>, ISingleton<T>
{
[SkipHotload]
public static T? Instance
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
var currentValue = field;
if(currentValue.IsValid())
return currentValue;
field = null;
var scene = Game.ActiveScene;
if(scene is null || scene.IsEditor)
return null;
var found = scene?.Get<T>();
var oldValue = Interlocked.CompareExchange(ref field, found, currentValue);
if(ReferenceEquals(oldValue, currentValue))
return found.IsValid() ? found : null;
currentValue = field;
return currentValue.IsValid() ? currentValue : null;
}
private set
{
Interlocked.Exchange(ref field, value);
}
}
protected sealed override void OnAwake()
{
if(!Scene.IsEditor)
{
if(Instance.IsValid() && Instance != this)
{
Log.Warning($"Enabled {this} (on {GameObject}), but another instance {Instance} was already enabled. Disabling {this}...");
return;
}
GameObject.Flags |= GameObjectFlags.DontDestroyOnLoad;
Instance = (T?)this;
}
OnAwake(false);
}
protected virtual void OnAwake(bool _) { }
protected sealed override void OnDestroy()
{
OnDestroy(false);
if(!Scene.IsEditor && object.ReferenceEquals(Instance, this))
Instance = null;
}
protected virtual void OnDestroy(bool _) { }
}