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 BossGate v1.1.1
plugins/BossGate.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("BossGate")] [assembly: AssemblyProduct("BossGate")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.1.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BossGate { [BepInPlugin("Igao.BossGate", "BossGate", "1.1.1")] public class BossGatePlugin : BaseUnityPlugin { internal class BossInfo { public string Key; public string[] PrefabHints; public string[] LocTokens; public string FallbackName; public string[] ItemHints; public string[] DefeatKeys; public ConfigEntry<int> Day; } public const string ModGuid = "Igao.BossGate"; public const string ModName = "BossGate"; public const string ModVersion = "1.1.1"; internal static ManualLogSource Log; internal static ConfigEntry<bool> LockAllBosses; internal static ConfigEntry<bool> RequirePreviousBossDefeated; internal static ConfigEntry<bool> KickClientsWithoutMod; internal static ConfigEntry<bool> PortalItemsRequireBossKill; internal static ConfigEntry<bool> PerPlayerProgress; internal static readonly List<BossInfo> Bosses = new List<BossInfo>(); internal static bool HasServerConfig; internal static bool SyncedLock; internal static bool SyncedPortalRule; internal static bool SyncedPreviousRule; internal static bool SyncedPerPlayer; internal static readonly Dictionary<string, int> SyncedDays = new Dictionary<string, int>(); private static readonly Dictionary<string, Dictionary<string, string>> Translations = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, string> BuiltInEnglish = new Dictionary<string, string> { { "msg_locked_day", "{boss} unlocks on day {day}\nCurrent day: {current}" }, { "msg_locked", "Boss summoning is not possible right now." }, { "msg_previous_boss", "Defeat the previous bosses first." } }; internal static readonly string[] NoHints = new string[0]; private float _nextTick; private static readonly string[] SealbreakerKeyHints = new string[2] { "dvergrkey", "sealbreaker" }; private static readonly string[] CitadelDoorHints = new string[4] { "bossentrance", "dvergrboss", "citadel", "vault" }; private static readonly HashSet<int> _loggedUnknown = new HashSet<int>(); private static readonly HashSet<string> _seenDoors = new HashSet<string>(); private static BossInfo Queen; private static float _lastNotify = -10f; internal static string[] Hints(string patterns) { if (string.IsNullOrEmpty(patterns)) { return NoHints; } return patterns.Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries); } private void Awake() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; MigrateMovedSettings(); LockAllBosses = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "LockAllBosses", false, new ConfigDescription("SERVER SETTING. If true, NO boss can be summoned, regardless of the day.", (AcceptableValueBase)null, new object[1] { "scm:live" })); RequirePreviousBossDefeated = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Progression", "RequirePreviousBossDefeated", false, new ConfigDescription("SERVER SETTING. If true, a boss can only be summoned after ALL the bosses before it in the progression order (Eikthyr, The Elder, Bonemass, Moder, Yagluth, The Queen, Fader, Kall Fimbulbringer) have been defeated in the world. Works together with the unlock days: the boss must be unlocked by the day AND the previous bosses must be dead. Eikthyr is never affected.", (AcceptableValueBase)null, new object[1] { "scm:live" })); KickClientsWithoutMod = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "KickClientsWithoutMod", false, new ConfigDescription("SERVER SETTING. If true, players who join without BossGate installed are disconnected after ~20 s. Without this, a client that does not have the mod can still summon bosses.", (AcceptableValueBase)null, new object[1] { "scm:live" })); PortalItemsRequireBossKill = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Portals", "PortalItemsRequireBossKill", false, new ConfigDescription("SERVER SETTING. false = vanilla: ores and metals that the game blocks can never go through portals. true = the blocked items of a biome can go through portals only after the boss of that biome has been defeated in the world (Black Forest ores: The Elder, iron: Bonemass, silver and dragon eggs: Moder, black metal: Yagluth, Mistlands items: The Queen, flametal: Fader, gold: Kall Fimbulbringer). Hildir's chests: the miniboss that guards the chest AND the boss of its biome (Brass: Brenna + The Elder, Silver: Geirrhafa + Moder, Bronze: Zil & Thungr + Yagluth). Items of a biome whose boss is still alive stay blocked.", (AcceptableValueBase)null, new object[1] { "scm:live" })); PerPlayerProgress = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Progression", "PerPlayerProgress", false, new ConfigDescription("SERVER SETTING. Decides whose boss kills count for RequirePreviousBossDefeated and PortalItemsRequireBossKill. false = world progress: a boss killed by anyone counts for every player. true = personal progress: each player only counts the bosses (and Hildir minibosses) they took part in killing in THIS world (hit it, or were within 40 m when it died). Kills made in other worlds never count. Kills are recorded even while this is false, as long as the players run BossGate 1.1.1 or newer.", (AcceptableValueBase)null, new object[1] { "scm:live" })); AddBoss(1, "Eikthyr", "eikthyr", "$enemy_eikthyr", "Eikthyr", "trophydeer", "defeated_eikthyr"); AddBoss(2, "TheElder", "gd_king", "$enemy_gdking", "The Elder", "ancientseed", "defeated_gdking"); AddBoss(3, "Bonemass", "bonemass", "$enemy_bonemass", "Bonemass", "witheredbone", "defeated_bonemass"); AddBoss(4, "Moder", "dragon", "$enemy_dragon", "Moder", "dragonegg", "defeated_dragon"); AddBoss(5, "Yagluth", "goblinking", "$enemy_goblinking", "Yagluth", "goblintotem", "defeated_goblinking"); AddBoss(6, "TheQueen", "seekerqueen", "$enemy_seekerqueen", "The Queen", "trophyseekerbrute|seekerbrute", "defeated_queen"); AddBoss(7, "Fader", "fader", "$enemy_fader", "Fader", "bell", "defeated_fader"); AddBoss(8, "Kall", "frozenking|kall|fimbul|frozen", "$enemy_frozenking|$enemy_kall", "Kall Fimbulbringer", "hatefulblood|hateful|frozenking|kall|fimbul|frozen", "defeated_frozenking_p3"); Queen = Bosses.Find((BossInfo b) => b.Key == "TheQueen"); PortalGate.Init(); LoadTranslations(); ((BaseUnityPlugin)this).Config.SettingChanged += delegate { ConfigSync.Broadcast(); }; Harmony val = new Harmony("Igao.BossGate"); Type[] types = typeof(BossGatePlugin).Assembly.GetTypes(); foreach (Type type in types) { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0) { try { val.CreateClassProcessor(type).Patch(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Patch '" + type.Name + "' could not be applied: " + ex.Message)); } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)"BossGate 1.1.1 loaded."); } private void Update() { if (!(Time.time < _nextTick)) { _nextTick = Time.time + 2f; ConfigSync.Tick(); } } private void MigrateMovedSettings() { try { PropertyInfo propertyInfo = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries"); Dictionary<ConfigDefinition, string> dictionary = ((propertyInfo != null) ? (propertyInfo.GetValue(((BaseUnityPlugin)this).Config, null) as Dictionary<ConfigDefinition, string>) : null); if (dictionary != null && dictionary.Count != 0) { MoveSetting(dictionary, "1 - General", "3 - Progression", "RequirePreviousBossDefeated"); MoveSetting(dictionary, "1 - General", "3 - Progression", "PerPlayerProgress"); MoveSetting(dictionary, "1 - General", "4 - Portals", "PortalItemsRequireBossKill"); MoveSetting(dictionary, "3 - Portals", "4 - Portals", "PortalItemsRequireBossKill"); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not migrate old settings (defaults are used): " + ex.Message)); } } private void MoveSetting(Dictionary<ConfigDefinition, string> orphans, string oldSection, string newSection, string key) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown ConfigDefinition key2 = new ConfigDefinition(oldSection, key); if (orphans.TryGetValue(key2, out var value)) { orphans.Remove(key2); ConfigDefinition key3 = new ConfigDefinition(newSection, key); if (!orphans.ContainsKey(key3)) { orphans[key3] = value; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Setting '" + key + "' moved from [" + oldSection + "] to [" + newSection + "] (value kept: " + value + ").")); } } } private void AddBoss(int order, string key, string prefabMatch, string token, string fallback, string itemMatch, string defeatKeys) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown PlayerProgress.Track(Hints(defeatKeys)); Bosses.Add(new BossInfo { Key = key, PrefabHints = Hints(prefabMatch), LocTokens = Hints(token), FallbackName = fallback, ItemHints = Hints(itemMatch), DefeatKeys = Hints(defeatKeys), Day = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Boss Unlock Day", order + " - " + fallback, 0, new ConfigDescription("SERVER SETTING. Game day from which " + fallback + " can be summoned. 0 = disabled (no day restriction).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 9999), new object[1] { "scm:live" })) }); } private static bool IsRemoteClient() { if ((Object)(object)ZNet.instance != (Object)null) { return !ZNet.instance.IsServer(); } return false; } internal static bool EffectiveLock() { if (IsRemoteClient()) { if (HasServerConfig) { return SyncedLock; } return false; } return LockAllBosses.Value; } internal static bool EffectivePortalRule() { if (IsRemoteClient()) { if (HasServerConfig) { return SyncedPortalRule; } return false; } return PortalItemsRequireBossKill.Value; } internal static bool EffectivePreviousRule() { if (IsRemoteClient()) { if (HasServerConfig) { return SyncedPreviousRule; } return false; } return RequirePreviousBossDefeated.Value; } internal static bool EffectivePerPlayer() { if (IsRemoteClient()) { if (HasServerConfig) { return SyncedPerPlayer; } return false; } return PerPlayerProgress.Value; } internal static int EffectiveDay(BossInfo boss) { if (IsRemoteClient()) { if (!HasServerConfig) { return 0; } if (!SyncedDays.TryGetValue(boss.Key, out var value)) { return 0; } return value; } return boss.Day.Value; } private void LoadTranslations() { Translations["English"] = new Dictionary<string, string>(BuiltInEnglish); try { string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Translations"); if (!Directory.Exists(text)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Translations folder not found: " + text)); return; } string[] files = Directory.GetFiles(text, "*.json"); foreach (string text2 in files) { try { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text2); Dictionary<string, string> dictionary = FlatJson.Parse(File.ReadAllText(text2, Encoding.UTF8)); if (dictionary.Count > 0) { Translations[fileNameWithoutExtension] = dictionary; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to read translation '" + text2 + "': " + ex.Message)); } } } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to load translations: " + ex2.Message)); } } private static string GetLanguage() { string selectedLanguage = LocHelper.GetSelectedLanguage(); if (string.Equals(selectedLanguage, "Chinese_Trad", StringComparison.OrdinalIgnoreCase)) { return "Chinese"; } return selectedLanguage; } internal static string T(string key) { if (Translations.TryGetValue(GetLanguage(), out var value) && value.TryGetValue(key, out var value2) && !string.IsNullOrEmpty(value2)) { return value2; } if (Translations.TryGetValue("English", out var value3) && value3.TryGetValue(key, out value2) && !string.IsNullOrEmpty(value2)) { return value2; } if (!BuiltInEnglish.TryGetValue(key, out value2)) { return key; } return value2; } private static string BossName(BossInfo boss) { try { string[] locTokens = boss.LocTokens; foreach (string text in locTokens) { string text2 = LocHelper.Localize(text); if (!string.IsNullOrEmpty(text2) && !text2.StartsWith("[")) { return text2; } } } catch { } return boss.FallbackName; } private static bool MatchesAny(string text, string[] hints) { if (string.IsNullOrEmpty(text) || hints == null) { return false; } for (int i = 0; i < hints.Length; i++) { if (text.Contains(hints[i])) { return true; } } return false; } private static string SafeName(Object o) { try { return (o != (Object)null) ? o.name.ToLowerInvariant() : ""; } catch { return ""; } } private static string ItemToken(OfferingBowl bowl) { try { return bowl.m_bossItem.m_itemData.m_shared.m_name.ToLowerInvariant(); } catch { return ""; } } internal static string ContextOf(Component c) { StringBuilder stringBuilder = new StringBuilder(); try { Transform val = c.transform; int num = 0; while ((Object)(object)val != (Object)null && num < 4) { stringBuilder.Append(((Object)val).name.ToLowerInvariant()).Append(' '); val = val.parent; num++; } } catch { } return stringBuilder.ToString(); } internal static BossInfo Identify(OfferingBowl bowl) { if ((Object)(object)bowl == (Object)null) { return null; } string text = SafeName((Object)(object)bowl.m_bossPrefab); string text2 = SafeName((Object)(object)bowl.m_bossItem); string text3 = ItemToken(bowl); string text4 = ContextOf((Component)(object)bowl); foreach (BossInfo boss in Bosses) { if (MatchesAny(text, boss.PrefabHints)) { return boss; } } foreach (BossInfo boss2 in Bosses) { if (MatchesAny(text2, boss2.ItemHints) || MatchesAny(text3, boss2.ItemHints)) { return boss2; } } string text5 = text + " " + text2 + " " + text3 + " " + text4; foreach (BossInfo boss3 in Bosses) { if (MatchesAny(text5, boss3.PrefabHints) || MatchesAny(text5, boss3.ItemHints)) { return boss3; } } if (_loggedUnknown.Add(((Object)bowl).GetInstanceID())) { Log.LogWarning((object)("Altar not recognized as any boss (day lock NOT applied). bossPrefab='" + text + "' bossItem='" + text2 + "' itemToken='" + text3 + "' altar='" + text4.Trim() + "'")); } return null; } internal static int GetCurrentDay() { try { EnvMan instance = EnvMan.instance; ZNet instance2 = ZNet.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null) { return 0; } double num = instance.m_dayLengthSec; if (num <= 0.0) { return 0; } return (int)Math.Floor(instance2.GetTimeSeconds() / num); } catch { return 0; } } internal static bool IsBlocked(OfferingBowl bowl, Humanoid user) { if ((Object)(object)user == (Object)null || (Object)(object)user != (Object)(object)Player.m_localPlayer) { return false; } return Evaluate(Identify(bowl)); } private static bool Evaluate(BossInfo boss) { if (EffectiveLock()) { Notify(T("msg_locked")); return true; } if (boss == null) { return false; } int num = EffectiveDay(boss); if (num > 0) { int currentDay = GetCurrentDay(); if (currentDay < num) { string text = T("msg_locked_day").Replace("{boss}", BossName(boss)).Replace("{day}", num.ToString()).Replace("{current}", currentDay.ToString()); Notify(text); return true; } } if (EffectivePreviousRule() && !PreviousBossesDefeated(boss)) { Notify(T("msg_previous_boss")); return true; } return false; } private static bool PreviousBossesDefeated(BossInfo boss) { foreach (BossInfo boss2 in Bosses) { if (boss2 == boss) { return true; } if (!IsBossDefeated(boss2)) { return false; } } return true; } internal static bool IsBossDefeated(BossInfo boss) { if (boss == null) { return false; } try { if (EffectivePerPlayer()) { return PlayerProgress.HasCredit(boss); } ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null) { return false; } string[] defeatKeys = boss.DefeatKeys; for (int i = 0; i < defeatKeys.Length; i++) { if (instance.GetGlobalKey(defeatKeys[i])) { return true; } } return false; } catch { return false; } } internal static bool IsBlockedDoor(Door door, Humanoid user) { if ((Object)(object)door == (Object)null || (Object)(object)user == (Object)null || (Object)(object)user != (Object)(object)Player.m_localPlayer) { return false; } string text = SafeName((Object)(object)door.m_keyItem); string text2 = ""; try { if ((Object)(object)door.m_keyItem != (Object)null) { text2 = door.m_keyItem.m_itemData.m_shared.m_name.ToLowerInvariant(); } } catch { } string text3 = ContextOf((Component)(object)door).Trim(); string text4 = ""; try { text4 = (door.m_name ?? "").ToLowerInvariant(); } catch { } bool flag = MatchesAny(text + " " + text2, SealbreakerKeyHints); bool flag2 = MatchesAny(text3 + " " + text4, CitadelDoorHints); bool flag3 = flag || flag2; if (_seenDoors.Add(text3 + "|" + text)) { Log.LogInfo((object)("Door used: object='" + text3 + "' m_name='" + text4 + "' keyItem='" + text + "' keyToken='" + text2 + "' -> Infested Citadel door: " + flag3 + " (byKey=" + flag + ", byName=" + flag2 + ")")); } if (!flag3) { return false; } return Evaluate(Queen); } private static void Notify(string text) { if (Time.time - _lastNotify < 1f) { return; } _lastNotify = Time.time; try { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true); } } catch { } } } internal static class LocHelper { private static bool _lookupDone; private static PropertyInfo _instanceProp; private static MethodInfo _getLang; private static MethodInfo _localize; private static object Instance() { if (!_lookupDone) { _lookupDone = true; Type type = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type type2 = null; try { type2 = assembly.GetType("Localization", throwOnError: false); } catch { } if (type2 != null) { type = type2; break; } } if (type != null) { _instanceProp = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public); _getLang = type.GetMethod("GetSelectedLanguage", BindingFlags.Instance | BindingFlags.Public); _localize = type.GetMethod("Localize", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); } } if (!(_instanceProp != null)) { return null; } return _instanceProp.GetValue(null, null); } public static string GetSelectedLanguage() { try { object obj = Instance(); if (obj != null && _getLang != null) { string text = _getLang.Invoke(obj, null) as string; if (!string.IsNullOrEmpty(text)) { return text; } } } catch { } return "English"; } public static string Localize(string text) { if (string.IsNullOrEmpty(text)) { return text; } try { object obj = Instance(); if (obj != null && _localize != null) { string text2 = _localize.Invoke(obj, new object[1] { text }) as string; if (!string.IsNullOrEmpty(text2)) { return text2; } } } catch { } return text; } } internal static class FlatJson { public static Dictionary<string, string> Parse(string json) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); int num = 0; int length = json.Length; while (num < length) { int pos = json.IndexOf('"', num); if (pos < 0) { break; } string key = ReadString(json, ref pos); int num2 = json.IndexOf(':', pos); if (num2 < 0) { break; } int pos2 = json.IndexOf('"', num2); if (pos2 < 0) { break; } string value = ReadString(json, ref pos2); dictionary[key] = value; num = pos2; } return dictionary; } private static string ReadString(string s, ref int pos) { StringBuilder stringBuilder = new StringBuilder(); int i; for (i = pos + 1; i < s.Length && s[i] != '"'; i++) { char c = s[i]; if (c == '\\' && i + 1 < s.Length) { i++; char c2 = s[i]; switch (c2) { case 'n': stringBuilder.Append('\n'); break; case 't': stringBuilder.Append('\t'); break; case 'r': stringBuilder.Append('\r'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'u': if (i + 4 < s.Length) { stringBuilder.Append((char)Convert.ToInt32(s.Substring(i + 1, 4), 16)); i += 4; } break; default: stringBuilder.Append(c2); break; } } else { stringBuilder.Append(c); } } pos = i + 1; return stringBuilder.ToString(); } } internal static class ConfigSync { private const string RpcConfig = "BossGate_Config"; private const string RpcAck = "BossGate_Ack"; private const int Protocol = 1; private const float AckTimeout = 20f; private static readonly HashSet<ZRpc> Acked = new HashSet<ZRpc>(); private static readonly Dictionary<ZNetPeer, float> Pending = new Dictionary<ZNetPeer, float>(); public static void Register(ZNet znet, ZNetPeer peer) { if ((Object)(object)znet == (Object)null || peer == null || peer.m_rpc == null) { return; } try { peer.m_rpc.Register<ZPackage>("BossGate_Config", (Action<ZRpc, ZPackage>)delegate(ZRpc connection, ZPackage pkg) { OnConfig(connection, pkg); }); peer.m_rpc.Register<ZPackage>("BossGate_Ack", (Action<ZRpc, ZPackage>)delegate(ZRpc connection, ZPackage pkg) { OnAck(connection); }); } catch (Exception ex) { BossGatePlugin.Log.LogWarning((object)("Could not register RPCs: " + ex.Message)); } if (!znet.IsServer()) { BossGatePlugin.HasServerConfig = false; BossGatePlugin.SyncedPortalRule = false; BossGatePlugin.SyncedPreviousRule = false; BossGatePlugin.SyncedPerPlayer = false; BossGatePlugin.SyncedDays.Clear(); } } public static ZNetPeer FindPeer(ZNet znet, ZRpc rpc) { if ((Object)(object)znet == (Object)null || rpc == null) { return null; } foreach (ZNetPeer peer in znet.GetPeers()) { if (peer != null && peer.m_rpc == rpc) { return peer; } } return null; } private static ZPackage Build() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(1); val.Write(BossGatePlugin.LockAllBosses.Value); val.Write(BossGatePlugin.Bosses.Count); foreach (BossGatePlugin.BossInfo boss in BossGatePlugin.Bosses) { val.Write(boss.Key); val.Write(boss.Day.Value); } val.Write(BossGatePlugin.PortalItemsRequireBossKill.Value); val.Write(BossGatePlugin.RequirePreviousBossDefeated.Value); val.Write(BossGatePlugin.PerPlayerProgress.Value); return val; } public static void SendTo(ZNetPeer peer) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || peer == null || peer.m_rpc == null || peer.m_uid == 0L) { return; } try { peer.m_rpc.Invoke("BossGate_Config", new object[1] { Build() }); if (!Pending.ContainsKey(peer)) { Pending[peer] = Time.time; } } catch (Exception ex) { BossGatePlugin.Log.LogWarning((object)("Failed to send config: " + ex.Message)); } } public static void Broadcast() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } foreach (ZNetPeer item in ZNet.instance.GetPeers().ToList()) { SendTo(item); } } private static bool TryReadBool(ZPackage pkg, out bool value) { value = false; try { value = pkg.ReadBool(); return true; } catch { return false; } } private static void OnConfig(ZRpc rpc, ZPackage pkg) { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { return; } try { int num = pkg.ReadInt(); if (num != 1) { BossGatePlugin.Log.LogWarning((object)"BossGate protocol mismatch with the server."); return; } bool syncedLock = pkg.ReadBool(); int num2 = pkg.ReadInt(); BossGatePlugin.SyncedDays.Clear(); for (int i = 0; i < num2; i++) { string key = pkg.ReadString(); int num3 = pkg.ReadInt(); BossGatePlugin.SyncedDays[key] = Mathf.Clamp(num3, 0, 9999); } bool value = false; bool value2 = false; if (TryReadBool(pkg, out var value3) && TryReadBool(pkg, out value)) { TryReadBool(pkg, out value2); } BossGatePlugin.SyncedPortalRule = value3; BossGatePlugin.SyncedPreviousRule = value; BossGatePlugin.SyncedPerPlayer = value2; BossGatePlugin.SyncedLock = syncedLock; BossGatePlugin.HasServerConfig = true; BossGatePlugin.Log.LogInfo((object)"Received BossGate config from the server."); rpc.Invoke("BossGate_Ack", new object[1] { (object)new ZPackage() }); } catch (Exception ex) { BossGatePlugin.Log.LogWarning((object)("Failed to read the server config: " + ex.Message)); } } private static void OnAck(ZRpc rpc) { Acked.Add(rpc); } private static void KickPeer(ZNet znet, ZNetPeer peer) { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; string[] array = new string[2] { "Kick", "Disconnect" }; foreach (string b in array) { MethodInfo[] methods = typeof(ZNet).GetMethods(bindingAttr); foreach (MethodInfo methodInfo in methods) { if (string.Equals(methodInfo.Name, b, StringComparison.Ordinal)) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(ZNetPeer)) { methodInfo.Invoke(znet, new object[1] { peer }); return; } } } } ZRpc rpc = peer.m_rpc; if (rpc != null) { rpc.Invoke("Disconnect", Array.Empty<object>()); } } public static void Tick() { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { Pending.Clear(); Acked.Clear(); return; } List<ZNetPeer> peers = instance.GetPeers(); Acked.RemoveWhere((ZRpc r) => !peers.Any((ZNetPeer p) => p.m_rpc == r)); foreach (ZNetPeer item in Pending.Keys.ToList()) { if (!peers.Contains(item) || Acked.Contains(item.m_rpc)) { Pending.Remove(item); } else if (BossGatePlugin.KickClientsWithoutMod.Value && Time.time - Pending[item] > 20f) { BossGatePlugin.Log.LogInfo((object)("Disconnecting a player who does not run BossGate (uid " + item.m_uid + ").")); Pending.Remove(item); try { KickPeer(instance, item); } catch (Exception ex) { BossGatePlugin.Log.LogWarning((object)("Could not disconnect the player: " + ex.Message)); } } } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] internal class ZNet_OnNewConnection_Patch { private static void Postfix(ZNet __instance, ZNetPeer peer) { ConfigSync.Register(__instance, peer); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] internal class ZNet_RPC_PeerInfo_Patch { private static void Postfix(ZNet __instance, ZRpc rpc) { try { if (!((Object)(object)__instance == (Object)null) && rpc != null && __instance.IsServer()) { ZNetPeer val = ConfigSync.FindPeer(__instance, rpc); if (val != null) { ConfigSync.SendTo(val); } } } catch (Exception ex) { BossGatePlugin.Log.LogWarning((object)("PeerInfo patch error: " + ex.Message)); } } } [HarmonyPatch(typeof(OfferingBowl), "UseItem")] internal class OfferingBowl_UseItem_Patch { private static bool Prefix(OfferingBowl __instance, Humanoid user, ItemData item, ref bool __result) { try { if (item == null || (Object)(object)__instance.m_bossItem == (Object)null) { return true; } if (item.m_shared.m_name != __instance.m_bossItem.m_itemData.m_shared.m_name) { return true; } if (BossGatePlugin.IsBlocked(__instance, user)) { __result = true; return false; } } catch (Exception ex) { BossGatePlugin.Log.LogWarning((object)("UseItem patch error: " + ex.Message)); } return true; } } [HarmonyPatch(typeof(OfferingBowl), "Interact")] internal class OfferingBowl_Interact_Patch { private static bool Prefix(OfferingBowl __instance, Humanoid user, bool hold, ref bool __result) { try { if (hold) { return true; } if (!__instance.m_useItemStands) { return true; } if (BossGatePlugin.IsBlocked(__instance, user)) { __result = true; return false; } } catch (Exception ex) { BossGatePlugin.Log.LogWarning((object)("Interact patch error: " + ex.Message)); } return true; } } [HarmonyPatch(typeof(Door), "Interact")] internal class Door_Interact_Patch { private static bool Prefix(Door __instance, Humanoid __0, bool __1, ref bool __result) { try { if (__1) { return true; } if (BossGatePlugin.IsBlockedDoor(__instance, __0)) { __result = true; return false; } } catch (Exception ex) { BossGatePlugin.Log.LogWarning((object)("Door.Interact patch error: " + ex.Message)); } return true; } } [HarmonyPatch(typeof(Door), "UseItem")] internal class Door_UseItem_Patch { private static bool Prefix(Door __instance, Humanoid __0, ref bool __result) { try { if (BossGatePlugin.IsBlockedDoor(__instance, __0)) { __result = true; return false; } } catch (Exception ex) { BossGatePlugin.Log.LogWarning((object)("Door.UseItem patch error: " + ex.Message)); } return true; } } internal static class PortalGate { private static readonly Dictionary<string, BossGatePlugin.BossInfo[]> ItemRequirements = new Dictionary<string, BossGatePlugin.BossInfo[]>(); private static readonly Dictionary<SharedData, string> IdCache = new Dictionary<SharedData, string>(); private static bool _errorLogged; private const int AlwaysBlockedToolTier = 1000; internal static void Init() { BossGatePlugin.BossInfo bossInfo = Boss("TheElder"); BossGatePlugin.BossInfo bossInfo2 = Boss("Moder"); BossGatePlugin.BossInfo bossInfo3 = Boss("Yagluth"); Map(new BossGatePlugin.BossInfo[1] { bossInfo }, "copper", "copperore", "copperscrap", "tin", "tinore", "bronze", "bronzescrap"); Map(new BossGatePlugin.BossInfo[1] { Boss("Bonemass") }, "iron", "ironore", "ironscrap", "ironpit"); Map(new BossGatePlugin.BossInfo[1] { bossInfo2 }, "silver", "silverore", "dragonegg"); Map(new BossGatePlugin.BossInfo[1] { bossInfo3 }, "blackmetal", "blackmetalscrap"); Map(new BossGatePlugin.BossInfo[1] { Boss("TheQueen") }, "mechanicalspring", "dvergrneedle"); Map(new BossGatePlugin.BossInfo[1] { Boss("Fader") }, "flametal", "flametalore", "flametalnew", "flametalorenew", "charredcogwheel"); Map(new BossGatePlugin.BossInfo[1] { Boss("Kall") }, "gold", "goldore"); Map(new BossGatePlugin.BossInfo[2] { Miniboss("Brenna", "bosshildir1"), bossInfo }, "chest_hildir1"); Map(new BossGatePlugin.BossInfo[2] { Miniboss("Geirrhafa", "bosshildir2"), bossInfo2 }, "chest_hildir2"); Map(new BossGatePlugin.BossInfo[2] { Miniboss("Zil & Thungr", "bosshildir3"), bossInfo3 }, "chest_hildir3"); } private static BossGatePlugin.BossInfo Boss(string key) { return BossGatePlugin.Bosses.Find((BossGatePlugin.BossInfo b) => b.Key == key); } private static BossGatePlugin.BossInfo Miniboss(string name, string defeatKey) { PlayerProgress.Track(new string[1] { defeatKey }); BossGatePlugin.BossInfo bossInfo = new BossGatePlugin.BossInfo(); bossInfo.Key = name; bossInfo.FallbackName = name; bossInfo.PrefabHints = BossGatePlugin.NoHints; bossInfo.LocTokens = BossGatePlugin.NoHints; bossInfo.ItemHints = BossGatePlugin.NoHints; bossInfo.DefeatKeys = new string[1] { defeatKey }; return bossInfo; } private static void Map(BossGatePlugin.BossInfo[] required, params string[] itemIds) { if (Array.IndexOf(required, null) < 0) { foreach (string key in itemIds) { ItemRequirements[key] = required; } } } internal static bool AllowsAll(Inventory inventory) { if (inventory == null || !BossGatePlugin.EffectivePortalRule()) { return false; } List<ItemData> allItems = inventory.GetAllItems(); if (allItems == null) { return false; } for (int i = 0; i < allItems.Count; i++) { if (!MayTravel(allItems[i])) { return false; } } return true; } internal static bool MayTravel(ItemData item) { if (item == null || item.m_shared == null) { return true; } if (item.m_shared.m_toolTier >= 1000) { return false; } if (item.m_shared.m_teleportable) { return true; } if (!ItemRequirements.TryGetValue(IdOf(item), out var value)) { return false; } for (int i = 0; i < value.Length; i++) { if (!BossGatePlugin.IsBossDefeated(value[i])) { return false; } } return true; } private static string IdOf(ItemData item) { if (IdCache.TryGetValue(item.m_shared, out var value)) { return value; } value = ""; GameObject dropPrefab = item.m_dropPrefab; if ((Object)(object)dropPrefab != (Object)null) { value = ((Object)dropPrefab).name; } else if (!string.IsNullOrEmpty(item.m_shared.m_name)) { value = item.m_shared.m_name; if (value.StartsWith("$item_", StringComparison.OrdinalIgnoreCase)) { value = value.Substring(6); } } int num = value.IndexOf('('); if (num > 0) { value = value.Substring(0, num); } value = value.Trim().ToLowerInvariant(); if (value.Length > 0 && (Object)(object)dropPrefab != (Object)null) { IdCache[item.m_shared] = value; } return value; } internal static void ReportOnce(Exception e) { if (!_errorLogged) { _errorLogged = true; BossGatePlugin.Log.LogWarning((object)("Portal rule error (vanilla portal behaviour is kept): " + e.Message)); } } } [HarmonyPatch(typeof(Inventory), "IsTeleportable")] internal class Inventory_IsTeleportable_Patch { [HarmonyPriority(600)] private static void Postfix(Inventory __instance, ref bool __result) { if (__result) { return; } try { if (PortalGate.AllowsAll(__instance)) { __result = true; } } catch (Exception e) { PortalGate.ReportOnce(e); } } } internal static class PlayerProgress { private const string RpcCredit = "BossGate_Credit"; private const float NearbyRange = 40f; internal static readonly HashSet<string> TrackedKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly List<Player> _nearby = new List<Player>(); private static readonly HashSet<long> _targets = new HashSet<long>(); private static readonly Dictionary<string, string> _personalKeys = new Dictionary<string, string>(); private static long _personalKeysWorld; private static bool _errorLogged; internal static void Track(string[] defeatKeys) { foreach (string item in defeatKeys) { TrackedKeys.Add(item); } } internal static void RegisterRpc() { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register<string>("BossGate_Credit", (Action<long, string>)OnCredit); } } private static long CurrentWorld() { try { return ((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetWorldUID() : 0; } catch { return 0L; } } private static string PersonalKey(string worldKey) { long num = CurrentWorld(); if (num == 0L) { return null; } if (num != _personalKeysWorld) { _personalKeys.Clear(); _personalKeysWorld = num; } if (!_personalKeys.TryGetValue(worldKey, out var value)) { value = "bossgate_" + num + "_" + worldKey.ToLowerInvariant(); _personalKeys[worldKey] = value; } return value; } internal static bool HasCredit(BossGatePlugin.BossInfo boss) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } string[] defeatKeys = boss.DefeatKeys; for (int i = 0; i < defeatKeys.Length; i++) { string text = PersonalKey(defeatKeys[i]); if (text != null && ((Humanoid)localPlayer).HaveUniqueKey(text)) { return true; } } return false; } internal static void CreditKill(Character character) { //IL_0091: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null || ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null) { return; } string defeatSetGlobalKey = character.m_defeatSetGlobalKey; if (string.IsNullOrEmpty(defeatSetGlobalKey) || !TrackedKeys.Contains(defeatSetGlobalKey)) { return; } ZNetView component = ((Component)character).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return; } _targets.Clear(); ZDO zDO = component.GetZDO(); int s_attackers = ZDOVars.s_attackers; string text = s_attackers.ToString(); foreach (PlayerInfo player in ZNet.instance.GetPlayerList()) { ZDOID characterID = player.m_characterID; if (!((ZDOID)(ref characterID)).IsNone() && zDO.GetBool(text + player.m_name, false)) { HashSet<long> targets = _targets; characterID = player.m_characterID; targets.Add(((ZDOID)(ref characterID)).UserID); } } _nearby.Clear(); Player.GetPlayersInRange(((Component)character).transform.position, 40f, _nearby); foreach (Player item in _nearby) { ZNetView val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent<ZNetView>() : null); if ((Object)(object)val != (Object)null && val.IsValid()) { _targets.Add(val.GetZDO().GetOwner()); } } foreach (long target in _targets) { if (target != 0L) { ZRoutedRpc.instance.InvokeRoutedRPC(target, "BossGate_Credit", new object[1] { defeatSetGlobalKey }); } } } private static void OnCredit(long sender, string key) { if (string.IsNullOrEmpty(key) || !TrackedKeys.Contains(key)) { return; } string text = PersonalKey(key); if (text == null) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsDedicated() && !Player.m_addUniqueKeyQueue.Contains(text)) { Player.m_addUniqueKeyQueue.Add(text); } } else if (!((Humanoid)localPlayer).HaveUniqueKey(text)) { ((Humanoid)localPlayer).AddUniqueKey(text); BossGatePlugin.Log.LogInfo((object)("Personal progress: credited with '" + key + "' in this world.")); } } internal static void ReportOnce(Exception e) { if (!_errorLogged) { _errorLogged = true; BossGatePlugin.Log.LogWarning((object)("Personal progress error: " + e.Message)); } } } [HarmonyPatch(typeof(Game), "Start")] internal class Game_Start_Patch { private static void Postfix() { try { PlayerProgress.RegisterRpc(); } catch (Exception e) { PlayerProgress.ReportOnce(e); } } } [HarmonyPatch(typeof(Character), "OnDeath")] internal class Character_OnDeath_Patch { private static void Prefix(Character __instance) { try { PlayerProgress.CreditKill(__instance); } catch (Exception e) { PlayerProgress.ReportOnce(e); } } } [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] internal class InventoryGrid_UpdateGui_Patch { private static readonly FieldInfo ElementsField = AccessTools.Field(typeof(InventoryGrid), "m_elements"); private static readonly FieldInfo NoTeleportField = AccessTools.Field(typeof(InventoryElement), "m_noteleport"); private static void Postfix(InventoryGrid __instance) { try { if (ElementsField == null || NoTeleportField == null || !BossGatePlugin.EffectivePortalRule()) { return; } Inventory inventory = __instance.GetInventory(); List<InventoryElement> list = ElementsField.GetValue(__instance) as List<InventoryElement>; if (inventory == null || list == null) { return; } int width = inventory.GetWidth(); List<ItemData> allItems = inventory.GetAllItems(); for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; if (val == null || val.m_shared == null || val.m_shared.m_teleportable || !PortalGate.MayTravel(val)) { continue; } int num = val.m_gridPos.y * width + val.m_gridPos.x; if (num >= 0 && num < list.Count && !((Object)(object)list[num] == (Object)null)) { object? value = NoTeleportField.GetValue(list[num]); Behaviour val2 = (Behaviour)((value is Behaviour) ? value : null); if ((Object)(object)val2 != (Object)null && val2.enabled) { val2.enabled = false; } } } } catch (Exception e) { PlayerProgress.ReportOnce(e); } } } }