From f88939e749e5c0e61b8ef3c5c3f043d18bb5d405 Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Tue, 4 Feb 2025 20:34:46 -0800 Subject: [PATCH 01/12] fix: Invert the direction in which the constructors redirect. --- .../Config/Attributes/ConfigFieldAttribute.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/ConfigFieldAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/ConfigFieldAttribute.cs index 39c082d7c..67d9a3633 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/ConfigFieldAttribute.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/ConfigFieldAttribute.cs @@ -64,9 +64,14 @@ public ConfigFieldAttribute( ConfigFieldType type, string tooltip = null, int group = -1, - string helpUrl = null) : this(label, type, tooltip, group, helpUrl) + string helpUrl = null) { PlatformsEnabledOn = enabledOn; + Label = label; + FieldType = type; + ToolTip = tooltip; + Group = group; + HelpURL = helpUrl; } public ConfigFieldAttribute( @@ -74,15 +79,8 @@ public ConfigFieldAttribute( ConfigFieldType type, string tooltip = null, int group = -1, - string helpUrl = null) - { - PlatformsEnabledOn = PlatformManager.Platform.Any; - HelpURL = helpUrl; - Label = label; - ToolTip = tooltip; - Group = group; - FieldType = type; - } + string helpUrl = null) : this(PlatformManager.Platform.Any, label, type, tooltip, group, helpUrl) + { } } } From f15b953f5ed88d70fa10068f011383adc136aae8 Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Tue, 4 Feb 2025 21:18:51 -0800 Subject: [PATCH 02/12] feat: Add field validation attribute system - and apply it to ProductVersion field member within ProductConfig. --- .../Attributes/FieldValidationAttribute.cs | 36 +++++++++ .../FieldValidationAttribute.cs.meta | 11 +++ .../Core/Config/Attributes/Validators.meta | 8 ++ .../Validators/LengthValidationAttribute.cs | 76 +++++++++++++++++++ .../LengthValidationAttribute.cs.meta | 11 +++ .../Validators/RegexValidationAttribute.cs | 63 +++++++++++++++ .../RegexValidationAttribute.cs.meta | 11 +++ .../Validators/StringValidationAttribute.cs | 51 +++++++++++++ .../StringValidationAttribute.cs.meta | 11 +++ .../ValidationAttribute.cs} | 7 +- .../ValidationAttribute.cs.meta} | 0 .../Runtime/Core/Config/ProductConfig.cs | 6 +- 12 files changed, 286 insertions(+), 5 deletions(-) create mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs create mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs.meta create mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators.meta create mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs create mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs.meta create mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs create mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs.meta create mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs create mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs.meta rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{FieldValidatorAttribute.cs => Validators/ValidationAttribute.cs} (83%) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{FieldValidatorAttribute.cs.meta => Validators/ValidationAttribute.cs.meta} (100%) diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs new file mode 100644 index 000000000..0160eb0b7 --- /dev/null +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 PlayEveryWare + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !EOS_DISABLE + +namespace PlayEveryWare.EpicOnlineServices +{ + using System; + + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public abstract class FieldValidationAttribute : Attribute + { + public abstract bool Validate(object value, out string errorMessage); + } +} + +#endif \ No newline at end of file diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs.meta new file mode 100644 index 000000000..6faee6035 --- /dev/null +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b4c773f9f3957e49b652ef8a3d4627c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators.meta new file mode 100644 index 000000000..f347ea1d4 --- /dev/null +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6f7d08e961222604598592968435bcd4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs new file mode 100644 index 000000000..a18a495a2 --- /dev/null +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2025 PlayEveryWare + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !EOS_DISABLE + +namespace PlayEveryWare.EpicOnlineServices +{ + using System; + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public class LengthValidationAttribute : StringValidationAttribute + { + private readonly int? _minLength; + private readonly int? _maxLength; + + // Constructor for specifying both min and max length + public LengthValidationAttribute(int minLength, int maxLength) + { + _minLength = minLength; + _maxLength = maxLength; + } + + // Constructor for specifying only min length + public LengthValidationAttribute(int minLength) + { + _minLength = minLength; + _maxLength = null; + } + + // Constructor for specifying only max length + public LengthValidationAttribute(int maxLength, bool isMaxOnly) + { + _minLength = null; + _maxLength = maxLength; + } + + public override bool ValidateStringField(string value, out string errorMessage) + { + if (value.Length < _minLength) + { + errorMessage = $"String must be at least {_minLength.Value} characters long."; + return false; + } + + if (value.Length > _maxLength) + { + errorMessage = $"String must be no more than {_maxLength.Value} characters long."; + return false; + } + + // There was no error - so no need for an error message. + errorMessage = string.Empty; + return true; + } + } +} + +#endif \ No newline at end of file diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs.meta new file mode 100644 index 000000000..10e2bc75c --- /dev/null +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 342ad1355b550334cb3ae439a3040bea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs new file mode 100644 index 000000000..d1156d281 --- /dev/null +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2025 PlayEveryWare + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !EOS_DISABLE + +namespace PlayEveryWare.EpicOnlineServices +{ + using System; + using UnityEngine; + using System.Text.RegularExpressions; + + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public class RegexValidationAttribute : StringValidationAttribute + { + private readonly string _validRegexPattern; + private readonly string _customErrorMessage; + + public RegexValidationAttribute(string validRegexPattern) : + this(validRegexPattern, $"Value must satisfy the following regex: {validRegexPattern}") + { } + + public RegexValidationAttribute(string validRegexPattern, string customErrorMessage) + { + _validRegexPattern = validRegexPattern; + _customErrorMessage = customErrorMessage; + } + + public override bool ValidateStringField(string value, out string errorMessage) + { + // Determine whether the value matches the regex pattern. + if (!Regex.IsMatch(value, _validRegexPattern)) + { + errorMessage = _customErrorMessage; + return false; + } + + // There was no error - so no need for an error message. + errorMessage = string.Empty; + return true; + } + } +} + +#endif \ No newline at end of file diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs.meta new file mode 100644 index 000000000..8b9063140 --- /dev/null +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d1dc0247cd1285341b2fb9e7ad3c09f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs new file mode 100644 index 000000000..d2ccb147f --- /dev/null +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 PlayEveryWare + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace PlayEveryWare.EpicOnlineServices +{ + using System; + using UnityEngine; + + [AttributeUsage(AttributeTargets.Field)] + + public abstract class StringValidationAttribute : ValidationAttribute + { + + public override bool Validate(object value, out string errorMessage) + { + // Check to make sure the value is of the correct type. + // This should really only fail if the editor for the config was improperly implemented, + // so it's appropriate to log an error here. + if (value is not string strValue) + { + errorMessage = "Invalid value type provided for field."; + Debug.LogError( + $"There was likely a problem with how the currently open configuration window was implemented. Please reach out to the support team for assistance."); + return false; + } + + return ValidateStringField(strValue, out errorMessage); + } + + public abstract bool ValidateStringField(string toValidate, out string errorMessage); + } +} \ No newline at end of file diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs.meta new file mode 100644 index 000000000..dcfab4951 --- /dev/null +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fe1862d5892d54845a4752136a43ceee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidatorAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs similarity index 83% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidatorAttribute.cs rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs index bab24907f..ee0e161e1 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidatorAttribute.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 PlayEveryWare + * Copyright (c) 2025 PlayEveryWare * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,12 +23,11 @@ namespace PlayEveryWare.EpicOnlineServices { using System; - using System.Reflection; [AttributeUsage(AttributeTargets.Field)] - public abstract class FieldValidatorAttribute : Attribute + public abstract class ValidationAttribute : Attribute { - public abstract bool FieldValueIsValid(object toValidate, out string configurationProblemMessage); + public abstract bool Validate(object toValidate, out string configurationProblemMessage); } } \ No newline at end of file diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidatorAttribute.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs.meta similarity index 100% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidatorAttribute.cs.meta rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs.meta diff --git a/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs b/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs index a6042ea96..66af2ff4b 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs @@ -40,6 +40,8 @@ namespace PlayEveryWare.EpicOnlineServices [ConfigGroup("Product Configuration", new[] { "", "Deployment Configuration" }, false)] public class ProductConfig : Config { + private const int EOS_INITIALIZEOPTIONS_PRODUCTVERSION_MAX_LENGTH = 64; + /// /// The product ID is a unique GUID labeled "Product ID" in the Epic /// Developer Portal. The name for this value can be set to anything - @@ -63,7 +65,9 @@ public class ProductConfig : Config [ConfigField("Version", ConfigFieldType.Text, "Use this to indicate to the EOS SDK your game version.", - 0)] + 0, "https://dev.epicgames.com/docs/api-ref/structs/eos-initialize-options")] + [RegexValidation("^[A-Za-z0-9._ !?()+=:-]+$\r\n", "Product version must consist of only the following characters: A-Z, a-z, 0-9, dot, underscore, space, exclamation mark, question mark, sign, hyphen, parenthesis, plus, minus, or colon characters.")] + [LengthValidation(1, EOS_INITIALIZEOPTIONS_PRODUCTVERSION_MAX_LENGTH)] public string ProductVersion; /// From b25e6d800218935c996de223bc62b0a105bb4eef Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Tue, 4 Feb 2025 21:32:17 -0800 Subject: [PATCH 03/12] fix: Restore other field validation attribute to its prior state. --- .../Core/Config/Attributes/FieldValidationAttribute.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs index 0160eb0b7..0e0f6fa4e 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs @@ -27,9 +27,9 @@ namespace PlayEveryWare.EpicOnlineServices using System; [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - public abstract class FieldValidationAttribute : Attribute + public abstract class FieldValidatorAttribute : Attribute { - public abstract bool Validate(object value, out string errorMessage); + public abstract bool FieldValueIsValid(object value, out string errorMessage); } } From 82adeb3646c0adea44e7fd60c5dd13155ab7d3a1 Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Wed, 5 Feb 2025 11:43:03 -0800 Subject: [PATCH 04/12] chore: Moved field validation attributes into directory. --- .../FieldValidationAttribute.cs | 0 .../FieldValidationAttribute.cs.meta | 2 +- .../{ => Validators}/FieldValidator.cs | 0 .../{ => Validators}/FieldValidator.cs.meta | 2 +- .../GUIDFieldValidatorAttribute.cs | 0 .../GUIDFieldValidatorAttribute.cs.meta | 2 +- .../Validators/LengthValidationAttribute.cs | 10 +++- .../NonEmptyStringFieldValidatorAttribute.cs | 0 ...EmptyStringFieldValidatorAttribute.cs.meta | 2 +- .../Validators/RegexValidationAttribute.cs | 27 ++++++++++- .../SandboxIDFieldValidatorAttribute.cs | 0 .../SandboxIDFieldValidatorAttribute.cs.meta | 2 +- .../Validators/StringValidationAttribute.cs | 48 +++++++++++++------ .../Validators/ValidationAttribute.cs | 33 ------------- .../Validators/ValidationAttribute.cs.meta | 11 ----- 15 files changed, 73 insertions(+), 66 deletions(-) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{ => Validators}/FieldValidationAttribute.cs (100%) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{ => Validators}/FieldValidationAttribute.cs.meta (83%) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{ => Validators}/FieldValidator.cs (100%) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{ => Validators}/FieldValidator.cs.meta (83%) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{ => Validators}/GUIDFieldValidatorAttribute.cs (100%) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{ => Validators}/GUIDFieldValidatorAttribute.cs.meta (83%) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{ => Validators}/NonEmptyStringFieldValidatorAttribute.cs (100%) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{ => Validators}/NonEmptyStringFieldValidatorAttribute.cs.meta (83%) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{ => Validators}/SandboxIDFieldValidatorAttribute.cs (100%) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/{ => Validators}/SandboxIDFieldValidatorAttribute.cs.meta (83%) delete mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs delete mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs.meta diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidationAttribute.cs similarity index 100% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidationAttribute.cs diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidationAttribute.cs.meta similarity index 83% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs.meta rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidationAttribute.cs.meta index 6faee6035..f5016e6e0 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidationAttribute.cs.meta +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidationAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8b4c773f9f3957e49b652ef8a3d4627c +guid: f07c746cc4a2bef428b1aa020ca61c9f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidator.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidator.cs similarity index 100% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidator.cs rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidator.cs diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidator.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidator.cs.meta similarity index 83% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidator.cs.meta rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidator.cs.meta index 73f66de2a..c30de2f5e 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/FieldValidator.cs.meta +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9209686f48242544997a71f68e50fa07 +guid: 10d2cf32aa930674697799f5f5cf5fb9 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/GUIDFieldValidatorAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/GUIDFieldValidatorAttribute.cs similarity index 100% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/GUIDFieldValidatorAttribute.cs rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/GUIDFieldValidatorAttribute.cs diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/GUIDFieldValidatorAttribute.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/GUIDFieldValidatorAttribute.cs.meta similarity index 83% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/GUIDFieldValidatorAttribute.cs.meta rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/GUIDFieldValidatorAttribute.cs.meta index b1be740c8..6e38aa098 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/GUIDFieldValidatorAttribute.cs.meta +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/GUIDFieldValidatorAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: abee8fbb46d0fac49a036589047fcbad +guid: 8cd3e5bb57648514eae4182799ae74f4 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs index a18a495a2..8c651bf6b 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs @@ -25,6 +25,12 @@ namespace PlayEveryWare.EpicOnlineServices { using System; + + /// + /// Used to describe a validation attribute that validates the length of a + /// string value. + /// TODO: Replace NonEmptyStringValidatorAttribute with this attribute. + /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public class LengthValidationAttribute : StringValidationAttribute { @@ -54,7 +60,9 @@ public LengthValidationAttribute(int maxLength, bool isMaxOnly) public override bool ValidateStringField(string value, out string errorMessage) { - if (value.Length < _minLength) + // If the minimum length is set and greater than 0, then fail if + // string is either null or is too short. + if (_minLength is > 0 && value == null || value.Length < _minLength) { errorMessage = $"String must be at least {_minLength.Value} characters long."; return false; diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/NonEmptyStringFieldValidatorAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/NonEmptyStringFieldValidatorAttribute.cs similarity index 100% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/NonEmptyStringFieldValidatorAttribute.cs rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/NonEmptyStringFieldValidatorAttribute.cs diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/NonEmptyStringFieldValidatorAttribute.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/NonEmptyStringFieldValidatorAttribute.cs.meta similarity index 83% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/NonEmptyStringFieldValidatorAttribute.cs.meta rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/NonEmptyStringFieldValidatorAttribute.cs.meta index 7e07cf0cd..8e750c510 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/NonEmptyStringFieldValidatorAttribute.cs.meta +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/NonEmptyStringFieldValidatorAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c6a317d0691bd974db94adbe0610a397 +guid: 7466b68bc02900741a20a7989003367b MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs index d1156d281..512120b4e 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/RegexValidationAttribute.cs @@ -25,19 +25,44 @@ namespace PlayEveryWare.EpicOnlineServices { using System; - using UnityEngine; using System.Text.RegularExpressions; + /// + /// ValidationAttribute used to make sure a string field matches a given + /// regex. + /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public class RegexValidationAttribute : StringValidationAttribute { + /// + /// The pattern that the value must match to pass. + /// private readonly string _validRegexPattern; + + /// + /// The error message to return if the regex pattern match fails. + /// private readonly string _customErrorMessage; + /// + /// Default value of the error message is set to describe the regex. + /// + /// The pattern to match. public RegexValidationAttribute(string validRegexPattern) : this(validRegexPattern, $"Value must satisfy the following regex: {validRegexPattern}") { } + /// + /// Determine the parameters for validating a string value with a given + /// regex pattern. + /// + /// + /// The pattern to check the value against for validation. + /// + /// + /// The error message should explain in plain language the meaning of + /// the regex pattern being utilized for validation. + /// public RegexValidationAttribute(string validRegexPattern, string customErrorMessage) { _validRegexPattern = validRegexPattern; diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/SandboxIDFieldValidatorAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/SandboxIDFieldValidatorAttribute.cs similarity index 100% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/SandboxIDFieldValidatorAttribute.cs rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/SandboxIDFieldValidatorAttribute.cs diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/SandboxIDFieldValidatorAttribute.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/SandboxIDFieldValidatorAttribute.cs.meta similarity index 83% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/SandboxIDFieldValidatorAttribute.cs.meta rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/SandboxIDFieldValidatorAttribute.cs.meta index 18acb95e9..d52d5c197 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/SandboxIDFieldValidatorAttribute.cs.meta +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/SandboxIDFieldValidatorAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2a321a87822af3449974f3b09d869bfb +guid: 1f28de13f7e7273489a24356543c9e0a MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs index d2ccb147f..ec609b3f0 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs @@ -23,29 +23,47 @@ namespace PlayEveryWare.EpicOnlineServices { using System; - using UnityEngine; + /// + /// Used to describe a validation attribute that operates on a string data + /// type. + /// [AttributeUsage(AttributeTargets.Field)] - - public abstract class StringValidationAttribute : ValidationAttribute + public abstract class StringValidationAttribute : FieldValidatorAttribute { - - public override bool Validate(object value, out string errorMessage) + public override bool FieldValueIsValid( + object value, + out string errorMessage) { - // Check to make sure the value is of the correct type. - // This should really only fail if the editor for the config was improperly implemented, - // so it's appropriate to log an error here. - if (value is not string strValue) + // Check to make sure the value is actually a string. + if (value is string strValue) { - errorMessage = "Invalid value type provided for field."; - Debug.LogError( - $"There was likely a problem with how the currently open configuration window was implemented. Please reach out to the support team for assistance."); - return false; + return ValidateStringField(strValue, out errorMessage); } - return ValidateStringField(strValue, out errorMessage); + // If this point is reached, it is because the attribute was + // improperly applied to a field member that is not of type string, + // therefore an invalid argument exception should be thrown. + throw new ArgumentException( + $"{nameof(StringValidationAttribute)} cannot be " + + $"applied to a field member whose type is not string."); } - public abstract bool ValidateStringField(string toValidate, out string errorMessage); + /// + /// Implement this function in deriving attributes to accomplish the + /// validation of the string value. + /// + /// + /// The string value to be validated. + /// + /// + /// The error message to use if validation fails. + /// + /// + /// True if the string value is valid, false otherwise. + /// + public abstract bool ValidateStringField( + string toValidate, + out string errorMessage); } } \ No newline at end of file diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs deleted file mode 100644 index ee0e161e1..000000000 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2025 PlayEveryWare - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -namespace PlayEveryWare.EpicOnlineServices -{ - using System; - - [AttributeUsage(AttributeTargets.Field)] - - public abstract class ValidationAttribute : Attribute - { - public abstract bool Validate(object toValidate, out string configurationProblemMessage); - } -} \ No newline at end of file diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs.meta deleted file mode 100644 index 1806af4aa..000000000 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/ValidationAttribute.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 70e4cb59556588240bcc533a7849c5c2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From ea1948d86138e28eebd6068c366ab70872135f3b Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Wed, 5 Feb 2025 12:25:02 -0800 Subject: [PATCH 05/12] feat: Add property to ConfigFieldAttribute for storing validators. --- .../Runtime/Core/Config/Attributes/ConfigFieldAttribute.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/ConfigFieldAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/ConfigFieldAttribute.cs index 67d9a3633..9592b0676 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/ConfigFieldAttribute.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/ConfigFieldAttribute.cs @@ -25,6 +25,7 @@ namespace PlayEveryWare.EpicOnlineServices { using System; + using System.Collections.Generic; [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class ConfigFieldAttribute : Attribute @@ -58,6 +59,8 @@ public class ConfigFieldAttribute : Attribute /// public PlatformManager.Platform PlatformsEnabledOn { get; } + public IEnumerable Validators { get; set; } + public ConfigFieldAttribute( PlatformManager.Platform enabledOn, string label, From d818118e2d34ea041de4d39a5e96135967c0baa3 Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Wed, 5 Feb 2025 13:44:39 -0800 Subject: [PATCH 06/12] fix: Complete implementation of the GUI warning when the product version is not set properly. --- .../Source/Editor/Utility/GUIEditorUtility.cs | 101 +++++++++++------- .../Runtime/Core/Config/ProductConfig.cs | 2 +- 2 files changed, 66 insertions(+), 37 deletions(-) diff --git a/Assets/Plugins/Source/Editor/Utility/GUIEditorUtility.cs b/Assets/Plugins/Source/Editor/Utility/GUIEditorUtility.cs index 9032284a6..2ce565fd3 100644 --- a/Assets/Plugins/Source/Editor/Utility/GUIEditorUtility.cs +++ b/Assets/Plugins/Source/Editor/Utility/GUIEditorUtility.cs @@ -36,7 +36,9 @@ namespace PlayEveryWare.EpicOnlineServices.Editor.Utility using System.Globalization; using System.Linq; using System.Reflection; + using System.Text; using UnityEditor; + using UnityEditor.VersionControl; using UnityEditorInternal; using UnityEngine; using Config = EpicOnlineServices.Config; @@ -53,7 +55,7 @@ public static class GUIEditorUtility /// private static readonly GUIStyle HINT_STYLE = new(GUI.skin.label) { - normal = new GUIStyleState() { textColor = Color.gray }, + normal = new GUIStyleState() { textColor = UnityEngine.Color.gray }, fontStyle = FontStyle.Italic }; @@ -245,18 +247,21 @@ public static void AssigningEnumField(string label, ref T value, float labelW .OrderBy(group => group.Key); } - private static IOrderedEnumerable> GetMembersByGroup() + private static IOrderedEnumerable FieldValidators)>> GetMembersByGroup() { var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance); var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); var members = fields.Cast().Concat(properties.Cast()); - return members + var groupedMembers = members .Where(member => member.GetCustomAttribute() != null) - .Select(member => (MemberInfo: member, FieldDetails: member.GetCustomAttribute())) + .Select(member => (MemberInfo: member, FieldDetails: member.GetCustomAttribute(), + FieldValidators: member.GetCustomAttributes())) .GroupBy(r => r.FieldDetails.Group) .OrderBy(group => group.Key); + + return groupedMembers; } delegate object RenderInputDelegate(ConfigFieldAttribute attribute, object value, float labelWidth); @@ -271,7 +276,6 @@ public static void AssigningEnumField(string label, ref T value, float labelW { typeof(float), (attr, val, width) => RenderInput(attr, (float)val, width) }, { typeof(double), (attr, val, width) => RenderInput(attr, (double)val, width) }, { typeof(bool), (attr, val, width) => RenderInput(attr, (bool)val, width) }, - { typeof(Version), (attr, val, width) => RenderInput(attr, (Version)val, width) }, { typeof(Guid), (attr, val, width) => RenderInput(attr, (Guid)val, width)}, { typeof(List), (attr, val, width) => RenderInput(attr, (List)val, width)}, #if !EOS_DISABLE @@ -500,6 +504,9 @@ public static void RenderInputs(ref T value) { continue; // Skip if MemberInfo is neither FieldInfo nor PropertyInfo } + + // Assign the validators + member.FieldDetails.Validators = member.FieldValidators; // Use the handler from the dictionary if (FieldHandlers.TryGetValue(member.FieldDetails.FieldType, out var handler)) @@ -1126,7 +1133,7 @@ public static WrappedInitializeThreadAffinity RenderInput(ConfigFieldAttribute a #endif private static Guid RenderInput(ConfigFieldAttribute configFieldDetails, Guid value, float labelWidth) { - return InputRendererWrapper(configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, value, + return InputRendererWrapper(configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, value, configFieldDetails.Validators, GuidField); } @@ -1212,22 +1219,13 @@ public static Deployment RenderInput(ConfigFieldAttribute configFieldAttribute, public static TEnum RenderEnumInput(ConfigFieldAttribute configFieldAttribute, TEnum value, float labelWidth) where TEnum : Enum { return InputRendererWrapper(configFieldAttribute.Label, configFieldAttribute.ToolTip, labelWidth, value, - EnumFlagsField, configFieldAttribute.HelpURL); + configFieldAttribute.Validators, EnumFlagsField, configFieldAttribute.HelpURL); } private static TEnum EnumFlagsField(GUIContent label, TEnum value, params GUILayoutOption[] options) where TEnum : Enum { return (TEnum)EditorGUILayout.EnumFlagsField(label, value, options); } - private static Version RenderInput(ConfigFieldAttribute configFieldAttribute, Version value, float labelWidth) - { - return RenderInput(value, configFieldAttribute.Label, configFieldAttribute.ToolTip, labelWidth); - } - - public static Version RenderInput(Version value, string label, string tooltip, float labelWidth) - { - return InputRendererWrapper(label, tooltip, labelWidth, value, VersionField); - } public static ProductionEnvironments RenderInput(ConfigFieldAttribute configFieldAttribute, ProductionEnvironments value, float labelWidth) @@ -1306,7 +1304,7 @@ public static string RenderInput(DirectoryPathFieldAttribute configFieldAttribut { EditorGUILayout.BeginHorizontal(); - string filePath = InputRendererWrapper(configFieldAttributeDetails.Label, value, labelWidth, tooltip, EditorGUILayout.TextField, configFieldAttributeDetails.HelpURL); + string filePath = InputRendererWrapper(configFieldAttributeDetails.Label, value, labelWidth, tooltip, configFieldAttributeDetails.Validators, EditorGUILayout.TextField, configFieldAttributeDetails.HelpURL); if (GUILayout.Button("Select", GUILayout.MaxWidth(MAXIMUM_BUTTON_WIDTH))) { @@ -1327,7 +1325,7 @@ public static string RenderInput(FilePathFieldAttribute configFieldAttributeDeta { EditorGUILayout.BeginHorizontal(); - string filePath = InputRendererWrapper(configFieldAttributeDetails.Label, value, labelWidth, tooltip, EditorGUILayout.TextField, configFieldAttributeDetails.HelpURL); + string filePath = InputRendererWrapper(configFieldAttributeDetails.Label, value, labelWidth, tooltip, configFieldAttributeDetails.Validators, EditorGUILayout.TextField, configFieldAttributeDetails.HelpURL); if (GUILayout.Button("Select", GUILayout.MaxWidth(MAXIMUM_BUTTON_WIDTH))) { @@ -1347,17 +1345,17 @@ public static string RenderInput(FilePathFieldAttribute configFieldAttributeDeta public static double RenderInput(ConfigFieldAttribute configFieldDetails, double value, float labelWidth) { - return InputRendererWrapper(configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, value, EditorGUILayout.DoubleField, configFieldDetails.HelpURL); + return InputRendererWrapper(configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, value, configFieldDetails.Validators, EditorGUILayout.DoubleField, configFieldDetails.HelpURL); } public static float RenderInput(ConfigFieldAttribute configFieldDetails, float value, float labelWidth) { - return InputRendererWrapper(configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, value, EditorGUILayout.FloatField, configFieldDetails.HelpURL); + return InputRendererWrapper(configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, value, configFieldDetails.Validators, EditorGUILayout.FloatField, configFieldDetails.HelpURL); } public static string RenderInput(ConfigFieldAttribute configFieldDetails, string value, float labelWidth) { - return InputRendererWrapper(configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, value, EditorGUILayout.TextField, configFieldDetails.HelpURL); + return InputRendererWrapper(configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, value, configFieldDetails.Validators, EditorGUILayout.TextField, configFieldDetails.HelpURL); } public static ulong RenderInput(ConfigFieldAttribute configFieldDetails, ulong value, float labelWidth) @@ -1365,17 +1363,7 @@ public static ulong RenderInput(ConfigFieldAttribute configFieldDetails, ulong v _ = SafeTranslatorUtility.TryConvert(value, out long temp); long longValue = InputRendererWrapper(configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, - temp, EditorGUILayout.LongField); - - return SafeTranslatorUtility.TryConvert(longValue, out ulong newValue) ? newValue : value; - } - - private static ulong RenderInput(string label, string tooltip, ulong value, float labelWidth) - { - _ = SafeTranslatorUtility.TryConvert(value, out long temp); - - long longValue = InputRendererWrapper(label, tooltip, labelWidth, - temp, EditorGUILayout.LongField); + temp, configFieldDetails.Validators, EditorGUILayout.LongField); return SafeTranslatorUtility.TryConvert(longValue, out ulong newValue) ? newValue : value; } @@ -1384,7 +1372,7 @@ public static uint RenderInput(ConfigFieldAttribute configFieldDetails, uint val { _ = SafeTranslatorUtility.TryConvert(value, out int temp); - int intValue = InputRendererWrapper(configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, temp, + int intValue = InputRendererWrapper(configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, temp, configFieldDetails.Validators, EditorGUILayout.IntField); return SafeTranslatorUtility.TryConvert(intValue, out uint newValue) ? newValue : value; @@ -1394,10 +1382,10 @@ public static bool RenderInput(ConfigFieldAttribute configFieldDetails, bool val { return InputRendererWrapper( configFieldDetails.Label, configFieldDetails.ToolTip, labelWidth, - value, EditorGUILayout.Toggle); + value, configFieldDetails.Validators, EditorGUILayout.Toggle); } - public delegate T TestDelegate(GUIContent label, T value, params GUILayoutOption[] options); + public delegate T InputRendererDelegate(GUIContent label, T value, params GUILayoutOption[] options); private static T InputRendererWithAlignedLabel(float labelWidth, Func renderFn) { @@ -1412,10 +1400,47 @@ private static T InputRendererWithAlignedLabel(float labelWidth, Func rend return newValue; } - private static T InputRendererWrapper(string label, string toolTip, float labelWidth, T value, TestDelegate renderFn, string helpURL = null) + private static void RunValidators(IEnumerable validators, object value, out bool isValid) + { + isValid = true; + StringBuilder errorMessageBuilder = new(); + foreach (var validator in validators) + { + // If field is valid then go to the next validator. + if (validator.FieldValueIsValid(value, out string errorMessage)) + { + continue; + } + + // Otherwise append message from the validator. + errorMessageBuilder.AppendLine(errorMessage); + } + + // If there are no error messages, then stop here + if (errorMessageBuilder.Length == 0) + { + return; + } + + isValid = false; + EditorGUILayout.HelpBox(errorMessageBuilder.ToString(), MessageType.Error); + } + + private static T InputRendererWrapper(string label, string toolTip, float labelWidth, T value, IEnumerable validators, InputRendererDelegate renderFn, string helpURL = null) { return InputRendererWithAlignedLabel(labelWidth, () => { + // Run validators for the config field. + RunValidators(validators, value, out bool isCurrentValueValid); + + // Store the previous background color so that it can be + // restored if need be. + Color previousBackgroundColor = GUI.backgroundColor; + if (!isCurrentValueValid) + { + GUI.backgroundColor = Color.red; + } + if (!string.IsNullOrEmpty(helpURL)) { EditorGUILayout.BeginHorizontal(); @@ -1429,6 +1454,10 @@ private static T InputRendererWrapper(string label, string toolTip, float lab EditorGUILayout.EndHorizontal(); } + // Restore the background color that was set before the field + // was rendered. + GUI.backgroundColor = previousBackgroundColor; + return newValue; }); } diff --git a/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs b/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs index 66af2ff4b..5702ee22a 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs @@ -66,7 +66,7 @@ public class ProductConfig : Config ConfigFieldType.Text, "Use this to indicate to the EOS SDK your game version.", 0, "https://dev.epicgames.com/docs/api-ref/structs/eos-initialize-options")] - [RegexValidation("^[A-Za-z0-9._ !?()+=:-]+$\r\n", "Product version must consist of only the following characters: A-Z, a-z, 0-9, dot, underscore, space, exclamation mark, question mark, sign, hyphen, parenthesis, plus, minus, or colon characters.")] + [RegexValidation("^[A-Za-z0-9._ !?()+=:-]+$", "Product version must consist of only the following characters: A-Z, a-z, 0-9, dot, underscore, space, exclamation mark, question mark, sign, hyphen, parenthesis, plus, minus, or colon characters.")] [LengthValidation(1, EOS_INITIALIZEOPTIONS_PRODUCTVERSION_MAX_LENGTH)] public string ProductVersion; From cfcfd2da8c4bcccc449bcc5e8efbcd8191d7e013 Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Wed, 5 Feb 2025 14:58:33 -0800 Subject: [PATCH 07/12] fix: Restored some things that were changed that did not need to be changed. --- .../Source/Editor/Utility/GUIEditorUtility.cs | 12 ++++++------ ...dationAttribute.cs => FieldValidatorAttribute.cs} | 11 ++++------- ...ibute.cs.meta => FieldValidatorAttribute.cs.meta} | 0 .../Runtime/Core/Config/ProductConfig.cs | 5 +++++ 4 files changed, 15 insertions(+), 13 deletions(-) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/{FieldValidationAttribute.cs => FieldValidatorAttribute.cs} (89%) rename com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/{FieldValidationAttribute.cs.meta => FieldValidatorAttribute.cs.meta} (100%) diff --git a/Assets/Plugins/Source/Editor/Utility/GUIEditorUtility.cs b/Assets/Plugins/Source/Editor/Utility/GUIEditorUtility.cs index 2ce565fd3..e681e441c 100644 --- a/Assets/Plugins/Source/Editor/Utility/GUIEditorUtility.cs +++ b/Assets/Plugins/Source/Editor/Utility/GUIEditorUtility.cs @@ -38,7 +38,6 @@ namespace PlayEveryWare.EpicOnlineServices.Editor.Utility using System.Reflection; using System.Text; using UnityEditor; - using UnityEditor.VersionControl; using UnityEditorInternal; using UnityEngine; using Config = EpicOnlineServices.Config; @@ -55,7 +54,7 @@ public static class GUIEditorUtility /// private static readonly GUIStyle HINT_STYLE = new(GUI.skin.label) { - normal = new GUIStyleState() { textColor = UnityEngine.Color.gray }, + normal = new GUIStyleState() { textColor = Color.gray }, fontStyle = FontStyle.Italic }; @@ -254,14 +253,12 @@ public static void AssigningEnumField(string label, ref T value, float labelW var members = fields.Cast().Concat(properties.Cast()); - var groupedMembers = members + return members .Where(member => member.GetCustomAttribute() != null) .Select(member => (MemberInfo: member, FieldDetails: member.GetCustomAttribute(), FieldValidators: member.GetCustomAttributes())) .GroupBy(r => r.FieldDetails.Group) .OrderBy(group => group.Key); - - return groupedMembers; } delegate object RenderInputDelegate(ConfigFieldAttribute attribute, object value, float labelWidth); @@ -1423,7 +1420,7 @@ private static void RunValidators(IEnumerable validator } isValid = false; - EditorGUILayout.HelpBox(errorMessageBuilder.ToString(), MessageType.Error); + EditorGUILayout.HelpBox(errorMessageBuilder.ToString(), MessageType.Warning); } private static T InputRendererWrapper(string label, string toolTip, float labelWidth, T value, IEnumerable validators, InputRendererDelegate renderFn, string helpURL = null) @@ -1438,6 +1435,9 @@ private static T InputRendererWrapper(string label, string toolTip, float lab Color previousBackgroundColor = GUI.backgroundColor; if (!isCurrentValueValid) { + // This sets the background color for the input field that + // is about to be rendered to red - further highlighting the + // field that has invalid values. GUI.backgroundColor = Color.red; } diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidatorAttribute.cs similarity index 89% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidationAttribute.cs rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidatorAttribute.cs index 0e0f6fa4e..73dbc6251 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidationAttribute.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidatorAttribute.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 PlayEveryWare + * Copyright (c) 2024 PlayEveryWare * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,17 +20,14 @@ * SOFTWARE. */ -#if !EOS_DISABLE - namespace PlayEveryWare.EpicOnlineServices { using System; + using System.Reflection; - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] public abstract class FieldValidatorAttribute : Attribute { public abstract bool FieldValueIsValid(object value, out string errorMessage); } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidationAttribute.cs.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidatorAttribute.cs.meta similarity index 100% rename from com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidationAttribute.cs.meta rename to com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/FieldValidatorAttribute.cs.meta diff --git a/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs b/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs index 5702ee22a..77e789acc 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs @@ -40,6 +40,11 @@ namespace PlayEveryWare.EpicOnlineServices [ConfigGroup("Product Configuration", new[] { "", "Deployment Configuration" }, false)] public class ProductConfig : Config { + /// + /// This is the maximum allowed length for the product version field + /// according to the EOS SDK documentation. The name of this field was + /// selected to mirror the one that exists within the EOS SDK itself. + /// private const int EOS_INITIALIZEOPTIONS_PRODUCTVERSION_MAX_LENGTH = 64; /// From dcb2599ca39c09fff8886634d1b17730b7da6bf1 Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Wed, 5 Feb 2025 15:13:12 -0800 Subject: [PATCH 08/12] fix: Mark the FieldValueIsValid function within StringValidationAttribute as sealed. --- .../Config/Attributes/Validators/StringValidationAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs index ec609b3f0..2a7372c1c 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/StringValidationAttribute.cs @@ -31,7 +31,7 @@ namespace PlayEveryWare.EpicOnlineServices [AttributeUsage(AttributeTargets.Field)] public abstract class StringValidationAttribute : FieldValidatorAttribute { - public override bool FieldValueIsValid( + public override sealed bool FieldValueIsValid( object value, out string errorMessage) { From 3c5fc649bb4abc06dc66c985c14a950a0e8a2912 Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Fri, 7 Feb 2025 11:17:36 -0800 Subject: [PATCH 09/12] chore: Add README.md to validation attribute directory. --- .../Core/Config/Attributes/Validators/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/README.md diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/README.md b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/README.md new file mode 100644 index 000000000..edc60b02c --- /dev/null +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/README.md @@ -0,0 +1,11 @@ +# Validator Attributes Usage & Application - HON + +This directory contains a variety of attributes that can be applied to classes that derive from `Config`. The attributes are mostly implemented to make effective unit testing of a now-obsolete config class `EOSConfig`, but there are a few additional things to be cognizant of: + +The following attributes were implemented to affect the user experience surrounding the entry of configuration values: + +- `StringValidationAttribute` +- `RegexValidationAttribute` +- `LengthValidationAttribute` + +The attributes above were applied to the `productVersion` field member of the `ProductConfig` class, and enable the user to see immediately when they add an invalid value to the product version field. These attributes and the system can (and likely should) be expanded and be applied to other field members of `ProductConfig`, and the various `PlatformConfig` implementing classes - so as to provide a similar user experience in other fields. \ No newline at end of file From f2316ccb98a82d29ef041be093435a3002da4f6e Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Fri, 7 Feb 2025 12:06:53 -0800 Subject: [PATCH 10/12] fix: Clarify conditional by adding parens. --- .../Attributes/Validators/LengthValidationAttribute.cs | 2 +- .../Core/Config/Attributes/Validators/README.md.meta | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/README.md.meta diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs index 8c651bf6b..0ebdaf754 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs @@ -62,7 +62,7 @@ public override bool ValidateStringField(string value, out string errorMessage) { // If the minimum length is set and greater than 0, then fail if // string is either null or is too short. - if (_minLength is > 0 && value == null || value.Length < _minLength) + if ((_minLength is > 0 && value == null) || value.Length < _minLength) { errorMessage = $"String must be at least {_minLength.Value} characters long."; return false; diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/README.md.meta b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/README.md.meta new file mode 100644 index 000000000..49c6cd0f3 --- /dev/null +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 6517396a6d333734297706e2246f03f4 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 2c8202be2ccc4a7893f9d58280e7523964366419 Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Fri, 7 Feb 2025 12:10:37 -0800 Subject: [PATCH 11/12] fix: Clarify conditional by explicitly checking for HasValue. --- .../Config/Attributes/Validators/LengthValidationAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs index 0ebdaf754..3f8dffdd4 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/Attributes/Validators/LengthValidationAttribute.cs @@ -68,7 +68,7 @@ public override bool ValidateStringField(string value, out string errorMessage) return false; } - if (value.Length > _maxLength) + if (_maxLength.HasValue && value.Length > _maxLength) { errorMessage = $"String must be no more than {_maxLength.Value} characters long."; return false; From 8ec5bebfffd9a69a59bbc033657101991c9260da Mon Sep 17 00:00:00 2001 From: Paul Hazen Date: Fri, 7 Feb 2025 12:13:04 -0800 Subject: [PATCH 12/12] fix: Correct regex pattern to exclude matching the equal sign. --- com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs b/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs index 77e789acc..95b2ead1d 100644 --- a/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs +++ b/com.playeveryware.eos/Runtime/Core/Config/ProductConfig.cs @@ -71,7 +71,7 @@ public class ProductConfig : Config ConfigFieldType.Text, "Use this to indicate to the EOS SDK your game version.", 0, "https://dev.epicgames.com/docs/api-ref/structs/eos-initialize-options")] - [RegexValidation("^[A-Za-z0-9._ !?()+=:-]+$", "Product version must consist of only the following characters: A-Z, a-z, 0-9, dot, underscore, space, exclamation mark, question mark, sign, hyphen, parenthesis, plus, minus, or colon characters.")] + [RegexValidation("^[A-Za-z0-9._ !?()+:-]+$", "Product version must consist of only the following characters: A-Z, a-z, 0-9, dot, underscore, space, exclamation mark, question mark, sign, hyphen, parenthesis, plus, minus, or colon characters.")] [LengthValidation(1, EOS_INITIALIZEOPTIONS_PRODUCTVERSION_MAX_LENGTH)] public string ProductVersion;