Decompiled source of SULFUR Together Scaler v0.2.1

SulfurTogetherScaler.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LevelGeneration;
using MakerGraphTool;
using MakerGraphTool.Nodes;
using Microsoft.CodeAnalysis;
using PerfectRandom.Sulfur.Core;
using PerfectRandom.Sulfur.Core.LevelGeneration;
using SULFURTogether.Api;
using UnityEngine;
using XNode;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("SulfurTogetherScaler")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1+d69d1912094b53f167392cb739f93d797db28d66")]
[assembly: AssemblyProduct("SulfurTogetherScaler")]
[assembly: AssemblyTitle("SulfurTogetherScaler")]
[assembly: AssemblyVersion("0.2.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 SulfurTogetherScaler
{
	internal static class FactorSync
	{
		internal sealed class State
		{
			public int FactorMilli = 1000;

			public string Chapter = "";

			public int LevelIndex = -1;

			public string Preset = "";

			public int Players = 1;
		}

		public const string ChannelId = "sts.factor";

		private const float PollSeconds = 0.5f;

		private static IExternalChannelRegistration _registration;

		private static float _accum;

		private static string _lastPeerKey = "";

		private static SessionRole _lastRole = (SessionRole)0;

		private static int _requestTicksLeft;

		private const int RequestTicks = 120;

		private static bool _requestSentOk;

		private static State _hostState;

		private static State _clientState;

		private static bool _warnedNoHostState;

		public static void Start()
		{
			try
			{
				_registration = NetExternalChannel.Register("sts.factor", (Action<string, byte[]>)OnReceive);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[STS] could not register channel 'sts.factor': " + ex.Message + ". Clients cannot receive the host's factor and will generate unscaled levels; disable the mod on the host or the levels will not match."));
			}
		}

		public static void Stop()
		{
			try
			{
				((IDisposable)_registration)?.Dispose();
			}
			catch
			{
			}
			_registration = null;
		}

		public static int FactorMilliFor(int players, ScalePreset preset)
		{
			if (players <= 1)
			{
				return 1000;
			}
			int num;
			int num2;
			if (preset == ScalePreset.Linear)
			{
				num = 1000;
				num2 = 4000;
			}
			else
			{
				num = 500;
				num2 = 3000;
			}
			long num3 = 1000 + (long)num * (long)(players - 1);
			if (num3 > num2)
			{
				num3 = num2;
			}
			return (int)num3;
		}

		public static int ScaleBudget(int budget, int factorMilli)
		{
			if (factorMilli == 1000 || budget <= 0)
			{
				return budget;
			}
			return (int)(((long)budget * (long)factorMilli + 500) / 1000);
		}

		public static string Fmt(int factorMilli)
		{
			return ((double)factorMilli / 1000.0).ToString("0.000", CultureInfo.InvariantCulture);
		}

		public static int FactorForLevelStart(string chapter, int levelIndex, out string source)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			int num = PlayerCount();
			SessionRole val = SafeRole();
			if (!NetSessionInfo.IsSessionActive || (int)val == 0 || num <= 1)
			{
				source = (((int)val == 0) ? "solo" : "session-of-one");
				return 1000;
			}
			if ((int)val == 1)
			{
				int num2 = FactorMilliFor(num, Plugin.Preset.Value);
				_hostState = new State
				{
					FactorMilli = num2,
					Chapter = chapter,
					LevelIndex = levelIndex,
					Preset = Plugin.Preset.Value.ToString(),
					Players = num
				};
				SendState(_hostState, (ExternalTarget)1, null);
				source = "host-frozen";
				return num2;
			}
			State clientState = _clientState;
			if (clientState != null)
			{
				bool flag = string.Equals(clientState.Chapter, chapter, StringComparison.Ordinal) && clientState.LevelIndex == levelIndex;
				source = (flag ? "host-state" : "host-state-earlier-level");
				return clientState.FactorMilli;
			}
			SendRequest();
			if (!_warnedNoHostState)
			{
				_warnedNoHostState = true;
				Plugin.Log.LogWarning((object)"[STS] no factor from the host; this level is not scaled. If this happens every level, the host does not have the mod.");
			}
			source = "no-host-state";
			return 1000;
		}

		public static void Tick(float dt)
		{
			//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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Invalid comparison between Unknown and I4
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Invalid comparison between Unknown and I4
			_accum += dt;
			if (_accum < 0.5f)
			{
				return;
			}
			_accum = 0f;
			try
			{
				SessionRole val = SafeRole();
				if (val != _lastRole)
				{
					OnRoleChanged(_lastRole, val);
					_lastRole = val;
				}
				if ((int)val == 0)
				{
					return;
				}
				if ((int)val == 2 && _clientState == null && _requestTicksLeft > 0)
				{
					_requestTicksLeft--;
					if (TrySend("R", (ExternalTarget)0, null, quiet: true))
					{
						if (!_requestSentOk)
						{
							Plugin.Log.LogInfo((object)"[STS] asked the host for its factor");
						}
						_requestSentOk = true;
					}
					else if (_requestTicksLeft == 0)
					{
						Plugin.Log.LogWarning((object)"[STS] could not reach the host within 60 s; levels are not scaled until its factor arrives");
					}
				}
				string text = PeerKey();
				if (!(text != _lastPeerKey))
				{
					return;
				}
				_lastPeerKey = text;
				if ((int)val == 1)
				{
					State state = _hostState ?? new State();
					if (_hostState == null)
					{
						int players = PlayerCount();
						state.FactorMilli = FactorMilliFor(players, Plugin.Preset.Value);
						state.Preset = Plugin.Preset.Value.ToString();
						state.Players = players;
					}
					SendState(state, (ExternalTarget)1, null);
					Plugin.Log.LogInfo((object)$"[STS] peers changed ({PlayerCount()} players); sent factor {Fmt(state.FactorMilli)} to all clients");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[STS] tick: " + ex.Message));
			}
		}

		private static void OnRoleChanged(SessionRole from, SessionRole to)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Invalid comparison between Unknown and I4
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Invalid comparison between Unknown and I4
			Plugin.Log.LogInfo((object)$"[STS] session role {from} -> {to}");
			if ((int)to == 0)
			{
				_hostState = null;
				_clientState = null;
				_lastPeerKey = "";
			}
			else if ((int)to == 2)
			{
				_clientState = null;
				_requestTicksLeft = 120;
				_requestSentOk = false;
				_warnedNoHostState = false;
			}
			else if ((int)to == 1)
			{
				_hostState = null;
			}
		}

		private static void OnReceive(string senderPeerId, byte[] payload)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Invalid comparison between Unknown and I4
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Invalid comparison between Unknown and I4
			try
			{
				string[] array = Encoding.UTF8.GetString(payload ?? Array.Empty<byte>()).Split(new char[1] { '|' });
				if (array.Length == 0)
				{
					return;
				}
				if (array[0] == "R")
				{
					if ((int)SafeRole() == 1)
					{
						State state = _hostState;
						if (state == null)
						{
							int players = PlayerCount();
							state = new State
							{
								FactorMilli = FactorMilliFor(players, Plugin.Preset.Value),
								Preset = Plugin.Preset.Value.ToString(),
								Players = players
							};
						}
						SendState(state, (ExternalTarget)2, senderPeerId);
						Plugin.Log.LogInfo((object)("[STS] peer " + senderPeerId + " asked; sent factor " + Fmt(state.FactorMilli)));
					}
				}
				else if (array[0] == "F" && array.Length >= 6 && (int)SafeRole() == 2)
				{
					State state2 = new State
					{
						FactorMilli = int.Parse(array[1], CultureInfo.InvariantCulture),
						Chapter = array[2],
						LevelIndex = int.Parse(array[3], CultureInfo.InvariantCulture),
						Preset = array[4],
						Players = int.Parse(array[5], CultureInfo.InvariantCulture)
					};
					if (state2.FactorMilli < 1000 || state2.FactorMilli > 10000)
					{
						Plugin.Log.LogWarning((object)$"[STS] ignored factor {state2.FactorMilli} from host (out of range)");
						return;
					}
					_clientState = state2;
					Plugin.Log.LogInfo((object)string.Format("[STS] host factor {0} preset={1} players={2} for {3}", Fmt(state2.FactorMilli), state2.Preset, state2.Players, (state2.LevelIndex < 0) ? "next level" : (state2.Chapter + "/" + state2.LevelIndex)));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[STS] bad message from " + senderPeerId + ": " + ex.Message));
			}
		}

		private static void SendState(State s, ExternalTarget target, string peerId)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			Send(string.Join("|", "F", s.FactorMilli.ToString(CultureInfo.InvariantCulture), s.Chapter ?? "", s.LevelIndex.ToString(CultureInfo.InvariantCulture), s.Preset ?? "", s.Players.ToString(CultureInfo.InvariantCulture)), target, peerId);
		}

		private static void SendRequest()
		{
			if (!TrySend("R", (ExternalTarget)0, null, quiet: true))
			{
				_requestTicksLeft = 120;
			}
		}

		private static void Send(string text, ExternalTarget target, string peerId)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			TrySend(text, target, peerId, quiet: false);
		}

		private static bool TrySend(string text, ExternalTarget target, string peerId, bool quiet)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (_registration == null)
			{
				return false;
			}
			try
			{
				bool num = NetExternalChannel.Send("sts.factor", Encoding.UTF8.GetBytes(text), (ExternalDelivery)0, target, peerId);
				if (!num && !quiet)
				{
					Plugin.Log.LogWarning((object)$"[STS] send to {target} refused by SULFUR Together: {text}");
				}
				return num;
			}
			catch (Exception ex)
			{
				if (!quiet)
				{
					Plugin.Log.LogWarning((object)$"[STS] send to {target} threw: {ex.Message}");
				}
				return false;
			}
		}

		private static SessionRole SafeRole()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: 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)
			try
			{
				return NetSessionInfo.Role;
			}
			catch
			{
				return (SessionRole)0;
			}
		}

		public static int PlayerCount()
		{
			try
			{
				IReadOnlyList<ExternalPeer> peers = NetSessionInfo.Peers;
				return (peers == null || peers.Count == 0) ? 1 : peers.Count;
			}
			catch
			{
				return 1;
			}
		}

		private static string PeerKey()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				IReadOnlyList<ExternalPeer> peers = NetSessionInfo.Peers;
				if (peers == null)
				{
					return "";
				}
				List<string> list = new List<string>(peers.Count);
				for (int i = 0; i < peers.Count; i++)
				{
					ExternalPeer val = peers[i];
					list.Add(((ExternalPeer)(ref val)).PeerId);
				}
				list.Sort(StringComparer.Ordinal);
				return string.Join(",", list);
			}
			catch
			{
				return "";
			}
		}
	}
	public enum ScalePreset
	{
		Mild,
		Linear
	}
	[BepInPlugin("com.s8systems.sulfur.togetherscaler", "SULFUR Together Scaler", "0.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.s8systems.sulfur.togetherscaler";

		public const string Name = "SULFUR Together Scaler";

		public const string Version = "0.2.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<ScalePreset> Preset;

		internal static ConfigEntry<string> ExcludedEnvironments;

		internal static ConfigEntry<string> ExcludedLevelSets;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, Live("Scale the number of enemies by the number of players in a SULFUR Together session."));
			Preset = ((BaseUnityPlugin)this).Config.Bind<ScalePreset>("General", "Preset", ScalePreset.Mild, Live("Mild: 2 players x1.5, 3 x2.0, 4 x2.5, 5+ x3.0. Linear: 2 players x2, 3 x3, 4+ x4. Only the host's setting matters. A change applies at the next level."));
			ExcludedEnvironments = ((BaseUnityPlugin)this).Config.Bind<string>("Exclusions", "ExcludedEnvironments", "ChurchHub,Onboarding,Onboarding_LoopAround,EndlessMode,ChallengeEnvironments,DebugChapter", Live("Comma-separated environment ids that are never scaled."));
			ExcludedLevelSets = ((BaseUnityPlugin)this).Config.Bind<string>("Exclusions", "ExcludedLevelSets", "", Live("Comma-separated level set names that are never scaled. Set names are in the [STS] level lines of the BepInEx log (set=)."));
			_harmony = new Harmony("com.s8systems.sulfur.togetherscaler");
			SpawnPatch.Apply(_harmony);
			FactorSync.Start();
			Log.LogInfo((object)string.Format("{0} {1} loaded. preset={2} enabled={3}", "SULFUR Together Scaler", "0.2.1", Preset.Value, Enabled.Value));
		}

		private static ConfigDescription Live(string text)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			return new ConfigDescription(text, (AcceptableValueBase)null, new object[1] { "LiveApply" });
		}

		private void Update()
		{
			FactorSync.Tick(Time.unscaledDeltaTime);
		}

		private void OnDestroy()
		{
			FactorSync.Stop();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class SpawnPatch
	{
		private static readonly FieldInfo PackBudgetField = AccessTools.Field(typeof(SpawnEnemiesNode), "packBudget");

		private static readonly MethodInfo ScaleMethod = AccessTools.Method(typeof(SpawnPatch), "ScaledBudget", (Type[])null, (Type[])null);

		private static int _activeFactorMilli = 1000;

		private static SpawnEnemiesNode _activeNode;

		private static string _activeLabel = "";

		private static int _unitsBefore;

		private static int _replacements;

		public static void Apply(Harmony harmony)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(SpawnEnemiesNode), "Execute", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Log.LogError((object)"[STS] SpawnEnemiesNode.Execute not found; the game changed. Nothing patched.");
				return;
			}
			MethodInfo methodInfo2 = AccessTools.EnumeratorMoveNext((MethodBase)methodInfo);
			if (methodInfo2 == null)
			{
				Plugin.Log.LogError((object)"[STS] SpawnEnemiesNode.Execute has no iterator MoveNext; nothing patched.");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(SpawnPatch), "ExecutePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			HarmonyMethod val = new HarmonyMethod(typeof(SpawnPatch), "MoveNextTranspiler", (Type[])null);
			harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(SpawnPatch), "MoveNextPostfix", (Type[])null), val, (HarmonyMethod)null, (HarmonyMethod)null);
			if (_replacements == 0)
			{
				Plugin.Log.LogError((object)"[STS] transpiler found no read of packBudget in the pack node; the factor will have no effect.");
			}
			else
			{
				Plugin.Log.LogInfo((object)$"[STS] patched {methodInfo2.DeclaringType?.Name}.MoveNext: {_replacements} packBudget read(s) scaled");
			}
		}

		public static int ScaledBudget(int budget)
		{
			return FactorSync.ScaleBudget(budget, _activeFactorMilli);
		}

		private static void ExecutePrefix(SpawnEnemiesNode __instance)
		{
			_activeFactorMilli = 1000;
			_activeNode = __instance;
			try
			{
				string text = "?";
				int num = -1;
				string name = "?";
				GameManager instance = StaticInstance<GameManager>.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					WorldEnvironment currentEnvironment = instance.currentEnvironment;
					if ((Object)(object)currentEnvironment != (Object)null)
					{
						text = ((object)Unsafe.As<WorldEnvironmentIds, WorldEnvironmentIds>(ref currentEnvironment.id)/*cast due to .constrained prefix*/).ToString();
						name = ((Object)currentEnvironment).name;
					}
					num = instance.currentLevelIndex;
				}
				string text2 = (((Object)(object)((MakerNodeBase)__instance).Context != (Object)null && (Object)(object)((MakerNodeBase)__instance).Context.Set != (Object)null) ? ((Object)((MakerNodeBase)__instance).Context.Set).name : (((Object)(object)((Node)__instance).graph != (Object)null) ? ((Object)((Node)__instance).graph).name : "?"));
				_activeLabel = $"{text}/{num} set={text2}";
				MakerGraphContext context = ((MakerNodeBase)__instance).Context;
				LevelGenerationContext val = (LevelGenerationContext)(object)((context is LevelGenerationContext) ? context : null);
				_unitsBefore = (((Object)(object)val != (Object)null && val.units != null) ? val.units.Count : (-1));
				int packBudget = __instance.packBudget;
				int num2 = FactorSync.PlayerCount();
				int num3;
				string source;
				if (!Plugin.Enabled.Value)
				{
					num3 = 1000;
					source = "disabled";
				}
				else if (InList(Plugin.ExcludedEnvironments.Value, text) || InList(Plugin.ExcludedEnvironments.Value, name))
				{
					num3 = 1000;
					source = "excluded-environment";
				}
				else if (InList(Plugin.ExcludedLevelSets.Value, text2))
				{
					num3 = 1000;
					source = "excluded-set";
				}
				else
				{
					num3 = FactorSync.FactorForLevelStart(text, num, out source);
				}
				_activeFactorMilli = num3;
				Plugin.Log.LogInfo((object)($"[STS] level {_activeLabel} players={num2} preset={Plugin.Preset.Value} factor={FactorSync.Fmt(num3)} " + $"budget {packBudget} -> {FactorSync.ScaleBudget(packBudget, num3)} density={__instance.packDensity} source={source}"));
			}
			catch (Exception arg)
			{
				_activeFactorMilli = 1000;
				Plugin.Log.LogWarning((object)$"[STS] level prefix failed, factor 1 for this level: {arg}");
			}
		}

		private static void MoveNextPostfix(bool __result)
		{
			if (__result)
			{
				return;
			}
			try
			{
				if ((Object)(object)_activeNode != (Object)null)
				{
					MakerGraphContext context = ((MakerNodeBase)_activeNode).Context;
					LevelGenerationContext val = (LevelGenerationContext)(object)((context is LevelGenerationContext) ? context : null);
					int num = (((Object)(object)val != (Object)null && val.units != null) ? val.units.Count : (-1));
					int num2 = ((num >= 0 && _unitsBefore >= 0) ? (num - _unitsBefore) : (-1));
					Plugin.Log.LogInfo((object)$"[STS] level {_activeLabel} done: factor={FactorSync.Fmt(_activeFactorMilli)} units placed by the pack node={num2} total units={num}");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[STS] level postfix: " + ex.Message));
			}
			finally
			{
				_activeFactorMilli = 1000;
				_activeNode = null;
			}
		}

		private static IEnumerable<CodeInstruction> MoveNextTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			int n = 0;
			foreach (CodeInstruction ci in instructions)
			{
				yield return ci;
				if (ci.opcode == OpCodes.Ldfld && ci.operand is FieldInfo fieldInfo && fieldInfo == PackBudgetField)
				{
					n++;
					yield return new CodeInstruction(OpCodes.Call, (object)ScaleMethod);
				}
			}
			_replacements = n;
		}

		private static bool InList(string csv, string name)
		{
			if (string.IsNullOrEmpty(csv) || string.IsNullOrEmpty(name))
			{
				return false;
			}
			string[] array = csv.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0 && string.Equals(text, name, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}
	}
}