Decompiled source of ExtraSyringeCustomization v0.3.3

plugins/ExtraSyringeCustomization.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Text.Json.Serialization;
using AK;
using Agents;
using AssetShards;
using BepInEx;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using BepInEx.Unity.IL2CPP.Utils.Collections;
using ExtraSyringeCustomization.Converters;
using ExtraSyringeCustomization.Packets;
using ExtraSyringeCustomization.Properties;
using ExtraSyringeCustomization.Properties.BuiltIn;
using ExtraSyringeCustomization.Registries;
using ExtraSyringeCustomization.Utils;
using FX_EffectSystem;
using Flaff.Collections.Registries;
using GTFO.API;
using GameData;
using HarmonyLib;
using Il2CppInterop.Runtime.Attributes;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using MTFO.Managers;
using Microsoft.CodeAnalysis;
using Player;
using SNetwork;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ExtraSyringeCustomization")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ExtraSyringeCustomization")]
[assembly: AssemblyTitle("ExtraSyringeCustomization")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ExtraSyringeCustomization
{
	public class CustomSyringeDefinition : IRegisterable
	{
		public uint SyringeID { get; set; }

		public string? DebugName { get; set; }

		public SyringePropertyList Properties { get; set; }

		public CustomSyringeDefinition()
		{
			Properties = new SyringePropertyList();
		}

		internal IEnumerator ActivateSequence(PlayerAgent player, SyringeFirstPerson syringe, Random random)
		{
			SyringePropertyBase[] properties = Properties.GetProperties();
			int index = 0;
			int propertyCount = properties.Length;
			List<SyringePropertyBase> batch = new List<SyringePropertyBase>(propertyCount);
			while (index < propertyCount)
			{
				SyringePropertyBase item = properties[index];
				batch.Add(item);
				if (index + 1 < propertyCount && !properties[index].Timing.IsRelativeToPreviousProperties)
				{
					index++;
					continue;
				}
				List<(IEnumerator, SyringePropertyBase)> batchSequences = new List<(IEnumerator, SyringePropertyBase)>();
				foreach (SyringePropertyBase item2 in batch)
				{
					batchSequences.Add((CreatePropertySequence(player, syringe, item2, random), item2));
				}
				while (batchSequences.Count > 0)
				{
					int num = 0;
					while (num < batchSequences.Count)
					{
						var (enumerator2, syringePropertyBase) = batchSequences[num];
						try
						{
							if (!enumerator2.MoveNext())
							{
								batchSequences.RemoveAt(num);
							}
							else
							{
								num++;
							}
						}
						catch (Exception value)
						{
							L.Error($"Error whilst processing property '{syringePropertyBase.Name}' on player {player.PlayerName} with syringe ID {SyringeID}: {value}");
							batchSequences.RemoveAt(num);
						}
					}
					yield return null;
				}
				batch.Clear();
				index++;
			}
		}

		private IEnumerator CreatePropertySequence(PlayerAgent player, SyringeFirstPerson syringe, SyringePropertyBase property, Random random)
		{
			SyringePropertySequenceInfo info = new SyringePropertySequenceInfo();
			if ((int)GameStateManager.CurrentStateName != 10)
			{
				info.ForceCompletion();
			}
			IEnumerator enumerator = CreatePropertyInnerSequence(player, syringe, property, random, info);
			while (enumerator.MoveNext() && !info.ForceComplete)
			{
				if ((int)GameStateManager.CurrentStateName != 10)
				{
					info.ForceCompletion();
				}
				yield return enumerator.Current;
			}
		}

		private IEnumerator CreatePropertyInnerSequence(PlayerAgent player, SyringeFirstPerson syringe, SyringePropertyBase property, Random random, SyringePropertySequenceInfo info)
		{
			double delay = property.Timing.Delay.GetValue(random);
			for (double t = 0.0; t < delay; t += info.DeltaTime)
			{
				if (info.ForceComplete)
				{
					break;
				}
				yield return null;
			}
			if (!info.ForceComplete)
			{
				IEnumerator sequence = property.Apply(player, syringe, random, info);
				while (sequence.MoveNext())
				{
					yield return sequence.Current;
				}
			}
		}

		string IRegisterable.GetID()
		{
			return CustomSyringeRegistry.SyringeIDToRegistryID(SyringeID);
		}
	}
	public sealed class ExtraSyringeHandler : MonoBehaviour
	{
		private class SyringeWrapper
		{
			private enum SyringeState
			{
				NONE,
				SETUP,
				INJECT,
				CLEANUP
			}

			private readonly Random m_random;

			private readonly _ApplySyringe_d__18 m_enumerator;

			private readonly CustomSyringeDefinition? m_syringe;

			private SyringeState m_state;

			public SyringeWrapper(_ApplySyringe_d__18 enumerator)
			{
				m_random = new Random();
				m_enumerator = enumerator;
				SyringeRegistryHandler.Syringes.TryGetEntry(((GameDataBlockBase<ItemDataBlock>)(object)((Item)enumerator.__4__this).ItemDataBlock).persistentID, out m_syringe);
				m_state = SyringeState.NONE;
			}

			public bool Equals(SyringeWrapper other)
			{
				return ((Il2CppObjectBase)m_enumerator).Pointer == ((Il2CppObjectBase)other.m_enumerator).Pointer;
			}

			public bool Equals(_ApplySyringe_d__18 other)
			{
				return ((Il2CppObjectBase)m_enumerator).Pointer == ((Il2CppObjectBase)other).Pointer;
			}

			public bool OnMoveNext(ref bool result)
			{
				//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ba: Expected O, but got Unknown
				//IL_0112: Unknown result type (might be due to invalid IL or missing references)
				//IL_0117: Unknown result type (might be due to invalid IL or missing references)
				//IL_0151: Unknown result type (might be due to invalid IL or missing references)
				//IL_0142: Unknown result type (might be due to invalid IL or missing references)
				m_state++;
				SyringeFirstPerson _4__this = m_enumerator.__4__this;
				L.Debug("MoveNext Syringe (" + ((GameDataBlockBase<ItemDataBlock>)(object)((Item)_4__this).ItemDataBlock).persistentID + ") " + m_state);
				if (m_syringe != null)
				{
					if (m_state == SyringeState.SETUP)
					{
						return true;
					}
					if (m_state == SyringeState.INJECT)
					{
						((Item)_4__this).Owner.FPItemHolder.ItemHiddenTrigger = true;
						Current.StartCoroutine(m_syringe.ActivateSequence(((Item)_4__this).Owner, _4__this, m_random));
						m_enumerator.__2__current = (Object)new WaitForSeconds(0.7f);
						result = true;
						return false;
					}
					if (m_state == SyringeState.CLEANUP)
					{
						for (int i = 0; i < s_syringeWrappers.Count; i++)
						{
							if (s_syringeWrappers[i].Equals(this))
							{
								s_syringeWrappers.RemoveAt(i);
								break;
							}
						}
						FirstPersonItemHolder fPItemHolder = ((ItemEquippable)_4__this).FPItemHolder;
						if (fPItemHolder != null)
						{
							fPItemHolder.AnimationSequenceEnd();
						}
						pItemData_Custom customData = ((Item)_4__this).GetCustomData();
						if ((customData.ammo -= 1f) <= 0.01f)
						{
							ItemDataBlock itemDataBlock = ((Item)_4__this).ItemDataBlock;
							PlayerBackpackManager.ClearLocalSlot((InventorySlot)((itemDataBlock == null) ? 5 : ((int)itemDataBlock.inventorySlot)), true, true);
						}
						else
						{
							((Item)_4__this).SetCustomData(customData, true);
						}
						_4__this.m_applyTriggered = false;
						result = false;
						return false;
					}
				}
				else
				{
					L.Debug("Custom Syringe was null!");
				}
				return true;
			}
		}

		private static ExtraSyringeHandler? s_current;

		private static readonly List<SyringeWrapper> s_syringeWrappers;

		public static string ConfigPath { get; internal set; }

		public static ExtraSyringeHandler Current
		{
			get
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Expected O, but got Unknown
				if ((Object)(object)s_current == (Object)null)
				{
					GameObject val = new GameObject();
					s_current = val.AddComponent<ExtraSyringeHandler>();
					Object.DontDestroyOnLoad((Object)val);
				}
				return s_current;
			}
		}

		static ExtraSyringeHandler()
		{
			s_syringeWrappers = new List<SyringeWrapper>();
			ConfigPath = "";
			ClassInjector.RegisterTypeInIl2Cpp<ExtraSyringeHandler>();
		}

		[HideFromIl2Cpp]
		public Coroutine StartCoroutine(CustomRoutine routine)
		{
			return ((MonoBehaviour)this).StartCoroutine(CollectionExtensions.WrapToIl2Cpp((IEnumerator)routine));
		}

		[HideFromIl2Cpp]
		public Coroutine StartCoroutine(IEnumerator routine)
		{
			return ((MonoBehaviour)this).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(routine));
		}

		internal static void ClearSyringeList()
		{
			s_syringeWrappers.Clear();
		}

		internal static bool InvokeMoveNextOnApplySyringe(_ApplySyringe_d__18 instance, ref bool result)
		{
			SyringeWrapper syringeWrapper = null;
			for (int i = 0; i < s_syringeWrappers.Count; i++)
			{
				SyringeWrapper syringeWrapper2 = s_syringeWrappers[i];
				if (syringeWrapper2.Equals(instance))
				{
					syringeWrapper = syringeWrapper2;
					break;
				}
			}
			if (syringeWrapper == null)
			{
				syringeWrapper = new SyringeWrapper(instance);
				s_syringeWrappers.Add(syringeWrapper);
			}
			return syringeWrapper.OnMoveNext(ref result);
		}
	}
	[BepInPlugin("dev.flaff.gtfo.ExtraSyringeCustomization", "ExtraSyringeCustomization", "0.3.3")]
	internal class MainPlugin : BasePlugin
	{
		internal static ManualLogSource? LogSource;

		public override void Load()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			LogSource = ((BasePlugin)this).Log;
			new Harmony("dev.flaff.gtfo.ExtraSyringeCustomization").PatchAll();
			SyringeRegistryHandler.Properties.Register<SyringeMeleeBuffTimeProperty>();
			SyringeRegistryHandler.Properties.Register<SyringeMovementMultiplierProperty>();
			SyringeRegistryHandler.Properties.Register<SyringeHealthValueProperty>();
			SyringeRegistryHandler.Properties.Register<SyringeAmmoValueProperty>();
			SyringeRegistryHandler.Properties.Register<SyringeToolValueProperty>();
			SyringeRegistryHandler.Properties.Register<SyringeDisinfectionValueProperty>();
			SyringeRegistryHandler.Properties.Register<SyringeInfectionValueProperty>();
			SyringeRegistryHandler.Properties.Register<SyringeSelfDestructProperty>();
			SyringeRegistryHandler.Properties.Register<SyringeDOTProperty>();
			SyringeRegistryHandler.Properties.Register<SyringeROTProperty>();
			NetworkAPI.RegisterEvent<SelfDestruct>("Syringe-Self-Destruct", (Action<ulong, SelfDestruct>)delegate(ulong _, SelfDestruct packet)
			{
				SNet_Player val = default(SNet_Player);
				if (SNet.Core.TryGetPlayer(packet.playerID, ref val, false))
				{
					SyringeSelfDestructProperty.DoExplodeEffect(((Il2CppObjectBase)val.PlayerAgent).Cast<PlayerAgent>());
				}
			});
		}
	}
	internal static class PLUGIN_CONSTANTS
	{
		public const string PLUGIN_NAME = "ExtraSyringeCustomization";

		public const string PLUGIN_VERSION = "0.3.3";

		public const string PLUGIN_GUID = "dev.flaff.gtfo.ExtraSyringeCustomization";
	}
	internal sealed class SyringePropertyInfo : IRegisterable
	{
		public string PropertyName { get; }

		public Type PropertyType { get; }

		public SyringePropertyInfo(string name, Type type)
		{
			PropertyName = name;
			PropertyType = type;
		}

		public static SyringePropertyInfo Create<T>() where T : SyringePropertyBase, new()
		{
			return new SyringePropertyInfo(new T().Name, typeof(T));
		}

		string IRegisterable.GetID()
		{
			return PropertyName;
		}
	}
}
namespace ExtraSyringeCustomization.Utils
{
	internal abstract class AppliedRoutine : DelayedRoutineBase
	{
		public double Time { get; }

