From e0b737ca0b0a8a07d1d321e2bc04a689afd7a8ef Mon Sep 17 00:00:00 2001 From: MS-crew <100300664+MS-crew@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:12:33 +0300 Subject: [PATCH 01/12] Update MirrorExtensions.cs Fix item owner being done in the wrong order Revert "Fix item owner being done in the wrong order" This reverts commit e8cfd54e2769cd39e93f8510bbd3148aa2a1b644. Update VoiceChatting.cs Update and rename VoiceChattingEventArgs.cs to SendingVoiceMessageEventArgs.cs Create ReceivingVoiceMessageEventArgs.cs Update Player.cs Update and rename ChangingSpeakerStatusAndVoiceChatting.cs to ChangingSpeakerStatus.cs Delete EXILED/Exiled.Events/EventArgs/Player/TransmittingEventArgs.cs Update Player.cs Create TransmittingEventArgs.cs Update Player.cs Update Player.cs Update Player.cs Update Player.cs Create VoiceChattingEventArgs.cs Update TransmittingEventArgs.cs Update Player.cs Update ReceivingVoiceMessageEventArgs.cs Update SendingVoiceMessageEventArgs.cs Update SendingVoiceMessageEventArgs.cs Update Player.cs Update Events.cs Update VoiceChatting.cs Update VoiceChatting.cs Update VoiceChatting.cs Update SendingVoiceMessageEventArgs.cs Update TransmittingEventArgs.cs Reverted braking changes deleted useless . doc checked its working / --- .../Player/ReceivingVoiceMessageEventArgs.cs | 68 ++++++++ .../EventArgs/Player/TransmittingEventArgs.cs | 17 +- .../Player/VoiceChattingEventArgs.cs | 8 +- EXILED/Exiled.Events/Events.cs | 4 + EXILED/Exiled.Events/Handlers/Player.cs | 38 ++++- .../Patches/Events/Player/VoiceChatting.cs | 150 ------------------ ...ceChatting.cs => ChangingSpeakerStatus.cs} | 9 +- 7 files changed, 128 insertions(+), 166 deletions(-) create mode 100644 EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs delete mode 100644 EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs rename EXILED/Exiled.Events/Patches/Events/Scp079/{ChangingSpeakerStatusAndVoiceChatting.cs => ChangingSpeakerStatus.cs} (92%) diff --git a/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs new file mode 100644 index 0000000000..22fea56160 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs @@ -0,0 +1,68 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// -----------------------------------------------------------------------using System; + +namespace Exiled.Events.EventArgs.Player +{ + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + using PlayerRoles.Voice; + using VoiceChat.Networking; + + using IVoiceRole = API.Features.Roles.IVoiceRole; + + /// + /// Contains all information before player receiving a voice message. + /// + public class ReceivingVoiceMessageEventArgs : IPlayerEvent, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// The voice message being sent. + /// The player receiving the voice message. + /// The player sending the voice message. + /// A value indicating whether the player is allowed to receive the voice message. + public ReceivingVoiceMessageEventArgs(VoiceMessage voiceMessage, Player receiver, Player sender, bool isAllowed) + { + Player = receiver; + VoiceMessage = voiceMessage; + Sender = sender; + + if (Sender.Role is IVoiceRole iVR) + { + VoiceModule = iVR.VoiceModule; + } + + IsAllowed = isAllowed; + } + + /// + /// Gets the player receiving the voice message. + /// + public Player Player { get; } + + /// + /// Gets the player sended the voice message. + /// + public Player Sender { get; } + + /// + /// Gets or sets the 's . + /// + public VoiceMessage VoiceMessage { get; set; } + + /// + /// Gets the 's . + /// + public VoiceModuleBase VoiceModule { get; } + + /// + /// Gets or sets a value indicating whether the player can receive the voice message. + /// + public bool IsAllowed { get; set; } + } +} diff --git a/EXILED/Exiled.Events/EventArgs/Player/TransmittingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/TransmittingEventArgs.cs index 96e1ffde45..5ccc41bb2a 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/TransmittingEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/TransmittingEventArgs.cs @@ -12,6 +12,8 @@ namespace Exiled.Events.EventArgs.Player using PlayerRoles.Voice; + using VoiceChat.Networking; + /// /// Contains all information regarding the player using the radio. /// @@ -23,16 +25,18 @@ public class TransmittingEventArgs : IPlayerEvent, IDeniableEvent /// /// /// - /// - /// + /// + /// /// /// /// /// - public TransmittingEventArgs(Player player, VoiceModuleBase voiceModule, bool isAllowed = true) + public TransmittingEventArgs(Player player, VoiceMessage voiceMessage, bool isAllowed) { Player = player; - VoiceModule = voiceModule; + VoiceMessage = voiceMessage; + if (player.Role is IVoiceRole voiceRole) + VoiceModule = voiceRole.VoiceModule; IsAllowed = isAllowed; } @@ -41,6 +45,11 @@ public TransmittingEventArgs(Player player, VoiceModuleBase voiceModule, bool is /// public Player Player { get; } + /// + /// Gets or sets the 's . + /// + public VoiceMessage VoiceMessage { get; set; } + /// /// Gets the 's . /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/VoiceChattingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/VoiceChattingEventArgs.cs index c67e7bb72f..c19601c694 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/VoiceChattingEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/VoiceChattingEventArgs.cs @@ -28,17 +28,15 @@ public class VoiceChattingEventArgs : IPlayerEvent, IDeniableEvent /// /// /// - /// - /// - /// /// /// /// - public VoiceChattingEventArgs(Player player, VoiceMessage voiceMessage, VoiceModuleBase voiceModule, bool isAllowed = true) + public VoiceChattingEventArgs(Player player, VoiceMessage voiceMessage, bool isAllowed) { Player = player; VoiceMessage = voiceMessage; - VoiceModule = voiceModule; + if (player.Role is IVoiceRole voiceRole) + VoiceModule = voiceRole.VoiceModule; IsAllowed = isAllowed; } diff --git a/EXILED/Exiled.Events/Events.cs b/EXILED/Exiled.Events/Events.cs index c941abd7ca..3047f06d17 100644 --- a/EXILED/Exiled.Events/Events.cs +++ b/EXILED/Exiled.Events/Events.cs @@ -92,6 +92,8 @@ public override void OnEnabled() LabApi.Events.Handlers.PlayerEvents.ReloadingWeapon += Handlers.Player.OnReloadingWeapon; LabApi.Events.Handlers.PlayerEvents.UnloadingWeapon += Handlers.Player.OnUnloadingWeapon; + LabApi.Events.Handlers.PlayerEvents.SendingVoiceMessage += Handlers.Player.OnSendingVoiceMessage; + LabApi.Events.Handlers.PlayerEvents.ReceivingVoiceMessage += Handlers.Player.OnReceivingVoiceMessage; LabApi.Events.Handlers.Scp127Events.Talking += Handlers.Scp127.OnTalking; LabApi.Events.Handlers.Scp127Events.Talked += Handlers.Scp127.OnTalked; @@ -135,6 +137,8 @@ public override void OnDisabled() LabApi.Events.Handlers.PlayerEvents.ReloadingWeapon -= Handlers.Player.OnReloadingWeapon; LabApi.Events.Handlers.PlayerEvents.UnloadingWeapon -= Handlers.Player.OnUnloadingWeapon; + LabApi.Events.Handlers.PlayerEvents.SendingVoiceMessage -= Handlers.Player.OnSendingVoiceMessage; + LabApi.Events.Handlers.PlayerEvents.ReceivingVoiceMessage -= Handlers.Player.OnReceivingVoiceMessage; LabApi.Events.Handlers.Scp127Events.Talking -= Handlers.Scp127.OnTalking; LabApi.Events.Handlers.Scp127Events.Talked -= Handlers.Scp127.OnTalked; diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs index 998b54f813..11a890b5bf 100644 --- a/EXILED/Exiled.Events/Handlers/Player.cs +++ b/EXILED/Exiled.Events/Handlers/Player.cs @@ -470,6 +470,11 @@ public class Player /// public static Event VoiceChatting { get; set; } = new(); + /// + /// Invoked before a receiving a voice message. + /// + public static Event ReceivingVoiceMessage { get; set; } = new(); + /// /// Invoked before a makes noise. /// @@ -1042,8 +1047,37 @@ public static void OnUnloadingWeapon(PlayerUnloadingWeaponEventArgs ev) /// /// Invoked after a presses the voicechat key. /// - /// The instance. - public static void OnVoiceChatting(VoiceChattingEventArgs ev) => VoiceChatting.InvokeSafely(ev); + /// The instance. + public static void OnVoiceChatting(PlayerSendingVoiceMessageEventArgs ev) + { + VoiceChattingEventArgs evVoiceChatting = new(ev.Player, ev.Message, ev.IsAllowed); + VoiceChatting.InvokeSafely(evVoiceChatting); + + ev.IsAllowed = evVoiceChatting.IsAllowed; + ev.Message = evVoiceChatting.VoiceMessage; + + if(ev.Message.Channel == VoiceChat.VoiceChatChannel.Radio) + { + TransmittingEventArgs evTransmitting = new(ev.Player, ev.Message, ev.IsAllowed); + OnTransmitting(evTransmitting); + + ev.Message = evTransmitting.VoiceMessage; + ev.IsAllowed = evTransmitting.IsAllowed; + } + } + + /// + /// Invoked before a receiving a voice message. + /// + /// The instance. + public static void OnReceivingVoiceMessage(PlayerReceivingVoiceMessageEventArgs ev) + { + ReceivingVoiceMessageEventArgs evReceivingVoiceMessage = new(ev.Message, ev.Player, ev.Sender, ev.IsAllowed); + ReceivingVoiceMessage.InvokeSafely(evReceivingVoiceMessage); + + ev.IsAllowed = evReceivingVoiceMessage.IsAllowed; + ev.Message = evReceivingVoiceMessage.VoiceMessage; + } /// /// Called before a makes noise. diff --git a/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs b/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs deleted file mode 100644 index a6d49a70f9..0000000000 --- a/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs +++ /dev/null @@ -1,150 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.Events.Patches.Events.Player -{ - using System.Collections.Generic; - using System.Reflection.Emit; - - using API.Features.Pools; - using API.Features.Roles; - using Exiled.Events.Attributes; - using Exiled.Events.EventArgs.Player; - - using HarmonyLib; - - using Mirror; - - using PlayerRoles.Voice; - - using VoiceChat; - using VoiceChat.Networking; - - using static HarmonyLib.AccessTools; - - /// - /// Patches . - /// Adds the event. - /// Adds the event. - /// - [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.VoiceChatting))] - [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.Transmitting))] - [HarmonyPatch(typeof(VoiceTransceiver), nameof(VoiceTransceiver.ServerReceiveMessage))] - internal static class VoiceChatting - { - private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) - { - List newInstructions = ListPool.Pool.Get(instructions); - - Label retLabel = generator.DefineLabel(); - Label isMutedLabel = generator.DefineLabel(); - Label skipLabel = generator.DefineLabel(); - List public static IReadOnlyDictionary> Dictionary => TypeToEvent; + /// + /// Gets a value indicating whether this event has any subscribers. + /// + public bool HasSubscribers => innerEvent.Count > 0 || innerAsyncEvent.Count > 0; + /// /// Subscribes a target to the inner event and checks if patching is possible, if dynamic patching is enabled. /// @@ -296,4 +301,4 @@ internal void InvokeAsync(T arg) } } } -} +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs index 11a890b5bf..70d2470871 100644 --- a/EXILED/Exiled.Events/Handlers/Player.cs +++ b/EXILED/Exiled.Events/Handlers/Player.cs @@ -902,6 +902,9 @@ public class Player /// The instance. public static void OnReloadingWeapon(PlayerReloadingWeaponEventArgs ev) { + if (!ReloadingWeapon.HasSubscribers) + return; + ReloadingWeaponEventArgs exiledEv = new(ev.FirearmItem.Base, ev.IsAllowed); ReloadingWeapon.InvokeSafely(exiledEv); ev.IsAllowed = exiledEv.IsAllowed; @@ -1015,6 +1018,9 @@ public static void OnReloadingWeapon(PlayerReloadingWeaponEventArgs ev) /// The instance. public static void OnUnloadingWeapon(PlayerUnloadingWeaponEventArgs ev) { + if (!UnloadingWeapon.HasSubscribers) + return; + UnloadingWeaponEventArgs exiledEv = new(ev.FirearmItem.Base, ev.IsAllowed); UnloadingWeapon.InvokeSafely(exiledEv); ev.IsAllowed = exiledEv.IsAllowed; @@ -1050,13 +1056,16 @@ public static void OnUnloadingWeapon(PlayerUnloadingWeaponEventArgs ev) /// The instance. public static void OnVoiceChatting(PlayerSendingVoiceMessageEventArgs ev) { - VoiceChattingEventArgs evVoiceChatting = new(ev.Player, ev.Message, ev.IsAllowed); - VoiceChatting.InvokeSafely(evVoiceChatting); + if (VoiceChatting.HasSubscribers) + { + VoiceChattingEventArgs evVoiceChatting = new(ev.Player, ev.Message, ev.IsAllowed); + VoiceChatting.InvokeSafely(evVoiceChatting); - ev.IsAllowed = evVoiceChatting.IsAllowed; - ev.Message = evVoiceChatting.VoiceMessage; + ev.IsAllowed = evVoiceChatting.IsAllowed; + ev.Message = evVoiceChatting.VoiceMessage; + } - if(ev.Message.Channel == VoiceChat.VoiceChatChannel.Radio) + if(ev.Message.Channel == VoiceChat.VoiceChatChannel.Radio && Transmitting.HasSubscribers) { TransmittingEventArgs evTransmitting = new(ev.Player, ev.Message, ev.IsAllowed); OnTransmitting(evTransmitting); @@ -1072,6 +1081,9 @@ public static void OnVoiceChatting(PlayerSendingVoiceMessageEventArgs ev) /// The instance. public static void OnReceivingVoiceMessage(PlayerReceivingVoiceMessageEventArgs ev) { + if (!ReceivingVoiceMessage.HasSubscribers) + return; + ReceivingVoiceMessageEventArgs evReceivingVoiceMessage = new(ev.Message, ev.Player, ev.Sender, ev.IsAllowed); ReceivingVoiceMessage.InvokeSafely(evReceivingVoiceMessage); From 82439a445b47d1c38a0c4747d51bfef64d6e90ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20SAVA=C5=9E?= Date: Sat, 31 Jan 2026 01:13:18 +0300 Subject: [PATCH 04/12] update --- .../Player/ReceivingVoiceMessageEventArgs.cs | 20 +- .../EventArgs/Player/TransmittingEventArgs.cs | 12 +- .../Player/VoiceChattingEventArgs.cs | 14 +- EXILED/Exiled.Events/Events.cs | 4 - EXILED/Exiled.Events/Handlers/Player.cs | 35 +-- .../Patches/Events/Player/VoiceChatting.cs | 214 ++++++++++++++++++ 6 files changed, 235 insertions(+), 64 deletions(-) create mode 100644 EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs diff --git a/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs index 22fea56160..d1063e75ba 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs @@ -12,8 +12,6 @@ namespace Exiled.Events.EventArgs.Player using PlayerRoles.Voice; using VoiceChat.Networking; - using IVoiceRole = API.Features.Roles.IVoiceRole; - /// /// Contains all information before player receiving a voice message. /// @@ -25,19 +23,15 @@ public class ReceivingVoiceMessageEventArgs : IPlayerEvent, IDeniableEvent /// The voice message being sent. /// The player receiving the voice message. /// The player sending the voice message. - /// A value indicating whether the player is allowed to receive the voice message. - public ReceivingVoiceMessageEventArgs(VoiceMessage voiceMessage, Player receiver, Player sender, bool isAllowed) + /// + /// + /// + public ReceivingVoiceMessageEventArgs(Player receiver, Player sender, VoiceModuleBase voiceModule, VoiceMessage voiceMessage) { + Sender = sender; Player = receiver; VoiceMessage = voiceMessage; - Sender = sender; - - if (Sender.Role is IVoiceRole iVR) - { - VoiceModule = iVR.VoiceModule; - } - - IsAllowed = isAllowed; + VoiceModule = voiceModule; } /// @@ -63,6 +57,6 @@ public ReceivingVoiceMessageEventArgs(VoiceMessage voiceMessage, Player receiver /// /// Gets or sets a value indicating whether the player can receive the voice message. /// - public bool IsAllowed { get; set; } + public bool IsAllowed { get; set; } = true; } } diff --git a/EXILED/Exiled.Events/EventArgs/Player/TransmittingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/TransmittingEventArgs.cs index 5ccc41bb2a..6cfbf1106a 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/TransmittingEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/TransmittingEventArgs.cs @@ -28,16 +28,14 @@ public class TransmittingEventArgs : IPlayerEvent, IDeniableEvent /// /// /// - /// - /// + /// + /// /// - public TransmittingEventArgs(Player player, VoiceMessage voiceMessage, bool isAllowed) + public TransmittingEventArgs(Player player, VoiceMessage voiceMessage, VoiceModuleBase voiceModule) { Player = player; VoiceMessage = voiceMessage; - if (player.Role is IVoiceRole voiceRole) - VoiceModule = voiceRole.VoiceModule; - IsAllowed = isAllowed; + VoiceModule = voiceModule; } /// @@ -58,6 +56,6 @@ public TransmittingEventArgs(Player player, VoiceMessage voiceMessage, bool isAl /// /// Gets or sets a value indicating whether the player can transmit. /// - public bool IsAllowed { get; set; } + public bool IsAllowed { get; set; } = true; } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Player/VoiceChattingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/VoiceChattingEventArgs.cs index c19601c694..5a488c3d2e 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/VoiceChattingEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/VoiceChattingEventArgs.cs @@ -25,19 +25,17 @@ public class VoiceChattingEventArgs : IPlayerEvent, IDeniableEvent /// /// /// + /// + /// + /// /// /// /// - /// - /// - /// - public VoiceChattingEventArgs(Player player, VoiceMessage voiceMessage, bool isAllowed) + public VoiceChattingEventArgs(Player player, VoiceModuleBase voiceModule, VoiceMessage voiceMessage) { Player = player; VoiceMessage = voiceMessage; - if (player.Role is IVoiceRole voiceRole) - VoiceModule = voiceRole.VoiceModule; - IsAllowed = isAllowed; + VoiceModule = voiceModule; } /// @@ -58,6 +56,6 @@ public VoiceChattingEventArgs(Player player, VoiceMessage voiceMessage, bool isA /// /// Gets or sets a value indicating whether the player can voicechat. /// - public bool IsAllowed { get; set; } + public bool IsAllowed { get; set; } = true; } } diff --git a/EXILED/Exiled.Events/Events.cs b/EXILED/Exiled.Events/Events.cs index d8cc7b960f..c941abd7ca 100644 --- a/EXILED/Exiled.Events/Events.cs +++ b/EXILED/Exiled.Events/Events.cs @@ -92,8 +92,6 @@ public override void OnEnabled() LabApi.Events.Handlers.PlayerEvents.ReloadingWeapon += Handlers.Player.OnReloadingWeapon; LabApi.Events.Handlers.PlayerEvents.UnloadingWeapon += Handlers.Player.OnUnloadingWeapon; - LabApi.Events.Handlers.PlayerEvents.SendingVoiceMessage += Handlers.Player.OnVoiceChatting; - LabApi.Events.Handlers.PlayerEvents.ReceivingVoiceMessage += Handlers.Player.OnReceivingVoiceMessage; LabApi.Events.Handlers.Scp127Events.Talking += Handlers.Scp127.OnTalking; LabApi.Events.Handlers.Scp127Events.Talked += Handlers.Scp127.OnTalked; @@ -137,8 +135,6 @@ public override void OnDisabled() LabApi.Events.Handlers.PlayerEvents.ReloadingWeapon -= Handlers.Player.OnReloadingWeapon; LabApi.Events.Handlers.PlayerEvents.UnloadingWeapon -= Handlers.Player.OnUnloadingWeapon; - LabApi.Events.Handlers.PlayerEvents.SendingVoiceMessage -= Handlers.Player.OnVoiceChatting; - LabApi.Events.Handlers.PlayerEvents.ReceivingVoiceMessage -= Handlers.Player.OnReceivingVoiceMessage; LabApi.Events.Handlers.Scp127Events.Talking -= Handlers.Scp127.OnTalking; LabApi.Events.Handlers.Scp127Events.Talked -= Handlers.Scp127.OnTalked; diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs index 70d2470871..362cec2a3a 100644 --- a/EXILED/Exiled.Events/Handlers/Player.cs +++ b/EXILED/Exiled.Events/Handlers/Player.cs @@ -1053,43 +1053,14 @@ public static void OnUnloadingWeapon(PlayerUnloadingWeaponEventArgs ev) /// /// Invoked after a presses the voicechat key. /// - /// The instance. - public static void OnVoiceChatting(PlayerSendingVoiceMessageEventArgs ev) - { - if (VoiceChatting.HasSubscribers) - { - VoiceChattingEventArgs evVoiceChatting = new(ev.Player, ev.Message, ev.IsAllowed); - VoiceChatting.InvokeSafely(evVoiceChatting); - - ev.IsAllowed = evVoiceChatting.IsAllowed; - ev.Message = evVoiceChatting.VoiceMessage; - } - - if(ev.Message.Channel == VoiceChat.VoiceChatChannel.Radio && Transmitting.HasSubscribers) - { - TransmittingEventArgs evTransmitting = new(ev.Player, ev.Message, ev.IsAllowed); - OnTransmitting(evTransmitting); - - ev.Message = evTransmitting.VoiceMessage; - ev.IsAllowed = evTransmitting.IsAllowed; - } - } + /// The instance. + public static void OnVoiceChatting(VoiceChattingEventArgs ev) => VoiceChatting.InvokeSafely(ev); /// /// Invoked before a receiving a voice message. /// /// The instance. - public static void OnReceivingVoiceMessage(PlayerReceivingVoiceMessageEventArgs ev) - { - if (!ReceivingVoiceMessage.HasSubscribers) - return; - - ReceivingVoiceMessageEventArgs evReceivingVoiceMessage = new(ev.Message, ev.Player, ev.Sender, ev.IsAllowed); - ReceivingVoiceMessage.InvokeSafely(evReceivingVoiceMessage); - - ev.IsAllowed = evReceivingVoiceMessage.IsAllowed; - ev.Message = evReceivingVoiceMessage.VoiceMessage; - } + public static void OnReceivingVoiceMessage(ReceivingVoiceMessageEventArgs ev) => ReceivingVoiceMessage.InvokeSafely(ev); /// /// Called before a makes noise. diff --git a/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs b/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs new file mode 100644 index 0000000000..bdee7fe5eb --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs @@ -0,0 +1,214 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- +#pragma warning disable SA1402 // File may only contain a single type +namespace Exiled.Events.Patches.Events.Player +{ + using System; + using System.Collections.Generic; + using System.Reflection; + using System.Reflection.Emit; + + using API.Features.Pools; + + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Player; + + using HarmonyLib; + + using Mirror; + + using PlayerRoles.Voice; + + using VoiceChat; + using VoiceChat.Networking; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Adds the event. + /// Adds the event. + /// + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.VoiceChatting))] + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.Transmitting))] + [HarmonyPatch(typeof(VoiceTransceiver), nameof(VoiceTransceiver.ServerReceiveMessage))] + internal static class VoiceChatting + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label retLabel = generator.DefineLabel(); + Label skipLabel = generator.DefineLabel(); + + LocalBuilder player = generator.DeclareLocal(typeof(API.Features.Player)); + LocalBuilder voiceModule = generator.DeclareLocal(typeof(VoiceModuleBase)); + LocalBuilder evTransmitting = generator.DeclareLocal(typeof(TransmittingEventArgs)); + LocalBuilder evVoiceChatting = generator.DeclareLocal(typeof(VoiceChattingEventArgs)); + + int offset = -1; + int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Newobj && (ConstructorInfo)i.operand == GetDeclaredConstructors(typeof(LabApi.Events.Arguments.PlayerEvents.PlayerSendingVoiceMessageEventArgs))[0]) + offset; + + newInstructions[index].labels.Add(skipLabel); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // Player.Get(msg.Speaker); + new(OpCodes.Ldarg_1), + new(OpCodes.Ldfld, Field(typeof(VoiceMessage), nameof(VoiceMessage.Speaker))), + new(OpCodes.Call, Method(typeof(API.Features.Player), nameof(API.Features.Player.Get), new[] { typeof(ReferenceHub) })), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, player.LocalIndex), + + // voiceModule + new(OpCodes.Ldloc_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(IVoiceRole), nameof(IVoiceRole.VoiceModule))), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, voiceModule.LocalIndex), + + // msg + new(OpCodes.Ldarg_1), + + // VoiceChattingEventArgs ev = new(Player, VoiceModuleBase, VoiceMessage); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(VoiceChattingEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, evVoiceChatting.LocalIndex), + + // Handlers.Player.OnVoiceChatting(ev); + new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnVoiceChatting))), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(VoiceChattingEventArgs), nameof(VoiceChattingEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + + // msg = ev.VoiceMessage; + new(OpCodes.Ldloc_S, evVoiceChatting.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(VoiceChattingEventArgs), nameof(VoiceChattingEventArgs.VoiceMessage))), + new(OpCodes.Starg_S, 1), + + // if(voiceModule.CurrentChannel != VoiceChatChannel.Radio) + // goto skipLabel; + new(OpCodes.Ldloc_S, voiceModule.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(VoiceModuleBase), nameof(VoiceModuleBase.CurrentChannel))), + new(OpCodes.Ldc_I4_S, (sbyte)VoiceChatChannel.Radio), + new(OpCodes.Ceq), + new(OpCodes.Brfalse_S, skipLabel), + + // player + new(OpCodes.Ldloc_S, player.LocalIndex), + + // msg + new(OpCodes.Ldarg_1), + + // voiceModule + new(OpCodes.Ldloc_S, voiceModule.LocalIndex), + + // TransmittingEventArgs ev = new TransmittingEventArgs(Player, VoiceMessage, VoiceModuleBase) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(TransmittingEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, evTransmitting.LocalIndex), + + // Handlers.Player.OnTransmitting(ev); + new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnTransmitting))), + + // if(!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(TransmittingEventArgs), nameof(TransmittingEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + + // msg = ev.VoiceMessage; + new(OpCodes.Ldloc_S, evTransmitting.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(TransmittingEventArgs), nameof(TransmittingEventArgs.VoiceMessage))), + new(OpCodes.Starg_S, 1), + }); + + newInstructions[newInstructions.Count - 1].WithLabels(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } + + /// + /// Patches . + /// Adds the event. + /// + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.ReceivingVoiceMessage))] + [HarmonyPatch(typeof(VoiceTransceiver), nameof(VoiceTransceiver.ServerReceiveMessage))] + + internal static class ReceivingVoiceMessage + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label skipHub = generator.DefineLabel(); + Label skipLabel = generator.DefineLabel(); + + LocalBuilder ev = generator.DeclareLocal(typeof(TransmittingEventArgs)); + + int offset = -2; + int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Newobj && (ConstructorInfo)i.operand == GetDeclaredConstructors(typeof(LabApi.Events.Arguments.PlayerEvents.PlayerReceivingVoiceMessageEventArgs))[0]) + offset; + + newInstructions[index].labels.Add(skipLabel); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // Player.Get(hub); + new(OpCodes.Ldloc_S, 4), + new(OpCodes.Call, Method(typeof(API.Features.Player), nameof(API.Features.Player.Get), new[] { typeof(ReferenceHub) })), + + // Player.Get(mgs.Speaker); + new(OpCodes.Ldarg_1), + new(OpCodes.Ldfld, Field(typeof(VoiceMessage), nameof(VoiceMessage.Speaker))), + new(OpCodes.Call, Method(typeof(API.Features.Player), nameof(API.Features.Player.Get), new[] { typeof(ReferenceHub) })), + + // voiceModule + new(OpCodes.Ldloc_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(IVoiceRole), nameof(IVoiceRole.VoiceModule))), + + // msg + new(OpCodes.Ldarg_1), + + // ReceivingVoiceMessageEventArgs ev = new(Player receiver, Player sender, VoiceModuleBase, VoiceMessage); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ReceivingVoiceMessageEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, ev.LocalIndex), + + // Handlers.Player.OnVoiceChatting(ev); + new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnReceivingVoiceMessage))), + + // if (!ev.IsAllowed) + // contunie; + new(OpCodes.Callvirt, PropertyGetter(typeof(ReceivingVoiceMessageEventArgs), nameof(ReceivingVoiceMessageEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, skipHub), + + // msg = ev.VoiceMessage; + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(ReceivingVoiceMessageEventArgs), nameof(ReceivingVoiceMessageEventArgs.VoiceMessage))), + new(OpCodes.Starg_S, 1), + }); + + offset = 1; + index = newInstructions.FindIndex(i => i.opcode == OpCodes.Callvirt && i.operand is MethodInfo mi && mi.Name == nameof(NetworkConnection.Send) && mi.IsGenericMethod) + offset; + + newInstructions[index].labels.Add(skipHub); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} +#pragma warning restore SA1402 // File may only contain a single type \ No newline at end of file From dafe93474eb5066ca8fcaa725b81f64c3be35d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20SAVA=C5=9E?= Date: Sat, 31 Jan 2026 12:28:47 +0300 Subject: [PATCH 05/12] pollable for hot events Update IPoolableEvent.cs --- .../EventArgs/Interfaces/IPoolableEvent.cs | 20 ++++++ .../Player/ReceivingVoiceMessageEventArgs.cs | 70 ++++++++++++++++--- EXILED/Exiled.Events/Handlers/Player.cs | 2 +- .../Patches/Events/Player/VoiceChatting.cs | 28 ++++++-- 4 files changed, 105 insertions(+), 15 deletions(-) create mode 100644 EXILED/Exiled.Events/EventArgs/Interfaces/IPoolableEvent.cs diff --git a/EXILED/Exiled.Events/EventArgs/Interfaces/IPoolableEvent.cs b/EXILED/Exiled.Events/EventArgs/Interfaces/IPoolableEvent.cs new file mode 100644 index 0000000000..18118f0337 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IPoolableEvent.cs @@ -0,0 +1,20 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Interfaces +{ + /// + /// Represents an event argument that supports object pooling. + /// + public interface IPoolableEvent + { + /// + /// Returns this event instance back to its pool. + /// + void Return(); + } +} diff --git a/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs index d1063e75ba..d4333753c4 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs @@ -7,25 +7,29 @@ namespace Exiled.Events.EventArgs.Player { + using System.Collections.Generic; + using Exiled.API.Features; using Exiled.Events.EventArgs.Interfaces; + using PlayerRoles.Voice; + using VoiceChat.Networking; /// /// Contains all information before player receiving a voice message. /// - public class ReceivingVoiceMessageEventArgs : IPlayerEvent, IDeniableEvent + public class ReceivingVoiceMessageEventArgs : IPlayerEvent, IDeniableEvent, IPoolableEvent { + private static readonly Stack Pool = new(2); + /// /// Initializes a new instance of the class. /// - /// The voice message being sent. /// The player receiving the voice message. /// The player sending the voice message. - /// - /// - /// + /// The sender's voice module. + /// The voice message being sent. public ReceivingVoiceMessageEventArgs(Player receiver, Player sender, VoiceModuleBase voiceModule, VoiceMessage voiceMessage) { Sender = sender; @@ -34,15 +38,19 @@ public ReceivingVoiceMessageEventArgs(Player receiver, Player sender, VoiceModul VoiceModule = voiceModule; } + private ReceivingVoiceMessageEventArgs() + { + } + /// /// Gets the player receiving the voice message. /// - public Player Player { get; } + public Player Player { get; private set; } /// /// Gets the player sended the voice message. /// - public Player Sender { get; } + public Player Sender { get; private set; } /// /// Gets or sets the 's . @@ -52,11 +60,57 @@ public ReceivingVoiceMessageEventArgs(Player receiver, Player sender, VoiceModul /// /// Gets the 's . /// - public VoiceModuleBase VoiceModule { get; } + public VoiceModuleBase VoiceModule { get; private set; } /// /// Gets or sets a value indicating whether the player can receive the voice message. /// public bool IsAllowed { get; set; } = true; + + /// + /// Rents an instance from the pool or creates a new one if the pool is empty. + /// + /// The player receiving the message. + /// The player sending the message. + /// The sender's voice module. + /// The voice message packet. + /// A reusable instance. + public static ReceivingVoiceMessageEventArgs Rent(Player receiver, Player sender, VoiceModuleBase module, VoiceMessage msg) + { + if (Pool.Count > 0) + { + ReceivingVoiceMessageEventArgs instance = Pool.Pop(); + instance.Init(receiver, sender, module, msg); + return instance; + } + + return new ReceivingVoiceMessageEventArgs(receiver, sender, module, msg); + } + + /// + /// Returns the instance to the pool and clears references to prevent memory leaks. + /// + public void Return() + { + Player = null; + Sender = null; + VoiceModule = null; + VoiceMessage = default; + IsAllowed = true; + + Pool.Push(this); + } + + /// + /// Resets the object state for reuse. + /// + private void Init(Player receiver, Player sender, VoiceModuleBase module, VoiceMessage msg) + { + Player = receiver; + Sender = sender; + VoiceModule = module; + VoiceMessage = msg; + IsAllowed = true; + } } } diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs index 362cec2a3a..8532325ded 100644 --- a/EXILED/Exiled.Events/Handlers/Player.cs +++ b/EXILED/Exiled.Events/Handlers/Player.cs @@ -1060,7 +1060,7 @@ public static void OnUnloadingWeapon(PlayerUnloadingWeaponEventArgs ev) /// Invoked before a receiving a voice message. /// /// The instance. - public static void OnReceivingVoiceMessage(ReceivingVoiceMessageEventArgs ev) => ReceivingVoiceMessage.InvokeSafely(ev); + public static void OnReceivingVoiceMessage(ReceivingVoiceMessageEventArgs ev) => ReceivingVoiceMessage.InvokeNormal(ev); /// /// Called before a makes noise. diff --git a/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs b/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs index bdee7fe5eb..ade78cd36b 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs @@ -151,18 +151,20 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); + Label evNull = generator.DefineLabel(); Label skipHub = generator.DefineLabel(); - Label skipLabel = generator.DefineLabel(); - LocalBuilder ev = generator.DeclareLocal(typeof(TransmittingEventArgs)); + LocalBuilder ev = generator.DeclareLocal(typeof(ReceivingVoiceMessageEventArgs)); int offset = -2; int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Newobj && (ConstructorInfo)i.operand == GetDeclaredConstructors(typeof(LabApi.Events.Arguments.PlayerEvents.PlayerReceivingVoiceMessageEventArgs))[0]) + offset; - newInstructions[index].labels.Add(skipLabel); - newInstructions.InsertRange(index, new CodeInstruction[] { + // ev = null; + new(OpCodes.Ldnull), + new(OpCodes.Stloc_S, ev.LocalIndex), + // Player.Get(hub); new(OpCodes.Ldloc_S, 4), new(OpCodes.Call, Method(typeof(API.Features.Player), nameof(API.Features.Player.Get), new[] { typeof(ReferenceHub) })), @@ -180,7 +182,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable i.opcode == OpCodes.Callvirt && i.operand is MethodInfo mi && mi.Name == nameof(NetworkConnection.Send) && mi.IsGenericMethod) + offset; - newInstructions[index].labels.Add(skipHub); + List