Skip to content
This repository was archived by the owner on Jul 8, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// -----------------------------------------------------------------------
// <copyright file="SwingingEventArgs.cs" company="ExMod Team">
// <copyright file="SwingingJailbirdEventArgs.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
Expand All @@ -14,19 +14,19 @@ namespace Exiled.Events.EventArgs.Item
/// <summary>
/// Contains all information before a player swings a <see cref="Jailbird"/>.
/// </summary>
public class SwingingEventArgs : IItemEvent, IDeniableEvent
public class SwingingJailbirdEventArgs : IItemEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="SwingingEventArgs"/> class.
/// Initializes a new instance of the <see cref="SwingingJailbirdEventArgs"/> class.
/// </summary>
/// <param name="player"><inheritdoc cref="Player"/></param>
/// <param name="swingItem">The item being swung.</param>
/// <param name="isAllowed">Whether the item can be swung.</param>
public SwingingEventArgs(ReferenceHub player, InventorySystem.Items.ItemBase swingItem, bool isAllowed = true)
/// <param name="cunHurt">Whether the item could cause harm.</param>
public SwingingJailbirdEventArgs(ReferenceHub player, InventorySystem.Items.ItemBase swingItem, bool cunHurt = true)
{
Player = Player.Get(player);
Jailbird = (Jailbird)Item.Get(swingItem);
IsAllowed = isAllowed;
CanHurt = cunHurt;
}

/// <summary>
Expand All @@ -45,8 +45,8 @@ public SwingingEventArgs(ReferenceHub player, InventorySystem.Items.ItemBase swi
public Item Item => Jailbird;

/// <summary>
/// Gets or sets a value indicating whether the item can be swung.
/// Gets or sets a value indicating whether the item could cause harm.
/// </summary>
public bool IsAllowed { get; set; }
public bool CanHurt { get; set; }
}
}
12 changes: 8 additions & 4 deletions EXILED/Exiled.Events/Features/Patcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ internal Patcher()
/// <param name="event">The <see cref="IExiledEvent"/> all matching patches should target.</param>
public void Patch(IExiledEvent @event)
{
List<Type> types = ListPool<Type>.Pool.Get();

try
{
List<Type> types = ListPool<Type>.Pool.Get(UnpatchedTypes.Where(x => x.GetCustomAttributes<EventPatchAttribute>().Any((epa) => epa.Event == @event)));
types.AddRange(UnpatchedTypes.Where(x => x.GetCustomAttributes<EventPatchAttribute>().Any(epa => epa.Event == @event)));

foreach (Type type in types)
{
Expand All @@ -68,12 +70,14 @@ public void Patch(IExiledEvent @event)
ReloadDisabledPatches();
UnpatchedTypes.Remove(type);
}

ListPool<Type>.Pool.Return(types);
}
catch (Exception ex)
{
Log.Error($"Patching by event failed!\n{ex}");
Log.Error($"Patching by event {@event.GetType().GenericTypeArguments[0].Name} failed!\n{ex}");
}
finally
{
ListPool<Type>.Pool.Return(types);
}
}

Expand Down
6 changes: 3 additions & 3 deletions EXILED/Exiled.Events/Handlers/Item.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static class Item
/// <summary>
/// Invoked before a melee item is swung.
/// </summary>
public static Event<SwingingEventArgs> Swinging { get; set; } = new();
public static Event<SwingingJailbirdEventArgs> Swinging { get; set; } = new();

/// <summary>
/// Invoked when a <see cref="API.Features.Items.Jailbird"/> starts charging.
Expand Down Expand Up @@ -103,8 +103,8 @@ public static class Item
/// <summary>
/// Called before a melee item is swung.
/// </summary>
/// <param name="ev">The <see cref="SwingingEventArgs"/> instance.</param>
public static void OnSwinging(SwingingEventArgs ev) => Swinging.InvokeSafely(ev);
/// <param name="ev">The <see cref="SwingingJailbirdEventArgs"/> instance.</param>
public static void OnSwinging(SwingingJailbirdEventArgs ev) => Swinging.InvokeSafely(ev);

/// <summary>
/// Called before a <see cref="API.Features.Items.Jailbird"/> that is being charged.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// -----------------------------------------------------------------------
// <copyright file="JailbirdChargeCompletePatch.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

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