		protected AppliedRoutine(double delay, double time)
			: base(delay)
		{
			Time = time;
		}

		public abstract void Apply();

		public abstract void Remove();

		protected override IEnumerator CreateRoutine()
		{
			float t = 0f;
			IEnumerator routine = CreateDelayRoutine(delegate(float newT)
			{
				t = newT;
			});
			while (routine.MoveNext())
			{
				yield return routine.Current;
			}
			Apply();
			for (; (double)t < Time; t += Time.deltaTime)
			{
				yield return null;
			}
			Remove();
		}
	}
	public abstract class CustomRoutine : IEnumerator
	{
		private readonly IEnumerator m_routine;

		object IEnumerator.Current => m_routine.Current;

		protected CustomRoutine()
		{
			m_routine = CreateRoutine();
		}

		protected abstract IEnumerator CreateRoutine();

		bool IEnumerator.MoveNext()
		{
			return m_routine.MoveNext();
		}

		void IEnumerator.Reset()
		{
			m_routine.Reset();
		}
	}
	public abstract class DelayedRoutine : DelayedRoutineBase
	{
		protected DelayedRoutine(double delay)
			: base(delay)
		{
		}

		public abstract void Activate();

		protected override IEnumerator CreateRoutine()
		{
			IEnumerator routine = CreateDelayRoutine();
			while (routine.MoveNext())
			{
				yield return routine.Current;
			}
			Activate();
		}
	}
	public abstract class DelayedRoutineBase : CustomRoutine
	{
		public double Delay { get; }

		protected DelayedRoutineBase(double delay)
		{
			Delay = delay;
		}

		protected IEnumerator CreateDelayRoutine()
		{
			return CreateDelayRoutine(null);
		}

		protected IEnumerator CreateDelayRoutine(Action<float>? timeRemainderCallback)
		{
			float t2;
			for (t2 = 0f; (double)t2 < Delay; t2 += Time.deltaTime)
			{
				yield return null;
			}
			t2 -= (float)Delay;
			timeRemainderCallback?.Invoke(t2);
		}
	}
	public class DelayValue : MinMaxValue
	{
		public bool HasDelay { get; set; }

		public override double GetValue(Random random)
		{
			if (!HasDelay)
			{
				return double.NaN;
			}
			return base.GetValue(random);
		}
	}
	public interface IMovementModifier
	{
		double WalkSpeedMultiplier { get; set; }

		double RunSpeedMultiplier { get; set; }

		double CrouchSpeedMultiplier { get; set; }

		double AirSpeedMultiplier { get; set; }

		void Remove();

		void SetAll(double value)
		{
			WalkSpeedMultiplier = value;
			RunSpeedMultiplier = value;
			CrouchSpeedMultiplier = value;
			AirSpeedMultiplier = value;
		}
	}
	internal static class L
	{
		private static ManualLogSource Source => MainPlugin.LogSource;

		public static void Msg(object data)
		{
			Source.LogMessage(data);
		}

		public static void Info(object data)
		{
			Source.LogInfo(data);
		}

		public static void Debug(object data)
		{
			Source.LogDebug(data);
		}

		public static void Warn(object data)
		{
			Source.LogWarning(data);
		}

		public static void Error(object data)
		{
			Source.LogError(data);
		}
	}
	public class MinMaxValue
	{
		public double Min { get; set; }

		public double Max { get; set; }

		public virtual double GetValue(double t)
		{
			if (t < 0.0)
			{
				t = 0.0;
			}
			else if (t > 1.0)
			{
				t = 1.0;
			}
			return GetValueUnclamped(t);
		}

		public virtual double GetValueUnclamped(double t)
		{
			double num = Min;
			if (num != Max)
			{
				num += (Max - Min) * t;
			}
			return num;
		}

		public virtual double GetValue(Random random)
		{
			double num = Min;
			if (num != Max)
			{
				num += random.NextDouble() * (Max - Min);
			}
			return num;
		}
	}
	public static class MovementMultiplierManager
	{
		private sealed class MovementModifier : IMovementModifier, IEquatable<MovementModifier>
		{
			private static ulong s_lastID;

			private double m_walkSpeed;

			private double m_runSpeed;

			private double m_crouchSpeed;

			private double m_airSpeed;

			private bool m_removed;

			public ulong ID { get; }

			public double WalkSpeedMultiplier
			{
				get
				{
					return m_walkSpeed;
				}
				set
				{
					if (!m_removed)
					{
						m_walkSpeed = value;
						OnModifierChange();
					}
				}
			}

			public double RunSpeedMultiplier
			{
				get
				{
					return m_runSpeed;
				}
				set
				{
					if (!m_removed)
					{
						m_runSpeed = value;
						OnModifierChange();
					}
				}
			}

			public double CrouchSpeedMultiplier
			{
				get
				{
					return m_crouchSpeed;
				}
				set
				{
					if (!m_removed)
					{
						m_crouchSpeed = value;
						OnModifierChange();
					}
				}
			}

			public double AirSpeedMultiplier
			{
				get
				{
					return m_airSpeed;
				}
				set
				{
					if (!m_removed)
					{
						m_airSpeed = value;
						OnModifierChange();
					}
				}
			}

			public MovementModifier()
			{
				m_walkSpeed = 1.0;
				m_runSpeed = 1.0;
				m_crouchSpeed = 1.0;
				m_airSpeed = 1.0;
				m_removed = false;
				ID = s_lastID;
				s_lastID++;
			}

			public void SetAll(double value)
			{
				m_walkSpeed = value;
				m_runSpeed = value;
				m_crouchSpeed = value;
				m_airSpeed = value;
				OnModifierChange();
			}

			public override int GetHashCode()
			{
				return ID.GetHashCode();
			}

			public override bool Equals(object? obj)
			{
				if (obj is MovementModifier other)
				{
					return Equals(other);
				}
				return false;
			}

			public bool Equals(MovementModifier? other)
			{
				if (other == null)
				{
					return false;
				}
				return other.ID == ID;
			}

			public void Remove()
			{
				if (!m_removed && s_modifiers.Remove(this))
				{
					OnModifierChange();
					m_removed = true;
				}
			}
		}

		public static List<Func<float>> s_onRefresh = new List<Func<float>>();

		private static readonly List<MovementModifier> s_modifiers = new List<MovementModifier>();

		private static float BaseWalkSpeed { get; set; } = 0f;


		private static float BaseRunSpeed { get; set; }

		private static float BaseCrouchSpeed { get; set; }

		private static float BaseAirSpeed { get; set; }

		private static void OnModifierChange()
		{
			PlayerDataBlock block = GameDataBlockBase<PlayerDataBlock>.GetBlock(1u);
			GetDefaultMovementSpeed();
			float num = 1f;
			foreach (Func<float> item in s_onRefresh)
			{
				num *= item();
			}
			double num2 = (double)BaseWalkSpeed * (double)num;
			double num3 = (double)BaseRunSpeed * (double)num;
			double num4 = (double)BaseCrouchSpeed * (double)num;
			double num5 = (double)BaseAirSpeed * (double)num;
			if (s_modifiers.Count > 0)
			{
				foreach (MovementModifier s_modifier in s_modifiers)
				{
					num2 *= s_modifier.WalkSpeedMultiplier;
					num3 *= s_modifier.RunSpeedMultiplier;
					num4 *= s_modifier.CrouchSpeedMultiplier;
					num5 *= s_modifier.AirSpeedMultiplier;
				}
			}
			block.walkMoveSpeed = (float)num2;
			block.runMoveSpeed = (float)num3;
			block.crouchMoveSpeed = (float)num4;
			block.airMoveSpeed = (float)num5;
		}

		public static (float walk, float run, float crouch, float air) GetDefaultMovementSpeed()
		{
			if (BaseWalkSpeed != 0f)
			{
				return (BaseWalkSpeed, BaseRunSpeed, BaseCrouchSpeed, BaseAirSpeed);
			}
			PlayerDataBlock block = GameDataBlockBase<PlayerDataBlock>.GetBlock(1u);
			BaseWalkSpeed = block.walkMoveSpeed;
			BaseRunSpeed = block.runMoveSpeed;
			BaseCrouchSpeed = block.crouchMoveSpeed;
			BaseAirSpeed = block.airMoveSpeed;
			return (BaseWalkSpeed, BaseRunSpeed, BaseCrouchSpeed, BaseAirSpeed);
		}

