Decompiled source of DropinMultiplayer v4.3.0

BepInEx/plugins/DropInMultiplayer/DropInMultiplayer.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DropInMultiplayer.Helpers;
using HG.Reflection;
using Microsoft.CodeAnalysis;
using On.RoR2;
using R2API.Utils;
using RoR2;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: OptIn]
[assembly: AssemblyTitle("DropInMultiplayer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DropInMultiplayer")]
[assembly: AssemblyCopyright("Copyright ©  2022")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("0321a436-2c1f-4f7b-8a99-becbbb8089d2")]
[assembly: AssemblyFileVersion("4.3.0.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("4.3.0.0")]
[module: UnverifiableCode]
[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 DropInMultiplayer
{
	public class DropInMultiplayerConfig
	{
		private const string DefaultWelcomeMessage = "Hello {username}! Join the game by typing '/join_as {survivor name}' in chat (or '/join_as random'). To get a list of availible survivors, type '/list_survivors' in chat";

		public ConfigEntry<bool> AllowRespawn { get; }

		public ConfigEntry<bool> JoinAsRandomByDefault { get; }

		public ConfigEntry<bool> AllowJoinAsHiddenSurvivors { get; }

		public ConfigEntry<bool> AllowJoinAsAllBodies { get; }

		public ConfigEntry<bool> GiveHereticItems { get; }

		public ConfigEntry<bool> PreventCaptainScrapAbuse { get; }

		public ConfigEntry<bool> PreventOperatorDroneAbuse { get; }

		public ConfigEntry<bool> GiveCatchUpItems { get; }

		public ConfigEntry<bool> GiveRejoiningPlayersCatchUpItems { get; }

		public ConfigEntry<bool> GiveCatchUpVoidItems { get; }

		public ConfigEntry<bool> GiveCatchUpLunarItems { get; }

		public ConfigEntry<bool> SendWelcomeMessage { get; }

		public ConfigEntry<string> CustomWelcomeMessage { get; }

		public DropInMultiplayerConfig(ConfigFile config)
		{
			AllowRespawn = config.Bind<bool>("General", "AllowRespawn", true, "When enabled dead players who use the join as command will be immediately respawned");
			JoinAsRandomByDefault = config.Bind<bool>("General", "JoinAsRandomByDefault", false, "When enabled newly joined players will be spawned as random survivor by default");
			AllowJoinAsHiddenSurvivors = config.Bind<bool>("Survivors", "AllowJoinAsHiddenSurvivors", true, "When enabled allows players to join as hidden characters, e.g. heretic");
			AllowJoinAsAllBodies = config.Bind<bool>("General", "AllowJoinAsAllBodies", false, "When enabled using the join as command will attempt to match with any body, e.g. join_as beetle, WARNING: Very Untested!");
			GiveHereticItems = config.Bind<bool>("Survivors", "GiveHereticItems", true, "When enabled joining as Heretic will give all 4 Heretic items automatically.");
			PreventCaptainScrapAbuse = config.Bind<bool>("Survivors", "PreventCaptainScrapAbuse", true, "When enabled Captain will not receive replacement Microbots if it was scrapped or removed.");
			GiveCatchUpItems = config.Bind<bool>("Items", "GiveCatchUpItems", true, "When enabled players will be given catch up items when joining");
			GiveRejoiningPlayersCatchUpItems = config.Bind<bool>("Items", "GiveRejoiningPlayersCatchUpItems", true, "When enabled players who leave and rejoin will be given catchup items, WARNING: Can be exploited by giving all items to one player then leaving and rejoining");
			GiveCatchUpVoidItems = config.Bind<bool>("Items", "GiveCatchUpVoidItems", false, "When enabled void items will be dropped when players start (requires GiveCatchUpItems enabled)");
			GiveCatchUpLunarItems = config.Bind<bool>("Items", "GiveCatchUpLunarItems", false, "When enabled lunar items will be dropped when players start (requires GiveCatchUpItems enabled)");
			SendWelcomeMessage = config.Bind<bool>("Chat Messages", "SendWelcomeMessage", true, "Sends the welcome message when a new player joins.");
			CustomWelcomeMessage = config.Bind<string>("Chat Messages", "CustomWelcomeMessage", "Hello {username}! Join the game by typing '/join_as {survivor name}' in chat (or '/join_as random'). To get a list of availible survivors, type '/list_survivors' in chat", "Format of welcome message. {username} will be replaced with joining users name, and {survivorlist} will be replaced by list of availible survivors.");
		}
	}
	internal enum JoinAsResult
	{
		Success,
		DeadAndNotAllowRespawn
	}
	internal class ChatCommand
	{
		public string Name { get; set; }

		public string HelpText { get; set; }

		public Func<NetworkUser, string[], string> Handler { get; set; }

		internal ChatCommand(string name, string helpText, Func<NetworkUser, string[], string> handler)
		{
			Name = name;
			HelpText = helpText;
			Handler = handler;
		}
	}
	[BepInPlugin("com.niwith.DropInMultiplayer", "Drop In Multiplayer", "4.3.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class DropInMultiplayer : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static Action<Run> <0>__Run_onRunStartGlobal;

			public static hook_RunCmd <1>__Console_RunCmd;

			public static hook_SetupUserCharacterMaster <2>__Run_SetupUserCharacterMaster;

			public static NetworkUserGenericDelegate <3>__NetworkUser_onPostNetworkUserStart;
		}

		public const string PluginGUID = "com.niwith.DropInMultiplayer";

		public const string PluginName = "Drop In Multiplayer";

		public const string PluginVersion = "4.3.0";

		private const string HelpHelpText = "Usage: help {command}\nDescription: Prints help text for command";

		private const string JoinAsHelpText = "Usage: join_as {survivor} {player (optional)}\nDescription: Join in-progress run as the given survivor";

		private const string ListSurvivorsHelpText = "Usage: list_survivors {player (optional)}\nDescription: Shows a list of all availible survivors for given player (or self)";

		private const string ListBodiesHelpText = "Usage: list_bodies\nDescription: Shows a list of all bodies to be used with join_as command (if AllowJoinAsAllBodies is true)";

		private const string CatchupHelpText = "Usage: catchup {player (optional)}\nDescription: Manually adds catch up items for the given player (or self)";

		private const string GiveRandomItemsHelpText = "Usage: give_random_items {count} {lunarEnabled} {voidEnabled} {player (optional)}\nDescription: Adds random items for the given player (or self)";

		private static readonly Random _rand = new Random();

		private static readonly Dictionary<string, ChatCommand> _chatCommands = new List<ChatCommand>
		{
			new ChatCommand("HELP", "Usage: help {command}\nDescription: Prints help text for command", Help),
			new ChatCommand("JOIN", "Usage: join_as {survivor} {player (optional)}\nDescription: Join in-progress run as the given survivor", JoinAs),
			new ChatCommand("JOIN_AS", "Usage: join_as {survivor} {player (optional)}\nDescription: Join in-progress run as the given survivor", JoinAs),
			new ChatCommand("LIST_SURVIVORS", "Usage: list_survivors {player (optional)}\nDescription: Shows a list of all availible survivors for given player (or self)", ListSurvivors),
			new ChatCommand("LIST_BODIES", "Usage: list_bodies\nDescription: Shows a list of all bodies to be used with join_as command (if AllowJoinAsAllBodies is true)", ListBodies)
		}.ToDictionary((ChatCommand rec) => rec.Name);

		private static HashSet<Inventory> captainBlacklistInventories;

		public static DropInMultiplayerConfig DropInConfig { get; set; }

		public static DropInMultiplayer Instance { get; set; }

		internal static ManualLogSource Logger { get; set; }

		public void Awake()
		{
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			DropInConfig = new DropInMultiplayerConfig(((BaseUnityPlugin)this).Config);
			SetupEventHandlers();
			((MonoBehaviour)Instance).StartCoroutine(SurvivorNameResolver.PreloadLanguageStrings());
		}

		private static void SetupEventHandlers()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			Run.onRunStartGlobal += Run_onRunStartGlobal;
			object obj = <>O.<1>__Console_RunCmd;
			if (obj == null)
			{
				hook_RunCmd val = Console_RunCmd;
				<>O.<1>__Console_RunCmd = val;
				obj = (object)val;
			}
			Console.RunCmd += (hook_RunCmd)obj;
			object obj2 = <>O.<2>__Run_SetupUserCharacterMaster;
			if (obj2 == null)
			{
				hook_SetupUserCharacterMaster val2 = Run_SetupUserCharacterMaster;
				<>O.<2>__Run_SetupUserCharacterMaster = val2;
				obj2 = (object)val2;
			}
			Run.SetupUserCharacterMaster += (hook_SetupUserCharacterMaster)obj2;
			object obj3 = <>O.<3>__NetworkUser_onPostNetworkUserStart;
			if (obj3 == null)
			{
				NetworkUserGenericDelegate val3 = NetworkUser_onPostNetworkUserStart;
				<>O.<3>__NetworkUser_onPostNetworkUserStart = val3;
				obj3 = (object)val3;
			}
			NetworkUser.onPostNetworkUserStart += (NetworkUserGenericDelegate)obj3;
		}

		private static void Run_onRunStartGlobal(Run run)
		{
			captainBlacklistInventories = new HashSet<Inventory>();
		}

		private static void Run_SetupUserCharacterMaster(orig_SetupUserCharacterMaster orig, Run self, NetworkUser user)
		{
			try
			{
				orig.Invoke(self, user);
			}
			catch (Exception ex)
			{
				Debug.LogException(ex);
				Logger.LogError((object)ex);
				Logger.LogMessage((object)"SetupUserCharacterMaster threw an exception and was caught");
			}
		}

		private static void Console_RunCmd(orig_RunCmd orig, Console self, CmdSender sender, string concommandName, List<string> userArgs)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			orig.Invoke(self, sender, concommandName, userArgs);
			if (!NetworkServer.active || (Object)(object)Run.instance == (Object)null || !concommandName.Equals("say", StringComparison.InvariantCultureIgnoreCase))
			{
				return;
			}
			string text = userArgs.FirstOrDefault();
			if (string.IsNullOrWhiteSpace(text) || !text.StartsWith("/"))
			{
				return;
			}
			string[] source = text.Split(new char[1] { ' ' });
			string text2 = source.FirstOrDefault().Substring(1);
			string[] arg = source.Skip(1).ToArray();
			if (!_chatCommands.TryGetValue(text2.ToUpperInvariant(), out var value))
			{
				SendChatMessage("Unable to find command, try /help");
				return;
			}
			string text3 = value.Handler(sender.networkUser, arg);
			if (!string.IsNullOrWhiteSpace(text3))
			{
				SendChatMessage(text3);
			}
		}

		private static void NetworkUser_onPostNetworkUserStart(NetworkUser networkUser)
		{
			if (NetworkServer.active && (Object)(object)Run.instance != (Object)null)
			{
				bool flag = (Object)(object)networkUser.master != (Object)null;
				Logger.LogMessage((object)(networkUser.userName + " has joined " + (flag ? "as a previously connected player" : "as a new player")));
				GreetNewPlayer(networkUser);
				if (DropInConfig.JoinAsRandomByDefault.Value && !flag)
				{
					Logger.LogMessage((object)"Spawning new player as random, since JoinAsRandomByDefault is true");
					((MonoBehaviour)Instance).StartCoroutine(SpawnPlayerAsRandomInternal(networkUser));
				}
				if (DropInConfig.GiveCatchUpItems.Value && DropInConfig.GiveRejoiningPlayersCatchUpItems.Value && flag)
				{
					GiveCatchUpItems(networkUser);
				}
			}
		}

		private static JoinAsResult SpawnPlayerWithBody(NetworkUser player, BodyIndex newBodyIndex)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player.master == (Object)null)
			{
				Logger.LogMessage((object)$"Spawning new player {player.userName} with bodyIndex = {newBodyIndex}");
				return SpawnNewPlayerWithBody(player, newBodyIndex);
			}
			Logger.LogMessage((object)$"Respawning existing player {player.userName} with bodyIndex = {newBodyIndex}");
			return RespawnExistingPlayerWithBody(player, newBodyIndex);
		}

		private static JoinAsResult SpawnNewPlayerWithBody(NetworkUser player, BodyIndex newBodyIndex)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			player.CmdSetBodyPreference(newBodyIndex);
			Reflection.SetFieldValue<bool>((object)Run.instance, "allowNewParticipants", true);
			Run.instance.OnUserAdded(player);
			Reflection.SetFieldValue<bool>((object)Run.instance, "allowNewParticipants", false);
			Transform spawnTransformForPlayer = GetSpawnTransformForPlayer(player);
			player.master.SpawnBody(spawnTransformForPlayer.position, spawnTransformForPlayer.rotation);
			HandleBodyItems(player, null, BodyCatalog.GetBodyPrefab(newBodyIndex));
			if (DropInConfig.GiveCatchUpItems.Value)
			{
				GiveCatchUpItems(player);
			}
			return JoinAsResult.Success;
		}

		private static JoinAsResult RespawnExistingPlayerWithBody(NetworkUser player, BodyIndex newBodyIndex)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			GameObject bodyPrefab = player.master.bodyPrefab;
			player.CmdSetBodyPreference(newBodyIndex);
			JoinAsResult result = JoinAsResult.Success;
			if ((((Object)(object)player.GetCurrentBody() == (Object)null && player.master.lostBodyToDeath) | ((Object)(object)player.GetCurrentBody() != (Object)null && player.GetCurrentBody().isRemoteOp)) && !DropInConfig.AllowRespawn.Value)
			{
				Logger.LogMessage((object)$"Unable immediately to spawn {player.userName} with bodyIndex = {newBodyIndex} due to being player being dead and AllowRespawn being set to false");
				result = JoinAsResult.DeadAndNotAllowRespawn;
			}
			else
			{
				player.master.originalBodyPrefab = player.master.bodyPrefab;
				Transform spawnTransformForPlayer = GetSpawnTransformForPlayer(player);
				player.master.Respawn(spawnTransformForPlayer.position, spawnTransformForPlayer.rotation, false);
			}
			HandleBodyItems(player, bodyPrefab, BodyCatalog.GetBodyPrefab(newBodyIndex));
			return result;
		}

		private static void HandleBodyItems(NetworkUser player, GameObject oldBodyPrefab, GameObject newBodyPrefab)
		{
			string text = null;
			string text2 = null;
			try
			{
				Inventory inventory = player.master.inventory;
				text = ((oldBodyPrefab != null) ? ((Object)oldBodyPrefab).name : null);
				text2 = ((Object)newBodyPrefab).name;
				switch (text)
				{
				case "CaptainBody":
					if (inventory.GetItemCountPermanent(Items.CaptainDefenseMatrix) <= 0 && DropInConfig.PreventCaptainScrapAbuse.Value)
					{
						captainBlacklistInventories.Add(inventory);
					}
					if (!captainBlacklistInventories.Contains(inventory))
					{
						inventory.RemoveItemPermanent(Items.CaptainDefenseMatrix, 1);
					}
					break;
				case "HereticBody":
					if (DropInConfig.GiveHereticItems.Value)
					{
						inventory.RemoveItemPermanent(Items.LunarPrimaryReplacement, 1);
						inventory.RemoveItemPermanent(Items.LunarSecondaryReplacement, 1);
						inventory.RemoveItemPermanent(Items.LunarSpecialReplacement, 1);
						inventory.RemoveItemPermanent(Items.LunarUtilityReplacement, 1);
					}
					break;
				case "DroneTechBody":
					Logger.LogMessage((object)"Removing drones from players swapping off Operator is work in progress");
					break;
				}
				if (!(text2 == "CaptainBody"))
				{
					if (text2 == "HereticBody" && DropInConfig.GiveHereticItems.Value)
					{
						inventory.GiveItemPermanent(Items.LunarPrimaryReplacement, 1);
						inventory.GiveItemPermanent(Items.LunarSecondaryReplacement, 1);
						inventory.GiveItemPermanent(Items.LunarSpecialReplacement, 1);
						inventory.GiveItemPermanent(Items.LunarUtilityReplacement, 1);
					}
				}
				else if (!captainBlacklistInventories.Contains(inventory) || !DropInConfig.PreventCaptainScrapAbuse.Value)
				{
					inventory.GiveItemPermanent(Items.CaptainDefenseMatrix, 1);
				}
			}
			catch (Exception ex)
			{
				Debug.LogException(ex);
				Logger.LogError((object)ex);
				Logger.LogMessage((object)("Handling body items for transition from " + (text ?? "none") + " to " + (text2 ?? "none") + " resulted in an exception"));
			}
		}

		private static string Help(NetworkUser sender, string[] args)
		{
			if (args.Length > 1)
			{
				return "Help requires either 0 or 1 argument";
			}
			if (args.Length == 1)
			{
				if (!_chatCommands.TryGetValue(args[0].ToUpperInvariant(), out var value))
				{
					return "Unable to find command, try /help";
				}
				return value.HelpText;
			}
			return "Availible Commands: " + string.Join(",", _chatCommands.Values.Select((ChatCommand command) => command.Name.ToLower()));
		}

		private static string JoinAs(NetworkUser sender, string[] args)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Invalid comparison between Unknown and I4
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length != 1 && args.Length != 2)
			{
				return "join as requires either 1 or 2 arguments";
			}
			NetworkUser val = ((args.Length == 1) ? sender : GetNetUserFromString(args[1]));
			if ((Object)(object)val == (Object)null)
			{
				return "Unable to find player with given name";
			}
			string text = args[0];
			SurvivorDef availibleSurvivorForPlayerByName = GetAvailibleSurvivorForPlayerByName(val, text);
			BodyIndex val2 = (BodyIndex)(-1);
			string text2;
			if ((Object)(object)availibleSurvivorForPlayerByName != (Object)null)
			{
				val2 = BodyCatalog.FindBodyIndex(availibleSurvivorForPlayerByName.bodyPrefab);
				text2 = Language.GetString(availibleSurvivorForPlayerByName.displayNameToken);
			}
			else
			{
				if (!DropInConfig.AllowJoinAsAllBodies.Value)
				{
					return "Unable to find survivor with the name " + text + ". Try /list_survivors to see the available names";
				}
				BodyIndex val3 = FindBodyIndexByName(text);
				if ((int)val3 == -1)
				{
					return "Unable to find survivor or body with given name";
				}
				val2 = val3;
				text2 = BodyCatalog.GetBodyName(val3);
			}
			JoinAsResult joinAsResult;
			try
			{
				joinAsResult = SpawnPlayerWithBody(val, val2);
			}
			catch (Exception ex)
			{
				Debug.LogException(ex);
				Logger.LogError((object)ex);
				return "An exception occured spawning " + val.userName + " as " + text2;
			}
			return joinAsResult switch
			{
				JoinAsResult.Success => "Spawning " + val.userName + " as " + text2, 
				JoinAsResult.DeadAndNotAllowRespawn => val.userName + " will be spawned as " + text2 + " next stage", 
				_ => "Unknown join as result", 
			};
		}

		private static string ListSurvivors(NetworkUser sender, string[] args)
		{
			if (args.Length > 1)
			{
				return "list survivors requires 0 or 1 argument";
			}
			NetworkUser val = ((args.Length == 0) ? sender : GetNetUserFromString(args[0]));
			if ((Object)(object)val == (Object)null)
			{
				return "Unable to find player with given name";
			}
			return GetSurvivorChatListForPlayer(val);
		}

		private static string ListBodies(NetworkUser sender, string[] args)
		{
			if (args.Length != 0)
			{
				return "list bodies requires no arguments";
			}
			return string.Join(", ", BodyCatalog.allBodyPrefabs.Select((GameObject prefab) => ((Object)prefab).name));
		}

		private static string Transform(NetworkUser sender, string[] args)
		{
			if (args.Length > 2 || args.Length == 0)
			{
				return "transform requires 1 or 2 arguments";
			}
			NetworkUser val = ((args.Length == 1) ? sender : GetNetUserFromString(args[1]));
			if ((Object)(object)val == (Object)null)
			{
				return "Unable to find player with given name";
			}
			val.master.TransformBody(args[0]);
			return "Transformed " + sender.userName + " into " + args[0];
		}

		private static string Catchup(NetworkUser sender, string[] args)
		{
			if (args.Length != 0 && args.Length != 1)
			{
				return "Catchup requires 0 or 1 argument";
			}
			NetworkUser val = ((args.Length == 0) ? sender : GetNetUserFromString(args[0]));
			if ((Object)(object)val == (Object)null)
			{
				return "Unable to find player with given name";
			}
			GiveCatchUpItems(val);
			return "Gave " + val.userName + " catch up items";
		}

		private static string GiveRandomItems(NetworkUser sender, string[] args)
		{
			if (args.Length != 3 && args.Length != 4)
			{
				return "Give random items requires 3 or 4 arguments";
			}
			NetworkUser val = ((args.Length == 3) ? sender : GetNetUserFromString(args[3]));
			if ((Object)(object)val == (Object)null)
			{
				return "Unable to find player with given name";
			}
			object obj;
			if (val == null)
			{
				obj = null;
			}
			else
			{
				CharacterMaster master = val.master;
				obj = ((master != null) ? master.inventory : null);
			}
			if ((Object)obj == (Object)null)
			{
				return "Player has no inventory so cannot be given items";
			}
			int num;
			bool lunarEnabled;
			bool voidEnabled;
			try
			{
				num = int.Parse(args[0]);
				lunarEnabled = bool.Parse(args[1]);
				voidEnabled = bool.Parse(args[2]);
			}
			catch
			{
				return "Unable to parse arguments";
			}
			GiveRandomItems(val.master.inventory, num, lunarEnabled, voidEnabled);
			return $"Gave {val.userName} {num} random items";
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void CommandJoinAs(ConCommandArgs args)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Debug.Log((object)JoinAs(args.sender, args.userArgs.ToArray()));
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void CommandShowSurvivors(ConCommandArgs args)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Debug.Log((object)ListSurvivors(args.sender, args.userArgs.ToArray()));
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void CommandListBodies(ConCommandArgs args)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Debug.Log((object)ListBodies(args.sender, args.userArgs.ToArray()));
		}

		private static NetworkUser GetNetUserFromString(string playerString)
		{
			if (!string.IsNullOrWhiteSpace(playerString))
			{
				if (int.TryParse(playerString, out var result))
				{
					if (result < NetworkUser.readOnlyInstancesList.Count && result >= 0)
					{
						return NetworkUser.readOnlyInstancesList[result];
					}
					return null;
				}
				foreach (NetworkUser readOnlyInstances in NetworkUser.readOnlyInstancesList)
				{
					if (readOnlyInstances.userName.Replace(" ", "").Equals(playerString.Replace(" ", ""), StringComparison.InvariantCultureIgnoreCase))
					{
						return readOnlyInstances;
					}
				}
				return null;
			}
			return null;
		}

		public static SurvivorDef[] GetAvailibleSurvivorsForPlayer(NetworkUser player)
		{
			return SurvivorCatalog.allSurvivorDefs.Where(SurvivorIsUnlockedAndAvailable).ToArray();
			static bool SurvivorIsUnlockedAndAvailable(SurvivorDef survivorDef)
			{
				if (!DropInConfig.AllowJoinAsHiddenSurvivors.Value && survivorDef.hidden)
				{
					Logger.LogMessage((object)("Survivor " + survivorDef.cachedName + " is not availible because survivor is hidden and AllowJoinAsHiddenSurvivors is false"));
					return false;
				}
				return true;
			}
		}

		public static SurvivorDef GetAvailibleSurvivorForPlayerByName(NetworkUser player, string name)
		{
			SurvivorDef[] availibleSurvivorsForPlayer = GetAvailibleSurvivorsForPlayer(player);
			if (string.Equals(name, "random", StringComparison.InvariantCultureIgnoreCase))
			{
				return availibleSurvivorsForPlayer[_rand.Next(availibleSurvivorsForPlayer.Length)];
			}
			string normalizedName = SurvivorNameResolver.Normalize(name);
			return availibleSurvivorsForPlayer.Where(NameStringMatches).FirstOrDefault();
			bool NameStringMatches(SurvivorDef survivorDef)
			{
				return SurvivorNameResolver.Matches(survivorDef, normalizedName);
			}
		}

		private static BodyIndex FindBodyIndexByName(string name)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			BodyIndex val = BodyCatalog.FindBodyIndexCaseInsensitive(name.EndsWith("Body", StringComparison.InvariantCultureIgnoreCase) ? name : (name + "Body"));
			if ((int)val != -1)
			{
				return val;
			}
			string text = SurvivorNameResolver.Normalize(name);
			if (text.EndsWith("body", StringComparison.Ordinal))
			{
				text = text.Substring(0, text.Length - "body".Length);
			}
			if (text.Length != 0)
			{
				foreach (GameObject allBodyPrefab in BodyCatalog.allBodyPrefabs)
				{
					if (Object.op_Implicit((Object)(object)allBodyPrefab))
					{
						string text2 = SurvivorNameResolver.Normalize(((Object)allBodyPrefab).name);
						if (text2.EndsWith("body", StringComparison.Ordinal))
						{
							text2 = text2.Substring(0, text2.Length - "body".Length);
						}
						if (string.Equals(text2, text, StringComparison.Ordinal))
						{
							return BodyCatalog.FindBodyIndex(allBodyPrefab);
						}
					}
				}
				return (BodyIndex)(-1);
			}
			return (BodyIndex)(-1);
		}

		private static void GiveCatchUpItems(NetworkUser player)
		{
			List<PickupIndex> list = new List<PickupIndex>();
			list.AddRange(Run.instance.availableTier1DropList);
			list.AddRange(Run.instance.availableTier2DropList);
			list.AddRange(Run.instance.availableTier3DropList);
			list.AddRange(Run.instance.availableBossDropList);
			list.AddRange(Run.instance.availableVoidTier1DropList);
			list.AddRange(Run.instance.availableVoidTier2DropList);
			list.AddRange(Run.instance.availableVoidTier3DropList);
			list.AddRange(Run.instance.availableVoidBossDropList);
			list.AddRange(Run.instance.availableLunarItemDropList);
			Inventory[] source = (from netUser in NetworkUser.readOnlyInstancesList.Where(delegate(NetworkUser netUser)
				{
					if ((Object)(object)netUser != (Object)(object)player)
					{
						object obj;
						if (netUser == null)
						{
							obj = null;
						}
						else
						{
							CharacterMaster master = netUser.master;
							obj = ((master != null) ? master.inventory : null);
						}
						return (Object)obj != (Object)null;
					}
					return false;
				})
				select netUser.master.inventory).ToArray();
			ItemIndex[] countItemIndexes = list.Select((PickupIndex pickupIndex) => PickupCatalog.GetPickupDef(pickupIndex).itemIndex).ToArray();
			int num = (int)((IEnumerable<Inventory>)source).Average((Func<Inventory, int>)CountInventoryItems);
			int num2 = num - CountInventoryItems(player.master.inventory);
			Logger.LogMessage((object)$"On average other players have {num} items, giving {player.userName} {num2} items to match");
			GiveRandomItems(player.master.inventory, num2, DropInConfig.GiveCatchUpLunarItems.Value, DropInConfig.GiveCatchUpVoidItems.Value);
			int CountInventoryItems(Inventory inventory)
			{
				return countItemIndexes.Sum((ItemIndex itemIndex) => inventory.GetItemCountPermanent(itemIndex));
			}
		}

		private static void GiveRandomItems(Inventory inventory, int count, bool lunarEnabled, bool voidEnabled)
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			if (count <= 0)
			{
				return;
			}
			WeightedSelection<List<PickupIndex>> val = new WeightedSelection<List<PickupIndex>>(8);
			val.AddChoice(Run.instance.availableTier1DropList, 100f);
			val.AddChoice(Run.instance.availableTier2DropList, 60f);
			val.AddChoice(Run.instance.availableTier3DropList, 4f);
			if (lunarEnabled)
			{
				val.AddChoice(Run.instance.availableLunarItemDropList, 4f);
			}
			if (voidEnabled)
			{
				val.AddChoice(Run.instance.availableVoidTier1DropList, 4f);
				val.AddChoice(Run.instance.availableVoidTier2DropList, 2.3999999f);
				val.AddChoice(Run.instance.availableVoidTier3DropList, 0.16f);
			}
			for (int i = 0; i < count; i++)
			{
				try
				{
					List<PickupIndex> list = val.Evaluate(Random.value);
					PickupDef pickupDef = PickupCatalog.GetPickupDef(list.ElementAtOrDefault(Random.Range(0, list.Count)));
					ItemIndex val2 = (ItemIndex)((pickupDef == null) ? (-1) : ((int)pickupDef.itemIndex));
					inventory.GiveItemPermanent(val2, 1);
				}
				catch (Exception ex)
				{
					Debug.LogException(ex);
					Logger.LogError((object)ex);
					Logger.LogMessage((object)"Exception occured giving player an item");
				}
			}
		}

		private static Transform GetSpawnTransformForPlayer(NetworkUser player)
		{
			CharacterBody currentBody = player.GetCurrentBody();
			Transform val = ((currentBody != null) ? ((Component)currentBody).transform : null);
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogMessage((object)(player.userName + " does not have a current body, spawning on another player"));
				NetworkUser? obj = NetworkUser.readOnlyInstancesList.Where((NetworkUser user) => (Object)(object)user.GetCurrentBody() != (Object)null).FirstOrDefault();
				val = ((obj != null) ? ((Component)obj.GetCurrentBody()).transform : null);
			}
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogMessage((object)("Unable to find alive player for " + player.userName + " to spawn on, defaulting to map spawn"));
				val = Stage.instance.GetPlayerSpawnTransform();
			}
			return val;
		}

		private static IEnumerator SpawnPlayerAsRandomInternal(NetworkUser player)
		{
			yield return (object)new WaitForSeconds(0.1f);
			SpawnPlayerWithBody(player, BodyCatalog.FindBodyIndex(GetAvailibleSurvivorForPlayerByName(player, "random").bodyPrefab));
		}

		private static string GetSurvivorChatListForPlayer(NetworkUser player)
		{
			string[] value = (from survivor in GetAvailibleSurvivorsForPlayer(player)
				select survivor.cachedName + " (" + Language.GetString(survivor.displayNameToken) + ")").ToArray();
			return string.Join(", ", value);
		}

		private static void GreetNewPlayer(NetworkUser player)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			if (DropInConfig.SendWelcomeMessage.Value)
			{
				string value = DropInConfig.CustomWelcomeMessage.Value;
				if (value.Length > 1000)
				{
					Logger.LogMessage((object)$"The custom welcome message has a length of {value.Length} which is longer than the limit of 1000 characters");
					return;
				}
				string baseToken = value.ReplaceOnce("{username}", player.userName).ReplaceOnce("{survivorlist}", GetSurvivorChatListForPlayer(player));
				Chat.SendBroadcastChat((ChatMessageBase)new SimpleChatMessage
				{
					baseToken = baseToken
				});
			}
		}

		private static void SendChatMessage(string message)
		{
			((MonoBehaviour)Instance).StartCoroutine(SendChatMessageInternal(message));
		}

		private static IEnumerator SendChatMessageInternal(string message)
		{
			yield return (object)new WaitForSeconds(0.1f);
			Chat.SendBroadcastChat((ChatMessageBase)new SimpleChatMessage
			{
				baseToken = message
			});
		}
	}
}
namespace DropInMultiplayer.Helpers
{
	internal class BodyManager
	{
		private readonly Run _run;

