Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding event TogglingRadio(Player, RadioItem, bool, bool) #2329

Merged
merged 20 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from 17 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
67 changes: 67 additions & 0 deletions Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// -----------------------------------------------------------------------
// <copyright file="TogglingRadioEventArgs.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.EventArgs.Player
{
using API.Features;

using Exiled.API.Features.Items;
using Interfaces;
using InventorySystem.Items.Radio;

/// <summary>
/// Contains all information before toggling a radio.
louis1706 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public class TogglingRadioEventArgs : IPlayerEvent, IDeniableEvent, IItemEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="TogglingRadioEventArgs" /> class.
/// </summary>
/// <param name="player">
/// <inheritdoc cref="Player" />
/// </param>
/// <param name="radio">
/// <inheritdoc cref="Radio" />
/// </param>
/// <param name="newState">
/// <inheritdoc cref="NewState" />
/// </param>
/// <param name="isAllowed">
/// <inheritdoc cref="IsAllowed" />
/// </param>
public TogglingRadioEventArgs(Player player, RadioItem radio, bool newState, bool isAllowed = true)
{
Player = player;
Radio = (Radio)Item.Get(radio);
NewState = newState;
IsAllowed = isAllowed;
}

/// <summary>
/// Gets the <see cref="API.Features.Items.Radio" /> which is being used.
/// </summary>
public Radio Radio { get; }

/// <inheritdoc/>
public Item Item => Radio;

/// <summary>
/// Gets a value indicating whether the radio is being turned on or off.
/// </summary>
public bool NewState { get; }

/// <summary>
/// Gets or sets a value indicating whether the radio can be turned on or off.
/// </summary>
public bool IsAllowed { get; set; }

/// <summary>
/// Gets the player who's using the radio.
/// </summary>
public Player Player { get; }
}
}
11 changes: 11 additions & 0 deletions Exiled.Events/Handlers/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,11 @@ public class Player
/// </summary>
public static Event<TogglingOverwatchEventArgs> TogglingOverwatch { get; set; } = new();

/// <summary>
/// Invoked before turning the <see cref="API.Features.Items.Radio" /> on/off.
/// </summary>
public static Event<TogglingRadioEventArgs> TogglingRadio { get; set; } = new();

/// <summary>
/// Invoked before a <see cref="API.Features.Player"/> searches a Pickup.
/// </summary>
Expand Down Expand Up @@ -905,6 +910,12 @@ public class Player
/// <param name="ev">The <see cref="TogglingOverwatchEventArgs"/> instance.</param>
public static void OnTogglingOverwatch(TogglingOverwatchEventArgs ev) => TogglingOverwatch.InvokeSafely(ev);

/// <summary>
/// Called before turning the radio on/off.
/// </summary>
/// <param name="ev">The <see cref="TogglingRadioEventArgs"/> instance.</param>
public static void OnTogglingRadio(TogglingRadioEventArgs ev) => TogglingRadio.InvokeSafely(ev);

/// <summary>
/// Called before a <see cref="API.Features.Player"/> searches a Pickup.
/// </summary>
Expand Down
115 changes: 115 additions & 0 deletions Exiled.Events/Patches/Events/Player/TogglingRadio.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// -----------------------------------------------------------------------
// <copyright file="TogglingRadio.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.Patches.Events.Player
{
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

using Exiled.API.Features.Items;
using Exiled.API.Features.Pools;
using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Player;

using HarmonyLib;

using InventorySystem.Items;
using InventorySystem.Items.Radio;

using PluginAPI.Events;

using static HarmonyLib.AccessTools;

/// <summary>
/// Patches <see cref="RadioItem.ServerProcessCmd" />.
/// Adds the <see cref="Handlers.Player.TogglingRadio" /> event.
/// </summary>
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.TogglingRadio))]
[HarmonyPatch(typeof(RadioItem), nameof(RadioItem.ServerProcessCmd))]
internal static class TogglingRadio
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Pool.Get(instructions);

Label retLabel = generator.DefineLabel();

Predicate<CodeInstruction> match = instruction => instruction.opcode == OpCodes.Newobj && (ConstructorInfo)instruction.operand == GetDeclaredConstructors(typeof(PlayerRadioToggleEvent))[0];
int offset = -4;
int index = newInstructions.FindIndex(match) + offset;

newInstructions.InsertRange(index, new[]
{
// Player.Get(this.Hub)
new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]),
new(OpCodes.Call, PropertyGetter(typeof(RadioItem), nameof(RadioItem.Owner))),
new(OpCodes.Call, Method(typeof(API.Features.Player), nameof(API.Features.Player.Get), new[] { typeof(ReferenceHub) })),

// this
new(OpCodes.Ldarg_0),

// true
new(OpCodes.Ldc_I4_1),

// true
new(OpCodes.Ldc_I4_1),

// TogglingRadioEventArgs ev = new TogglingRadioEventArgs(Player, Radio, bool);
NaoUnderscore marked this conversation as resolved.
Show resolved Hide resolved
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(TogglingRadioEventArgs))[0]),
new(OpCodes.Dup),

// Handlers.Player.OnTogglingRadio(ev);
new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnTogglingRadio))),

// if(!ev.IsAllowed)
// return;
new(OpCodes.Callvirt, PropertyGetter(typeof(TogglingRadioEventArgs), nameof(TogglingRadioEventArgs.IsAllowed))),
new(OpCodes.Brfalse_S, retLabel),
});

index = newInstructions.FindLastIndex(match) + offset;

newInstructions.InsertRange(index, new[]
{
// Player.Get(this.Hub)
new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]),
new(OpCodes.Call, PropertyGetter(typeof(RadioItem), nameof(RadioItem.Owner))),
new(OpCodes.Call, Method(typeof(API.Features.Player), nameof(API.Features.Player.Get), new[] { typeof(ReferenceHub) })),

// this
new(OpCodes.Ldarg_0),

// false
new(OpCodes.Ldc_I4_0),

// true
new(OpCodes.Ldc_I4_1),

// TogglingRadioEventArgs ev = new TogglingRadioEventArgs(Player, Radio, bool);
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(TogglingRadioEventArgs))[0]),
new(OpCodes.Dup),

// Handlers.Player.OnTogglingRadio(ev);
new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnTogglingRadio))),

// if(!ev.IsAllowed)
// return;
new(OpCodes.Callvirt, PropertyGetter(typeof(TogglingRadioEventArgs), nameof(TogglingRadioEventArgs.IsAllowed))),
new(OpCodes.Brfalse_S, retLabel),
});

newInstructions[newInstructions.Count - 1].labels.Add(retLabel);

foreach (var instruction in newInstructions)
yield return instruction;

ListPool<CodeInstruction>.Pool.Return(newInstructions);
}
}
}