using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using KillMeForMyPower.Commands; using KillMeForMyPower.Managers; using KillMeForMyPower.Restrictions.BossNameManagement; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("PowersForbiddenForNewbies")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("PowersForbiddenForNewbies")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e78efca2-b045-4686-8901-4fedba31614f")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace KillMeForMyPower { public class KillMeForMyPowerUtils { private static BossNameEnum parseBossName(string value) { Logger.Log("Parsing value: " + value); return (BossNameEnum)Enum.Parse(typeof(BossNameEnum), value, ignoreCase: true); } public static BossNameEnum findBossNameByPrefabName(string value, bool logSilent = false) { Logger.Log("Parsing prefab bossName " + value + " into Enum"); foreach (BossNameEnum value2 in Enum.GetValues(typeof(BossNameEnum))) { if (value2.GetFightBossname() == value || value2.ToString() == value) { return value2; } } if (!logSilent) { Logger.LogWarning(value + " not a vanilla/monstrum boss name."); } return BossNameEnum.None; } public static string getBossNameTranslation(BossNameEnum bossName) { return bossName.GetTranslationKey(); } public static bool HasDefeatedBossNameStr(string bossName) { BossNameEnum bossNameEnum; try { bossNameEnum = parseBossName(bossName); } catch (Exception ex) { Logger.LogError("Error in HasDefeatedBossName with " + bossName + " in parseBossName. " + ex); return false; } return HasDefeatedBossName(bossNameEnum); } public static bool HasDefeatedBossName(BossNameEnum bossNameEnum) { bool flag = BossNameUtils.IsBossPowerGrantedForPlayer(bossNameEnum, Player.m_localPlayer); Logger.Log($"hasDefeated(1) for player {Player.m_localPlayer.GetPlayerName()}: {flag}"); if (!flag) { flag = ConfigurationFile.activateMidPlayDetection.Value && ((Humanoid)Player.m_localPlayer).HaveUniqueKey(bossNameEnum.GetPowerKey()); Logger.Log($"hasDefeated(2) for player {Player.m_localPlayer.GetPlayerName()}: {flag}"); if (flag) { Logger.Log("Learned the power before. Granting!"); ZRoutedRpc.instance.InvokeRoutedRPC(0L, "RPC_BossPowerGrantServer", new object[2] { bossNameEnum.ToString(), Player.m_localPlayer.GetPlayerName() }); } } return flag; } public static int GetCurrentDay() { return (int)GameManager.GetPrivateMethod(EnvMan.instance, "GetCurrentDay"); } public static int GetBossMinimumDayForPower(string bossName) { return parseBossName(bossName) switch { BossNameEnum.Eikthyr => ConfigurationFile.daysBossEikthyr.Value, BossNameEnum.TheElder => ConfigurationFile.daysBossElder.Value, BossNameEnum.Bonemass => ConfigurationFile.daysBossBonemass.Value, BossNameEnum.Moder => ConfigurationFile.daysBossModer.Value, BossNameEnum.Yagluth => ConfigurationFile.daysBossYagluth.Value, BossNameEnum.Queen => ConfigurationFile.daysBossQueen.Value, BossNameEnum.Fader => ConfigurationFile.daysBossFader.Value, _ => ConfigurationFile.daysBossModded.Value, }; } public static bool bossIsKilled(string bossToCheck) { return bossIsKilled(parseBossName(bossToCheck)); } public static bool bossIsKilled(BossNameEnum bossToCheck) { return HasDefeatedBossName(bossToCheck); } } public enum Effects { None, Burning, Frost, Lightning, Poison } public enum BossDropRule { Default, OnePlayer, DropsForEachPlayerNearby } internal class ConfigurationFile { private static ConfigEntry _serverConfigLocked; public static ConfigEntry debug; public static ConfigEntry activateMidPlayDetection; public static ConfigEntry powerCommandsAdminPlayersList; public static ConfigEntry forbiddenMessage; public static ConfigEntry vendorLocalRestrictions; public static ConfigEntry vendorHaldorBossToKill; public static ConfigEntry vendorHaldorRestrictions; public static ConfigEntry vendorHildirBossToKill; public static ConfigEntry vendorHildirRestrictions; public static ConfigEntry vendorBogWitchBossToKill; public static ConfigEntry vendorBogWitchRestrictions; public static ConfigEntry forbiddenVendorMessage; public static ConfigEntry scareEffect; public static ConfigEntry scareEffectDuration; public static ConfigEntry minimumPlayersAroundRange; public static ConfigEntry minimumPlayersAroundForbiddenMessage; public static ConfigEntry minimumPlayersAroundAmount; public static ConfigEntry restrictEnteringDungeonsBeforeKillingBossesAdmins; public static ConfigEntry restrictEnteringDungeonsBeforeKillingBosses; public static ConfigEntry restrictEnteringDungeonsBeforeKillingBossesMessage; public static ConfigEntry restrictExitingQueenDungeonIfAliveOrAlerted; public static ConfigEntry restrictUsingKeyItems; public static ConfigEntry restrictUsingKeyItemsMessage; public static ConfigEntry itemRestrictionAvailableTooltipMessage; public static ConfigEntry itemRestrictionAvailableTooltipYes; public static ConfigEntry itemRestrictionAvailableTooltipNo; public static ConfigEntry grantKillToNearbyPlayers; public static ConfigEntry dropsBossItems; public static ConfigEntry dropsBossTrophies; public static ConfigEntry minLevelToSpawnBoss; public static ConfigEntry minLevelToSpawnBossNotMet; public static ConfigEntry daysBossEikthyr; public static ConfigEntry daysBossElder; public static ConfigEntry daysBossBonemass; public static ConfigEntry daysBossModer; public static ConfigEntry daysBossYagluth; public static ConfigEntry daysBossQueen; public static ConfigEntry daysBossFader; public static ConfigEntry daysBossModded; public static ConfigEntry maxLevelBeforeBoss1Eikthyr; public static ConfigEntry maxLevelBeforeBoss2TheElder; public static ConfigEntry maxLevelBeforeBoss3Bonemass; public static ConfigEntry maxLevelBeforeBoss4Moder; public static ConfigEntry maxLevelBeforeBoss5Yagluth; public static ConfigEntry maxLevelBeforeBoss6Queen; public static ConfigEntry maxLevelBeforeBoss7Fader; public static ConfigEntry playerListForBoss1EikthyrPower; public static ConfigEntry playerListForBoss2TheElderPower; public static ConfigEntry playerListForBoss3BonemassPower; public static ConfigEntry playerListForBoss4ModerPower; public static ConfigEntry playerListForBoss5YagluthPower; public static ConfigEntry playerListForBoss6QueenPower; public static ConfigEntry playerListForBoss7FaderPower; public static ConfigEntry playerListForBoss8TherzieGorrPower; public static ConfigEntry playerListForBoss8TherzieBrutalisPower; public static ConfigEntry playerListForBoss8TherzieStormHeraldPower; public static ConfigEntry playerListForBoss8TherzieSythrakPower; private static ConfigFile configFile; private static readonly ConfigSync ConfigSync = new ConfigSync("Turbero.KillMeForMyPower") { DisplayName = "Kill Me For My Power", CurrentVersion = "2.3.1", MinimumRequiredVersion = "2.3.1" }; internal static void LoadConfig(BaseUnityPlugin plugin) { //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Expected O, but got Unknown //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Expected O, but got Unknown configFile = plugin.Config; _serverConfigLocked = config("1 - General", "Lock Configuration", value: true, "If on, the configuration is locked and can be changed by server admins only."); ConfigSync.AddLockingConfigEntry(_serverConfigLocked); debug = config("1 - General", "DebugMode", value: false, "Enabling/Disabling the debugging in the console (default = false)", synchronizedSetting: false); activateMidPlayDetection = config("1 - General", "ActivateMidPlayDetection", value: true, "Adds boss power detection to identify if the player had used the power before installing the mod in a mid-play (default = false)"); powerCommandsAdminPlayersList = config("1 - General", "Power Commands Admin Players List", "", "List of additional player names that can help admin servers to assign powers to other players or themselves using the mod commands."); forbiddenMessage = config("2 - Config", "ForbiddenMessage", "Kill the forsaken first!", "Message to show when you cannot obtain the forsaken power"); grantKillToNearbyPlayers = config("2 - Config", "GrantKillToNearbyPlayers", value: true, "Allows nearby players to grant the boss kill (default = true)"); scareEffect = config("2 - Config", "Scare Effect", Effects.Lightning, "Set up the effect when trying to access restricted items or dungeons (default = Lightning)"); scareEffectDuration = config("2 - Config", "Scare Effect Duration", 3, "Duration in seconds of the scare effect (default = 3)"); minimumPlayersAroundAmount = config("2 - Config", "Minimum Players Around - Amount", 1, new ConfigDescription("Minimum of players around an altar to spawn a boss, including yourself (default = 3)", (AcceptableValueBase)(object)new AcceptableValueRange(1, 50), Array.Empty())); minimumPlayersAroundRange = config("2 - Config", "Minimum Players Around - Range", 10f, new ConfigDescription("Range to detect minimum of players around an altar to spawn a boss, including yourself (default = 10)", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty())); minimumPlayersAroundForbiddenMessage = config("2 - Config", "Minimum Players Around - Forbidden Message", "You need at least {0} people to spawn the boss!", "Message to show when you cannot spawn the boss due to insufficient people around"); vendorLocalRestrictions = config("2.1 - Vendor Restrictions", "VendorLocalRestrictions", value: true, "Vendors allow buying items based on personal progress, not global (default = true)"); vendorHaldorBossToKill = config("2.1 - Vendor Restrictions", "VendorHaldorBossToKill", BossNameEnum.Eikthyr, "Boss to be killed before being able to talk to Haldor (default = Eikthyr). Set to 'None' to remove this restriction. Possible values: Eikthyr,TheElder,Bonemass,Moder,Yagluth,Queen,Fader,None"); vendorHaldorRestrictions = config("2.1 - Vendor Restrictions", "VendorHaldorRestrictions", "BeltStrength,Eikthyr;YmirRemains,TheElder;Thunderstone,TheElder;ChickenEgg,Yagluth", "Restricted items for Haldor split by comma and semicolon. Ex: BeltStrength,Eikthyr;YmirRemains,TheElder;Thunderstone,TheElder;ChickenEgg,Yagluth. Available boss names: Eikthyr,TheElder,Bonemass,Moder,Yagluth,Queen,Fader (empty = nothing to restrict)"); vendorHildirBossToKill = config("2.1 - Vendor Restrictions", "VendorHildirBossToKill", BossNameEnum.Eikthyr, "Boss to be killed before being able to talk to Haldor (default = Eikthyr). Set to 'None' to remove this restriction. Possible values: Eikthyr,TheElder,Bonemass,Moder,Yagluth,Queen,Fader,None"); vendorHildirRestrictions = config("2.1 - Vendor Restrictions", "VendorHildirRestrictions", "", "Restricted items for Hildir split by comma and semicolon. Ex: Ironpit,Moder. Available boss names: Eikthyr,TheElder,Bonemass,Moder,Yagluth,Queen,Fader (empty = nothing to restrict)"); vendorBogWitchBossToKill = config("2.1 - Vendor Restrictions", "VendorBogWitchBossToKill", BossNameEnum.TheElder, "Boss to be killed before being able to talk to Haldor (default = TheElder). Set to 'None' to remove this restriction. Possible values: Eikthyr,TheElder,Bonemass,Moder,Yagluth,Queen,Fader,None"); vendorBogWitchRestrictions = config("2.1 - Vendor Restrictions", "VendorBogWitchRestrictions", "", "Restricted items for BogWitch split by comma. Ex: MeadTrollPheromones,TheElder;SpicePlains,Moder;SpiceAshlands,Queen. Available boss names: Eikthyr,TheElder,Bonemass,Moder,Yagluth,Queen,Fader (empty = nothing to restrict)"); forbiddenVendorMessage = config("2.1 - Vendor Restrictions", "ForbiddenVendorMessage", "You have not killed {0} yet to buy my stuff!", "Message to show when you cannot buy from a NPC"); restrictEnteringDungeonsBeforeKillingBossesAdmins = config("2.2 - Dungeon Restrictions", "Restrict Entering Dungeons Before Killing Bosses Admins", value: false, "Restricts accessing dungeons to admins as any regular player. (default = false)"); restrictEnteringDungeonsBeforeKillingBosses = config("2.2 - Dungeon Restrictions", "Restrict Entering Dungeons Before Killing Bosses", value: true, "Restricts accessing dungeons in biomes if you haven't killed the boss of the previous biome. (default = true)"); restrictEnteringDungeonsBeforeKillingBossesMessage = config("2.2 - Dungeon Restrictions", "Restrict Entering Dungeons Before Killing Bosses Message", "You must defeat {0} to access this dungeon first!", "Message to show the access restriction to dungeons if previous biome boss has not been defeated."); restrictExitingQueenDungeonIfAliveOrAlerted = config("2.2 - Dungeon Restrictions", "Restrict Exiting Queen Dungeon If Alive or Alerted", value: true, "Blocks user the Queen room without killing her! (default = false)"); restrictUsingKeyItems = config("2.3 - Item Restrictions", "RestrictUsingKeyItems", value: true, "Restricts using pickaxe antler, crypt key, wishbone and wisplight until you kill the previous boss even if you get them from someone else. (default = true)"); restrictUsingKeyItemsMessage = config("2.3 - Item Restrictions", "RestrictUsingKeyItemsMessage", "You must kill {0} before doing that action!", "Message to show when you cannot equip or do an action with an important progression item"); itemRestrictionAvailableTooltipMessage = config("2.3 - Item Restrictions", "ItemRestrictionAvailableTooltipMessage", "Available to use", "Message to show in item descriptions to know when you can start using them"); itemRestrictionAvailableTooltipYes = config("2.3 - Item Restrictions", "ItemRestrictionAvailableTooltipMessageYes", "YES", "Message to show in item descriptions confirming when you can use them"); itemRestrictionAvailableTooltipNo = config("2.3 - Item Restrictions", "ItemRestrictionAvailableTooltipMessageNo", "NO", "Message to show in item descriptions confirming when you cannot use them"); dropsBossItems = config("2.4 - Boss drops rules", "Item drops", BossDropRule.Default, "If not default, drops a number of boss items according to the selected rule"); dropsBossTrophies = config("2.4 - Boss drops rules", "Trophy drops", BossDropRule.Default, "If not default, drops a number of trophies according to the selected rule"); minLevelToSpawnBoss = config("2.5 - EpicMMO integration", "Minimum level required", value: true, "If true and epicMMO is enabled, the player needs to have the same level as the boss as minimum to spawn it (default = true)"); minLevelToSpawnBossNotMet = config("2.5 - EpicMMO integration", "Not minimum level message", "You need at least level {0} to start this battle", "Message to indicate the player doesn't have the minimum necessary level to spawn the boss"); daysBossEikthyr = config("3 - Days", "DaysBossEikthyr", 100000, "Minimum number of days until the Eikthyr power cannot be obtained without killing him (default = 10000)"); daysBossElder = config("3 - Days", "DaysBossElder", 100000, "Minimum number of days until the Elder power cannot be obtained without killing him (default = 10000)"); daysBossBonemass = config("3 - Days", "DaysBossBonemass", 100000, "Minimum number of days until the Bonemass power cannot be obtained without killing him (default = 10000)"); daysBossModer = config("3 - Days", "DaysBossModer", 100000, "Minimum number of days until the Moder power cannot be obtained without killing him (default = 10000)"); daysBossYagluth = config("3 - Days", "DaysBossYagluth", 100000, "Minimum number of days until the Yagluth power cannot be obtained without killing him (default = 10000)"); daysBossQueen = config("3 - Days", "DaysBossQueen", 100000, "Minimum number of days until the Queen power cannot be obtained without killing him (default = 10000)"); daysBossFader = config("3 - Days", "DaysBossFader", 100000, "Minimum number of days until the Fader power cannot be obtained without killing him (default = 10000)"); daysBossModded = config("3 - Days", "DaysBossModded", 100000, "Minimum number of days until any modded boss power cannot be obtained without killing him (default = 10000)"); maxLevelBeforeBoss1Eikthyr = config("4 - Max levels", "MaxLevelBeforeBoss1Eikthyr", 100f, "Maximum skill level that player can level up skills before killing Eikthyr (default = 100)"); maxLevelBeforeBoss2TheElder = config("4 - Max levels", "MaxLevelBeforeBoss2TheElder", 100f, "Maximum skill level that player can level up skills before killing The Elder (default = 100)"); maxLevelBeforeBoss3Bonemass = config("4 - Max levels", "MaxLevelBeforeBoss3Bonemass", 100f, "Maximum skill level that player can level up skills before killing Bonemass (default = 100)"); maxLevelBeforeBoss4Moder = config("4 - Max levels", "MaxLevelBeforeBoss4Moder", 100f, "Maximum skill level that player can level up skills before killing Moder (default = 100)"); maxLevelBeforeBoss5Yagluth = config("4 - Max levels", "MaxLevelBeforeBoss5Yagluth", 100f, "Maximum skill level that player can level up skills before killing Yagluth (default = 100)"); maxLevelBeforeBoss6Queen = config("4 - Max levels", "MaxLevelBeforeBoss6Queen", 100f, "Maximum skill level that player can level up skills before killing The Queen (default = 100)"); maxLevelBeforeBoss7Fader = config("4 - Max levels", "MaxLevelBeforeBoss7Fader", 100f, "Maximum skill level that player can level up skills before killing Fader (default = 100)"); playerListForBoss1EikthyrPower = config("5 - Power Granted player lists", "Player List for Boss 1 - Eikthyr power", "", "List of player names that can use Eikthyr power after defeating him"); playerListForBoss2TheElderPower = config("5 - Power Granted player lists", "Player List for Boss 2 - The Elder power", "", "List of player names that can use The Elder power after defeating him"); playerListForBoss3BonemassPower = config("5 - Power Granted player lists", "Player List for Boss 3 - Bonemass power", "", "List of player names that can use Bonemass power after defeating him"); playerListForBoss4ModerPower = config("5 - Power Granted player lists", "Player List for Boss 4 - Moder power", "", "List of player names that can use Moder power after defeating her"); playerListForBoss5YagluthPower = config("5 - Power Granted player lists", "Player List for Boss 5 - Yagluth power", "", "List of player names that can use Yagluth power after defeating him"); playerListForBoss6QueenPower = config("5 - Power Granted player lists", "Player List for Boss 6 - Queen power", "", "List of player names that can use Queen power after defeating her"); playerListForBoss7FaderPower = config("5 - Power Granted player lists", "Player List for Boss 7 - Fader power", "", "List of player names that can use Fader power after defeating him"); playerListForBoss8TherzieGorrPower = config("5 - Power Granted player lists", "Player List for Therzie Boss - Gorr power", "", "List of player names that can use Gorr power after defeating him"); playerListForBoss8TherzieBrutalisPower = config("5 - Power Granted player lists", "Player List for Therzie Boss - Brutalis power", "", "List of player names that can use Brutalis power after defeating him"); playerListForBoss8TherzieStormHeraldPower = config("5 - Power Granted player lists", "Player List for Therzie Boss - StormHerald power", "", "List of player names that can use StormHerald power after defeating him"); playerListForBoss8TherzieSythrakPower = config("5 - Power Granted player lists", "Player List for Therzie Boss - Sythrak power", "", "List of player names that can use Sythrak power after defeating him"); } private static ConfigEntry config(string group, string name, T value, string description, bool synchronizedSetting = true) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty()), synchronizedSetting); } private static ConfigEntry config(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags); ConfigEntry val2 = configFile.Bind(group, name, value, val); SyncedConfigEntry syncedConfigEntry = ConfigSync.AddConfigEntry(val2); syncedConfigEntry.SynchronizedConfig = synchronizedSetting; return val2; } } [BepInPlugin("Turbero.KillMeForMyPower", "Kill Me For My Power", "2.3.1")] public class KillMeForMyPower : BaseUnityPlugin { [CompilerGenerated] private sealed class d__6 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public KillMeForMyPower <>4__this; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__6(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; break; } if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null) { <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; } PowerManagementCommands.RegisterConsoleCommand(); CheckBossesCommand.RegisterConsoleCommand(); ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)("Console commands registered successfully. IsServer: " + ZNet.instance.IsServer().ToString().ToUpperInvariant())); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const string GUID = "Turbero.KillMeForMyPower"; public const string NAME = "Kill Me For My Power"; public const string VERSION = "2.3.1"; private readonly Harmony harmony = new Harmony("Turbero.KillMeForMyPower"); private void Awake() { ConfigurationFile.LoadConfig((BaseUnityPlugin)(object)this); harmony.PatchAll(); } private void Start() { ((MonoBehaviour)this).StartCoroutine(WaitForNetworking()); } [IteratorStateMachine(typeof(d__6))] private IEnumerator WaitForNetworking() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__6(0) { <>4__this = this }; } private void onDestroy() { harmony.UnpatchSelf(); } } public static class Logger { public static ManualLogSource logger = Logger.CreateLogSource("Kill Me For My Power"); internal static void Log(object s) { if (ConfigurationFile.debug.Value) { logger.LogInfo((object)s?.ToString()); } } internal static void LogInfo(object s) { logger.LogInfo((object)s?.ToString()); } internal static void LogWarning(object s) { logger.LogWarning((object)s?.ToString()); } internal static void LogError(object s) { logger.LogError((object)s?.ToString()); } } } namespace KillMeForMyPower.Restrictions { [HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")] public class BossDropsRules { public static void Postfix(ref Character ___m_character, ref List> __result) { if ((ConfigurationFile.dropsBossTrophies.Value == BossDropRule.Default && ConfigurationFile.dropsBossItems.Value == BossDropRule.Default) || !___m_character.IsBoss()) { return; } int countPlayersNearby = GetCountPlayersNearby(___m_character); Logger.Log("result distinct drops count: " + __result.Count); Logger.Log("result total items count: " + __result.Select((KeyValuePair elem) => elem.Value).Sum()); Logger.Log("countPlayersNearby: " + countPlayersNearby); List> list = new List>(); foreach (KeyValuePair item in __result) { GameObject key = item.Key; int num = item.Value; string text = ((Object)key).name.ToLowerInvariant(); Logger.Log($"Previous amount for {text}: {num}"); if (ConfigurationFile.dropsBossItems.Value != 0) { int num2 = ((ConfigurationFile.dropsBossItems.Value != BossDropRule.DropsForEachPlayerNearby) ? 1 : countPlayersNearby); if (text.ToLowerInvariant().Contains("hardantler") || text.ToLowerInvariant().Contains("yagluthdrop")) { num = 3 * num2; } else if (text.ToLowerInvariant().Contains("cryptkey") || text.ToLowerInvariant().Contains("wishbone")) { num = num2; } else if (text.ToLowerInvariant().Contains("dragontear")) { num = 10 * num2; } else if (text.ToLowerInvariant().Contains("queendrop") || text.ToLowerInvariant().Contains("faderdrop")) { num = 5 * num2; } } if (text.ToLowerInvariant().Contains("trophy")) { num = ((ConfigurationFile.dropsBossItems.Value == BossDropRule.OnePlayer) ? 1 : countPlayersNearby); } Logger.Log($"New amount for {text}: {num}"); list.Add(new KeyValuePair(key, num)); } __result = list; Logger.Log("__result new total items count: " + __result.Select((KeyValuePair elem) => elem.Value).Sum()); } private static int GetCountPlayersNearby(Character boss) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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) BaseAI component = ((Component)boss).GetComponent(); float num = (Object.op_Implicit((Object)(object)component) ? component.m_viewRange : 0f); Logger.Log(string.Format("Detection boss range for {0} is {1} meters", ((Object)boss).name.Replace("(Clone)", ""), num)); List list = new List(); Vector3 position = ((Component)Player.m_localPlayer).transform.position; Player.GetPlayersInRange(position, num, list); return list.Count; } } [HarmonyPatch(typeof(Character), "OnDeath")] public class RegisterBossDefeatPatch { public static void Postfix(Character __instance) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance != (Object)null) || !__instance.IsBoss()) { return; } string text = ((Object)__instance).name.Replace("(Clone)", ""); Player localPlayer = Player.m_localPlayer; List list = new List(); list.Add(localPlayer.GetPlayerName()); if (ConfigurationFile.grantKillToNearbyPlayers.Value) { Logger.LogInfo("Finding nearby players to grant the kill"); BaseAI component = ((Component)__instance).GetComponent(); float num = (Object.op_Implicit((Object)(object)component) ? component.m_viewRange : 0f); Logger.LogInfo(string.Format("Detection boss range for {0} is {1} meters", ((Object)__instance).name.Replace("(Clone)", ""), num)); Vector3 position = ((Component)__instance).transform.position; List allPlayers = Player.GetAllPlayers(); foreach (Player item in allPlayers) { if (!((Object)(object)item == (Object)null) && !(item.GetPlayerName() == localPlayer.GetPlayerName()) && Vector3.Distance(((Component)item).transform.position, position) <= num) { list.Add(item.GetPlayerName()); } } } ZRoutedRpc.instance.InvokeRoutedRPC(0L, "RPC_BossPowerGrantServer", new object[2] { text, string.Join(",", list) }); } } [HarmonyPatch(typeof(Player), "Save")] public class Player_Save_Null_Key_Clean_Patch { [HarmonyPrefix] public static void Prefix(Player __instance) { List list = __instance.m_customData.Keys.Where((string k) => string.IsNullOrEmpty(k) || k == "null").ToList(); foreach (string item in list) { Logger.LogWarning("Removing null key from player " + __instance.GetPlayerName()); ((Humanoid)__instance).RemoveUniqueKey(item); } } } public class RPC_BossPowerGrantCalls { public static void RPC_BossPowerGrantServer(long sender, string bossEnumStr, string playersToGrant) { BossNameEnum bossNameEnum = KillMeForMyPowerUtils.findBossNameByPrefabName(bossEnumStr, logSilent: true); if (bossNameEnum != BossNameEnum.None) { Logger.Log($"[RPC_BossPowerGrantServer] RPC sent from sender {sender} with {bossEnumStr} ({bossNameEnum}) and {playersToGrant}"); string[] array = playersToGrant.Split(new char[1] { ',' }); string[] array2 = array; foreach (string playerName in array2) { BossNameUtils.GrantBossPowerToPlayer(bossNameEnum, playerName, isAdd: true); } } } public static void RPC_BossPowerRemoveGrantServer(long sender, string bossEnumStr, string playersToRemoveGrant) { BossNameEnum bossNameEnum = KillMeForMyPowerUtils.findBossNameByPrefabName(bossEnumStr); if (bossNameEnum != BossNameEnum.None) { Logger.Log($"[RPC_BossPowerRemoveGrantServer] RPC sent from sender {sender} with {bossEnumStr} ({bossNameEnum}) and {playersToRemoveGrant}"); string[] array = playersToRemoveGrant.Split(new char[1] { ',' }); string[] array2 = array; foreach (string playerName in array2) { BossNameUtils.GrantBossPowerToPlayer(bossNameEnum, playerName, isAdd: false); } } } } public class BossPowerRestriction { [HarmonyPatch(typeof(Player), "ActivateGuardianPower")] public static class ActivateGuardianPowerPatch { public static bool Prefix(Player __instance, ref bool __result) { string guardianPowerName = __instance.GetGuardianPowerName(); if (guardianPowerName == null || guardianPowerName.Equals("")) { return true; } string text = guardianPowerName.Replace("GP_", ""); Logger.Log("ActivateGuardianPower - guardianPowerName: " + text); if (!KillMeForMyPowerUtils.HasDefeatedBossNameStr(text) && KillMeForMyPowerUtils.GetCurrentDay() < KillMeForMyPowerUtils.GetBossMinimumDayForPower(text)) { ((Character)__instance).Message((MessageType)2, ConfigurationFile.forbiddenMessage.Value, 0, (Sprite)null); ApplyBlockedEffect(text); __result = false; return false; } return true; } } [HarmonyPatch(typeof(ItemStand), "DelayedPowerActivation")] public static class DelayedPowerActivationPatch { public static bool Prefix(ItemStand __instance) { StatusEffect guardianPower = __instance.m_guardianPower; string text = ((guardianPower != null) ? ((Object)guardianPower).name.Replace("GP_", "") : null); Logger.Log("DelayedPowerActivation - ItemStand name: " + text); if (!KillMeForMyPowerUtils.HasDefeatedBossNameStr(text) && KillMeForMyPowerUtils.GetCurrentDay() < KillMeForMyPowerUtils.GetBossMinimumDayForPower(text)) { ((Character)Player.m_localPlayer).Message((MessageType)2, ConfigurationFile.forbiddenMessage.Value, 0, (Sprite)null); ApplyBlockedEffect(text); return false; } return true; } } private static void ApplyBlockedEffect(string bossName) { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Expected O, but got Unknown //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Expected O, but got Unknown //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Expected O, but got Unknown //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Expected O, but got Unknown //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Expected O, but got Unknown //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Expected O, but got Unknown //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Expected O, but got Unknown //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Expected O, but got Unknown SEMan sEMan = ((Character)Player.m_localPlayer).GetSEMan(); string text = string.Join(";", from se in sEMan.GetStatusEffects() select ((Object)se).name); BossNameEnum bossNameEnum = (BossNameEnum)Enum.Parse(typeof(BossNameEnum), bossName, ignoreCase: true); if (bossNameEnum == BossNameEnum.Eikthyr && !text.Contains("Lightning")) { StatusEffect val = ((sEMan != null) ? sEMan.AddStatusEffect("Lightning".GetHashCode(), false, 0, 0f) : null); val.m_ttl = 5f; } else if (bossNameEnum == BossNameEnum.TheElder && !text.Contains("Burning")) { SE_Burning val2 = (SE_Burning)((sEMan != null) ? sEMan.AddStatusEffect("Burning".GetHashCode(), false, 0, 0f) : null); val2.AddFireDamage(Math.Max(2f, ((Character)Player.m_localPlayer).GetHealth() - 10f)); } else if (bossNameEnum == BossNameEnum.Bonemass && !text.Contains("Poison")) { SE_Poison val3 = (SE_Poison)((sEMan != null) ? sEMan.AddStatusEffect("Poison".GetHashCode(), false, 0, 0f) : null); val3.AddDamage(Math.Max(1f, ((Character)Player.m_localPlayer).GetHealth() - 10f)); } else if (bossNameEnum == BossNameEnum.Moder && !text.Contains("Frost")) { SE_Frost val4 = (SE_Frost)((sEMan != null) ? sEMan.AddStatusEffect("Frost".GetHashCode(), false, 0, 0f) : null); ((StatusEffect)val4).m_ttl = 5f; } else if (bossNameEnum == BossNameEnum.Yagluth && !text.Contains("Burning")) { SE_Burning val5 = (SE_Burning)((sEMan != null) ? sEMan.AddStatusEffect("Burning".GetHashCode(), false, 0, 0f) : null); val5.AddFireDamage(Math.Max(2f, ((Character)Player.m_localPlayer).GetHealth() - 10f)); } else if (bossNameEnum == BossNameEnum.Queen && !text.Contains("Poison")) { SE_Poison val6 = (SE_Poison)((sEMan != null) ? sEMan.AddStatusEffect("Poison".GetHashCode(), false, 0, 0f) : null); val6.AddDamage(Math.Max(1f, ((Character)Player.m_localPlayer).GetHealth() - 10f)); } else if (bossNameEnum == BossNameEnum.Fader && !text.Contains("Burning")) { SE_Burning val7 = (SE_Burning)((sEMan != null) ? sEMan.AddStatusEffect("Burning".GetHashCode(), false, 0, 0f) : null); val7.AddFireDamage(Math.Max(2f, ((Character)Player.m_localPlayer).GetHealth() - 10f)); } else if (bossNameEnum == BossNameEnum.SE_Boss_Gorr && !text.Contains("Frost")) { SE_Frost val8 = (SE_Frost)((sEMan != null) ? sEMan.AddStatusEffect("Frost".GetHashCode(), false, 0, 0f) : null); ((StatusEffect)val8).m_ttl = 5f; } else if (bossNameEnum == BossNameEnum.SE_Boss_Brutalis && !text.Contains("Burning")) { SE_Burning val9 = (SE_Burning)((sEMan != null) ? sEMan.AddStatusEffect("Burning".GetHashCode(), false, 0, 0f) : null); val9.AddFireDamage(Math.Max(2f, ((Character)Player.m_localPlayer).GetHealth() - 10f)); } else if (bossNameEnum == BossNameEnum.SE_Boss_Sythrak && !text.Contains("Burning")) { SE_Burning val10 = (SE_Burning)((sEMan != null) ? sEMan.AddStatusEffect("Burning".GetHashCode(), false, 0, 0f) : null); val10.AddFireDamage(Math.Max(2f, ((Character)Player.m_localPlayer).GetHealth() - 10f)); } else if (bossNameEnum == BossNameEnum.SE_Boss_StormHerald && !text.Contains("Lightning")) { StatusEffect val11 = ((sEMan != null) ? sEMan.AddStatusEffect("Lightning".GetHashCode(), false, 0, 0f) : null); val11.m_ttl = 5f; } } } public class DungeonRestriction { [HarmonyPatch(typeof(Teleport), "OnTriggerEnter")] [HarmonyPriority(700)] public class Teleport_OnTriggerEnter_Patch { [HarmonyPrefix] public static bool Prefix(Teleport __instance, Collider collider) { if (!ConfigurationFile.restrictEnteringDungeonsBeforeKillingBosses.Value) { return true; } Logger.Log("[Teleport - OnTriggerEnter] entered"); string text = ((Object)__instance).name.Replace("(Clone)", "").ToLower(); Logger.Log("[Teleport - OnTriggerEnter] Trying to teleport the player through " + text); Player componentInParent = ((Component)collider).GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null) { return true; } if (!ConfigurationFile.restrictEnteringDungeonsBeforeKillingBossesAdmins.Value && GameManager.IsAdmin(componentInParent)) { Logger.Log("Player " + componentInParent.GetPlayerName() + " is admin. Skipping restriction to enter..."); return true; } return canCrossEntrance(text, componentInParent); } } [HarmonyPatch(typeof(Player), "Interact")] public class DetectDungeonInteractPatch { [HarmonyPrefix] public static bool Prefix(GameObject go, Player __instance) { if ((Object)(object)go == (Object)null) { return true; } if (((Object)go).name != "Cube") { return true; } Logger.Log("[Player - Interact] Cube touched"); if (!ConfigurationFile.restrictEnteringDungeonsBeforeKillingBossesAdmins.Value && GameManager.IsAdmin(__instance)) { Logger.Log("Player " + __instance.GetPlayerName() + " is admin. Skipping restriction to enter..."); return true; } return canCrossEntrance(((Character)__instance).InInterior() ? "exteriorgateway" : "gateway", __instance); } } private static bool canCrossEntrance(string targetEntranceName, Player player) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Invalid comparison between Unknown and I4 //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Invalid comparison between Unknown and I4 //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Invalid comparison between Unknown and I4 if (targetEntranceName == "gateway") { if ((int)player.GetCurrentBiome() == 8 && !KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Eikthyr)) { return negateAccess(player, BossNameEnum.Eikthyr); } if ((int)player.GetCurrentBiome() == 2 && !KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.TheElder)) { return negateAccess(player, BossNameEnum.TheElder); } if ((int)player.GetCurrentBiome() == 4 && !KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Bonemass)) { return negateAccess(player, BossNameEnum.Bonemass); } if ((int)player.GetCurrentBiome() == 512 && !KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Yagluth)) { return negateAccess(player, BossNameEnum.Yagluth); } if ((int)player.GetCurrentBiome() == 32 && !KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Queen)) { return negateAccess(player, BossNameEnum.Queen); } } if (ConfigurationFile.restrictExitingQueenDungeonIfAliveOrAlerted.Value && targetEntranceName == "exteriorgateway" && IsQueenNearbyAndAlert(player)) { ((Character)player).Message((MessageType)2, Localization.instance.Localize("$msg_blockedbyboss"), 0, (Sprite)null); Effects.scareEffect(); return false; } return true; } private static bool negateAccess(Player player, BossNameEnum bossName) { ((Character)player).Message((MessageType)2, ConfigurationFile.restrictEnteringDungeonsBeforeKillingBossesMessage.Value.Replace("{0}", Localization.instance.Localize(bossName.GetTranslationKey())), 0, (Sprite)null); Effects.scareEffect(); return false; } public static bool IsQueenNearbyAndAlert(Player player) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)player).transform.position; foreach (Character allCharacter in Character.GetAllCharacters()) { if ((Object)(object)allCharacter != (Object)null && !allCharacter.IsDead() && ((Object)allCharacter).name.StartsWith("SeekerQueen", StringComparison.OrdinalIgnoreCase)) { bool flag = ((Component)allCharacter).GetComponent().IsAlerted(); bool flag2 = Location.IsInsideActiveBossDungeon(position); Logger.Log($"isAlerted {flag}, isBossActive {flag2}"); if (!flag && !flag2) { Logger.LogInfo("SeekerQueen is neither close nor active. Lucky!"); return false; } float num = Vector3.Distance(position, ((Component)allCharacter).transform.position); Logger.Log("SeekerQueen is " + num + " meters away!"); if (num <= 60f) { return true; } } } return false; } } public class Effects { public static void scareEffect() { global::KillMeForMyPower.Effects value = ConfigurationFile.scareEffect.Value; if (value != 0) { SEMan sEMan = ((Character)Player.m_localPlayer).GetSEMan(); if ((Object)(object)((sEMan != null) ? sEMan.GetStatusEffect(value.ToString().GetHashCode()) : null) == (Object)null) { StatusEffect val = ((sEMan != null) ? sEMan.AddStatusEffect(value.ToString().GetHashCode(), false, 0, 0f) : null); val.m_ttl = ConfigurationFile.scareEffectDuration.Value; } } } } [HarmonyPatch(typeof(Humanoid), "EquipItem")] public class PlayerEquipItemPatch { [HarmonyPrefix] public static bool EquipItemPrefix(Humanoid __instance, ItemData item, bool triggerEquipEffects, ref bool __result) { if (!ConfigurationFile.restrictUsingKeyItems.Value) { return true; } if (__instance is Player) { if (item.m_shared.m_name == "$item_pickaxe_antler" && !KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Eikthyr)) { ((Character)__instance).Message((MessageType)2, ConfigurationFile.restrictUsingKeyItemsMessage.Value.Replace("{0}", BossNameEnum.Eikthyr.GetTranslationKey()), 0, (Sprite)null); Effects.scareEffect(); __result = false; return false; } if (item.m_shared.m_name == "$item_wishbone" && !KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Bonemass)) { ((Character)__instance).Message((MessageType)2, ConfigurationFile.restrictUsingKeyItemsMessage.Value.Replace("{0}", BossNameEnum.Bonemass.GetTranslationKey()), 0, (Sprite)null); Effects.scareEffect(); __result = false; return false; } if (item.m_shared.m_name == "$item_demister" && !KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Yagluth)) { ((Character)__instance).Message((MessageType)2, ConfigurationFile.restrictUsingKeyItemsMessage.Value.Replace("{0}", BossNameEnum.Yagluth.GetTranslationKey()), 0, (Sprite)null); Effects.scareEffect(); __result = false; return false; } } return true; } } [HarmonyPatch(typeof(Door), "Interact")] public class SwampKeyRestrictionPatch { [HarmonyPrefix] public static bool InteractPrefix(Door __instance, Humanoid character, bool hold, ref bool __result) { if (!ConfigurationFile.restrictUsingKeyItems.Value) { return true; } if (((Object)__instance).name.Contains("sunken_crypt_gate") && character is Player && !KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.TheElder)) { ((Character)character).Message((MessageType)2, ConfigurationFile.restrictUsingKeyItemsMessage.Value.Replace("{0}", BossNameEnum.TheElder.GetTranslationKey()), 0, (Sprite)null); Effects.scareEffect(); __result = false; return false; } return true; } } [HarmonyPatch(typeof(UITooltip), "UpdateTextElements")] public class UITooltipPatch { public static void Postfix(UITooltip __instance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown GameObject val = (GameObject)GameManager.GetPrivateValue(__instance, "m_tooltip", BindingFlags.Static | BindingFlags.NonPublic); if (!((Object)(object)val != (Object)null)) { return; } Transform val2 = Utils.FindChild(val.transform, "Text", (IterativeSearchType)0); if ((Object)(object)val2 != (Object)null && __instance.m_topic != null) { if (__instance.m_topic == "$item_pickaxe_antler") { updateTooltipText(__instance, val2, BossNameEnum.Eikthyr); } else if (__instance.m_topic == "$item_demister") { updateTooltipText(__instance, val2, BossNameEnum.Yagluth); } else if (__instance.m_topic == "$item_wishbone") { updateTooltipText(__instance, val2, BossNameEnum.Bonemass); } else if (__instance.m_topic == "$item_cryptkey") { updateTooltipText(__instance, val2, BossNameEnum.TheElder); } } } private static void updateTooltipText(UITooltip __instance, Transform transform, BossNameEnum bossToCheck) { bool flag = KillMeForMyPowerUtils.HasDefeatedBossName(bossToCheck); string text = (flag ? ConfigurationFile.itemRestrictionAvailableTooltipYes.Value : ConfigurationFile.itemRestrictionAvailableTooltipNo.Value); string text2 = (flag ? "green" : "red"); string text3 = ConfigurationFile.itemRestrictionAvailableTooltipMessage.Value + ": " + text + ""; __instance.m_text = __instance.m_text.Replace("_description", "_description\n" + text3); ((Component)transform).GetComponent().text = Localization.instance.Localize(__instance.m_text); string[] array = ((Component)transform).GetComponent().text.Split(new char[1] { '\n' }); ((Component)transform).GetComponent().text = string.Join("\n", array, 0, array.Length - 1); } } [HarmonyPatch(typeof(OfferingBowl), "InitiateSpawnBoss")] public static class InitiateSpawnBossPatch { private static bool Prefix(OfferingBowl __instance, Vector3 point, bool removeItemsFromInventory) { //IL_0038: 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_003e: Unknown result type (might be due to invalid IL or missing references) Logger.Log("Detecting players around " + ConfigurationFile.minimumPlayersAroundRange.Value + " meters..."); List list = new List(); Vector3 position = ((Component)Player.m_localPlayer).transform.position; Player.GetPlayersInRange(position, ConfigurationFile.minimumPlayersAroundRange.Value, list); if (list.Count < ConfigurationFile.minimumPlayersAroundAmount.Value) { Logger.Log($"{list} players detected. Minimum required: {ConfigurationFile.minimumPlayersAroundAmount.Value}. Cancelling..."); ((Character)Player.m_localPlayer).Message((MessageType)2, ConfigurationFile.minimumPlayersAroundForbiddenMessage.Value.Replace("{0}", ConfigurationFile.minimumPlayersAroundAmount.Value.ToString()), 0, (Sprite)null); return false; } Logger.Log("No monsters detected."); if (ConfigurationFile.minLevelToSpawnBoss.Value && EpicMMOSystem_API.IsLoaded()) { try { string name = ((Object)__instance.m_bossPrefab).name; object obj = GetMonsters()[name + "(Clone)"]; if (obj != null) { int monsterLevel = GetMonsterLevel(obj); if (EpicMMOSystem_API.GetLevel() < monsterLevel) { ((Character)Player.m_localPlayer).Message((MessageType)2, ConfigurationFile.minLevelToSpawnBossNotMet.Value.Replace("{0}", monsterLevel.ToString()), 0, (Sprite)null); return false; } Logger.Log("Level enough to spawn."); } } catch (Exception ex) { Logger.LogWarning("Error when calculating monster level. Ignoring restriction. Error: " + ex); } } return true; } private static IDictionary GetMonsters() { Type type = Type.GetType("EpicMMOSystem.DataMonsters, EpicMMOSystem"); if (type == null) { return new Dictionary(); } return type.GetField("dictionary", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as IDictionary; } private static int GetMonsterLevel(object monster) { FieldInfo field = monster.GetType().GetField("level", BindingFlags.Instance | BindingFlags.Public); if (field != null) { return (int)field.GetValue(monster); } return 0; } } public class LevelCalculation { public static bool canSkillUp(float skillLevelValue) { return (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Eikthyr) || !(skillLevelValue >= ConfigurationFile.maxLevelBeforeBoss1Eikthyr.Value)) && (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.TheElder) || !(skillLevelValue >= ConfigurationFile.maxLevelBeforeBoss2TheElder.Value)) && (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Bonemass) || !(skillLevelValue >= ConfigurationFile.maxLevelBeforeBoss3Bonemass.Value)) && (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Moder) || !(skillLevelValue >= ConfigurationFile.maxLevelBeforeBoss4Moder.Value)) && (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Yagluth) || !(skillLevelValue >= ConfigurationFile.maxLevelBeforeBoss5Yagluth.Value)) && (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Queen) || !(skillLevelValue >= ConfigurationFile.maxLevelBeforeBoss6Queen.Value)) && (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Fader) || !(skillLevelValue >= ConfigurationFile.maxLevelBeforeBoss7Fader.Value)); } private static float currentMaxSkillLevel() { if (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Fader)) { return float.MaxValue; } if (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Queen)) { return ConfigurationFile.maxLevelBeforeBoss7Fader.Value; } if (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Yagluth)) { return ConfigurationFile.maxLevelBeforeBoss6Queen.Value; } if (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Moder)) { return ConfigurationFile.maxLevelBeforeBoss5Yagluth.Value; } if (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Bonemass)) { return ConfigurationFile.maxLevelBeforeBoss4Moder.Value; } if (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.TheElder)) { return ConfigurationFile.maxLevelBeforeBoss3Bonemass.Value; } if (KillMeForMyPowerUtils.HasDefeatedBossName(BossNameEnum.Eikthyr)) { return ConfigurationFile.maxLevelBeforeBoss2TheElder.Value; } return ConfigurationFile.maxLevelBeforeBoss1Eikthyr.Value; } public static bool reviewAndUpdateSkill(Player player, SkillType skillType, string buffName) { //IL_0007: 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) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) Skill val = ((Character)player).GetSkills().GetSkillList().Find((Skill s) => s.m_info.m_skill == skillType); if (val == null) { return true; } int num = (int)val.m_level; float num2 = (float)num + val.GetLevelPercentage(); float num3 = currentMaxSkillLevel(); bool flag = num2 < num3; Logger.Log($"Decision with skill {skillType}, skillLevel {num}, percentage {val.GetLevelPercentage()} and currentLevel {num2}: {flag}"); if (!flag) { val.m_level = Math.Min(num, num3); val.m_accumulator = 0f; Logger.Log($"{skillType} rounded to max allowed value: {val.m_level}"); if (GameManager.isDetailedLevelsInstalled()) { Logger.Log("Checking DetailedLevels buff..."); List list = (List)GameManager.GetPrivateValue(((Character)Player.m_localPlayer).GetSEMan(), "m_statusEffects"); StatusEffect val2 = list.Find((StatusEffect effect) => effect.m_name.Contains(buffName)); if ((Object)(object)val2 != (Object)null) { Logger.Log("Fixing value in buff"); val2.m_name = $"{buffName}: {val.m_level}"; } } } return flag; } } [HarmonyPatch(typeof(Player), "RaiseSkill")] public class Player_RaiseSkill_Prefix_Patch { private static bool Prefix(Player __instance, SkillType skill, float value) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance != (Object)null) { float skillLevel = ((Character)__instance).GetSkills().GetSkillLevel(skill); Logger.Log($"Checking skill level availability {skill} with {skillLevel}..."); if (skillLevel < 100f) { return LevelCalculation.reviewAndUpdateSkill(__instance, skill, "$skill_" + ((object)(SkillType)(ref skill)).ToString().ToLower()); } } return true; } } [HarmonyPatch] [HarmonyPriority(700)] public class Dodge_ManualUpdate_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Player), "Dodge", (Type[])null, (Type[])null); } private static void Postfix(ref Player __instance, Vector3 dodgeDir) { Logger.Log("Checking dodge skill here since it doesn't seem to go through Player.RaiseSkill..."); WaitForSecondsAsync(__instance, 0.1f); } private static async Task WaitForSecondsAsync(Player player, float seconds) { await Task.Delay((int)(Math.Max(0f, seconds) * 1000f)); LevelCalculation.reviewAndUpdateSkill(player, (SkillType)108, "$skill_dodge"); } } [HarmonyPatch(typeof(Character), "Damage")] public class BloodMagic_ManualUpdate_Patch { private static void Postfix(Character __instance, HitData hit) { if (!((Object)(object)__instance != (Object)null)) { return; } Character attacker = hit.GetAttacker(); if (!((Object)(object)attacker != (Object)null)) { return; } if (attacker.IsTamed() && __instance.IsMonsterFaction(0f)) { WaitForSecondsAsync(null, 0.1f); } else if (attacker.IsMonsterFaction(0f) && ((object)__instance).GetType() == typeof(Player)) { WaitForSecondsAsync((Player)(object)((__instance is Player) ? __instance : null), 0.1f); } else if (((Object)attacker).name.Contains("Troll_Summoned")) { if (((object)__instance).GetType() == typeof(Player)) { WaitForSecondsAsync((Player)(object)((__instance is Player) ? __instance : null), 0.1f); } else if (__instance.IsTamed()) { WaitForSecondsAsync(null, 0.1f); } } } private static async Task WaitForSecondsAsync(Player player, float seconds) { Logger.Log("Checking blood magic here since it doesn't seem to go through Player.RaiseSkill..."); await Task.Delay((int)(Math.Max(0f, seconds) * 1000f)); LevelCalculation.reviewAndUpdateSkill(((Object)(object)player == (Object)null) ? Player.m_localPlayer : player, (SkillType)10, "$skill_bloodmagic"); } } [HarmonyPatch(typeof(SkillsDialog), "Setup")] [HarmonyPriority(100)] public class SkillsDialogAdditions_Patch { private static void Postfix(SkillsDialog __instance, ref Player player, ref List ___m_elements) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) List skillList = ((Character)player).GetSkills().GetSkillList(); for (int i = 0; i < skillList.Count; i++) { GameObject val = ___m_elements[i]; Skill val2 = skillList[i]; Color color = (LevelCalculation.canSkillUp((int)val2.m_level) ? Color.white : Color.red); ((Component)Utils.FindChild(val.transform, "leveltext", (IterativeSearchType)0)).GetComponent().color = color; } } } public class TraderSetup { [HarmonyPatch(typeof(Trader), "Interact")] public class TraderInteractPatch { [HarmonyPrefix] public static bool InteractPrefix(Trader __instance, Humanoid character, bool hold, bool alt) { Logger.Log("**Trader.Interact called for " + ((Object)((Component)__instance).gameObject).name); if (!ConfigurationFile.vendorLocalRestrictions.Value) { return true; } if (!grantedVendor(__instance)) { string mandatoryBossToKill = getMandatoryBossToKill(__instance); if (mandatoryBossToKill != null) { ((Character)character).Message((MessageType)2, ConfigurationFile.forbiddenVendorMessage.Value.Replace("{0}", mandatoryBossToKill), 0, (Sprite)null); return false; } } return true; } private static string getMandatoryBossToKill(Trader trader) { if (((Object)((Component)trader).gameObject).name.StartsWith("Haldor") && ConfigurationFile.vendorHaldorBossToKill.Value != BossNameEnum.None) { return Localization.instance.Localize(KillMeForMyPowerUtils.getBossNameTranslation(ConfigurationFile.vendorHaldorBossToKill.Value)); } if (((Object)((Component)trader).gameObject).name.StartsWith("Hildir") && ConfigurationFile.vendorHildirBossToKill.Value != BossNameEnum.None) { return Localization.instance.Localize(KillMeForMyPowerUtils.getBossNameTranslation(ConfigurationFile.vendorHildirBossToKill.Value)); } if (((Object)((Component)trader).gameObject).name.StartsWith("BogWitch") && ConfigurationFile.vendorBogWitchBossToKill.Value != BossNameEnum.None) { return Localization.instance.Localize(KillMeForMyPowerUtils.getBossNameTranslation(ConfigurationFile.vendorBogWitchBossToKill.Value)); } return null; } private static bool grantedVendor(Trader trader) { bool flag = ((Object)((Component)trader).gameObject).name.StartsWith("Haldor") && KillMeForMyPowerUtils.bossIsKilled(ConfigurationFile.vendorHaldorBossToKill.Value); bool flag2 = ((Object)((Component)trader).gameObject).name.StartsWith("Hildir") && KillMeForMyPowerUtils.bossIsKilled(ConfigurationFile.vendorHildirBossToKill.Value); bool flag3 = ((Object)((Component)trader).gameObject).name.StartsWith("BogWitch") && KillMeForMyPowerUtils.bossIsKilled(ConfigurationFile.vendorBogWitchBossToKill.Value); return flag || flag2 || flag3; } } [HarmonyPatch(typeof(Trader), "GetAvailableItems")] public class TraderGetAvailableItemsPatch { [HarmonyPostfix] public static void GetAvailableItemsPostfix(Trader __instance, ref List __result) { Logger.Log("**Trader.GetAvailableItems called for " + ((Object)((Component)__instance).gameObject).name); if (!ConfigurationFile.vendorLocalRestrictions.Value) { return; } List list = new List(); if (((Object)__instance).name.Contains("Haldor")) { addCustomItemsToRemove(__result, list, ConfigurationFile.vendorHaldorRestrictions.Value); } else if (((Object)__instance).name.Contains("Hildir")) { addCustomItemsToRemove(__result, list, ConfigurationFile.vendorHildirRestrictions.Value); } else if (((Object)__instance).name.Contains("BogWitch")) { addCustomItemsToRemove(__result, list, ConfigurationFile.vendorBogWitchRestrictions.Value); } foreach (TradeItem item in list) { __result.Remove(item); } } private static void addCustomItemsToRemove(List __result, List customItemsToRemove, string vendorRestrictions) { if (string.IsNullOrEmpty(vendorRestrictions)) { return; } string[] array = vendorRestrictions.Split(new char[1] { ';' }); string[] array2 = array; foreach (string text in array2) { string[] array3 = text.Split(new char[1] { ',' }); Logger.Log("Checking vendor item " + array3[0] + " against " + array3[1]); foreach (TradeItem item in __result) { Logger.Log("- Checking " + ((Object)((Component)item.m_prefab).gameObject).name + " from the vendor list..."); if (((Object)((Component)item.m_prefab).gameObject).name == array3[0]) { if (!KillMeForMyPowerUtils.bossIsKilled(array3[1])) { customItemsToRemove.Add(item); Logger.Log(array3[1] + " will be excluded."); } else { Logger.Log(array3[1] + " will NOT be excluded."); } break; } } } } } [HarmonyPatch(typeof(Vegvisir), "Interact")] public class HildirMapTablePatch { public static bool Prefix(Vegvisir __instance, Humanoid character, bool hold, bool alt, ref bool __result) { if (character is Player && __instance.m_locations.FindAll((VegvisrLocation loc) => (int)loc.m_pinType == 14 || (int)loc.m_pinType == 15 || (int)loc.m_pinType == 16).Count > 0 && !KillMeForMyPowerUtils.HasDefeatedBossName(ConfigurationFile.vendorHildirBossToKill.Value)) { ((Character)character).Message((MessageType)2, ConfigurationFile.restrictUsingKeyItemsMessage.Value.Replace("{0}", ConfigurationFile.vendorHildirBossToKill.Value.GetTranslationKey()), 0, (Sprite)null); Effects.scareEffect(); __result = false; return false; } return true; } } } } namespace KillMeForMyPower.Restrictions.BossNameManagement { public enum BossNameEnum { [BossNameAttr("Eikthyr", "GP_Eikthyr", "$enemy_eikthyr", "playerListForBoss1EikthyrPower")] Eikthyr, [BossNameAttr("gd_king", "GP_TheElder", "$enemy_gdking", "playerListForBoss2TheElderPower")] TheElder, [BossNameAttr("Bonemass", "GP_Bonemass", "$enemy_bonemass", "playerListForBoss3BonemassPower")] Bonemass, [BossNameAttr("Dragon", "GP_Moder", "$enemy_dragon", "playerListForBoss4ModerPower")] Moder, [BossNameAttr("GoblinKing", "GP_Yagluth", "$enemy_goblinking", "playerListForBoss5YagluthPower")] Yagluth, [BossNameAttr("SeekerQueen", "GP_Queen", "$enemy_seekerqueen", "playerListForBoss6QueenPower")] Queen, [BossNameAttr("Fader", "GP_Fader", "$enemy_fader", "playerListForBoss7FaderPower")] Fader, [BossNameAttr(null, null, null, null)] None, [BossNameAttr("BossGorr_TW", "SE_Boss_Gorr", "Gorr", "playerListForBoss8TherzieGorrPower")] SE_Boss_Gorr, [BossNameAttr("BossBrutalis_TW", "SE_Boss_Brutalis", "Brutalis", "playerListForBoss8TherzieBrutalisPower")] SE_Boss_Brutalis, [BossNameAttr("BossStormHerald_TW", "SE_Boss_StormHerald", "StormHerald", "playerListForBoss8TherzieStormHeraldPower")] SE_Boss_StormHerald, [BossNameAttr("BossSythrak_TW", "SE_Boss_Sythrak", "Sythrak", "playerListForBoss8TherzieSythrakPower")] SE_Boss_Sythrak } internal class BossNameAttr : Attribute { public string prefabBossName { get; private set; } public string translationKey { get; private set; } public string powerKey { get; private set; } public string configurationListName { get; private set; } internal BossNameAttr(string prefabBossName, string powerKey, string translationKey, string configurationListName) { this.prefabBossName = prefabBossName; this.powerKey = powerKey; this.translationKey = translationKey; this.configurationListName = configurationListName; } } public static class BossNameFields { public static string GetGrantedPlayerNamesList(this BossNameEnum p) { BossNameAttr attr = GetAttr(p); return ((ConfigEntry)(typeof(ConfigurationFile).GetField(attr.configurationListName, BindingFlags.Static | BindingFlags.Public)?.GetValue(null)))?.Value; } public static string GetFightBossname(this BossNameEnum p) { BossNameAttr attr = GetAttr(p); return attr.prefabBossName; } public static string GetTranslationKey(this BossNameEnum p) { BossNameAttr attr = GetAttr(p); return attr.translationKey; } public static string GetPowerKey(this BossNameEnum p) { BossNameAttr attr = GetAttr(p); return attr.powerKey; } public static string GetConfigurationListName(this BossNameEnum p) { BossNameAttr attr = GetAttr(p); return attr.configurationListName; } private static BossNameAttr GetAttr(BossNameEnum p) { return (BossNameAttr)Attribute.GetCustomAttribute(ForValue(p), typeof(BossNameAttr)); } private static MemberInfo ForValue(BossNameEnum p) { return typeof(BossNameEnum).GetField(Enum.GetName(typeof(BossNameEnum), p)); } } public static class BossNameUtils { public static void GrantBossPowerToPlayer(BossNameEnum bossNameEnum, string playerName, bool isAdd) { if (bossNameEnum != BossNameEnum.None) { UpdateConfigurationGrantedPlayersList(bossNameEnum, playerName, isAdd); Logger.LogInfo("Player " + playerName + " " + (isAdd ? "defeated" : "removed") + $" {bossNameEnum}."); } } public static bool IsBossPowerGrantedForPlayer(BossNameEnum bossNameEnum, Player player) { string grantedPlayerNamesList = bossNameEnum.GetGrantedPlayerNamesList(); return grantedPlayerNamesList.Contains(player.GetPlayerName()); } [MethodImpl(MethodImplOptions.Synchronized)] public static void UpdateConfigurationGrantedPlayersList(BossNameEnum bossNameEnum, string playerName, bool isAdd) { string grantedPlayerNamesList = bossNameEnum.GetGrantedPlayerNamesList(); if ((isAdd && grantedPlayerNamesList.Contains(playerName)) || (!isAdd && !grantedPlayerNamesList.Contains(playerName))) { return; } string text; if (!string.IsNullOrEmpty(grantedPlayerNamesList)) { string[] collection = grantedPlayerNamesList.Split(new char[1] { ',' }); List list = new List(collection); if (isAdd) { list.Add(playerName); } else { list.Remove(playerName); } list.Sort(); text = string.Join(",", list.ToArray()); } else { text = (isAdd ? playerName : ""); } Logger.Log("Result to save: " + text); switch (bossNameEnum) { case BossNameEnum.Eikthyr: ConfigurationFile.playerListForBoss1EikthyrPower.Value = text; break; case BossNameEnum.TheElder: ConfigurationFile.playerListForBoss2TheElderPower.Value = text; break; case BossNameEnum.Bonemass: ConfigurationFile.playerListForBoss3BonemassPower.Value = text; break; case BossNameEnum.Moder: ConfigurationFile.playerListForBoss4ModerPower.Value = text; break; case BossNameEnum.Yagluth: ConfigurationFile.playerListForBoss5YagluthPower.Value = text; break; case BossNameEnum.Queen: ConfigurationFile.playerListForBoss6QueenPower.Value = text; break; case BossNameEnum.Fader: ConfigurationFile.playerListForBoss7FaderPower.Value = text; break; case BossNameEnum.SE_Boss_Gorr: ConfigurationFile.playerListForBoss8TherzieGorrPower.Value = text; break; case BossNameEnum.SE_Boss_Brutalis: ConfigurationFile.playerListForBoss8TherzieBrutalisPower.Value = text; break; case BossNameEnum.SE_Boss_StormHerald: ConfigurationFile.playerListForBoss8TherzieStormHeraldPower.Value = text; break; case BossNameEnum.SE_Boss_Sythrak: ConfigurationFile.playerListForBoss8TherzieSythrakPower.Value = text; break; default: Logger.LogWarning("Not saved, boss not found!"); break; } } } [HarmonyPatch(typeof(Game), "Start")] public class GameStartPatch { private static void Prefix() { ZRoutedRpc.instance.Register("RPC_BossPowerGrantServer", (Action)RPC_BossPowerGrantCalls.RPC_BossPowerGrantServer); ZRoutedRpc.instance.Register("RPC_BossPowerRemoveGrantServer", (Action)RPC_BossPowerGrantCalls.RPC_BossPowerRemoveGrantServer); } } } namespace KillMeForMyPower.Managers { public static class EpicMMOSystem_API { private enum API_State { NotReady, NotInstalled, Ready } public enum Attribut { Strength, Agility, Intellect, Body, Vigour, Special } private static API_State state; private static MethodInfo eGetLevel; private static MethodInfo eAddExp; private static MethodInfo eGetAttribute; private static MethodInfo eGetAttributeRusty; private static MethodInfo eSetSingleRate; public static bool IsLoaded() { Init(); return state == API_State.Ready; } public static int GetLevel() { int result = 0; Init(); if (eGetLevel != null) { result = (int)eGetLevel.Invoke(null, null); } return result; } public static int GetAttribute(Attribut attribute) { int result = 0; Init(); if (eGetAttribute != null) { result = (int)eGetAttribute.Invoke(null, new object[1] { attribute }); } return result; } public static int GetAttributeRusty(string attribute) { int result = 0; Init(); if (eGetAttributeRusty != null) { result = (int)eGetAttributeRusty.Invoke(null, new object[1] { attribute }); } return result; } public static void AddExp(int value) { Init(); eAddExp?.Invoke(null, new object[1] { value }); } public static void SetSingleRate(float rate) { Init(); eSetSingleRate?.Invoke(null, new object[1] { rate }); } private static void Init() { API_State aPI_State = state; if ((uint)(aPI_State - 1) > 1u) { if (Type.GetType("EpicMMOSystem.EpicMMOSystem, EpicMMOSystem") == null) { state = API_State.NotInstalled; return; } state = API_State.Ready; Type type = Type.GetType("API.EMMOS_API, EpicMMOSystem"); eGetLevel = type?.GetMethod("GetLevel", BindingFlags.Static | BindingFlags.Public); eAddExp = type?.GetMethod("AddExp", BindingFlags.Static | BindingFlags.Public); eGetAttribute = type?.GetMethod("GetAttribute", BindingFlags.Static | BindingFlags.Public); eGetAttributeRusty = type?.GetMethod("GetAttribute", BindingFlags.Static | BindingFlags.Public); eSetSingleRate = type?.GetMethod("SetSingleRate", BindingFlags.Static | BindingFlags.Public); } } } public class GameManager { public static object GetPrivateValue(object obj, string name, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic) { return obj.GetType().GetField(name, bindingAttr)?.GetValue(obj); } public static object GetPrivateMethod(object obj, string name, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic) { return obj.GetType().GetMethod(name, bindingAttr)?.Invoke(obj, null); } public static bool isDetailedLevelsInstalled() { return Chainloader.PluginInfos.ContainsKey("Turbero.DetailedLevels"); } public static bool IsAdmin(Player player) { //IL_0080: 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) Logger.Log("[IsAdmin] Checking powerCommandsAdminPlayersList..."); string playerName = player.GetPlayerName(); if (ConfigurationFile.powerCommandsAdminPlayersList.Value.Contains(playerName)) { return true; } Logger.Log("[IsAdmin] Finding All PlayerInfo..."); List list = ZNet.instance.GetPlayerList().FindAll((PlayerInfo p) => p.m_name == playerName); if (list.Count == 0) { return false; } string userID = list[0].m_userInfo.m_id.m_userID; Logger.Log("[IsAdmin] Matching steamID " + userID + " in adminList..."); return (Object)(object)ZNet.instance != (Object)null && ZNet.instance.GetAdminList() != null && ZNet.instance.GetAdminList().Contains(userID); } } } namespace KillMeForMyPower.Commands { public class PowerManagementCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; public static ConsoleEvent <>9__0_1; internal void b__0_0(ConsoleEventArgs args) { if (!GameManager.IsAdmin(Player.m_localPlayer)) { args.Context.AddString("You are not an admin."); return; } if (args.Args.Length < 3) { args.Context.AddString("Usage: add_power "); return; } ZRoutedRpc.instance.InvokeRoutedRPC(0L, "RPC_BossPowerGrantServer", new object[2] { args.Args[2], args.Args[1] }); } internal void b__0_1(ConsoleEventArgs args) { if (!GameManager.IsAdmin(Player.m_localPlayer)) { args.Context.AddString("You are not an admin."); return; } if (args.Args.Length < 3) { args.Context.AddString("Usage: add_power "); return; } ZRoutedRpc.instance.InvokeRoutedRPC(0L, "RPC_BossPowerRemoveGrantServer", new object[2] { args.Args[2], args.Args[1] }); } } public static void RegisterConsoleCommand() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { if (!GameManager.IsAdmin(Player.m_localPlayer)) { args.Context.AddString("You are not an admin."); } else if (args.Args.Length < 3) { args.Context.AddString("Usage: add_power "); } else { ZRoutedRpc.instance.InvokeRoutedRPC(0L, "RPC_BossPowerGrantServer", new object[2] { args.Args[2], args.Args[1] }); } }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("add_power", "[player] [boss_name]", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>c.<>9__0_1; if (obj2 == null) { ConsoleEvent val2 = delegate(ConsoleEventArgs args) { if (!GameManager.IsAdmin(Player.m_localPlayer)) { args.Context.AddString("You are not an admin."); } else if (args.Args.Length < 3) { args.Context.AddString("Usage: add_power "); } else { ZRoutedRpc.instance.InvokeRoutedRPC(0L, "RPC_BossPowerRemoveGrantServer", new object[2] { args.Args[2], args.Args[1] }); } }; <>c.<>9__0_1 = val2; obj2 = (object)val2; } new ConsoleCommand("remove_power", "[boss_order] [player]", (ConsoleEvent)obj2, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } public class CheckBossesCommand { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; internal void b__0_0(ConsoleEventArgs args) { bool flag = false; string playerName = Player.m_localPlayer.GetPlayerName(); args.Context.AddString("=== Bosses killed and allowed to use their powers ==="); foreach (BossNameEnum value in Enum.GetValues(typeof(BossNameEnum))) { if (!string.IsNullOrEmpty(value.GetConfigurationListName())) { string grantedPlayerNamesList = value.GetGrantedPlayerNamesList(); if ((!string.IsNullOrEmpty(grantedPlayerNamesList) && grantedPlayerNamesList.Contains(playerName)) || (ConfigurationFile.activateMidPlayDetection.Value && ((Humanoid)Player.m_localPlayer).HaveUniqueKey(value.GetPowerKey()))) { args.Context.AddString("- " + Localization.instance.Localize(value.GetTranslationKey())); flag = true; } } } if (!flag) { args.Context.AddString("NONE"); } } } public static void RegisterConsoleCommand() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { bool flag = false; string playerName = Player.m_localPlayer.GetPlayerName(); args.Context.AddString("=== Bosses killed and allowed to use their powers ==="); foreach (BossNameEnum value in Enum.GetValues(typeof(BossNameEnum))) { if (!string.IsNullOrEmpty(value.GetConfigurationListName())) { string grantedPlayerNamesList = value.GetGrantedPlayerNamesList(); if ((!string.IsNullOrEmpty(grantedPlayerNamesList) && grantedPlayerNamesList.Contains(playerName)) || (ConfigurationFile.activateMidPlayDetection.Value && ((Humanoid)Player.m_localPlayer).HaveUniqueKey(value.GetPowerKey()))) { args.Context.AddString("- " + Localization.instance.Localize(value.GetTranslationKey())); flag = true; } } } if (!flag) { args.Context.AddString("NONE"); } }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("bosses_permissions", "", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 ServerSync { [PublicAPI] internal abstract class OwnConfigEntryBase { public object? LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [PublicAPI] internal class SyncedConfigEntry : OwnConfigEntryBase { public readonly ConfigEntry SourceConfig; public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig; public T Value { get { return SourceConfig.Value; } set { SourceConfig.Value = value; } } public SyncedConfigEntry(ConfigEntry sourceConfig) { SourceConfig = sourceConfig; } public void AssignLocalValue(T value) { if (LocalBaseValue == null) { Value = value; } else { LocalBaseValue = value; } } } internal abstract class CustomSyncedValueBase { public object? LocalBaseValue; public readonly string Identifier; public readonly Type Type; private object? boxedValue; protected bool localIsOwner; public readonly int Priority; public object? BoxedValue { get { return boxedValue; } set { boxedValue = value; this.ValueChanged?.Invoke(); } } public event Action? ValueChanged; protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority) { Priority = priority; Identifier = identifier; Type = type; configSync.AddCustomValue(this); localIsOwner = configSync.IsSourceOfTruth; configSync.SourceOfTruthChanged += delegate(bool truth) { localIsOwner = truth; }; } } [PublicAPI] internal sealed class CustomSyncedValue : CustomSyncedValueBase { public T Value { get { return (T)base.BoxedValue; } set { base.BoxedValue = value; } } public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0) : base(configSync, identifier, typeof(T), priority) { Value = value; } public void AssignLocalValue(T value) { if (localIsOwner) { Value = value; } else { LocalBaseValue = value; } } } internal class ConfigurationManagerAttributes { [UsedImplicitly] public bool? ReadOnly = false; } [PublicAPI] internal class ConfigSync { [HarmonyPatch(typeof(ZRpc), "HandlePackage")] private static class SnatchCurrentlyHandlingRPC { public static ZRpc? currentRpc; [HarmonyPrefix] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class RegisterRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance) { isServer = __instance.IsServer(); foreach (ConfigSync configSync2 in configSyncs) { ZRoutedRpc.instance.Register(configSync2.Name + " ConfigSync", (Action)configSync2.RPC_FromOtherClientConfigSync); if (isServer) { configSync2.InitialSyncDone = true; Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections")); } } if (isServer) { ((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges()); } static void SendAdmin(List peers, bool isAdmin) { ZPackage package = ConfigsToPackage(null, null, new PackageEntry[1] { new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = isAdmin } }); ConfigSync configSync = configSyncs.First(); if (configSync != null) { ((MonoBehaviour)ZNet.instance).StartCoroutine(configSync.sendZPackage(peers, package)); } } static IEnumerator WatchAdminListChanges() { MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); List CurrentList = new List(adminList.GetList()); while (true) { yield return (object)new WaitForSeconds(30f); if (!adminList.GetList().SequenceEqual(CurrentList)) { CurrentList = new List(adminList.GetList()); List adminPeer = ZNet.instance.GetPeers().Where(delegate(ZNetPeer p) { string hostName = p.m_rpc.GetSocket().GetHostName(); return ((object)listContainsId == null) ? adminList.Contains(hostName) : ((bool)listContainsId.Invoke(ZNet.instance, new object[2] { adminList, hostName })); }).ToList(); List nonAdminPeer = ZNet.instance.GetPeers().Except(adminPeer).ToList(); SendAdmin(nonAdminPeer, isAdmin: false); SendAdmin(adminPeer, isAdmin: true); } } } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterClientRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { return; } foreach (ConfigSync configSync in configSyncs) { peer.m_rpc.Register(configSync.Name + " ConfigSync", (Action)configSync.RPC_FromServerConfigSync); } } } private class ParsedConfigs { public readonly Dictionary configValues = new Dictionary(); public readonly Dictionary customValues = new Dictionary(); } [HarmonyPatch(typeof(ZNet), "Shutdown")] private class ResetConfigsOnShutdown { [HarmonyPostfix] private static void Postfix() { ProcessingServerUpdate = true; foreach (ConfigSync configSync in configSyncs) { configSync.resetConfigsFromServer(); configSync.IsSourceOfTruth = true; configSync.InitialSyncDone = false; } ProcessingServerUpdate = false; } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private class SendConfigsAfterLogin { private class BufferingSocket : ISocket { public volatile bool finished = false; public volatile int versionMatchQueued = -1; public readonly List Package = new List(); public readonly ISocket Original; public BufferingSocket(ISocket original) { Original = original; } public bool IsConnected() { return Original.IsConnected(); } public ZPackage Recv() { return Original.Recv(); } public int GetSendQueueSize() { return Original.GetSendQueueSize(); } public int GetCurrentSendRate() { return Original.GetCurrentSendRate(); } public bool IsHost() { return Original.IsHost(); } public void Dispose() { Original.Dispose(); } public bool GotNewData() { return Original.GotNewData(); } public void Close() { Original.Close(); } public string GetEndPointString() { return Original.GetEndPointString(); } public void GetAndResetStats(out int totalSent, out int totalRecv) { Original.GetAndResetStats(ref totalSent, ref totalRecv); } public void GetConnectionQuality(out float localQuality, out float remoteQuality, out int ping, out float outByteSec, out float inByteSec) { Original.GetConnectionQuality(ref localQuality, ref remoteQuality, ref ping, ref outByteSec, ref inByteSec); } public ISocket Accept() { return Original.Accept(); } public int GetHostPort() { return Original.GetHostPort(); } public bool Flush() { return Original.Flush(); } public string GetHostName() { return Original.GetHostName(); } public void VersionMatch() { if (finished) { Original.VersionMatch(); } else { versionMatchQueued = Package.Count; } } public void Send(ZPackage pkg) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown int pos = pkg.GetPos(); pkg.SetPos(0); int num = pkg.ReadInt(); if ((num == StringExtensionMethods.GetStableHashCode("PeerInfo") || num == StringExtensionMethods.GetStableHashCode("RoutedRPC") || num == StringExtensionMethods.GetStableHashCode("ZDOData")) && !finished) { ZPackage val = new ZPackage(pkg.GetArray()); val.SetPos(pos); Package.Add(val); } else { pkg.SetPos(pos); Original.Send(pkg); } } } [HarmonyPriority(800)] [HarmonyPrefix] private static void Prefix(ref Dictionary? __state, ZNet __instance, ZRpc rpc) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Invalid comparison between Unknown and I4 if (__instance.IsServer()) { BufferingSocket value = new BufferingSocket(rpc.GetSocket()); AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, value); object? obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (val != null && (int)ZNet.m_onlineBackend > 0) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, value); } if (__state == null) { __state = new Dictionary(); } __state[Assembly.GetExecutingAssembly()] = value; } } [HarmonyPostfix] private static void Postfix(Dictionary __state, ZNet __instance, ZRpc rpc) { ZRpc rpc2 = rpc; ZNet __instance2 = __instance; Dictionary __state2 = __state; ZNetPeer peer; if (__instance2.IsServer()) { object obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance2, new object[1] { rpc2 }); peer = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (peer == null) { SendBufferedData(); } else { ((MonoBehaviour)__instance2).StartCoroutine(sendAsync()); } } void SendBufferedData() { if (rpc2.GetSocket() is BufferingSocket bufferingSocket) { AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc2, bufferingSocket.Original); object? obj2 = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance2, new object[1] { rpc2 }); ZNetPeer val = (ZNetPeer)((obj2 is ZNetPeer) ? obj2 : null); if (val != null) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, bufferingSocket.Original); } } BufferingSocket bufferingSocket2 = __state2[Assembly.GetExecutingAssembly()]; bufferingSocket2.finished = true; for (int i = 0; i < bufferingSocket2.Package.Count; i++) { if (i == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } bufferingSocket2.Original.Send(bufferingSocket2.Package[i]); } if (bufferingSocket2.Package.Count == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } } IEnumerator sendAsync() { foreach (ConfigSync configSync in configSyncs) { List entries = new List(); if (configSync.CurrentVersion != null) { entries.Add(new PackageEntry { section = "Internal", key = "serverversion", type = typeof(string), value = configSync.CurrentVersion }); } MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); entries.Add(new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = (((object)listContainsId == null) ? ((object)adminList.Contains(rpc2.GetSocket().GetHostName())) : listContainsId.Invoke(ZNet.instance, new object[2] { adminList, rpc2.GetSocket().GetHostName() })) }); ZPackage package = ConfigsToPackage(configSync.allConfigs.Select((OwnConfigEntryBase c) => c.BaseConfig), configSync.allCustomValues, entries, partial: false); yield return ((MonoBehaviour)__instance2).StartCoroutine(configSync.sendZPackage(new List { peer }, package)); } SendBufferedData(); } } } private class PackageEntry { public string section = null; public string key = null; public Type type = null; public object? value; } [HarmonyPatch(typeof(ConfigEntryBase), "GetSerializedValue")] private static class PreventSavingServerInfo { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, ref string __result) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || isWritableConfig(ownConfigEntryBase)) { return true; } __result = TomlTypeConverter.ConvertToString(ownConfigEntryBase.LocalBaseValue, __instance.SettingType); return false; } } [HarmonyPatch(typeof(ConfigEntryBase), "SetSerializedValue")] private static class PreventConfigRereadChangingValues { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, string value) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || ownConfigEntryBase.LocalBaseValue == null) { return true; } try { ownConfigEntryBase.LocalBaseValue = TomlTypeConverter.ConvertToValue(value, __instance.SettingType); } catch (Exception ex) { Debug.LogWarning((object)$"Config value of setting \"{__instance.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}"); } return false; } } private class InvalidDeserializationTypeException : Exception { public string expected = null; public string received = null; public string field = ""; } public static bool ProcessingServerUpdate; public readonly string Name; public string? DisplayName; public string? CurrentVersion; public string? MinimumRequiredVersion; public bool ModRequired = false; private bool? forceConfigLocking; private bool isSourceOfTruth = true; private static readonly HashSet configSyncs; private readonly HashSet allConfigs = new HashSet(); private HashSet allCustomValues = new HashSet(); private static bool isServer; private static bool lockExempt; private OwnConfigEntryBase? lockedConfig = null; private const byte PARTIAL_CONFIGS = 1; private const byte FRAGMENTED_CONFIG = 2; private const byte COMPRESSED_CONFIG = 4; private readonly Dictionary> configValueCache = new Dictionary>(); private readonly List> cacheExpirations = new List>(); private static long packageCounter; public bool IsLocked { get { bool? flag = forceConfigLocking; bool num; if (!flag.HasValue) { if (lockedConfig == null) { goto IL_0052; } num = ((IConvertible)lockedConfig.BaseConfig.BoxedValue).ToInt32(CultureInfo.InvariantCulture) != 0; } else { num = flag.GetValueOrDefault(); } if (!num) { goto IL_0052; } int result = ((!lockExempt) ? 1 : 0); goto IL_0053; IL_0053: return (byte)result != 0; IL_0052: result = 0; goto IL_0053; } set { forceConfigLocking = value; } } public bool IsAdmin => lockExempt || isSourceOfTruth; public bool IsSourceOfTruth { get { return isSourceOfTruth; } private set { if (value != isSourceOfTruth) { isSourceOfTruth = value; this.SourceOfTruthChanged?.Invoke(value); } } } public bool InitialSyncDone { get; private set; } = false; public event Action? SourceOfTruthChanged; private event Action? lockedConfigChanged; static ConfigSync() { ProcessingServerUpdate = false; configSyncs = new HashSet(); lockExempt = false; packageCounter = 0L; RuntimeHelpers.RunClassConstructor(typeof(VersionCheck).TypeHandle); } public ConfigSync(string name) { Name = name; configSyncs.Add(this); new VersionCheck(this); } public SyncedConfigEntry AddConfigEntry(ConfigEntry configEntry) { ConfigEntry configEntry2 = configEntry; OwnConfigEntryBase ownConfigEntryBase = configData((ConfigEntryBase)(object)configEntry2); SyncedConfigEntry syncedEntry = ownConfigEntryBase as SyncedConfigEntry; if (syncedEntry == null) { syncedEntry = new SyncedConfigEntry(configEntry2); AccessTools.DeclaredField(typeof(ConfigDescription), "k__BackingField").SetValue(((ConfigEntryBase)configEntry2).Description, new object[1] { new ConfigurationManagerAttributes() }.Concat(((ConfigEntryBase)configEntry2).Description.Tags ?? Array.Empty()).Concat(new SyncedConfigEntry[1] { syncedEntry }).ToArray()); configEntry2.SettingChanged += delegate { if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig) { Broadcast(ZRoutedRpc.Everybody, (ConfigEntryBase)configEntry2); } }; allConfigs.Add(syncedEntry); } return syncedEntry; } public SyncedConfigEntry AddLockingConfigEntry(ConfigEntry lockingConfig) where T : IConvertible { if (lockedConfig != null) { throw new Exception("Cannot initialize locking ConfigEntry twice"); } lockedConfig = AddConfigEntry(lockingConfig); lockingConfig.SettingChanged += delegate { this.lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { CustomSyncedValueBase customValue2 = customValue; if (allCustomValues.Select((CustomSyncedValueBase v) => v.Identifier).Concat(new string[1] { "serverversion" }).Contains(customValue2.Identifier)) { throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion)"); } allCustomValues.Add(customValue2); allCustomValues = new HashSet(allCustomValues.OrderByDescending((CustomSyncedValueBase v) => v.Priority)); customValue2.ValueChanged += delegate { if (!ProcessingServerUpdate) { Broadcast(ZRoutedRpc.Everybody, customValue2); } }; } private void RPC_FromServerConfigSync(ZRpc rpc, ZPackage package) { lockedConfigChanged += serverLockedSettingChanged; IsSourceOfTruth = false; if (HandleConfigSyncRPC(0L, package, clientUpdate: false)) { InitialSyncDone = true; } } private void RPC_FromOtherClientConfigSync(long sender, ZPackage package) { HandleConfigSyncRPC(sender, package, clientUpdate: true); } private bool HandleConfigSyncRPC(long sender, ZPackage package, bool clientUpdate) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Expected O, but got Unknown try { if (isServer && IsLocked) { ZRpc? currentRpc = SnatchCurrentlyHandlingRPC.currentRpc; object obj; if (currentRpc == null) { obj = null; } else { ISocket socket = currentRpc.GetSocket(); obj = ((socket != null) ? socket.GetHostName() : null); } string text = (string)obj; if (text != null) { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList val = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); if (!(((object)methodInfo == null) ? val.Contains(text) : ((bool)methodInfo.Invoke(ZNet.instance, new object[2] { val, text })))) { return false; } } } cacheExpirations.RemoveAll(delegate(KeyValuePair kv) { if (kv.Key < DateTimeOffset.Now.Ticks) { configValueCache.Remove(kv.Value); return true; } return false; }); byte b = package.ReadByte(); if ((b & 2u) != 0) { long num = package.ReadLong(); string text2 = sender.ToString() + num; if (!configValueCache.TryGetValue(text2, out SortedDictionary value)) { value = new SortedDictionary(); configValueCache[text2] = value; cacheExpirations.Add(new KeyValuePair(DateTimeOffset.Now.AddSeconds(60.0).Ticks, text2)); } int key = package.ReadInt(); int num2 = package.ReadInt(); value.Add(key, package.ReadByteArray()); if (value.Count < num2) { return false; } configValueCache.Remove(text2); package = new ZPackage(value.Values.SelectMany((byte[] a) => a).ToArray()); b = package.ReadByte(); } ProcessingServerUpdate = true; if ((b & 4u) != 0) { byte[] buffer = package.ReadByteArray(); MemoryStream stream = new MemoryStream(buffer); MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress)) { deflateStream.CopyTo(memoryStream); } package = new ZPackage(memoryStream.ToArray()); b = package.ReadByte(); } if ((b & 1) == 0) { resetConfigsFromServer(); } ParsedConfigs parsedConfigs = ReadConfigsFromPackage(package); ConfigFile val2 = null; bool saveOnConfigSet = false; foreach (KeyValuePair configValue in parsedConfigs.configValues) { if (!isServer && configValue.Key.LocalBaseValue == null) { configValue.Key.LocalBaseValue = configValue.Key.BaseConfig.BoxedValue; } if (val2 == null) { val2 = configValue.Key.BaseConfig.ConfigFile; saveOnConfigSet = val2.SaveOnConfigSet; val2.SaveOnConfigSet = false; } configValue.Key.BaseConfig.BoxedValue = configValue.Value; } if (val2 != null) { val2.SaveOnConfigSet = saveOnConfigSet; } foreach (KeyValuePair customValue in parsedConfigs.customValues) { if (!isServer) { CustomSyncedValueBase key2 = customValue.Key; if (key2.LocalBaseValue == null) { key2.LocalBaseValue = customValue.Key.BoxedValue; } } customValue.Key.BoxedValue = customValue.Value; } Debug.Log((object)string.Format("Received {0} configs and {1} custom values from {2} for mod {3}", parsedConfigs.configValues.Count, parsedConfigs.customValues.Count, (isServer || clientUpdate) ? $"client {sender}" : "the server", DisplayName ?? Name)); if (!isServer) { serverLockedSettingChanged(); } return true; } finally { ProcessingServerUpdate = false; } } private ParsedConfigs ReadConfigsFromPackage(ZPackage package) { ParsedConfigs parsedConfigs = new ParsedConfigs(); Dictionary dictionary = allConfigs.Where((OwnConfigEntryBase c) => c.SynchronizedConfig).ToDictionary((OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "_" + c.BaseConfig.Definition.Key, (OwnConfigEntryBase c) => c); Dictionary dictionary2 = allCustomValues.ToDictionary((CustomSyncedValueBase c) => c.Identifier, (CustomSyncedValueBase c) => c); int num = package.ReadInt(); for (int i = 0; i < num; i++) { string text = package.ReadString(); string text2 = package.ReadString(); string text3 = package.ReadString(); Type type = Type.GetType(text3); if (text3 == "" || type != null) { object obj; try { obj = ((text3 == "") ? null : ReadValueWithTypeFromZPackage(package, type)); } catch (InvalidDeserializationTypeException ex) { Debug.LogWarning((object)("Got unexpected struct internal type " + ex.received + " for field " + ex.field + " struct " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + ex.expected)); continue; } OwnConfigEntryBase value2; if (text == "Internal") { CustomSyncedValueBase value; if (text2 == "serverversion") { if (obj?.ToString() != CurrentVersion) { Debug.LogWarning((object)("Received server version is not equal: server version = " + (obj?.ToString() ?? "null") + "; local version = " + (CurrentVersion ?? "unknown"))); } } else if (text2 == "lockexempt") { if (obj is bool flag) { lockExempt = flag; } } else if (dictionary2.TryGetValue(text2, out value)) { if ((text3 == "" && (!value.Type.IsValueType || Nullable.GetUnderlyingType(value.Type) != null)) || GetZPackageTypeString(value.Type) == text3) { parsedConfigs.customValues[value] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for internal value " + text2 + " for mod " + (DisplayName ?? Name) + ", expecting " + value.Type.AssemblyQualifiedName)); } } else if (dictionary.TryGetValue(text + "_" + text2, out value2)) { Type type2 = configType(value2.BaseConfig); if ((text3 == "" && (!type2.IsValueType || Nullable.GetUnderlyingType(type2) != null)) || GetZPackageTypeString(type2) == text3) { parsedConfigs.configValues[value2] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + type2.AssemblyQualifiedName)); } else { Debug.LogWarning((object)("Received unknown config entry " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ". This may happen if client and server versions of the mod do not match.")); } continue; } Debug.LogWarning((object)("Got invalid type " + text3 + ", abort reading of received configs")); return new ParsedConfigs(); } return parsedConfigs; } private static bool isWritableConfig(OwnConfigEntryBase config) { OwnConfigEntryBase config2 = config; ConfigSync configSync = configSyncs.FirstOrDefault((ConfigSync cs) => cs.allConfigs.Contains(config2)); if (configSync == null) { return true; } return configSync.IsSourceOfTruth || !config2.SynchronizedConfig || config2.LocalBaseValue == null || (!configSync.IsLocked && (config2 != configSync.lockedConfig || lockExempt)); } private void serverLockedSettingChanged() { foreach (OwnConfigEntryBase allConfig in allConfigs) { configAttribute(allConfig.BaseConfig).ReadOnly = !isWritableConfig(allConfig); } } private void resetConfigsFromServer() { ConfigFile val = null; bool saveOnConfigSet = false; foreach (OwnConfigEntryBase item in allConfigs.Where((OwnConfigEntryBase config) => config.LocalBaseValue != null)) { if (val == null) { val = item.BaseConfig.ConfigFile; saveOnConfigSet = val.SaveOnConfigSet; val.SaveOnConfigSet = false; } item.BaseConfig.BoxedValue = item.LocalBaseValue; item.LocalBaseValue = null; } if (val != null) { val.SaveOnConfigSet = saveOnConfigSet; } foreach (CustomSyncedValueBase item2 in allCustomValues.Where((CustomSyncedValueBase config) => config.LocalBaseValue != null)) { item2.BoxedValue = item2.LocalBaseValue; item2.LocalBaseValue = null; } lockedConfigChanged -= serverLockedSettingChanged; serverLockedSettingChanged(); } private IEnumerator distributeConfigToPeers(ZNetPeer peer, ZPackage package) { ZNetPeer peer2 = peer; ZRoutedRpc rpc = ZRoutedRpc.instance; if (rpc == null) { yield break; } byte[] data = package.GetArray(); if (data != null && data.LongLength > 250000) { int fragments = (int)(1 + (data.LongLength - 1) / 250000); long packageIdentifier = ++packageCounter; int fragment = 0; while (fragment < fragments) { foreach (bool item in waitForQueue()) { yield return item; } if (peer2.m_socket.IsConnected()) { ZPackage fragmentedPackage = new ZPackage(); fragmentedPackage.Write((byte)2); fragmentedPackage.Write(packageIdentifier); fragmentedPackage.Write(fragment); fragmentedPackage.Write(fragments); fragmentedPackage.Write(data.Skip(250000 * fragment).Take(250000).ToArray()); SendPackage(fragmentedPackage); if (fragment != fragments - 1) { yield return true; } int num = fragment + 1; fragment = num; continue; } break; } yield break; } foreach (bool item2 in waitForQueue()) { yield return item2; } SendPackage(package); void SendPackage(ZPackage pkg) { string text = Name + " ConfigSync"; if (isServer) { peer2.m_rpc.Invoke(text, new object[1] { pkg }); } else { rpc.InvokeRoutedRPC(peer2.m_server ? 0 : peer2.m_uid, text, new object[1] { pkg }); } } IEnumerable waitForQueue() { float timeout = Time.time + 30f; while (peer2.m_socket.GetSendQueueSize() > 20000) { if (Time.time > timeout) { Debug.Log((object)$"Disconnecting {peer2.m_uid} after 30 seconds config sending timeout"); peer2.m_rpc.Invoke("Error", new object[1] { (object)(ConnectionStatus)5 }); ZNet.instance.Disconnect(peer2); break; } yield return false; } } } private IEnumerator sendZPackage(long target, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return Enumerable.Empty().GetEnumerator(); } List list = (List)AccessTools.DeclaredField(typeof(ZRoutedRpc), "m_peers").GetValue(ZRoutedRpc.instance); if (target != ZRoutedRpc.Everybody) { list = list.Where((ZNetPeer p) => p.m_uid == target).ToList(); } return sendZPackage(list, package); } private IEnumerator sendZPackage(List peers, ZPackage package) { ZPackage package2 = package; if (!Object.op_Implicit((Object)(object)ZNet.instance)) { yield break; } byte[] rawData = package2.GetArray(); if (rawData != null && rawData.LongLength > 10000) { ZPackage compressedPackage = new ZPackage(); compressedPackage.Write((byte)4); MemoryStream output = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(output, CompressionLevel.Optimal)) { deflateStream.Write(rawData, 0, rawData.Length); } compressedPackage.Write(output.ToArray()); package2 = compressedPackage; } List> writers = (from peer in peers where peer.IsReady() select peer into p select distributeConfigToPeers(p, package2)).ToList(); writers.RemoveAll((IEnumerator writer) => !writer.MoveNext()); while (writers.Count > 0) { yield return null; writers.RemoveAll((IEnumerator writer) => !writer.MoveNext()); } } private void Broadcast(long target, params ConfigEntryBase[] configs) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(configs); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private void Broadcast(long target, params CustomSyncedValueBase[] customValues) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(null, customValues); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private static OwnConfigEntryBase? configData(ConfigEntryBase config) { return config.Description.Tags?.OfType().SingleOrDefault(); } public static SyncedConfigEntry? ConfigData(ConfigEntry config) { return ((ConfigEntryBase)config).Description.Tags?.OfType>().SingleOrDefault(); } private static T configAttribute(ConfigEntryBase config) { return config.Description.Tags.OfType().First(); } private static Type configType(ConfigEntryBase config) { return configType(config.SettingType); } private static Type configType(Type type) { return type.IsEnum ? Enum.GetUnderlyingType(type) : type; } private static ZPackage ConfigsToPackage(IEnumerable? configs = null, IEnumerable? customValues = null, IEnumerable? packageEntries = null, bool partial = true) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown List list = configs?.Where((ConfigEntryBase config) => configData(config).SynchronizedConfig).ToList() ?? new List(); List list2 = customValues?.ToList() ?? new List(); ZPackage val = new ZPackage(); val.Write((byte)(partial ? 1 : 0)); val.Write(list.Count + list2.Count + (packageEntries?.Count() ?? 0)); foreach (PackageEntry item in packageEntries ?? Array.Empty()) { AddEntryToPackage(val, item); } foreach (CustomSyncedValueBase item2 in list2) { AddEntryToPackage(val, new PackageEntry { section = "Internal", key = item2.Identifier, type = item2.Type, value = item2.BoxedValue }); } foreach (ConfigEntryBase item3 in list) { AddEntryToPackage(val, new PackageEntry { section = item3.Definition.Section, key = item3.Definition.Key, type = configType(item3), value = item3.BoxedValue }); } return val; } private static void AddEntryToPackage(ZPackage package, PackageEntry entry) { package.Write(entry.section); package.Write(entry.key); package.Write((entry.value == null) ? "" : GetZPackageTypeString(entry.type)); AddValueToZPackage(package, entry.value); } private static string GetZPackageTypeString(Type type) { return type.AssemblyQualifiedName; } private static void AddValueToZPackage(ZPackage package, object? value) { Type type = value?.GetType(); if (value is Enum) { value = ((IConvertible)value).ToType(Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture); } else { if (value is ICollection collection) { package.Write(collection.Count); { foreach (object item in collection) { AddValueToZPackage(package, item); } return; } } if ((object)type != null && type.IsValueType && !type.IsPrimitive) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); package.Write(fields.Length); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { package.Write(GetZPackageTypeString(fieldInfo.FieldType)); AddValueToZPackage(package, fieldInfo.GetValue(value)); } return; } } ZRpc.Serialize(new object[1] { value }, ref package); } private static object ReadValueWithTypeFromZPackage(ZPackage package, Type type) { if ((object)type != null && type.IsValueType && !type.IsPrimitive && !type.IsEnum) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); int num = package.ReadInt(); if (num != fields.Length) { throw new InvalidDeserializationTypeException { received = $"(field count: {num})", expected = $"(field count: {fields.Length})" }; } object uninitializedObject = FormatterServices.GetUninitializedObject(type); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { string text = package.ReadString(); if (text != GetZPackageTypeString(fieldInfo.FieldType)) { throw new InvalidDeserializationTypeException { received = text, expected = GetZPackageTypeString(fieldInfo.FieldType), field = fieldInfo.Name }; } fieldInfo.SetValue(uninitializedObject, ReadValueWithTypeFromZPackage(package, fieldInfo.FieldType)); } return uninitializedObject; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { int num2 = package.ReadInt(); IDictionary dictionary = (IDictionary)Activator.CreateInstance(type); Type type2 = typeof(KeyValuePair<, >).MakeGenericType(type.GenericTypeArguments); FieldInfo field = type2.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = type2.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic); for (int j = 0; j < num2; j++) { object obj = ReadValueWithTypeFromZPackage(package, type2); dictionary.Add(field.GetValue(obj), field2.GetValue(obj)); } return dictionary; } if (type != typeof(List) && type.IsGenericType) { Type type3 = typeof(ICollection<>).MakeGenericType(type.GenericTypeArguments[0]); if ((object)type3 != null && type3.IsAssignableFrom(type)) { int num3 = package.ReadInt(); object obj2 = Activator.CreateInstance(type); MethodInfo method = type3.GetMethod("Add"); for (int k = 0; k < num3; k++) { method.Invoke(obj2, new object[1] { ReadValueWithTypeFromZPackage(package, type.GenericTypeArguments[0]) }); } return obj2; } } ParameterInfo parameterInfo = (ParameterInfo)FormatterServices.GetUninitializedObject(typeof(ParameterInfo)); AccessTools.DeclaredField(typeof(ParameterInfo), "ClassImpl").SetValue(parameterInfo, type); List source = new List(); ZRpc.Deserialize(new ParameterInfo[2] { null, parameterInfo }, package, ref source); return source.First(); } } [PublicAPI] [HarmonyPatch] internal class VersionCheck { private static readonly HashSet versionChecks; private static readonly Dictionary notProcessedNames; public string Name; private string? displayName; private string? currentVersion; private string? minimumRequiredVersion; public bool ModRequired = true; private string? ReceivedCurrentVersion; private string? ReceivedMinimumRequiredVersion; private readonly List ValidatedClients = new List(); private ConfigSync? ConfigSync; public string DisplayName { get { return displayName ?? Name; } set { displayName = value; } } public string CurrentVersion { get { return currentVersion ?? "0.0.0"; } set { currentVersion = value; } } public string MinimumRequiredVersion { get { return minimumRequiredVersion ?? (ModRequired ? CurrentVersion : "0.0.0"); } set { minimumRequiredVersion = value; } } private static void PatchServerSync() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown Patches patchInfo = PatchProcessor.GetPatchInfo((MethodBase)AccessTools.DeclaredMethod(typeof(ZNet), "Awake", (Type[])null, (Type[])null)); if (patchInfo != null && patchInfo.Postfixes.Count((Patch p) => p.PatchMethod.DeclaringType == typeof(ConfigSync.RegisterRPCPatch)) > 0) { return; } Harmony val = new Harmony("org.bepinex.helpers.ServerSync"); foreach (Type item in from t in typeof(ConfigSync).GetNestedTypes(BindingFlags.NonPublic).Concat(new Type[1] { typeof(VersionCheck) }) where t.IsClass select t) { val.PatchAll(item); } } static VersionCheck() { versionChecks = new HashSet(); notProcessedNames = new Dictionary(); typeof(ThreadingHelper).GetMethod("StartSyncInvoke").Invoke(ThreadingHelper.Instance, new object[1] { new Action(PatchServerSync) }); } public VersionCheck(string name) { Name = name; ModRequired = true; versionChecks.Add(this); } public VersionCheck(ConfigSync configSync) { ConfigSync = configSync; Name = ConfigSync.Name; versionChecks.Add(this); } public void Initialize() { ReceivedCurrentVersion = null; ReceivedMinimumRequiredVersion = null; if (ConfigSync != null) { Name = ConfigSync.Name; DisplayName = ConfigSync.DisplayName; CurrentVersion = ConfigSync.CurrentVersion; MinimumRequiredVersion = ConfigSync.MinimumRequiredVersion; ModRequired = ConfigSync.ModRequired; } } private bool IsVersionOk() { if (ReceivedMinimumRequiredVersion == null || ReceivedCurrentVersion == null) { return !ModRequired; } bool flag = new Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion); bool flag2 = new Version(ReceivedCurrentVersion) >= new Version(MinimumRequiredVersion); return flag && flag2; } private string ErrorClient() { if (ReceivedMinimumRequiredVersion == null) { return DisplayName + " is not installed on the server."; } return (new Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion)) ? (DisplayName + " may not be higher than version " + ReceivedCurrentVersion + ". You have version " + CurrentVersion + ".") : (DisplayName + " needs to be at least version " + ReceivedMinimumRequiredVersion + ". You have version " + CurrentVersion + "."); } private string ErrorServer(ZRpc rpc) { return "Disconnect: The client (" + rpc.GetSocket().GetHostName() + ") doesn't have the correct " + DisplayName + " version " + MinimumRequiredVersion; } private string Error(ZRpc? rpc = null) { return (rpc == null) ? ErrorClient() : ErrorServer(rpc); } private static VersionCheck[] GetFailedClient() { return versionChecks.Where((VersionCheck check) => !check.IsVersionOk()).ToArray(); } private static VersionCheck[] GetFailedServer(ZRpc rpc) { ZRpc rpc2 = rpc; return versionChecks.Where((VersionCheck check) => check.ModRequired && !check.ValidatedClients.Contains(rpc2)).ToArray(); } private static void Logout() { Game.instance.Logout(true, true); AccessTools.DeclaredField(typeof(ZNet), "m_connectionStatus").SetValue(null, (object)(ConnectionStatus)3); } private static void DisconnectClient(ZRpc rpc) { rpc.Invoke("Error", new object[1] { 3 }); } private static void CheckVersion(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, null); } private static void CheckVersion(ZRpc rpc, ZPackage pkg, Action? original) { string text = pkg.ReadString(); string text2 = pkg.ReadString(); string text3 = pkg.ReadString(); bool flag = false; foreach (VersionCheck versionCheck in versionChecks) { if (!(text != versionCheck.Name)) { Debug.Log((object)("Received " + versionCheck.DisplayName + " version " + text3 + " and minimum version " + text2 + " from the " + (ZNet.instance.IsServer() ? "client" : "server") + ".")); versionCheck.ReceivedMinimumRequiredVersion = text2; versionCheck.ReceivedCurrentVersion = text3; if (ZNet.instance.IsServer() && versionCheck.IsVersionOk()) { versionCheck.ValidatedClients.Add(rpc); } flag = true; } } if (flag) { return; } pkg.SetPos(0); if (original != null) { original(rpc, pkg); if (pkg.GetPos() == 0) { notProcessedNames.Add(text, text3); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyPrefix] private static bool RPC_PeerInfo(ZRpc rpc, ZNet __instance) { VersionCheck[] array = (__instance.IsServer() ? GetFailedServer(rpc) : GetFailedClient()); if (array.Length == 0) { return true; } VersionCheck[] array2 = array; foreach (VersionCheck versionCheck in array2) { Debug.LogWarning((object)versionCheck.Error(rpc)); } if (__instance.IsServer()) { DisconnectClient(rpc); } else { Logout(); } return false; } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] [HarmonyPrefix] private static void RegisterAndCheckVersion(ZNetPeer peer, ZNet __instance) { //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Expected O, but got Unknown notProcessedNames.Clear(); IDictionary dictionary = (IDictionary)typeof(ZRpc).GetField("m_functions", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(peer.m_rpc); if (dictionary.Contains(StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck"))) { object obj = dictionary[StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck")]; Action action = (Action)obj.GetType().GetField("m_action", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj); peer.m_rpc.Register("ServerSync VersionCheck", (Action)delegate(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, action); }); } else { peer.m_rpc.Register("ServerSync VersionCheck", (Action)CheckVersion); } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.Initialize(); if (versionCheck.ModRequired || __instance.IsServer()) { Debug.Log((object)("Sending " + versionCheck.DisplayName + " version " + versionCheck.CurrentVersion + " and minimum version " + versionCheck.MinimumRequiredVersion + " to the " + (__instance.IsServer() ? "client" : "server") + ".")); ZPackage val = new ZPackage(); val.Write(versionCheck.Name); val.Write(versionCheck.MinimumRequiredVersion); val.Write(versionCheck.CurrentVersion); peer.m_rpc.Invoke("ServerSync VersionCheck", new object[1] { val }); } } } [HarmonyPatch(typeof(ZNet), "Disconnect")] [HarmonyPrefix] private static void RemoveDisconnected(ZNetPeer peer, ZNet __instance) { if (!__instance.IsServer()) { return; } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.ValidatedClients.Remove(peer.m_rpc); } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] [HarmonyPostfix] private static void ShowConnectionError(FejdStartup __instance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) if (!__instance.m_connectionFailedPanel.activeSelf || (int)ZNet.GetConnectionStatus() != 3) { return; } bool flag = false; VersionCheck[] failedClient = GetFailedClient(); if (failedClient.Length != 0) { string text = string.Join("\n", failedClient.Select((VersionCheck check) => check.Error())); TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + text; flag = true; } foreach (KeyValuePair item in notProcessedNames.OrderBy, string>((KeyValuePair kv) => kv.Key)) { if (!__instance.m_connectionFailedError.text.Contains(item.Key)) { TMP_Text connectionFailedError2 = __instance.m_connectionFailedError; connectionFailedError2.text = connectionFailedError2.text + "\nServer expects you to have " + item.Key + " (Version: " + item.Value + ") installed."; flag = true; } } if (flag) { RectTransform component = ((Component)__instance.m_connectionFailedPanel.transform.Find("Image")).GetComponent(); Vector2 sizeDelta = component.sizeDelta; sizeDelta.x = 675f; component.sizeDelta = sizeDelta; __instance.m_connectionFailedError.ForceMeshUpdate(false, false); float num = __instance.m_connectionFailedError.renderedHeight + 105f; RectTransform component2 = ((Component)((Component)component).transform.Find("ButtonOk")).GetComponent(); component2.anchoredPosition = new Vector2(component2.anchoredPosition.x, component2.anchoredPosition.y - (num - component.sizeDelta.y) / 2f); sizeDelta = component.sizeDelta; sizeDelta.y = num; component.sizeDelta = sizeDelta; } } } }