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();
+ }
+ }
+}
+