Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SharedHostUpgrades by Schleimi v1.0.3
BepInEx/plugins/SharedHostUpgrades_by_Schleimi/SharedHostUpgrades_by_Schleimi.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Photon.Pun; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace SharedHostUpgrades; [BepInPlugin("schleimi.sharedhostupgrades", "Shared Host Upgrades by Schleimi", "1.0.3")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "schleimi.sharedhostupgrades"; public const string Name = "Shared Host Upgrades by Schleimi"; public const string Version = "1.0.3"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> PollSeconds; internal static ConfigEntry<float> SettleSeconds; internal static ConfigEntry<float> RpcIntervalSeconds; private void Awake() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Host-only upgrade sharing."); PollSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PollSeconds", 1f, new ConfigDescription("How often the host checks for newly used upgrades.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 10f), new object[0])); SettleSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SettleSeconds", 0.5f, new ConfigDescription("Quiet time after the last upgrade before synchronization begins.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), new object[0])); RpcIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RpcIntervalSeconds", 0.15f, new ConfigDescription("Minimum delay between shared upgrade RPCs.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), new object[0])); new Harmony("schleimi.sharedhostupgrades").PatchAll(); Log.LogInfo((object)"Shared Host Upgrades by Schleimi 1.0.3 loaded; host delta synchronizer installed."); } } [HarmonyPatch(typeof(PlayerHealth), "Start")] public static class PlayerHealthHostSyncPatch { public static void Postfix(PlayerHealth __instance) { if (!((Object)(object)__instance == (Object)null) && !(((Object)__instance).name == "PlayerAvatarMenu")) { HostUpgradeSyncRunner hostUpgradeSyncRunner = ((Component)__instance).gameObject.GetComponent<HostUpgradeSyncRunner>(); if ((Object)(object)hostUpgradeSyncRunner == (Object)null) { hostUpgradeSyncRunner = ((Component)__instance).gameObject.AddComponent<HostUpgradeSyncRunner>(); } hostUpgradeSyncRunner.ScheduleMapStart(); } } } public sealed class HostUpgradeSyncRunner : MonoBehaviour { private sealed class UpgradeDelta { internal readonly string Key; internal readonly string PlayerId; internal readonly int Amount; internal UpgradeDelta(string key, string playerId, int amount) { Key = key; PlayerId = playerId; Amount = amount; } } private sealed class UpgradeCommand { internal readonly string Key; internal readonly string PlayerId; internal UpgradeCommand(string key, string playerId) { Key = key; PlayerId = playerId; } } private static HostUpgradeSyncRunner active; private readonly Dictionary<string, int> hostLevels = new Dictionary<string, int>(); private readonly Dictionary<string, Dictionary<string, int>> observed = new Dictionary<string, Dictionary<string, int>>(); private float initializeAt = -1f; private float nextPoll; private bool initialized; private bool reconciliationPending; private bool reconciliationRunning; private float reconcileAt; private float nextRpcAt; private readonly Queue<UpgradeCommand> rpcQueue = new Queue<UpgradeCommand>(); private void Awake() { if ((Object)(object)active == (Object)null) { active = this; } } private void OnDestroy() { if ((Object)(object)active == (Object)(object)this) { active = null; } } internal void ScheduleMapStart() { if ((Object)(object)active != (Object)(object)this) { if ((Object)(object)active != (Object)null) { active.ScheduleMapStart(); } } else { initializeAt = Time.time + 2f; initialized = false; CancelReconciliation(); } } private void Update() { if ((Object)(object)active != (Object)(object)this || !Plugin.Enabled.Value || !SemiFunc.IsMasterClientOrSingleplayer()) { return; } if (initializeAt >= 0f && Time.time >= initializeAt) { if (InitializeFromHost()) { initializeAt = -1f; nextPoll = Time.time + Plugin.PollSeconds.Value; } else { initializeAt = Time.time + 1f; } } else { if (!initialized) { return; } if (reconciliationRunning) { ProcessReconciliation(); } else if (reconciliationPending) { if (Time.time >= reconcileAt) { BeginReconciliation(); } } else if (!(Time.time < nextPoll)) { nextPoll = Time.time + Plugin.PollSeconds.Value; DetectNewUpgradeSteps(); } } } private bool InitializeFromHost() { if (!PlayersReady()) { return false; } string text = HostSteamId(); if (string.IsNullOrEmpty(text)) { return false; } hostLevels.Clear(); foreach (KeyValuePair<string, Dictionary<string, int>> item in UpgradeDictionaries()) { item.Value.TryGetValue(text, out var value); hostLevels[item.Key] = value; } initialized = true; CancelReconciliation(); CaptureObserved(); reconciliationPending = true; reconcileAt = Time.time + Plugin.SettleSeconds.Value; Plugin.Log.LogInfo((object)"Host upgrade values initialized after map start."); return true; } private void DetectNewUpgradeSteps() { if (!PlayersReady()) { return; } List<UpgradeDelta> list = new List<UpgradeDelta>(); foreach (KeyValuePair<string, Dictionary<string, int>> item in UpgradeDictionaries()) { foreach (KeyValuePair<string, int> item2 in item.Value) { int num = ObservedLevel(item.Key, item2.Key); if (item2.Value > num) { list.Add(new UpgradeDelta(item.Key, item2.Key, item2.Value - num)); } } } if (list.Count == 0) { return; } string text = HostSteamId(); if (string.IsNullOrEmpty(text)) { return; } foreach (UpgradeDelta item3 in list) { hostLevels.TryGetValue(item3.Key, out var value); if (item3.PlayerId == text) { hostLevels[item3.Key] = value + item3.Amount; } else { Apply(item3.Key, text, item3.Amount); hostLevels[item3.Key] = value + item3.Amount; } Plugin.Log.LogInfo((object)("Host received " + item3.Key + " +" + item3.Amount + " from " + item3.PlayerId + ".")); } CaptureObserved(); reconciliationPending = true; reconcileAt = Time.time + Plugin.SettleSeconds.Value; Plugin.Log.LogInfo((object)("Shared upgrade synchronization queued: " + list.Count + " new upgrade step(s).")); } private void BeginReconciliation() { if (!PlayersReady()) { reconciliationPending = true; reconcileAt = Time.time + 1f; return; } reconciliationPending = false; rpcQueue.Clear(); foreach (KeyValuePair<string, Dictionary<string, int>> item in UpgradeDictionaries()) { if (!hostLevels.TryGetValue(item.Key, out var value)) { continue; } List<PlayerAvatar> list = SemiFunc.PlayerGetAll(); if (list == null) { continue; } for (int i = 0; i < list.Count; i++) { string text = Field(list[i], "steamID") as string; if (!string.IsNullOrEmpty(text)) { item.Value.TryGetValue(text, out var value2); int num = Math.Max(0, value - value2); for (int j = 0; j < num; j++) { rpcQueue.Enqueue(new UpgradeCommand(item.Key, text)); } } } } if (rpcQueue.Count == 0) { Plugin.Log.LogInfo((object)"Shared upgrade synchronization: all players already match the host."); return; } reconciliationRunning = true; nextRpcAt = Time.time; Plugin.Log.LogInfo((object)("Shared upgrade synchronization started: " + rpcQueue.Count + " queued RPC(s).")); } private void ProcessReconciliation() { if (!PlayersReady()) { CancelReconciliation(); reconciliationPending = true; reconcileAt = Time.time + 1f; Plugin.Log.LogInfo((object)"Shared upgrade synchronization paused until player avatars are ready."); } else if (!(Time.time < nextRpcAt)) { if (rpcQueue.Count == 0) { reconciliationRunning = false; Plugin.Log.LogInfo((object)"Shared upgrade synchronization completed."); return; } UpgradeCommand upgradeCommand = rpcQueue.Dequeue(); Apply(upgradeCommand.Key, upgradeCommand.PlayerId, 1); CaptureObservedLevel(upgradeCommand.Key, upgradeCommand.PlayerId); nextRpcAt = Time.time + Plugin.RpcIntervalSeconds.Value; } } private void CancelReconciliation() { reconciliationPending = false; reconciliationRunning = false; rpcQueue.Clear(); } private void CaptureObserved() { observed.Clear(); foreach (KeyValuePair<string, Dictionary<string, int>> item in UpgradeDictionaries()) { observed[item.Key] = new Dictionary<string, int>(item.Value); } } private void CaptureObservedLevel(string key, string playerId) { if (!observed.TryGetValue(key, out var value)) { value = new Dictionary<string, int>(); observed[key] = value; } foreach (KeyValuePair<string, Dictionary<string, int>> item in UpgradeDictionaries()) { if (item.Key != key) { continue; } item.Value.TryGetValue(playerId, out var value2); value[playerId] = value2; break; } } private int ObservedLevel(string key, string playerId) { Dictionary<string, int> value; int value2; return (observed.TryGetValue(key, out value) && value.TryGetValue(playerId, out value2)) ? value2 : 0; } private static void Apply(string key, string steamId, int levels) { if (levels > 0 && !((Object)(object)PunManager.instance == (Object)null)) { PhotonView component = ((Component)PunManager.instance).GetComponent<PhotonView>(); if (!((Object)(object)component == (Object)null)) { component.RPC("TesterUpgradeCommandRPC", (RpcTarget)0, new object[3] { steamId, key.Substring("playerUpgrade".Length), levels }); } } } private static string HostSteamId() { return Field(SemiFunc.PlayerAvatarLocal(), "steamID") as string; } private static bool PlayersReady() { List<PlayerAvatar> list = SemiFunc.PlayerGetAll(); if (list == null || list.Count == 0) { return false; } for (int i = 0; i < list.Count; i++) { PlayerAvatar val = list[i]; if ((Object)(object)val == (Object)null) { return false; } if (Field(val, "photonView") == null) { return false; } if (Field(val, "playerHealth") == null) { return false; } if (Field(val, "physGrabber") == null) { return false; } if (Field(val, "tumble") == null) { return false; } } return true; } private static IEnumerable<KeyValuePair<string, Dictionary<string, int>>> UpgradeDictionaries() { object obj = Field(StatsManager.instance, "dictionaryOfDictionaries"); SortedDictionary<string, Dictionary<string, int>> sortedDictionary = obj as SortedDictionary<string, Dictionary<string, int>>; SortedDictionary<string, Dictionary<string, int>> sortedDictionary2 = new SortedDictionary<string, Dictionary<string, int>>(); if (sortedDictionary == null) { return sortedDictionary2; } foreach (KeyValuePair<string, Dictionary<string, int>> item in sortedDictionary) { if (!(item.Key == "playerUpgradeTumbleLaunch") && item.Key.StartsWith("playerUpgrade", StringComparison.Ordinal) && item.Value != null) { sortedDictionary2.Add(item.Key, item.Value); } } return sortedDictionary2; } private static object Field(object target, string name) { if (target == null) { return null; } FieldInfo field = target.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return (field == null) ? null : field.GetValue(target); } }