Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of InvasionMod v0.9.5
InvasionMod.Core.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
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
The result has been truncated due to the large size, download it to view full contents!
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