		public static IMovementModifier CreateModifier()
		{
			MovementModifier movementModifier = new MovementModifier();
			s_modifiers.Add(movementModifier);
			return movementModifier;
		}

		public static void AddRefreshCallback(Func<float> onRefresh)
		{
			s_onRefresh.Add(onRefresh);
		}

		public static void Refresh()
		{
			OnModifierChange();
		}
	}
	public class OverTimeIntervalValue : MinMaxValue
	{
		public bool RandomizeEveryInterval { get; set; }
	}
	public class OverTimeValue : MinMaxValue
	{
		public bool IsTimeBased { get; set; }
	}
	public static class PathUtil
	{
		public static string NiceRelativePath(string relativeTo, string path)
		{
			path = path.Replace(Path.DirectorySeparatorChar, '/');
			string text = Path.GetRelativePath(relativeTo.Replace('/', Path.DirectorySeparatorChar), path.EndsWith('/') ? path : (path + "/")).Replace(Path.DirectorySeparatorChar, '/');
			if (text == ".")
			{
				text = "";
			}
			text = "/" + text;
			if (text.EndsWith("/"))
			{
				text = text.Substring(0, text.Length - 1);
			}
			return text;
		}
	}
}
namespace ExtraSyringeCustomization.Registries
{
	public sealed class CustomSyringePropertyRegistry
	{
		internal sealed class RegistryImpl : Registry<SyringePropertyInfo>
		{
			protected override void OnRegisterFailed(SyringePropertyInfo entry, string failReason)
			{
				L.Warn("Failed to add syringe property with name '" + entry.PropertyName + "': " + failReason);
			}

			protected override void OnRegistered(SyringePropertyInfo entry)
			{
				L.Info("Added syringe property with name '" + entry.PropertyName + "'");
			}
		}

		private readonly RegistryImpl m_registry;

		internal RegistryImpl Registry => m_registry;

		public CustomSyringePropertyRegistry()
		{
			m_registry = new RegistryImpl();
		}

		public void Register<T>() where T : SyringePropertyBase, new()
		{
			((RegistryBase<SyringePropertyInfo>)(object)Registry).Register(SyringePropertyInfo.Create<T>());
		}

		public void UnRegister<T>() where T : SyringePropertyBase, new()
		{
			((RegistryBase<SyringePropertyInfo>)(object)Registry).UnRegister(SyringePropertyInfo.Create<T>());
		}
	}
	public sealed class CustomSyringeRegistry : Registry<CustomSyringeDefinition>
	{
		private const string SYRINGE_PREFIX = "SYRINGE_";

		public bool TryGetEntry(uint syringeID, [NotNullWhen(true)] out CustomSyringeDefinition? syringe)
		{
			return ((RegistryBase<CustomSyringeDefinition>)(object)this).TryGetEntry(SyringeIDToRegistryID(syringeID), ref syringe);
		}

		protected override void OnRegistered(CustomSyringeDefinition entry)
		{
			uint syringeID = entry.SyringeID;
			Dictionary<uint, ItemDataBlock> s_blockByID = GameDataBlockBase<ItemDataBlock>.s_blockByID;
			if (s_blockByID == null || !s_blockByID.ContainsKey(syringeID))
			{
				L.Debug($"Added syringe with id '{syringeID}'");
				L.Warn($"Custom Syringe Definition with ID '{syringeID}' does not currently have an item datablock entry");
			}
			else
			{
				L.Debug($"Added syringe with id '{syringeID}' (ItemDataBlock - {((GameDataBlockBase<ItemDataBlock>)(object)GameDataBlockBase<ItemDataBlock>.GetBlock(syringeID)).name})");
			}
		}

		public static string SyringeIDToRegistryID(uint id)
		{
			return "SYRINGE_" + id;
		}

		public static uint RegistryIDToSyringeID(string id)
		{
			if (id.StartsWith("SYRINGE_"))
			{
				return uint.Parse(id.Substring("SYRINGE_".Length));
			}
			throw new ArgumentException("Invalid Registry ID given", "id");
		}
	}
	public static class SyringeRegistryHandler
	{
		public static CustomSyringeRegistry Syringes { get; } = new CustomSyringeRegistry();


		public static CustomSyringePropertyRegistry Properties { get; } = new CustomSyringePropertyRegistry();

	}
}
namespace ExtraSyringeCustomization.Properties
{
	public class PropertySequenceTiming
	{
		public bool IsRelativeToPreviousProperties { get; set; }

		public DelayValue Delay { get; set; } = new DelayValue();

	}
	public abstract class SyringePropertyBase : IRegisterable
	{
		public bool Enabled { get; set; }

		public PropertySequenceTiming Timing { get; set; } = new PropertySequenceTiming();


		public abstract string Name { get; }

		public abstract IEnumerator Apply(PlayerAgent player, SyringeFirstPerson syringe, Random random, SyringePropertySequenceInfo sequenceInfo);

		string IRegisterable.GetID()
		{
			return Name;
		}

		public virtual Type? GetDataType()
		{
			return null;
		}

		public virtual SyringePropertyData? GetData()
		{
			return null;
		}

		public virtual void SetData(SyringePropertyData? data)
		{
		}
	}
	public abstract class SyringePropertyBase<T> : SyringePropertyBase where T : SyringePropertyData, new()
	{
		public T Data { get; set; } = new T();


		public override Type? GetDataType()
		{
			return typeof(T);
		}

		public sealed override SyringePropertyData? GetData()
		{
			return Data;
		}

		public sealed override void SetData(SyringePropertyData? data)
		{
			if (data is T data2)
			{
				Data = data2;
			}
			else
			{
				Data = new T();
			}
		}
	}
	public class SyringePropertyData
	{
	}
	[JsonConverter(typeof(SyringePropertyListConverter))]
	public sealed class SyringePropertyList
	{
		private readonly Dictionary<string, SyringePropertyBase> m_properties = new Dictionary<string, SyringePropertyBase>();

		public void AddProperty(SyringePropertyBase property)
		{
			if (property == null)
			{
				throw new ArgumentNullException("property");
			}
			string name = property.Name;
			if (m_properties.ContainsKey(name))
			{
				throw new ArgumentException("Syringe Property with name '" + name + "' already exists!", "property");
			}
			m_properties.Add(name, property);
		}

		public IEnumerator<SyringePropertyBase> GetAllProperties()
		{
			return m_properties.Values.GetEnumerator();
		}

		public SyringePropertyBase[] GetProperties()
		{
			return m_properties.Values.Where((SyringePropertyBase property) => property.Enabled).ToArray();
		}

		public IEnumerable<KeyValuePair<string, SyringePropertyBase>> GetEntries()
		{
			foreach (string key in m_properties.Keys)
			{
				yield return new KeyValuePair<string, SyringePropertyBase>(key, m_properties[key]);
			}
		}
	}
	public sealed class SyringePropertySequenceInfo
	{
		private bool m_forceComplete;

		public bool ForceComplete => m_forceComplete;

		public double DeltaTime
		{
			get
			{
				if (m_forceComplete)
				{
					return double.MaxValue;
				}
				return Time.deltaTime;
			}
		}

		public void ForceCompletion()
		{
			m_forceComplete = true;
		}
	}
}
namespace ExtraSyringeCustomization.Properties.BuiltIn
{
	public sealed class SyringeAmmoValueProperty : SyringePropertyMinMaxBase
	{
		public override string Name => "Ammo";

		public override IEnumerator Apply(PlayerAgent player, SyringeFirstPerson syringe, Random random, SyringePropertySequenceInfo info)
		{
			float num = (float)base.Data.GetValue(random);
			PlayerBackpackManager.GiveAmmoToPlayer(player.Owner, num, num, 0f);
			yield break;
		}
	}
	public sealed class SyringeDisinfectionValueProperty : SyringePropertyMinMaxBase
	{
		public override string Name => "Disinfection";

		public override IEnumerator Apply(PlayerAgent player, SyringeFirstPerson syringe, Random random, SyringePropertySequenceInfo info)
		{
			player.Damage.ModifyInfection(new pInfection
			{
				amount = 0f - (float)base.Data.GetValue(random),
				effect = (pInfectionEffect)1,
				mode = (pInfectionMode)1
			}, true, true);
			yield break;
		}
	}
	public sealed class SyringeDOTProperty : SyringePropertyBase<SyringeDOTProperty.JsonData>
	{
		public sealed class JsonData : SyringePropertyData
		{
			public MinMaxValue TotalTime { get; set; } = new MinMaxValue();


			public OverTimeIntervalValue DamageInterval { get; set; } = new OverTimeIntervalValue();


			public OverTimeValue Damage { get; set; } = new OverTimeValue();


			public bool ClearWhenDown { get; set; } = true;

		}

		public override string Name => "DamageOverTime";

		public override IEnumerator Apply(PlayerAgent player, SyringeFirstPerson syringe, Random random, SyringePropertySequenceInfo info)
		{
			double value = base.Data.TotalTime.GetValue(random);
			L.Debug($"Apply DOT! (Total Time: {value})");
			IEnumerator routine = InnerRoutine(player, random, value, info);
			while (!info.ForceComplete && routine.MoveNext())
			{
				yield return routine.Current;
			}
		}

		private IEnumerator InnerRoutine(PlayerAgent player, Random random, double totalTime, SyringePropertySequenceInfo info)
		{
			double t = 0.0;
			double interval = 0.0;
			double damageInterval = base.Data.DamageInterval.GetValue(random);
			while (t < totalTime)
			{
				while (interval < damageInterval && t < totalTime)
				{
					yield return null;
					interval += info.DeltaTime;
					t += info.DeltaTime;
				}
				interval -= damageInterval;
				if ((int)player.Locomotion.m_currentStateEnum == 7)
				{
					if (base.Data.ClearWhenDown)
					{
						break;
					}
					yield return null;
					continue;
				}
				double num = ((!base.Data.Damage.IsTimeBased) ? base.Data.Damage.GetValue(random) : base.Data.Damage.GetValue(t / totalTime));
				((Dam_SyncedDamageBase)player.Damage).NoAirDamage((float)num);
				if (base.Data.DamageInterval.RandomizeEveryInterval)
				{
					damageInterval = base.Data.DamageInterval.GetValue(random);
				}
			}
		}
	}
	public sealed class SyringeHealthValueProperty : SyringePropertyMinMaxBase
	{
		public override string Name => "Health";