using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Item;
using HarmonyLib;
using InventorySystem.Items.Jailbird;
using Mirror;
using NorthwoodLib.Pools;

using static HarmonyLib.AccessTools;

using Item = Exiled.API.Features.Items.Item;
using Player = Exiled.API.Features.Player;

/// <summary>
/// Patches <see cref="JailbirdItem.ServerProcessCmd(NetworkReader)" />.
/// Adds the <see cref="Handlers.Item.JailbirdChargeComplete" /> event.
/// </summary>
[EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.JailbirdChargeComplete))]
[HarmonyPatch(typeof(JailbirdItem), nameof(JailbirdItem.ServerProcessCmd))]
internal static class JailbirdChargeCompletePatch
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Shared.Rent(instructions);

LocalBuilder ev = generator.DeclareLocal(typeof(JailbirdChargeCompleteEventArgs));

Label continueLabel = generator.DefineLabel();

const int offset = 1;
int index = newInstructions.FindLastIndex(i => i.Calls(Method(typeof(Stopwatch), nameof(Stopwatch.Reset)))) + offset;

newInstructions[index].WithLabels(continueLabel);

newInstructions.InsertRange(index, new CodeInstruction[]
{
// ev = new JailbirdChargeCompleteEventArgs(this.Owner, this, true)
new (OpCodes.Ldarg_0),
new (OpCodes.Callvirt, PropertyGetter(typeof(JailbirdItem), nameof(JailbirdItem.Owner))),
new (OpCodes.Ldarg_0),
new (OpCodes.Ldc_I4_1),
new (OpCodes.Newobj, GetDeclaredConstructors(typeof(JailbirdChargeCompleteEventArgs))[0]),
new (OpCodes.Dup),
new (OpCodes.Dup),
new (OpCodes.Stloc_S, ev),

// Handlers.Item.OnJailbirdChargeComplete(ev)
new (OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnJailbirdChargeComplete))),

// if (ev.IsAllowed) goto continueLabel
new (OpCodes.Callvirt, PropertyGetter(typeof(JailbirdChargeCompleteEventArgs), nameof(JailbirdChargeCompleteEventArgs.IsAllowed))),
new (OpCodes.Brtrue_S, continueLabel),

// this.SendRpc(JailbirdMessageType.ChargeFailed, null)
new (OpCodes.Ldarg_0),
new (OpCodes.Ldc_I4_S, (sbyte)JailbirdMessageType.ChargeFailed),
new (OpCodes.Ldnull),
new (OpCodes.Call, Method(typeof(JailbirdItem), nameof(JailbirdItem.SendRpc))),

// ev.Player.RemoveItem(ev.Item, false)
new (OpCodes.Ldloc_S, ev),
new (OpCodes.Callvirt, PropertyGetter(typeof(ChargingJailbirdEventArgs), nameof(ChargingJailbirdEventArgs.Player))),
new (OpCodes.Ldloc_S, ev),
new (OpCodes.Callvirt, PropertyGetter(typeof(ChargingJailbirdEventArgs), nameof(ChargingJailbirdEventArgs.Item))),
new (OpCodes.Ldc_I4_0),
new (OpCodes.Callvirt, Method(typeof(Player), nameof(Player.RemoveItem), new[] { typeof(Item), typeof(bool) })),
new (OpCodes.Pop),

// ev.Player.CurrentItem = ev.Item
new (OpCodes.Ldloc_S, ev),
new (OpCodes.Callvirt, PropertyGetter(typeof(ChargingJailbirdEventArgs), nameof(ChargingJailbirdEventArgs.Player))),
new (OpCodes.Ldloc_S, ev),
new (OpCodes.Callvirt, PropertyGetter(typeof(ChargingJailbirdEventArgs), nameof(ChargingJailbirdEventArgs.Item))),
new (OpCodes.Call, PropertySetter(typeof(Player), nameof(Player.CurrentItem))),

// return
new (OpCodes.Ret),

// continueLabel:
});

foreach (CodeInstruction instruction in newInstructions)
yield return instruction;

ListPool<CodeInstruction>.Shared.Return(newInstructions);
}
}
}
102 changes: 102 additions & 0 deletions EXILED/Exiled.Events/Patches/Events/Item/JailbirdChargingPatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// -----------------------------------------------------------------------
// <copyright file="JailbirdChargingPatch.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

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

