From db2219af3ba350bcb238fece92545b60fee7ccfb Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:56:57 +0200 Subject: [PATCH 1/3] Nullable Vector3 from PR on ExSLMod#47 taken from https://github.com/ExSLMod-Team/EXILED/pull/47 --- .../CustomConverters/VectorsConverter.cs | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/EXILED/Exiled.Loader/Features/Configs/CustomConverters/VectorsConverter.cs b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/VectorsConverter.cs index 944e2f2784..63f413247d 100644 --- a/EXILED/Exiled.Loader/Features/Configs/CustomConverters/VectorsConverter.cs +++ b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/VectorsConverter.cs @@ -12,6 +12,7 @@ namespace Exiled.Loader.Features.Configs.CustomConverters using System.Globalization; using System.IO; + using Exiled.API.Features; using Exiled.API.Features.Pools; using UnityEngine; @@ -21,18 +22,37 @@ namespace Exiled.Loader.Features.Configs.CustomConverters using YamlDotNet.Serialization; /// - /// Converts a Vector2, Vector3 or Vector4 to Yaml configs and vice versa. + /// Converts a Vector2, Vector3 or Vector4 (including nullable) to Yaml configs and vice versa. /// public sealed class VectorsConverter : IYamlTypeConverter { /// - public bool Accepts(Type type) => type == typeof(Vector2) || type == typeof(Vector3) || type == typeof(Vector4); + public bool Accepts(Type type) + { + Type baseType = Nullable.GetUnderlyingType(type) ?? type; + return baseType == typeof(Vector2) || baseType == typeof(Vector3) || baseType == typeof(Vector4); + } /// public object ReadYaml(IParser parser, Type type) { + Type baseType = Nullable.GetUnderlyingType(type) ?? type; + + if (parser.TryConsume(out Scalar scalar)) + { + if (string.IsNullOrEmpty(scalar.Value) || scalar.Value.Equals("null", StringComparison.OrdinalIgnoreCase)) + { + if (Nullable.GetUnderlyingType(type) != null) + return null; + + Log.Error($"Cannot assign null to non-nullable type {baseType.FullName}."); + } + + Log.Error($"Expected mapping, but got scalar: {scalar.Value}"); + } + if (!parser.TryConsume(out _)) - throw new InvalidDataException($"Cannot deserialize object of type {type.FullName}."); + Log.Error($"Cannot deserialize object of type {type.FullName}."); List coordinates = ListPool.Pool.Get(4); int i = 0; @@ -45,16 +65,17 @@ public object ReadYaml(IParser parser, Type type) continue; } - if (!parser.TryConsume(out Scalar scalar) || !float.TryParse(scalar.Value, NumberStyles.Float, CultureInfo.GetCultureInfo("en-US"), out float coordinate)) + if (!parser.TryConsume(out Scalar coordScalar) || + !float.TryParse(coordScalar.Value, NumberStyles.Float, CultureInfo.GetCultureInfo("en-US"), out float coordinate)) { ListPool.Pool.Return(coordinates); - throw new InvalidDataException($"Invalid float value."); + throw new InvalidDataException("Invalid float value."); } coordinates.Add(coordinate); } - object vector = Activator.CreateInstance(type, coordinates.ToArray()); + object vector = Activator.CreateInstance(baseType, coordinates.ToArray()); ListPool.Pool.Return(coordinates); @@ -64,6 +85,12 @@ public object ReadYaml(IParser parser, Type type) /// public void WriteYaml(IEmitter emitter, object value, Type type) { + if (value is null) + { + emitter.Emit(new Scalar("null")); + return; + } + Dictionary coordinates = DictionaryPool.Pool.Get(); if (value is Vector2 vector2) From c40e670b9868e4da6e577d72397162e01a999128 Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:01:54 +0200 Subject: [PATCH 2/3] ColorConverter Nullable too --- .../CustomConverters/ColorConverter.cs | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/EXILED/Exiled.Loader/Features/Configs/CustomConverters/ColorConverter.cs b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/ColorConverter.cs index 6bb6768cab..f248c2e829 100644 --- a/EXILED/Exiled.Loader/Features/Configs/CustomConverters/ColorConverter.cs +++ b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/ColorConverter.cs @@ -12,6 +12,7 @@ namespace Exiled.Loader.Features.Configs.CustomConverters using System.Globalization; using System.IO; + using Exiled.API.Features; using Exiled.API.Features.Pools; using UnityEngine; @@ -26,13 +27,32 @@ namespace Exiled.Loader.Features.Configs.CustomConverters public sealed class ColorConverter : IYamlTypeConverter { /// - public bool Accepts(Type type) => type == typeof(Color); + public bool Accepts(Type type) + { + Type baseType = Nullable.GetUnderlyingType(type) ?? type; + return baseType == typeof(Color); + } /// public object ReadYaml(IParser parser, Type type) { + Type baseType = Nullable.GetUnderlyingType(type) ?? type; + + if (parser.TryConsume(out Scalar scalar)) + { + if (string.IsNullOrEmpty(scalar.Value) || scalar.Value.Equals("null", StringComparison.OrdinalIgnoreCase)) + { + if (Nullable.GetUnderlyingType(type) != null) + return null; + + Log.Error($"Cannot assign null to non-nullable type {baseType.FullName}."); + } + + Log.Error($"Expected mapping, but got scalar: {scalar.Value}"); + } + if (!parser.TryConsume(out _)) - throw new InvalidDataException($"Cannot deserialize object of type {type.FullName}"); + Log.Error($"Cannot deserialize object of type {type.FullName}."); List coordinates = ListPool.Pool.Get(4); int i = 0; @@ -45,7 +65,7 @@ public object ReadYaml(IParser parser, Type type) continue; } - if (!parser.TryConsume(out Scalar scalar) || !float.TryParse(scalar.Value, NumberStyles.Float, CultureInfo.GetCultureInfo("en-US"), out float coordinate)) + if (!parser.TryConsume(out Scalar coordScalar) || !float.TryParse(coordScalar.Value, NumberStyles.Float, CultureInfo.GetCultureInfo("en-US"), out float coordinate)) { ListPool.Pool.Return(coordinates); throw new InvalidDataException("Invalid float value."); @@ -64,6 +84,12 @@ public object ReadYaml(IParser parser, Type type) /// public void WriteYaml(IEmitter emitter, object value, Type type) { + if (value is null) + { + emitter.Emit(new Scalar("null")); + return; + } + Dictionary coordinates = DictionaryPool.Pool.Get(); if (value is Color color) From d8a0ce32945b4f2358a71a2c0952e20f318cc8d4 Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:02:27 +0200 Subject: [PATCH 3/3] doc --- .../Features/Configs/CustomConverters/ColorConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXILED/Exiled.Loader/Features/Configs/CustomConverters/ColorConverter.cs b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/ColorConverter.cs index f248c2e829..fb29d82930 100644 --- a/EXILED/Exiled.Loader/Features/Configs/CustomConverters/ColorConverter.cs +++ b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/ColorConverter.cs @@ -22,7 +22,7 @@ namespace Exiled.Loader.Features.Configs.CustomConverters using YamlDotNet.Serialization; /// - /// Converts to Yaml configs and vice versa. + /// Converts (including nullable) to Yaml configs and vice versa. /// public sealed class ColorConverter : IYamlTypeConverter {