// 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 float. In almost all cases, you can replace your default type with the protected one for added security. /// [Serializable] public struct ProtectedFloat : 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 obfuscated value of the protected. /// private UIntFloat obfuscatedValue; /// /// Used for calculation of the int/float values for the secured value. /// private UIntFloat manager; /// /// A secret key used to obfuscate the true value. /// private UInt32 secret; /// /// A honeypot pretending to be the original value. If some user tries to change this value via a cheat/hack engine, you will get notified. /// The protected value will keep its true value. /// [SerializeField] private float fakeValue; /// /// Unity serialization hook. So the right values will be serialized. /// public void OnBeforeSerialize() { this.fakeValue = Value; } /// /// Unity deserialization hook. So the right values will be deserialized. /// public void OnAfterDeserialize() { this = this.fakeValue; } /// /// Create a new protected float with _Value. /// /// The initial value for the protected float. public ProtectedFloat(float value = 0) { // Initialization this.isInitialized = true; this.secret = (UInt32)GlobalSettings.RandomProvider.RandomInt32(1, Int32.MaxValue); // this.obfuscatedValue.intValue = 0; this.obfuscatedValue.floatValue = value; this.obfuscatedValue.intValue = this.obfuscatedValue.intValue ^ this.secret; // this.manager.intValue = 0; this.manager.floatValue = 0; // this.hasIntegrity = true; // this.fakeValue = value; } /// /// Gets and sets the true unencrypted field value. /// public float 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(float _Value) { // Obfuscate the value. this.manager.floatValue = _Value; this.manager.intValue = this.manager.intValue ^ this.secret; this.obfuscatedValue.floatValue = this.manager.floatValue; // Assign the fake value. this.fakeValue = _Value; } /// /// Unobfuscates the secured value and returns the true unencrypted value. /// /// The true unencrypted value. private float UnObfuscate() { // Get the unobfuscated value. this.manager.intValue = this.obfuscatedValue.intValue ^ this.secret; // Return the unobfuscated value. return this.manager.floatValue; } /// /// Obfuscates the current value, generating a new random secret key. /// public void Obfuscate() { // Unobfuscate the secured value. float var_UnobfuscatedValue = this.UnObfuscate(); // Create a new random secret. this.secret = (UInt32)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. float 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; } /// /// Disposes of the resources associated with the protected float. /// public void Dispose() { this.obfuscatedValue.intValue = 0; this.manager.intValue = 0; this.secret = 0; } /// /// Returns a string representation of the protected float. /// /// A string representation of the protected float. 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.floatValue.GetHashCode(); } #region Serialization /// /// Used to serialize the protected to the player prefs. /// /// The obfuscated value of the protected. /// The secret key used to obfuscate the true value. internal void Serialize(out uint _ObfuscatedValue, out uint _Secret) { _ObfuscatedValue = this.obfuscatedValue.intValue; _Secret = this.secret; } /// /// Used to deserialize the protected from the player prefs. /// /// The obfuscated value of the protected. /// The secret key used to obfuscate the true value. internal void Deserialize(uint _ObfuscatedValue, uint _Secret) { this.obfuscatedValue.intValue = _ObfuscatedValue; this.secret = _Secret; this.fakeValue = this.UnObfuscate(); } #endregion #region Implicit operators /// /// Implicitly converts a float to a ProtectedFloat. /// /// The float value to be converted. /// A new instance of ProtectedFloat with the converted value. public static implicit operator ProtectedFloat(float _Value) { return new ProtectedFloat(_Value); } /// /// Implicitly converts a ProtectedFloat to a float. /// /// The ProtectedFloat to be converted. /// The unencrypted float value. public static implicit operator float(ProtectedFloat _Value) { return _Value.Value; } /// /// Implicitly converts a ProtectedFloat to a ProtectedInt16. /// /// The ProtectedFloat to be converted. /// A new instance of ProtectedInt16 with the converted value. public static implicit operator ProtectedInt16(ProtectedFloat _Value) { return new ProtectedInt16((Int16)_Value.Value); } /// /// Implicitly converts a ProtectedInt16 to a ProtectedFloat. /// /// The ProtectedInt16 to be converted. /// A new instance of ProtectedFloat with the converted value. public static implicit operator ProtectedFloat(ProtectedInt16 _Value) { return new ProtectedFloat((float)_Value.Value); } /// /// Implicitly converts a ProtectedFloat to a ProtectedInt32. /// /// The ProtectedFloat to be converted. /// A new instance of ProtectedInt32 with the converted value. public static implicit operator ProtectedInt32(ProtectedFloat _Value) { return new ProtectedInt32((Int32)_Value.Value); } /// /// Implicitly converts a ProtectedInt32 to a ProtectedFloat. /// /// The ProtectedInt32 to be converted. /// A new instance of ProtectedFloat with the converted value. public static implicit operator ProtectedFloat(ProtectedInt32 _Value) { return new ProtectedFloat((float)_Value.Value); } /// /// Implicitly converts a ProtectedFloat to a ProtectedInt64. /// /// The ProtectedFloat to be converted. /// A new instance of ProtectedInt64 with the converted value. public static implicit operator ProtectedInt64(ProtectedFloat _Value) { return new ProtectedInt64((Int64)_Value.Value); } /// /// Implicitly converts a ProtectedInt64 to a ProtectedFloat. /// /// The ProtectedInt64 to be converted. /// A new instance of ProtectedFloat with the converted value. public static implicit operator ProtectedFloat(ProtectedInt64 _Value) { return new ProtectedFloat((float)_Value.Value); } #endregion #region Calculation operators /// /// Adds two ProtectedFloat instances. /// /// The first ProtectedFloat instance. /// The second ProtectedFloat instance. /// A new instance of ProtectedFloat representing the sum of the two instances. public static ProtectedFloat operator +(ProtectedFloat v1, ProtectedFloat v2) { return new ProtectedFloat(v1.Value + v2.Value); } /// /// Subtracts one ProtectedFloat instance from another. /// /// The first ProtectedFloat instance. /// The second ProtectedFloat instance. /// A new instance of ProtectedFloat representing the result of the subtraction. public static ProtectedFloat operator -(ProtectedFloat v1, ProtectedFloat v2) { return new ProtectedFloat(v1.Value - v2.Value); } /// /// Multiplies two ProtectedFloat instances. /// /// The first ProtectedFloat instance. /// The second ProtectedFloat instance. /// A new instance of ProtectedFloat representing the product of the two instances. public static ProtectedFloat operator *(ProtectedFloat v1, ProtectedFloat v2) { return new ProtectedFloat(v1.Value * v2.Value); } /// /// Divides one ProtectedFloat instance by another. /// /// The numerator ProtectedFloat instance. /// The denominator ProtectedFloat instance. /// A new instance of ProtectedFloat representing the result of the division. public static ProtectedFloat operator /(ProtectedFloat v1, ProtectedFloat v2) { return new ProtectedFloat(v1.Value / v2.Value); } #endregion #region Equality operators /// /// Determines whether two ProtectedFloat instances are equal. /// /// The first ProtectedFloat instance. /// The second ProtectedFloat instance. /// True if the values of the two instances are equal; otherwise, false. public static bool operator ==(ProtectedFloat v1, ProtectedFloat v2) { return v1.Value == v2.Value; } /// /// Determines whether two ProtectedFloat instances are not equal. /// /// The first ProtectedFloat instance. /// The second ProtectedFloat instance. /// True if the values of the two instances are not equal; otherwise, false. public static bool operator !=(ProtectedFloat v1, ProtectedFloat v2) { return v1.Value != v2.Value; } /// /// Determines whether the specified object is equal to the current ProtectedFloat instance. /// /// 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 ProtectedFloat) { return this.Value == ((ProtectedFloat)obj).Value; } return this.Value.Equals(obj); } /// /// Compares two ProtectedFloat instances for less than. /// /// The first ProtectedFloat instance. /// The second ProtectedFloat instance. /// True if the value of the first instance is less than the value of the second instance; otherwise, false. public static bool operator <(ProtectedFloat v1, ProtectedFloat v2) { return v1.Value < v2.Value; } /// /// Compares two ProtectedFloat instances for less than or equal. /// /// The first ProtectedFloat instance. /// The second ProtectedFloat 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 <=(ProtectedFloat v1, ProtectedFloat v2) { return v1.Value <= v2.Value; } /// /// Compares two ProtectedFloat instances for greater than. /// /// The first ProtectedFloat instance. /// The second ProtectedFloat instance. /// True if the value of the first instance is greater than the value of the second instance; otherwise, false. public static bool operator >(ProtectedFloat v1, ProtectedFloat v2) { return v1.Value > v2.Value; } /// /// Compares two ProtectedFloat instances for greater than or equal. /// /// The first ProtectedFloat instance. /// The second ProtectedFloat 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 >=(ProtectedFloat v1, ProtectedFloat v2) { return v1.Value >= v2.Value; } #endregion } }