From 98a297ff4ee0e5640b449881721bf6a1bd92ee09 Mon Sep 17 00:00:00 2001 From: Zaldaryon <273555259+Zaldaryon@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:40:29 -0300 Subject: [PATCH 1/3] Warn when a mod patches a method group friendly fire relies on StratumHarmonyVisibility already logs every mod Harmony patch for triage. This adds a targeted check: when /friendlyfire is off, walk the patched methods for the ones the toggle depends on (the damage entry points, the melee interaction handler, the projectile impact path, CreateExplosion) and log a warning naming any mod that also patches one. A mod that replaces one of those and does not call the original can silently defeat the toggle. Nothing can prevent that, so the goal is just to make it visible instead of a mystery. Runs regardless of the Diagnostics.LogModHarmonyPatches flag, since it is one short feature warning rather than the full patch dump, and stays silent when the toggle is on or nothing patches those methods. Part of #277 --- .../ServerSystemStratum.cs | 1 + .../StratumHarmonyVisibility.cs | 75 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs b/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs index d517c57f..2cc1e590 100644 --- a/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs +++ b/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs @@ -106,6 +106,7 @@ public override void OnBeginRunGame() StratumUpdateChecker.Start(server); StratumServerStats.Start(server); StratumHarmonyVisibility.LogPatchedMethods(server); + StratumHarmonyVisibility.WarnFriendlyFireConflicts(); if (StratumRuntime.Config.Backup.Enabled) { new StratumBackupScheduler(server); diff --git a/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs b/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs index fc833530..cd45fd88 100644 --- a/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs +++ b/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Reflection; using HarmonyLib; +using Vintagestory.API.Server; namespace Vintagestory.Server; @@ -68,6 +69,80 @@ public static void LogPatchedMethods(ServerMain server) } } + // Methods the group friendly fire toggle (#277) relies on to drop a hit between group mates. + // A mod that Harmony-patches one of these and skips the original can silently defeat the + // toggle. No server platform can stop that, so when the toggle is on we name the mod in the + // log for triage. Keyed by the declaring type's simple name plus the method name, which is + // enough to be unambiguous here and avoids caring about overloads or full namespaces. + private static readonly HashSet FriendlyFireCriticalMethods = new HashSet(System.StringComparer.Ordinal) + { + "Entity.ReceiveDamage", + "Entity.ShouldReceiveDamage", + "EntityAgent.ReceiveDamage", + "EntityAgent.ShouldReceiveDamage", + "EntityHumanoid.ShouldReceiveDamage", + "EntityPlayer.ShouldReceiveDamage", + "EntityAgent.OnInteract", + "EntityBehaviorHealth.OnEntityReceiveDamage", + "ServerMain.CreateExplosion", + "ServerSystemEntitySimulation.HandleEntityInteraction", + "EntityProjectileBase.CanDealDamage", + "EntityProjectileBase.DealDamage", + "EntityProjectileBase.ImpactOnEntity", + }; + + // Runs regardless of Diagnostics.LogModHarmonyPatches: this is a targeted safety warning for + // one feature, not the full patch dump. Only emits anything when /friendlyfire is off (the + // toggle is active) and a mod actually patches one of the methods it depends on. + public static void WarnFriendlyFireConflicts() + { + if (!StratumFriendlyFireHook.BlockGroupDamage) + { + return; + } + + foreach (MethodBase method in Harmony.GetAllPatchedMethods()) + { + string key = (method.DeclaringType?.Name ?? "?") + "." + method.Name; + if (!FriendlyFireCriticalMethods.Contains(key)) + { + continue; + } + + Patches info = Harmony.GetPatchInfo(method); + if (info == null) + { + continue; + } + + HashSet owners = new HashSet(System.StringComparer.OrdinalIgnoreCase); + CollectOwners(owners, info.Prefixes); + CollectOwners(owners, info.Postfixes); + CollectOwners(owners, info.Transpilers); + CollectOwners(owners, info.Finalizers); + owners.Remove("(unknown)"); + if (owners.Count == 0) + { + continue; + } + + StratumRuntime.LogWarning($"harmony visibility: group friendly fire (/friendlyfire) relies on {key}, which mod(s) {string.Join(", ", owners.OrderBy(o => o, System.StringComparer.OrdinalIgnoreCase))} also patch. Confirm a hit between two group members is still blocked with that mod loaded."); + } + } + + private static void CollectOwners(HashSet owners, IReadOnlyCollection patches) + { + if (patches == null) + { + return; + } + + foreach (Patch patch in patches) + { + owners.Add(string.IsNullOrWhiteSpace(patch.owner) ? "(unknown)" : patch.owner); + } + } + private static void CountOwners(Dictionary byOwner, IReadOnlyCollection patches, System.Action increment) { if (patches == null) From a0c1677a3eda8d22510217afaed2080b04db5df7 Mon Sep 17 00:00:00 2001 From: Zaldaryon <273555259+Zaldaryon@users.noreply.github.com> Date: Tue, 8 Sep 2026 19:23:46 -0300 Subject: [PATCH 2/3] Refresh friendly-fire Harmony warnings --- .../Vintagestory.Server/ServerSystemStratum.cs | 1 - .../Vintagestory.Server/StratumFriendlyFireSystem.cs | 1 + .../Vintagestory.Server/StratumHarmonyVisibility.cs | 7 +++---- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs b/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs index 2cc1e590..d517c57f 100644 --- a/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs +++ b/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs @@ -106,7 +106,6 @@ public override void OnBeginRunGame() StratumUpdateChecker.Start(server); StratumServerStats.Start(server); StratumHarmonyVisibility.LogPatchedMethods(server); - StratumHarmonyVisibility.WarnFriendlyFireConflicts(); if (StratumRuntime.Config.Backup.Enabled) { new StratumBackupScheduler(server); diff --git a/sources/VintagestoryLib/Vintagestory.Server/StratumFriendlyFireSystem.cs b/sources/VintagestoryLib/Vintagestory.Server/StratumFriendlyFireSystem.cs index 1c4631e7..12b49e76 100644 --- a/sources/VintagestoryLib/Vintagestory.Server/StratumFriendlyFireSystem.cs +++ b/sources/VintagestoryLib/Vintagestory.Server/StratumFriendlyFireSystem.cs @@ -42,6 +42,7 @@ public StratumFriendlyFireSystem(ServerMain server) public static void Apply(StratumFriendlyFireConfig cfg) { StratumFriendlyFireHook.BlockGroupDamage = cfg != null && !cfg.AllowGroupDamage; + StratumHarmonyVisibility.WarnFriendlyFireConflicts(); } private bool CheckAccess(TextCommandCallingArgs args, out TextCommandResult failure) diff --git a/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs b/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs index cd45fd88..2c778c58 100644 --- a/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs +++ b/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs @@ -71,7 +71,7 @@ public static void LogPatchedMethods(ServerMain server) // Methods the group friendly fire toggle (#277) relies on to drop a hit between group mates. // A mod that Harmony-patches one of these and skips the original can silently defeat the - // toggle. No server platform can stop that, so when the toggle is on we name the mod in the + // toggle. No server platform can stop that, so when friendly fire is off we name the mod in the // log for triage. Keyed by the declaring type's simple name plus the method name, which is // enough to be unambiguous here and avoids caring about overloads or full namespaces. private static readonly HashSet FriendlyFireCriticalMethods = new HashSet(System.StringComparer.Ordinal) @@ -92,8 +92,8 @@ public static void LogPatchedMethods(ServerMain server) }; // Runs regardless of Diagnostics.LogModHarmonyPatches: this is a targeted safety warning for - // one feature, not the full patch dump. Only emits anything when /friendlyfire is off (the - // toggle is active) and a mod actually patches one of the methods it depends on. + // one feature, not the full patch dump. It is called after the friendly-fire state is applied, + // including startup, /friendlyfire changes, and /stratum reload. public static void WarnFriendlyFireConflicts() { if (!StratumFriendlyFireHook.BlockGroupDamage) @@ -120,7 +120,6 @@ public static void WarnFriendlyFireConflicts() CollectOwners(owners, info.Postfixes); CollectOwners(owners, info.Transpilers); CollectOwners(owners, info.Finalizers); - owners.Remove("(unknown)"); if (owners.Count == 0) { continue; From 2a00622c527de20c384f041ddef74c3aaf3d77eb Mon Sep 17 00:00:00 2001 From: Zaldaryon <273555259+Zaldaryon@users.noreply.github.com> Date: Tue, 8 Sep 2026 20:53:44 -0300 Subject: [PATCH 3/3] Recheck Harmony conflicts after mod loading --- .../VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs | 1 + .../Vintagestory.Server/StratumHarmonyVisibility.cs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs b/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs index d517c57f..2cc1e590 100644 --- a/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs +++ b/sources/VintagestoryLib/Vintagestory.Server/ServerSystemStratum.cs @@ -106,6 +106,7 @@ public override void OnBeginRunGame() StratumUpdateChecker.Start(server); StratumServerStats.Start(server); StratumHarmonyVisibility.LogPatchedMethods(server); + StratumHarmonyVisibility.WarnFriendlyFireConflicts(); if (StratumRuntime.Config.Backup.Enabled) { new StratumBackupScheduler(server); diff --git a/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs b/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs index 2c778c58..323dd4ef 100644 --- a/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs +++ b/sources/VintagestoryLib/Vintagestory.Server/StratumHarmonyVisibility.cs @@ -84,7 +84,6 @@ public static void LogPatchedMethods(ServerMain server) "EntityPlayer.ShouldReceiveDamage", "EntityAgent.OnInteract", "EntityBehaviorHealth.OnEntityReceiveDamage", - "ServerMain.CreateExplosion", "ServerSystemEntitySimulation.HandleEntityInteraction", "EntityProjectileBase.CanDealDamage", "EntityProjectileBase.DealDamage",