Decompiled source of InvasionMod v0.9.5

InvasionMod.Core.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using InvasionMod.Core.Application.Ports;
using InvasionMod.Core.Domain.Auditing;
using InvasionMod.Core.Domain.Discovery;
using InvasionMod.Core.Domain.Inventory;
using InvasionMod.Core.Domain.Mods;
using InvasionMod.Core.Domain.Net;
using InvasionMod.Core.Domain.Progression;
using InvasionMod.Core.Domain.Rewards;
using InvasionMod.Core.Domain.Spawn;
using InvasionMod.Core.Domain.Stash;
using InvasionMod.Core.Domain.Telemetry;
using InvasionMod.Core.Domain.Tiers;
using InvasionMod.Core.Domain.Travel;
using InvasionMod.Core.Domain.Visit;
using InvasionMod.Core.Domain.World;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("InvasionMod.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ca9fe9e02e2084916b1d40cb37a0ed04836b5f9d")]
[assembly: AssemblyProduct("InvasionMod.Core")]
[assembly: AssemblyTitle("InvasionMod.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[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 InvasionMod.Core.Domain.World
{
	public sealed class BossProgress
	{
		public string Prefab { get; }

		public int Tier { get; }

		public string VanillaKey { get; }

		public bool NeedsModKey => VanillaKey == null;

		public BossProgress(string prefab, int tier, string vanillaKey)
		{
			Prefab = prefab ?? throw new ArgumentNullException("prefab");
			Tier = tier;
			VanillaKey = vanillaKey;
		}
	}
	public interface IWorldDefeats
	{
		bool IsDefeated(BossProgress boss);

		void Record(BossProgress boss);
	}
	public static class BossTable
	{
		public const string ModKeyPrefix = "invasionmod_boss_";

		public static string ModKeyName(string bossPrefab)
		{
			return "invasionmod_boss_" + (bossPrefab ?? string.Empty).ToLowerInvariant();
		}

		public static BossProgress[] CreateDefault()
		{
			return new BossProgress[7]
			{
				new BossProgress("Eikthyr", 1, "defeated_eikthyr"),
				new BossProgress("gd_king", 2, "defeated_gdking"),
				new BossProgress("Bonemass", 3, "defeated_bonemass"),
				new BossProgress("Dragon", 4, "defeated_dragon"),
				new BossProgress("GoblinKing", 5, "defeated_goblinking"),
				new BossProgress("SeekerQueen", 6, "defeated_queen"),
				new BossProgress("Fader", 7, "defeated_fader")
			};
		}
	}
	public static class CreativeModifiers
	{
		public const string MarkKey = "invasionmod_creative_used";

		public const string HostReason = "This world used hammer or creative mode (free building or crafting), so it cannot be opened to invasions.";

		public const string VisitorReason = "used hammer or creative mode (free building or crafting).";

		private static readonly string[] Names = new string[5] { "NoBuildCost", "NoCraftCost", "AllPiecesUnlocked", "AllRecipesUnlocked", "NoWorkbench" };

		public static string FirstActive(IWorldModifiers world)
		{
			if (world == null)
			{
				return null;
			}
			string[] names = Names;
			foreach (string text in names)
			{
				if (world.IsActive(text))
				{
					return text;
				}
			}
			return null;
		}
	}
	public interface IWorldModifiers
	{
		bool IsActive(string modifier);

		bool IsMarked(string key);

		void Mark(string key);
	}
	public sealed class WorldTierCalculator
	{
		private readonly BossProgress[] _bosses;

		public IReadOnlyList<BossProgress> Bosses => _bosses;

		public WorldTierCalculator(IEnumerable<BossProgress> bosses)
		{
			if (bosses == null)
			{
				throw new ArgumentNullException("bosses");
			}
			_bosses = new List<BossProgress>(bosses).ToArray();
		}

		public int ComputeTier(IWorldDefeats defeats)
		{
			if (defeats == null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < _bosses.Length; i++)
			{
				BossProgress bossProgress = _bosses[i];
				if (bossProgress.Tier > num && defeats.IsDefeated(bossProgress))
				{
					num = bossProgress.Tier;
				}
			}
			return num;
		}

		public BossProgress FindByPrefab(string prefab)
		{
			if (string.IsNullOrEmpty(prefab))
			{
				return null;
			}
			for (int i = 0; i < _bosses.Length; i++)
			{
				if (string.Equals(_bosses[i].Prefab, prefab, StringComparison.Ordinal))
				{
					return _bosses[i];
				}
			}
			return null;
		}

		public List<BossProgress> Defeated(IWorldDefeats defeats)
		{
			List<BossProgress> list = new List<BossProgress>();
			if (defeats == null)
			{
				return list;
			}
			for (int i = 0; i < _bosses.Length; i++)
			{
				if (defeats.IsDefeated(_bosses[i]))
				{
					list.Add(_bosses[i]);
				}
			}
			return list;
		}

		public List<BossProgress> UnreadableByGame(IWorldDefeats defeats)
		{
			List<BossProgress> list = new List<BossProgress>();
			if (defeats == null)
			{
				return list;
			}
			for (int i = 0; i < _bosses.Length; i++)
			{
				if (_bosses[i].NeedsModKey && !defeats.IsDefeated(_bosses[i]))
				{
					list.Add(_bosses[i]);
				}
			}
			return list;
		}
	}
}
namespace InvasionMod.Core.Domain.Visit
{
	public sealed class InvasionStatus
	{
		public static readonly InvasionStatus Idle = new InvasionStatus(inProgress: false, forcesPvp: false, InvasionCause.None, null, allowsPanel: true);

		private static readonly InvasionStatus Travelling = new InvasionStatus(inProgress: true, forcesPvp: false, InvasionCause.Travelling, "A trip is already in progress. Wait for it to finish before looking for another world.");

		private static readonly InvasionStatus Invading = new InvasionStatus(inProgress: true, forcesPvp: true, InvasionCause.Invading, "You are already invading another world. Finish this invasion before starting another.");

		private const string VisitingReason = "Only the host can use the invasion panel.";

		private static readonly InvasionStatus Visiting = new InvasionStatus(inProgress: true, forcesPvp: false, InvasionCause.Visiting, "Only the host can use the invasion panel.");

		private static readonly InvasionStatus VisitingInvaded = new InvasionStatus(inProgress: true, forcesPvp: true, InvasionCause.Visiting, "Only the host can use the invasion panel.");

		private static readonly InvasionStatus BeingInvaded = new InvasionStatus(inProgress: true, forcesPvp: true, InvasionCause.BeingInvaded, "Your world is being invaded. You cannot leave while someone is inside.");

		private static readonly InvasionStatus Hosting = new InvasionStatus(inProgress: true, forcesPvp: false, InvasionCause.Hosting, "There are players in your world. You cannot invade while someone is inside.", allowsPanel: true);

		public bool InProgress { get; }

		public bool ForcesPvp { get; }

		public InvasionCause Cause { get; }

		public string BlockReason { get; }

		public bool AllowsPanel { get; }

		public string PanelBlockReason
		{
			get
			{
				if (!AllowsPanel)
				{
					return BlockReason;
				}
				return null;
			}
		}

		private InvasionStatus(bool inProgress, bool forcesPvp, InvasionCause cause, string blockReason, bool allowsPanel = false)
		{
			InProgress = inProgress;
			ForcesPvp = forcesPvp;
			Cause = cause;
			BlockReason = blockReason;
			AllowsPanel = allowsPanel;
		}

		public static InvasionStatus Evaluate(VisitSession session, ISessionContext context)
		{
			if (session == null || context == null)
			{
				return Idle;
			}
			if (context.TravelPending)
			{
				return Travelling;
			}
			if (session.InForeignWorld)
			{
				if (session.IsInvader)
				{
					return Invading;
				}
				if (!session.WorldInvaded)
				{
					return Visiting;
				}
				return VisitingInvaded;
			}
			if (!context.IsHost)
			{
				return Idle;
			}
			if (session.InvaderCount > 0)
			{
				return BeingInvaded;
			}
			if (context.RemotePeerCount <= 0)
			{
				return Idle;
			}
			return Hosting;
		}
	}
	public enum InvasionCause
	{
		None,
		Travelling,
		Invading,
		BeingInvaded,
		Visiting,
		Hosting
	}
	public interface ISessionContext
	{
		bool IsHost { get; }

		int RemotePeerCount { get; }

		bool TravelPending { get; }
	}
	public sealed class VisitSession
	{
		private readonly HashSet<long> _invaders = new HashSet<long>();

		private bool _pvpManual;

		private int _worldTier;

		private int _ownTier = 7;

		public bool WorldOpen { get; set; }

		public bool InForeignWorld { get; private set; }

		public bool IsInvader { get; private set; }

		public int Tier
		{
			get
			{
				if (!IsInvader || _ownTier >= _worldTier)
				{
					return _worldTier;
				}
				return _ownTier;
			}
		}

		public HostSkills HostSkills { get; private set; } = HostSkills.Unknown;

		public bool PvpManual
		{
			get
			{
				return _pvpManual;
			}
			set
			{
				_pvpManual = value;
			}
		}

		public int InvaderCount => _invaders.Count;

		public bool WorldInvaded { get; private set; }

		public void EnterForeignWorld(int tier, HostSkills hostSkills = null)
		{
			InForeignWorld = true;
			_worldTier = TierScale.Clamp(tier);
			HostSkills = hostSkills ?? HostSkills.Unknown;
		}

		public bool IsInvaderPeer(long peer)
		{
			return _invaders.Contains(peer);
		}

		public bool AddInvader(long peer)
		{
			return _invaders.Add(peer);
		}

		public bool RemoveInvader(long peer)
		{
			return _invaders.Remove(peer);
		}

		public int ClearInvaders()
		{
			int count = _invaders.Count;
			_invaders.Clear();
			return count;
		}

		public void SetWorldInvaded(bool invaded)
		{
			WorldInvaded = invaded;
		}

		public void MarkInvader(int ownTier = 7)
		{
			IsInvader = true;
			_ownTier = TierScale.Clamp(ownTier);
		}

		public void Reset()
		{
			WorldOpen = false;
			InForeignWorld = false;
			IsInvader = false;
			WorldInvaded = false;
			_invaders.Clear();
			_worldTier = 0;
			_ownTier = 7;
			HostSkills = HostSkills.Unknown;
			_pvpManual = false;
		}

		public bool IsPvpForced(InvasionStatus invasion)
		{
			if (!_pvpManual)
			{
				return invasion?.ForcesPvp ?? false;
			}
			return true;
		}
	}
}
namespace InvasionMod.Core.Domain.Travel
{
	public static class NetworkVersionRule
	{
		public static string Mismatch(int host, int local)
		{
			if (host == 0 || host == local)
			{
				return null;
			}
			if (host <= local)
			{
				return "the host's world is on an older game version than yours";
			}
			return "the host's world is on a newer game version than yours";
		}
	}
	public sealed class PasswordBroker
	{
		private string _pending;

		public void Arm(string password)
		{
			_pending = password;
		}

		public string Take()
		{
			string pending = _pending;
			_pending = null;
			return pending;
		}

		public void Clear()
		{
			_pending = null;
		}
	}
}
namespace InvasionMod.Core.Domain.Tiers
{
	public interface IRecipeIndex
	{
		IReadOnlyList<string> IngredientsOf(string outputName);
	}
	public sealed class ItemKey
	{
		private const string KeyPrefix = "$";

		private static readonly string[] PrefixesToStrip = new string[2] { "item_", "recipe_" };

		private char[] _buffer = new char[128];

		public string Normalize(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return string.Empty;
			}
			int startIndex = 0;
			if (name[0] == "$"[0])
			{
				startIndex = 1;
			}
			string text = name.Substring(startIndex);
			for (int i = 0; i < PrefixesToStrip.Length; i++)
			{
				string text2 = PrefixesToStrip[i];
				if (text.Length > text2.Length && text.StartsWith(text2, StringComparison.OrdinalIgnoreCase))
				{
					text = text.Substring(text2.Length);
					break;
				}
			}
			int length = text.Length;
			if (_buffer.Length < length)
			{
				_buffer = new char[length];
			}
			char[] buffer = _buffer;
			int num = 0;
			for (int j = 0; j < length; j++)
			{
				char c = text[j];
				if (char.IsLetterOrDigit(c))
				{
					buffer[num++] = char.ToLowerInvariant(c);
				}
			}
			if (num != 0)
			{
				return new string(buffer, 0, num);
			}
			return string.Empty;
		}
	}
	public interface ITierResolver
	{
		int Resolve(string itemName);
	}
	public sealed class TierResolver : ITierResolver
	{
		public const int MaxRecipeDepth = 4;

		private readonly TierTable _table;

		private readonly ItemKey _key;

		private readonly IRecipeIndex _recipes;

		private readonly Dictionary<string, int> _cache = new Dictionary<string, int>(StringComparer.Ordinal);

		private readonly HashSet<string> _visiting = new HashSet<string>(StringComparer.Ordinal);

		public TierResolver(TierTable table, ItemKey key, IRecipeIndex recipes)
		{
			_table = table ?? throw new ArgumentNullException("table");
			_key = key ?? throw new ArgumentNullException("key");
			_recipes = recipes;
		}

		public void Invalidate()
		{
			_cache.Clear();
			_visiting.Clear();
		}

		public int Resolve(string itemName)
		{
			if (string.IsNullOrEmpty(itemName))
			{
				return 0;
			}
			if (_cache.TryGetValue(itemName, out var value))
			{
				return value;
			}
			int num = Compute(itemName, 0);
			_cache[itemName] = num;
			return num;
		}

		private int Compute(string itemName, int depth)
		{
			if (_table.TryGetTier(itemName, out var tier))
			{
				return tier;
			}
			if (_recipes == null)
			{
				return 0;
			}
			return ResolveByRecipe(itemName, depth);
		}

		private int ResolveByRecipe(string name, int depth)
		{
			if (depth >= 4)
			{
				return 0;
			}
			IReadOnlyList<string> readOnlyList = _recipes.IngredientsOf(name);
			if (readOnlyList == null || readOnlyList.Count == 0)
			{
				return 0;
			}
			if (!_visiting.Add(name))
			{
				return 0;
			}
			try
			{
				int num = 0;
				for (int i = 0; i < readOnlyList.Count; i++)
				{
					string text = readOnlyList[i];
					if (string.IsNullOrEmpty(text))
					{
						continue;
					}
					if (depth != 0 || !_cache.TryGetValue(text, out var value))
					{
						value = Compute(text, depth + 1);
						if (depth == 0)
						{
							_cache[text] = value;
						}
					}
					if (value > num)
					{
						num = value;
					}
				}
				return num;
			}
			finally
			{
				_visiting.Remove(name);
			}
		}
	}
	public static class TierScale
	{
		public const int Wild = 0;

		public const int Eikthyr = 1;

		public const int Elder = 2;

		public const int Bonemass = 3;

		public const int Moder = 4;

		public const int Yagluth = 5;

		public const int Queen = 6;

		public const int Fader = 7;

		public const int Highest = 7;

		public const int Count = 8;

		private static readonly string[] Names = new string[8] { "Wild", "Eikthyr", "Elder", "Bonemass", "Moder", "Yagluth", "Queen", "Fader" };

		public static string Name(int tier)
		{
			if (tier < 0 || tier >= Names.Length)
			{
				return "?" + tier;
			}
			return Names[tier];
		}

		public static bool IsValid(int tier)
		{
			if (tier >= 0)
			{
				return tier < Names.Length;
			}
			return false;
		}

		public static int Clamp(int tier)
		{
			if (tier < 0)
			{
				return 0;
			}
			if (tier <= 7)
			{
				return tier;
			}
			return 7;
		}
	}
	public sealed class TierTable
	{
		private readonly ItemKey _key;

		private readonly Dictionary<string, int> _byKey;

		private readonly Dictionary<string, string> _originalByKey;

		private readonly Dictionary<string, string> _collisions;

		private readonly HashSet<string> _aliased = new HashSet<string>(StringComparer.Ordinal);

		public int Count => _byKey.Count;

		public IEnumerable<KeyValuePair<string, string>> Collisions => _collisions;

		public TierTable(IEnumerable<KeyValuePair<string, int>> entries, ItemKey key)
		{
			_key = key ?? throw new ArgumentNullException("key");
			_byKey = new Dictionary<string, int>(StringComparer.Ordinal);
			_originalByKey = new Dictionary<string, string>(StringComparer.Ordinal);
			_collisions = new Dictionary<string, string>(StringComparer.Ordinal);
			if (entries == null)
			{
				return;
			}
			foreach (KeyValuePair<string, int> entry in entries)
			{
				string key2 = _key.Normalize(entry.Key);
				if (_byKey.ContainsKey(key2))
				{
					_collisions[key2] = entry.Key;
					continue;
				}
				_byKey.Add(key2, entry.Value);
				_originalByKey.Add(key2, entry.Key);
			}
		}

		public bool TryGetTier(string itemName, out int tier)
		{
			if (string.IsNullOrEmpty(itemName))
			{
				tier = 0;
				return false;
			}
			return _byKey.TryGetValue(_key.Normalize(itemName), out tier);
		}

		public IReadOnlyList<string> MissingFrom(ISet<string> normalizedGameItems)
		{
			if (normalizedGameItems == null)
			{
				return Array.Empty<string>();
			}
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, string> item in _originalByKey)
			{
				if (!_aliased.Contains(item.Key) && !normalizedGameItems.Contains(item.Key))
				{
					list.Add(item.Value);
				}
			}
			return list;
		}

		public bool AddAlias(string tableName, string gameName)
		{
			if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(gameName))
			{
				return false;
			}
			string text = _key.Normalize(tableName);
			if (!_byKey.TryGetValue(text, out var value))
			{
				return false;
			}
			string text2 = _key.Normalize(gameName);
			if (text2.Length == 0 || _byKey.ContainsKey(text2))
			{
				return false;
			}
			_byKey.Add(text2, value);
			_aliased.Add(text);
			return true;
		}

		public static TierTable CreateDefault()
		{
			return CreateDefault(new ItemKey());
		}

		public static TierTable CreateDefault(ItemKey key)
		{
			return new TierTable(new KeyValuePair<string, int>[92]
			{
				new KeyValuePair<string, int>("HardAntler", 1),
				new KeyValuePair<string, int>("CopperOre", 1),
				new KeyValuePair<string, int>("Copper", 1),
				new KeyValuePair<string, int>("CopperScrap", 1),
				new KeyValuePair<string, int>("TinOre", 1),
				new KeyValuePair<string, int>("Tin", 1),
				new KeyValuePair<string, int>("Bronze", 1),
				new KeyValuePair<string, int>("BronzeNails", 1),
				new KeyValuePair<string, int>("BronzeScrap", 1),
				new KeyValuePair<string, int>("FineWood", 1),
				new KeyValuePair<string, int>("CryptKey", 2),
				new KeyValuePair<string, int>("IronScrap", 2),
				new KeyValuePair<string, int>("IronOre", 2),
				new KeyValuePair<string, int>("Iron", 2),
				new KeyValuePair<string, int>("IronNails", 2),
				new KeyValuePair<string, int>("Chain", 2),
				new KeyValuePair<string, int>("ElderBark", 2),
				new KeyValuePair<string, int>("Ooze", 2),
				new KeyValuePair<string, int>("Entrails", 2),
				new KeyValuePair<string, int>("Guck", 2),
				new KeyValuePair<string, int>("Bloodbag", 2),
				new KeyValuePair<string, int>("Witheredbone", 2),
				new KeyValuePair<string, int>("Wishbone", 3),
				new KeyValuePair<string, int>("SilverOre", 3),
				new KeyValuePair<string, int>("Silver", 3),
				new KeyValuePair<string, int>("WolfPelt", 3),
				new KeyValuePair<string, int>("WolfFang", 3),
				new KeyValuePair<string, int>("WolfHairBundle", 3),
				new KeyValuePair<string, int>("Obsidian", 3),
				new KeyValuePair<string, int>("FreezeGland", 3),
				new KeyValuePair<string, int>("Crystal", 3),
				new KeyValuePair<string, int>("DragonEgg", 3),
				new KeyValuePair<string, int>("DragonTear", 4),
				new KeyValuePair<string, int>("BlackMetalScrap", 4),
				new KeyValuePair<string, int>("BlackMetal", 4),
				new KeyValuePair<string, int>("LoxPelt", 4),
				new KeyValuePair<string, int>("LoxMeat", 4),
				new KeyValuePair<string, int>("LoxMeat_Cooked", 4),
				new KeyValuePair<string, int>("Needle", 4),
				new KeyValuePair<string, int>("Flax", 4),
				new KeyValuePair<string, int>("LinenThread", 4),
				new KeyValuePair<string, int>("Barley", 4),
				new KeyValuePair<string, int>("BarleyFlour", 4),
				new KeyValuePair<string, int>("Cloudberries", 4),
				new KeyValuePair<string, int>("GoblinTotem", 4),
				new KeyValuePair<string, int>("YagluthThing", 5),
				new KeyValuePair<string, int>("Wisp", 5),
				new KeyValuePair<string, int>("Sap", 5),
				new KeyValuePair<string, int>("Eitr", 5),
				new KeyValuePair<string, int>("SoftTissue", 5),
				new KeyValuePair<string, int>("Carapace", 5),
				new KeyValuePair<string, int>("Mandible", 5),
				new KeyValuePair<string, int>("Bilebag", 5),
				new KeyValuePair<string, int>("BlackCore", 5),
				new KeyValuePair<string, int>("YggdrasilWood", 5),
				new KeyValuePair<string, int>("BlackMarble", 5),
				new KeyValuePair<string, int>("RoyalJelly", 5),
				new KeyValuePair<string, int>("JotunPuffs", 5),
				new KeyValuePair<string, int>("Magecap", 5),
				new KeyValuePair<string, int>("Bug_Meat", 5),
				new KeyValuePair<string, int>("Bug_Meat_Cooked", 5),
				new KeyValuePair<string, int>("Hare_Meat", 5),
				new KeyValuePair<string, int>("Hare_Meat_Cooked", 5),
				new KeyValuePair<string, int>("Chicken_Meat", 5),
				new KeyValuePair<string, int>("Chicken_Meat_Cooked", 5),
				new KeyValuePair<string, int>("DvergrKeyFragment", 5),
				new KeyValuePair<string, int>("DvergrKey", 5),
				new KeyValuePair<string, int>("Queen_Drop", 6),
				new KeyValuePair<string, int>("FlametalOre", 6),
				new KeyValuePair<string, int>("Flametal", 6),
				new KeyValuePair<string, int>("CharredBone", 6),
				new KeyValuePair<string, int>("Grausten", 6),
				new KeyValuePair<string, int>("BlackWood", 6),
				new KeyValuePair<string, int>("AskHide", 6),
				new KeyValuePair<string, int>("AskBladder", 6),
				new KeyValuePair<string, int>("Asksvin_Meat", 6),
				new KeyValuePair<string, int>("Asksvin_Meat_Cooked", 6),
				new KeyValuePair<string, int>("MorgenSinew", 6),
				new KeyValuePair<string, int>("MorgenHeart", 6),
				new KeyValuePair<string, int>("CelestialFeather", 6),
				new KeyValuePair<string, int>("MoltenCore", 6),
				new KeyValuePair<string, int>("SulfurStone", 6),
				new KeyValuePair<string, int>("BellFragment", 6),
				new KeyValuePair<string, int>("VoltureEgg", 6),
				new KeyValuePair<string, int>("ProustitePowder", 6),
				new KeyValuePair<string, int>("CharcoalResin", 6),
				new KeyValuePair<string, int>("BonemawScale", 6),
				new KeyValuePair<string, int>("BonemawTooth", 6),
				new KeyValuePair<string, int>("BonemawMeat", 6),
				new KeyValuePair<string, int>("BonemawMeat_Cooked", 6),
				new KeyValuePair<string, int>("Fader_Drop", 7),
				new KeyValuePair<string, int>("FaderEmber", 7)
			}, key ?? throw new ArgumentNullException("key"));
		}
	}
}
namespace InvasionMod.Core.Domain.Telemetry
{
	public static class FirestoreJson
	{
		public const string AnonymousSignUp = "{\"returnSecureToken\":true}";

