Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion BossNotifier.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="Plugin.cs" />
<Compile Include="FikaIntegration.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
Expand Down Expand Up @@ -67,12 +68,21 @@
<Reference Include="UnityEngine.CoreModule">
<HintPath>References\UnityEngine.CoreModule.dll</HintPath>
</Reference>
<Reference Include="com.fika.core">
<HintPath>References\Fika.Core.dll</HintPath>
</Reference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="packets\BossNotifier.fika.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="packets\BossNotifier.fika.csproj" />
</ItemGroup>
<PropertyGroup>
<PostBuildEvent>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</PostBuildEvent>
</PropertyGroup>
</Project>
</Project>
126 changes: 126 additions & 0 deletions FikaIntegration.cs
Original file line number Diff line number Diff line change
@@ -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<FikaNetworkManagerCreatedEvent>(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<BossListPacket>(OnBossListPacket));
BossNotifierPlugin.Log(LogLevel.Info, "Registered BossListPacket handler for Fika client via event.");
netMan.RegisterPacket(new Action<VicinityNotificationPacket>(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<string, string> bossesInRaid)
{
var netMan = Singleton<FikaServer>.Instance;
if (netMan)
{
BossNotifierPlugin.Log(LogLevel.Debug, "FikaServer instance found, sending BossListPacket to all clients");
var packet = new BossListPacket
{
BossNames = new List<string>(),
Locations = new List<string>()
};
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<FikaServer>.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;
}
}
}

63 changes: 43 additions & 20 deletions Plugin.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BepInEx;
using System;
using BepInEx;
using SPT.Reflection.Patching;
using SPT.Reflection.Utils;
using System.Reflection;
Expand All @@ -10,18 +11,14 @@
using Comfort.Common;
using BepInEx.Logging;
using System.Text;
using System;
using HarmonyLib;
using BepInEx.Bootstrap;

#pragma warning disable IDE0051 // Remove unused private members

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<KeyboardShortcut> showBossesKeyCode;
public static ConfigEntry<bool> showNotificationsOnRaidStart;
Expand Down Expand Up @@ -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.");
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<string>();

// 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<IBotGame>.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<IBotGame>.Instance.BotsController.ZonesLeaveController.IsDay();
}

// Get whether location is unlocked or not.
bool isLocationUnlocked = intelCenterLevel >= BossNotifierPlugin.intelCenterLocationUnlockLevel.Value;
Expand Down
41 changes: 41 additions & 0 deletions packets/BossListPacket.cs
Original file line number Diff line number Diff line change
@@ -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<string> BossNames;
public List<string> 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<string>();
if (Locations == null) Locations = new List<string>();
BossNames.Clear();
Locations.Clear();
int count = reader.GetInt();
for (int i = 0; i < count; i++)
{
BossNames.Add(reader.GetString());
Locations.Add(reader.GetString());
}
}
}
}

14 changes: 14 additions & 0 deletions packets/BossNotifier.fika.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net471</TargetFramework>
<AssemblyName>BossNotifier.fika</AssemblyName>
<RootNamespace>BossNotifier.Packets</RootNamespace>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Reference Include="com.fika.core">
<HintPath>..\References\Fika.Core.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

22 changes: 22 additions & 0 deletions packets/VicinityNotificationPacket.cs
Original file line number Diff line number Diff line change
@@ -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();
}
}
}