Decompiled source of DropinMultiplayer v4.0.0

DropInMultiplayer.dll

Decompiled 2 months 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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DropInMultiplayer.Helpers;
using HG.Reflection;
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("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.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> 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!");
			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 lunar 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", "3.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class DropInMultiplayer : BaseUnityPlugin
	{
		public const string PluginGUID = "com.niwith.DropInMultiplayer";

		public const string PluginName = "Drop In Multiplayer";

		public const string PluginVersion = "3.0.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);

		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();
		}

		private static void SetupEventHandlers()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			Console.RunCmd += new hook_RunCmd(Console_RunCmd);
			Run.SetupUserCharacterMaster += new hook_SetupUserCharacterMaster(Run_SetupUserCharacterMaster);
			NetworkUser.onPostNetworkUserStart += new NetworkUserGenericDelegate(NetworkUser_onPostNetworkUserStart);
		}

		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_009b: 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("/"))
			{
				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");
				}
				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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: 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)
			GameObject bodyPrefab = player.master.bodyPrefab;
			player.CmdSetBodyPreference(newBodyIndex);
			JoinAsResult result = JoinAsResult.Success;
			if ((Object)(object)player.GetCurrentBody() == (Object)null && player.master.lostBodyToDeath && !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
			{
				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;
				if (text == "CaptainBody")
				{
					inventory.RemoveItem(Items.CaptainDefenseMatrix, 1);
				}
				if (text == "HereticBody")
				{
					inventory.RemoveItem(Items.LunarPrimaryReplacement, 1);
					inventory.RemoveItem(Items.LunarSecondaryReplacement, 1);
					inventory.RemoveItem(Items.LunarSpecialReplacement, 1);
					inventory.RemoveItem(Items.LunarUtilityReplacement, 1);
				}
				if (text2 == "CaptainBody")
				{
					inventory.GiveItem(Items.CaptainDefenseMatrix, 1);
				}
				if (text2 == "HereticBody")
				{
					inventory.GiveItem(Items.LunarPrimaryReplacement, 1);
					inventory.GiveItem(Items.LunarSecondaryReplacement, 1);
					inventory.GiveItem(Items.LunarSpecialReplacement, 1);
					inventory.GiveItem(Items.LunarUtilityReplacement, 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_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Invalid comparison between Unknown and I4
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: 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)
			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 given name";
				}
				BodyIndex val3 = BodyCatalog.FindBodyIndexCaseInsensitive(text.EndsWith("Body", StringComparison.InvariantCultureIgnoreCase) ? text : (text + "Body"));
				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)];
			}
			return availibleSurvivorsForPlayer.Where(NameStringMatches).FirstOrDefault();
			bool NameStringMatches(SurvivorDef survivorDef)
			{
				if (!string.Equals(survivorDef.cachedName, name, StringComparison.InvariantCultureIgnoreCase))
				{
					return string.Equals(Language.GetString(survivorDef.displayNameToken).Replace(" ", ""), name.Replace(" ", ""), StringComparison.InvariantCultureIgnoreCase);
				}
				return true;
			}
		}

		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.GetItemCount(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.GiveItem(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 string.Equals(survivorDef.cachedName, 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;
		}
	}
}