diff --git a/BossNotifier.csproj b/BossNotifier.csproj index 867701e..b246ec0 100644 --- a/BossNotifier.csproj +++ b/BossNotifier.csproj @@ -38,6 +38,7 @@ + @@ -67,12 +68,21 @@ References\UnityEngine.CoreModule.dll + + References\Fika.Core.dll + + + + + + + mkdir $(TargetDir)BepInEx\plugins\ copy /Y "$(TargerDir)$(TargetFileName)" "C:\Games\SPT 3.10.3\BepInEx\plugins\$(TargetFileName)" copy /Y "$(TargetDir)$(TargetFileName)" "$(TargetDir)BepInEx\plugins\$(TargetFileName)" powershell.exe -command Compress-Archive -Force -Path BepInEx $(TargetName).zip - \ No newline at end of file + diff --git a/FikaIntegration.cs b/FikaIntegration.cs new file mode 100644 index 0000000..306c457 --- /dev/null +++ b/FikaIntegration.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using BepInEx.Logging; +using Comfort.Common; +using Fika.Core.Coop.Utils; +using Fika.Core.Modding; +using Fika.Core.Modding.Events; +using Fika.Core.Networking; +using BossNotifier.Packets; +using LiteNetLib; + +namespace BossNotifier +{ + // Static class to handle all Fika-related functionality + public static class FikaIntegration + { + // Register Fika event handlers + public static void Initialize() + { + BossNotifierPlugin.Log(LogLevel.Debug, "FikaIntegration.Initialize called"); + // Register Fika packet handler using Fika's event system by calling new Action to avoid errors when fika + // is not present. Thanks Tyfon for this tip. + FikaEventDispatcher.SubscribeEvent(new Action(OnFikaNetworkManagerCreated)); + BossNotifierPlugin.Log(LogLevel.Debug, "Subscribed to FikaNetworkManagerCreated event"); + } + + // Handler for Fika network manager creation event + private static void OnFikaNetworkManagerCreated(FikaNetworkManagerCreatedEvent evt) + { + BossNotifierPlugin.Log(LogLevel.Debug, "OnFikaNetworkManagerCreated event received"); + // Only register if this is a client + if (IsClient()) + { + BossNotifierPlugin.Log(LogLevel.Debug, "IsClient is true"); + var netMan = evt.Manager as FikaClient; + if (!netMan) return; + BossNotifierPlugin.Log(LogLevel.Debug, "FikaClient instance found, registering packet"); + netMan.RegisterPacket(new Action(OnBossListPacket)); + BossNotifierPlugin.Log(LogLevel.Info, "Registered BossListPacket handler for Fika client via event."); + netMan.RegisterPacket(new Action(OnVicinityNotificationPacket)); + BossNotifierPlugin.Log(LogLevel.Info, "Registered VicinityNotificationPacket handler for Fika client via event."); + } + } + + // Called when a BossListPacket is received from the host + private static void OnBossListPacket(BossListPacket packet) + { + BossNotifierPlugin.Log(LogLevel.Debug, $"OnBossListPacket called with {packet.BossNames.Count} bosses"); + BossNotifierPlugin.Log(LogLevel.Info, "Received BossListPacket from host."); + BossNotifierPlugin.Log(LogLevel.Info, $"Bosses in raid: {string.Join(", ", packet.BossNames)}"); + + BossLocationSpawnPatch.bossesInRaid.Clear(); + for (int i = 0; i < packet.BossNames.Count; i++) + { + BossNotifierPlugin.Log(LogLevel.Debug, $"Boss: {packet.BossNames[i]}, Location: {packet.Locations[i]}"); + BossLocationSpawnPatch.bossesInRaid[packet.BossNames[i]] = packet.Locations[i]; + } + + // Regenerate notifications if the mono instance exists + if (BossNotifierMono.Instance) + { + BossNotifierMono.Instance.GenerateBossNotifications(); + } + } + + // Called when a VicinityNotificationPacket is received from the host + private static void OnVicinityNotificationPacket(VicinityNotificationPacket packet) + { + BossNotifierPlugin.Log(LogLevel.Debug, $"OnVicinityNotificationPacket called with message: {packet.Message}"); + // Enqueue the message for display on the client + BotBossPatch.vicinityNotifications.Enqueue(packet.Message); + } + + // Send the boss list to all clients (called from host) + public static void SendBossListToClients(Dictionary bossesInRaid) + { + var netMan = Singleton.Instance; + if (netMan) + { + BossNotifierPlugin.Log(LogLevel.Debug, "FikaServer instance found, sending BossListPacket to all clients"); + var packet = new BossListPacket + { + BossNames = new List(), + Locations = new List() + }; + foreach (var kvp in bossesInRaid) + { + packet.BossNames.Add(kvp.Key); + packet.Locations.Add(kvp.Value); + } + netMan.SendDataToAll(ref packet, DeliveryMethod.ReliableOrdered); + } + else + { + BossNotifierPlugin.Log(LogLevel.Debug, "FikaServer instance not found, skipping packet send"); + } + } + + // Send a vicinity notification to all clients (called from host) + public static void SendVicinityNotificationToClients(string message) + { + var netMan = Singleton.Instance; + if (netMan) + { + BossNotifierPlugin.Log(LogLevel.Debug, $"FikaServer instance found, sending VicinityNotificationPacket to all clients: {message}"); + var packet = new VicinityNotificationPacket { Message = message }; + netMan.SendDataToAll(ref packet, DeliveryMethod.ReliableOrdered); + } + else + { + BossNotifierPlugin.Log(LogLevel.Debug, "FikaServer instance not found, skipping vicinity notification packet send"); + } + } + + public static bool IsClient() + { + return FikaBackendUtils.IsClient; + } + + public static bool IsHost() + { + return FikaBackendUtils.IsServer; + } + } +} + diff --git a/Plugin.cs b/Plugin.cs index dc01172..aecefa4 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -1,4 +1,5 @@ -using BepInEx; +using System; +using BepInEx; using SPT.Reflection.Patching; using SPT.Reflection.Utils; using System.Reflection; @@ -10,8 +11,7 @@ using Comfort.Common; using BepInEx.Logging; using System.Text; -using System; -using HarmonyLib; +using BepInEx.Bootstrap; #pragma warning disable IDE0051 // Remove unused private members @@ -19,9 +19,6 @@ namespace BossNotifier { [BepInPlugin("Mattdokn.BossNotifier", "BossNotifier", "1.6.0")] [BepInDependency("com.fika.core", BepInDependency.DependencyFlags.SoftDependency)] public class BossNotifierPlugin : BaseUnityPlugin { - public static FieldInfo FikaIsPlayerHost; - - // Configuration entries public static ConfigEntry showBossesKeyCode; public static ConfigEntry showNotificationsOnRaidStart; @@ -105,14 +102,11 @@ public static void Log(LogLevel level, string msg) { {"ZoneCard1", "Card 1" }, }; + public static bool IsFikaPresent; + private void Awake() { logger = Logger; - Type FikaUtilExternalType = Type.GetType("Fika.Core.Coop.Utils.FikaBackendUtils, Fika.Core", false); - if (FikaUtilExternalType != null) { - FikaIsPlayerHost = AccessTools.Field(FikaUtilExternalType, "MatchingType"); - } - // Initialize configuration entries showBossesKeyCode = Config.Bind("General", "Keyboard Shortcut", new KeyboardShortcut(KeyCode.O), "Key to show boss notifications."); showNotificationsOnRaidStart = Config.Bind("General", "Show Bosses on Raid Start", true, "Show boss notifications on raid start."); @@ -141,6 +135,21 @@ private void Awake() { Config.SettingChanged += Config_SettingChanged; Logger.LogInfo($"Plugin BossNotifier is loaded!"); + + // Initialize Fika integration + if (FikaPresent()) FikaIntegration.Initialize(); + } + + private static bool FikaPresent() + { + if (Chainloader.PluginInfos.TryGetValue("com.fika.core", out PluginInfo pluginInfo)) + { + IsFikaPresent = true; + return true; + } + + IsFikaPresent = false; + return false; } // Event handler for configuration changes @@ -205,11 +214,19 @@ private static void TryAddBoss(string boss, string location) { // Add the boss entry bossesInRaid.Add(boss, location); } + // After updating, send the new boss list to all clients if Fika is present and this is the host + if (BossNotifierPlugin.IsFikaPresent) { + try { + FikaIntegration.SendBossListToClients(bossesInRaid); + } catch (Exception ex) { + Logger.LogError($"Error sending boss list to clients: {ex}"); + } + } } - // Handle boss location spawns [PatchPostfix] private static void PatchPostfix(BossLocationSpawn __instance) { + if (BossNotifierPlugin.IsFikaPresent && FikaIntegration.IsClient()) return; // If the boss will spawn if (__instance.ShallSpawn) { // Get it's name, if no name found then return. @@ -263,6 +280,12 @@ private static void PatchPostfix(BotBoss __instance) { vicinityNotifications.Enqueue($"{name} {(BossNotifierPlugin.pluralBosses.Contains(name) ? "have" : "has")} been detected in your vicinity."); + // Sync vicinity notification to clients if running as host + if (BossNotifierPlugin.IsFikaPresent && FikaIntegration.IsHost()) + { + FikaIntegration.SendVicinityNotificationToClients($"{name} {(BossNotifierPlugin.pluralBosses.Contains(name) ? "have" : "has")} been detected in your vicinity."); + } + //if (BossNotifierMono.Instance.intelCenterLevel >= BossNotifierPlugin.intelCenterDetectedUnlockLevel.Value) { // NotificationManagerClass.DisplayMessageNotification($"{name} {(BossNotifierPlugin.pluralBosses.Contains(name) ? "have" : "has")} been detected in your vicinity.", ENotificationDurationType.Long); // BossNotifierMono.Instance.GenerateBossNotifications(); @@ -291,7 +314,6 @@ class BossNotifierMono : MonoBehaviour { public int intelCenterLevel; private void SendBossNotifications() { - if (!ShouldFunction()) return; if (intelCenterLevel < BossNotifierPlugin.intelCenterUnlockLevel.Value) return; // If we have no notifications to display, send one saying there's no bosses located. @@ -346,18 +368,19 @@ public void OnDestroy() { BotBossPatch.spawnedBosses.Clear(); } - public bool ShouldFunction() { - if (BossNotifierPlugin.FikaIsPlayerHost == null) return true; - return (int)BossNotifierPlugin.FikaIsPlayerHost.GetValue(null) == 2; - } - public void GenerateBossNotifications() { // Clear out boss notification cache bossNotificationMessages = new List(); - // Check if it's daytime to prevent showing Cultist notif. + bool isDayTime; // This is the same method that DayTimeCultists patches so if that mod is installed then this always returns false - bool isDayTime = Singleton.Instance.BotsController.ZonesLeaveController.IsDay(); + if (BossNotifierPlugin.IsFikaPresent && FikaIntegration.IsClient()) { + // IBotGame.Instance is null as a Fika client, so we skip the isDayTime check, maybe there's an alternate way to check for day time? + BossNotifierPlugin.Log(LogLevel.Debug, "FikaIntegration.IsClient() is true, skipping isDayTime check"); + isDayTime = false; + } else { + isDayTime = Singleton.Instance.BotsController.ZonesLeaveController.IsDay(); + } // Get whether location is unlocked or not. bool isLocationUnlocked = intelCenterLevel >= BossNotifierPlugin.intelCenterLocationUnlockLevel.Value; diff --git a/packets/BossListPacket.cs b/packets/BossListPacket.cs new file mode 100644 index 0000000..d2e5ec1 --- /dev/null +++ b/packets/BossListPacket.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using LiteNetLib; +using LiteNetLib.Utils; + +namespace BossNotifier.Packets +{ + // Packet for synchronizing boss list between host and clients + public struct BossListPacket : INetSerializable + { + public List BossNames; + public List Locations; + + public void Serialize(NetDataWriter writer) + { + writer.Put(BossNames?.Count ?? 0); + if (BossNames != null && Locations != null) + { + for (int i = 0; i < BossNames.Count; i++) + { + writer.Put(BossNames[i]); + writer.Put(Locations[i]); + } + } + } + + public void Deserialize(NetDataReader reader) + { + if (BossNames == null) BossNames = new List(); + if (Locations == null) Locations = new List(); + BossNames.Clear(); + Locations.Clear(); + int count = reader.GetInt(); + for (int i = 0; i < count; i++) + { + BossNames.Add(reader.GetString()); + Locations.Add(reader.GetString()); + } + } + } +} + diff --git a/packets/BossNotifier.fika.csproj b/packets/BossNotifier.fika.csproj new file mode 100644 index 0000000..63ea993 --- /dev/null +++ b/packets/BossNotifier.fika.csproj @@ -0,0 +1,14 @@ + + + net471 + BossNotifier.fika + BossNotifier.Packets + Library + + + + ..\References\Fika.Core.dll + + + + diff --git a/packets/VicinityNotificationPacket.cs b/packets/VicinityNotificationPacket.cs new file mode 100644 index 0000000..572d825 --- /dev/null +++ b/packets/VicinityNotificationPacket.cs @@ -0,0 +1,22 @@ +using LiteNetLib; +using LiteNetLib.Utils; + +namespace BossNotifier.Packets +{ + // Packet for synchronizing vicinity notifications between host and clients + public struct VicinityNotificationPacket : INetSerializable + { + public string Message; + + public void Serialize(NetDataWriter writer) + { + writer.Put(Message); + } + + public void Deserialize(NetDataReader reader) + { + Message = reader.GetString(); + } + } +} +