		public static string Commit(string projectId, string uid, string session, string version, IReadOnlyList<TelemetryEntry> entries, Func<string> newId)
		{
			StringBuilder stringBuilder = new StringBuilder(256 + entries.Count * 384);
			stringBuilder.Append("{\"writes\":[");
			for (int i = 0; i < entries.Count; i++)
			{
				TelemetryEntry telemetryEntry = entries[i];
				if (i > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append("{\"update\":{\"name\":");
				AppendString(stringBuilder, "projects/" + projectId + "/databases/(default)/documents/logs/" + newId());
				stringBuilder.Append(",\"fields\":{");
				stringBuilder.Append("\"ts\":{\"timestampValue\":");
				AppendString(stringBuilder, Timestamp(telemetryEntry.TimestampUtc));
				stringBuilder.Append('}');
				AppendField(stringBuilder, "level", telemetryEntry.Level);
				if (!string.IsNullOrEmpty(telemetryEntry.EventName))
				{
					AppendField(stringBuilder, "event", telemetryEntry.EventName);
				}
				AppendField(stringBuilder, "msg", telemetryEntry.Message);
				AppendField(stringBuilder, "version", version);
				AppendField(stringBuilder, "session", session);
				AppendField(stringBuilder, "uid", uid);
				stringBuilder.Append("}},\"currentDocument\":{\"exists\":false}}");
			}
			stringBuilder.Append("]}");
			return stringBuilder.ToString();
		}

		public static string RefreshForm(string refreshToken)
		{
			return "grant_type=refresh_token&refresh_token=" + Uri.EscapeDataString(refreshToken ?? string.Empty);
		}

		public static string Timestamp(DateTime utc)
		{
			return utc.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture);
		}

		public static string ReadString(string json, string field)
		{
			if (string.IsNullOrEmpty(json) || string.IsNullOrEmpty(field))
			{
				return null;
			}
			string text = "\"" + field + "\"";
			int num = json.IndexOf(text, StringComparison.Ordinal);
			if (num < 0)
			{
				return null;
			}
			int num2 = json.IndexOf(':', num + text.Length);
			if (num2 < 0)
			{
				return null;
			}
			int num3 = json.IndexOf('"', num2 + 1);
			if (num3 < 0 || json.Substring(num2 + 1, num3 - num2 - 1).Trim().Length > 0)
			{
				return null;
			}
			int num4 = json.IndexOf('"', num3 + 1);
			if (num4 >= 0)
			{
				return json.Substring(num3 + 1, num4 - num3 - 1);
			}
			return null;
		}

		private static void AppendField(StringBuilder json, string name, string value)
		{
			json.Append(",\"").Append(name).Append("\":{\"stringValue\":");
			AppendString(json, value ?? string.Empty);
			json.Append('}');
		}

		private static void AppendString(StringBuilder json, string value)
		{
			json.Append('"');
			foreach (char c in value)
			{
				switch (c)
				{
				case '"':
					json.Append("\\\"");
					continue;
				case '\\':
					json.Append("\\\\");
					continue;
				case '\n':
					json.Append("\\n");
					continue;
				case '\r':
					json.Append("\\r");
					continue;
				case '\t':
					json.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder = json.Append("\\u");
					int num = c;
					stringBuilder.Append(num.ToString("x4", CultureInfo.InvariantCulture));
				}
				else
				{
					json.Append(c);
				}
			}
			json.Append('"');
		}
	}
	public sealed class TelemetryEntry
	{
		public const string Info = "info";

		public const string Error = "error";

		public DateTime TimestampUtc { get; }

		public string Level { get; }

		public string EventName { get; }

		public string Message { get; }

		public TelemetryEntry(DateTime timestampUtc, string level, string eventName, string message)
		{
			TimestampUtc = timestampUtc;
			Level = level ?? "info";
			EventName = eventName;
			Message = message ?? string.Empty;
		}
	}
	public static class TelemetryText
	{
		public const int MaxLength = 3900;

		private const string Cut = " [...]";

		public static string Scrub(string message, IReadOnlyList<string> secrets)
		{
			if (string.IsNullOrEmpty(message))
			{
				return string.Empty;
			}
			string text = message;
			if (secrets != null)
			{
				for (int i = 0; i < secrets.Count; i++)
				{
					string text2 = secrets[i];
					if (!string.IsNullOrEmpty(text2) && text2.Length >= 3)
					{
						text = Replace(text, text2, "~");
						text = Replace(text, text2.Replace('\\', '/'), "~");
					}
				}
			}
			if (text.Length > 3900)
			{
				return text.Substring(0, 3900 - " [...]".Length) + " [...]";
			}
			return text;
		}

		private static string Replace(string text, string find, string with)
		{
			int num = text.IndexOf(find, StringComparison.OrdinalIgnoreCase);
			if (num < 0)
			{
				return text;
			}
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			int num2 = 0;
			while (num >= 0)
			{
				stringBuilder.Append(text, num2, num - num2).Append(with);
				num2 = num + find.Length;
				num = text.IndexOf(find, num2, StringComparison.OrdinalIgnoreCase);
			}
			return stringBuilder.Append(text, num2, text.Length - num2).ToString();
		}
	}
}
namespace InvasionMod.Core.Domain.Stash
{
	public interface IStash
	{
		string Location { get; }

		int Count { get; }

		bool Append(IReadOnlyList<StashedItem> items);

		bool Replace(IReadOnlyList<StashedItem> items);

		IReadOnlyList<StashedItem> Load();

		void Clear();
	}
	public sealed class StashedItem
	{
		public string Name { get; set; }

		public int Stack { get; set; }

		public float Durability { get; set; }

		public int Quality { get; set; }

		public int Variant { get; set; }

		public int WorldLevel { get; set; }

		public long CrafterId { get; set; }

		public string CrafterName { get; set; }
	}
}
namespace InvasionMod.Core.Domain.Spawn
{
	public readonly struct SpawnPoint
	{
		public float X { get; }

		public float Y { get; }

		public float Z { get; }

		public SpawnPoint(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		public override string ToString()
		{
			return "(" + X.ToString("0") + ", " + Y.ToString("0") + ", " + Z.ToString("0") + ")";
		}
	}
	public readonly struct TerrainSample
	{
		public float Height { get; }

		public bool IsLava { get; }

		public bool IsObstructed { get; }

		public TerrainSample(float height, bool isLava, bool isObstructed)
		{
			Height = height;
			IsLava = isLava;
			IsObstructed = isObstructed;
		}
	}
	public interface ITerrainProbe
	{
		float WaterLevel { get; }

		bool TrySample(float x, float z, out TerrainSample sample);
	}
	public static class SpawnSiteRules
	{
		public const float MinDistance = 100f;

		public const float MaxDistance = 300f;

		public static readonly float[] FallbackRings = new float[5] { 200f, 150f, 250f, 100f, 300f };

		public const int AnglesPerRing = 36;

		public const float MinHeightAboveWater = 1f;

		public const float NeighborDistance = 2f;

		public const float MaxSlope = 0.7f;

		public const float MaxWorldRadius = 9800f;

		public const float NearestLandMaxDistance = 3000f;

		public const float NearestLandStep = 40f;

		public const float NearestLandArc = 25f;

		public const float NearestLandSpread = 200f;

		public static float ClampDistance(float distance)
		{
			if (!(distance < 100f))
			{
				if (!(distance > 300f))
				{
					return distance;
				}
				return 300f;
			}
			return 100f;
		}

		public static List<SpawnPoint> Candidates(ITerrainProbe terrain, float hostX, float hostZ, float startAngle, int max, float distance)
		{
			if (terrain == null)
			{
				throw new ArgumentNullException("terrain");
			}
			List<SpawnPoint> list = new List<SpawnPoint>();
			if (ScanRing(terrain, hostX, hostZ, ClampDistance(distance), 36, startAngle, list, max))
			{
				return list;
			}
			float[] fallbackRings = FallbackRings;
			foreach (float radius in fallbackRings)
			{
				if (ScanRing(terrain, hostX, hostZ, radius, 36, startAngle, list, max))
				{
					return list;
				}
			}
			if (list.Count == 0)
			{
				NearestLand(terrain, hostX, hostZ, startAngle, list, max);
			}
			return list;
		}

		private static void NearestLand(ITerrainProbe terrain, float hostX, float hostZ, float startAngle, List<SpawnPoint> found, int max)
		{
			float num = 3000f;
			for (float num2 = 40f; num2 <= num; num2 += 40f)
			{
				int angles = Math.Max(36, (int)Math.Ceiling(Math.PI * 2.0 * (double)num2 / 25.0));
				int count = found.Count;
				if (ScanRing(terrain, hostX, hostZ, num2, angles, startAngle, found, max))
				{
					break;
				}
				if (count == 0 && found.Count > 0)
				{
					num = Math.Min(num, num2 + 200f);
				}
			}
		}

		private static bool ScanRing(ITerrainProbe terrain, float hostX, float hostZ, float radius, int angles, float startAngle, List<SpawnPoint> found, int max)
		{
			double num = Math.PI * 2.0 / (double)angles;
			for (int i = 0; i < angles; i++)
			{
				double num2 = (double)startAngle + (double)i * num;
				float x = hostX + (float)(Math.Sin(num2) * (double)radius);
				float z = hostZ + (float)(Math.Cos(num2) * (double)radius);
				if (IsWalkable(terrain, x, z, out var height))
				{
					found.Add(new SpawnPoint(x, height, z));
					if (found.Count >= max)
					{
						return true;
					}
				}
			}
			return false;
		}

		public static bool IsWalkable(ITerrainProbe terrain, float x, float z, out float height)
		{
			height = 0f;
			if (x * x + z * z > 96040000f)
			{
				return false;
			}
			if (!terrain.TrySample(x, z, out var sample) || !IsDryGround(terrain, sample) || sample.IsObstructed)
			{
				return false;
			}
			if (!IsGentleNeighbor(terrain, sample, x + 2f, z) || !IsGentleNeighbor(terrain, sample, x - 2f, z) || !IsGentleNeighbor(terrain, sample, x, z + 2f) || !IsGentleNeighbor(terrain, sample, x, z - 2f))
			{
				return false;
			}
			height = sample.Height;
			return true;
		}

		private static bool IsDryGround(ITerrainProbe terrain, TerrainSample sample)
		{
			if (!sample.IsLava)
			{
				return sample.Height >= terrain.WaterLevel + 1f;
			}
			return false;
		}

		private static bool IsGentleNeighbor(ITerrainProbe terrain, TerrainSample center, float x, float z)
		{
			if (terrain.TrySample(x, z, out var sample) && IsDryGround(terrain, sample))
			{
				return Math.Abs(sample.Height - center.Height) <= 1.4f;
			}
			return false;
		}
	}
}
namespace InvasionMod.Core.Domain.Rewards
{
	public static class IdolTable
	{
		private static readonly string[] Materials = new string[8] { "wood", "bronze", "iron", "silver", "black metal", "black marble", "flametal", "blood gold" };

		public static string Prefab(int tier, bool battle)
		{
			return "Upgrader" + TierScale.Clamp(tier) + (battle ? "Weapon" : "Armor");
		}

		public static string DisplayName(int tier, bool battle)
		{
			return (battle ? "Battle idol" : "Protection idol") + " (" + Materials[TierScale.Clamp(tier)] + ")";
		}
	}
	public static class KillRewardTable
	{
		public const int StackSize = 50;

		private static readonly string[] Prefabs = new string[3] { "Raspberry", "Blueberries", "Resin" };

		private static readonly string[] Names = new string[3] { "raspberries", "blueberries", "resin" };

		public static int Count => Prefabs.Length;

		public static int Pick(double random)
		{
			int num = (int)(random * (double)Prefabs.Length);
			if (num >= 0)
			{
				if (num < Prefabs.Length)
				{
					return num;
				}
				return Prefabs.Length - 1;
			}
			return 0;
		}

		public static string Prefab(int index)
		{
			return Prefabs[index];
		}

		public static string DisplayName(int index)
		{
			return 50 + " " + Names[index];
		}
	}
}
namespace InvasionMod.Core.Domain.Progression
{
	public static class GuardianPowerTable
	{
		public static int TierOf(string powerName)
		{
			if (string.IsNullOrEmpty(powerName))
			{
				return 0;
			}
			return powerName.Trim().ToLowerInvariant() switch
			{
				"gp_eikthyr" => 1, 
				"gp_theelder" => 2, 
				"gp_bonemass" => 3, 
				"gp_moder" => 4, 
				"gp_yagluth" => 5, 
				"gp_queen" => 6, 
				"gp_fader" => 7, 
				_ => 7, 
			};
		}

		public static bool Allows(string powerName, int worldTier)
		{
			return TierOf(powerName) <= worldTier;
		}
	}
	public sealed class HostSkills
	{
		public const int DefaultMargin = 10;

		public const float MaxLevel = 100f;

		private readonly Dictionary<int, float> _levels;

		public static HostSkills Unknown { get; } = new HostSkills(null, 10);

		public int Margin { get; }

		public IReadOnlyDictionary<int, float> Levels => _levels;

		public HostSkills(IEnumerable<KeyValuePair<int, float>> levels, int margin)
		{
			_levels = new Dictionary<int, float>();
			if (levels != null)
			{
				foreach (KeyValuePair<int, float> level in levels)
				{
					_levels[level.Key] = level.Value;
				}
			}
			Margin = Math.Max(0, Math.Min(100, margin));
		}

		public float HostLevel(int skill)
		{
			if (!_levels.TryGetValue(skill, out var value))
			{
				return 0f;
			}
			return value;
		}

		public float CapFor(int skill)
		{
			float num = HostLevel(skill) + (float)Margin;
			if (!(num > 100f))
			{
				return num;
			}
			return 100f;
		}

		public float Cap(int skill, float level)
		{
			float num = CapFor(skill);
			if (!(level > num))
			{
				return level;
			}
			return num;
		}
	}
	public sealed class ProgressionGate
	{
		private readonly ITierResolver _tiers;

		public static int HighestAllowed => 7;

		public ProgressionGate(ITierResolver tiers)
		{
			_tiers = tiers;
		}

		public int TierOf(string itemName)
		{
			return _tiers.Resolve(itemName);
		}

		public bool Exceeds(string itemName, int worldTier)
		{
			return TierOf(itemName) > worldTier;
		}

		public bool Allows(string itemName, int worldTier)
		{
			return !Exceeds(itemName, worldTier);
		}
	}
}
namespace InvasionMod.Core.Domain.Password
{
	public static class WorldPasswordPolicy
	{
		public const int FallbackMinimumLength = 5;

		public static PasswordValidation Validate(string password, int minimumLength, string worldName, string worldSeedName)
		{
			if (string.IsNullOrEmpty(password))
			{
				return PasswordValidation.Invalid("la clave esta vacia");
			}
			if (password.Length < minimumLength)
			{
				return PasswordValidation.Invalid($"la clave tiene {password.Length} caracteres y Valheim pide {minimumLength} o mas");
			}
			if (!string.IsNullOrEmpty(worldName) && worldName.Contains(password))
			{
				return PasswordValidation.Invalid("la clave esta escrita dentro del nombre del mundo ('" + worldName + "'), y Valheim rechaza eso");
			}
			if (!string.IsNullOrEmpty(worldSeedName) && worldSeedName.Contains(password))
			{
				return PasswordValidation.Invalid("la clave esta escrita dentro de la semilla del mundo ('" + worldSeedName + "'), y Valheim rechaza eso");
			}
			return PasswordValidation.Valid;
		}
	}
	public readonly struct PasswordValidation
	{
		public static readonly PasswordValidation Valid = new PasswordValidation(isValid: true, null);

		public bool IsValid { get; }

		public string Error { get; }

		private PasswordValidation(bool isValid, string error)
		{
			IsValid = isValid;
			Error = error;
		}

		public static PasswordValidation Invalid(string error)
		{
			return new PasswordValidation(isValid: false, error);
		}
	}
}
namespace InvasionMod.Core.Domain.Net
{
	public enum GateAuthorityVerdict
	{
		Accepted,
		NotFromServer,
		LocalIsHost
	}
	public static class GateAuthority
	{
		public static GateAuthorityVerdict Decide(bool localIsHost, long sender, long serverPeer)
		{
			if (localIsHost)
			{
				return GateAuthorityVerdict.LocalIsHost;
			}
			if (sender != serverPeer)
			{
				return GateAuthorityVerdict.NotFromServer;
			}
			return GateAuthorityVerdict.Accepted;
		}

		public static string DescribeRejection(GateAuthorityVerdict verdict, long sender, long serverPeer)
		{
			if (verdict != GateAuthorityVerdict.NotFromServer)
			{
				return "Tope de progresion ignorado: esta maquina es el host, el tope lo decide el y no lo recibe.";
			}
			return "Tope de progresion ignorado: vino del peer " + sender + " y el host de este mundo es " + serverPeer + ".";
		}
	}
	public sealed class VisitorReport
	{
		public string ModVersion { get; }

		public string ModsFingerprint { get; }

		public int AppliedTier { get; }

		public int HighestItemTier { get; }

		public VisitorReport(string modVersion, string modsFingerprint, int appliedTier, int highestItemTier)
		{
			ModVersion = modVersion ?? string.Empty;
			ModsFingerprint = modsFingerprint ?? string.Empty;
			AppliedTier = appliedTier;
			HighestItemTier = highestItemTier;
		}
	}
	public static class VisitorVerification
	{
		public static string ExplainTimeout(float seconds)
		{
			return "your mod did not confirm the cap to the host within " + (int)seconds + " s. It may not be working: check the BepInEx log";
		}

		public static string Check(VisitorReport report, string hostVersion, string expectedFingerprint, int worldTier, bool invader = true)
		{
			return Evaluate(report, hostVersion, expectedFingerprint, worldTier, invader, forVisitor: false);
		}

		public static string Explain(VisitorReport report, string hostVersion, string expectedFingerprint, int worldTier, bool invader = true)
		{
			return Evaluate(report, hostVersion, expectedFingerprint, worldTier, invader, forVisitor: true);
		}

		public static bool HasAllowedMods(VisitorReport report, string expectedFingerprint)
		{
			if (report != null && !string.IsNullOrEmpty(report.ModsFingerprint))
			{
				return string.Equals(report.ModsFingerprint, expectedFingerprint, StringComparison.Ordinal);
			}
			return false;
		}

		private static string Evaluate(VisitorReport report, string hostVersion, string expectedFingerprint, int worldTier, bool invader, bool forVisitor)
		{
			if (report == null)
			{
				if (!forVisitor)
				{
					return "did not send the report";
				}
				return "your mod did not send the cap confirmation to the host";
			}
			if (!ModVersion.TryParse(report.ModVersion, out var version) || !ModVersion.TryParse(hostVersion, out var version2) || !version.IsSameMinorAs(version2))
			{
				if (!forVisitor)
				{
					return "has version " + report.ModVersion + " of the mod and the host has " + hostVersion;
				}
				return "you have version " + report.ModVersion + " of the mod and the host has " + hostVersion + ". Install the same version";
			}
			if (!HasAllowedMods(report, expectedFingerprint))
			{
				if (!forVisitor)
				{
					return "has other mods loaded besides InvasionMod and Jotunn (fingerprint " + report.ModsFingerprint + ")";
				}
				return "you have other mods loaded besides InvasionMod and Jotunn (fingerprint " + report.ModsFingerprint + "). Remove them to join";
			}
			if (!invader)
			{
				return null;
			}
			if (report.AppliedTier > worldTier)
			{
				if (!forVisitor)
				{
					return "claims the cap " + TierScale.Name(report.AppliedTier) + " and the world is " + TierScale.Name(worldTier);
				}
				return "your mod applied the cap " + TierScale.Name(report.AppliedTier) + " and this world is " + TierScale.Name(worldTier);
			}
			if (report.HighestItemTier > report.AppliedTier)
			{
				if (!forVisitor)
				{
					return "still has an item of rank " + TierScale.Name(report.HighestItemTier) + " with the cap " + TierScale.Name(report.AppliedTier) + ": the filter was not applied";
				}
				return "you still have an item of rank " + TierScale.Name(report.HighestItemTier) + " and this world allows up to " + TierScale.Name(report.AppliedTier) + ": the filter could not set it aside";
			}
			return null;
		}
	}
}
namespace InvasionMod.Core.Domain.Mods
{
	public static class ModFingerprint
	{
		public static string Compute(IEnumerable<string> thirdPartyGuids)
		{
			if (thirdPartyGuids == null)
			{
				return Fingerprint(string.Empty);
			}
			List<string> list = new List<string>(thirdPartyGuids);
			list.Sort(StringComparer.Ordinal);
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < list.Count; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append(list[i] ?? string.Empty);
			}
			return Fingerprint(stringBuilder.ToString());
		}

		public static string Fingerprint(string value)
		{
			uint num = 2166136261u;
			for (int i = 0; i < value.Length; i++)
			{
				num ^= value[i];
				num *= 16777619;
			}
			return num.ToString("x8");
		}

		public static string Describe(IReadOnlyCollection<string> hostGuids, IReadOnlyCollection<string> localGuids)
		{
			List<string> list = Except(hostGuids, localGuids);
			List<string> list2 = Except(localGuids, hostGuids);
			List<string> list3 = new List<string>(2);
			if (list.Count > 0)
			{
				list3.Add("only the host has: " + string.Join(", ", list));
			}
			if (list2.Count > 0)
			{
				list3.Add("only you have: " + string.Join(", ", list2));
			}
			if (list3.Count != 0)
			{
				return string.Join(" / ", list3);
			}
			return null;
		}

		private static List<string> Except(IReadOnlyCollection<string> source, IReadOnlyCollection<string> other)
		{
			List<string> list = new List<string>();
			if (source == null)
			{
				return list;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (other != null)
			{
				foreach (string item in other)
				{
					if (item != null)
					{
						hashSet.Add(item);
					}
				}
			}
			foreach (string item2 in source)
			{
				if (item2 != null && !hashSet.Contains(item2))
				{
					list.Add(item2);
				}
			}
			list.Sort(StringComparer.Ordinal);
			return list;
		}
	}
	public static class ModSetPolicy
	{
		public static readonly IReadOnlyList<string> AllowedGuids = new string[4] { "com.null.invasionmod", "com.jotunn.jotunn", "com.ValheimModding.NewtonsoftJsonDetector", "com.ValheimModding.YamlDotNetDetector" };

		public static string CleanFingerprint => ComputeFingerprint(null);

		public static HashSet<string> BuildAllowed()
		{
			return new HashSet<string>(AllowedGuids, StringComparer.OrdinalIgnoreCase);
		}

		public static string BlockReason(IReadOnlyList<string> foreignNames)
		{
			if (foreignNames == null || foreignNames.Count == 0)
			{
				return null;
			}
			return "Invasions are disabled while other mods are loaded: " + string.Join(", ", foreignNames) + ". Only InvasionMod and Jotunn are allowed.";
		}

