// 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.
}
}
}