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 ShieldBubble v1.1.0
plugins/ShieldBubble.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ShieldBubble")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+2ab0bf440f910a5473a0f10a312d094411679934")] [assembly: AssemblyProduct("ShieldBubble")] [assembly: AssemblyTitle("ShieldBubble")] [assembly: AssemblyVersion("1.0.0.0")] namespace ShieldBubble; public static class ConfigStore { public static float Radius { get; set; } = 30f; public static bool Enabled { get; set; } = true; } public static class Net { public const float AckTimeoutSeconds = 10f; public const float RepushSeconds = 10f; public const string RpcModAck = "SRModAck"; public const string RpcConfig = "SRConfig"; private static readonly Dictionary<long, float> s_firstSeen = new Dictionary<long, float>(); private static readonly Dictionary<long, bool> s_acked = new Dictionary<long, bool>(); private static readonly Dictionary<long, float> s_lastPush = new Dictionary<long, float>(); private static bool s_dirty; private static float s_now; private static int s_ackRetransmits; public static bool HasServerConfig { get; private set; } public static float ServerRadius { get; private set; } public static bool ServerEnabled { get; private set; } public static (float radius, bool enabled) Effective() { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || instance.IsServer()) { return (radius: ConfigStore.Radius, enabled: ConfigStore.Enabled); } if (HasServerConfig) { return (radius: ServerRadius, enabled: ServerEnabled); } return (radius: ConfigStore.Radius, enabled: ConfigStore.Enabled); } public static void ClientSendAck(long serverUid) { if (!HasServerConfig && serverUid > 0) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { s_ackRetransmits++; Plugin.Warn($"[ShieldBubble] ack retransmit n={s_ackRetransmits} → uid {serverUid}"); instance.InvokeRoutedRPC(serverUid, "SRModAck", Array.Empty<object>()); } } } public static void ClientOnServerConfig(long sender, float radius, bool enabled) { ServerRadius = radius; ServerEnabled = enabled; HasServerConfig = true; s_ackRetransmits = 0; Plugin.Warn($"[ShieldBubble] config from server: radius={radius:0} m, enabled={enabled}"); } public static void ResetClient() { HasServerConfig = false; ServerRadius = 0f; ServerEnabled = false; s_ackRetransmits = 0; } public static void MarkDirty() { s_dirty = true; } public static void ServerOnModAck(long senderUid) { s_acked[senderUid] = true; if (ConfigStore.Enabled) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(senderUid, "SRConfig", new object[2] { ConfigStore.Radius, ConfigStore.Enabled }); s_lastPush[senderUid] = s_now; Plugin.Warn($"[ShieldBubble] mod ack from '{PeerName(senderUid)}' (uid={senderUid}) → pushed config (radius={ConfigStore.Radius:0} m, enabled={ConfigStore.Enabled})"); } } } public static void ServerPollOnce(float now) { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { return; } s_now = now; List<ZNetPeer> connectedPeers = instance.GetConnectedPeers(); if (connectedPeers == null) { return; } List<long> list = new List<long>(); foreach (long key in s_firstSeen.Keys) { if (!PeersContain(connectedPeers, key)) { list.Add(key); } } foreach (long item in list) { s_firstSeen.Remove(item); s_lastPush.Remove(item); } List<long> list2 = new List<long>(); foreach (long key2 in s_acked.Keys) { if (!PeersContain(connectedPeers, key2)) { list2.Add(key2); } } foreach (long item2 in list2) { s_acked.Remove(item2); s_lastPush.Remove(item2); } if (connectedPeers.Count == 0) { if (ZRoutedRpc.instance != null) { s_dirty = false; } return; } bool enabled = ConfigStore.Enabled; float radius = ConfigStore.Radius; ZRoutedRpc instance2 = ZRoutedRpc.instance; for (int i = 0; i < connectedPeers.Count; i++) { ZNetPeer val = connectedPeers[i]; if (val == null || !val.IsReady()) { continue; } long uid = val.m_uid; if (!enabled) { continue; } if (!s_acked.TryGetValue(uid, out var _)) { if (!s_firstSeen.ContainsKey(uid)) { s_firstSeen[uid] = now; Plugin.Warn($"[ShieldBubble] peer '{val.m_playerName}' (uid={uid}) waiting for mod ack"); } if (now - s_firstSeen[uid] > 10f) { Plugin.Warn($"[ShieldBubble] kicking '{val.m_playerName}' (uid={uid}): missing mod (no ack after {10f:0} s)"); s_firstSeen.Remove(uid); s_lastPush.Remove(uid); instance.Disconnect(val); } continue; } float value2; bool flag = s_lastPush.TryGetValue(uid, out value2) && now - value2 >= 10f; if ((s_dirty || flag) && instance2 != null) { instance2.InvokeRoutedRPC(uid, "SRConfig", new object[2] { radius, enabled }); s_lastPush[uid] = now; Plugin.Warn($"[ShieldBubble] pushed config to '{val.m_playerName}' (uid={uid}): radius={radius:0} m, enabled={enabled}"); } } if (instance2 != null) { s_dirty = false; } } private static bool PeersContain(List<ZNetPeer> peers, long uid) { for (int i = 0; i < peers.Count; i++) { if (peers[i] != null && peers[i].m_uid == uid) { return true; } } return false; } private static string PeerName(long uid) { ZNet instance = ZNet.instance; List<ZNetPeer> list = (((Object)(object)instance != (Object)null) ? instance.GetConnectedPeers() : null); if (list != null) { for (int i = 0; i < list.Count; i++) { if (list[i] != null && list[i].m_uid == uid) { return list[i].m_playerName; } } } return "?"; } public static void Reset() { HasServerConfig = false; ServerRadius = 0f; ServerEnabled = false; s_firstSeen.Clear(); s_acked.Clear(); s_lastPush.Clear(); s_dirty = false; s_now = 0f; s_ackRetransmits = 0; } } [HarmonyPatch] internal static class Patches { private static readonly FieldInfo s_radiusTargetField = AccessTools.Field(typeof(ShieldGenerator), "m_radiusTarget"); private static bool s_warned; private static void WarnRadiusFieldMissing() { if (!s_warned) { s_warned = true; Plugin.Warn("[ShieldBubble] ShieldGenerator.m_radiusTarget field not found; UpdateShield postfix is inactive (API change?)"); } } [HarmonyPatch(typeof(ShieldGenerator), "Start")] [HarmonyPrefix] private static void StartPrefix(ShieldGenerator __instance) { (float, bool) tuple = Net.Effective(); if (tuple.Item2) { var (num, _) = tuple; if (!(num <= 0f)) { __instance.m_minShieldRadius = num; __instance.m_maxShieldRadius = num; __instance.m_startStopSpeed = 5f; } } } [HarmonyPatch(typeof(ShieldGenerator), "UpdateShield")] [HarmonyPostfix] private static void UpdateShieldPostfix(ShieldGenerator __instance, ZNetView ___m_nview, float ___m_lastFuel) { (float, bool) tuple = Net.Effective(); if (!tuple.Item2) { return; } var (num, _) = tuple; if (num <= 0f || (Object)(object)___m_nview == (Object)null || !___m_nview.IsValid() || ___m_lastFuel <= 0f) { return; } if (s_radiusTargetField == null) { WarnRadiusFieldMissing(); return; } if (__instance.m_minShieldRadius != num) { __instance.m_minShieldRadius = num; } if (__instance.m_maxShieldRadius != num) { __instance.m_maxShieldRadius = num; } s_radiusTargetField.SetValue(__instance, num); } [HarmonyPatch(typeof(ShieldDomeImageEffect), "Awake")] [HarmonyPostfix] private static void EffectAwakePostfix(ShieldDomeImageEffect __instance) { (float, bool) tuple = Net.Effective(); if (!tuple.Item2) { return; } var (num, _) = tuple; if (!(num <= 0f)) { float num2 = num + 50f; if (num2 > __instance.m_drawDistance) { __instance.m_drawDistance = num2; } } } } [BepInPlugin("dr0per.shieldbubble", "Shield Bubble", "1.1.0")] public class Plugin : BaseUnityPlugin { private ConfigEntry<float> _radius; private ConfigEntry<bool> _enabled; private static readonly WaitForSeconds Wait1s = new WaitForSeconds(1f); public static Plugin Instance { get; private set; } private void Awake() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) Instance = this; _radius = ((BaseUnityPlugin)this).Config.Bind<float>("Shield", "Radius", 30f, new ConfigDescription("Shield generator radius in meters (1-500).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 500f), (object[])null)); _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Shield", "Enabled", true, new ConfigDescription("Enable the mod.", (AcceptableValueBase)null, (object[])null)); Sync(); _radius.SettingChanged += delegate { Sync(); Net.MarkDirty(); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[ShieldBubble] Radius changed to {_radius.Value} m"); }; _enabled.SettingChanged += delegate { Sync(); Net.MarkDirty(); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[ShieldBubble] Enabled changed to {_enabled.Value}"); }; new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID).PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"ShieldBubble ready: enabled={_enabled.Value}, radius={_radius.Value} m"); } private void Start() { ((MonoBehaviour)this).StartCoroutine(RegisterHandlersLoop()); ((MonoBehaviour)this).StartCoroutine(ClientSyncLoop()); ((MonoBehaviour)this).StartCoroutine(ServerPollLoop()); } private IEnumerator RegisterHandlersLoop() { ZRoutedRpc instance; ZNet instance2; while (true) { instance = ZRoutedRpc.instance; instance2 = ZNet.instance; if (instance != null && (Object)(object)instance2 != (Object)null) { break; } yield return Wait1s; } if (instance2.IsServer()) { instance.Register("SRModAck", (Action<long>)Net.ServerOnModAck); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[ShieldBubble] handlers registered (server: SRModAck)"); } else { instance.Register<float, bool>("SRConfig", (Action<long, float, bool>)Net.ClientOnServerConfig); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[ShieldBubble] handlers registered (client: SRConfig)"); } } private IEnumerator ClientSyncLoop() { float serverReadyTime = -1f; bool warnedNoConfig = false; bool wasServerReady = false; while (true) { ZNet instance = ZNet.instance; if ((Object)(object)instance != (Object)null && !instance.IsServer()) { ZNetPeer serverPeer = instance.GetServerPeer(); if (serverPeer != null) { if (serverReadyTime < 0f) { serverReadyTime = Time.time; } Net.ClientSendAck(serverPeer.m_uid); if (!warnedNoConfig && Time.time - serverReadyTime >= 15f) { warnedNoConfig = true; ((BaseUnityPlugin)this).Logger.LogWarning((object)"[ShieldBubble] no server config received after 15 s: is the server missing the mod, or is Enabled=false? Using local config."); } } else if (wasServerReady) { Net.ResetClient(); serverReadyTime = -1f; warnedNoConfig = false; ((BaseUnityPlugin)this).Logger.LogWarning((object)"[ShieldBubble] server lost connection: reset client sync (will re-ack on reconnect)"); } wasServerReady = serverPeer != null; } yield return Wait1s; } } private IEnumerator ServerPollLoop() { while (true) { Net.ServerPollOnce(Time.time); yield return Wait1s; } } private void Sync() { ConfigStore.Radius = _radius.Value; ConfigStore.Enabled = _enabled.Value; } public static void Warn(string message) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogWarning((object)message); } } }