		public static List<KeyValuePair<string, string>> FindForeign(IReadOnlyDictionary<string, string> pluginNames, ISet<string> allowed)
		{
			List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
			if (pluginNames == null)
			{
				return list;
			}
			foreach (KeyValuePair<string, string> pluginName in pluginNames)
			{
				if (allowed == null || !allowed.Contains(pluginName.Key))
				{
					list.Add(new KeyValuePair<string, string>(pluginName.Key, pluginName.Value ?? pluginName.Key));
				}
			}
			list.Sort((KeyValuePair<string, string> a, KeyValuePair<string, string> b) => string.Compare(a.Value, b.Value, StringComparison.OrdinalIgnoreCase));
			return list;
		}

		public static List<string> ThirdPartyGuids(IReadOnlyDictionary<string, string> pluginNames, ISet<string> allowed)
		{
			List<string> list = new List<string>();
			if (pluginNames == null)
			{
				return list;
			}
			foreach (string key in pluginNames.Keys)
			{
				if (allowed == null || !allowed.Contains(key))
				{
					list.Add(key);
				}
			}
			return list;
		}

		public static string ComputeFingerprint(IReadOnlyCollection<string> thirdPartyGuids)
		{
			if (thirdPartyGuids == null || thirdPartyGuids.Count == 0)
			{
				return Fnv1a(string.Empty);
			}
			List<string> list = new List<string>(thirdPartyGuids);
			list.Sort(StringComparer.Ordinal);
			return Fnv1a(string.Join(",", list));
		}

		private static string Fnv1a(string value)
		{
			uint num = 2166136261u;
			for (int i = 0; i < value.Length; i++)
			{
				num ^= value[i];
				num *= 16777619;
			}
			return num.ToString("x8");
		}
	}
	public readonly struct ModVersion : IComparable<ModVersion>, IEquatable<ModVersion>
	{
		private static readonly char[] SuffixMarkers = new char[2] { '-', '+' };

		public int Major { get; }

		public int Minor { get; }

		public string Patch { get; }

		public ModVersion(int major, int minor, string patch)
		{
			Major = major;
			Minor = minor;
			Patch = patch ?? string.Empty;
		}

		public static bool TryParse(string value, out ModVersion version)
		{
			version = default(ModVersion);
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			string text = value.Trim();
			if (text.Length == 0)
			{
				return false;
			}
			int num = text.IndexOfAny(SuffixMarkers);
			if (num >= 0)
			{
				text = text.Substring(0, num);
			}
			string[] array = text.Split(new char[1] { '.' });
			if (array.Length == 0 || array.Length > 4)
			{
				return false;
			}
			if (!TryNumber(array[0], out var number))
			{
				return false;
			}
			int number2 = 0;
			if (array.Length > 1 && !TryNumber(array[1], out number2))
			{
				return false;
			}
			string patch = ((array.Length > 2) ? string.Join(".", array, 2, array.Length - 2) : string.Empty);
			version = new ModVersion(number, number2, patch);
			return true;
		}

		private static bool TryNumber(string value, out int number)
		{
			return int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out number);
		}

		public bool IsSameMinorAs(ModVersion other)
		{
			if (Major == other.Major)
			{
				return Minor == other.Minor;
			}
			return false;
		}

		public int CompareTo(ModVersion other)
		{
			int num = Major.CompareTo(other.Major);
			if (num != 0)
			{
				return num;
			}
			return Minor.CompareTo(other.Minor);
		}

		public bool Equals(ModVersion other)
		{
			return CompareTo(other) == 0;
		}

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

		public override int GetHashCode()
		{
			return (Major * 397) ^ Minor;
		}

		public override string ToString()
		{
			if (Patch.Length != 0)
			{
				return Major + "." + Minor + "." + Patch;
			}
			return Major + "." + Minor;
		}
	}
}
namespace InvasionMod.Core.Domain.Inventory
{
	public readonly struct InventoryEntry
	{
		public string Name { get; }

		public int Stack { get; }

		public float Durability { get; }

		public int Quality { get; }

		public int Variant { get; }

		public int WorldLevel { get; }

		public long CrafterId { get; }

		public string CrafterName { get; }

		public bool Equipped { get; }

		public InventoryEntry(string name, int stack, float durability, int quality, int variant, int worldLevel, long crafterId, string crafterName, bool equipped)
		{
			Name = name;
			Stack = stack;
			Durability = durability;
			Quality = quality;
			Variant = variant;
			WorldLevel = worldLevel;
			CrafterId = crafterId;
			CrafterName = crafterName;
			Equipped = equipped;
		}
	}
}
namespace InvasionMod.Core.Domain.Discovery
{
	public static class LobbyCompatibilityPolicy
	{
		public static LobbyCompatibility Evaluate(LobbyDescriptor lobby, string localVersion, string localFingerprint, string modsMismatchDetail)
		{
			if (lobby == null)
			{
				return LobbyCompatibility.Blocked("the world returned no data.");
			}
			LobbyCompatibility lobbyCompatibility = CheckVersion(lobby, localVersion);
			if (lobbyCompatibility != null)
			{
				return lobbyCompatibility;
			}
			if (lobby.CreativeUsed)
			{
				return LobbyCompatibility.Blocked("used hammer or creative mode (free building or crafting).");
			}
			return CheckMods(lobby, localFingerprint, modsMismatchDetail) ?? LobbyCompatibility.Ok;
		}

		private static LobbyCompatibility CheckVersion(LobbyDescriptor lobby, string localVersion)
		{
			if (!ModVersion.TryParse(lobby.OwnerVersion, out var version))
			{
				return LobbyCompatibility.Blocked("The host does not report its mod version, so there is no way to know whether the progression filter will work.");
			}
			if (!ModVersion.TryParse(localVersion, out var version2))
			{
				return LobbyCompatibility.Blocked("The version of this mod cannot be read.");
			}
			if (version.IsSameMinorAs(version2))
			{
				return null;
			}
			return LobbyCompatibility.Blocked("has version " + version.ToString() + " of the mod and you have " + version2.ToString() + ". With different versions the progression filter does not work the same on both sides.");
		}

		private static LobbyCompatibility CheckMods(LobbyDescriptor lobby, string localFingerprint, string modsMismatchDetail)
		{
			if (string.IsNullOrEmpty(lobby.ModsFingerprint))
			{
				return LobbyCompatibility.Blocked("The host did not publish its set of mods.");
			}
			if (string.IsNullOrEmpty(localFingerprint))
			{
				return LobbyCompatibility.Blocked("The set of mods of this machine cannot be read.");
			}
			if (string.Equals(lobby.ModsFingerprint, localFingerprint, StringComparison.Ordinal))
			{
				return null;
			}
			return LobbyCompatibility.Blocked(string.IsNullOrEmpty(modsMismatchDetail) ? ("has a different set of mods than yours (its fingerprint is " + lobby.ModsFingerprint + " and yours is " + localFingerprint + ").") : ("different mods (" + modsMismatchDetail + ")"));
		}
	}
	public sealed class LobbyDescriptor
	{
		public ulong LobbyId { get; set; }

		public ulong OwnerId { get; set; }

		public string Label { get; set; }

		public string WorldName { get; set; }

		public bool NeedsPassword { get; set; }

		public string WorldPassword { get; set; }

		public string TierText { get; set; }

		public int Tier { get; set; } = -1;

		public string OwnerName { get; set; }

		public string OwnerVersion { get; set; }

		public string ModsFingerprint { get; set; }

		public bool CreativeUsed { get; set; }
	}
	public sealed class LobbyCompatibility
	{
		public static readonly LobbyCompatibility Ok = new LobbyCompatibility(compatible: true, null);

		public bool Compatible { get; }

		public string Reason { get; }

		private LobbyCompatibility(bool compatible, string reason)
		{
			Compatible = compatible;
			Reason = reason;
		}

		public static LobbyCompatibility Blocked(string reason)
		{
			return new LobbyCompatibility(compatible: false, reason);
		}
	}
	public static class LobbyKeys
	{
		public const string Active = "invasionmod_active";

		public const string Version = "invasionmod_version";

		public const string Tier = "invasionmod_tier";

		public const string GamePassword = "password";

		public const string GameWorldName = "name";

		public const string Password = "invasionmod_password";

		public const string Mods = "invasionmod_mods";

		public const string CreativeUsed = "invasionmod_creative";
	}
	public static class RandomWorldPick
	{
		public static WorldListing Choose(IReadOnlyList<WorldListing> results, ulong lastOwner, Func<double> random)
		{
			if (random == null)
			{
				throw new ArgumentNullException("random");
			}
			List<WorldListing> list = new List<WorldListing>();
			List<WorldListing> list2 = new List<WorldListing>();
			foreach (WorldListing item in results ?? Array.Empty<WorldListing>())
			{
				if (item != null && item.Compatible)
				{
					list.Add(item);
					if (lastOwner == 0L || item.OwnerId != lastOwner)
					{
						list2.Add(item);
					}
				}
			}
			List<WorldListing> list3 = ((list2.Count > 0) ? list2 : list);
			if (list3.Count == 0)
			{
				return null;
			}
			int val = (int)(random() * (double)list3.Count);
			return list3[Math.Min(Math.Max(val, 0), list3.Count - 1)];
		}
	}
	public static class TierGap
	{
		public const int MaxDifference = 1;

		public static string Describe(int homeTier, int worldTier)
		{
			if (!TierScale.IsValid(homeTier) || !TierScale.IsValid(worldTier) || homeTier == worldTier)
			{
				return null;
			}
			if (worldTier >= homeTier)
			{
				return "is further ahead than your character (" + TierScale.Name(homeTier) + ")";
			}
			return "you enter with gear of rank " + TierScale.Name(worldTier);
		}

		public static string BlockReason(int homeTier, int worldTier)
		{
			if (!TierScale.IsValid(homeTier))
			{
				return null;
			}
			if (!TierScale.IsValid(worldTier))
			{
				return "did not publish its rank, so there is no way to know whether your character is in its range.";
			}
			int num = homeTier - worldTier;
			if (num <= 1 && num >= -1)
			{
				return null;
			}
			return "your character is " + TierScale.Name(homeTier) + " and this world is " + TierScale.Name(worldTier) + ": invaders must be within " + 1 + " rank of the host.";
		}
	}
	public sealed class WorldListing
	{
		public ulong LobbyId { get; }

		public ulong OwnerId { get; }

		public string WorldName { get; }

		public string TierText { get; }

		public int Tier { get; }

		public string OwnerName { get; }

		public string OwnerVersion { get; }

		public bool NeedsPassword { get; }

		public string WorldPassword { get; }

		public bool Compatible { get; }

		public string Reason { get; }

		public string Label
		{
			get
			{
				string text = WorldName + "  [" + TierText + "]";
				if (!string.IsNullOrEmpty(OwnerName))
				{
					return text + "  -  " + OwnerName;
				}
				return text;
			}
		}

		public WorldListing(LobbyDescriptor lobby, LobbyCompatibility compatibility)
		{
			LobbyId = lobby.LobbyId;
			OwnerId = lobby.OwnerId;
			WorldName = lobby.WorldName;
			TierText = lobby.TierText;
			Tier = lobby.Tier;
			OwnerName = lobby.OwnerName ?? string.Empty;
			OwnerVersion = lobby.OwnerVersion ?? string.Empty;
			NeedsPassword = lobby.NeedsPassword;
			WorldPassword = lobby.WorldPassword;
			Compatible = compatibility.Compatible;
			Reason = compatibility.Reason;
		}
	}
}
namespace InvasionMod.Core.Domain.Auditing
{
	public readonly struct EquipmentViolation
	{
		public string Slot { get; }

		public string ItemName { get; }

		public int ItemTier { get; }

		public EquipmentViolation(string slot, string itemName, int itemTier)
		{
			Slot = slot;
			ItemName = itemName;
			ItemTier = itemTier;
		}

		public override string ToString()
		{
			return ItemName + " (" + Slot + ")";
		}
	}
	public enum AuditAction
	{
		Notify,
		Kick
	}
	public sealed class AuditVerdict
	{
		public string PlayerName { get; }

		public IReadOnlyList<EquipmentViolation> Violations { get; }

		public int WorldTier { get; }

		public bool Clean => Violations.Count == 0;

		public string Signature
		{
			get
			{
				if (Violations.Count == 0)
				{
					return string.Empty;
				}
				string[] array = new string[Violations.Count];
				for (int i = 0; i < Violations.Count; i++)
				{
					array[i] = Violations[i].ToString();
				}
				return string.Join("; ", array);
			}
		}

		public AuditVerdict(string playerName, IReadOnlyList<EquipmentViolation> violations, int worldTier)
		{
			PlayerName = playerName;
			Violations = violations;
			WorldTier = worldTier;
		}
	}
}
namespace InvasionMod.Core.Application.UseCases
{
	public sealed class AnnounceDeaths
	{
		public const float SecondsPerMessage = 2f;

		private readonly ISessionContext _session;

		private readonly INetworkGateway _network;

		private readonly IProgressionNotifier _notifier;

		private readonly IModLogger _log;

		private readonly IClock _clock;

		private readonly Queue<string> _pending = new Queue<string>();

		private float _nextAt;

		public int PendingCount => _pending.Count;

		public AnnounceDeaths(ISessionContext session, INetworkGateway network, IProgressionNotifier notifier, IModLogger log, IClock clock)
		{
			_session = session ?? throw new ArgumentNullException("session");
			_network = network ?? throw new ArgumentNullException("network");
			_notifier = notifier ?? throw new ArgumentNullException("notifier");
			_log = log ?? throw new ArgumentNullException("log");
			_clock = clock ?? throw new ArgumentNullException("clock");
		}

		public static string Message(string who)
		{
			return (string.IsNullOrEmpty(who) ? "Someone" : who) + " has died.";
		}

		public static string KillMessage(string who, string reward)
		{
			return "You killed " + (string.IsNullOrEmpty(who) ? "a player" : who) + ". " + reward;
		}

		public bool ReportLocalDeath(string playerName, long killerPeer = 0L)
		{
			if (_session.IsHost)
			{
				return false;
			}
			_network.BroadcastPlayerDied(playerName ?? string.Empty, killerPeer);
			return true;
		}

		public bool OnPlayerDied(long sender, long localPeer, string playerName, string reward = null)
		{
			if (sender == localPeer)
			{
				return false;
			}
			string text = ((reward == null) ? Message(playerName) : KillMessage(playerName, reward));
			_log.Info("Muerte en la sesion: " + text + " (peer " + sender + ").");
			_pending.Enqueue(text);
			Tick();
			return true;
		}

		public void Tick()
		{
			if (_pending.Count != 0)
			{
				float now = _clock.Now;
				if (!(now < _nextAt))
				{
					_notifier.NotifyPlayer(_pending.Dequeue());
					_nextAt = now + 2f;
				}
			}
		}

		public void Clear()
		{
			_pending.Clear();
			_nextAt = 0f;
		}
	}
	public sealed class AuditVisitorEquipment
	{
		private readonly IEquipmentSource _equipment;

		private readonly IVisitorExpeller _expeller;

		private readonly ProgressionGate _gate;

		private readonly IProgressionNotifier _notifier;

		private readonly IModLogger _log;

		private readonly Dictionary<long, string> _lastReport = new Dictionary<long, string>();

		private readonly HashSet<long> _kicked = new HashSet<long>();

		public IReadOnlyCollection<long> AuditedPlayers => _lastReport.Keys;

		public AuditVisitorEquipment(IEquipmentSource equipment, IVisitorExpeller expeller, ProgressionGate gate, IProgressionNotifier notifier, IModLogger log)
		{
			_equipment = equipment;
			_expeller = expeller ?? throw new ArgumentNullException("expeller");
			_gate = gate;
			_notifier = notifier;
			_log = log;
		}

		public int Execute(int worldTier, AuditAction action)
		{
			return Execute(worldTier, (long _) => action);
		}

		public int Execute(int worldTier, Func<long, AuditAction> actionFor)
		{
			return Execute(worldTier, actionFor, (long _) => true);
		}

		public int Execute(int worldTier, Func<long, AuditAction> actionFor, Func<long, bool> ready)
		{
			IReadOnlyList<RemoteEquipment> readOnlyList = _equipment.ReadRemotes();
			if (readOnlyList == null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < readOnlyList.Count; i++)
			{
				if (ready(readOnlyList[i].Peer) && Apply(readOnlyList[i], worldTier, actionFor(readOnlyList[i].Peer)))
				{
					num++;
				}
			}
			return num;
		}

		private bool Apply(RemoteEquipment remote, int worldTier, AuditAction action)
		{
			List<EquipmentViolation> list = new List<EquipmentViolation>(4);
			for (int i = 0; i < remote.Slots.Count; i++)
			{
				EquipmentSlot equipmentSlot = remote.Slots[i];
				if (!string.IsNullOrEmpty(equipmentSlot.ItemName))
				{
					int num = _gate.TierOf(equipmentSlot.ItemName);
					if (num > worldTier)
					{
						list.Add(new EquipmentViolation(equipmentSlot.SlotName, equipmentSlot.ItemName, num));
					}
				}
			}
			AuditVerdict auditVerdict = new AuditVerdict(remote.PlayerName, list, worldTier);
			string signature = auditVerdict.Signature;
			string value;
			bool num2 = !_lastReport.TryGetValue(remote.Peer, out value) || !(value == signature);
			_lastReport[remote.Peer] = signature;
			if (num2)
			{
				if (auditVerdict.Clean)
				{
					_log.Info("'" + remote.PlayerName + "': equipo dentro del tope (" + TierScale.Name(worldTier) + ").");
				}
				else
				{
					string message = "'" + remote.PlayerName + "' has gear above the cap (" + TierScale.Name(worldTier) + "): " + signature;
					_log.Warning(message);
					_notifier.NotifyPlayer(message);
				}
			}
			if (!auditVerdict.Clean && action == AuditAction.Kick && _kicked.Add(remote.Peer))
			{
				_log.Warning("Expulsando a '" + remote.PlayerName + "': equipo por encima del tope.");
				_expeller.Expel(remote.Peer, "you have something equipped above what this world allows (" + TierScale.Name(worldTier) + "): " + signature);
			}
			return !auditVerdict.Clean;
		}

		public void ResetReports()
		{
			_lastReport.Clear();
			_kicked.Clear();
		}
	}
	public sealed class CapVisitorStats
	{
		private readonly VisitSession _session;

		private readonly ISessionContext _context;

		private readonly ProgressionGate _gate;

		private readonly IProgressionNotifier _notifier;

		private readonly IModLogger _log;

		private readonly HashSet<string> _foodsReported = new HashSet<string>(StringComparer.Ordinal);

		public bool IsActive
		{
			get
			{
				if (_session.InForeignWorld && _session.IsInvader)
				{
					return !_context.IsHost;
				}
				return false;
			}
		}

		public CapVisitorStats(VisitSession session, ISessionContext context, ProgressionGate gate, IProgressionNotifier notifier, IModLogger log)
		{
			_session = session ?? throw new ArgumentNullException("session");
			_context = context ?? throw new ArgumentNullException("context");
			_gate = gate ?? throw new ArgumentNullException("gate");
			_notifier = notifier ?? throw new ArgumentNullException("notifier");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public float CapSkill(int skill, float level)
		{
			if (!IsActive)
			{
				return level;
			}
			return _session.HostSkills.Cap(skill, level);
		}

		public bool FoodCounts(string foodItemName)
		{
			if (!IsActive || string.IsNullOrEmpty(foodItemName))
			{
				return true;
			}
			int num = _gate.TierOf(foodItemName);
			if (num <= _session.Tier)
			{
				return true;
			}
			if (_foodsReported.Add(foodItemName))
			{
				_log.Info("Comida por encima del tope (" + TierScale.Name(_session.Tier) + "): " + foodItemName + " (" + TierScale.Name(num) + ") no suma mientras estes de visita.");
				_notifier.NotifyPlayer("A food you brought does not count in this world (it allows up to " + TierScale.Name(_session.Tier) + "). It counts again in your own world.");
			}
			return false;
		}

		public bool CanUseGuardianPower(string powerName)
		{
			if (IsActive)
			{
				return GuardianPowerTable.Allows(powerName, _session.Tier);
			}
			return true;
		}

		public void NotifyPowerBlocked(string powerName)
		{
			_notifier.NotifyPlayer("Your boss power cannot be used in this world (it allows up to " + TierScale.Name(_session.Tier) + "). It works again in your own world.");
			_log.Info("Poder de jefe bloqueado de visita: " + powerName + ".");
		}

		public void Reset()
		{
			_foodsReported.Clear();
		}
	}
	public sealed class CharacterTierSource : IWorldProgressSource
	{
		private readonly IKnownItems _known;

		private readonly ProgressionGate _gate;

		private int _cachedCount = -1;

		private int _cachedTier;

		public CharacterTierSource(IKnownItems known, ProgressionGate gate)
		{
			_known = known ?? throw new ArgumentNullException("known");
			_gate = gate ?? throw new ArgumentNullException("gate");
		}

		public int ReadTier()
		{
			if (!_known.HasLocalPlayer)
			{
				_cachedCount = -1;
				return 0;
			}
			int count = _known.Count;
			if (count == _cachedCount)
			{
				return _cachedTier;
			}
			int num = 0;
			for (int i = 0; i < count; i++)
			{
				if (num >= 7)
				{
					break;
				}
				string text = _known.Get(i);
				if (!string.IsNullOrEmpty(text))
				{
					int num2 = _gate.TierOf(text);
					if (num2 > num)
					{
						num = num2;
					}
				}
			}
			_cachedCount = count;
			_cachedTier = TierScale.Clamp(num);
			return _cachedTier;
		}

		public void Invalidate()
		{
			_cachedCount = -1;
		}
	}
	public sealed class DetectHostLost
	{
		public const float Debounce = 6f;

		public const string MenuMessage = "The connection to the host's world was lost. You are back at the main menu.";

		private readonly ISessionContext _session;

		private readonly IPlayerGateway _player;

		private readonly IConnectionMonitor _connection;

		private readonly IMenuGateway _menu;

		private readonly IProgressionNotifier _notifier;

		private readonly IModLogger _log;

		private readonly IClock _clock;

		private float _badSince = -1f;

		private bool _awaitingMenu;

		public bool AwaitingMenu => _awaitingMenu;

		public DetectHostLost(ISessionContext session, IPlayerGateway player, IConnectionMonitor connection, IMenuGateway menu, IProgressionNotifier notifier, IModLogger log, IClock clock)
		{
			_session = session ?? throw new ArgumentNullException("session");
			_player = player ?? throw new ArgumentNullException("player");
			_connection = connection ?? throw new ArgumentNullException("connection");
			_menu = menu ?? throw new ArgumentNullException("menu");
			_notifier = notifier ?? throw new ArgumentNullException("notifier");
			_log = log ?? throw new ArgumentNullException("log");
			_clock = clock ?? throw new ArgumentNullException("clock");
		}

		public void Tick()
		{
			if (_awaitingMenu)
			{
				if (_menu.TryShowConnectionError("The connection to the host's world was lost. You are back at the main menu."))
				{
					_awaitingMenu = false;
				}
				return;
			}
			if (!_player.HasLocalPlayer || _session.IsHost || !_connection.IsDegraded)
			{
				_badSince = -1f;
				return;
			}
			float now = _clock.Now;
			if (_badSince < 0f)
			{
				_badSince = now;
			}
			else if (now - _badSince >= 6f)
			{
				Kick();
			}
		}