		public override IEnumerator Apply(PlayerAgent player, SyringeFirstPerson syringe, Random random, SyringePropertySequenceInfo info)
		{
			PlayerBackpackManager.PickupHealthRel((float)base.Data.GetValue(random), player);
			yield break;
		}
	}
	public sealed class SyringeInfectionValueProperty : SyringePropertyMinMaxBase
	{
		public override string Name => "Infection";

		public override IEnumerator Apply(PlayerAgent player, SyringeFirstPerson syringe, Random random, SyringePropertySequenceInfo info)
		{
			player.Damage.ModifyInfection(new pInfection
			{
				amount = (float)base.Data.GetValue(random),
				effect = (pInfectionEffect)0,
				mode = (pInfectionMode)1
			}, true, true);
			yield break;
		}
	}
	public sealed class SyringeMeleeBuffTimeProperty : SyringePropertyMinMaxBase
	{
		public override string Name => "MeleeBuffTime";

		public override IEnumerator Apply(PlayerAgent player, SyringeFirstPerson syringe, Random random, SyringePropertySequenceInfo info)
		{
			double value = base.Data.GetValue(random);
			float num = (float)(Clock.TimeDouble + value);
			L.Debug($"Apply MeleeBuffTime! (Time: {value}, Game End Time: {num})");
			player.MeleeBuffTimer = num;
			yield break;
		}
	}
	public sealed class SyringeMovementMultiplierProperty : SyringePropertyMinMaxBase<SyringeMovementMultiplierProperty.JsonData>
	{
		public sealed class JsonData : BasicMinMaxData
		{
			public MinMaxValue Time { get; set; } = new MinMaxValue();


			public bool ClearWhenDown { get; set; } = true;

		}

		public override string Name => "MovementMultiplier";

		public override IEnumerator Apply(PlayerAgent player, SyringeFirstPerson syringe, Random random, SyringePropertySequenceInfo info)
		{
			double value = base.Data.Time.GetValue(random);
			double value2 = base.Data.GetValue(random);
			L.Debug($"Apply Movement Multiplier! (time: {value}, value: {value2})");
			IMovementModifier modifier = MovementMultiplierManager.CreateModifier();
			modifier.SetAll(value2);
			IEnumerator routine = InnerRoutine(value, info);
			while (!info.ForceComplete && routine.MoveNext() && (!base.Data.ClearWhenDown || (int)player.Locomotion.m_currentStateEnum != 7))
			{
				yield return routine.Current;
			}
			modifier.Remove();
		}

		private static IEnumerator InnerRoutine(double time, SyringePropertySequenceInfo info)
		{
			for (double t = 0.0; t < time; t += info.DeltaTime)
			{
				yield return null;
			}
		}
	}
	public class BasicMinMaxData : SyringePropertyData
	{
		public double Min { get; set; }

		public double Max { get; set; }

		public double GetValue(Random random)
		{
			double num = Min;
			if (num != Max)
			{
				num += random.NextDouble() * (Max - Min);
			}
			return num;
		}
	}
	public abstract class SyringePropertyMinMaxBase<TData> : SyringePropertyBase<TData> where TData : BasicMinMaxData, new()
	{
	}
	public abstract class SyringePropertyMinMaxBase : SyringePropertyMinMaxBase<BasicMinMaxData>
	{
	}
	public sealed class SyringeROTProperty : SyringePropertyBase<SyringeROTProperty.JsonData>
	{
		public sealed class JsonData : SyringePropertyData
		{
			public MinMaxValue TotalTime { get; set; } = new MinMaxValue();


			public OverTimeIntervalValue RegenInterval { get; set; } = new OverTimeIntervalValue();


			public OverTimeValue Health { get; set; } = new OverTimeValue();


			public bool ClearWhenDown { get; set; } = true;

		}

		public override string Name => "RegenOverTime";

		public override IEnumerator Apply(PlayerAgent player, SyringeFirstPerson syringe, Random random, SyringePropertySequenceInfo info)
		{
			double value = base.Data.TotalTime.GetValue(random);
			L.Debug($"Apply ROT! (Total Time: {value})");
			IEnumerator routine = InnerRoutine(player, random, value, info);
			while (!info.ForceComplete && routine.MoveNext())
			{
				yield return routine.Current;
			}
		}

		private IEnumerator InnerRoutine(PlayerAgent player, Random random, double totalTime, SyringePropertySequenceInfo info)
		{
			double t = 0.0;
			double interval = 0.0;
			double regenInterval = base.Data.RegenInterval.GetValue(random);
			while (t < totalTime)
			{
				while (interval < regenInterval && t < totalTime)
				{
					yield return null;
					interval += info.DeltaTime;
					t += info.DeltaTime;
				}
				interval -= regenInterval;
				if ((int)player.Locomotion.m_currentStateEnum == 7)
				{
					if (base.Data.ClearWhenDown)
					{
						break;
					}
					yield return null;
					continue;
				}
				double num = ((!base.Data.Health.IsTimeBased) ? base.Data.Health.GetValue(random) : base.Data.Health.GetValue(t / totalTime));
				double num2 = num / (double)player.PlayerData.health;
				player.GiveHealth(player, (float)num2);
				if (base.Data.RegenInterval.RandomizeEveryInterval)
				{
					regenInterval = base.Data.RegenInterval.GetValue(random);
				}
			}
		}
	}
	public sealed class SyringeSelfDestructProperty : SyringePropertyBase<SyringeSelfDestructProperty.JsonData>
	{
		public sealed class JsonData : SyringePropertyData
		{
			public double Damage { get; set; }

			public double Radius { get; set; }
		}

		private static FX_Pool s_detonateFX;

		public override string Name => "SelfDestruct";

		private static FX_Pool DetonateFX
		{
			get
			{
				if ((Object)(object)s_detonateFX == (Object)null)
				{
					s_detonateFX = FX_Manager.GetEffectPool(AssetShardManager.GetLoadedAsset<GameObject>("Assets/AssetPrefabs/FX_Effects/FX_Tripmine.prefab", false));
				}
				return s_detonateFX;
			}
		}

		public override IEnumerator Apply(PlayerAgent player, SyringeFirstPerson syringe, Random random, SyringePropertySequenceInfo info)
		{
			L.Debug("Self Destruct : Activate!");
			DamageUtil.DoExplosionDamage(player.FPSCamera.Position, (float)base.Data.Radius, (float)base.Data.Damage, LayerManager.MASK_EXPLOSION_TARGETS, LayerManager.MASK_EXPLOSION_BLOCKERS, false, 0f);
			DoExplodeEffect(player);
			((Dam_SyncedDamageBase)player.Damage).ExplosionDamage((float)base.Data.Damage, ((Agent)player).Position, Vector3.up, 0u);
			NetworkAPI.InvokeEvent<SelfDestruct>("Syringe-Self-Destruct", new SelfDestruct
			{
				playerID = player.Owner.Lookup
			}, (SNet_ChannelType)2);
			yield break;
		}

		public static void DoExplodeEffect(PlayerAgent player)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			player.Sound.Post(EVENTS.STICKYMINEEXPLODE, true);
			((FX_EffectBase)DetonateFX.AquireEffect()).Play((FX_Trigger)null, ((Component)player).transform.position, Quaternion.LookRotation(Vector3.up));
		}
	}
	public sealed class SyringeToolValueProperty : SyringePropertyMinMaxBase
	{
		public override string Name => "Tool";

		public override IEnumerator Apply(PlayerAgent player, SyringeFirstPerson syringe, Random random, SyringePropertySequenceInfo info)
		{
			float num = (float)base.Data.GetValue(random);
			PlayerBackpackManager.GiveAmmoToPlayer(player.Owner, 0f, 0f, num);
			yield break;
		}
	}
}
namespace ExtraSyringeCustomization.Patches
{
	[HarmonyPatch]
	internal sealed class GameDataInitPatch
	{
		private class RawJSONFile
		{
			public List<CustomSyringeDefinition> Syringes { get; set; }

			public RawJSONFile()
			{
				Syringes = new List<CustomSyringeDefinition>();
			}
		}

