// System using System; using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; // GUPS - AntiCheat - Core using GUPS.AntiCheat.Core.Watch; // GUPS - AntiCheat using GUPS.AntiCheat.Detector; namespace GUPS.AntiCheat.Protected.Collection.Chain { /// /// Represents a generic data chain that implements the interface and is observed by the primitive cheating detector. /// Using this class, you can ensure that a chain of data is not modified without your knowledge. Each time you append or remove an item from the /// chain, the class verifies its integrity and notifies the primitive cheating detector if a change is detected. This can be performance costly, /// so do not use it for large amounts of data. /// Only primitive types are supported. /// /// The type of elements stored in the data chain, must be a nullable value type. /// /// /// The class represents a data chain that implements the interface. It allows /// you to observe changes in the data chain through the interface. The class supports the storage of /// elements of primitive types and ensures that modifications to the data chain trigger notifications to subscribed observers. /// /// /// The class maintains a linked list of elements, and changes to the data chain are monitored by computing hash codes and notifying the /// primitive detector when a unallowed change is detected. /// /// public class DataChain : IDataChain, IWatchedSubject where T: struct { /// /// Represents the data chain as readonly linked list. /// private readonly LinkedList chain; /// /// The current hash code of the data chain. /// private Int32 hash; /// /// Gets the linked list containing the elements of the data chain. /// public LinkedList Chain => this.chain; /// /// Get if the protected value has integrity, i.e., whether it has maintained its original state. /// public bool HasIntegrity { get; private set; } = true; /// /// Initializes a new instance of the class. /// public DataChain() { // Initialize the data chain as an empty linked list. this.chain = new LinkedList(); // Compute the initial hash code. this.hash = this.GetHashCode(); } /// /// Appends a new item to the end of the data chain. /// /// The item to be appended to the data chain. /// True if the element is appended successfully and the chain has its integrity; otherwise, false. public bool Append(T _Item) { // Verify the integrity of the chain, each time a new item is appended. if (!this.CheckIntegrity()) { // The integrity of the chain is compromised, return false. return false; } // Append new item to chain. this.chain.AddLast(_Item); // Update hash. this.hash = this.GetHashCode(); // The integrity of the chain is maintained, return true. return true; } /// /// Appends a new item to the end of the data chain. /// /// The item to be appended to the data chain. /// True if the element is appended successfully and the chain has its integrity; otherwise, false. public async Task AppendAsync(T _Item) { // Verify the integrity of the chain, each time a new item is appended. bool var_HasIntegrity = await Task.Run(() => this.CheckIntegrity()).ConfigureAwait(true); if (!var_HasIntegrity) { // The integrity of the chain is compromised, return false. return false; } // Append new item to chain. this.chain.AddLast(_Item); // Update hash. this.hash = await Task.Run(() => this.GetHashCode()).ConfigureAwait(true); // The integrity of the chain is maintained, return true. return true; } /// /// Removes the last item from the end of the data chain. /// /// True if the element could be removed successfully and the chain has its integrity; otherwise, false. public bool RemoveLast() { // Verify the integrity of the chain, each time an item is removed. if (!this.CheckIntegrity()) { // The integrity of the chain is compromised, return false. return false; } // Append new item to chain. this.chain.RemoveLast(); // Update hash. this.hash = this.GetHashCode(); // The integrity of the chain is maintained, return true. return true; } /// /// Removes the last item from the end of the data chain. /// /// True if the element could be removed successfully and the chain has its integrity; otherwise, false. public async Task RemoveLastAsync() { // Verify the integrity of the chain, each time an item is removed. bool var_HasIntegrity = await Task.Run(() => this.CheckIntegrity()).ConfigureAwait(true); if (!var_HasIntegrity) { // The integrity of the chain is compromised, return false. return false; } // Append new item to chain. this.chain.RemoveLast(); // Update hash. this.hash = this.GetHashCode(); // The integrity of the chain is maintained, return true. return true; } /// /// Verifies the integrity of the data chain, notifying observers if a change is detected. /// /// True if the data chain is verified successfully; otherwise, false. public bool CheckIntegrity() { // If the integrity of the chain is already compromised, return false. if (!this.HasIntegrity) { return false; } // Get the current hash code. Int32 currentHash = this.GetHashCode(); // Verify the integrity of the data chain. if (this.hash != currentHash) { this.HasIntegrity = false; } // Notify the primitive cheating detectoor of the result if the chain has no longer integrity. if (!this.HasIntegrity) { AntiCheatMonitor.Instance.GetDetector()?.OnNext(this); } return this.HasIntegrity; } /// /// Computes the hash code for the data chain based on the hash codes of its elements. /// /// A hash code for the current data chain. public override Int32 GetHashCode() { // Initialize the hash code with a prime number. int var_Hash = 17; // Make sure to not throw an exception when an overflow occurs and wrap the result. unchecked { // Iterate through the list and add each item to the hash code. foreach (T item in this.chain) { var_Hash = var_Hash + item.GetHashCode() * 23; } } // Return the final hash code. return var_Hash; } /// /// Determines whether the specified object is equal to the current data chain. /// /// The object to compare with the current data chain. /// True if the specified object is equal to the current data chain; otherwise, false. public override bool Equals(object _Obj) { if (_Obj == null || GetType() != _Obj.GetType()) { return false; } DataChain other = (DataChain)_Obj; if (this.chain.Count != other.chain.Count) { return false; } LinkedListNode thisNode = this.chain.First; LinkedListNode otherNode = other.chain.First; while (thisNode != null && otherNode != null) { if (!thisNode.Value.Equals(otherNode.Value)) { return false; } thisNode = thisNode.Next; otherNode = otherNode.Next; } return true; } /// /// Provides an enumerator for iterating over the elements of the data chain. /// /// An enumerator for the data chain. public IEnumerator GetEnumerator() { return this.chain.GetEnumerator(); } /// /// Provides a non-generic enumerator for iterating over the elements of the data chain. /// /// A non-generic enumerator for the data chain. IEnumerator IEnumerable.GetEnumerator() { return this.chain.GetEnumerator(); } /// /// Does nothing. /// public void Dispose() { // Does nothing. } } }