		private void Kick()
		{
			_awaitingMenu = true;
			_badSince = -1f;
			_log.Error("Conexion con el mundo del host perdida a mitad de invasion (status " + _connection.StatusText + "). Se vuelve al menu principal.");
			_notifier.NotifyPlayer("The connection to this world was lost. Returning to the main menu...");
			_menu.LeaveWorld();
		}
	}
	public sealed class EndInvasionOnHostDeath
	{
		private readonly VisitSession _visit;

		private readonly ISessionContext _session;

		private readonly INetworkGateway _network;

		private readonly IProgressionNotifier _notifier;

		private readonly IModLogger _log;

		public EndInvasionOnHostDeath(VisitSession visit, ISessionContext session, INetworkGateway network, IProgressionNotifier notifier, IModLogger log)
		{
			_visit = visit ?? throw new ArgumentNullException("visit");
			_session = session ?? throw new ArgumentNullException("session");
			_network = network ?? throw new ArgumentNullException("network");
			_notifier = notifier ?? throw new ArgumentNullException("notifier");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public static string Message(string killerName)
		{
			return (string.IsNullOrEmpty(killerName) ? "You died" : (killerName + " killed you")) + ". The invasion is over: the invaders return to their worlds.";
		}

		public bool Execute(string hostName, long killerPeer, long localPeer, string killerName)
		{
			if (!_session.IsHost || _visit.InvaderCount == 0)
			{
				return false;
			}
			bool flag = killerPeer != 0L && killerPeer != localPeer;
			long killerPeer2 = (flag ? killerPeer : 0);
			string text = (flag ? killerName : string.Empty);
			_network.BroadcastHostDied(hostName ?? string.Empty, killerPeer2);
			_visit.ClearInvaders();
			_network.BroadcastInvasion(active: false);
			_log.Info("Invasion: murio el host" + (flag ? (" a manos de " + (string.IsNullOrEmpty(text) ? "un invasor" : text) + " (peer " + killerPeer2 + ")") : string.Empty) + ". La invasion termino: los invasores vuelven a su mundo.");
			_notifier.NotifyPlayer(Message(text));
			return true;
		}
	}
	public sealed class EnforceProgressionGate
	{
		public const float SweepInterval = 2f;

		private readonly IInventoryGateway _inventory;

		private readonly IStash _stash;

		private readonly ProgressionGate _gate;

		private readonly VisitSession _session;

		private readonly ISessionContext _context;

		private readonly IProgressionNotifier _notifier;

		private readonly IModLogger _log;

		private readonly IClock _clock;

		private readonly List<StashedItem> _excess = new List<StashedItem>();

		private readonly List<int> _toRemove = new List<int>();

		private readonly List<string> _summary = new List<string>();

		private float _nextSweep;

		private bool _applying;

		public bool IsActive
		{
			get
			{
				if (_session.InForeignWorld && _session.IsInvader)
				{
					return !_context.IsHost;
				}
				return false;
			}
		}

		public EnforceProgressionGate(IInventoryGateway inventory, IStash stash, ProgressionGate gate, VisitSession session, ISessionContext context, IProgressionNotifier notifier, IModLogger log, IClock clock)
		{
			_inventory = inventory;
			_stash = stash;
			_gate = gate;
			_session = session;
			_context = context;
			_notifier = notifier;
			_log = log;
			_clock = clock;
		}

		public void Tick()
		{
			if (IsActive && !_applying)
			{
				float now = _clock.Now;
				if (!(now < _nextSweep))
				{
					_nextSweep = now + 2f;
					Apply();
				}
			}
		}

		public int Apply()
		{
			if (!IsActive || !_inventory.HasLocalPlayer || _applying)
			{
				return 0;
			}
			_applying = true;
			try
			{
				return ApplyInternal();
			}
			finally
			{
				_applying = false;
			}
		}

		private int ApplyInternal()
		{
			int tier = _session.Tier;
			int count = _inventory.Count;
			if (count <= 0)
			{
				return 0;
			}
			_excess.Clear();
			_toRemove.Clear();
			_summary.Clear();
			for (int num = count - 1; num >= 0; num--)
			{
				InventoryEntry inventoryEntry = _inventory.Get(num);
				int num2 = _gate.TierOf(inventoryEntry.Name);
				if (num2 > tier)
				{
					if (inventoryEntry.Equipped)
					{
						_inventory.Unequip(num);
					}
					_excess.Add(new StashedItem
					{
						Name = _inventory.PrefabNameAt(num),
						Stack = inventoryEntry.Stack,
						Durability = inventoryEntry.Durability,
						Quality = inventoryEntry.Quality,
						Variant = inventoryEntry.Variant,
						WorldLevel = inventoryEntry.WorldLevel,
						CrafterId = inventoryEntry.CrafterId,
						CrafterName = inventoryEntry.CrafterName
					});
					_summary.Add(inventoryEntry.Name + " (" + TierScale.Name(num2) + ")");
					_toRemove.Add(num);
				}
			}
			if (_excess.Count == 0)
			{
				return 0;
			}
			if (!_stash.Append(_excess))
			{
				return 0;
			}
			int count2 = _toRemove.Count;
			for (int i = 0; i < count2; i++)
			{
				int num3 = _toRemove[i];
				if (num3 < _inventory.Count)
				{
					_inventory.RemoveAt(num3);
				}
			}
			_inventory.NotifyChanged();
			_inventory.SaveProfile();
			_log.Info("Tope " + TierScale.Name(tier) + ": " + count2 + " item(s) apartados hasta volver a tu mundo. Archivo: " + _stash.Location + "\n  " + string.Join("\n  ", _summary));
			_notifier.NotifyPlayer(count2 + " item(s) were set aside until you return to your world (this world allows up to " + TierScale.Name(tier) + ").");
			return count2;
		}

		public bool ShouldBlock(string itemName)
		{
			if (!IsActive || string.IsNullOrEmpty(itemName))
			{
				return false;
			}
			return _gate.Exceeds(itemName, _session.Tier);
		}

		public void NotifyBlocked(string itemName)
		{
			_notifier.NotifyBlocked(itemName, _session.Tier);
		}
	}
	public sealed class ExpelVisitors : IVisitorExpeller
	{
		public const float Grace = 15f;

		private readonly INetworkGateway _network;

		private readonly IPeerDirectory _peers;

		private readonly IModLogger _log;

		private readonly ITelemetry _telemetry;

		private readonly IClock _clock;

		private readonly Dictionary<long, float> _kickAt = new Dictionary<long, float>();

		private readonly List<long> _due = new List<long>();

		public ExpelVisitors(INetworkGateway network, IPeerDirectory peers, IModLogger log, IClock clock, ITelemetry telemetry = null)
		{
			_network = network ?? throw new ArgumentNullException("network");
			_peers = peers ?? throw new ArgumentNullException("peers");
			_log = log ?? throw new ArgumentNullException("log");
			_clock = clock ?? throw new ArgumentNullException("clock");
			_telemetry = telemetry ?? NullTelemetry.Instance;
		}

		public bool IsLeaving(long peer)
		{
			return _kickAt.ContainsKey(peer);
		}

		public void Expel(long peer, string reason)
		{
			if (peer != 0L && !_kickAt.ContainsKey(peer))
			{
				_kickAt[peer] = _clock.Now + 15f;
				_network.SendExpelled(peer, reason ?? string.Empty);
				_log.Info("Se le aviso al peer " + peer + " que se vaya: " + reason + ". Si sigue en " + 15 + " s, se lo echa.");
				_telemetry.Event("visitor_expelled", reason);
			}
		}

		public void Tick()
		{
			if (_kickAt.Count == 0)
			{
				return;
			}
			float now = _clock.Now;
			_due.Clear();
			foreach (KeyValuePair<long, float> item in _kickAt)
			{
				if (now >= item.Value)
				{
					_due.Add(item.Key);
				}
			}
			for (int i = 0; i < _due.Count; i++)
			{
				long num = _due[i];
				_kickAt.Remove(num);
				_log.Warning("El peer " + num + " no se fue solo tras el aviso: se lo echa.");
				_peers.Kick(num);
			}
		}

		public void OnPeerLeft(long peer)
		{
			_kickAt.Remove(peer);
		}

		public void Reset()
		{
			_kickAt.Clear();
		}
	}
	public sealed class GrantInvasionReward
	{
		private readonly VisitSession _visit;

		private readonly IStash _stash;

		private readonly Func<double> _random;

		private readonly IModLogger _log;

		public GrantInvasionReward(VisitSession visit, IStash stash, Func<double> random, IModLogger log)
		{
			_visit = visit ?? throw new ArgumentNullException("visit");
			_stash = stash ?? throw new ArgumentNullException("stash");
			_random = random ?? throw new ArgumentNullException("random");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public string Execute()
		{
			int tier = _visit.Tier;
			bool battle = _random() < 0.5;
			string text = IdolTable.Prefab(tier, battle);
			string text2 = IdolTable.DisplayName(tier, battle);
			StashedItem stashedItem = new StashedItem
			{
				Name = text,
				Stack = 1,
				Durability = 100f,
				Quality = 1,
				Variant = 0,
				WorldLevel = 0,
				CrafterId = 0L,
				CrafterName = string.Empty
			};
			if (!_stash.Append(new StashedItem[1] { stashedItem }))
			{
				_log.Error("Recompensa: no se pudo guardar el " + text2 + " (" + text + ").");
				return null;
			}
			_log.Info("Recompensa: " + text2 + " (" + text + "), se entrega al volver a casa.");
			return text2;
		}
	}
	public sealed class GrantKillReward
	{
		private readonly VisitSession _visit;

		private readonly InvasionStatusProvider _status;

		private readonly IInventoryGateway _inventory;

		private readonly IStash _stash;

		private readonly Func<double> _random;

		private readonly IModLogger _log;

		public GrantKillReward(VisitSession visit, InvasionStatusProvider status, IInventoryGateway inventory, IStash stash, Func<double> random, IModLogger log)
		{
			_visit = visit ?? throw new ArgumentNullException("visit");
			_status = status ?? throw new ArgumentNullException("status");
			_inventory = inventory ?? throw new ArgumentNullException("inventory");
			_stash = stash ?? throw new ArgumentNullException("stash");
			_random = random ?? throw new ArgumentNullException("random");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public static string InInventory(string reward)
		{
			return "Reward: " + reward + ".";
		}

		public static string AtHome(string reward)
		{
			return "Reward: " + reward + ", delivered when you arrive at your world.";
		}

		public static string InventoryFull(string reward)
		{
			return "Reward: " + reward + ". Your inventory is full: it is delivered the next time you load your world.";
		}

		public string Execute(long victimPeer, long localPeer, long killerPeer)
		{
			if (killerPeer == 0L || killerPeer != localPeer || victimPeer == localPeer || !_status.InvasionActive)
			{
				return null;
			}
			int index = KillRewardTable.Pick(_random());
			string text = KillRewardTable.Prefab(index);
			string text2 = KillRewardTable.DisplayName(index);
			StashedItem stashedItem = new StashedItem
			{
				Name = text,
				Stack = 50,
				Durability = 100f,
				Quality = 1,
				Variant = 0,
				WorldLevel = 0,
				CrafterId = 0L,
				CrafterName = string.Empty
			};
			bool isInvader = _visit.IsInvader;
			if (!isInvader && _inventory.HasLocalPlayer && _inventory.Add(stashedItem))
			{
				_inventory.NotifyChanged();
				_log.Info("Recompensa por matar a un jugador: " + text2 + " (" + text + "), en la mochila.");
				return InInventory(text2);
			}
			if (!_stash.Append(new StashedItem[1] { stashedItem }))
			{
				_log.Error("Recompensa por matar a un jugador: no se pudo guardar " + text2 + " (" + text + ").");
				return null;
			}
			_log.Info("Recompensa por matar a un jugador: " + text2 + " (" + text + "), " + (isInvader ? "se entrega al volver a casa." : "no entro en la mochila, se entrega en la proxima carga."));
			if (!isInvader)
			{
				return InventoryFull(text2);
			}
			return AtHome(text2);
		}
	}
	public sealed class GuardCreativeWorld
	{
		private readonly IWorldModifiers _world;

		private readonly ISessionContext _context;

		private readonly IModLogger _log;

		public GuardCreativeWorld(IWorldModifiers world, ISessionContext context, IModLogger log)
		{
			_world = world ?? throw new ArgumentNullException("world");
			_context = context ?? throw new ArgumentNullException("context");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public bool Execute()
		{
			if (!_context.IsHost)
			{
				return false;
			}
			if (_world.IsMarked("invasionmod_creative_used"))
			{
				return true;
			}
			string text = CreativeModifiers.FirstActive(_world);
			if (text == null)
			{
				return false;
			}
			_world.Mark("invasionmod_creative_used");
			_log.Warning("Este mundo tiene el modificador " + text + " (modo martillo o creativo): queda marcado con invasionmod_creative_used y ya no se puede abrir a invasiones, aunque el modificador se apague.");
			return true;
		}
	}
	public sealed class InvadeRandomWorld
	{
		public const float RetryInterval = 15f;

		public const float SearchTimeout = 30f;

		private readonly SearchOpenWorlds _search;

		private readonly JoinListedWorld _join;

		private readonly ISearchView _view;

		private readonly Func<double> _random;

		private readonly IModLogger _log;

		private readonly IClock _clock;

		private readonly Func<string> _blockReason;

		private bool _waiting;

		private bool _searching;

		private float _startedAt;

		private float _searchStartedAt;

		private float _nextSearchAt;

		private int _rounds;

		private ulong _lastOwner;

		public bool Waiting => _waiting;

		public ulong LastOwner => _lastOwner;

		public string StatusText
		{
			get
			{
				if (!_waiting)
				{
					return null;
				}
				return SearchingStatus(_clock.Now - _startedAt);
			}
		}

		public InvadeRandomWorld(SearchOpenWorlds search, JoinListedWorld join, ISearchView view, Func<double> random, IModLogger log, IClock clock, Func<string> blockReason)
		{
			_search = search ?? throw new ArgumentNullException("search");
			_join = join ?? throw new ArgumentNullException("join");
			_view = view ?? throw new ArgumentNullException("view");
			_random = random ?? throw new ArgumentNullException("random");
			_log = log ?? throw new ArgumentNullException("log");
			_clock = clock ?? throw new ArgumentNullException("clock");
			_blockReason = blockReason ?? throw new ArgumentNullException("blockReason");
		}

		public static string SearchingStatus(float elapsed)
		{
			int num = ((!(elapsed < 0f)) ? ((int)elapsed) : 0);
			string text = new string('.', 1 + num % 3);
			return "Attempting to invade" + text + "\n" + num / 60 + ":" + (num % 60).ToString("00");
		}

		public void Execute()
		{
			if (_waiting)
			{
				Cancel();
				return;
			}
			string text = _blockReason();
			if (text != null)
			{
				_view.ShowStatus(text);
				return;
			}
			_waiting = true;
			_rounds = 0;
			_startedAt = _clock.Now;
			_log.Info("Invasion al azar: buscando mundos.");
			StartSearch();
			ShowSearching();
		}

		public void Cancel(string reason = null)
		{
			if (_waiting)
			{
				_log.Info("Invasion al azar: se dejo de buscar despues de " + (int)(_clock.Now - _startedAt) + " s y " + _rounds + " busqueda(s)" + ((reason == null) ? "." : (": " + reason)));
				Reset();
				_view.ShowStatus(reason);
			}
		}

		public void Reset()
		{
			_waiting = false;
			_searching = false;
		}

		public void Tick()
		{
			if (!_waiting)
			{
				return;
			}
			float now = _clock.Now;
			if (_searching)
			{
				if (_search.Finished)
				{
					_searching = false;
					if (TryTravel())
					{
						return;
					}
					_nextSearchAt = now + 15f;
				}
				else if (now - _searchStartedAt > 30f)
				{
					_log.Warning("Invasion al azar: Steam no termino la busqueda en " + 30 + " s, se reintenta.");
					StartSearch();
				}
			}
			else if (now >= _nextSearchAt)
			{
				StartSearch();
			}
			ShowSearching();
		}

		private void StartSearch()
		{
			_searching = true;
			_rounds++;
			_searchStartedAt = _clock.Now;
			_search.Search();
		}

		private void ShowSearching()
		{
			_view.ShowStatus(StatusText);
		}

		private bool TryTravel()
		{
			WorldListing worldListing = RandomWorldPick.Choose(_search.Results, _lastOwner, _random);
			if (worldListing == null)
			{
				_log.Info("Invasion al azar: busqueda " + _rounds + " sin mundo compatible (" + _search.Results.Count + " en la lista). Se vuelve a buscar en " + 15 + " s.");
				return false;
			}
			_waiting = false;
			bool flag = _lastOwner != 0L && worldListing.OwnerId == _lastOwner;
			_log.Info("Invasion al azar: " + worldListing.Label + (flag ? " (el mismo de la vez pasada: es el unico abierto)" : string.Empty) + ", despues de " + (int)(_clock.Now - _startedAt) + " s.");
			if (_join.Execute(worldListing) == TravelRequestOutcome.Started)
			{
				_lastOwner = worldListing.OwnerId;
			}
			return true;
		}
	}
	public sealed class InvasionStatusProvider
	{
		private readonly VisitSession _session;

		private readonly ISessionContext _context;

		private readonly IClock _clock;

		private long _frame = -1L;

		private InvasionStatus _status = InvasionStatus.Idle;

		public InvasionStatus Current
		{
			get
			{
				long frame = _clock.Frame;
				if (frame != _frame)
				{
					_frame = frame;
					_status = InvasionStatus.Evaluate(_session, _context);
				}
				return _status;
			}
		}

		public bool InProgress => Current.InProgress;

		public string BlockReason => Current.BlockReason;

		public string PanelBlockReason => Current.PanelBlockReason;

		public bool IsPvpForced => _session.IsPvpForced(Current);

		public bool InvasionActive => Current.ForcesPvp;

		public InvasionStatusProvider(VisitSession session, ISessionContext context, IClock clock)
		{
			_session = session;
			_context = context;
			_clock = clock;
		}
	}
	public sealed class JoinListedWorld
	{
		private readonly Func<IReadOnlyList<WorldListing>> _results;

		private readonly PasswordBroker _passwords;

		private readonly TravelToWorld _travel;

		private readonly ISearchView _view;

		private readonly IModLogger _log;

		public JoinListedWorld(Func<IReadOnlyList<WorldListing>> results, PasswordBroker passwords, TravelToWorld travel, ISearchView view, IModLogger log)
		{
			_results = results ?? throw new ArgumentNullException("results");
			_passwords = passwords ?? throw new ArgumentNullException("passwords");
			_travel = travel ?? throw new ArgumentNullException("travel");
			_view = view ?? throw new ArgumentNullException("view");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public TravelRequestOutcome? Execute(int index)
		{
			IReadOnlyList<WorldListing> readOnlyList = _results();
			if (index < 0 || index >= readOnlyList.Count)
			{
				_log.Warning("Join(" + index + ") llego pero solo hay " + readOnlyList.Count + " mundo(s) cargados. Click sin efecto.");
				return null;
			}
			_log.Info("Join(" + index + ") -> " + readOnlyList[index].Label);
			return Execute(readOnlyList[index]);
		}

		public TravelRequestOutcome? Execute(WorldListing chosen)
		{
			if (chosen == null)
			{
				_log.Warning("Join sin mundo elegido. Sin efecto.");
				return null;
			}
			if (!chosen.Compatible)
			{
				_log.Warning("No se puede unir a '" + chosen.WorldName + "': " + chosen.Reason + ".");
				_view.ShowStatus("Cannot join: " + chosen.Reason + ".");
				return null;
			}
			_passwords.Arm(string.IsNullOrEmpty(chosen.WorldPassword) ? null : chosen.WorldPassword);
			TravelRequestOutcome num = _travel.Request(chosen.LobbyId, chosen.OwnerId, chosen.Label, chosen.WorldName);
			if (num != TravelRequestOutcome.Started)
			{
				_passwords.Clear();
			}
			return num;
		}
	}
	public sealed class KeepInvasionPvp
	{
		private readonly VisitSession _visit;

		private readonly InvasionStatusProvider _status;

		private readonly IPlayerGateway _player;

		private readonly IModLogger _log;

		private bool _wasForced;

		public KeepInvasionPvp(VisitSession visit, InvasionStatusProvider status, IPlayerGateway player, IModLogger log)
		{
			_visit = visit ?? throw new ArgumentNullException("visit");
			_status = status ?? throw new ArgumentNullException("status");
			_player = player ?? throw new ArgumentNullException("player");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public bool Execute()
		{
			if (!_player.HasLocalPlayer || _player.IsPvpEnabled || !_status.IsPvpForced)
			{
				return false;
			}
			_player.SetPvp(enabled: true);
			_log.Info("PvP forzado: estaba apagado en este jugador (" + _status.Current.Cause.ToString() + "), se prende.");
			return true;
		}

		public void Tick()
		{
			bool isPvpForced = _status.IsPvpForced;
			bool wasForced = _wasForced;
			_wasForced = isPvpForced;
			if (isPvpForced)
			{
				Execute();
			}
			else if (wasForced && _player.HasLocalPlayer && _player.IsPvpEnabled)
			{
				_player.SetPvp(_visit.PvpManual);
				_log.Info("PvP forzado quitado: ya no hay invasores (" + _status.Current.Cause.ToString() + ").");
			}
		}

		public GateAuthorityVerdict OnInvasionState(bool active, long sender, long serverPeer, bool localIsHost)
		{
			GateAuthorityVerdict gateAuthorityVerdict = GateAuthority.Decide(localIsHost, sender, serverPeer);
			switch (gateAuthorityVerdict)
			{
			case GateAuthorityVerdict.LocalIsHost:
				return gateAuthorityVerdict;
			default:
				_log.Warning("Aviso de invasion ignorado: vino del peer " + sender + " y el host de este mundo es " + serverPeer + ".");
				return gateAuthorityVerdict;
			case GateAuthorityVerdict.Accepted:
				_visit.SetWorldInvaded(active);
				return gateAuthorityVerdict;
			}
		}
	}
	public sealed class KeepInvasionVisible
	{
		private readonly InvasionStatusProvider _status;

		private readonly IPlayerGateway _player;

		private readonly IModLogger _log;

		private bool _wasForced;

		private bool _before;

		public bool IsForced
		{
			get
			{
				InvasionCause cause = _status.Current.Cause;
				if (cause != InvasionCause.BeingInvaded)
				{
					return cause == InvasionCause.Invading;
				}
				return true;
			}
		}

		public KeepInvasionVisible(InvasionStatusProvider status, IPlayerGateway player, IModLogger log)
		{
			_status = status ?? throw new ArgumentNullException("status");
			_player = player ?? throw new ArgumentNullException("player");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public void Tick()
		{
			if (IsForced)
			{
				if (!_wasForced)
				{
					_wasForced = true;
					_before = _player.IsPositionPublic;
					_log.Info("Posicion en el mapa forzada por la invasion (" + _status.Current.Cause.ToString() + "); antes estaba " + (_before ? "publica." : "oculta."));
				}
				if (!_player.IsPositionPublic)
				{
					_player.SetPositionPublic(enabled: true);
				}
			}
			else if (_wasForced)
			{
				_wasForced = false;
				_player.SetPositionPublic(_before);
				_log.Info("Posicion en el mapa: la invasion termino, vuelve a estar " + (_before ? "publica." : "oculta."));
			}
		}
	}
	public sealed class PlaceInvaderSpawn
	{
		public const float AnchorWait = 6f;

		public const int MaxCandidates = 8;

		private readonly ISessionContext _session;