		private readonly DropInMultiplayerConfig _config;

		private static Random _rand = new Random();

		internal BodyManager(DropInMultiplayerConfig config, Run run)
		{
			_config = config;
			_run = run;
		}

		public SurvivorDef[] GetAvailibleSurvivorsForPlayer(NetworkUser player)
		{
			return SurvivorCatalog.allSurvivorDefs.Where(SurvivorIsUnlockedAndAvailable).ToArray();
			bool SurvivorIsUnlockedAndAvailable(SurvivorDef survivorDef)
			{
				if (_config.AllowJoinAsHiddenSurvivors.Value || !survivorDef.hidden)
				{
					if (!survivorDef.CheckRequiredExpansionEnabled((NetworkUser)null))
					{
						return false;
					}
					UnlockableDef unlockableDef = survivorDef.unlockableDef;
					if ((Object)(object)unlockableDef != (Object)null)
					{
						return _run.IsUnlockableUnlocked(unlockableDef);
					}
					return false;
				}
				return false;
			}
		}

		public SurvivorDef GetAvailibleSurvivorForPlayerByName(NetworkUser player, string name)
		{
			SurvivorDef[] availibleSurvivorsForPlayer = GetAvailibleSurvivorsForPlayer(player);
			if (string.Equals(name, "random", StringComparison.InvariantCultureIgnoreCase))
			{
				return availibleSurvivorsForPlayer[_rand.Next(availibleSurvivorsForPlayer.Length)];
			}
			return availibleSurvivorsForPlayer.Where(NameStringMatches).FirstOrDefault();
			bool NameStringMatches(SurvivorDef survivorDef)
			{
				return SurvivorNameResolver.Matches(survivorDef, SurvivorNameResolver.Normalize(name));
			}
		}
	}
	public static class HelperExtensions
	{
		public static void SetPrivateFieldValue(this object instance, string fieldName, object value)
		{
			instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic).SetValue(instance, value);
		}

