// System
using System;
// Unity
using UnityEngine;
// GUPS - AntiCheat - Core
using GUPS.AntiCheat.Core.Protected;
// GUPS - AntiCheat
using GUPS.AntiCheat.Detector;
using GUPS.AntiCheat.Settings;
namespace GUPS.AntiCheat.Protected
{
///
/// Represents a protected double value that is encrypted and includes mechanisms to maintain integrity.
/// In most scenarios, this type can be used as a drop-in replacement for the default double type.
///
[Serializable]
public struct ProtectedDouble : IProtected, IDisposable, ISerializationCallbackReceiver
{
///
/// A struct does not have a default constructor that is called when the structure is created. Therefore, the protected primitive must return
/// a default value if it does not have an assigned value.
///
private bool isInitialized;
///
/// Gets a value indicating whether the protected value has integrity, i.e., whether it has maintained its original state.
///
private bool hasIntegrity;
///
/// Gets a value indicating whether the protected value has integrity, i.e., whether it has maintained its original state.
///
public bool HasIntegrity { get => hasIntegrity || !isInitialized; private set => hasIntegrity = value; }
///
/// The encrypted true value.
///
private ULongDouble obfuscatedValue;
///
/// Used for calculation of the long/double values for the secured value.
///
private ULongDouble manager;
///
/// A secret key the true value gets encrypted with.
///
private UInt64 secret;
///
/// A honeypot pretending to be the original value. If a user attempts to change this value via a cheat/hack engine,
/// you will be notified. The protected value will retain its true value.
///
[SerializeField]
private double fakeValue;
///
/// Unity serialization hook. Ensures the correct values are serialized.
///
public void OnBeforeSerialize()
{
this.fakeValue = Value;
}
///
/// Unity deserialization hook. Ensures the correct values are deserialized.
///
public void OnAfterDeserialize()
{
this = this.fakeValue;
}
///
/// Creates a new protected double with the specified value.
///
/// The initial value of the protected double.
public ProtectedDouble(double value = 0)
{
// Initialization
this.isInitialized = true;
this.secret = (ulong)GlobalSettings.RandomProvider.RandomInt32(1, Int32.MaxValue);
//
this.obfuscatedValue.longValue = 0;
this.obfuscatedValue.doubleValue = value;
this.obfuscatedValue.longValue = this.obfuscatedValue.longValue ^ this.secret;
//
this.manager.longValue = 0;
this.manager.doubleValue = 0;
//
this.hasIntegrity = true;
//
this.fakeValue = value;
}
///
/// Gets and sets the true unencrypted field value.
///
public double Value
{
get
{
if (!this.isInitialized)
{
return 0;
}
if (!this.CheckIntegrity())
{
AntiCheatMonitor.Instance.GetDetector()?.OnNext(this);
}
return this.UnObfuscate();
}
set { this.Obfuscate(value); }
}
///
/// Gets the true unencrypted field value.
///
object IProtected.Value => this.Value;
///
/// Obfuscates the specified value, encrypting it with the secret key.
///
/// The value to be obfuscated.
private void Obfuscate(double _Value)
{
// Obfuscate the value.
this.manager.doubleValue = _Value;
this.manager.longValue = this.manager.longValue ^ this.secret;
this.obfuscatedValue.doubleValue = this.manager.doubleValue;
// Assign the fake value.
this.fakeValue = _Value;
}
///
/// Unobfuscates the secured value and returns the true unencrypted value.
///
/// The true unencrypted value.
private double UnObfuscate()
{
// Get the unobfuscated value.
this.manager.longValue = this.obfuscatedValue.longValue ^ this.secret;
// Return the unobfuscated value.
return this.manager.doubleValue;
}
///
/// Obfuscates the current value, generating a new random secret key.
///
public void Obfuscate()
{
// Unobfuscate the secured value.
double var_UnobfuscatedValue = this.UnObfuscate();
// Create a new random secret.
this.secret = (ulong)GlobalSettings.RandomProvider.RandomInt32(1, Int32.MaxValue);
// Obfuscate the value.
this.Obfuscate(var_UnobfuscatedValue);
}
///
/// Checks the integrity of the protected value, detecting if an attacker changed the honeypot fake value.
///
/// True if the protected value has integrity; otherwise, false.
public bool CheckIntegrity()
{
// Unobfuscate the secured value.
double var_UnobfuscatedValue = this.UnObfuscate();
// Check if an attacker changed the honeypot fake value.
if (this.fakeValue != var_UnobfuscatedValue)
{
this.HasIntegrity = false;
}
// Return the integrity status.
return this.HasIntegrity;
}
///
/// Releases the resources used by the .
///
public void Dispose()
{
this.obfuscatedValue.longValue = 0;
this.manager.longValue = 0;
this.secret = 0;
}
///
/// Returns a string that represents the current .
///
/// A string representation of the protected double value.
public override string ToString()
{
return this.Value.ToString();
}
///
/// Serves as a hash function for a particular type.
///
/// A hash code for the current .
public override int GetHashCode()
{
return this.obfuscatedValue.doubleValue.GetHashCode();
}
#region Implicit operators
///
/// Implicitly converts a double to a .
///
/// The double value to be converted.
/// A new instance of with the converted value.
public static implicit operator ProtectedDouble(double _Value)
{
return new ProtectedDouble(_Value);
}
///
/// Implicitly converts a to a double.
///
/// The to be converted.
/// The unencrypted double value.
public static implicit operator double(ProtectedDouble _Value)
{
return _Value.Value;
}
///
/// Implicitly converts a to a .
///
/// The to be converted.
/// A new instance of with the converted value.
public static implicit operator ProtectedInt16(ProtectedDouble _Value)
{
return new ProtectedInt16((Int16)_Value.Value);
}
///
/// Implicitly converts a to a .
///
/// The to be converted.
/// A new instance of with the converted value.
public static implicit operator ProtectedDouble(ProtectedInt16 _Value)
{
return new ProtectedDouble((double)_Value.Value);
}
///
/// Implicitly converts a to a .
///
/// The to be converted.
/// A new instance of with the converted value.
public static implicit operator ProtectedInt32(ProtectedDouble _Value)
{
return new ProtectedInt32((Int32)_Value.Value);
}
///
/// Implicitly converts a to a .
///
/// The to be converted.
/// A new instance of with the converted value.
public static implicit operator ProtectedDouble(ProtectedInt32 _Value)
{
return new ProtectedDouble((double)_Value.Value);
}
///
/// Implicitly converts a to a .
///
/// The to be converted.
/// A new instance of with the converted value.
public static implicit operator ProtectedInt64(ProtectedDouble _Value)
{
return new ProtectedInt64((Int64)_Value.Value);
}
///
/// Implicitly converts a to a .
///
/// The to be converted.
/// A new instance of with the converted value.
public static implicit operator ProtectedDouble(ProtectedInt64 _Value)
{
return new ProtectedDouble((double)_Value.Value);
}
///
/// Implicitly converts a to a .
///
/// The to be converted.
/// A new instance of with the converted value.
public static implicit operator ProtectedFloat(ProtectedDouble _Value)
{
return new ProtectedFloat((float)_Value.Value);
}
///
/// Implicitly converts a to a .
///
/// The to be converted.
/// A new instance of with the converted value.
public static implicit operator ProtectedDouble(ProtectedFloat _Value)
{
return new ProtectedDouble((double)_Value.Value);
}
#endregion
#region Calculation operations
///
/// Adds two instances.
///
/// The first instance.
/// The second instance.
/// A new instance representing the sum of the two values.
public static ProtectedDouble operator +(ProtectedDouble v1, ProtectedDouble v2)
{
return new ProtectedDouble(v1.Value + v2.Value);
}
///
/// Subtracts one instance from another.
///
/// The first instance.
/// The second instance.
/// A new instance representing the difference of the two values.
public static ProtectedDouble operator -(ProtectedDouble v1, ProtectedDouble v2)
{
return new ProtectedDouble(v1.Value - v2.Value);
}
///
/// Multiplies two instances.
///
/// The first instance.
/// The second instance.
/// A new instance representing the product of the two values.
public static ProtectedDouble operator *(ProtectedDouble v1, ProtectedDouble v2)
{
return new ProtectedDouble(v1.Value * v2.Value);
}
///
/// Divides one instance by another.
///
/// The dividend instance.
/// The divisor instance.
/// A new instance representing the quotient of the division.
public static ProtectedDouble operator /(ProtectedDouble v1, ProtectedDouble v2)
{
return new ProtectedDouble(v1.Value / v2.Value);
}
#endregion
#region Equality operations
///
/// Checks if two instances are equal.
///
/// The first instance.
/// The second instance.
/// True if the values of the two instances are equal; otherwise, false.
public static bool operator ==(ProtectedDouble v1, ProtectedDouble v2)
{
return v1.Value == v2.Value;
}
///
/// Checks if two instances are not equal.
///
/// The first instance.
/// The second instance.
/// True if the values of the two instances are not equal; otherwise, false.
public static bool operator !=(ProtectedDouble v1, ProtectedDouble v2)
{
return v1.Value != v2.Value;
}
///
/// Determines whether the specified is equal to the current .
///
/// The object to compare with the current instance.
/// True if the specified object is equal to the current instance; otherwise, false.
public override bool Equals(object obj)
{
if (obj is ProtectedDouble)
{
return this.Value == ((ProtectedDouble)obj).Value;
}
return this.Value.Equals(obj);
}
///
/// Compares two instances for less than.
///
/// The first instance.
/// The second instance.
/// True if the value of the first instance is less than the value of the second instance; otherwise, false.
public static bool operator <(ProtectedDouble v1, ProtectedDouble v2)
{
return v1.Value < v2.Value;
}
///
/// Compares two instances for less than or equal.
///
/// The first instance.
/// The second instance.
/// True if the value of the first instance is less than or equal to the value of the second instance; otherwise, false.
public static bool operator <=(ProtectedDouble v1, ProtectedDouble v2)
{
return v1.Value <= v2.Value;
}
///
/// Compares two instances for greater than.
///
/// The first instance.
/// The second instance.
/// True if the value of the first instance is greater than the value of the second instance; otherwise, false.
public static bool operator >(ProtectedDouble v1, ProtectedDouble v2)
{
return v1.Value > v2.Value;
}
///
/// Compares two instances for greater than or equal.
///
/// The first instance.
/// The second instance.
/// True if the value of the first instance is greater than or equal to the value of the second instance; otherwise, false.
public static bool operator >=(ProtectedDouble v1, ProtectedDouble v2)
{
return v1.Value >= v2.Value;
}
#endregion
}
}