		private readonly INetworkGateway _network;

		private readonly ILocalPosition _position;

		private readonly ITerrainProbe _plannedTerrain;

		private readonly ITerrainProbe _loadedTerrain;

		private readonly IModLogger _log;

		private readonly IClock _clock;

		private readonly Func<bool> _arrivingAsInvader;

		private List<SpawnPoint> _candidates = new List<SpawnPoint>();

		private int _index;

		private SpawnPoint _host;

		private bool _anchorReceived;

		private bool _done;

		private float? _waitStartedAt;

		public bool HasTarget
		{
			get
			{
				if (!_done)
				{
					return _index < _candidates.Count;
				}
				return false;
			}
		}

		public SpawnPoint Target => _candidates[_index];

		public PlaceInvaderSpawn(ISessionContext session, INetworkGateway network, ILocalPosition position, ITerrainProbe plannedTerrain, ITerrainProbe loadedTerrain, IModLogger log, IClock clock, Func<bool> arrivingAsInvader)
		{
			_arrivingAsInvader = arrivingAsInvader ?? throw new ArgumentNullException("arrivingAsInvader");
			_session = session ?? throw new ArgumentNullException("session");
			_network = network ?? throw new ArgumentNullException("network");
			_position = position ?? throw new ArgumentNullException("position");
			_plannedTerrain = plannedTerrain ?? throw new ArgumentNullException("plannedTerrain");
			_loadedTerrain = loadedTerrain ?? throw new ArgumentNullException("loadedTerrain");
			_log = log ?? throw new ArgumentNullException("log");
			_clock = clock ?? throw new ArgumentNullException("clock");
		}

		public bool SendAnchor(long peer)
		{
			if (!_session.IsHost)
			{
				return false;
			}
			if (!_position.TryGet(out var position))
			{
				_log.Warning("Aparicion del invasor: el host no sabe donde esta, el invasor aparece donde diga el juego.");
				return false;
			}
			_network.SendSpawnAnchor(peer, position);
			return true;
		}

		public GateAuthorityVerdict ReceiveAnchor(long sender, long serverPeer, SpawnPoint host, float startAngle, float distance)
		{
			GateAuthorityVerdict gateAuthorityVerdict = GateAuthority.Decide(_session.IsHost, sender, serverPeer);
			if (gateAuthorityVerdict != GateAuthorityVerdict.Accepted)
			{
				if (gateAuthorityVerdict == GateAuthorityVerdict.NotFromServer)
				{
					_log.Warning("Posicion del host ignorada: vino del peer " + sender + " y el host es " + serverPeer + ".");
				}
				return gateAuthorityVerdict;
			}
			if (_done)
			{
				return gateAuthorityVerdict;
			}
			if (!_arrivingAsInvader())
			{
				_done = true;
				_log.Info("Aparicion: no se llego por el panel de busqueda (amigo del host), aparece donde diga el juego.");
				return gateAuthorityVerdict;
			}
			_anchorReceived = true;
			_host = host;
			_index = 0;
			_candidates = SpawnSiteRules.Candidates(_plannedTerrain, host.X, host.Z, startAngle, 8, distance);
			if (_candidates.Count == 0)
			{
				_done = true;
				_log.Warning("Aparicion del invasor: no hay tierra a menos de " + 3000f.ToString("0") + " m del host " + host.ToString() + ", aparece donde diga el juego.");
			}
			else
			{
				_log.Info("Aparicion del invasor: " + _candidates.Count + " lugar(es) posibles cerca del host " + host.ToString() + ".");
			}
			return gateAuthorityVerdict;
		}

		public bool ShouldWaitForAnchor()
		{
			if (_done || _anchorReceived || _session.IsHost)
			{
				return false;
			}
			if (!_arrivingAsInvader())
			{
				_done = true;
				return false;
			}
			float now = _clock.Now;
			if (!_waitStartedAt.HasValue)
			{
				_waitStartedAt = now;
			}
			if (now - _waitStartedAt.Value < 6f)
			{
				return true;
			}
			_done = true;
			_log.Info("Aparicion del invasor: no llego la posicion del host, aparece donde diga el juego.");
			return false;
		}

		public bool TryConfirmTarget(out SpawnPoint spawn)
		{
			spawn = default(SpawnPoint);
			if (!HasTarget)
			{
				return false;
			}
			SpawnPoint target = Target;
			if (SpawnSiteRules.IsWalkable(_loadedTerrain, target.X, target.Z, out var height))
			{
				spawn = new SpawnPoint(target.X, height, target.Z);
				return true;
			}
			_index++;
			if (_index < _candidates.Count)
			{
				_log.Info("Aparicion del invasor: " + target.ToString() + " no sirve en el terreno real, se prueba otro.");
			}
			else
			{
				_done = true;
				_log.Warning("Aparicion del invasor: ningun lugar cerca del host sirvio, aparece donde diga el juego.");
			}
			return false;
		}

		public void Complete(SpawnPoint spawn)
		{
			_done = true;
			double num = spawn.X - _host.X;
			double num2 = spawn.Z - _host.Z;
			_log.Info("Aparicion del invasor: " + spawn.ToString() + ", a " + Math.Sqrt(num * num + num2 * num2).ToString("0") + " m del host.");
		}

		public void Reset()
		{
			_candidates = new List<SpawnPoint>();
			_index = 0;
			_anchorReceived = false;
			_done = false;
			_waitStartedAt = null;
		}
	}
	public sealed class ReceiveWorldGate
	{
		private readonly VisitSession _session;

		private readonly ISessionContext _context;

		private readonly EnforceProgressionGate _enforce;

		private readonly IModLogger _log;

		public ReceiveWorldGate(VisitSession session, ISessionContext context, EnforceProgressionGate enforce, IModLogger log)
		{
			_session = session;
			_context = context;
			_enforce = enforce;
			_log = log;
		}

		public GateAuthorityVerdict Execute(int tier, HostSkills hostSkills, long sender, long serverPeer)
		{
			GateAuthorityVerdict gateAuthorityVerdict = GateAuthority.Decide(_context.IsHost, sender, serverPeer);
			if (gateAuthorityVerdict != GateAuthorityVerdict.Accepted)
			{
				_log.Warning(GateAuthority.DescribeRejection(gateAuthorityVerdict, sender, serverPeer));
				return gateAuthorityVerdict;
			}
			int tier2 = TierScale.Clamp(tier);
			_session.EnterForeignWorld(tier2, hostSkills);
			_log.Info("Tope de progresion recibido: " + TierScale.Name(tier2) + " (" + tier2 + "), habilidades hasta las del host +" + _session.HostSkills.Margin + ".");
			try
			{
				_enforce.Apply();
			}
			catch (Exception ex)
			{
				_log.Error("Tope de progresion recibido (" + tier2 + ") pero NO se pudo aplicar al inventario: " + ex);
			}
			return GateAuthorityVerdict.Accepted;
		}
	}
	public sealed class RecordBossDefeat
	{
		private readonly WorldTierCalculator _calculator;

		private readonly IWorldDefeats _defeats;

		private readonly IWorldProgressCache _cache;

		private readonly IModLogger _log;

		public RecordBossDefeat(WorldTierCalculator calculator, IWorldDefeats defeats, IWorldProgressCache cache, IModLogger log)
		{
			_calculator = calculator ?? throw new ArgumentNullException("calculator");
			_defeats = defeats ?? throw new ArgumentNullException("defeats");
			_cache = cache ?? throw new ArgumentNullException("cache");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public bool Execute(string prefab)
		{
			BossProgress bossProgress = _calculator.FindByPrefab(prefab);
			if (bossProgress == null)
			{
				_log.Warning("WorldGate: el jefe '" + prefab + "' murio pero no esta en la tabla de jefes, asi que no se puede contarlo como progreso. Agregalo a BossTable.CreateDefault().");
				return false;
			}
			_defeats.Record(bossProgress);
			_cache.Invalidate();
			return true;
		}
	}
	public sealed class ReportGateCompliance
	{
		private readonly VisitSession _session;

		private readonly ISessionContext _context;

		private readonly IInventoryGateway _inventory;

		private readonly ProgressionGate _gate;

		private readonly EnforceProgressionGate _filter;

		private readonly INetworkGateway _network;

		private readonly string _modVersion;

		private readonly Func<string> _fingerprint;

		private readonly IModLogger _log;

		private bool _pending;

		public bool Pending => _pending;

		public ReportGateCompliance(VisitSession session, ISessionContext context, IInventoryGateway inventory, ProgressionGate gate, EnforceProgressionGate filter, INetworkGateway network, string modVersion, Func<string> fingerprint, IModLogger log)
		{
			_session = session ?? throw new ArgumentNullException("session");
			_context = context ?? throw new ArgumentNullException("context");
			_inventory = inventory ?? throw new ArgumentNullException("inventory");
			_gate = gate ?? throw new ArgumentNullException("gate");
			_filter = filter ?? throw new ArgumentNullException("filter");
			_network = network ?? throw new ArgumentNullException("network");
			_modVersion = modVersion ?? string.Empty;
			_fingerprint = fingerprint ?? throw new ArgumentNullException("fingerprint");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public void OnGateAccepted()
		{
			_pending = true;
		}

		public bool Tick()
		{
			if (!_pending)
			{
				return false;
			}
			if (!_session.InForeignWorld || _context.IsHost)
			{
				_pending = false;
				return false;
			}
			if (!_inventory.HasLocalPlayer)
			{
				return false;
			}
			_pending = false;
			_filter.Apply();
			int num = HighestItemTier();
			VisitorReport visitorReport = new VisitorReport(_modVersion, _fingerprint(), _session.Tier, num);
			_network.SendVisitorReport(visitorReport);
			_log.Info("Reporte al host: tope " + TierScale.Name(visitorReport.AppliedTier) + ", item mas alto " + TierScale.Name(num) + ", mods " + visitorReport.ModsFingerprint + ".");
			return true;
		}

		private int HighestItemTier()
		{
			int num = 0;
			int count = _inventory.Count;
			for (int i = 0; i < count; i++)
			{
				InventoryEntry inventoryEntry = _inventory.Get(i);
				int num2 = _gate.TierOf(inventoryEntry.Name);
				if (num2 > num)
				{
					num = num2;
				}
			}
			return num;
		}

		public void Reset()
		{
			_pending = false;
		}
	}
	public sealed class ReportWorldProgress
	{
		private readonly WorldTierCalculator _calculator;

		private readonly IWorldDefeats _defeats;

		private readonly IWorldProgressSource _progress;

		private readonly IModLogger _log;

		public ReportWorldProgress(WorldTierCalculator calculator, IWorldDefeats defeats, IWorldProgressSource progress, IModLogger log)
		{
			_calculator = calculator ?? throw new ArgumentNullException("calculator");
			_defeats = defeats ?? throw new ArgumentNullException("defeats");
			_progress = progress ?? throw new ArgumentNullException("progress");
			_log = log ?? throw new ArgumentNullException("log");
		}

		public void Execute(string day)
		{
			int tier = _progress.ReadTier();
			List<BossProgress> list = _calculator.Defeated(_defeats);
			_log.Info("WorldGate: dia " + day + ", rango " + TierScale.Name(tier) + " (" + tier + "). Jefes derrotados: " + ((list.Count == 0) ? "ninguno" : Join(list)));
			List<BossProgress> list2 = _calculator.UnreadableByGame(_defeats);
			if (list2.Count != 0)
			{
				_log.Warning("WorldGate: el juego no publica clave para " + Join(list2) + ", asi que solo el mod puede saber si estan derrotados. Si el mundo se abrio antes de instalar el mod, esas muertes no se ven y el rango queda mas bajo de lo real (no mas alto).");
			}
		}

		private static string Join(List<BossProgress> bosses)
		{
			string[] array = new string[bosses.Count];
			for (int i = 0; i < bosses.Count; i++)
			{
				array[i] = bosses[i].Prefab;
			}
			return string.Join(", ", array);
		}
	}
	public sealed class RestoreStashedItems
	{
		private readonly IInventoryGateway _inventory;

		private readonly IStash _stash;

		private readonly IGraveGateway _grave;

		private readonly IModLogger _log;

		private readonly List<StashedItem> _los

InvasionMod.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using InvasionMod.Composition;
using InvasionMod.Core.Application.Ports;
using InvasionMod.Core.Application.UseCases;
using InvasionMod.Core.Domain.Auditing;
using InvasionMod.Core.Domain.Discovery;
using InvasionMod.Core.Domain.Inventory;
using InvasionMod.Core.Domain.Mods;
using InvasionMod.Core.Domain.Net;
using InvasionMod.Core.Domain.Password;
using InvasionMod.Core.Domain.Progression;
using InvasionMod.Core.Domain.Spawn;
using InvasionMod.Core.Domain.Stash;
using InvasionMod.Core.Domain.Tiers;
using InvasionMod.Core.Domain.Travel;
using InvasionMod.Core.Domain.Visit;
using InvasionMod.Core.Domain.World;
using InvasionMod.Infrastructure.Audit;
using InvasionMod.Infrastructure.Config;
using InvasionMod.Infrastructure.Discovery;
using InvasionMod.Infrastructure.Mods;
using InvasionMod.Infrastructure.Net;
using InvasionMod.Infrastructure.Persistence;
using InvasionMod.Infrastructure.Services;
using InvasionMod.Infrastructure.Telemetry;
using InvasionMod.Infrastructure.Travel;
using InvasionMod.Infrastructure.Ui;
using InvasionMod.Infrastructure.Valheim;
using InvasionMod.Infrastructure.World;
using Jotunn;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.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 InvasionMod
{
	[BepInPlugin("com.null.invasionmod", "InvasionMod", "0.9.5")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.null.invasionmod";

		public const string PluginName = "InvasionMod";

		public const string PluginVersion = "0.9.5";

		private const float FaultThrottleSeconds = 30f;

		private readonly Harmony _harmony = new Harmony("com.null.invasionmod");

		private ModSettings _settings;

		private static readonly Dictionary<string, float> _lastFault = new Dictionary<string, float>();

		private void Awake()
		{
			_settings = new ModSettings(((BaseUnityPlugin)this).Config);
			ModServices.Build("0.9.5", _settings);
			_harmony.PatchAll();
			ModServices.Log.Info("InvasionMod 0.9.5 cargado");
			ModServices.Telemetry.Event("mod_loaded", "InvasionMod 0.9.5, Valheim " + Version.GetVersionString(false));
		}

		private static void Fault(string what, Exception e)
		{
			float time = Time.time;
			if (!_lastFault.TryGetValue(what, out var value) || !(time - value < 30f))
			{
				_lastFault[what] = time;
				ModServices.Log.Error($"{what} fallo, se saltea este frame: {e}");
			}
		}

		private void Update()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!ModServices.LocalPlayer.HasLocalPlayer)
				{
					if (ModServices.SearchPanel.IsOpen)
					{
						ModServices.SearchPanel.Close();
					}
				}
				else if (Input.GetKeyDown(_settings.SearchKey))
				{
					string text = ModServices.StatusProvider.PanelBlockReason ?? ModSetPolicy.BlockReason(ModServices.Mods.ForeignNames);
					if (text != null)
					{
						if (ModServices.SearchPanel.IsOpen)
						{
							ModServices.SearchPanel.Close();
						}
						ModServices.Notifier.NotifyPlayer(text);
					}
					else
					{
						ModServices.SearchPanel.Toggle();
					}
				}
				if (ModServices.SearchPanel.IsOpen && ModServices.StatusProvider.PanelBlockReason != null)
				{
					ModServices.SearchPanel.Close();
					ModServices.Notifier.NotifyPlayer(ModServices.StatusProvider.PanelBlockReason);
				}
			}
			catch (Exception e)
			{
				Fault("SearchPanel", e);
			}
			try
			{
				ModServices.Search.Tick();
				if (ModServices.RandomInvasion.Waiting && ModServices.StatusProvider.BlockReason != null)
				{
					ModServices.RandomInvasion.Cancel(ModServices.StatusProvider.BlockReason);
					ModServices.Notifier.NotifyPlayer("Invasion search stopped: " + ModServices.StatusProvider.BlockReason);
				}
				ModServices.RandomInvasion.Tick();
				ModServices.SearchIndicator.Tick(ModServices.Overlay.Active ? null : ModServices.RandomInvasion.StatusText);
				ModServices.SearchPanel.RefreshInvadeButton(ModServices.RandomInvasion.Waiting);
			}
			catch (Exception e2)
			{
				Fault("SearchOpenWorlds", e2);
			}
			try
			{
				ModServices.Overlay.Tick();
				if (Input.GetKeyDown((KeyCode)27))
				{
					ModServices.Travel.Cancel();
				}
				ModServices.Travel.Tick();
				ModServices.Badge.Tick();
			}
			catch (Exception e3)
			{
				Fault("TravelToWorld", e3);
			}
			try
			{
				ModServices.Filter.Tick();
				ModServices.Compliance.Tick();
			}
			catch (Exception e4)
			{
				Fault("EnforceProgressionGate", e4);
			}
			try
			{
				ModServices.HostLost.Tick();
			}
			catch (Exception e5)
			{
				Fault("DetectHostLost", e5);
			}
			try
			{
				ModServices.ReturnHome.Tick();
				ModServices.Curtain.Tick(ModServices.ReturnHome.CurtainAlpha, ModServices.ReturnHome.CurtainTitle, ModServices.ReturnHome.CurtainSubtitle);
			}
			catch (Exception e6)
			{
				Fault("ReturnHome", e6);
			}
			try
			{
				ModServices.InvasionPvp.Tick();
				ModServices.InvasionVisibility.Tick();
				ModServices.InvaderTag.Tick();
			}
			catch (Exception e7)
			{
				Fault("KeepInvasionPvp", e7);
			}
			try
			{
				ModServices.Telemetry.Tick();
			}
			catch (Exception e8)
			{
				Fault("ShipTelemetry", e8);
			}
			try
			{
				ModServices.Deaths.Tick();
			}
			catch (Exception e9)
			{
				Fault("AnnounceDeaths", e9);
			}
			if (!ModServices.Session.IsHost)
			{
				return;
			}
			try
			{
				ModServices.HostAudit.Tick();
				ModServices.VisitorCheck.Tick();
				ModServices.Expeller.Tick();
			}
			catch (Exception e10)
			{
				Fault("RunHostAudit", e10);
			}
			try
			{
				if (Input.GetKeyDown(_settings.PvpKey))
				{
					ModServices.ManualPvp.Execute();
				}
			}
			catch (Exception e11)
			{
				Fault("Toggle de PVP", e11);
			}
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
		}
	}
}
namespace InvasionMod.Patches
{
	[HarmonyPatch(typeof(Character), "OnDeath")]
	internal static class Character_OnDeath_RecordBoss
	{
		private static void Postfix(Character __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && __instance.m_boss && !((Object)(object)ZoneSystem.instance == (Object)null))
			{
				ModServices.BossDefeat.Execute(Utils.GetPrefabName(((Component)__instance).gameObject));
			}
		}
	}
	[HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")]
	internal static class GameCamera_UpdateMouseCapture_TravelCursor
	{
		private static void Postfix()
		{
			if (ModServices.SearchPanel.IsOpen && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				ZCursor.LockState = (CursorLockMode)0;
				ZCursor.Show();
			}
		}
	}
	[HarmonyPatch(typeof(PlayerController), "InInventoryEtc")]
	internal static class PlayerController_InInventoryEtc_ModPanel
	{
		private static void Postfix(ref bool __result)
		{
			if (ModServices.SearchPanel.IsOpen)
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class Player_OnDeath_ReturnHome
	{
		private static void Postfix(Player __instance)
		{
			try
			{
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					ModServices.ReturnHome.Request((ReturnHomeReason)0);
					string name;
					long num = KillerPeer(((Character)__instance).m_lastHit, out name);
					if (ModServices.Session.IsHost)
					{
						long num2 = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.GetUID() : 0);
						ModServices.HostDeath.Execute(__instance.GetPlayerName(), num, num2, name);
					}
					else
					{
						ModServices.Deaths.ReportLocalDeath(__instance.GetPlayerName(), num);
					}
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("DeathPatches: fallo al procesar la muerte, se sigue igual: " + ex);
			}
		}

		private static long KillerPeer(HitData hit, out string name)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Invalid comparison between Unknown and I4
			name = string.Empty;
			if (hit == null)
			{
				return 0L;
			}
			Character attacker = hit.GetAttacker();
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if (val != null)
			{
				name = val.GetPlayerName();
				ZDO val2 = (((Object)(object)((Character)val).m_nview != (Object)null) ? ((Character)val).m_nview.GetZDO() : null);
				if (val2 != null && val2.GetOwner() != 0L)
				{
					return val2.GetOwner();
				}
			}
			if ((int)hit.m_hitType != 2 || ((ZDOID)(ref hit.m_attacker)).IsNone())
			{
				return 0L;
			}
			return ((ZDOID)(ref hit.m_attacker)).UserID;
		}
	}
	[HarmonyPatch(typeof(Player), "CreateTombStone")]
	internal static class Player_CreateTombStone_KeepEquipment
	{
		internal static bool Active;

		private static void Prefix(Player __instance)
		{
			try
			{
				Active = (Object)(object)__instance == (Object)(object)Player.m_localPlayer && ModServices.ReturnHome.KeepsEquipmentOnDeath;
			}
			catch (Exception ex)
			{
				Active = false;
				ModServices.Log.Error("DeathPatches: no se pudo decidir si el invasor conserva el equipo: " + ex);
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			Active = false;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "UnequipAllItems")]
	internal static class Humanoid_UnequipAllItems_KeepEquipment
	{
		private static bool Prefix(Humanoid __instance)
		{
			if (Player_CreateTombStone_KeepEquipment.Active)
			{
				return !((Object)(object)__instance == (Object)(object)Player.m_localPlayer);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(EnemyHud), "UpdateHuds")]
	internal static class EnemyHud_UpdateHuds_RedBarForInvader
	{
		private struct NameStyle
		{
			public Color Color;

			public float OutlineWidth;

			public Color32 OutlineColor;
		}

		private static readonly Color NameColor = Color.red;

		private static readonly Color OutlineColor = Color.black;

		private const float OutlineWidth = 0.25f;

		private static readonly Dictionary<HudData, NameStyle> _painted = new Dictionary<HudData, NameStyle>();

		private static readonly List<HudData> _gone = new List<HudData>();

		private static void Postfix(EnemyHud __instance)
		{
			if (__instance.m_huds == null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			ForgetClosedHuds(__instance);
			foreach (KeyValuePair<Character, HudData> hud in __instance.m_huds)
			{
				Character key = hud.Key;
				Player val = (Player)(object)((key is Player) ? key : null);
				if (val != null && !((Object)(object)val == (Object)(object)localPlayer) && hud.Value != null)
				{
					if (InvaderTag.IsInvader(val))
					{
						Paint(hud.Value);
					}
					else
					{
						Unpaint(hud.Value);
					}
				}
			}
		}

		private static void Paint(HudData hud)
		{
			//IL_0014: 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)
			//IL_0096: 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)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: 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)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hud.m_healthFastFriendly != (Object)null)
			{
				hud.m_healthFastFriendly.SetColor(Color.red);
			}
			if ((Object)(object)hud.m_healthSlow != (Object)null)
			{
				hud.m_healthSlow.SetColor(Color.red);
			}
			TMP_Text name = (TMP_Text)(object)hud.m_name;
			if (!((Object)(object)name == (Object)null))
			{
				if (!_painted.ContainsKey(hud))
				{
					_painted[hud] = new NameStyle
					{
						Color = ((Graphic)name).color,
						OutlineWidth = name.outlineWidth,
						OutlineColor = name.outlineColor
					};
				}
				if (((Graphic)name).color != NameColor)
				{
					((Graphic)name).color = NameColor;
				}
				if (!Mathf.Approximately(name.outlineWidth, 0.25f))
				{
					name.outlineWidth = 0.25f;
				}
				Color32 outlineColor = name.outlineColor;
				if (!((Color32)(ref outlineColor)).Equals(Color32.op_Implicit(OutlineColor)))
				{
					name.outlineColor = Color32.op_Implicit(OutlineColor);
				}
			}
		}

		private static void Unpaint(HudData hud)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hud.m_healthFastFriendly != (Object)null)
			{
				hud.m_healthFastFriendly.ResetColor();
			}
			if ((Object)(object)hud.m_healthSlow != (Object)null)
			{
				hud.m_healthSlow.ResetColor();
			}
			if (!((Object)(object)hud.m_name == (Object)null) && _painted.TryGetValue(hud, out var value))
			{
				_painted.Remove(hud);
				((Graphic)hud.m_name).color = value.Color;
				((TMP_Text)hud.m_name).outlineWidth = value.OutlineWidth;
				((TMP_Text)hud.m_name).outlineColor = value.OutlineColor;
			}
		}

		private static void ForgetClosedHuds(EnemyHud enemyHud)
		{
			if (_painted.Count == 0)
			{
				return;
			}
			_gone.Clear();
			foreach (HudData key in _painted.Keys)
			{
				if ((Object)(object)key.m_character == (Object)null || !enemyHud.m_huds.ContainsKey(key.m_character))
				{
					_gone.Add(key);
				}
			}
			foreach (HudData item in _gone)
			{
				_painted.Remove(item);
			}
		}
	}
	internal static class ItemGate
	{
		public static bool Block(ItemData item)
		{
			string text = item?.m_shared?.m_name;
			if (!ModServices.Filter.ShouldBlock(text))
			{
				return false;
			}
			ModServices.Filter.NotifyBlocked(text);
			return true;
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData)
	})]
	internal static class Inventory_MoveItemToThis_Block
	{
		private static bool Prefix(Inventory fromInventory, ItemData item)
		{
			return !ItemGate.Block(item);
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData),
		typeof(int),
		typeof(int),
		typeof(int)
	})]
	internal static class Inventory_MoveItemToThisAmount_Block
	{
		private static bool Prefix(Inventory fromInventory, ItemData item)
		{
			return !ItemGate.Block(item);
		}
	}
	[HarmonyPatch(typeof(Humanoid), "Pickup")]
	internal static class Humanoid_Pickup_Block
	{
		private static bool Prefix(GameObject go)
		{
			return !ItemGate.Block((go == null) ? null : go.GetComponent<ItemDrop>()?.m_itemData);
		}
	}
	[HarmonyPatch(typeof(Humanoid), "PickupPrefab")]
	internal static class Humanoid_PickupPrefab_Block
	{
		private static bool Prefix(GameObject prefab)
		{
			return !ItemGate.Block((prefab == null) ? null : prefab.GetComponent<ItemDrop>()?.m_itemData);
		}
	}
	[HarmonyPatch(typeof(Player), "AddKnownItem")]
	internal static class Player_AddKnownItem_RefreshTier
	{
		private static void Prefix(Player __instance, out int __state)
		{
			__state = -1;
			try
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && ModServices.Session.IsHost)
				{
					__state = ModServices.InvasionTier.ReadTier();
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("No se pudo leer el rango antes de conocer un item: " + ex);
			}
		}

