using UnityEngine; using UnityEngine.Events; public abstract class ActDetectorBase : MonoBehaviour { protected const string CONTAINER_NAME = "anticheat detectors"; protected const string MENU_PATH = "poc/anticheat/"; protected const string GAME_OBJECT_MENU_PATH = "GameObject/Create Other/" + MENU_PATH; protected static GameObject detectorsContainer; /// /// Allows to start detector automatically. /// Otherwise, you'll need to call StartDetection() method to start it. /// /// Useful in conjunction with proper Detection Event configuration in the inspector. /// Allows to use detector without writing any code except the actual reaction on cheating. [Tooltip("Automatically start detector. Detection Event will be called on detection.")] public bool autoStart = false; /// /// Detector will survive new level (scene) load if checked. Otherwise it will be destroyed. /// /// On dispose Detector follows 2 rules: /// - if Game Object's name is "Anti-Cheat Toolkit Detectors": it will be automatically /// destroyed if no other Detectors left attached regardless of any other components or children;
/// - if Game Object's name is NOT "Anti-Cheat Toolkit Detectors": 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; /// /// Detector component will be automatically disposed after firing callback if enabled. /// Otherwise, it will just stop internal processes. /// /// On dispose Detector follows 2 rules: /// - if Game Object's name is "Anti-Cheat Toolkit Detectors": it will be automatically /// destroyed if no other Detectors left attached regardless of any other components or children;
/// - if Game Object's name is NOT "Anti-Cheat Toolkit Detectors": it will be automatically destroyed only /// if it has neither other components nor children attached; [Tooltip("Automatically dispose Detector after firing callback.")] public bool autoDispose = true; [SerializeField] protected UnityEvent detectionEvent = null; protected UnityAction detectionAction = null; [SerializeField] protected bool detectionEventHasListener = false; protected bool isRunning; protected bool started; #region detectors placement #if UNITY_EDITOR /*[UnityEditor.MenuItem(GAME_OBJECT_MENU_PATH + "All detectors", false, 0)] private static void AddAllDetectorsToScene() { AddInjectionDetectorToScene(); AddSpeedHackDetectorToScene(); } [UnityEditor.MenuItem(GAME_OBJECT_MENU_PATH + InjectionDetector.COMPONENT_NAME, false, 1)] private static void AddInjectionDetectorToScene() { SetupDetectorInScene(); } [UnityEditor.MenuItem(GAME_OBJECT_MENU_PATH + SpeedHackDetector.COMPONENT_NAME, false, 1)] private static void AddSpeedHackDetectorToScene() { SetupDetectorInScene(); }*/ static void SetupDetectorInScene() where T : ActDetectorBase { T component = FindObjectOfType(); string detectorName = typeof(T).Name; if(component !=null) { if(component.gameObject.name == CONTAINER_NAME) { UnityEditor.EditorUtility.DisplayDialog(detectorName + " already exists!", detectorName + " already exists in scene and correctly placed on object \"" + CONTAINER_NAME + "\"", "OK"); } else { int dialogResult = UnityEditor.EditorUtility.DisplayDialogComplex(detectorName + " already exists!", detectorName + " already exists in scene and placed on object \"" + component.gameObject.name + "\". Do you wish to move it to the Game Object \"" + CONTAINER_NAME + "\" or delete it from scene at all?", "Move", "Delete", "Cancel"); switch (dialogResult) { case 0: GameObject container = GameObject.Find(CONTAINER_NAME); if (container == null) { container = new GameObject(CONTAINER_NAME); } T newComponent = container.AddComponent(); UnityEditor.EditorUtility.CopySerialized(component, newComponent); DestroyDetectorImmediate(component); break; case 1: DestroyDetectorImmediate(component); break; } } }else { GameObject container = GameObject.Find(CONTAINER_NAME); if (container == null) { container = new GameObject(CONTAINER_NAME); UnityEditor.Undo.RegisterCreatedObjectUndo(container, "Create " + CONTAINER_NAME); } UnityEditor.Undo.AddComponent(container); UnityEditor.EditorUtility.DisplayDialog(detectorName + " added!", detectorName + " successfully added to the object \"" + CONTAINER_NAME + "\"", "OK"); } } static void DestroyDetectorImmediate(ActDetectorBase component) { if (component.transform.childCount == 0 && component.GetComponentsInChildren(true).Length <= 2) { DestroyImmediate(component.gameObject); } else { DestroyImmediate(component); } } #endif #endregion #region UNITY_MESSAGES private void Start() { if (detectorsContainer == null && gameObject.name == CONTAINER_NAME) { detectorsContainer = gameObject; } if (autoStart && !started) { StartDetectionAutomatically(); } } private void OnEnable() { if (!started || (!detectionEventHasListener && detectionAction == null)) return; ResumeDetector(); } private void OnDisable() { if (!started) return; PauseDetector(); } private void OnApplicationQuit() { DisposeInternal(); } protected virtual void OnDestroy() { StopDetectionInternal(); if (transform.childCount == 0 && GetComponentsInChildren().Length <= 2) { Destroy(gameObject); } else if (name == CONTAINER_NAME && GetComponentsInChildren().Length <= 1) { Destroy(gameObject); } } #endregion protected virtual bool Init(ActDetectorBase instance, string detectorName) { if (instance != null && instance != this && instance.keepAlive) { Destroy(this); return false; } DontDestroyOnLoad(gameObject); return true; } protected virtual void DisposeInternal() { Destroy(this); } internal virtual void OnCheatingDetected() { if (detectionAction != null) detectionAction(); if (detectionEventHasListener) detectionEvent.Invoke(); //if (autoDispose) //{ // DisposeInternal(); //} //else //{ // StopDetectionInternal(); //} } protected abstract void StartDetectionAutomatically(); protected abstract void StopDetectionInternal(); protected abstract void PauseDetector(); protected abstract void ResumeDetector(); }