From 151684c093b6c60aed6312cd9f73f5fbce186484 Mon Sep 17 00:00:00 2001 From: PUDGE133 Date: Wed, 1 Apr 2026 13:54:02 +0300 Subject: [PATCH 1/4] - Deleted a non-existent room "HczStraightVariant" - Deleted incorrect error method "AttachmentIdentifier.TryParse" - Renamed "AttachmentName" converter because its name was incorrect and interfered with the new converter - Created a new converter "Attachment Identifier Converter" for the "Attachment Identifier" structure - Implemented new changes to the Loader class --- EXILED/Exiled.API/Enums/RoomType.cs | 5 -- EXILED/Exiled.API/Features/Room.cs | 3 +- .../Structs/AttachmentIdentifier.cs | 26 +------ EXILED/Exiled.CustomRoles/CustomRoles.cs | 3 +- .../AttachmentIdentifierConverter.cs | 69 +++++++++++++++++++ ...onverter.cs => AttachmentNameConverter.cs} | 4 +- EXILED/Exiled.Loader/Loader.cs | 6 +- 7 files changed, 81 insertions(+), 35 deletions(-) create mode 100644 EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentIdentifierConverter.cs rename EXILED/Exiled.Loader/Features/Configs/CustomConverters/{AttachmentIdentifiersConverter.cs => AttachmentNameConverter.cs} (91%) diff --git a/EXILED/Exiled.API/Enums/RoomType.cs b/EXILED/Exiled.API/Enums/RoomType.cs index 6dceeea6e2..9af78fc1fe 100644 --- a/EXILED/Exiled.API/Enums/RoomType.cs +++ b/EXILED/Exiled.API/Enums/RoomType.cs @@ -324,11 +324,6 @@ public enum RoomType /// HczStraightPipeRoom, - /// - /// Heavy Containment Zone's straight hall. - /// - HczStraightVariant, - /// /// Entrance Zone's straight hall with Dr.L's and conference room 9b locked room. /// diff --git a/EXILED/Exiled.API/Features/Room.cs b/EXILED/Exiled.API/Features/Room.cs index 846a76509c..329df59fbf 100644 --- a/EXILED/Exiled.API/Features/Room.cs +++ b/EXILED/Exiled.API/Features/Room.cs @@ -500,8 +500,7 @@ private static RoomType FindType(GameObject gameObject) "HCZ_Corner_Deep" => RoomType.HczCornerDeep, "HCZ_Straight" => RoomType.HczStraight, "HCZ_Straight_C" => RoomType.HczStraightC, - "HCZ_Straight_PipeRoom"=> RoomType.HczStraightPipeRoom, - "HCZ_Straight Variant" => RoomType.HczStraightVariant, + "HCZ_Straight_PipeRoom" => RoomType.HczStraightPipeRoom, "HCZ_ChkpA" => RoomType.HczElevatorA, "HCZ_ChkpB" => RoomType.HczElevatorB, "HCZ_127" => RoomType.Hcz127, diff --git a/EXILED/Exiled.API/Structs/AttachmentIdentifier.cs b/EXILED/Exiled.API/Structs/AttachmentIdentifier.cs index cc27b57a05..2c58c8dfd1 100644 --- a/EXILED/Exiled.API/Structs/AttachmentIdentifier.cs +++ b/EXILED/Exiled.API/Structs/AttachmentIdentifier.cs @@ -12,7 +12,7 @@ namespace Exiled.API.Structs using System.Linq; using Exiled.API.Enums; - + using Exiled.API.Features.Items; using InventorySystem.Items.Firearms.Attachments; using InventorySystem.Items.Firearms.Attachments.Components; @@ -129,33 +129,13 @@ internal AttachmentIdentifier(uint code, AttachmentName name, AttachmentSlot slo /// A value representing the subtraction between the two operands. public static uint operator -(uint left, AttachmentIdentifier right) => left - right.Code; - /// - /// Converts the string representation of a to its equivalent. - /// A return value indicates whether the conversion is succeeded or failed. - /// - /// The to convert. - /// The converted . - /// if was converted successfully; otherwise, . - public static bool TryParse(string s, out AttachmentIdentifier identifier) - { - identifier = default; - - foreach (AttachmentIdentifier attId in Features.Items.Firearm.AvailableAttachments.Values.SelectMany(kvp => kvp.Where(kvp2 => kvp2.Name.ToString() == s))) - { - identifier = attId; - return true; - } - - return false; - } - /// /// Gets a by name. /// /// Weapons . /// Attachment name. /// instance. - public static AttachmentIdentifier Get(FirearmType type, AttachmentName name) => Features.Items.Firearm.AvailableAttachments[type].FirstOrDefault(identifier => identifier.Name == name); + public static AttachmentIdentifier Get(FirearmType type, AttachmentName name) => Firearm.AvailableAttachments[type].FirstOrDefault(identifier => identifier.Name == name); /// /// Gets the all 's for type, by slot. @@ -163,7 +143,7 @@ public static bool TryParse(string s, out AttachmentIdentifier identifier) /// Weapons . /// Attachment slot. /// instance. - public static IEnumerable Get(FirearmType type, AttachmentSlot slot) => Features.Items.Firearm.AvailableAttachments[type].Where(identifier => identifier.Slot == slot); + public static IEnumerable Get(FirearmType type, AttachmentSlot slot) => Firearm.AvailableAttachments[type].Where(identifier => identifier.Slot == slot); /// /// Converts the string representation of a to its equivalent. diff --git a/EXILED/Exiled.CustomRoles/CustomRoles.cs b/EXILED/Exiled.CustomRoles/CustomRoles.cs index a66457b3bd..f52f08f3b3 100644 --- a/EXILED/Exiled.CustomRoles/CustomRoles.cs +++ b/EXILED/Exiled.CustomRoles/CustomRoles.cs @@ -36,7 +36,8 @@ public CustomRoles() Loader.Deserializer = new DeserializerBuilder() .WithTypeConverter(new VectorsConverter()) .WithTypeConverter(new ColorConverter()) - .WithTypeConverter(new AttachmentIdentifiersConverter()) + .WithTypeConverter(new AttachmentNameConverter()) + .WithTypeConverter(new AttachmentIdentifierConverter()) .WithNamingConvention(UnderscoredNamingConvention.Instance) .WithNodeDeserializer(inner => new AbstractClassNodeTypeResolver(inner, new AggregateExpectationTypeResolver(UnderscoredNamingConvention.Instance)), s => s.InsteadOf()) .IgnoreFields() diff --git a/EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentIdentifierConverter.cs b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentIdentifierConverter.cs new file mode 100644 index 0000000000..5d952dc34d --- /dev/null +++ b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentIdentifierConverter.cs @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Loader.Features.Configs.CustomConverters +{ + using System; + using System.Linq; + + using API.Structs; + + using Exiled.API.Enums; + using Exiled.API.Features.Items; + + using InventorySystem.Items.Firearms.Attachments; + + using YamlDotNet.Core; + using YamlDotNet.Core.Events; + using YamlDotNet.Serialization; + + /// + /// Converter and to . + /// + public sealed class AttachmentIdentifierConverter : IYamlTypeConverter + { + /// + public bool Accepts(Type type) => type == typeof(AttachmentIdentifier); + + /// + public object ReadYaml(IParser parser, Type type) + { + if (!parser.TryConsume(out Scalar scalar)) + throw new InvalidOperationException($"Expected a scalar for {nameof(AttachmentIdentifier)}."); + + string[] parts = scalar?.Value?.Split(':'); + if (parts.Length != 3) + throw new InvalidOperationException($"Invalid AttachmentIdentifier format: '{scalar?.Value}'. Expected 'FirearmType:AttachmentName'."); + + if (!Enum.TryParse(parts[0], true, out AttachmentName attachmentName)) + throw new InvalidOperationException($"Invalid AttachmentName: '{parts[0]}'."); + + if (!Enum.TryParse(parts[1], true, out AttachmentSlot attachmentSlot)) + throw new InvalidOperationException($"Invalid AttachmentName: '{parts[1]}'."); + + if (!uint.TryParse(parts[2], out uint code)) + throw new InvalidOperationException($"Invalid AttachmentName: '{parts[2]}'."); + + return Firearm.AvailableAttachments + .SelectMany(kvp => kvp.Value) + .FirstOrDefault(att => att.Name == attachmentName && att.Slot == attachmentSlot && att.Code == code); + } + + /// + public void WriteYaml(IEmitter emitter, object value, Type type) + { + AttachmentIdentifier attId = default; + + if (value is AttachmentIdentifier castAttId) + attId = castAttId; + + // If anyone ever looks at this code and doesn't understand why it's implemented the way it is, here's an explanation. The problem is that the NW code doesn't provide a way to properly serialize attachments into a string so that this string can then be deserialized back into an object while maintaining integrity. Therefore, literally the only way to obtain an object is to store it as three properties. Storing only AttachmentName will cause problems. Storing it as "FirearmType:AttachmentName" will also cause problems. + // https://discord.com/channels/656673194693885975/1002713309876854924/1488655471697989682 + emitter.Emit(new Scalar($"{attId.Name}:{attId.Slot}:{attId.Code}")); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentIdentifiersConverter.cs b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentNameConverter.cs similarity index 91% rename from EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentIdentifiersConverter.cs rename to EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentNameConverter.cs index 950078e802..d40ea7d446 100644 --- a/EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentIdentifiersConverter.cs +++ b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentNameConverter.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // Copyright (c) ExMod Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. // @@ -22,7 +22,7 @@ namespace Exiled.Loader.Features.Configs.CustomConverters /// /// Converts a of to Yaml configs and vice versa. /// - public sealed class AttachmentIdentifiersConverter : IYamlTypeConverter + public sealed class AttachmentNameConverter : IYamlTypeConverter { /// public bool Accepts(Type type) => type == typeof(AttachmentName); diff --git a/EXILED/Exiled.Loader/Loader.cs b/EXILED/Exiled.Loader/Loader.cs index 00754c8c09..0fe42a2d58 100644 --- a/EXILED/Exiled.Loader/Loader.cs +++ b/EXILED/Exiled.Loader/Loader.cs @@ -106,7 +106,8 @@ public Loader() public static ISerializer Serializer { get; set; } = new SerializerBuilder() .WithTypeConverter(new VectorsConverter()) .WithTypeConverter(new ColorConverter()) - .WithTypeConverter(new AttachmentIdentifiersConverter()) + .WithTypeConverter(new AttachmentNameConverter()) + .WithTypeConverter(new AttachmentIdentifierConverter()) .WithEventEmitter(eventEmitter => new TypeAssigningEventEmitter(eventEmitter)) .WithTypeInspector(inner => new CommentGatheringTypeInspector(inner)) .WithEmissionPhaseObjectGraphVisitor(args => new CommentsObjectGraphVisitor(args.InnerVisitor)) @@ -121,7 +122,8 @@ public Loader() public static IDeserializer Deserializer { get; set; } = new DeserializerBuilder() .WithTypeConverter(new VectorsConverter()) .WithTypeConverter(new ColorConverter()) - .WithTypeConverter(new AttachmentIdentifiersConverter()) + .WithTypeConverter(new AttachmentNameConverter()) + .WithTypeConverter(new AttachmentIdentifierConverter()) .WithNamingConvention(UnderscoredNamingConvention.Instance) .WithNodeDeserializer(inner => new ValidatingNodeDeserializer(inner), deserializer => deserializer.InsteadOf()) .IgnoreFields() From 44fcfd4cf7ff38a5e2d8cf046bae63398f63223d Mon Sep 17 00:00:00 2001 From: PUDGE133 Date: Wed, 1 Apr 2026 15:06:21 +0300 Subject: [PATCH 2/4] - Cancel fix about non exists room --- EXILED/Exiled.API/Enums/RoomType.cs | 5 +++++ EXILED/Exiled.API/Features/Room.cs | 1 + 2 files changed, 6 insertions(+) diff --git a/EXILED/Exiled.API/Enums/RoomType.cs b/EXILED/Exiled.API/Enums/RoomType.cs index 9af78fc1fe..6dceeea6e2 100644 --- a/EXILED/Exiled.API/Enums/RoomType.cs +++ b/EXILED/Exiled.API/Enums/RoomType.cs @@ -324,6 +324,11 @@ public enum RoomType /// HczStraightPipeRoom, + /// + /// Heavy Containment Zone's straight hall. + /// + HczStraightVariant, + /// /// Entrance Zone's straight hall with Dr.L's and conference room 9b locked room. /// diff --git a/EXILED/Exiled.API/Features/Room.cs b/EXILED/Exiled.API/Features/Room.cs index 329df59fbf..df59720dae 100644 --- a/EXILED/Exiled.API/Features/Room.cs +++ b/EXILED/Exiled.API/Features/Room.cs @@ -501,6 +501,7 @@ private static RoomType FindType(GameObject gameObject) "HCZ_Straight" => RoomType.HczStraight, "HCZ_Straight_C" => RoomType.HczStraightC, "HCZ_Straight_PipeRoom" => RoomType.HczStraightPipeRoom, + "HCZ_Straight Variant" => RoomType.HczStraightVariant, // As of the current date 01.04.2026 this room does not exist. It hasn't existed for quite some time. "HCZ_ChkpA" => RoomType.HczElevatorA, "HCZ_ChkpB" => RoomType.HczElevatorB, "HCZ_127" => RoomType.Hcz127, From 9c3b16ba0b52dd95807b3640a3b34f73f4d1579d Mon Sep 17 00:00:00 2001 From: PUDGE133 Date: Wed, 1 Apr 2026 15:12:35 +0300 Subject: [PATCH 3/4] oops --- .../CustomConverters/AttachmentIdentifierConverter.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentIdentifierConverter.cs b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentIdentifierConverter.cs index 5d952dc34d..1feb3cd2c6 100644 --- a/EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentIdentifierConverter.cs +++ b/EXILED/Exiled.Loader/Features/Configs/CustomConverters/AttachmentIdentifierConverter.cs @@ -37,16 +37,16 @@ public object ReadYaml(IParser parser, Type type) string[] parts = scalar?.Value?.Split(':'); if (parts.Length != 3) - throw new InvalidOperationException($"Invalid AttachmentIdentifier format: '{scalar?.Value}'. Expected 'FirearmType:AttachmentName'."); + throw new InvalidOperationException($"Invalid AttachmentIdentifier format: '{scalar?.Value}'. Expected \"AttachmentName:AttachmentSlot:AttachmentCode\"."); if (!Enum.TryParse(parts[0], true, out AttachmentName attachmentName)) throw new InvalidOperationException($"Invalid AttachmentName: '{parts[0]}'."); if (!Enum.TryParse(parts[1], true, out AttachmentSlot attachmentSlot)) - throw new InvalidOperationException($"Invalid AttachmentName: '{parts[1]}'."); + throw new InvalidOperationException($"Invalid AttachmentSlot: '{parts[1]}'."); if (!uint.TryParse(parts[2], out uint code)) - throw new InvalidOperationException($"Invalid AttachmentName: '{parts[2]}'."); + throw new InvalidOperationException($"Invalid AttachmentCode: '{parts[2]}'."); return Firearm.AvailableAttachments .SelectMany(kvp => kvp.Value) @@ -61,7 +61,7 @@ public void WriteYaml(IEmitter emitter, object value, Type type) if (value is AttachmentIdentifier castAttId) attId = castAttId; - // If anyone ever looks at this code and doesn't understand why it's implemented the way it is, here's an explanation. The problem is that the NW code doesn't provide a way to properly serialize attachments into a string so that this string can then be deserialized back into an object while maintaining integrity. Therefore, literally the only way to obtain an object is to store it as three properties. Storing only AttachmentName will cause problems. Storing it as "FirearmType:AttachmentName" will also cause problems. + // If anyone ever looks at this code and doesn't understand why it's implemented the way it is, here's an explanation. The problem is that the NW code doesn't provide a way to properly serialize attachments into a string so that this string can then be deserialized back into an object while maintaining integrity. Therefore, literally the only way to obtain an object is to store it as three properties. Storing only "AttachmentName" will cause problems. Storing it as "FirearmType:AttachmentName" will also cause problems. // https://discord.com/channels/656673194693885975/1002713309876854924/1488655471697989682 emitter.Emit(new Scalar($"{attId.Name}:{attId.Slot}:{attId.Code}")); } From beff5aa048056f9b03b590a34c2336eddfcda0ec Mon Sep 17 00:00:00 2001 From: PUDGE133 <47304666+PUDGE133@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:42:27 +0300 Subject: [PATCH 4/4] Update EXILED/Exiled.API/Features/Room.cs Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com> --- EXILED/Exiled.API/Features/Room.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXILED/Exiled.API/Features/Room.cs b/EXILED/Exiled.API/Features/Room.cs index df59720dae..01fa683790 100644 --- a/EXILED/Exiled.API/Features/Room.cs +++ b/EXILED/Exiled.API/Features/Room.cs @@ -501,7 +501,7 @@ private static RoomType FindType(GameObject gameObject) "HCZ_Straight" => RoomType.HczStraight, "HCZ_Straight_C" => RoomType.HczStraightC, "HCZ_Straight_PipeRoom" => RoomType.HczStraightPipeRoom, - "HCZ_Straight Variant" => RoomType.HczStraightVariant, // As of the current date 01.04.2026 this room does not exist. It hasn't existed for quite some time. + "HCZ_Straight Variant" => RoomType.HczStraightVariant, "HCZ_ChkpA" => RoomType.HczElevatorA, "HCZ_ChkpB" => RoomType.HczElevatorB, "HCZ_127" => RoomType.Hcz127,