		public static string ReplaceOnce(this string source, string replacetoken, string replacewith)
		{
			int num = source.IndexOf(replacetoken);
			if (num > -1)
			{
				return source.Substring(0, num) + replacewith + source.Substring(num + replacetoken.Length);
			}
			return source;
		}
	}
	internal static class SurvivorNameResolver
	{
		private static readonly Regex RichTextTagRegex = new Regex("<[^>]+>", RegexOptions.Compiled);

		private static readonly Dictionary<SurvivorDef, string[]> NameCache = new Dictionary<SurvivorDef, string[]>();

		private static readonly Dictionary<string, string[]> ExtraAliases = new Dictionary<string, string[]>
		{
			{
				"engi",
				new string[1] { "engineer" }
			},
			{
				"toolbot",
				new string[1] { "mult" }
			},
			{
				"treebot",
				new string[1] { "rex" }
			},
			{
				"voidsurvivor",
				new string[1] { "voidfiend" }
			},
			{
				"dronetech",
				new string[1] { "operator" }
			},
			{
				"enforcer2",
				new string[1] { "enforcer" }
			}
		};

		internal static bool PreloadComplete { get; private set; }

		internal static string Normalize(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return string.Empty;
			}
			string text = RichTextTagRegex.Replace(value, string.Empty);
			string text2;
			try
			{
				text2 = text.Normalize(NormalizationForm.FormKC);
			}
			catch (ArgumentException)
			{
				text2 = text;
			}
			StringBuilder stringBuilder = new StringBuilder(text2.Length);
			string text3 = text2;
			foreach (char c in text3)
			{
				if (char.IsLetterOrDigit(c))
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
			}
			return stringBuilder.ToString();
		}

		internal static bool Matches(SurvivorDef survivorDef, string normalizedQuery)
		{
			if ((Object)(object)survivorDef == (Object)null || string.IsNullOrEmpty(normalizedQuery))
			{
				return false;
			}
			return GetNames(survivorDef).Contains(normalizedQuery);
		}

		internal static IEnumerator PreloadLanguageStrings()
		{
			while (!Language.GetAllLanguages().Any())
			{
				yield return null;
			}
			Language[] array = Language.GetAllLanguages().ToArray();
			foreach (Language val in array)
			{
				if (val != null && !val.stringsLoaded)
				{
					try
					{
						val.LoadStrings();
					}
					catch (Exception arg)
					{
						Debug.LogWarning((object)$"DropInMultiplayer: failed to preload language {val.name}: {arg}");
					}
					yield return null;
				}
			}
			PreloadComplete = true;
		}

		private static string[] GetNames(SurvivorDef survivorDef)
		{
			if (NameCache.TryGetValue(survivorDef, out var value))
			{
				return value;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			AddName(hashSet, survivorDef.cachedName);
			GameObject bodyPrefab = survivorDef.bodyPrefab;
			if (Object.op_Implicit((Object)(object)bodyPrefab))
			{
				AddName(hashSet, ((Object)bodyPrefab).name);
				if (((Object)bodyPrefab).name.EndsWith("Body", StringComparison.OrdinalIgnoreCase))
				{
					AddName(hashSet, ((Object)bodyPrefab).name.Substring(0, ((Object)bodyPrefab).name.Length - "Body".Length));
				}
			}
			string displayNameToken = survivorDef.displayNameToken;
			if (!string.IsNullOrEmpty(displayNameToken))
			{
				AddName(hashSet, Language.GetString(displayNameToken));
				AddName(hashSet, Language.GetString(displayNameToken, "en"));
				foreach (Language allLanguage in Language.GetAllLanguages())
				{
					if (allLanguage != null && !string.IsNullOrEmpty(allLanguage.name))
					{
						AddName(hashSet, Language.GetString(displayNameToken, allLanguage.name));
					}
				}
			}
			if (ExtraAliases.TryGetValue(Normalize(survivorDef.cachedName), out var value2))
			{
				string[] array = value2;
				foreach (string value3 in array)
				{
					AddName(hashSet, value3);
				}
			}
			string[] array2 = hashSet.ToArray();
			if (PreloadComplete)
			{
				NameCache[survivorDef] = array2;
			}
			return array2;
		}

		private static void AddName(HashSet<string> names, string value)
		{
			string text = Normalize(value);
			if (text.Length > 0)
			{
				names.Add(text);
			}
		}
	}
}