		private static void Postfix(int __state)
		{
			if (__state < 0)
			{
				return;
			}
			try
			{
				int num = ModServices.InvasionTier.ReadTier();
				if (num > __state)
				{
					ModServices.Log.Info("El rango del personaje subio a " + TierScale.Name(num) + ": se republica el tope.");
					ModServices.WorldOpen.Refresh();
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("No se pudo revisar el rango al conocer un item: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnDestroy")]
	internal static class ZNet_OnDestroy_ResetVisit
	{
		private static void Postfix()
		{
			ModServices.OnWorldLeft();
		}
	}
	[HarmonyPatch(typeof(ZNet), "Disconnect")]
	internal static class ZNet_Disconnect_InvaderLeft
	{
		private static void Prefix(ZNetPeer peer)
		{
			try
			{
				if (peer != null)
				{
					ModServices.WelcomeInvader.OnPeerLeft(peer.m_uid);
					ModServices.VisitorCheck.OnPeerLeft(peer.m_uid);
					ModServices.Expeller.OnPeerLeft(peer.m_uid);
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("No se pudo procesar la salida de un jugador: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Awake")]
	internal static class ZNet_Awake_RegisterRpc
	{
		private static void Postfix()
		{
			InvasionModNet.Register(delegate(bool forced)
			{
				ModServices.ManualPvp.ApplyRemoteToggle(forced);
			}, OnGate, OnHostDied, OnPlayerDied, OnInvaderArrived, OnInvasionState, OnSpawnAnchor, OnVisitorReport, OnExpelled, OnNewPeer);
		}

		private static void OnHostDied(long sender, string hostName, long killer)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				long num = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.GetUID() : 0);
				ModServices.ReturnHome.OnHostDied(sender, InvasionModNet.ServerPeerId, num, killer, hostName);
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("El handler de la muerte del host fallo: " + ex);
			}
		}

		private static void OnExpelled(long sender, string reason)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Localization.instance != null && !string.IsNullOrEmpty(reason))
				{
					reason = Localization.instance.Localize(reason);
				}
				ModServices.ReturnHome.OnExpelled(sender, InvasionModNet.ServerPeerId, reason);
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("El handler de la expulsion fallo: " + ex);
			}
		}

		private static void OnPlayerDied(long sender, string playerName, long killer)
		{
			try
			{
				long num = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.GetUID() : 0);
				string text = ModServices.KillReward.Execute(sender, num, killer);
				ModServices.Deaths.OnPlayerDied(sender, num, playerName, text);
				ModServices.WelcomeInvader.OnPlayerDied(sender);
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("El handler del aviso de muerte fallo: " + ex);
			}
		}

		private static void OnInvaderArrived(long sender, int characterTier)
		{
			try
			{
				ModServices.WelcomeInvader.OnInvaderAnnounced(sender, PlayerName(sender), characterTier);
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("No se pudo procesar la llegada del invasor " + sender + ": " + ex);
			}
		}

		private static void OnInvasionState(long sender, bool active)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ModServices.InvasionPvp.OnInvasionState(active, sender, InvasionModNet.ServerPeerId, ModServices.Session.IsHost);
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("El handler del estado de invasion fallo: " + ex);
			}
		}

		private static void OnSpawnAnchor(long sender, SpawnPoint host)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				float num = Random.Range(0f, (float)Math.PI * 2f);
				float num2 = Random.Range(100f, 300f);
				ModServices.InvaderSpawn.ReceiveAnchor(sender, InvasionModNet.ServerPeerId, host, num, num2);
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("El handler de la posicion del host fallo: " + ex);
			}
		}

		private static void OnGate(long sender, int tier, HostSkills hostSkills)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			long serverPeerId = InvasionModNet.ServerPeerId;
			try
			{
				if ((int)ModServices.GateReceiver.Execute(tier, hostSkills, sender, serverPeerId) == 0)
				{
					ModServices.Compliance.OnGateAccepted();
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("El handler del tope de progresion fallo: " + ex);
			}
		}

		private static void OnNewPeer(long peer)
		{
			try
			{
				ModServices.InvaderSpawn.SendAnchor(peer);
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("No se pudo mandar la posicion del host al peer " + peer + ": " + ex);
			}
			try
			{
				ModServices.VisitorCheck.OnPeerConnected(peer);
				ModServices.WelcomeInvader.OnPeerConnected(peer);
			}
			catch (Exception ex2)
			{
				ModServices.Log.Error("No se pudo procesar la entrada del peer " + peer + ": " + ex2);
			}
		}

		private static void OnVisitorReport(long sender, VisitorReport report)
		{
			try
			{
				ModServices.VisitorCheck.OnReport(sender, report);
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("No se pudo procesar el reporte del peer " + sender + ": " + ex);
			}
		}

