From 272b14f0e0b27ec024490cb27723995e671bc7d3 Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Mon, 9 Mar 2026 09:35:36 -0500 Subject: [PATCH 1/6] Change murders to check with the host beforehand --- MiraAPI/Networking/CustomMurderRpc.cs | 59 ++++++++++++++++++++++++--- MiraAPI/Networking/MiraRpc.cs | 5 +++ 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/MiraAPI/Networking/CustomMurderRpc.cs b/MiraAPI/Networking/CustomMurderRpc.cs index 7629ebc..b0ca585 100644 --- a/MiraAPI/Networking/CustomMurderRpc.cs +++ b/MiraAPI/Networking/CustomMurderRpc.cs @@ -7,10 +7,12 @@ using Assets.CoreScripts; using BepInEx.Unity.IL2CPP.Utils; using MiraAPI.Events; using MiraAPI.Events.Vanilla.Gameplay; +using MiraAPI.Utilities; using Reactor.Networking.Attributes; using Reactor.Networking.Rpc; using Reactor.Utilities; using Reactor.Utilities.Extensions; +using Rewired; using UnityEngine; using Object = UnityEngine.Object; @@ -22,7 +24,7 @@ namespace MiraAPI.Networking; public static class CustomMurderRpc { /// - /// Networked Custom Murder method. + /// Networked Custom Murder method, ran by clients for the host to confirm kills. /// /// The killer. /// The player to murder. @@ -54,7 +56,7 @@ public static class CustomMurderRpc } /// - /// Networked Custom Murder method, which checks for meetings as well. + /// Networked Custom Murder method, which checks for meetings as well, ran by clients for the host to confirm kills. /// /// The killer. /// The player to murder. @@ -77,10 +79,6 @@ public static class CustomMurderRpc bool showKillAnim = true, bool playKillSound = true) { - if (LobbyBehaviour.Instance) - { - return; - } var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError; var beforeMurderEvent = new BeforeMurderEvent(source, target, inMeeting); @@ -96,6 +94,55 @@ public static class CustomMurderRpc murderResultFlags = MurderResultFlags.FailedError; } + if (!PlayerControl.LocalPlayer.IsHost()) + { + return; + } + + RpcConfirmCustomMurder( + PlayerControl.LocalPlayer, + source, + target, + inMeeting, + murderResultFlags, + resetKillTimer, + createDeadBody, + teleportMurderer, + showKillAnim, + playKillSound); + } + + /// + /// Networked Custom Murder method, which checks for meetings as well, ran by clients for the host to confirm kills. + /// + /// The game host. + /// The killer. + /// The player to murder. + /// Whether the murder is intended to be triggered in a meeting. + /// End result for the murder. + /// Should the kill timer be reset. + /// Should a dead body be created. + /// Should the killer be snapped to the dead player. + /// Should the kill animation be shown. + /// Should the kill sound be played. + [MethodRpc((uint)MiraRpc.ConfirmCustomMurder, LocalHandling = RpcLocalHandling.Before)] + public static void RpcConfirmCustomMurder( + this PlayerControl host, + PlayerControl source, + PlayerControl target, + MeetingCheck inMeeting, + MurderResultFlags murderResultFlags, + bool resetKillTimer = true, + bool createDeadBody = true, + bool teleportMurderer = true, + bool showKillAnim = true, + bool playKillSound = true) + { + if (LobbyBehaviour.Instance || !host.IsHost()) + { + return; + } + var murderResultFlags2 = MurderResultFlags.DecisionByHost | murderResultFlags; source.CustomMurder( diff --git a/MiraAPI/Networking/MiraRpc.cs b/MiraAPI/Networking/MiraRpc.cs index 28c6c7a..edc3a94 100644 --- a/MiraAPI/Networking/MiraRpc.cs +++ b/MiraAPI/Networking/MiraRpc.cs @@ -35,6 +35,11 @@ public enum MiraRpc : uint /// CustomMurder, + /// + /// Custom Murder RPC Confirmation. + /// + ConfirmCustomMurder, + /// /// Custom Game Over RPC. /// From 1813fe5a8414b5deeecaec41c1c2a8ce46d95e06 Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Mon, 9 Mar 2026 09:43:00 -0500 Subject: [PATCH 2/6] Adjust functions --- MiraAPI/Networking/CustomMurderRpc.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/MiraAPI/Networking/CustomMurderRpc.cs b/MiraAPI/Networking/CustomMurderRpc.cs index b0ca585..379daf7 100644 --- a/MiraAPI/Networking/CustomMurderRpc.cs +++ b/MiraAPI/Networking/CustomMurderRpc.cs @@ -103,7 +103,6 @@ public static class CustomMurderRpc PlayerControl.LocalPlayer, source, target, - inMeeting, murderResultFlags, resetKillTimer, createDeadBody, @@ -118,7 +117,6 @@ public static class CustomMurderRpc /// The game host. /// The killer. /// The player to murder. - /// Whether the murder is intended to be triggered in a meeting. /// End result for the murder. /// Should the kill timer be reset. /// Should a dead body be created. @@ -130,7 +128,6 @@ public static class CustomMurderRpc this PlayerControl host, PlayerControl source, PlayerControl target, - MeetingCheck inMeeting, MurderResultFlags murderResultFlags, bool resetKillTimer = true, bool createDeadBody = true, From 8940de76bb5d50d18863775bb34a4e3238f33c31 Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Tue, 10 Mar 2026 00:18:55 -0500 Subject: [PATCH 3/6] Add one last murder check --- MiraAPI/Networking/CustomMurderRpc.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MiraAPI/Networking/CustomMurderRpc.cs b/MiraAPI/Networking/CustomMurderRpc.cs index 379daf7..8389ae1 100644 --- a/MiraAPI/Networking/CustomMurderRpc.cs +++ b/MiraAPI/Networking/CustomMurderRpc.cs @@ -79,6 +79,10 @@ public static class CustomMurderRpc bool showKillAnim = true, bool playKillSound = true) { + if (LobbyBehaviour.Instance) + { + return; + } var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError; var beforeMurderEvent = new BeforeMurderEvent(source, target, inMeeting); From 9270de408de52eba1f93f87946e557e7936d45be Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Tue, 10 Mar 2026 18:04:22 -0500 Subject: [PATCH 4/6] Verify that target isn't already dead or dc'd --- MiraAPI/Networking/CustomMurderRpc.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MiraAPI/Networking/CustomMurderRpc.cs b/MiraAPI/Networking/CustomMurderRpc.cs index 8389ae1..1e3f08f 100644 --- a/MiraAPI/Networking/CustomMurderRpc.cs +++ b/MiraAPI/Networking/CustomMurderRpc.cs @@ -139,7 +139,7 @@ public static class CustomMurderRpc bool showKillAnim = true, bool playKillSound = true) { - if (LobbyBehaviour.Instance || !host.IsHost()) + if (LobbyBehaviour.Instance || !host.IsHost() || target.Data.IsDead || target.Data.Disconnected) { return; } From dcb4a3876f7c991eee5b0c423c7a0939e6966621 Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Wed, 11 Mar 2026 00:23:32 -0500 Subject: [PATCH 5/6] Fix neutral color in view pane --- .../Patches/Options/LobbyViewPanePatches.cs | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs index 32c0d2a..4a629f5 100644 --- a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs +++ b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs @@ -210,12 +210,19 @@ public static class LobbyViewPanePatches list.Add(roleBehaviour); } + var color = (i == 0) ? Palette.CrewmateRoleBlue : Palette.ImpostorRoleRed; + if (roleBehaviour is ICustomRole custom && (custom.Team is not ModdedRoleTeams.Crewmate && + custom.Team is not ModdedRoleTeams.Impostor)) + { + color = Color.grey; + } + viewSettingsInfoPanelRoleVariant.SetInfo( roleBehaviour.NiceName, numPerGame, chancePerGame, 61, - (i == 0) ? Palette.CrewmateRoleBlue : Palette.ImpostorRoleRed, + color, roleBehaviour.RoleIconSolid, i == 0, flag); @@ -513,6 +520,34 @@ public static class LobbyViewPanePatches instance.scrollBar.SetYBoundsMax(-num); } + public static void SetModdedHeader(this CategoryHeaderRoleVariant header, StringNames roleName, int maskLayer, ModdedRoleTeams team, Sprite? roleIcon = null) + { + header.SetHeader(roleName, maskLayer); + if (team is ModdedRoleTeams.Crewmate) + { + header.Background.color = Palette.CrewmateRoleHeaderBlue; + header.Divider.color = Palette.CrewmateRoleHeaderBlue; + header.Title.color = Palette.CrewmateRoleHeaderTextBlue; + } + else if (team is ModdedRoleTeams.Impostor) + { + header.Background.color = Palette.ImpostorRoleHeaderRed; + header.Divider.color = Palette.ImpostorRoleHeaderRed; + header.Title.color = Palette.ImpostorRoleHeaderTextRed; + } + else + { + header.Background.color = Color.grey; + header.Divider.color = Color.grey; + header.Title.color = new Color32(50, 50, 50, 255); + } + if (roleIcon != null && header.icon != null) + { + header.icon.material.SetInt(PlayerMaterial.MaskLayer, maskLayer); + header.icon.sprite = roleIcon; + } + } + private static float SetUpAdvancedRoleViewPanel( AdvancedRoleViewPanel viewPanel, Type roleType, @@ -536,10 +571,10 @@ public static class LobbyViewPanePatches return 0; } - viewPanel.header.SetHeader( + viewPanel.header.SetModdedHeader( role.StringName, maskLayer, - role.TeamType == RoleTeamTypes.Crewmate, + customRole.Team, customRole.Configuration.Icon != null ? customRole.Configuration.Icon.LoadAsset() : MiraAssets.Empty.LoadAsset()); viewPanel.header.icon.transform.localScale = new Vector3(0.465f, 0.465f, 1f); viewPanel.divider.material.SetInt(PlayerMaterial.MaskLayer, maskLayer); From 49d77fcf40a62dd6a52af6ec588fc6b4f9d4e5ad Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Wed, 11 Mar 2026 15:40:50 -0500 Subject: [PATCH 6/6] Fix issues with PlayerControl.Die race condition --- MiraAPI/Networking/CustomMurderRpc.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MiraAPI/Networking/CustomMurderRpc.cs b/MiraAPI/Networking/CustomMurderRpc.cs index 1e3f08f..d31e185 100644 --- a/MiraAPI/Networking/CustomMurderRpc.cs +++ b/MiraAPI/Networking/CustomMurderRpc.cs @@ -127,7 +127,7 @@ public static class CustomMurderRpc /// Should the killer be snapped to the dead player. /// Should the kill animation be shown. /// Should the kill sound be played. - [MethodRpc((uint)MiraRpc.ConfirmCustomMurder, LocalHandling = RpcLocalHandling.Before)] + [MethodRpc((uint)MiraRpc.ConfirmCustomMurder, LocalHandling = RpcLocalHandling.After)] public static void RpcConfirmCustomMurder( this PlayerControl host, PlayerControl source,