using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Item;
using HarmonyLib;
using InventorySystem.Items.Jailbird;
using Mirror;
using NorthwoodLib.Pools;

using static HarmonyLib.AccessTools;

using Item = Exiled.API.Features.Items.Item;
using Player = Exiled.API.Features.Player;

/// <summary>
/// Patches <see cref="JailbirdItem.ServerProcessCmd(NetworkReader)" />.
/// Adds the <see cref="Handlers.Item.ChargingJailbird" /> event.
/// </summary>
[EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.ChargingJailbird))]
[HarmonyPatch(typeof(JailbirdItem), nameof(JailbirdItem.ServerProcessCmd))]
internal static class JailbirdChargingPatch
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Shared.Rent(instructions);

LocalBuilder ev = generator.DeclareLocal(typeof(ChargingJailbirdEventArgs));

Label continueLabel = generator.DefineLabel();

const int offset = -2;
int index = newInstructions.FindIndex(i => i.Calls(Method(typeof(Stopwatch), nameof(Stopwatch.Start)))) + offset;

List<Label> labels = newInstructions[index].ExtractLabels();

newInstructions[index].WithLabels(continueLabel);

newInstructions.InsertRange(index, new CodeInstruction[]
{
// ev = new ChargingJailbirdEventArgs(this.Owner, this, true)
new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels),
new (OpCodes.Callvirt, PropertyGetter(typeof(JailbirdItem), nameof(JailbirdItem.Owner))),
new (OpCodes.Ldarg_0),
new (OpCodes.Ldc_I4_1),
new (OpCodes.Newobj, GetDeclaredConstructors(typeof(ChargingJailbirdEventArgs))[0]),
new (OpCodes.Dup),
new (OpCodes.Dup),
new (OpCodes.Stloc_S, ev),

// Handlers.Item.OnChargingJailbird(ev)
new (OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnChargingJailbird))),

// if (ev.IsAllowed) goto continueLabel
new (OpCodes.Callvirt, PropertyGetter(typeof(ChargingJailbirdEventArgs), nameof(ChargingJailbirdEventArgs.IsAllowed))),
new (OpCodes.Brtrue_S, continueLabel),

// this.SendRpc(JailbirdMessageType.ChargeFailed, null)
new (OpCodes.Ldarg_0),
new (OpCodes.Ldc_I4_S, (sbyte)JailbirdMessageType.ChargeFailed),
new (OpCodes.Ldnull),
new (OpCodes.Call, Method(typeof(JailbirdItem), nameof(JailbirdItem.SendRpc))),

// ev.Player.RemoveItem(ev.Item, false)
new (OpCodes.Ldloc_S, ev),
new (OpCodes.Callvirt, PropertyGetter(typeof(ChargingJailbirdEventArgs), nameof(ChargingJailbirdEventArgs.Player))),
new (OpCodes.Ldloc_S, ev),
new (OpCodes.Callvirt, PropertyGetter(typeof(ChargingJailbirdEventArgs), nameof(ChargingJailbirdEventArgs.Item))),
new (OpCodes.Ldc_I4_0),
new (OpCodes.Callvirt, Method(typeof(Player), nameof(Player.RemoveItem), new[] { typeof(Item), typeof(bool) })),
new (OpCodes.Pop),

// ev.Player.AddItem(ev.Item)
new (OpCodes.Ldloc_S, ev),
new (OpCodes.Callvirt, PropertyGetter(typeof(JailbirdChargeCompleteEventArgs), nameof(JailbirdChargeCompleteEventArgs.Player))),
new (OpCodes.Ldloc_S, ev),
new (OpCodes.Callvirt, PropertyGetter(typeof(JailbirdChargeCompleteEventArgs), nameof(JailbirdChargeCompleteEventArgs.Item))),
new (OpCodes.Call, Method(typeof(Player), nameof(Player.AddItem), new[] { typeof(Item) })),

// return
new (OpCodes.Ret),

// continueLabel:
});

foreach (CodeInstruction instruction in newInstructions)
yield return instruction;

ListPool<CodeInstruction>.Shared.Return(newInstructions);
}
}
}
Loading
Loading