		[HarmonyPatch(typeof(GameDataInit), "Initialize")]
		[HarmonyPostfix]
		[HarmonyWrapSafe]
		public static void InitializePatch()
		{
			string text = ConfigManager.CustomPath;
			if (text == null)
			{
				text = Path.Combine(ConfigManager.GameDataPath, "Custom");
			}
			if (!Directory.Exists(text))
			{
				Directory.CreateDirectory(text);
			}
			string text2 = Path.Combine(ConfigManager.CustomPath, "ExtraSyringeCustomization");
			if (!Directory.Exists(text2))
			{
				Directory.CreateDirectory(text2);
			}
			string text3 = Path.Combine(text2, "syringes.json");
			string path = Path.Combine(text2, "GENERATED_syringe-properties.json");
			string path2 = Path.Combine(text2, "GENERATED_example-syringes.json");
			ExtraSyringeHandler.ConfigPath = text2;
			IEnumerable<SyringePropertyInfo> entries = ((RegistryBase<SyringePropertyInfo>)(object)SyringeRegistryHandler.Properties.Registry).GetEntries();
			if (!File.Exists(path))
			{
				SyringePropertyList syringePropertyList = new SyringePropertyList();
				foreach (SyringePropertyInfo item in entries)
				{
					syringePropertyList.AddProperty((SyringePropertyBase)Activator.CreateInstance(item.PropertyType));
				}
				L.Info("Generating List of all Syringe Properties at '" + PathUtil.NiceRelativePath(ConfigManager.GameDataPath, path) + "'");
				File.WriteAllText(path, JsonSerializer.Serialize(syringePropertyList, new JsonSerializerOptions
				{
					WriteIndented = true
				}));
			}
			else
			{
				L.Debug("Skipping Syringe Property List file generation. (Delete '" + PathUtil.NiceRelativePath(ConfigManager.GameDataPath, path) + "' to regenerate it)");
			}
			if (!File.Exists(path2))
			{
				RawJSONFile rawJSONFile = new RawJSONFile();
				CustomSyringeDefinition customSyringeDefinition = new CustomSyringeDefinition();
				customSyringeDefinition.SyringeID = 999u;
				rawJSONFile.Syringes.Add(customSyringeDefinition);
				foreach (SyringePropertyInfo item2 in entries)
				{
					customSyringeDefinition.Properties.AddProperty((SyringePropertyBase)Activator.CreateInstance(item2.PropertyType));
				}
				L.Info("Generating Syringe Example File at '" + PathUtil.NiceRelativePath(ConfigManager.GameDataPath, path2) + "'");
				File.WriteAllText(path2, JsonSerializer.Serialize(rawJSONFile, new JsonSerializerOptions
				{
					WriteIndented = true
				}));
			}
			else
			{
				L.Debug("Skipping Syringe Example file generation. (Delete '" + PathUtil.NiceRelativePath(ConfigManager.GameDataPath, path2) + "' to regenerate it)");
			}
			L.Debug("Custom Syringes Path: " + text3);
			if (!File.Exists(text3))
			{
				L.Warn("No syringe definition file found. (Create one at '" + PathUtil.NiceRelativePath(ConfigManager.GameDataPath, text3) + "')");
				return;
			}
			try
			{
				RawJSONFile rawJSONFile2 = JsonSerializer.Deserialize<RawJSONFile>(File.ReadAllText(text3), new JsonSerializerOptions
				{
					AllowTrailingCommas = true
				});
				if (rawJSONFile2 == null)
				{
					L.Warn("Syringe File was empty! Rewriting to basic blank json!");
					rawJSONFile2 = new RawJSONFile();
					File.WriteAllText(text3, JsonSerializer.Serialize(rawJSONFile2, new JsonSerializerOptions
					{
						WriteIndented = true
					}));
					return;
				}
				if (rawJSONFile2.Syringes == null)
				{
					L.Warn("No 'Syringes' property was defined in JSON! Rewriting json to include it!");
					rawJSONFile2.Syringes = new List<CustomSyringeDefinition>();
					File.WriteAllText(text3, JsonSerializer.Serialize(rawJSONFile2, new JsonSerializerOptions
					{
						WriteIndented = true
					}));
					return;
				}
				foreach (CustomSyringeDefinition syringe in rawJSONFile2.Syringes)
				{
					if (syringe != null)
					{
						try
						{
							((RegistryBase<CustomSyringeDefinition>)(object)SyringeRegistryHandler.Syringes).Register(syringe);
						}
						catch (Exception value)
						{
							L.Error($"Error whilst adding syringe with id {syringe.SyringeID}: {value}");
						}
					}
				}
			}
			catch (Exception value2)
			{
				L.Error($"Error whilst reading syringes file: {value2}");
			}
		}
	}
	[HarmonyPatch]
	internal static class SyringePatches
	{
		[HarmonyPatch(typeof(_ApplySyringe_d__18), "MoveNext")]
		[HarmonyPrefix]
		[HarmonyWrapSafe]
		public static bool MoveNextPatch(_ApplySyringe_d__18 __instance, ref bool __result)
		{
			return ExtraSyringeHandler.InvokeMoveNextOnApplySyringe(__instance, ref __result);
		}

		[HarmonyPatch(typeof(GS_Lobby), "Enter")]
		[HarmonyPostfix]
		[HarmonyWrapSafe]
		public static void EnterLobbyPatch()
		{
			ExtraSyringeHandler.ClearSyringeList();
		}
	}
}
namespace ExtraSyringeCustomization.Packets
{
	public struct SelfDestruct
	{
		public ulong playerID;
	}
	public struct Vec3
	{
		public float x;

		public float y;

		public float z;

		public Vec3(Vector3 vec)
			: this(vec.x, vec.y, vec.z)
		{
		}//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)


		public Vec3(float x, float y, float z)
		{
			this.x = x;
			this.y = y;
			this.z = z;
		}

		public static implicit operator Vec3(Vector3 vec)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new Vec3(vec);
		}

		public static implicit operator Vector3(Vec3 vec)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(vec.x, vec.y, vec.z);
		}
	}
}
namespace ExtraSyringeCustomization.Converters
{
	public sealed class SyringePropertyConverter : JsonConverter<SyringePropertyBase>
	{
		private sealed class V1DataFormat
		{
			public DelayValue? Delay { get; set; }

			public bool Enabled { get; set; }
		}

		private readonly string? m_fixedID;

		private readonly bool m_asMap;

		public SyringePropertyConverter()
		{
			m_fixedID = null;
			m_asMap = false;
		}

		public SyringePropertyConverter(string id)
		{
			m_fixedID = id;
			m_asMap = true;
		}

		public override bool CanConvert(Type typeToConvert)
		{
			return typeof(SyringePropertyBase).IsAssignableFrom(typeToConvert);
		}

		private static string ReadPropertyName(ref Utf8JsonReader reader, JsonSerializerOptions options)
		{
			string text = reader.GetString();
			if (options.PropertyNameCaseInsensitive)
			{
				text = text.ToLower();
			}
			return text;
		}

		private static string GetPropertyName(string baseName, JsonSerializerOptions options)
		{
			string text = options.PropertyNamingPolicy?.ConvertName(baseName) ?? baseName;
			if (options.PropertyNameCaseInsensitive)
			{
				text = text.ToLower();
			}
			return text;
		}

		private void ParseAsV1MapData(ref Utf8JsonReader reader, SyringePropertyBase result, JsonSerializerOptions options)
		{
			Utf8JsonReader reader2 = reader;
			V1DataFormat v1DataFormat = JsonSerializer.Deserialize<V1DataFormat>(ref reader2, options);
			result.Enabled = v1DataFormat?.Enabled ?? true;
			if (v1DataFormat?.Delay != null)
			{
				result.Timing.Delay = v1DataFormat.Delay;
			}
			Type dataType = result.GetDataType();
			if (dataType != null)
			{
				SyringePropertyData syringePropertyData = (SyringePropertyData)JsonSerializer.Deserialize(ref reader, dataType, options);
				if (syringePropertyData == null)
				{
					syringePropertyData = (SyringePropertyData)Activator.CreateInstance(dataType);
				}
				result.SetData(syringePropertyData);
			}
			else
			{
				reader = reader2;
			}
		}

		public override SyringePropertyBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
		{
			Utf8JsonReader reader2 = reader;
			SyringePropertyBase syringePropertyBase = null;
			if (reader.TokenType == JsonTokenType.Null)
			{
				return syringePropertyBase;
			}
			if (reader.TokenType != JsonTokenType.StartObject)
			{
				throw new JsonException($"Unexpected token '{reader.TokenType}' (expected {1})");
			}
			string text = m_fixedID;
			if (!string.IsNullOrEmpty(text))
			{
				SyringePropertyInfo syringePropertyInfo = default(SyringePropertyInfo);
				if (!((RegistryBase<SyringePropertyInfo>)(object)SyringeRegistryHandler.Properties.Registry).TryGetEntry(text, ref syringePropertyInfo))
				{
					throw new JsonException("Failed to resolve custom syringe property with id '" + text + "'");
				}
				syringePropertyBase = (SyringePropertyBase)Activator.CreateInstance(syringePropertyInfo.PropertyType);
			}
			string propertyName = GetPropertyName("ID", options);
			bool? flag = null;
			string propertyName2 = GetPropertyName("Enabled", options);
			PropertySequenceTiming propertySequenceTiming = null;
			string propertyName3 = GetPropertyName("Timing", options);
			SyringePropertyData syringePropertyData = null;
			string propertyName4 = GetPropertyName("Data", options);
			bool flag2 = false;
			SyringePropertyInfo syringePropertyInfo2 = default(SyringePropertyInfo);
			while (reader.Read())
			{
				if (reader.TokenType == JsonTokenType.EndObject)
				{
					if (text == null || syringePropertyBase == null)
					{
						throw new JsonException("An ID field is required!");
					}
					if (!flag.HasValue)
					{
						flag = true;
					}
					if (flag2 && syringePropertyData == null)
					{
						throw new JsonException("A Data field is required!");
					}
					syringePropertyBase.Enabled = flag.Value;
					if (flag2)
					{
						syringePropertyBase.SetData(syringePropertyData);
					}
					return syringePropertyBase;
				}
				if (reader.TokenType != JsonTokenType.PropertyName)
				{
					throw new JsonException($"Unexpected token '{reader.TokenType}' (expected {5} or {2})");
				}
				string text2 = ReadPropertyName(ref reader, options);
				if (!reader.Read())
				{
					break;
				}
				if (!m_asMap && text2 == propertyName)
				{
					if (text != null)
					{
						throw new JsonException("An ID field is already specified!");
					}
					text = reader.GetString();
					if (text == null)
					{
						throw new JsonException("Syringe Property ID cannot be null");
					}
					if (!((RegistryBase<SyringePropertyInfo>)(object)SyringeRegistryHandler.Properties.Registry).TryGetEntry(text, ref syringePropertyInfo2))
					{
						throw new JsonException("Failed to resolve custom syringe property with id '" + text + "'");
					}
					syringePropertyBase = (SyringePropertyBase)Activator.CreateInstance(syringePropertyInfo2.PropertyType);
					flag2 = syringePropertyBase.GetDataType() != null;
				}
				else if (text2 == propertyName2)
				{
					if (flag.HasValue)
					{
						throw new JsonException("An Enabled field is already specified!");
					}
					flag = reader.GetBoolean();
				}
				else if (text2 == propertyName3)
				{
					if (propertySequenceTiming != null)
					{
						throw new JsonException("A Timing field is already specified!");
					}
					propertySequenceTiming = JsonSerializer.Deserialize<PropertySequenceTiming>(ref reader, options);
					if (propertySequenceTiming == null)
					{
						propertySequenceTiming = new PropertySequenceTiming();
					}
				}
				else if (text2 == propertyName4)
				{
					if (syringePropertyData != null)
					{
						throw new JsonException("A Data field is already specified!");
					}
					if (text == null)
					{
						throw new JsonException("An ID field must come before a Data field!");
					}
					if (!flag2)
					{
						throw new JsonException("A Data field is not allowed for syringe property '" + text + "'");
					}
					Type dataType = syringePropertyBase.GetDataType();
					syringePropertyData = (SyringePropertyData)JsonSerializer.Deserialize(ref reader, dataType, options);
					if (syringePropertyData == null)
					{
						syringePropertyData = (SyringePropertyData)Activator.CreateInstance(dataType);
					}
				}
				else
				{
					if (m_asMap)
					{
						ParseAsV1MapData(ref reader2, syringePropertyBase, options);
						reader = reader2;
						return syringePropertyBase;
					}
					L.Warn("Unsupport field for json syringe property '" + text2 + "'");
					reader.Skip();
				}
			}
			throw new JsonException("Unexpected end of input.");
		}

