From 9d27472f954a794d4eab4fc86ff9a46f99c720cf Mon Sep 17 00:00:00 2001 From: theDevJade Date: Wed, 24 Jul 2024 12:46:12 -0700 Subject: [PATCH 1/5] May of fixed custom modules, needs testing --- .../API/Features/CustomModule.cs | 53 ++++++-- .../API/Features/CustomModuleSerializer.cs | 113 ++++++++++++++++++ 2 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 Exiled.CustomModules/API/Features/CustomModuleSerializer.cs diff --git a/Exiled.CustomModules/API/Features/CustomModule.cs b/Exiled.CustomModules/API/Features/CustomModule.cs index 4d2aa0dcc3..828f3e7997 100644 --- a/Exiled.CustomModules/API/Features/CustomModule.cs +++ b/Exiled.CustomModules/API/Features/CustomModule.cs @@ -5,6 +5,10 @@ // // ----------------------------------------------------------------------- +using Exiled.API.Features.Serialization; +using Exiled.API.Features.Serialization.CustomConverters; +using YamlDotNet.Serialization.NodeDeserializers; + namespace Exiled.CustomModules.API.Features { using System; @@ -407,6 +411,41 @@ public override bool Equals(object obj) /// The 32-bit signed hash code of the current object instance. public override int GetHashCode() => base.GetHashCode(); + /// + /// Gets or sets the serializer for configs and translations. + /// + private static ISerializer ModuleSerializer { get; set; } = new SerializerBuilder() + .WithTypeConverter(new VectorsConverter()) + .WithTypeConverter(new ColorConverter()) + .WithTypeConverter(new AttachmentIdentifiersConverter()) + .WithTypeConverter(new EnumClassConverter()) + .WithTypeConverter(new PrivateConstructorConverter()) + .WithTypeConverter(new CustomModuleSerializer()) + .WithEventEmitter(eventEmitter => new TypeAssigningEventEmitter(eventEmitter)) + .WithTypeInspector(inner => new CommentGatheringTypeInspector(inner)) + .WithEmissionPhaseObjectGraphVisitor(args => new CommentsObjectGraphVisitor(args.InnerVisitor)) + .WithNamingConvention(UnderscoredNamingConvention.Instance) + .IgnoreFields() + .DisableAliases() + .Build(); + + /// + /// Gets or sets the deserializer for configs and translations. + /// + private static IDeserializer ModuleDeserializer { get; set; } = new DeserializerBuilder() + .WithTypeConverter(new VectorsConverter()) + .WithTypeConverter(new ColorConverter()) + .WithTypeConverter(new AttachmentIdentifiersConverter()) + .WithTypeConverter(new EnumClassConverter()) + .WithTypeConverter(new PrivateConstructorConverter()) + .WithTypeConverter(new CustomModuleSerializer()) + .WithNamingConvention(UnderscoredNamingConvention.Instance) + .WithNodeDeserializer(inner => new ValidatingNodeDeserializer(inner), deserializer => deserializer.InsteadOf()) + .WithDuplicateKeyChecking() + .IgnoreFields() + .IgnoreUnmatchedProperties() + .Build(); + /// /// Serializes the current module to a file specified by . /// @@ -424,13 +463,13 @@ public void SerializeModule() if (File.Exists(FilePath) && File.Exists(PointerPath)) { - File.WriteAllText(FilePath, EConfig.Serializer.Serialize(this)); - File.WriteAllText(PointerPath, EConfig.Serializer.Serialize(Config)); + File.WriteAllText(FilePath, ModuleSerializer.Serialize(this)); + File.WriteAllText(PointerPath, ModuleSerializer.Serialize(Config)); return; } - File.WriteAllText(FilePath ?? throw new ArgumentNullException(nameof(FilePath)), EConfig.Serializer.Serialize(this)); - File.WriteAllText(PointerPath ?? throw new ArgumentNullException(nameof(PointerPath)), EConfig.Serializer.Serialize(Config)); + File.WriteAllText(FilePath ?? throw new ArgumentNullException(nameof(FilePath)), ModuleSerializer.Serialize(this)); + File.WriteAllText(PointerPath ?? throw new ArgumentNullException(nameof(PointerPath)), ModuleSerializer.Serialize(Config)); } /// @@ -454,7 +493,7 @@ public void DeserializeModule() { try { - Config = EConfig.Deserializer.Deserialize(File.ReadAllText(PointerPath)); + Config = ModuleDeserializer.Deserialize(File.ReadAllText(PointerPath)); } catch { @@ -470,7 +509,7 @@ public void DeserializeModule() return; } - CustomModule deserializedModule = EConfig.Deserializer.Deserialize(File.ReadAllText(FilePath), GetType()) as CustomModule; + CustomModule deserializedModule = ModuleDeserializer.Deserialize(File.ReadAllText(FilePath), GetType()) as CustomModule; CopyProperties(deserializedModule); foreach (string file in Directory.GetFiles(ChildPath)) @@ -480,7 +519,7 @@ public void DeserializeModule() try { - Config = EConfig.Deserializer.Deserialize(File.ReadAllText(file)); + Config = ModuleDeserializer.Deserialize(File.ReadAllText(file)); } catch { diff --git a/Exiled.CustomModules/API/Features/CustomModuleSerializer.cs b/Exiled.CustomModules/API/Features/CustomModuleSerializer.cs new file mode 100644 index 0000000000..2502122eb5 --- /dev/null +++ b/Exiled.CustomModules/API/Features/CustomModuleSerializer.cs @@ -0,0 +1,113 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features +{ + using System; + using System.Reflection; + + using Attributes; + using Generic; + using YamlDotNet.Core; + using YamlDotNet.Core.Events; + using YamlDotNet.Serialization; + + /// + /// A IYamlTypeConverter for custom modules. + /// + public class CustomModuleSerializer : IYamlTypeConverter + { + /// + public bool Accepts(Type type) + { + return type == typeof(ModulePointer) || type == typeof(ModulePointer<>); + } + + /// + public object ReadYaml(IParser parser, Type type) + { + if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(ModulePointer<>)) + throw new InvalidOperationException($"Unsupported type: {type.FullName}"); + Type genericArgument = type.GetGenericArguments()[0]; + MethodInfo method = GetType().GetMethod(nameof(ReadYamlGeneric), BindingFlags.NonPublic | BindingFlags.Instance); + MethodInfo genericMethod = method?.MakeGenericMethod(genericArgument); + return genericMethod?.Invoke(this, new object[] { parser }); + } + + /// + public void WriteYaml(IEmitter emitter, object value, Type type) + { + if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(ModulePointer<>)) + throw new ArgumentException("The type must be the generic ModulePointer."); + Type genericArgument = type.GetGenericArguments()[0]; + MethodInfo method = GetType().GetMethod(nameof(WriteYamlGeneric), BindingFlags.NonPublic | BindingFlags.Instance); + MethodInfo genericMethod = method?.MakeGenericMethod(genericArgument); + genericMethod?.Invoke(this, new[] { ref emitter, ref value }); + } + + private void WriteYamlGeneric(IEmitter emitter, object value) + where TType : CustomModule + { + if (value is not ModulePointer pointer) + { + throw new ArgumentNullException(nameof(pointer), @"Value is not a valid ModulePointer."); + } + + emitter.Emit(new MappingStart(null, null, false, MappingStyle.Block)); + + emitter.Emit(new Scalar(null, "id")); + emitter.Emit(new Scalar(null, pointer.Id.ToString())); + + emitter.Emit(new Scalar(null, "module")); + emitter.Emit(new Scalar(null, typeof(TType).Name)); + + emitter.Emit(new Scalar(null, "assembly")); + emitter.Emit(new Scalar(null, pointer.GetType().Assembly.FullName)); + + emitter.Emit(new MappingEnd()); + } + + private object ReadYamlGeneric(IParser parser) + where TType : CustomModule + { + parser.Consume(); + + parser.Consume(); // id key + string id = parser.Consume().Value; + + parser.Consume(); // module key + string module = parser.Consume().Value; + + parser.Consume(); // assembly key + string assemblyName = parser.Consume().Value; + + parser.Consume(); + + // Load the specified assembly + Assembly assembly = Assembly.Load(assemblyName); + if (assembly == null) + { + throw new InvalidOperationException($"Assembly '{assemblyName}' could not be loaded."); + } + + // Create an instance of the appropriate ModulePointer subclass + foreach (Type t in assembly.GetTypes()) + { + ModuleIdentifierAttribute moduleIdentifier = t.GetCustomAttribute(); + if (moduleIdentifier == null || moduleIdentifier.Id != uint.Parse(id) || !typeof(ModulePointer).IsAssignableFrom(t)) + continue; + ModulePointer instance = Activator.CreateInstance(t) as ModulePointer; + if (instance == null) + continue; + instance.Id = Convert.ToUInt32(id); + return instance; + } + + throw new InvalidOperationException($"Could not find a suitable ModulePointer<{typeof(TType).Name}> type for id {id}"); + } + } +} \ No newline at end of file From 03711653e2b6037fd4d9c5a752efa9304f323fae Mon Sep 17 00:00:00 2001 From: theDevJade Date: Wed, 24 Jul 2024 14:30:49 -0700 Subject: [PATCH 2/5] Fixed build errors & documentation --- Exiled.CustomModules/API/Features/CustomModule.cs | 4 ++-- Exiled.CustomModules/API/Features/CustomModuleSerializer.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Exiled.CustomModules/API/Features/CustomModule.cs b/Exiled.CustomModules/API/Features/CustomModule.cs index 828f3e7997..87956e514f 100644 --- a/Exiled.CustomModules/API/Features/CustomModule.cs +++ b/Exiled.CustomModules/API/Features/CustomModule.cs @@ -412,7 +412,7 @@ public override bool Equals(object obj) public override int GetHashCode() => base.GetHashCode(); /// - /// Gets or sets the serializer for configs and translations. + /// Gets or sets the serializer for custom modules. /// private static ISerializer ModuleSerializer { get; set; } = new SerializerBuilder() .WithTypeConverter(new VectorsConverter()) @@ -430,7 +430,7 @@ public override bool Equals(object obj) .Build(); /// - /// Gets or sets the deserializer for configs and translations. + /// Gets or sets the deserializer for custom modules. /// private static IDeserializer ModuleDeserializer { get; set; } = new DeserializerBuilder() .WithTypeConverter(new VectorsConverter()) diff --git a/Exiled.CustomModules/API/Features/CustomModuleSerializer.cs b/Exiled.CustomModules/API/Features/CustomModuleSerializer.cs index 2502122eb5..9a5375feb9 100644 --- a/Exiled.CustomModules/API/Features/CustomModuleSerializer.cs +++ b/Exiled.CustomModules/API/Features/CustomModuleSerializer.cs @@ -46,7 +46,7 @@ public void WriteYaml(IEmitter emitter, object value, Type type) Type genericArgument = type.GetGenericArguments()[0]; MethodInfo method = GetType().GetMethod(nameof(WriteYamlGeneric), BindingFlags.NonPublic | BindingFlags.Instance); MethodInfo genericMethod = method?.MakeGenericMethod(genericArgument); - genericMethod?.Invoke(this, new[] { ref emitter, ref value }); + genericMethod?.Invoke(this, new[] { emitter, value }); } private void WriteYamlGeneric(IEmitter emitter, object value) From b7fcdbd034a5edbf294b2c2ef9ad4a166a38596c Mon Sep 17 00:00:00 2001 From: theDevJade Date: Wed, 24 Jul 2024 14:34:28 -0700 Subject: [PATCH 3/5] Fixed StyleCop Errors --- .../API/Features/CustomModule.cs | 77 +++++++++---------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/Exiled.CustomModules/API/Features/CustomModule.cs b/Exiled.CustomModules/API/Features/CustomModule.cs index 87956e514f..ca1bb0924c 100644 --- a/Exiled.CustomModules/API/Features/CustomModule.cs +++ b/Exiled.CustomModules/API/Features/CustomModule.cs @@ -5,10 +5,6 @@ // // ----------------------------------------------------------------------- -using Exiled.API.Features.Serialization; -using Exiled.API.Features.Serialization.CustomConverters; -using YamlDotNet.Serialization.NodeDeserializers; - namespace Exiled.CustomModules.API.Features { using System; @@ -22,10 +18,13 @@ namespace Exiled.CustomModules.API.Features using Exiled.API.Features.Attributes; using Exiled.API.Features.Core; using Exiled.API.Features.DynamicEvents; + using Exiled.API.Features.Serialization; + using Exiled.API.Features.Serialization.CustomConverters; using Exiled.API.Interfaces; using Exiled.CustomModules.API.Enums; using Exiled.CustomModules.API.Features.Attributes; using YamlDotNet.Serialization; + using YamlDotNet.Serialization.NodeDeserializers; /// /// Represents a marker class for custom modules. @@ -213,6 +212,41 @@ internal string PointerPath } } + /// + /// Gets or sets the serializer for custom modules. + /// + private static ISerializer ModuleSerializer { get; set; } = new SerializerBuilder() + .WithTypeConverter(new VectorsConverter()) + .WithTypeConverter(new ColorConverter()) + .WithTypeConverter(new AttachmentIdentifiersConverter()) + .WithTypeConverter(new EnumClassConverter()) + .WithTypeConverter(new PrivateConstructorConverter()) + .WithTypeConverter(new CustomModuleSerializer()) + .WithEventEmitter(eventEmitter => new TypeAssigningEventEmitter(eventEmitter)) + .WithTypeInspector(inner => new CommentGatheringTypeInspector(inner)) + .WithEmissionPhaseObjectGraphVisitor(args => new CommentsObjectGraphVisitor(args.InnerVisitor)) + .WithNamingConvention(UnderscoredNamingConvention.Instance) + .IgnoreFields() + .DisableAliases() + .Build(); + + /// + /// Gets or sets the deserializer for custom modules. + /// + private static IDeserializer ModuleDeserializer { get; set; } = new DeserializerBuilder() + .WithTypeConverter(new VectorsConverter()) + .WithTypeConverter(new ColorConverter()) + .WithTypeConverter(new AttachmentIdentifiersConverter()) + .WithTypeConverter(new EnumClassConverter()) + .WithTypeConverter(new PrivateConstructorConverter()) + .WithTypeConverter(new CustomModuleSerializer()) + .WithNamingConvention(UnderscoredNamingConvention.Instance) + .WithNodeDeserializer(inner => new ValidatingNodeDeserializer(inner), deserializer => deserializer.InsteadOf()) + .WithDuplicateKeyChecking() + .IgnoreFields() + .IgnoreUnmatchedProperties() + .Build(); + /// /// Compares two operands: and . /// @@ -411,41 +445,6 @@ public override bool Equals(object obj) /// The 32-bit signed hash code of the current object instance. public override int GetHashCode() => base.GetHashCode(); - /// - /// Gets or sets the serializer for custom modules. - /// - private static ISerializer ModuleSerializer { get; set; } = new SerializerBuilder() - .WithTypeConverter(new VectorsConverter()) - .WithTypeConverter(new ColorConverter()) - .WithTypeConverter(new AttachmentIdentifiersConverter()) - .WithTypeConverter(new EnumClassConverter()) - .WithTypeConverter(new PrivateConstructorConverter()) - .WithTypeConverter(new CustomModuleSerializer()) - .WithEventEmitter(eventEmitter => new TypeAssigningEventEmitter(eventEmitter)) - .WithTypeInspector(inner => new CommentGatheringTypeInspector(inner)) - .WithEmissionPhaseObjectGraphVisitor(args => new CommentsObjectGraphVisitor(args.InnerVisitor)) - .WithNamingConvention(UnderscoredNamingConvention.Instance) - .IgnoreFields() - .DisableAliases() - .Build(); - - /// - /// Gets or sets the deserializer for custom modules. - /// - private static IDeserializer ModuleDeserializer { get; set; } = new DeserializerBuilder() - .WithTypeConverter(new VectorsConverter()) - .WithTypeConverter(new ColorConverter()) - .WithTypeConverter(new AttachmentIdentifiersConverter()) - .WithTypeConverter(new EnumClassConverter()) - .WithTypeConverter(new PrivateConstructorConverter()) - .WithTypeConverter(new CustomModuleSerializer()) - .WithNamingConvention(UnderscoredNamingConvention.Instance) - .WithNodeDeserializer(inner => new ValidatingNodeDeserializer(inner), deserializer => deserializer.InsteadOf()) - .WithDuplicateKeyChecking() - .IgnoreFields() - .IgnoreUnmatchedProperties() - .Build(); - /// /// Serializes the current module to a file specified by . /// From 329de7366bd8e0cc06ea03f0b858ae8615edb0ac Mon Sep 17 00:00:00 2001 From: theDevJade Date: Thu, 25 Jul 2024 18:46:10 -0700 Subject: [PATCH 4/5] Added complex node deserializers that handle CustomRole's and RoleSettings. Also hooked them into custom modules, tested on a server no errors. --- .../API/Features/CustomModule.cs | 6 +- .../API/Features/CustomModuleDeserializer.cs | 25 ++++ .../API/Features/CustomModuleSerializer.cs | 113 ------------------ .../API/Features/CustomRoles/RoleSettings.cs | 2 +- .../Deserializers/CustomRoleDeserializer.cs | 83 +++++++++++++ .../Deserializers/RoleSettingsDeserializer.cs | 55 +++++++++ 6 files changed, 167 insertions(+), 117 deletions(-) create mode 100644 Exiled.CustomModules/API/Features/CustomModuleDeserializer.cs delete mode 100644 Exiled.CustomModules/API/Features/CustomModuleSerializer.cs create mode 100644 Exiled.CustomModules/API/Features/Deserializers/CustomRoleDeserializer.cs create mode 100644 Exiled.CustomModules/API/Features/Deserializers/RoleSettingsDeserializer.cs diff --git a/Exiled.CustomModules/API/Features/CustomModule.cs b/Exiled.CustomModules/API/Features/CustomModule.cs index ca1bb0924c..c9f50eed9d 100644 --- a/Exiled.CustomModules/API/Features/CustomModule.cs +++ b/Exiled.CustomModules/API/Features/CustomModule.cs @@ -5,6 +5,8 @@ // // ----------------------------------------------------------------------- +using Exiled.CustomModules.API.Features.Generic; + namespace Exiled.CustomModules.API.Features { using System; @@ -221,7 +223,6 @@ internal string PointerPath .WithTypeConverter(new AttachmentIdentifiersConverter()) .WithTypeConverter(new EnumClassConverter()) .WithTypeConverter(new PrivateConstructorConverter()) - .WithTypeConverter(new CustomModuleSerializer()) .WithEventEmitter(eventEmitter => new TypeAssigningEventEmitter(eventEmitter)) .WithTypeInspector(inner => new CommentGatheringTypeInspector(inner)) .WithEmissionPhaseObjectGraphVisitor(args => new CommentsObjectGraphVisitor(args.InnerVisitor)) @@ -239,9 +240,8 @@ internal string PointerPath .WithTypeConverter(new AttachmentIdentifiersConverter()) .WithTypeConverter(new EnumClassConverter()) .WithTypeConverter(new PrivateConstructorConverter()) - .WithTypeConverter(new CustomModuleSerializer()) .WithNamingConvention(UnderscoredNamingConvention.Instance) - .WithNodeDeserializer(inner => new ValidatingNodeDeserializer(inner), deserializer => deserializer.InsteadOf()) + .WithNodeDeserializer(inner => new CustomModuleDeserializer(), deserializer => deserializer.InsteadOf()) .WithDuplicateKeyChecking() .IgnoreFields() .IgnoreUnmatchedProperties() diff --git a/Exiled.CustomModules/API/Features/CustomModuleDeserializer.cs b/Exiled.CustomModules/API/Features/CustomModuleDeserializer.cs new file mode 100644 index 0000000000..799f7fcf50 --- /dev/null +++ b/Exiled.CustomModules/API/Features/CustomModuleDeserializer.cs @@ -0,0 +1,25 @@ +namespace Exiled.CustomModules.API.Features +{ + using System; + + using Exiled.CustomModules.API.Features.CustomRoles; + using Exiled.CustomModules.API.Features.Deserializers; + using YamlDotNet.Core; + using YamlDotNet.Serialization; + + /// + public class CustomModuleDeserializer : INodeDeserializer + { + /// + public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object value) + { + if (CustomRoleDeserializer.IsCustomRoleType(expectedType)) + return CustomRoleDeserializer.Deserialize(parser, expectedType, nestedObjectDeserializer, out value); + if (expectedType == typeof(RoleSettings)) + return RoleSettingsDeserializer.Deserialize(parser, expectedType, nestedObjectDeserializer, out value); + + value = null; + return false; + } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/CustomModuleSerializer.cs b/Exiled.CustomModules/API/Features/CustomModuleSerializer.cs deleted file mode 100644 index 9a5375feb9..0000000000 --- a/Exiled.CustomModules/API/Features/CustomModuleSerializer.cs +++ /dev/null @@ -1,113 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomModules.API.Features -{ - using System; - using System.Reflection; - - using Attributes; - using Generic; - using YamlDotNet.Core; - using YamlDotNet.Core.Events; - using YamlDotNet.Serialization; - - /// - /// A IYamlTypeConverter for custom modules. - /// - public class CustomModuleSerializer : IYamlTypeConverter - { - /// - public bool Accepts(Type type) - { - return type == typeof(ModulePointer) || type == typeof(ModulePointer<>); - } - - /// - public object ReadYaml(IParser parser, Type type) - { - if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(ModulePointer<>)) - throw new InvalidOperationException($"Unsupported type: {type.FullName}"); - Type genericArgument = type.GetGenericArguments()[0]; - MethodInfo method = GetType().GetMethod(nameof(ReadYamlGeneric), BindingFlags.NonPublic | BindingFlags.Instance); - MethodInfo genericMethod = method?.MakeGenericMethod(genericArgument); - return genericMethod?.Invoke(this, new object[] { parser }); - } - - /// - public void WriteYaml(IEmitter emitter, object value, Type type) - { - if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(ModulePointer<>)) - throw new ArgumentException("The type must be the generic ModulePointer."); - Type genericArgument = type.GetGenericArguments()[0]; - MethodInfo method = GetType().GetMethod(nameof(WriteYamlGeneric), BindingFlags.NonPublic | BindingFlags.Instance); - MethodInfo genericMethod = method?.MakeGenericMethod(genericArgument); - genericMethod?.Invoke(this, new[] { emitter, value }); - } - - private void WriteYamlGeneric(IEmitter emitter, object value) - where TType : CustomModule - { - if (value is not ModulePointer pointer) - { - throw new ArgumentNullException(nameof(pointer), @"Value is not a valid ModulePointer."); - } - - emitter.Emit(new MappingStart(null, null, false, MappingStyle.Block)); - - emitter.Emit(new Scalar(null, "id")); - emitter.Emit(new Scalar(null, pointer.Id.ToString())); - - emitter.Emit(new Scalar(null, "module")); - emitter.Emit(new Scalar(null, typeof(TType).Name)); - - emitter.Emit(new Scalar(null, "assembly")); - emitter.Emit(new Scalar(null, pointer.GetType().Assembly.FullName)); - - emitter.Emit(new MappingEnd()); - } - - private object ReadYamlGeneric(IParser parser) - where TType : CustomModule - { - parser.Consume(); - - parser.Consume(); // id key - string id = parser.Consume().Value; - - parser.Consume(); // module key - string module = parser.Consume().Value; - - parser.Consume(); // assembly key - string assemblyName = parser.Consume().Value; - - parser.Consume(); - - // Load the specified assembly - Assembly assembly = Assembly.Load(assemblyName); - if (assembly == null) - { - throw new InvalidOperationException($"Assembly '{assemblyName}' could not be loaded."); - } - - // Create an instance of the appropriate ModulePointer subclass - foreach (Type t in assembly.GetTypes()) - { - ModuleIdentifierAttribute moduleIdentifier = t.GetCustomAttribute(); - if (moduleIdentifier == null || moduleIdentifier.Id != uint.Parse(id) || !typeof(ModulePointer).IsAssignableFrom(t)) - continue; - ModulePointer instance = Activator.CreateInstance(t) as ModulePointer; - if (instance == null) - continue; - instance.Id = Convert.ToUInt32(id); - return instance; - } - - throw new InvalidOperationException($"Could not find a suitable ModulePointer<{typeof(TType).Name}> type for id {id}"); - } - } -} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/CustomRoles/RoleSettings.cs b/Exiled.CustomModules/API/Features/CustomRoles/RoleSettings.cs index 0ee8bcff1f..fb2c1815a0 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/RoleSettings.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/RoleSettings.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // Copyright (c) Exiled Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. // diff --git a/Exiled.CustomModules/API/Features/Deserializers/CustomRoleDeserializer.cs b/Exiled.CustomModules/API/Features/Deserializers/CustomRoleDeserializer.cs new file mode 100644 index 0000000000..a5c8f79aef --- /dev/null +++ b/Exiled.CustomModules/API/Features/Deserializers/CustomRoleDeserializer.cs @@ -0,0 +1,83 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features.Deserializers +{ + using System; + using System.Reflection; + + using Exiled.CustomModules.API.Features.CustomRoles; + using YamlDotNet.Core; + using YamlDotNet.Core.Events; + + /// + /// The deserializer for Custom Roles. + /// + public static class CustomRoleDeserializer + { + /// + /// The actual deserializer. + /// + /// The Yaml Parser. + /// The type. + /// The base deserializer as backup. + /// If valid, returns this. + /// A bool stating if it was successful or not. + public static bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object value) + { + value = Activator.CreateInstance(expectedType); + parser.Consume(); + + while (parser.TryConsume(out Scalar scalar)) + { + PropertyInfo property = expectedType.GetProperty(scalar.Value, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); + if (property != null) + { + object propertyValue = nestedObjectDeserializer(parser, property.PropertyType); + property.SetValue(value, propertyValue); + } + else if (scalar.Value.Equals("settings", StringComparison.OrdinalIgnoreCase)) + { + PropertyInfo settingsProperty = expectedType.GetProperty("Settings", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); + if (settingsProperty != null) + { + object settingsValue = nestedObjectDeserializer(parser, settingsProperty.PropertyType); + settingsProperty.SetValue(value, settingsValue); + } + } + else + { + // Skip unknown properties + parser.SkipThisAndNestedEvents(); + } + } + + parser.Consume(); + return true; + } + + /// + /// A function that returns whether a type is a custom role. + /// + /// The Type. + /// A bool that says if the type is a custom role. + public static bool IsCustomRoleType(Type type) + { + while (type != null) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(CustomRole<>)) + { + return true; + } + + type = type.BaseType; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/Deserializers/RoleSettingsDeserializer.cs b/Exiled.CustomModules/API/Features/Deserializers/RoleSettingsDeserializer.cs new file mode 100644 index 0000000000..52964112d8 --- /dev/null +++ b/Exiled.CustomModules/API/Features/Deserializers/RoleSettingsDeserializer.cs @@ -0,0 +1,55 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features.Deserializers +{ + using System; + using System.Reflection; + + using Exiled.CustomModules.API.Features.CustomRoles; + using YamlDotNet.Core; + using YamlDotNet.Core.Events; + + /// + /// The deserializer for Role Settings. + /// + public static class RoleSettingsDeserializer + { + /// + /// The actual deserializer. + /// + /// The Yaml Parser. + /// The type. + /// The base deserializer as backup. + /// If valid, returns this. + /// A bool stating if it was successful or not. + public static bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object value) + { + RoleSettings roleSettings = new RoleSettings(); + parser.Consume(); + + while (parser.TryConsume(out Scalar scalar)) + { + PropertyInfo property = typeof(RoleSettings).GetProperty(scalar.Value, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); + if (property != null) + { + object propertyValue = nestedObjectDeserializer(parser, property.PropertyType); + property.SetValue(roleSettings, propertyValue); + } + else + { + // If the property does not exist, skip the scalar value + parser.SkipThisAndNestedEvents(); + } + } + + parser.Consume(); + value = roleSettings; + return true; + } + } +} \ No newline at end of file From 660688bd0ccda733e551270501b0ffbad6bfa820 Mon Sep 17 00:00:00 2001 From: theDevJade Date: Thu, 25 Jul 2024 19:04:07 -0700 Subject: [PATCH 5/5] Fixed Stylecop (grr) --- Exiled.CustomModules/API/Features/CustomModule.cs | 2 -- .../API/Features/CustomModuleDeserializer.cs | 14 +++++++++++++- .../API/Features/CustomRoles/RoleSettings.cs | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Exiled.CustomModules/API/Features/CustomModule.cs b/Exiled.CustomModules/API/Features/CustomModule.cs index c9f50eed9d..e5d5ab1806 100644 --- a/Exiled.CustomModules/API/Features/CustomModule.cs +++ b/Exiled.CustomModules/API/Features/CustomModule.cs @@ -5,8 +5,6 @@ // // ----------------------------------------------------------------------- -using Exiled.CustomModules.API.Features.Generic; - namespace Exiled.CustomModules.API.Features { using System; diff --git a/Exiled.CustomModules/API/Features/CustomModuleDeserializer.cs b/Exiled.CustomModules/API/Features/CustomModuleDeserializer.cs index 799f7fcf50..746fb70d07 100644 --- a/Exiled.CustomModules/API/Features/CustomModuleDeserializer.cs +++ b/Exiled.CustomModules/API/Features/CustomModuleDeserializer.cs @@ -1,4 +1,11 @@ -namespace Exiled.CustomModules.API.Features +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features { using System; @@ -14,9 +21,14 @@ public class CustomModuleDeserializer : INodeDeserializer public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object value) { if (CustomRoleDeserializer.IsCustomRoleType(expectedType)) + { return CustomRoleDeserializer.Deserialize(parser, expectedType, nestedObjectDeserializer, out value); + } + if (expectedType == typeof(RoleSettings)) + { return RoleSettingsDeserializer.Deserialize(parser, expectedType, nestedObjectDeserializer, out value); + } value = null; return false; diff --git a/Exiled.CustomModules/API/Features/CustomRoles/RoleSettings.cs b/Exiled.CustomModules/API/Features/CustomRoles/RoleSettings.cs index fb2c1815a0..0ee8bcff1f 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/RoleSettings.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/RoleSettings.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // Copyright (c) Exiled Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. //