		private static string PlayerName(long peer)
		{
			try
			{
				ZNet instance = ZNet.instance;
				return ((instance == null) ? null : instance.GetPeer(peer)?.m_playerName) ?? string.Empty;
			}
			catch (Exception ex)
			{
				ModServices.Log.Warning("No se pudo leer el nombre del peer " + peer + ": " + ex.Message);
				return string.Empty;
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_ClientHandshake")]
	internal static class ZNet_RPC_ClientHandshake_UseTripPassword
	{
		private static void Prefix(bool __1, out string __state)
		{
			__state = FejdStartup.ServerPassword;
			string text = ModServices.Passwords.Take();
			if (ModServices.Settings.AutoFill && __1 && !string.IsNullOrEmpty(text))
			{
				FejdStartup.ServerPassword = text;
				ModServices.Log.Info("Clave del mundo contestada sola: el invasor no la escribe.");
			}
		}

		private static void Postfix(string __state)
		{
			FejdStartup.ServerPassword = __state;
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnPasswordEntered")]
	internal static class ZNet_OnPasswordEntered_UseConfigPassword
	{
		private static void Prefix(ref string __0)
		{
			if (ModServices.Settings.AutoFill)
			{
				string text = ModServices.Passwords.Take();
				if (!string.IsNullOrEmpty(text))
				{
					__0 = text;
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Update")]
	internal static class ZNet_Update_AutoFillPasswordDialog
	{
		private static void Postfix(ZNet __instance)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			if (!ModServices.Settings.AutoFill)
			{
				return;
			}
			try
			{
				if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.m_passwordDialog == (Object)null || !ZNet.IsPasswordDialogShowing())
				{
					return;
				}
				string text = ModServices.Passwords.Take();
				if (!string.IsNullOrEmpty(text))
				{
					TMP_InputField componentInChildren = ((Component)__instance.m_passwordDialog).GetComponentInChildren<TMP_InputField>();
					if ((Object)(object)componentInChildren == (Object)null)
					{
						ModServices.Log.Warning("El dialogo de clave abrio pero no tiene campo de texto, no se puede autocompletar. Hay que escribirla a mano.");
						return;
					}
					componentInChildren.text = text;
					((Selectable)componentInChildren).Select();
					componentInChildren.OnSubmit(new BaseEventData(EventSystem.current));
					ModServices.Log.Info("Clave completada sola, el visitante no la escribe.");
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("No se pudo completar la clave sola: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_ServerHandshake")]
	internal static class ZNet_RPC_ServerHandshake_AlwaysAskPassword
	{
		private static void Prefix(ref string __1)
		{
			if (ModServices.Settings.ForceCommunity && !string.IsNullOrEmpty(__1))
			{
				__1 = string.Empty;
				ModServices.Log.Info("Alguien entra con clave secreta de la plataforma: igual se le pide la clave del mundo.");
			}
		}
	}
	[HarmonyPatch(typeof(Player), "SetPVP")]
	internal static class Player_SetPVP_WhenForced
	{
		private static void Prefix(ref bool enabled)
		{
			if (ModServices.StatusProvider.IsPvpForced)
			{
				enabled = true;
			}
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "OnWorldStart")]
	internal static class FejdStartup_OnWorldStart_ForceServerOptions
	{
		private static bool _warned;

		private static bool Prefix(FejdStartup __instance)
		{
			if (!ModServices.Settings.ForceCommunity)
			{
				return true;
			}
			WarnOnceIfForeignMods();
			World world = __instance.m_world;
			if (!ModServices.Settings.TryValidate(world, out var error))
			{
				ModServices.Log.Error("No se forzo comunidad: " + error + ". Arreglar 'Server > Password' en BepInEx/config/com.null.invasionmod.cfg, o poner 'ForceCommunity' en false para abrir un mundo privado.");
				return true;
			}
			string password = ModServices.Settings.Password;
			if ((Object)(object)__instance.m_openServerToggle != (Object)null)
			{
				__instance.m_openServerToggle.isOn = true;
			}
			if ((Object)(object)__instance.m_publicServerToggle != (Object)null)
			{
				__instance.m_publicServerToggle.isOn = true;
			}
			if ((Object)(object)__instance.m_crossplayServerToggle != (Object)null)
			{
				__instance.m_crossplayServerToggle.isOn = true;
			}
			if ((Object)(object)__instance.m_serverPassword != (Object)null)
			{
				((TMP_InputField)__instance.m_serverPassword).text = password;
			}
			ModServices.Settings.SetWorldPassword(password);
			ModServices.Log.Info("Servidor forzado: comunidad + crossplay, clave de la config " + $"({password.Length} caracteres). Rango del mundo: {TierScale.Name(ModServices.WorldProgress.ReadTier())}.");
			return true;
		}

		private static void WarnOnceIfForeignMods()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			if (_warned)
			{
				return;
			}
			_warned = true;
			IReadOnlyList<string> foreignNames = ModServices.Mods.ForeignNames;
			if (foreignNames.Count == 0)
			{
				return;
			}
			string text = string.Join(", ", foreignNames);
			ModServices.Log.Warning("Plugins de mas: " + text + ". Las invasiones quedan desactivadas.");
			try
			{
				if (UnifiedPopup.IsAvailable())
				{
					UnifiedPopup.Push((PopupBase)new WarningPopup("InvasionMod: other mods loaded", "You have these mods loaded:\n\n" + text + "\n\nInvasions are disabled: you cannot invade, open your world to invaders, or join another player's world. Only InvasionMod and Jotunn are allowed, so that everyone plays under the same conditions.\n\nRemove them (or use a separate mod manager profile) to play invasions.", (PopupButtonCallback)null, true));
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Warning("No se pudo mostrar el aviso de mods: " + ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "Update")]
	internal static class FejdStartup_Update_LockServerOptions
	{
		private static void Postfix(FejdStartup __instance)
		{
			if (!ModServices.Settings.ForceCommunity)
			{
				return;
			}
			string password = ModServices.Settings.Password;
			if ((Object)(object)__instance.m_serverPassword != (Object)null && string.IsNullOrEmpty(((TMP_InputField)__instance.m_serverPassword).text))
			{
				((TMP_InputField)__instance.m_serverPassword).text = password;
			}
			if ((Object)(object)__instance.m_openServerToggle != (Object)null && !__instance.m_openServerToggle.isOn)
			{
				__instance.m_openServerToggle.isOn = true;
			}
			if ((Object)(object)__instance.m_publicServerToggle != (Object)null && !__instance.m_publicServerToggle.isOn)
			{
				__instance.m_publicServerToggle.isOn = true;
			}
			if ((Object)(object)__instance.m_crossplayServerToggle != (Object)null && !__instance.m_crossplayServerToggle.isOn)
			{
				__instance.m_crossplayServerToggle.isOn = true;
			}
			Lock(__instance.m_openServerToggle);
			Lock(__instance.m_publicServerToggle);
			Lock(__instance.m_crossplayServerToggle);
			if ((Object)(object)__instance.m_serverPassword != (Object)null)
			{
				if (((Selectable)__instance.m_serverPassword).interactable)
				{
					((Selectable)__instance.m_serverPassword).interactable = false;
				}
				if (!((TMP_InputField)__instance.m_serverPassword).readOnly)
				{
					((TMP_InputField)__instance.m_serverPassword).readOnly = true;
				}
			}
		}

		private static void Lock(Toggle toggle)
		{
			if ((Object)(object)toggle != (Object)null && ((Selectable)toggle).interactable)
			{
				((Selectable)toggle).interactable = false;
			}
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "OnWorldStart")]
	internal static class FejdStartup_OnWorldStart_RememberHome
	{
		private static void Postfix(FejdStartup __instance)
		{
			try
			{
				if (__instance.m_startingWorld)
				{
					ModServices.HomeWorld.Remember(__instance);
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Warning("No se pudieron anotar las opciones del mundo propio: " + ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(Game), "FindSpawnPoint")]
	internal static class Game_FindSpawnPoint_NearHost
	{
		private const float LoadGrace = 4f;

		private const float SpawnLift = 0.5f;

		private static float _waited;

		private static SpawnPoint _waitingFor;

		private static bool Prefix(Game __instance, ref Vector3 point, ref bool usedLogoutPoint, float dt, ref bool __result)
		{
			try
			{
				return Run(__instance, ref point, ref usedLogoutPoint, dt, ref __result);
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("SpawnPatches: fallo al elegir donde aparecer, decide el juego: " + ex);
				return true;
			}
		}

		private static bool Run(Game game, ref Vector3 point, ref bool usedLogoutPoint, float dt, ref bool result)
		{
			//IL_0025: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			if (!game.m_firstSpawn || game.m_respawnAfterDeath || ModServices.Session.IsHost)
			{
				return true;
			}
			PlaceInvaderSpawn invaderSpawn = ModServices.InvaderSpawn;
			point = Vector3.zero;
			usedLogoutPoint = false;
			result = false;
			if (!invaderSpawn.HasTarget)
			{
				return !invaderSpawn.ShouldWaitForAnchor();
			}
			SpawnPoint target = invaderSpawn.Target;
			if (!Same(target, _waitingFor))
			{
				_waitingFor = target;
				_waited = 0f;
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(((SpawnPoint)(ref target)).X, ((SpawnPoint)(ref target)).Y, ((SpawnPoint)(ref target)).Z);
			ZNet.instance.SetReferencePosition(val);
			_waited += dt;
			if (_waited < 4f || !ZNetScene.instance.IsAreaReady(val))
			{
				return false;
			}
			SpawnPoint val2 = default(SpawnPoint);
			if (!invaderSpawn.TryConfirmTarget(ref val2))
			{
				return false;
			}
			float num = ((SpawnPoint)(ref val2)).Y;
			float num2 = default(float);
			if (ZoneSystem.instance.GetSolidHeight(new Vector3(((SpawnPoint)(ref val2)).X, num, ((SpawnPoint)(ref val2)).Z), ref num2, 1000) && num2 > num)
			{
				num = num2;
			}
			point = new Vector3(((SpawnPoint)(ref val2)).X, num + 0.5f, ((SpawnPoint)(ref val2)).Z);
			invaderSpawn.Complete(val2);
			result = true;
			return false;
		}

		private static bool Same(SpawnPoint a, SpawnPoint b)
		{
			if (((SpawnPoint)(ref a)).X == ((SpawnPoint)(ref b)).X)
			{
				return ((SpawnPoint)(ref a)).Z == ((SpawnPoint)(ref b)).Z;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Skills), "GetSkillLevel")]
	internal static class Skills_GetSkillLevel_Cap
	{
		private static void Postfix(Skills __instance, SkillType skillType, ref float __result)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected I4, but got Unknown
			if (!((Object)(object)__instance.m_player == (Object)null) && !((Object)(object)__instance.m_player != (Object)(object)Player.m_localPlayer))
			{
				__result = Mathf.Floor(ModServices.StatCaps.CapSkill((int)skillType, __result));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
	internal static class Player_GetTotalFoodValue_Cap
	{
		private static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !ModServices.StatCaps.IsActive)
			{
				return;
			}
			try
			{
				foreach (Food food in __instance.m_foods)
				{
					if (!ModServices.StatCaps.FoodCounts(food?.m_item?.m_shared?.m_name))
					{
						hp -= food.m_health;
						stamina -= food.m_stamina;
						eitr -= food.m_eitr;
					}
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("No se pudo aplicar el tope a la comida: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "StartGuardianPower")]
	internal static class Player_StartGuardianPower_Cap
	{
		private static bool Prefix(Player __instance, ref bool __result)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			string guardianPowerName = __instance.GetGuardianPowerName();
			if (string.IsNullOrEmpty(guardianPowerName) || ModServices.StatCaps.CanUseGuardianPower(guardianPowerName))
			{
				return true;
			}
			ModServices.StatCaps.NotifyPowerBlocked(guardianPowerName);
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "ActivateGuardianPower")]
	internal static class Player_ActivateGuardianPower_Cap
	{
		private static bool Prefix(Player __instance, ref bool __result)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || ModServices.StatCaps.CanUseGuardianPower(__instance.GetGuardianPowerName()))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "OnSkillLevelup")]
	internal static class Player_OnSkillLevelup_RefreshCap
	{
		private static void Postfix(Player __instance)
		{
			try
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && ModServices.Session.IsHost && ModServices.Session.RemotePeerCount > 0)
				{
					ModServices.WorldOpen.Refresh();
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("No se pudo reenviar el tope al subir de nivel: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(ZSteamMatchmaking), "OnLobbyCreated")]
	internal static class ZSteamMatchmaking_OnLobbyCreated_PublishData
	{
		private static void Postfix(LobbyCreated_t data, bool ioError)
		{
			if (!ioError)
			{
				ModServices.LobbyPublisher.Publish();
			}
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	internal static class Character_RPC_Damage_ProtectReturning
	{
		private static bool Prefix(Character __instance)
		{
			try
			{
				return !((Object)(object)__instance == (Object)(object)Player.m_localPlayer) || !ModServices.ReturnHome.IsProtected;
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("VictoryPatches: no se pudo revisar el dano al invasor: " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "StartAttack")]
	internal static class Humanoid_StartAttack_ProtectReturning
	{
		private static bool Prefix(Humanoid __instance, ref bool __result)
		{
			try
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && ModServices.ReturnHome.IsProtected)
				{
					__result = false;
					return false;
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("VictoryPatches: no se pudo revisar el ataque del invasor: " + ex);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(TombStone), "Interact")]
	internal static class TombStone_Interact_ProtectReturning
	{
		private static bool Prefix(Humanoid character, ref bool __result)
		{
			try
			{
				if ((Object)(object)character == (Object)(object)Player.m_localPlayer && ModServices.ReturnHome.IsProtected)
				{
					((Character)character).Message((MessageType)2, "The invasion is over: you cannot loot tombstones.", 0, (Sprite)null, false);
					__result = false;
					return false;
				}
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("VictoryPatches: no se pudo revisar la tumba: " + ex);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	internal static class Character_RPC_Damage_BlockUnverified
	{
		private static bool Prefix(Character __instance, HitData hit)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (hit == null || ModServices.VisitorCheck.UnverifiedCount == 0 || !ModServices.Session.IsHost)
				{
					return true;
				}
				if ((Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsOwner())
				{
					return true;
				}
				long num = ModServices.Peers.PeerOfCharacter(hit.m_attacker);
				return num == 0L || !ModServices.VisitorCheck.BlocksDamageFrom(num);
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("No se pudo revisar el dano de un visitante: " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class Player_OnSpawned_WorldReady
	{
		private static World _auditedWorld;

		private static void Postfix(Player __instance)
		{
			try
			{
				Run();
			}
			catch (Exception ex)
			{
				ModServices.Log.Error("WorldLoadPatches: fallo en OnSpawned, se sigue igual: " + ex);
			}
		}

		private static void Run()
		{
			ModServices.InvasionPvp.Execute();
			if (ModServices.Session.IsHost)
			{
				World worldIfIsHost = ZNet.GetWorldIfIsHost();
				if (_auditedWorld != worldIfIsHost)
				{
					_auditedWorld = worldIfIsHost;
					ModServices.OnWorldLoaded();
					string text = (((Object)(object)EnvMan.instance == (Object)null) ? "?" : EnvMan.instance.GetDay().ToString());
					ModServices.ProgressReport.Execute(text);
					ModServices.Telemetry.Event("world_loaded", "dia " + text + ", rango " + TierScale.Name(ModServices.WorldOpen.CurrentTier));
				}
				ModServices.WorldOpen.Refresh();
				int num = ModServices.StashRestore.Execute();
				int buried = ModServices.StashRestore.Buried;
				int notDelivered = ModServices.StashRestore.NotDelivered;
				string text2 = RestoreStashedItems.ArrivalMessage(num - buried - notDelivered, buried, notDelivered, ModServices.ReturnHome.TakePendingReward());
				string text3 = ModServices.ReturnHome.TakeArrivalNotice();
				if (text2 != null || text3 != null)
				{
					ModServices.Notifier.NotifyPlayer((text3 == null) ? text2 : ((text2 == null) ? text3 : (text3 + " " + text2)));
				}
			}
			else if (ModServices.Visit.InForeignWorld)
			{
				ModServices.Filter.Apply();
			}
		}
	}
}
namespace InvasionMod.Infrastructure.World
{
	internal sealed class SteamLobbyPublisher : IWorldLobbyPublisher
	{
		private readonly VisitSession _session;

		private readonly ISessionContext _context;

		private readonly IWorldProgressSource _world;

		private readonly Func<string> _password;

		private readonly Func<string> _fingerprint;

		private readonly Func<bool> _creativeUsed;

		private readonly string _version;

		private readonly IModLogger _log;

		public SteamLobbyPublisher(VisitSession session, ISessionContext context, IWorldProgressSource world, string version, Func<string> password, Func<string> fingerprint, Func<bool> creativeUsed, IModLogger log)
		{
			_log = log ?? throw new ArgumentNullException("log");
			_session = session;
			_context = context;
			_world = world;
			_version = version;
			_password = password;
			_fingerprint = fingerprint;
			_creativeUsed = creativeUsed;
		}

		public void Publish()
		{
			//IL_0019: 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_008e: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			if (_context.IsHost)
			{
				ZSteamMatchmaking instance = ZSteamMatchmaking.m_instance;
				if (instance != null)
				{
					CSteamID myLobby = instance.m_myLobby;
					bool flag = _creativeUsed?.Invoke() ?? false;
					bool flag2 = _session.WorldOpen && !flag;
					int num = _world.ReadTier();
					string text = _password?.Invoke() ?? string.Empty;
					string text2 = _fingerprint?.Invoke() ?? string.Empty;
					SteamMatchmaking.SetLobbyData(myLobby, "invasionmod_active", flag2 ? "1" : "0");
					SteamMatchmaking.SetLobbyData(myLobby, "invasionmod_version", _version);
					SteamMatchmaking.SetLobbyData(myLobby, "invasionmod_tier", num.ToString());
					SteamMatchmaking.SetLobbyData(myLobby, "invasionmod_password", text);
					SteamMatchmaking.SetLobbyData(myLobby, "invasionmod_mods", text2);
					SteamMatchmaking.SetLobbyData(myLobby, "invasionmod_creative", flag ? "1" : "0");
					_log.Info(string.Format("Lobby de Steam ({0}) actualizado: {1}={2}, ", myLobby.m_SteamID, "invasionmod_active", flag2) + "invasionmod_version=" + _version + ", invasionmod_tier=" + TierScale.Name(num) + ", invasionmod_mods=" + text2 + ", " + string.Format("{0}={1}", "invasionmod_creative", flag));
				}
			}
		}
	}
	internal sealed class ValheimWorldModifiers : IWorldModifiers
	{
		public bool IsActive(string modifier)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance != (Object)null && Enum.TryParse<GlobalKeys>(modifier, out GlobalKeys result))
			{
				return instance.GetGlobalKey(result);
			}
			return false;
		}

		public bool IsMarked(string key)
		{
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance != (Object)null)
			{
				return instance.GetGlobalKey(key);
			}
			return false;
		}

		public void Mark(string key)
		{
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				ZoneSystem.instance.SetGlobalKey(key);
			}
		}
	}
	public sealed class ValheimWorldProgressSource : IWorldProgressSource, IWorldProgressCache
	{
		private readonly IWorldDefeats _defeats;

		private readonly WorldTierCalculator _calculator;

		private int _cachedTier = -1;

		public ValheimWorldProgressSource(WorldTierCalculator calculator, IWorldDefeats defeats)
		{
			_calculator = calculator;
			_defeats = defeats;
		}

		public int ReadTier()
		{
			if (_cachedTier >= 0)
			{
				return _cachedTier;
			}
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				_cachedTier = 0;
				return _cachedTier;
			}
			_cachedTier = _calculator.ComputeTier(_defeats);
			return _cachedTier;
		}

		public void Invalidate()
		{
			_cachedTier = -1;
		}
	}
	public sealed class WorldDefeats : IWorldDefeats
	{
		private static readonly Dictionary<string, GlobalKeys> VanillaKeys = new Dictionary<string, GlobalKeys>(StringComparer.Ordinal)
		{
			{
				"defeated_eikthyr",
				(GlobalKeys)42
			},
			{
				"defeated_bonemass",
				(GlobalKeys)46
			},
			{
				"defeated_gdking",
				(GlobalKeys)45
			},
			{
				"defeated_dragon",
				(GlobalKeys)43
			},
			{
				"defeated_goblinking",
				(GlobalKeys)44
			}
		};

		private readonly IModLogger _log;

		public WorldDefeats(IModLogger log)
		{
			_log = log ?? throw new ArgumentNullException("log");
		}

		public bool IsDefeated(BossProgress boss)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (boss == null)
			{
				return false;
			}
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if (boss.VanillaKey != null && (VanillaKeys.TryGetValue(boss.VanillaKey, out var value) ? instance.GetGlobalKey(value) : instance.GetGlobalKey(boss.VanillaKey)))
			{
				return true;
			}
			return instance.GetGlobalKey(BossTable.ModKeyName(boss.Prefab));
		}

		public void Record(BossProgress boss)
		{
			if (boss != null && !((Object)(object)ZoneSystem.instance == (Object)null))
			{
				string text = BossTable.ModKeyName(boss.Prefab);
				if (!ZoneSystem.instance.GetGlobalKey(text))
				{
					_log.Info("WorldGate: jefe registrado por el mod: " + boss.Prefab + " -> " + TierScale.Name(boss.Tier) + " (" + text + ")");
				}
				ZoneSystem.instance.SetGlobalKey(text);
			}
		}
	}
}
namespace InvasionMod.Infrastructure.Valheim
{
	internal sealed class GameClock : IClock
	{
		public float Now => Time.time;

		public long Frame => Time.frameCount;
	}
	public sealed class InvaderTag
	{
		private const string Key = "InvasionMod_Invader";

		private readonly VisitSession _visit;

		public InvaderTag(VisitSession visit)
		{
			_visit = visit ?? throw new ArgumentNullException("visit");
		}

		internal static bool IsInvader(Player player)
		{
			ZDO val = (((Object)(object)player != (Object)null && (Object)(object)((Character)player).m_nview != (Object)null) ? ((Character)player).m_nview.GetZDO() : null);
			if (val != null)
			{
				return val.GetBool("InvasionMod_Invader", false);
			}
			return false;
		}

		public void Tick()
		{
			if (_visit.IsInvader)
			{
				Player localPlayer = Player.m_localPlayer;
				ZDO val = (((Object)(object)localPlayer != (Object)null && (Object)(object)((Character)localPlayer).m_nview != (Object)null && ((Character)localPlayer).m_nview.IsOwner()) ? ((Character)localPlayer).m_nview.GetZDO() : null);
				if (val != null && !val.GetBool("InvasionMod_Invader", false))
				{
					val.Set("InvasionMod_Invader", true);
				}
			}
		}
	}
	internal sealed class ValheimGraveGateway : IGraveGateway
	{
		private readonly IModLogger _log;

		public ValheimGraveGateway(IModLogger log)
		{
			_log = log ?? throw new ArgumentNullException("log");
		}

		public IReadOnlyList<StashedItem> Bury(IReadOnlyList<StashedItem> items)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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)
			List<StashedItem> list = new List<StashedItem>();
			if (items == null || items.Count == 0)
			{
				return list;
			}
			Inventory val = null;
			int i = 0;
			try
			{
				for (; i < items.Count; i++)
				{
					StashedItem val2 = items[i];
					string text = ValheimInventoryGateway.ResolvePrefabName(val2.Name);
					if (text == null)
					{
						list.Add(val2);
						continue;
					}
					if (val == null)
					{
						val = Create();
						if (val == null)
						{
							break;
						}
					}
					Vector2i val3 = val.FindEmptySlot(true);
					if (val3.x < 0)
					{
						list.Add(val2);
					}
					else
					{
						val.AddItem(text, val2.Stack, val2.Durability, val3, false, val2.Quality, val2.Variant, val2.CrafterId, val2.CrafterName ?? string.Empty, new Dictionary<string, string>(), val2.WorldLevel, true, false, false);
					}
				}
			}
			catch (Exception ex)
			{
				_log.Error("No se pudo llenar la tumba de lo que volvio de la invasion: " + ex);
			}
			for (; i < items.Count; i++)
			{
				list.Add(items[i]);
			}
			return list;
		}

		private Inventory Create()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)localPlayer.m_tombstone == (Object)null || val == null)
			{
				_log.Warning("No se pudo crear la tumba: no hay jugador local, prefab de tumba o perfil.");
				return null;
			}
			GameObject obj = Object.Instantiate<GameObject>(localPlayer.m_tombstone, ((Character)localPlayer).GetCenterPoint(), ((Component)localPlayer).transform.rotation);
			Container component = obj.GetComponent<Container>();
			TombStone component2 = obj.GetComponent<TombStone>();
			if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
			{
				_log.Warning("No se pudo crear la tumba: el prefab no trae Container o TombStone.");
				return null;
			}
			component2.Setup(val.GetName(), val.GetPlayerID());
			return component.GetInventory();
		}
	}
	internal sealed class ValheimHostSkillSource : IHostSkillSource
	{
		private readonly Func<int> _margin;

		public ValheimHostSkillSource(Func<int> margin)
		{
			_margin = margin ?? throw new ArgumentNullException("margin");
		}

		public HostSkills Read()
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected I4, but got Unknown
			List<KeyValuePair<int, float>> list = new List<KeyValuePair<int, float>>();
			Skills val = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Character)Player.m_localPlayer).GetSkills() : null);
			if ((Object)(object)val != (Object)null)
			{
				foreach (Skill skill in val.GetSkillList())
				{
					if (skill?.m_info != null)
					{
						list.Add(new KeyValuePair<int, float>((int)skill.m_info.m_skill, skill.m_level));
					}
				}
			}
			return new HostSkills((IEnumerable<KeyValuePair<int, float>>)list, _margin());
		}
	}
	internal sealed class ValheimInventoryGateway : IInventoryGateway
	{
		private readonly IModLogger _log;

		private static readonly List<ItemData> Empty = new List<ItemData>(0);

		public bool HasLocalPlayer => (Object)(object)Player.m_localPlayer != (Object)null;

		public int Count => Items(Resolve()).Count;

		public ValheimInventoryGateway(IModLogger log)
		{
			_log = log ?? throw new ArgumentNullException("log");
		}

		public InventoryEntry Get(int index)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = Items(Resolve())[index];
			return new InventoryEntry(val.m_shared?.m_name, val.m_stack, val.m_durability, val.m_quality, val.m_variant, val.m_worldLevel, val.m_crafterID, val.m_crafterName, val.m_equipped);
		}

		public string PrefabNameAt(int index)
		{
			ItemData val = Items(Resolve())[index];
			if (!((Object)(object)val.m_dropPrefab != (Object)null))
			{
				return val.m_shared?.m_name;
			}
			return ((Object)val.m_dropPrefab).name;
		}

		public void Unequip(int index)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				((Humanoid)localPlayer).UnequipItem(Items(((Humanoid)localPlayer).GetInventory())[index], false);
			}
		}

		public void RemoveAt(int index)
		{
			Inventory val = Resolve();
			if (val != null)
			{
				List<ItemData> list = Items(val);
				if (index >= 0 && index < list.Count)
				{
					val.RemoveItem(list[index]);
				}
			}
		}

		public bool Add(StashedItem item)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || item == null)
			{
				return false;
			}
			string text = ResolvePrefabName(item.Name);
			if (text == null)
			{
				_log.Warning("No existe el item \"" + item.Name + "\" en este juego; se deja en la stash.");
				return false;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			if (!inventory.CanAddItem(ObjectDB.instance.GetItemPrefab(text), item.Stack))
			{
				return false;
			}
			ItemData val = inventory.AddItem(text, item.Stack, item.Quality, item.Variant, item.CrafterId, item.CrafterName ?? string.Empty, false, true);
			if (val == null)
			{
				return false;
			}
			List<ItemData> list = Items(inventory);
			int num = list.IndexOf(val);
			if (num >= 0)
			{
				list[num].m_durability = item.Durability;
			}
			return true;
		}

		public void NotifyChanged()
		{
			Inventory val = Resolve();
			if (val != null)
			{
				val.Changed(true, false);
			}
		}

		public void SaveProfile()
		{
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Game.instance == (Object)null))
			{
				PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
				if (playerProfile == null)
				{
					_log.Warning("No hay PlayerProfile, el cambio no se guardo.");
					return;
				}
				playerProfile.SavePlayerData(Player.m_localPlayer);
				Game.instance.SavePlayerProfile(false, false);
			}
		}

		internal static string ResolvePrefabName(string name)
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			if ((Object)(object)instance.GetItemPrefab(name) != (Object)null)
			{
				return name;
			}
			foreach (GameObject item in instance.m_items)
			{
				ItemDrop val = (((Object)(object)item != (Object)null) ? item.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val != (Object)null && string.Equals(val.m_itemData.m_shared.m_name, name, StringComparison.Ordinal))
				{
					return ((Object)item).name;
				}
			}
			return null;
		}

		private static Inventory Resolve()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				return ((Humanoid)localPlayer).GetInventory();
			}
			return null;
		}

		private static List<ItemData> Items(Inventory inventory)
		{
			if (inventory != null)
			{
				return inventory.GetAllItems();
			}
			return Empty;
		}
	}
	internal sealed class ValheimKnownItems : IKnownItems
	{
		private readonly List<string> _names = new List<string>();

		private Player _copiedFrom;

		private int _copiedCount = -1;

		public bool HasLocalPlayer => (Object)(object)Player.m_localPlayer != (Object)null;

		public int Count
		{
			get
			{
				Sync();
				return _names.Count;
			}
		}

		public string Get(int index)
		{
			Sync();
			if (index < 0 || index >= _names.Count)
			{
				return null;
			}
			return _names[index];
		}

		private void Sync()
		{
			Player localPlayer = Player.m_localPlayer;
			HashSet<string> hashSet = (((Object)(object)localPlayer != (Object)null) ? localPlayer.m_knownMaterial : null);
			if (hashSet == null)
			{
				_names.Clear();
				_copiedFrom = null;
				_copiedCount = -1;
			}
			else if (_copiedFrom != localPlayer || _copiedCount != hashSet.Count)
			{
				_names.Clear();
				_names.AddRange(hashSet);
				_copiedFrom = localPlayer;
				_copiedCount = hashSet.Count;
			}
		}
	}
	internal sealed class ValheimPlayerGateway : IPlayerGateway
	{
		public bool HasLocalPlayer => (Object)(object)Player.m_localPlayer != (Object)null;

		public bool IsPvpEnabled
		{
			get
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					return ((Character)localPlayer).IsPVPEnabled();
				}
				return false;
			}
		}

		public bool IsPositionPublic
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsReferencePositionPublic();
				}
				return false;
			}
		}

		public void SetPvp(bool enabled)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				localPlayer.SetPVP(enabled);
			}
		}

		public void SetPositionPublic(bool enabled)
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				ZNet.instance.SetPublicReferencePosition(enabled);
			}
		}
	}
	internal sealed class ValheimSessionContext : ISessionContext
	{
		private readonly Func<bool> _travelPending;

		public bool IsHost
		{
			get
			{
				ZNet instance = ZNet.instance;
				if ((Object)(object)instance != (Object)null)
				{
					return instance.IsServer();
				}
				return false;
			}
		}

		public int RemotePeerCount
		{
			get
			{
				ZNet instance = ZNet.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return 0;
				}
				return instance.GetPeers()?.Count ?? 0;
			}
		}

		public bool TravelPending => _travelPending();

		public ValheimSessionContext(Func<bool> travelPending)
		{
			_travelPending = travelPending ?? throw new ArgumentNullException("travelPending");
		}
	}
	internal sealed class GeneratedTerrainProbe : ITerrainProbe
	{
		public float WaterLevel
		{
			get
			{
				if (!((Object)(object)ZoneSystem.instance != (Object)null))
				{
					return 30f;
				}
				return ZoneSystem.instance.m_waterLevel;
			}
		}

		public bool TrySample(float x, float z, out TerrainSample sample)
		{
			//IL_001e: 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)
			//IL_0037: 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)
			WorldGenerator instance = WorldGenerator.instance;
			if (instance == null)
			{
				sample = default(TerrainSample);
				return false;
			}
			float height = instance.GetHeight(x, z);
			bool flag = ZoneSystem.IsLavaPreHeightmap(new Vector3(x, height, z), 0.6f);
			sample = new TerrainSample(height, flag, false);
			return true;
		}
	}
	internal sealed class LoadedTerrainProbe : ITerrainProbe
	{
		private const float ObstructionHeight = 1f;

		public float WaterLevel
		{
			get
			{
				if (!((Object)(object)ZoneSystem.instance != (Object)null))
				{
					return 30f;
				}
				return ZoneSystem.instance.m_waterLevel;
			}
		}

		public bool TrySample(float x, float z, out TerrainSample sample)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			sample = default(TerrainSample);
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(x, 0f, z);
			float num = default(float);
			if (!instance.GetGroundHeight(val, ref num))
			{
				return false;
			}
			val.y = num;
			float num2 = default(float);
			bool flag = instance.GetSolidHeight(val, ref num2, 1000) && num2 - num > 1f;
			sample = new TerrainSample(num, instance.IsLava(val, false), flag);
			return true;
		}
	}
	internal sealed class ValheimLocalPosition : ILocalPosition
	{
		public bool TryGet(out SpawnPoint position)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val;
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				val = ((Component)Player.m_localPlayer).transform.position;
			}
			else
			{
				if (!((Object)(object)ZNet.instance != (Object)null))
				{
					position = default(SpawnPoint);
					return false;
				}
				val = ZNet.instance.GetReferencePosition();
			}
			position = new SpawnPoint(val.x, val.y, val.z);
			return true;
		}
	}
}
namespace InvasionMod.Infrastructure.Ui
{
	public sealed class ExitCurtain
	{
		private GameObject _panel;

		private GameObject _parent;

		private Image _background;

		private Text _title;

		private Text _subtitle;

		public void Tick(float alpha, string title, string subtitle)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			if (alpha <= 0f)
			{
				Destroy();
				return;
			}
			EnsureBuilt();
			if (!((Object)(object)_background == (Object)null))
			{
				((Graphic)_background).color = new Color(0f, 0f, 0f, alpha);
				if ((Object)(object)_title != (Object)null)
				{
					_title.text = title ?? string.Empty;
					Color color = ((Graphic)_title).color;
					color.a = alpha;
					((Graphic)_title).color = color;
				}
				if ((Object)(object)_subtitle != (Object)null)
				{
					_subtitle.text = subtitle ?? string.Empty;
					Color color2 = ((Graphic)_subtitle).color;
					color2.a = alpha;
					((Graphic)_subtitle).color = color2;
				}
			}
		}