		public override void Write(Utf8JsonWriter writer, SyringePropertyBase value, JsonSerializerOptions options)
		{
			writer.WriteStartObject();
			if (!m_asMap)
			{
				writer.WriteString("ID", m_fixedID);
			}
			writer.WritePropertyName("Timing");
			JsonSerializer.Serialize(writer, value.Timing ?? new PropertySequenceTiming(), options);
			writer.WriteBoolean("Enabled", value.Enabled);
			Type dataType = value.GetDataType();
			if (dataType != null)
			{
				writer.WritePropertyName("Data");
				JsonSerializer.Serialize(writer, value.GetData(), dataType, options);
			}
			writer.WriteEndObject();
		}
	}
	public class SyringePropertyListConverter : JsonConverter<SyringePropertyList>
	{
		private readonly bool m_writeAsMap;

		public SyringePropertyListConverter()
			: this(writeAsMap: false)
		{
		}

		public SyringePropertyListConverter(bool writeAsMap)
		{
			m_writeAsMap = writeAsMap;
		}

		private static SyringePropertyList ReadAsArray(ref Utf8JsonReader reader, JsonSerializerOptions options)
		{
			SyringePropertyList syringePropertyList = new SyringePropertyList();
			new JsonSerializerOptions(options).Converters.Add(new SyringePropertyConverter());
			while (reader.Read())
			{
				if (reader.TokenType == JsonTokenType.EndArray)
				{
					return syringePropertyList;
				}
				if (reader.TokenType != JsonTokenType.StartObject)
				{
					throw new JsonException($"Unexpected token '{reader.TokenType}' (expected {1} or {4})");
				}
				SyringePropertyBase syringePropertyBase = JsonSerializer.Deserialize<SyringePropertyBase>(ref reader, options);
				if (syringePropertyBase != null)
				{
					syringePropertyList.AddProperty(syringePropertyBase);
				}
			}
			throw new JsonException("Unexpected end of input.");
		}

		private static SyringePropertyList ReadAsMap(ref Utf8JsonReader reader, JsonSerializerOptions options)
		{
			SyringePropertyList syringePropertyList = new SyringePropertyList();
			SyringePropertyInfo syringePropertyInfo = default(SyringePropertyInfo);
			while (reader.Read())
			{
				if (reader.TokenType == JsonTokenType.EndObject)
				{
					return syringePropertyList;
				}
				if (reader.TokenType != JsonTokenType.PropertyName)
				{
					throw new JsonException($"Unexpected token '{reader.TokenType}' (expected {5})");
				}
				string @string = reader.GetString();
				if (@string == null || !((RegistryBase<SyringePropertyInfo>)(object)SyringeRegistryHandler.Properties.Registry).TryGetEntry(@string, ref syringePropertyInfo))
				{
					throw new JsonException("Failed to resolve custom syringe property with id '" + @string + "'");
				}
				JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions(options);
				jsonSerializerOptions.Converters.Add(new SyringePropertyConverter(@string));
				SyringePropertyBase syringePropertyBase = (SyringePropertyBase)JsonSerializer.Deserialize(ref reader, syringePropertyInfo.PropertyType, jsonSerializerOptions);
				if (syringePropertyBase == null)
				{
					syringePropertyBase = (SyringePropertyBase)Activator.CreateInstance(syringePropertyInfo.PropertyType);
					syringePropertyBase.Enabled = false;
				}
				syringePropertyList.AddProperty(syringePropertyBase);
			}
			throw new JsonException("Unexpected end of input.");
		}

		public override SyringePropertyList Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
		{
			if (reader.TokenType == JsonTokenType.Null)
			{
				return new SyringePropertyList();
			}
			if (reader.TokenType == JsonTokenType.StartObject)
			{
				return ReadAsMap(ref reader, options);
			}
			if (reader.TokenType == JsonTokenType.StartArray)
			{
				return ReadAsArray(ref reader, options);
			}
			throw new JsonException($"Unexpected token '{reader.TokenType}' (expected {1} or {3})");
		}

		public override void Write(Utf8JsonWriter writer, SyringePropertyList value, JsonSerializerOptions options)
		{
			if (m_writeAsMap)
			{
				WriteAsMap(writer, value, options);
			}
			else
			{
				WriteAsArray(writer, value, options);
			}
		}

		private static void WriteAsArray(Utf8JsonWriter writer, SyringePropertyList value, JsonSerializerOptions options)
		{
			writer.WriteStartArray();
			JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions(options);
			jsonSerializerOptions.Converters.Add(new SyringePropertyConverter());
			SyringePropertyBase[] properties = value.GetProperties();
			foreach (SyringePropertyBase value2 in properties)
			{
				JsonSerializer.Serialize(writer, value2, jsonSerializerOptions);
			}
			writer.WriteEndArray();
		}

		private static void WriteAsMap(Utf8JsonWriter writer, SyringePropertyList value, JsonSerializerOptions options)
		{
			writer.WriteStartObject();
			foreach (KeyValuePair<string, SyringePropertyBase> entry in value.GetEntries())
			{
				writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(entry.Key) ?? entry.Key);
				JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions(options);
				jsonSerializerOptions.Converters.Add(new SyringePropertyConverter(entry.Key));
				JsonSerializer.Serialize(writer, entry.Value, entry.Value.GetType(), jsonSerializerOptions);
			}
			writer.WriteEndObject();
		}
	}
}

