diff --git a/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs new file mode 100644 index 0000000000..4b491f739a --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/ReceivingVoiceMessageEventArgs.cs @@ -0,0 +1,60 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Player +{ + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + using PlayerRoles.Voice; + using VoiceChat.Networking; + + /// + /// Contains all information before a player receives a voice message. + /// + public class ReceivingVoiceMessageEventArgs : IPlayerEvent, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// The player receiving the voice message. + /// The player sending the voice message. + /// The senders voice module. + /// The voice message being sent. + public ReceivingVoiceMessageEventArgs(Player receiver, Player sender, VoiceModuleBase voiceModule, VoiceMessage voiceMessage) + { + Sender = sender; + Player = receiver; + VoiceMessage = voiceMessage; + VoiceModule = voiceModule; + } + + /// + /// Gets the player receiving the voice message. + /// + public Player Player { get; } + + /// + /// Gets the player sending 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; } = true; + } +} diff --git a/EXILED/Exiled.Events/EventArgs/Player/TransmittingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/TransmittingEventArgs.cs index 96e1ffde45..6cfbf1106a 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,17 +25,17 @@ public class TransmittingEventArgs : IPlayerEvent, IDeniableEvent /// /// /// + /// + /// + /// /// /// /// - /// - /// - /// - public TransmittingEventArgs(Player player, VoiceModuleBase voiceModule, bool isAllowed = true) + public TransmittingEventArgs(Player player, VoiceMessage voiceMessage, VoiceModuleBase voiceModule) { Player = player; + VoiceMessage = voiceMessage; VoiceModule = voiceModule; - IsAllowed = isAllowed; } /// @@ -41,6 +43,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 . /// @@ -49,6 +56,6 @@ public TransmittingEventArgs(Player player, VoiceModuleBase voiceModule, bool is /// /// 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 c67e7bb72f..5a488c3d2e 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/VoiceChattingEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/VoiceChattingEventArgs.cs @@ -25,21 +25,17 @@ public class VoiceChattingEventArgs : IPlayerEvent, IDeniableEvent /// /// /// - /// - /// - /// /// /// /// - /// - /// + /// + /// /// - public VoiceChattingEventArgs(Player player, VoiceMessage voiceMessage, VoiceModuleBase voiceModule, bool isAllowed = true) + public VoiceChattingEventArgs(Player player, VoiceModuleBase voiceModule, VoiceMessage voiceMessage) { Player = player; VoiceMessage = voiceMessage; VoiceModule = voiceModule; - IsAllowed = isAllowed; } /// @@ -60,6 +56,6 @@ public VoiceChattingEventArgs(Player player, VoiceMessage voiceMessage, VoiceMod /// /// 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/Features/Event.cs b/EXILED/Exiled.Events/Features/Event.cs index bbbc638401..7db233c910 100644 --- a/EXILED/Exiled.Events/Features/Event.cs +++ b/EXILED/Exiled.Events/Features/Event.cs @@ -45,8 +45,6 @@ private record AsyncRegistration(CustomAsyncEventHandler handler, int priority); private readonly List innerAsyncEvent = new(); - private bool patched; - /// /// Initializes a new instance of the class. /// @@ -60,6 +58,11 @@ public Event() /// public static IReadOnlyList List => EventsValue; + /// + /// Gets a value indicating whether the Harmony patch for this event has been applied. + /// + public bool Patched { get; private set; } = !Events.Instance.Config.UseDynamicPatching; + /// /// Subscribes a to the inner event, and checks patches if dynamic patching is enabled. /// @@ -124,10 +127,10 @@ public void Subscribe(CustomEventHandler handler, int priority) { Log.Assert(Events.Instance is not null, $"{nameof(Events.Instance)} is null, please ensure you have exiled_events enabled!"); - if (Events.Instance.Config.UseDynamicPatching && !patched) + if (Events.Instance.Config.UseDynamicPatching && !Patched) { Events.Instance.Patcher.Patch(this); - patched = true; + Patched = true; } if (handler == null) @@ -163,10 +166,10 @@ public void Subscribe(CustomAsyncEventHandler handler, int priority) { Log.Assert(Events.Instance is not null, $"{nameof(Events.Instance)} is null, please ensure you have exiled_events enabled!"); - if (Events.Instance.Config.UseDynamicPatching && !patched) + if (Events.Instance.Config.UseDynamicPatching && !Patched) { Events.Instance.Patcher.Patch(this); - patched = true; + Patched = true; } if (handler == null) diff --git a/EXILED/Exiled.Events/Features/Event{T}.cs b/EXILED/Exiled.Events/Features/Event{T}.cs index 2d6252b91e..90cdb6ba98 100644 --- a/EXILED/Exiled.Events/Features/Event{T}.cs +++ b/EXILED/Exiled.Events/Features/Event{T}.cs @@ -50,8 +50,6 @@ private record AsyncRegistration(CustomAsyncEventHandler handler, int priorit private readonly List innerAsyncEvent = new(); - private bool patched; - /// /// Initializes a new instance of the class. /// @@ -65,6 +63,11 @@ public Event() /// public static IReadOnlyDictionary> Dictionary => TypeToEvent; + /// + /// Gets a value indicating whether the Harmony patch for this event has been applied. + /// + public bool Patched { get; private set; } = !Events.Instance.Config.UseDynamicPatching; + /// /// Subscribes a target to the inner event and checks if patching is possible, if dynamic patching is enabled. /// @@ -129,10 +132,10 @@ public void Subscribe(CustomEventHandler handler, int priority) { Log.Assert(Events.Instance is not null, $"{nameof(Events.Instance)} is null, please ensure you have exiled_events enabled!"); - if (Events.Instance.Config.UseDynamicPatching && !patched) + if (Events.Instance.Config.UseDynamicPatching && !Patched) { Events.Instance.Patcher.Patch(this); - patched = true; + Patched = true; } if (handler == null) @@ -168,10 +171,10 @@ public void Subscribe(CustomAsyncEventHandler handler, int priority) { Log.Assert(Events.Instance is not null, $"{nameof(Events.Instance)} is null, please ensure you have exiled_events enabled!"); - if (Events.Instance.Config.UseDynamicPatching && !patched) + if (Events.Instance.Config.UseDynamicPatching && !Patched) { Events.Instance.Patcher.Patch(this); - patched = true; + Patched = true; } if (handler == null) @@ -296,4 +299,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 998b54f813..54fd199634 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 receives a voice message. + /// + public static Event ReceivingVoiceMessage { get; set; } = new(); + /// /// Invoked before a makes noise. /// @@ -897,6 +902,9 @@ public class Player /// The instance. public static void OnReloadingWeapon(PlayerReloadingWeaponEventArgs ev) { + if (!ReloadingWeapon.Patched) + return; + ReloadingWeaponEventArgs exiledEv = new(ev.FirearmItem.Base, ev.IsAllowed); ReloadingWeapon.InvokeSafely(exiledEv); ev.IsAllowed = exiledEv.IsAllowed; @@ -1010,6 +1018,9 @@ public static void OnReloadingWeapon(PlayerReloadingWeaponEventArgs ev) /// The instance. public static void OnUnloadingWeapon(PlayerUnloadingWeaponEventArgs ev) { + if (!UnloadingWeapon.Patched) + return; + UnloadingWeaponEventArgs exiledEv = new(ev.FirearmItem.Base, ev.IsAllowed); UnloadingWeapon.InvokeSafely(exiledEv); ev.IsAllowed = exiledEv.IsAllowed; @@ -1045,6 +1056,12 @@ public static void OnUnloadingWeapon(PlayerUnloadingWeaponEventArgs ev) /// The instance. public static void OnVoiceChatting(VoiceChattingEventArgs ev) => VoiceChatting.InvokeSafely(ev); + /// + /// Invoked before a receives a voice message. + /// + /// The instance. + public static void OnReceivingVoiceMessage(ReceivingVoiceMessageEventArgs ev) => ReceivingVoiceMessage.InvokeSafely(ev); + /// /// Called before a makes noise. /// diff --git a/EXILED/Exiled.Events/Handlers/Scp127.cs b/EXILED/Exiled.Events/Handlers/Scp127.cs index b3f86534d3..bfdb1bcada 100644 --- a/EXILED/Exiled.Events/Handlers/Scp127.cs +++ b/EXILED/Exiled.Events/Handlers/Scp127.cs @@ -43,6 +43,9 @@ public static class Scp127 /// The instance. public static void OnTalking(Scp127TalkingEventArgs ev) { + if (!Talking.Patched) + return; + TalkingEventArgs eventArgs = new(API.Features.Items.Item.Get(ev.Scp127Item.Base), ev.VoiceLine, ev.Priority, ev.IsAllowed); Talking.InvokeSafely(eventArgs); @@ -64,6 +67,9 @@ public static void OnTalked(Scp127TalkedEventArgs ev) /// The instance. public static void OnGainingExperience(Scp127GainingExperienceEventArgs ev) { + if (!GainingExperience.Patched) + return; + GainingExperienceEventArgs eventArgs = new(API.Features.Items.Item.Get(ev.Scp127Item.Base), ev.ExperienceGain, ev.IsAllowed); GainingExperience.InvokeSafely(eventArgs); diff --git a/EXILED/Exiled.Events/Patches/Events/Player/ReceivingVoiceMessage.cs b/EXILED/Exiled.Events/Patches/Events/Player/ReceivingVoiceMessage.cs new file mode 100644 index 0000000000..fcefcbbd0d --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Player/ReceivingVoiceMessage.cs @@ -0,0 +1,100 @@ +// ----------------------------------------------------------------------- +// +// 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; + 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.Networking; + + using static HarmonyLib.AccessTools; + + /// + /// 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(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[] + { + // 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(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) })), + + // 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) + // continue; + 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); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs b/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs index a6d49a70f9..17ba25773c 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/VoiceChatting.cs @@ -4,14 +4,14 @@ // Licensed under the CC BY-SA 3.0 license. // // ----------------------------------------------------------------------- - namespace Exiled.Events.Patches.Events.Player { using System.Collections.Generic; + using System.Reflection; using System.Reflection.Emit; using API.Features.Pools; - using API.Features.Roles; + using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; @@ -41,58 +41,41 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); Label retLabel = generator.DefineLabel(); - Label isMutedLabel = generator.DefineLabel(); Label skipLabel = generator.DefineLabel(); - List