		private void EnsureBuilt()
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			GameObject customGUIFront = GUIManager.CustomGUIFront;
			if ((Object)(object)customGUIFront == (Object)null || ((Object)(object)_panel != (Object)null && _parent == customGUIFront))
			{
				return;
			}
			Destroy();
			_parent = customGUIFront;
			_panel = new GameObject("cortina", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			});
			_panel.transform.SetParent(customGUIFront.transform, false);
			RectTransform val = (RectTransform)_panel.transform;
			val.anchorMin = Vector2.zero;
			val.anchorMax = Vector2.one;
			val.offsetMin = Vector2.zero;
			val.offsetMax = Vector2.zero;
			_background = _panel.GetComponent<Image>();
			((Graphic)_background).raycastTarget = false;
			GUIManager instance = GUIManager.Instance;
			if (instance != null)
			{
				GameObject val2 = instance.CreateText(string.Empty, _panel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, instance.AveriaSerifBold, 48, instance.ValheimOrange, true, Color.black, 1200f, 90f, false);
				_title = val2.GetComponent<Text>();
				if ((Object)(object)_title != (Object)null)
				{
					_title.alignment = (TextAnchor)4;
					((Graphic)_title).raycastTarget = false;
				}
				GameObject val3 = instance.CreateText(string.Empty, _panel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, -64f), instance.AveriaSerif, 24, Color.white, true, Color.black, 1200f, 44f, false);
				_subtitle = val3.GetComponent<Text>();
				if ((Object)(object)_subtitle != (Object)null)
				{
					_subtitle.alignment = (TextAnchor)4;
					((Graphic)_subtitle).raycastTarget = false;
				}
			}
		}

		private void Destroy()
		{
			if ((Object)(object)_panel != (Object)null)
			{
				Object.Destroy((Object)(object)_panel);
			}
			_panel = null;
			_parent = null;
			_background = null;
			_title = null;
			_subtitle = null;
		}
	}
	public sealed class MenuBadge
	{
		private readonly string _text;

		private GameObject _label;

		private GameObject _parent;

		public MenuBadge(string text)
		{
			_text = text ?? string.Empty;
		}

		public void Tick()
		{
			if (!((Object)(object)FejdStartup.instance != (Object)null))
			{
				if ((Object)(object)_label != (Object)null)
				{
					Destroy();
				}
			}
			else
			{
				EnsureBuilt();
			}
		}

		private void EnsureBuilt()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_0094: 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)
			GUIManager instance = GUIManager.Instance;
			GameObject customGUIFront = GUIManager.CustomGUIFront;
			if (instance != null && !((Object)(object)customGUIFront == (Object)null) && (!((Object)(object)_label != (Object)null) || _parent != customGUIFront))
			{
				Destroy();
				_parent = customGUIFront;
				_label = instance.CreateText(string.Empty, customGUIFront.transform, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(14f, 12f), instance.AveriaSerifBold, 14, new Color(0.75f, 0.75f, 0.75f, 0.85f), false, Color.black, 420f, 26f, false);
				Text component = _label.GetComponent<Text>();
				if ((Object)(object)component != (Object)null)
				{
					component.alignment = (TextAnchor)6;
					component.horizontalOverflow = (HorizontalWrapMode)1;
					component.verticalOverflow = (VerticalWrapMode)1;
					component.text = _text;
				}
			}
		}

		private void Destroy()
		{
			if ((Object)(object)_label != (Object)null)
			{
				Object.Destroy((Object)(object)_label);
			}
			_label = null;
			_parent = null;
		}
	}
	public sealed class SearchIndicator
	{
		private const float Width = 420f;

		private const float Height = 56f;

		private const float Top = 70f;

		private GameObject _text;

		private GameObject _parent;

		public void Tick(string message)
		{
			//IL_0048: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(message))
			{
				Destroy();
			}
			else if (EnsureBuilt())
			{
				Text component = _text.GetComponent<Text>();
				if (!((Object)(object)component == (Object)null))
				{
					component.text = message;
					Color color = ((GUIManager.Instance != null) ? GUIManager.Instance.ValheimOrange : Color.white);
					color.a = Mathf.Lerp(0.55f, 1f, Mathf.Abs(Mathf.Sin(Time.time * 2.2f)));
					((Graphic)component).color = color;
				}
			}
		}

		private bool EnsureBuilt()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			GameObject customGUIFront = GUIManager.CustomGUIFront;
			GUIManager instance = GUIManager.Instance;
			if ((Object)(object)customGUIFront == (Object)null || instance == null)
			{
				return false;
			}
			if ((Object)(object)_text != (Object)null && _parent == customGUIFront)
			{
				return true;
			}
			Destroy();
			_parent = customGUIFront;
			_text = instance.CreateText(string.Empty, customGUIFront.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -70f), instance.AveriaSerifBold, 20, instance.ValheimOrange, true, Color.black, 420f, 56f, false);
			Text component = _text.GetComponent<Text>();
			if ((Object)(object)component != (Object)null)
			{
				component.alignment = (TextAnchor)4;
				component.horizontalOverflow = (HorizontalWrapMode)0;
				component.verticalOverflow = (VerticalWrapMode)0;
				((Graphic)component).raycastTarget = false;
			}
			return true;
		}

		private void Destroy()
		{
			if ((Object)(object)_text != (Object)null)
			{
				Object.Destroy((Object)(object)_text);
			}
			_text = null;
			_parent = null;
		}
	}
	public sealed class SearchPanel : ISearchView
	{
		private const int MaxRows = 5;

		private const float PanelWidth = 540f;

		private const float Margin = 20f;

		private const float ContentWidth = 500f;

		private const float InvadeWidth = 240f;

		private const float AdminTop = 138f;

		private const float StatusHeight = 44f;

		private const float RowHeight = 76f;

		private const float RowGap = 6f;

		private const float RowPadding = 10f;

		private const float JoinWidth = 104f;

		private const float JoinHeight = 32f;

		private readonly IModLogger _log;

		private readonly ToggleWorldOpen _worldOpen;

		private readonly Func<bool> _adminList;

		private readonly Action _invade;

		private readonly Action _search;

		private readonly Action<int> _join;

		private GameObject _panel;

		private Transform _parent;

		private GameObject _statusText;

		private GameObject _publishButton;

		private GameObject _invadeButton;

		private readonly List<GameObject> _rowTexts = new List<GameObject>();

		private readonly List<GameObject> _rowButtons = new List<GameObject>();

		private readonly List<GameObject> _rowBacks = new List<GameObject>();

		private bool _hookedScene;

		private bool _admin;

		private const string InvadeText = "Invade random world";

		private const string StopText = "Stop searching";

		public bool IsOpen
		{
			get
			{
				if ((Object)(object)_panel != (Object)null)
				{
					return _panel.activeInHierarchy;
				}
				return false;
			}
		}

		private static float StatusTop(bool admin)
		{
			if (!admin)
			{
				return 138f;
			}
			return 180f;
		}

		private static float RowsTop(bool admin)
		{
			return StatusTop(admin) + 44f + 8f;
		}

		private static float PanelHeight(bool admin)
		{
			if (!admin)
			{
				return StatusTop(admin: false) + 44f + 20f;
			}
			return RowsTop(admin: true) + 410f + 20f;
		}

		public SearchPanel(IModLogger log, ToggleWorldOpen worldOpen, Func<bool> adminList, Action invade, Action search, Action<int> join)
		{
			_log = log ?? throw new ArgumentNullException("log");
			_worldOpen = worldOpen ?? throw new ArgumentNullException("worldOpen");
			_adminList = adminList ?? throw new ArgumentNullException("adminList");
			_invade = invade ?? throw new ArgumentNullException("invade");
			_search = search ?? throw new ArgumentNullException("search");
			_join = join ?? throw new ArgumentNullException("join");
		}

		public void Toggle()
		{
			try
			{
				if (!EnsureBuilt())
				{
					_log.Warning("SearchPanel: la GUI de Jotunn todavia no existe, F7 no hace nada.");
					return;
				}
				_panel.SetActive(!_panel.activeSelf);
				RefreshPublishButton();
			}
			catch (Exception ex)
			{
				_log.Error("SearchPanel: fallo al abrir el panel: " + ex);
			}
		}

		public void Close()
		{
			if ((Object)(object)_panel != (Object)null)
			{
				_panel.SetActive(false);
			}
		}

		private bool EnsureBuilt()
		{
			//IL_00cc: 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_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Expected O, but got Unknown
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Expected O, but got Unknown
			//IL_033b: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0362: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Expected O, but got Unknown
			//IL_041d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0424: Expected O, but got Unknown
			//IL_045f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0479: Unknown result type (might be due to invalid IL or missing references)
			//IL_047e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0496: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0503: Unknown result type (might be due to invalid IL or missing references)
			//IL_050d: Expected O, but got Unknown
			//IL_0537: Unknown result type (might be due to invalid IL or missing references)
			//IL_0546: Unknown result type (might be due to invalid IL or missing references)
			//IL_054b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0558: Unknown result type (might be due to invalid IL or missing references)
			//IL_055e: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0621: Unknown result type (might be due to invalid IL or missing references)
			//IL_062b: Expected O, but got Unknown
			GUIManager instance = GUIManager.Instance;
			GameObject customGUIFront = GUIManager.CustomGUIFront;
			if (instance == null || (Object)(object)customGUIFront == (Object)null)
			{
				return false;
			}
			Transform transform = customGUIFront.transform;
			int num = (_admin ? 5 : 0);
			if ((Object)(object)_panel != (Object)null && _rowTexts.Count == num && _rowButtons.Count == num && _rowBacks.Count == num && (Object)(object)_publishButton != (Object)null && _parent == transform)
			{
				return true;
			}
			_admin = _adminList();
			_rowTexts.Clear();
			_rowButtons.Clear();
			_rowBacks.Clear();
			_panel = null;
			_parent = transform;
			_panel = instance.CreateWoodpanel(transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 540f, PanelHeight(_admin));
			_parent = transform;
			GameObject go = instance.CreateText(_admin ? "Invasions (admin)" : "Invasions", _panel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, instance.AveriaSerifBold, 24, instance.ValheimOrange, true, Color.black, 500f, 32f, false);
			Place(go, 20f, 16f, 500f, 32f);
			FitText(go, (TextAnchor)4);
			_publishButton = instance.CreateButton("Allow invasion", _panel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, 220f, 34f);
			Place(_publishButton, 160f, 54f, 220f, 34f);
			((UnityEvent)_publishButton.GetComponent<Button>().onClick).AddListener(new UnityAction(TogglePublish));
			_invadeButton = instance.CreateButton("Invade random world", _panel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, 240f, 34f);
			Place(_invadeButton, 150f, 96f, 240f, 34f);
			((UnityEvent)_invadeButton.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				Click("invadir un mundo al azar", _invade);
			});
			if (_admin)
			{
				GameObject obj = instance.CreateButton("World list (admin)", _panel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, 240f, 32f);
				Place(obj, 150f, 138f, 240f, 32f);
				((UnityEvent)obj.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
				{
					Click("buscar la lista de mundos", _search);
				});
			}
			_statusText = instance.CreateText(string.Empty, _panel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, instance.AveriaSerif, 15, Color.white, true, Color.black, 500f, 44f, false);
			Place(_statusText, 20f, StatusTop(_admin), 500f, 44f);
			FitText(_statusText, (TextAnchor)4);
			_statusText.SetActive(false);
			for (int num2 = 0; num2 < (_admin ? 5 : 0); num2++)
			{
				float num3 = RowsTop(admin: true) + (float)num2 * 82f;
				int index = num2;
				GameObject val = new GameObject("fila", new Type[3]
				{
					typeof(RectTransform),
					typeof(Image),
					typeof(Button)
				});
				val.transform.SetParent(_panel.transform, false);
				Place(val, 20f, num3, 500f, 76f);
				Image component = val.GetComponent<Image>();
				((Graphic)component).color = Color.white;
				Button component2 = val.GetComponent<Button>();
				((Selectable)component2).targetGraphic = (Graphic)(object)component;
				ColorBlock colors = ((Selectable)component2).colors;
				((ColorBlock)(ref colors)).normalColor = new Color(0f, 0f, 0f, 0.28f);
				((ColorBlock)(ref colors)).highlightedColor = new Color(1f, 0.72f, 0.36f, 0.22f);
				((ColorBlock)(ref colors)).pressedColor = new Color(1f, 0.72f, 0.36f, 0.35f);
				((ColorBlock)(ref colors)).selectedColor = ((ColorBlock)(ref colors)).normalColor;
				((Selectable)component2).colors = colors;
				((UnityEvent)component2.onClick).AddListener((UnityAction)delegate
				{
					Join(index);
				});
				val.SetActive(false);
				float num4 = 366f;
				GameObject val2 = instance.CreateText(string.Empty, _panel.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), Vector2.zero, instance.AveriaSerif, 15, Color.white, false, Color.black, num4, 68f, false);
				Place(val2, 30f, num3 + 4f, num4, 68f);
				FitText(val2, (TextAnchor)3);
				((Graphic)val2.GetComponent<Text>()).raycastTarget = false;
				val2.SetActive(false);
				GameObject val3 = instance.CreateButton("Join", _panel.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), Vector2.zero, 104f, 32f);
				Place(val3, 406f, num3 + 22f, 104f, 32f);
				((UnityEvent)val3.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
				{
					Join(index);
				});
				val3.SetActive(false);
				_rowBacks.Add(val);
				_rowTexts.Add(val2);
				_rowButtons.Add(val3);
			}
			_panel.SetActive(false);
			if (!_hookedScene)
			{
				_hookedScene = true;
				SceneManager.sceneLoaded += delegate
				{
					Close();
				};
			}
			_log.Info("SearchPanel: panel construido" + (_admin ? " en modo admin, con la lista de mundos." : "."));
			return true;
		}

		private void Join(int index)
		{
			Click("unirse al mundo " + index, delegate
			{
				_join(index);
			});
		}

		private void Click(string what, Action action)
		{
			try
			{
				action();
			}
			catch (Exception ex)
			{
				_log.Error("SearchPanel: fallo al " + what + ": " + ex);
				ShowStatus("Could not start the trip. Check the log.");
			}
		}

		private void TogglePublish()
		{
			try
			{
				if (!_worldOpen.Execute() && _worldOpen.BlockReason != null)
				{
					ShowStatus(_worldOpen.BlockReason);
				}
			}
			catch (Exception ex)
			{
				_log.Error("SearchPanel: fallo al cambiar la visibilidad del mundo: " + ex);
			}
			RefreshPublishButton();
		}

		public void RefreshPublishButton()
		{
			if (!((Object)(object)_publishButton == (Object)null))
			{
				Text componentInChildren = _publishButton.GetComponentInChildren<Text>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.text = (_worldOpen.IsOpen ? "Stop allowing" : "Allow invasion");
				}
			}
		}

		public void RefreshInvadeButton(bool searching)
		{
			if (!((Object)(object)_invadeButton == (Object)null))
			{
				Text componentInChildren = _invadeButton.GetComponentInChildren<Text>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.text = (searching ? "Stop searching" : "Invade random world");
				}
			}
		}

		private static void Place(GameObject go, float left, float top, float width, float height)
		{
			//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_0016: 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_002b: 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_0040: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (RectTransform)go.transform;
			val.anchorMin = new Vector2(0f, 1f);
			val.anchorMax = new Vector2(0f, 1f);
			val.pivot = new Vector2(0f, 1f);
			val.sizeDelta = new Vector2(width, height);
			val.anchoredPosition = new Vector2(left, 0f - top);
		}

		private static void FitText(GameObject go, TextAnchor alignment)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			Text component = go.GetComponent<Text>();
			if (!((Object)(object)component == (Object)null))
			{
				component.supportRichText = true;
				component.horizontalOverflow = (HorizontalWrapMode)0;
				component.verticalOverflow = (VerticalWrapMode)0;
				component.resizeTextForBestFit = true;
				component.resizeTextMinSize = 10;
				component.resizeTextMaxSize = component.fontSize;
				component.alignment = alignment;
			}
		}

		public void ShowResults(IReadOnlyList<WorldListing> results, string status)
		{
			RefreshResults(results, status);
		}

		public void RefreshResults(IReadOnlyList<WorldListing> results, string status = null)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!EnsureBuilt())
				{
					_log.Warning("SearchPanel: llegaron resultados pero no hay panel, se ignoran.");
					return;
				}
				for (int i = 0; i < _rowTexts.Count; i++)
				{
					bool flag = i < results.Count;
					if (flag)
					{
						WorldListing val = results[i];
						Text component = _rowTexts[i].GetComponent<Text>();
						if ((Object)(object)component != (Object)null)
						{
							component.text = RowText(val);
							Color color = ((Graphic)component).color;
							color.a = (val.Compatible ? 1f : 0.45f);
							((Graphic)component).color = color;
						}
						Text componentInChildren = _rowButtons[i].GetComponentInChildren<Text>();
						if ((Object)(object)componentInChildren != (Object)null)
						{
							componentInChildren.text = (val.Compatible ? "Join" : "Blocked");
						}
						Image component2 = _rowButtons[i].GetComponent<Image>();
						if ((Object)(object)component2 != (Object)null)
						{
							((Graphic)component2).color = (val.Compatible ? new Color(1f, 1f, 1f, 1f) : new Color(0.5f, 0.5f, 0.5f, 0.7f));
						}
					}
					_rowBacks[i].SetActive(flag);
					_rowTexts[i].SetActive(flag);
					_rowButtons[i].SetActive(flag);
				}
				RefreshPublishButton();
				ShowStatus(status);
			}
			catch (Exception ex)
			{
				_log.Error("SearchPanel: no se pudieron mostrar los resultados: " + ex);
			}
		}

		private string RowText(WorldListing result)
		{
			string text = (result.Compatible ? "#FFB75C" : "#FFB75C73");
			string text2 = "<b><color=" + text + ">" + Escape(result.WorldName) + "</color></b>";
			string text3 = (string.IsNullOrEmpty(result.OwnerVersion) ? "unknown version" : result.OwnerVersion);
			text3 = text3 + "  ·  " + result.TierText;
			if (!string.IsNullOrEmpty(result.OwnerName))
			{
				text3 = text3 + "  ·  " + Escape(result.OwnerName);
			}
			if (result.Compatible)
			{
				string text4 = TierGap.Describe(_worldOpen.CurrentTier, result.Tier);
				string text5 = (result.NeedsPassword ? "Password protected" : null);
				if (text4 != null)
				{
					text5 = ((text5 == null) ? Capitalize(text4) : (text5 + "  ·  " + text4));
				}
				else if (text5 != null)
				{
					text5 += ": you will be asked for it on entry";
				}
				if (text5 != null)
				{
					text3 = text3 + "\n" + text5;
				}
			}
			if (!result.Compatible)
			{
				text3 = text3 + "\n<color=#FF8A80B0>" + Escape(result.Reason) + "</color>";
			}
			return text2 + "\n" + text3;
		}

		private static string Capitalize(string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				return char.ToUpperInvariant(value[0]) + value.Substring(1);
			}
			return value;
		}

		private static string Escape(string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				return value.Replace("<", "<\u200b");
			}
			return string.Empty;
		}

		public void ShowStatus(string message)
		{
			if (!((Object)(object)_statusText == (Object)null))
			{
				Text component = _statusText.GetComponent<Text>();
				if ((Object)(object)component != (Object)null)
				{
					component.text = message;
				}
				_statusText.SetActive(!string.IsNullOrEmpty(message));
			}
		}
	}
	public sealed class TravelOverlay
	{
		private GameObject _panel;

		private GameObject _title;

		private GameObject _name;

		private GameObject _status;

		private GameObject _parent;

		private string _titleText;

		private string _nameText;

		private string _statusText;

		private bool _isError;

		private float _errorUntil;

		private readonly Color NameColor = Color.white;

		private readonly Color StatusColor = new Color(0.8f, 0.8f, 0.8f, 1f);

		private readonly Color ErrorColor = new Color(0.9f, 0.25f, 0.2f, 1f);

		public bool Active => !string.IsNullOrEmpty(_titleText);

		public void Set(string title, string worldName, string status)
		{
			_titleText = title;
			_nameText = worldName;
			_statusText = status;
			_isError = false;
		}

		public void ShowError(string worldName, string message, float seconds)
		{
			_titleText = "Could not join";
			_nameText = worldName;
			_statusText = message;
			_isError = true;
			_errorUntil = Time.time + seconds;
		}

		public void Clear()
		{
			_titleText = null;
			_nameText = null;
			_statusText = null;
			_isError = false;
			Destroy();
		}

		public void Tick()
		{
			if (Active)
			{
				if (_isError && Time.time >= _errorUntil)
				{
					Clear();
					return;
				}
				EnsureBuilt();
				Apply();
			}
		}

		private void EnsureBuilt()
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			GameObject customGUIFront = GUIManager.CustomGUIFront;
			if (!((Object)(object)customGUIFront == (Object)null) && (!((Object)(object)_panel != (Object)null) || _parent != customGUIFront))
			{
				Destroy();
				_parent = customGUIFront;
				_panel = new GameObject("viaje", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(Image)
				});
				_panel.transform.SetParent(customGUIFront.transform, false);
				RectTransform val = (RectTransform)_panel.transform;
				val.anchorMin = Vector2.zero;
				val.anchorMax = Vector2.one;
				val.offsetMin = Vector2.zero;
				val.offsetMax = Vector2.zero;
				Image component = _panel.GetComponent<Image>();
				((Graphic)component).color = new Color(0f, 0f, 0f, _isError ? 0.8f : 0.55f);
				((Graphic)component).raycastTarget = true;
				GUIManager instance = GUIManager.Instance;
				if (instance != null)
				{
					_title = Line(instance, 30, instance.ValheimOrange, 190f);
					_name = Line(instance, 24, Color.white, 20f);
					_status = Line(instance, 17, new Color(0.8f, 0.8f, 0.8f, 1f), -150f);
				}
			}
		}

		private Color TitleColor(Color normal)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (!_isError)
			{
				return normal;
			}
			return ErrorColor;
		}

		private void Apply()
		{
			//IL_0019: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			GUIManager instance = GUIManager.Instance;
			if (instance != null)
			{
				Put(_title, _titleText, TitleColor(instance.ValheimOrange), Blink());
				Put(_name, _nameText, NameColor, 1f);
				Put(_status, _statusText, StatusColor, 1f);
			}
		}

		private float Blink()
		{
			return Mathf.Lerp(0.45f, 1f, Mathf.Abs(Mathf.Sin(Time.time * 4.4f)));
		}

		private void Put(GameObject go, string value, Color baseColor, float alphaScale)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)go == (Object)null))
			{
				Text component = go.GetComponent<Text>();
				if (!((Object)(object)component == (Object)null))
				{
					component.text = value ?? string.Empty;
					Color color = baseColor;
					color.a *= alphaScale;
					((Graphic)component).color = color;
				}
			}
		}

		private GameObject Line(GUIManager gui, int size, Color color, float y)
		{
			//IL_001b: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = gui.CreateText(string.Empty, _panel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, gui.AveriaSerifBold, size, color, true, Color.black, 1200f, 70f, true);
			((RectTransform)obj.transform).anchoredPosition = new Vector2(0f, y);
			Text component = obj.GetComponent<Text>();
			if ((Object)(object)component != (Object)null)
			{
				component.horizontalOverflow = (HorizontalWrapMode)0;
				component.verticalOverflow = (VerticalWrapMode)1;
				component.resizeTextForBestFit = false;
				component.alignment = (TextAnchor)4;
			}
			return obj;
		}

		private void Destroy()
		{
			if ((Object)(object)_panel != (Object)null)
			{
				Object.Destroy((Object)(object)_panel);
			}
			_panel = null;
			_title = null;
			_name = null;
			_status = null;
			_parent = null;
		}
	}
}
namespace InvasionMod.Infrastructure.Travel
{
	internal sealed class TravelView : ITravelView
	{
		private readonly TravelOverlay _overlay;

		private readonly SearchPanel _panel;

		public TravelView(TravelOverlay overlay, SearchPanel panel)
		{
			_overlay = overlay ?? throw new ArgumentNullException("overlay");
			_panel = panel ?? throw new ArgumentNullException("panel");
		}

		public void ShowProgress(string title, string worldName, string status)
		{
			_overlay.Set(title, worldName, status);
		}

		public void ShowError(string worldName, string message, float seconds)
		{
			_overlay.ShowError(worldName, message, seconds);
		}

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

		public void CloseSearch()
		{
			_panel.Close();
		}

		public void ShowSearchStatus(string message)
		{
			_panel.ShowStatus(message);
		}
	}
	internal sealed class ValheimConnectionMonitor : IConnectionMonitor
	{
		public bool IsDegraded
		{
			get
			{
				ZNet instance = ZNet.instance;
				if ((Object)(object)instance != (Object)null)
				{
					return instance.HasBadConnection();
				}
				return false;
			}
		}

		public string StatusText => ((object)ZNet.GetConnectionStatus()/*cast due to .constrained prefix*/).ToString();
	}
	public sealed class ValheimHomeWorldGateway : IHomeWorldGateway
	{
		private readonly IModLogger _log;

		private bool _remembered;

		private bool _open;

		private bool _public;

		private bool _crossplay;

		private string _password = string.Empty;

		public ValheimHomeWorldGateway(IModLogger log)
		{
			_log = log;
		}

		public void Remember(FejdStartup startup)
		{
			_remembered = true;
			_open = (Object)(object)startup.m_openServerToggle != (Object)null && startup.m_openServerToggle.isOn;
			_public = (Object)(object)startup.m_publicServerToggle != (Object)null && startup.m_publicServerToggle.isOn;
			_crossplay = (Object)(object)startup.m_crossplayServerToggle != (Object)null && startup.m_crossplayServerToggle.isOn;
			_password = (((Object)(object)startup.m_serverPassword != (Object)null) ? (((TMP_InputField)startup.m_serverPassword).text ?? string.Empty) : string.Empty);
		}

		public HomeStartResult TryStartHome()
		{
			FejdStartup instance = FejdStartup.instance;
			if (!((Object)(object)instance == (Object)null) && instance.m_profiles != null)
			{
				string text = PlatformPrefs.GetString("world", "");
				World val = FindWorld(text);
				if (val == null)
				{
					_log.Warning("No se encontro el mundo propio '" + text + "' para volver.");
					return (HomeStartResult)2;
				}
				if (_remembered)
				{
					RestoreOptions(instance);
				}
				_log.Info("Volviendo al mundo propio '" + val.m_name + "'.");
				instance.m_world = val;
				instance.OnWorldStart();
				if (!instance.m_startingWorld)
				{
					_log.Warning("El juego no arranco el mundo propio todavia: puede estar esperando el login o mostrando un aviso.");
				}
				return (HomeStartResult)1;
			}
			return (HomeStartResult)0;
		}

		private static World FindWorld(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			List<World> worldList = SaveSystem.GetWorldList();
			if (worldList == null)
			{
				return null;
			}
			foreach (World item in worldList)
			{
				if (item.m_name == name)
				{
					return item;
				}
			}
			return null;
		}

		private void RestoreOptions(FejdStartup startup)
		{
			bool flag = _public || _crossplay;
			bool isOn = _open && (!flag || !string.IsNullOrEmpty(_password));
			if ((Object)(object)startup.m_openServerToggle != (Object)null)
			{
				startup.m_openServerToggle.isOn = isOn;
			}
			if ((Object)(object)startup.m_publicServerToggle != (Object)null)
			{
				startup.m_publicServerToggle.isOn = _public;
			}
			if ((Object)(object)startup.m_crossplayServerToggle != (Obje