plugins/Flaff.Collections.Registries.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Text.Json.Serialization;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("Flaff.Collections.Registries")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Flaff.Collections.Registries")]
[assembly: AssemblyTitle("Flaff.Collections.Registries")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace Flaff.Collections.Registries
{
	public class FailedToRegisterException : Exception
	{
		public IRegistry? Registry { get; }

		public IRegisterable? Item { get; }

		protected FailedToRegisterException(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
		}

		public FailedToRegisterException()
		{
		}

		public FailedToRegisterException(IRegistry registry)
		{
			Registry = registry;
		}

		public FailedToRegisterException(IRegisterable item)
		{
			Item = item;
		}

		public FailedToRegisterException(IRegistry registry, IRegisterable item)
		{
			Registry = registry;
			Item = item;
		}

		public FailedToRegisterException(string message)
			: base(message)
		{
		}

		public FailedToRegisterException(IRegistry registry, string message)
			: base(message)
		{
			Registry = registry;
		}

		public FailedToRegisterException(IRegisterable item, string message)
			: base(message)
		{
			Item = item;
		}

		public FailedToRegisterException(IRegistry registry, IRegisterable item, string message)
			: base(message)
		{
			Registry = registry;
			Item = item;
		}

		public FailedToRegisterException(string message, Exception innerException)
			: base(message, innerException)
		{
		}

		public FailedToRegisterException(IRegisterable item, string message, Exception innerException)
			: base(message, innerException)
		{
			Item = item;
		}

		public FailedToRegisterException(IRegistry registry, string message, Exception innerException)
			: base(message, innerException)
		{
			Registry = registry;
		}

		public FailedToRegisterException(IRegistry registry, IRegisterable item, string message, Exception innerException)
			: base(message, innerException)
		{
			Registry = registry;
			Item = item;
		}
	}
	public interface IRegisterable
	{
		string GetID();
	}
	public interface IRegisterListener : IRegisterable
	{
		void OnRegister();

		void OnRegisterFail(string failReason);
	}
	public interface IRegistry : ICollection, IEnumerable
	{
		IRegisterable this[string id] { get; }

		bool ContainsEntry(string id);

		bool ContainsEntry(IRegisterable entry);

		IEnumerable<IRegisterable> GetEntries();

		IEnumerable<string> GetAllIDs();

		bool TryGetEntry(string id, [NotNullWhen(true)] out IRegisterable? entry);

		void Register(IRegisterable entry);

		bool UnRegister(string id);

		bool UnRegister(IRegisterable entry);

		void UnRegisterAll();
	}
	public interface IRegistry<T> : IRegistry, ICollection, IEnumerable, ICollection<T>, IEnumerable<T> where T : IRegisterable
	{
		new T this[string id] { get; }

		bool ContainsEntry(T entry);

		bool UnRegister(T entry);

		new IEnumerable<T> GetEntries();

		void Register(T entry);

		bool TryGetEntry(string id, [NotNullWhen(true)] out T? entry);
	}
	public interface IRegistryList<TElement> : IList<TElement>, ICollection<TElement>, IEnumerable<TElement>, IEnumerable where TElement : IRegisterable
	{
		TElement this[string id] { get; set; }
	}
	public static class IRegistryListExtensions
	{
		public static bool ContainsID<T>(this IRegistryList<T> list, string id) where T : IRegisterable
		{
			if (id == null)
			{
				throw new ArgumentNullException("id");
			}
			return list.IndexOfID(id) != -1;
		}

		public static int IndexOfID<T>(this IRegistryList<T> list, string id) where T : IRegisterable
		{
			if (id == null)
			{
				throw new ArgumentNullException("id");
			}
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].GetID() == id)
				{
					return i;
				}
			}
			return -1;
		}

		public static bool RemoveID<T>(this IRegistryList<T> list, string id) where T : IRegisterable
		{
			if (id == null)
			{
				throw new ArgumentNullException("id");
			}
			int num = list.IndexOfID(id);
			if (num == -1)
			{
				return false;
			}
			list.RemoveAt(num);
			return true;
		}

		public static bool TryGetValue<T>(this IRegistryList<T> list, string id, [NotNullWhen(true)] out T? value) where T : IRegisterable
		{
			int num = list.IndexOfID(id);
			if (num > -1)
			{
				value = list[num];
				return true;
			}
			value = default(T);
			return false;
		}

		public static string[] GetIDS<T>(this IRegistryList<T> list) where T : IRegisterable
		{
			List<string> list2 = new List<string>();
			foreach (T item in list)
			{
				string iD = item.GetID();
				if (!list2.Contains(iD))
				{
					list2.Add(iD);
				}
			}
			return list2.ToArray();
		}

		public static T[] GetValues<T>(this IRegistryList<T> list, string id) where T : IRegisterable
		{
			if (id == null)
			{
				return Array.Empty<T>();
			}
			List<T> list2 = new List<T>();
			foreach (T item in list)
			{
				if (item.GetID() == id)
				{
					list2.Add(item);
				}
			}
			return list2.ToArray();
		}
	}
	public interface IUnregisterListener : IRegisterable
	{
		void OnUnregister();
	}
	internal static class L
	{
		private static ManualLogSource Source { get; }

		public static void Debug(object data)
		{
			Source.LogDebug(data);
		}

		static L()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			Source = new ManualLogSource("RegistryUtil");
			Logger.Sources.Add((ILogSource)(object)Source);
		}
	}
	public class Registry<T> : RegistryBase<T> where T : IRegisterable
	{
		private readonly Dictionary<string, T> m_entries;

		public override int Count => m_entries.Count;

		public Registry()
		{
			m_entries = new Dictionary<string, T>();
		}

		public override IEnumerable<T> GetEntries()
		{
			return m_entries.Values;
		}

		public override IEnumerable<string> GetAllIDs()
		{
			return m_entries.Keys;
		}

		public override bool TryGetEntry(string id, [NotNullWhen(true)] out T? entry)
		{
			return m_entries.TryGetValue(id, out entry);
		}

		protected sealed override void AddEntry(T entry)
		{
			m_entries.Add(entry.GetID(), entry);
		}

		protected sealed override void RemoveEntry(string id)
		{
			m_entries.Remove(id);
		}

		protected override bool ContainsEntryWithID(string id)
		{
			return m_entries.ContainsKey(id);
		}
	}
	public abstract class RegistryBase<T> : IRegistry<T>, IRegistry, ICollection, IEnumerable, ICollection<T>, IEnumerable<T> where T : IRegisterable
	{
		protected virtual string DebugName => GetType().Name;

		public abstract int Count { get; }

		public virtual bool IsReadOnly => false;

		public virtual T this[string id]
		{
			get
			{
				if (id == null)
				{
					throw new ArgumentNullException("id");
				}
				if (TryGetEntry(id, out T entry))
				{
					return entry;
				}
				throw new KeyNotFoundException("There is no entry with id '" + id + "'");
			}
		}

		IRegisterable IRegistry.this[string id] => this[id];

		bool ICollection.IsSynchronized => false;

		object ICollection.SyncRoot => this;

		public abstract IEnumerable<T> GetEntries();

		public abstract IEnumerable<string> GetAllIDs();

		protected virtual bool CanRegister(T entry, [NotNullWhen(false)] out string? failReason)
		{
			if (IsReadOnly)
			{
				failReason = "Registry is Read Only";
				return false;
			}
			string iD = entry.GetID();
			if (iD == null)
			{
				failReason = "Entry ID is null.";
				return false;
			}
			if (ContainsEntry(iD))
			{
				failReason = "Entry with ID '" + iD + "' already exists";
				return false;
			}
			failReason = null;
			return true;
		}

		protected virtual void OnRegistered(T entry)
		{
		}

		protected virtual void OnUnRegistered(T entry)
		{
		}

		protected virtual void OnRegisterFailed(T entry, string failReason)
		{
		}

		public abstract bool TryGetEntry(string id, [NotNullWhen(true)] out T? entry);

		protected abstract bool ContainsEntryWithID(string id);

		protected abstract void AddEntry(T entry);

		protected virtual void RemoveEntry(T entry)
		{
			RemoveEntry(entry.GetID());
		}

		protected abstract void RemoveEntry(string id);

		public void Register(T entry)
		{
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			FailedToRegisterException ex = null;
			string failReason;
			try
			{
				if (!CanRegister(entry, out failReason))
				{
					ex = new FailedToRegisterException(this, entry, failReason);
				}
			}
			catch (Exception innerException)
			{
				failReason = "Failed verifying ability to register item";
				ex = new FailedToRegisterException(this, entry, failReason, innerException);
			}
			if (failReason != null)
			{
				InvokeOnRegisterFailed(entry, failReason);
				throw ex;
			}
			AddEntry(entry);
			InvokeOnRegistered(entry);
		}

		public bool UnRegister(T entry)
		{
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			return UnRegister(entry.GetID());
		}

		public bool UnRegister(string id)
		{
			if (id == null)
			{
				throw new ArgumentNullException("id");
			}
			if (!ContainsEntry(id))
			{
				return false;
			}
			T entry = this[id];
			RemoveEntry(id);
			InvokeOnUnRegistered(entry);
			return true;
		}

		public void UnRegisterAll()
		{
			string[] array = GetAllIDs().ToArray();
			List<Exception> list = new List<Exception>();
			string[] array2 = array;
			foreach (string id in array2)
			{
				try
				{
					UnRegister(id);
				}
				catch (Exception item)
				{
					if (ContainsEntryWithID(id))
					{
						RemoveEntry(id);
					}
					list.Add(item);
				}
			}
			if (list.Count == 0)
			{
				return;
			}
			if (list.Count == 1)
			{
				throw new Exception("Failed to unregister an entry", list[0]);
			}
			throw new AggregateException("Failed to unregister multiple entries", list.ToArray());
		}

		public bool ContainsEntry(string id)
		{
			if (id != null)
			{
				return ContainsEntryWithID(id);
			}
			return true;
		}

		public bool ContainsEntry(T entry)
		{
			if (entry != null && TryGetEntry(entry.GetID(), out T entry2))
			{
				return entry2.Equals(entry);
			}
			return false;
		}

		public void CopyTo(T[] array, int index)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}
			if (index + Count >= array.Length)
			{
				throw new ArgumentOutOfRangeException("index");
			}
			foreach (T entry in GetEntries())
			{
				array[index++] = entry;
			}
		}

		private void InvokeOnRegistered(T entry)
		{
			try
			{
				OnRegistered(entry);
			}
			catch (Exception ex)
			{
				L.Debug("[" + DebugName + "] Caught Exception whilst handling OnRegistered callback: " + ex);
			}
			if (!((object)entry is IRegisterListener registerListener))
			{
				return;
			}
			try
			{
				registerListener.OnRegister();
			}
			catch (Exception ex2)
			{
				L.Debug("[" + DebugName + "] Caught Exception whilst handling OnRegistered callback: " + ex2);
			}
		}

		private void InvokeOnRegisterFailed(T entry, string failReason)
		{
			try
			{
				OnRegisterFailed(entry, failReason);
			}
			catch (Exception ex)
			{
				L.Debug("[" + DebugName + "] Caught Exception whilst handling OnRegisterFail callback: " + ex);
			}
			if (!((object)entry is IRegisterListener registerListener))
			{
				return;
			}
			try
			{
				registerListener.OnRegisterFail(failReason);
			}
			catch (Exception ex2)
			{
				L.Debug("[" + DebugName + "] Caught Exception whilst handling OnRegisterFail callback: " + ex2);
			}
		}

		private void InvokeOnUnRegistered(T entry)
		{
			try
			{
				OnUnRegistered(entry);
			}
			catch (Exception ex)
			{
				L.Debug("[" + DebugName + "] Caught Exception whilst handling OnUnRegistered callback: " + ex);
			}
			if (!((object)entry is IUnregisterListener unregisterListener))
			{
				return;
			}
			try
			{
				unregisterListener.OnUnregister();
			}
			catch (Exception ex2)
			{
				L.Debug("[" + DebugName + "] Caught Exception whilst handling OnUnRegistered callback: " + ex2);
			}
		}

		public IEnumerator<T> GetEnumerator()
		{
			return GetEntries().GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		bool IRegistry.TryGetEntry(string id, [NotNullWhen(true)] out IRegisterable? entry)
		{
			if (TryGetEntry(id, out T entry2))
			{
				entry = entry2;
				return true;
			}
			entry = null;
			return false;
		}

		void IRegistry.Register(IRegisterable entry)
		{
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			if (!(entry is T entry2))
			{
				throw new FailedToRegisterException(this, entry, $"Cannot register entries of type '{entry.GetType()}'");
			}
			Register(entry2);
		}

		bool IRegistry.ContainsEntry(IRegisterable entry)
		{
			if (entry != null && TryGetEntry(entry.GetID(), out T entry2))
			{
				return entry2.Equals(entry);
			}
			return false;
		}

		IEnumerable<IRegisterable> IRegistry.GetEntries()
		{
			return GetEntries().Select((Func<T, IRegisterable>)((T entry) => entry));
		}

		bool IRegistry.UnRegister(IRegisterable entry)
		{
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			if (!(entry is T entry2))
			{
				throw new InvalidCastException($"Cannot register entries of type '{entry.GetType()}'");
			}
			return UnRegister(entry2);
		}

		void ICollection<T>.Add(T item)
		{
			Register(item);
		}

		bool ICollection<T>.Remove(T item)
		{
			return UnRegister(item);
		}

		void ICollection<T>.Clear()
		{
			UnRegisterAll();
		}

		bool ICollection<T>.Contains(T item)
		{
			return ContainsEntry(item);
		}

		void ICollection.CopyTo(Array array, int index)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}
			if (array.Rank != 1)
			{
				throw new ArgumentException($"Unsupported array rank '{array.Rank}' (only supports Rank 1)", "array");
			}
			if (!array.GetType().GetElementType().IsAssignableFrom(typeof(T)))
			{
				throw new ArgumentException("Cannot add values to array, as types don't match", "array");
			}
			if (index + Count >= array.Length)
			{
				throw new ArgumentOutOfRangeException("index");
			}
			foreach (T entry in GetEntries())
			{
				array.SetValue(entry, index++);
			}
		}
	}
	public class RegistryElementFactorySettings : IRegisterable
	{
		public Type Type { get; }

		public string ID { get; }

		public RegistryElementFactorySettings(Type type, string id)
		{
			Type = type ?? throw new ArgumentNullException("type");
			ID = id ?? throw new ArgumentNullException("id");
		}

		public IRegisterable CreateInstance()
		{
			return ((IRegisterable)Activator.CreateInstance(Type)) ?? throw new InvalidOperationException();
		}

		string IRegisterable.GetID()
		{
			return ID;
		}
	}
	public class RegistryElementFactorySettings<TElement> : RegistryElementFactorySettings where TElement : IRegisterable
	{
		public RegistryElementFactorySettings(TElement element)
			: base(element.GetType(), element.GetID())
		{
		}

		public new TElement CreateInstance()
		{
			return (TElement)base.CreateInstance();
		}
	}
	public class RegistryList<TElement> : IRegistryList<TElement>, IList<TElement>, ICollection<TElement>, IEnumerable<TElement>, IEnumerable where TElement : IRegisterable
	{
		private readonly List<TElement> m_elements;

		public TElement this[int index]
		{
			get
			{
				return m_elements[index];
			}
			set
			{
				m_elements[index] = value;
			}
		}

		public int Count => m_elements.Count;

		public bool IsReadOnly => false;

		public TElement this[string id]
		{
			get
			{
				int num = this.IndexOfID(id);
				if (num == -1)
				{
					throw new ArgumentOutOfRangeException("id", "There is no element with id '" + id + "'");
				}
				return this[num];
			}
			set
			{
				if (value == null)
				{
					throw new ArgumentNullException("value");
				}
				int num = this.IndexOfID(id);
				if (num == -1)
				{
					Add(value);
				}
				else
				{
					this[num] = value;
				}
			}
		}

		public RegistryList()
		{
			m_elements = new List<TElement>();
		}

		public void Add(TElement item)
		{
			if (item == null)
			{
				throw new ArgumentNullException("item");
			}
			m_elements.Add(item);
		}

		public void Clear()
		{
			m_elements.Clear();
		}

		public bool Contains(TElement item)
		{
			if (item == null)
			{
				return false;
			}
			return m_elements.Contains(item);
		}

		public void CopyTo(TElement[] array, int arrayIndex)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}
			if (arrayIndex + Count >= array.Length)
			{
				throw new ArgumentOutOfRangeException("arrayIndex");
			}
			for (int i = 0; i < Count; i++)
			{
				array[arrayIndex++] = this[i];
			}
		}

		public IEnumerator<TElement> GetEnumerator()
		{
			return m_elements.GetEnumerator();
		}

		public int IndexOf(TElement item)
		{
			if (item == null)
			{
				throw new ArgumentNullException("item");
			}
			return m_elements.IndexOf(item);
		}

		public void Insert(int index, TElement item)
		{
			if (item == null)
			{
				throw new ArgumentNullException("item");
			}
			m_elements.Insert(index, item);
		}

		public bool Remove(TElement item)
		{
			if (item == null)
			{
				throw new ArgumentNullException("item");
			}
			return m_elements.Remove(item);
		}

		public void RemoveAt(int index)
		{
			m_elements.RemoveAt(index);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	}
	public abstract class RegistryOfFactory<TElement, TElementFactoryInfo> : Registry<TElementFactoryInfo> where TElement : IRegisterable where TElementFactoryInfo : RegistryElementFactorySettings<TElement>
	{
		protected abstract TElementFactoryInfo CreateFactoryInfo(TElement element);

		public void Register<T>() where T : TElement, new()
		{
			Register((TElement)(object)new T());
		}

		public void Register(TElement element)
		{
			if (element == null)
			{
				throw new ArgumentNullException("element");
			}
			Register(CreateFactoryInfo(element));
		}

		public bool UnRegister(TElement element)
		{
			if (element == null)
			{
				throw new ArgumentNullException("element");
			}
			return UnRegister(element.GetID());
		}

		public TElement CreateElement(string id)
		{
			return this[id].CreateInstance();
		}

		public bool TryCreateElement(string id, [NotNullWhen(true)] out TElement? element)
		{
			if (TryGetEntry(id, out TElementFactoryInfo entry))
			{
				try
				{
					element = entry.CreateInstance();
					return true;
				}
				catch (Exception ex)
				{
					L.Debug("Failed creating element for factory with id '" + id + "': " + ex);
				}
			}
			element = default(TElement);
			return false;
		}
	}
	public class RegistryOfFactory<TElement> : RegistryOfFactory<TElement, RegistryElementFactorySettings<TElement>> where TElement : IRegisterable
	{
		protected sealed override RegistryElementFactorySettings<TElement> CreateFactoryInfo(TElement element)
		{
			return new RegistryElementFactorySettings<TElement>(element);
		}
	}
}
namespace Flaff.Collections.Registries.JsonConverters
{
	public abstract class RegistryListConverterBase<TList, TElement> : JsonConverter<TList> where TList : IRegistryList<TElement>, new() where TElement : IRegisterable
	{
		public RegistryListWriteMode WriteMode { get; set; }

