2025-05-18 01:04:31 +08:00

145 lines
3.4 KiB
C#

#region copyright
// ---------------------------------------------------------------
// Copyright (C) Dmitriy Yukhanov - focus [https://codestage.net]
// ---------------------------------------------------------------
#endregion
namespace CodeStage.AntiCheat.Common
{
using UnityEngine;
using UnityEngine.SceneManagement;
internal class ContainerHolder
{
public const string ContainerName = "Anti-Cheat Toolkit";
public static GameObject container;
}
public abstract class KeepAliveBehaviour<T> : MonoBehaviour where T: KeepAliveBehaviour<T>
{
/// <summary>
/// Will survive new level (scene) load if checked. Otherwise it will be destroyed.
/// </summary>
/// On dispose Behaviour follows 2 rules:
/// - if Game Object's name is "Anti-Cheat Toolkit": it will be automatically
/// destroyed if no other Behaviours left attached regardless of any other components or children;<br/>
/// - if Game Object's name is NOT "Anti-Cheat Toolkit": it will be automatically destroyed only
/// if it has neither other components nor children attached;
[Tooltip("Detector will survive new level (scene) load if checked.")]
public bool keepAlive = true;
protected int instancesInScene;
#region static instance
/// <summary>
/// Allows reaching public properties from code.
/// Can be null if behaviour does not exist in scene or if accessed at or before Awake phase.
/// </summary>
public static T Instance { get; protected set; }
protected static T GetOrCreateInstance
{
get
{
if (Instance != null)
{
return Instance;
}
if (ContainerHolder.container == null)
{
ContainerHolder.container = new GameObject(ContainerHolder.ContainerName);
}
Instance = ContainerHolder.container.AddComponent<T>();
return Instance;
}
}
#endregion
#region unity messages
#if ACTK_EXCLUDE_OBFUSCATION
[System.Reflection.Obfuscation(Exclude = true)]
#endif
protected virtual void Awake()
{
instancesInScene++;
if (Init(Instance, GetComponentName()))
{
Instance = (T)this;
}
}
protected virtual void Start()
{
if (ContainerHolder.container == null && gameObject.name == ContainerHolder.ContainerName)
{
ContainerHolder.container = gameObject;
}
SceneManager.sceneLoaded += OnSceneLoaded;
}
protected virtual void OnDestroy()
{
var componentsCount = GetComponentsInChildren<Component>().Length;
if (transform.childCount == 0 && componentsCount <= 2)
{
Destroy(gameObject);
}
else if (name == ContainerHolder.ContainerName && componentsCount <= 2)
{
Destroy(gameObject);
}
instancesInScene--;
SceneManager.sceneLoaded -= OnSceneLoaded;
if (Instance == this)
{
Instance = null;
}
}
#endregion
protected virtual void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (instancesInScene < 2)
{
if (!keepAlive)
{
DisposeInternal();
}
}
else
{
if (!keepAlive && Instance != this)
{
DisposeInternal();
}
}
}
protected virtual bool Init(T instance, string detectorName)
{
if (instance != null && instance != this && instance.keepAlive)
{
DisposeInternal();
return false;
}
DontDestroyOnLoad(transform.parent != null ? transform.root.gameObject : gameObject);
return true;
}
protected virtual void DisposeInternal()
{
Destroy(this);
}
protected abstract string GetComponentName();
}
}