		public override TList? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
		{
			TList val = new TList();
			switch (reader.TokenType)
			{
			case JsonTokenType.StartObject:
				ReadAsMap(val, ref reader, typeToConvert, options);
				break;
			case JsonTokenType.StartArray:
				ReadAsList(val, ref reader, typeToConvert, options);
				break;
			default:
				throw new JsonException($"Unexpected token '{reader.TokenType}' (expected {1}, or {3})");
			case JsonTokenType.Null:
				break;
			}
			return val;
		}

		private void ReadAsList(TList list, ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
		{
			int num = 0;
			while (reader.Read())
			{
				if (reader.TokenType == JsonTokenType.EndArray)
				{
					return;
				}
				if (reader.TokenType == JsonTokenType.StartObject)
				{
					TElement item;
					try
					{
						item = ReadElement(ref reader, typeToConvert, options);
					}
					catch (JsonException innerException)
					{
						throw new JsonException($"Uncaught json exception whilst parsing element index {num}", innerException);
					}
					list.Add(item);
					num++;
					continue;
				}
				throw new JsonException($"Unexpected token {reader.TokenType} for element index {num}. (Expected {4} or {1})");
			}
			throw new JsonException($"Unexpected end of input at element index {num}");
		}

		private void ReadAsMap(TList list, ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
		{
			int num = 0;
			while (reader.Read())
			{
				if (reader.TokenType == JsonTokenType.EndObject)
				{
					return;
				}
				if (reader.TokenType != JsonTokenType.PropertyName)
				{
					throw new JsonException($"Unexpected token {reader.TokenType}. Expected {5} at element index {num}");
				}
				string text = reader.GetString() ?? throw new JsonException($"ID was null at element index {num}");
				if (!reader.Read())
				{
					break;
				}
				if (reader.TokenType == JsonTokenType.StartObject || reader.TokenType == JsonTokenType.Null)
				{
					TElement item;
					try
					{
						item = ReadElement(text, ref reader, typeToConvert, options);
					}
					catch (JsonException innerException)
					{
						throw new JsonException($"Uncaught json exception whilst parsing element index {num} (ID: {text})", innerException);
					}
					list.Add(item);
					num++;
					continue;
				}
				throw new JsonException($"Unexpected token {reader.TokenType} for element index {num}. (Expected {4} or {1})");
			}
			throw new JsonException($"Unexpected end of input at element index {num}");
		}

		private TElement ReadElement(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
		{
			if (reader.Read())
			{
				if (reader.TokenType != JsonTokenType.PropertyName)
				{
					throw new JsonException($"Unexpected token {reader.TokenType}. Expected {5}");
				}
				string text = reader.GetString() ?? throw new JsonException("ID Property name null!");
				if (text.ToLower() != "id")
				{
					throw new JsonException("First property of an element should be an ID field! Instead got " + text + ".");
				}
				if (reader.Read())
				{
					if (reader.TokenType == JsonTokenType.Null)
					{
						throw new JsonException("An ID cannot be 'null'");
					}
					if (reader.TokenType != JsonTokenType.String)
					{
						throw new JsonException($"Unexpected token {reader.TokenType}. Expected {7}");
					}
					string @string = reader.GetString();
					return ReadElement(@string, ref reader, typeToConvert, options);
				}
			}
			throw new JsonException("Unexpected end of input!");
		}

		private TElement ReadElement(string id, ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
		{
			TElement val = CreateElementFromID(id);
			FillElement(val, ref reader, options);
			return val;
		}

		protected abstract TElement CreateElementFromID(string id);

		protected abstract void FillElement(TElement element, ref Utf8JsonReader reader, JsonSerializerOptions options);

		public override void Write(Utf8JsonWriter writer, TList value, JsonSerializerOptions options)
		{
			switch (WriteMode)
			{
			case RegistryListWriteMode.List:
				WriteAsList(writer, value, options);
				break;
			case RegistryListWriteMode.Map:
				WriteAsMap(writer, value, options);
				break;
			}
		}

		private void WriteAsList(Utf8JsonWriter writer, TList value, JsonSerializerOptions options)
		{
			writer.WriteStartArray();
			if (value == null)
			{
				writer.WriteEndArray();
				return;
			}
			foreach (TElement item in value)
			{
				writer.WriteStartObject();
				writer.WritePropertyName("ID");
				writer.WriteStringValue(item.GetID());
				WriteElementProperties(writer, item, options);
				writer.WriteEndObject();
			}
			writer.WriteEndArray();
		}

		private void WriteAsMap(Utf8JsonWriter writer, TList value, JsonSerializerOptions options)
		{
			writer.WriteStartObject();
			if (value == null)
			{
				writer.WriteEndObject();
				return;
			}
			foreach (TElement item in value)
			{
				writer.WritePropertyName(item.GetID());
				writer.WriteStartObject();
				WriteElementProperties(writer, item, options);
				writer.WriteEndObject();
			}
			writer.WriteEndObject();
		}

		protected abstract void WriteElementProperties(Utf8JsonWriter writer, TElement element, JsonSerializerOptions options);
	}
	public enum RegistryListWriteMode : byte
	{
		List,
		Map
	}
}