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

New Shooting patch #249

Merged
merged 9 commits into from
Dec 7, 2024
Merged
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
88 changes: 27 additions & 61 deletions EXILED/Exiled.Events/EventArgs/Player/ShootingEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,106 +8,72 @@
namespace Exiled.Events.EventArgs.Player
{
using API.Features;

using Exiled.API.Features.Items;

using Interfaces;

using InventorySystem.Items.Firearms.BasicMessages;

using RelativePositioning;

using InventorySystem.Items.Firearms.Modules.Misc;
using UnityEngine;

using BaseFirearm = InventorySystem.Items.Firearms.Firearm;

/// <summary>
/// Contains all information before a player fires a weapon.
/// ClaimedTarget and Player transform values are modified by <see cref="PlayerRoles.FirstPersonControl.FpcBacktracker"/> according to <see cref="ShotBacktrackData"/> sent by the Player and do not match the actual values.
VALERA771 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public class ShootingEventArgs : IPlayerEvent, IDeniableEvent, IFirearmEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="ShootingEventArgs" /> class.
/// </summary>
/// <param name="shooter">
/// <inheritdoc cref="Player" />
/// </param>
/// <param name="firearm">
/// <inheritdoc cref="Firearm" />
/// The <see cref="BaseFirearm"/> that is being fired.
/// </param>
public ShootingEventArgs(Player shooter, BaseFirearm firearm)
/// <param name="shotBacktrackData">
/// <see cref="ShotBacktrackData"/> sent by the client.
/// </param>
public ShootingEventArgs(BaseFirearm firearm, ref ShotBacktrackData shotBacktrackData)
{
Player = shooter;
Firearm = Item.Get(firearm).As<Firearm>();

// ShotMessage = msg;
Firearm = (Firearm)Item.Get(firearm);
Player = Firearm.Owner;
ShotBacktrackData = shotBacktrackData;
}

/// <summary>
/// Gets the player who's shooting.
/// Gets the player who is shooting.
/// </summary>
public Player Player { get; }

/// <summary>
/// Gets the target <see cref="API.Features.Items.Firearm" />.
/// Gets the target that client claims it hit.
/// </summary>
public Firearm Firearm { get; }

/// <inheritdoc/>
public Item Item => Firearm;
/// <remarks>This value is controlled by the shooting player and should not be trusted. Can be null.</remarks>
public Player ClaimedTarget => ShotBacktrackData.HasPrimaryTarget ? Player.Get(ShotBacktrackData.PrimaryTargetHub) : null;

/*
/// <summary>
/// Gets or sets the <see cref="ShotMessage" /> for the event.
/// Gets the <see cref="ShotBacktrackData" />. This object contains the data sent by the client to the server.
/// </summary>
public ShotMessage ShotMessage { get; set; }
/// <remarks>Values are controlled by the shooting player and should not be trusted.</remarks>
public ShotBacktrackData ShotBacktrackData { get; }

/// <summary>
/// Gets or sets the position of the shot.
/// Gets or sets the exact direction of the shot before the bullet spread is applied.
/// </summary>
public Vector3 ShotPosition
public Vector3 Direction
{
get => ShotMessage.TargetPosition.Position;
set
{
ShotMessage msg = ShotMessage;
ShotMessage = new ShotMessage
{
ShooterPosition = msg.ShooterPosition,
ShooterCameraRotation = msg.ShooterCameraRotation,
ShooterWeaponSerial = msg.ShooterWeaponSerial,
TargetPosition = new RelativePosition(value),
TargetRotation = msg.TargetRotation,
TargetNetId = msg.TargetNetId,
};
}
get => Player.CameraTransform.forward;
set => Player.CameraTransform.forward = value; // It is going to be reset by FpcBacktracker the same frame, so why we can set it freely.
}

/// <summary>
/// Gets or sets the netId of the target of the shot.
/// Gets the firearm that is being fired.
/// </summary>
public uint TargetNetId
{
get => ShotMessage.TargetNetId;
set
{
ShotMessage msg = ShotMessage;
ShotMessage = new ShotMessage
{
ShooterPosition = msg.ShooterPosition,
ShooterCameraRotation = msg.ShooterCameraRotation,
ShooterWeaponSerial = msg.ShooterWeaponSerial,
TargetPosition = msg.TargetPosition,
TargetRotation = msg.TargetRotation,
TargetNetId = value,
};
}
}
*/
public Firearm Firearm { get; }

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

/// <summary>
/// Gets or sets a value indicating whether the shot can be fired.
/// </summary>
public bool IsAllowed { get; set; } = true;
}
}
}
75 changes: 49 additions & 26 deletions EXILED/Exiled.Events/Patches/Events/Player/Shooting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,14 @@
namespace Exiled.Events.Patches.Events.Player
{
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

using API.Features;
using API.Features.Pools;

using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Player;

using HarmonyLib;

using InventorySystem.Items.Firearms;
using InventorySystem.Items.Firearms.BasicMessages;
using InventorySystem.Items.Firearms.Modules;
using InventorySystem.Items.Firearms.Modules.Misc;

using static HarmonyLib.AccessTools;
Expand All @@ -39,34 +34,62 @@ private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstructi
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Pool.Get(instructions);

Label returnLabel = generator.DefineLabel();

int offset = 1;
int index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Stloc_2) + offset;

newInstructions.InsertRange(
index,
Label continueLabel1 = generator.DefineLabel();
Label continueLabel2 = generator.DefineLabel();

/*
[] <= Here
IL_0078: ldarg.2 // processingMethod
IL_0079: ldarg.0 // this
IL_007a: ldfld class ReferenceHub InventorySystem.Items.Firearms.Modules.Misc.ShotBacktrackData::PrimaryTargetHub
IL_007f: callvirt instance void class [mscorlib]System.Action`1<class ReferenceHub>::Invoke(!0/*class ReferenceHub* /)
*/
int hasTargetIndex = newInstructions.FindIndex(instruction => instruction.IsLdarg(2));

/*
[] <= Here
IL_0092: ldarg.2 // processingMethod
IL_0093: ldnull // null
IL_0094: callvirt instance void class [mscorlib]System.Action`1<class ReferenceHub>::Invoke(!0/*class ReferenceHub* /)
*/
int noTargetIndex = newInstructions.FindIndex(hasTargetIndex + 1, instruction => instruction.IsLdarg(2));
List<Label> noTargetLabels = newInstructions[noTargetIndex].ExtractLabels();
IRacle1 marked this conversation as resolved.
Show resolved Hide resolved

ConstructorInfo constructorInfo = Constructor(
typeof(ShootingEventArgs),
new[] { typeof(InventorySystem.Items.Firearms.Firearm), typeof(ShotBacktrackData).MakeByRefType() });
IRacle1 marked this conversation as resolved.
Show resolved Hide resolved

CodeInstruction[] patchInstructions1 = GetInstructions(continueLabel1);
CodeInstruction[] patchInstructions2 = GetInstructions(continueLabel2);

CodeInstruction[] GetInstructions(Label continueLabel) =>
new[]
{
// Player.Get(firearm.Owner)
// ShootingEventArgs ev = new(firearm, this)
new(OpCodes.Ldarg_1),
new(OpCodes.Callvirt, PropertyGetter(typeof(Firearm), nameof(Firearm.Owner))),
new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),

// firearm
new CodeInstruction(OpCodes.Ldarg_1).MoveLabelsFrom(newInstructions[index]),

// ShootingEventArgs ev = new(Player, firearm)
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ShootingEventArgs))[0]),
new(OpCodes.Dup),
new(OpCodes.Ldarg_0),
new(OpCodes.Newobj, constructorInfo),

// Handlers.Player.OnShooting(ev)
new(OpCodes.Dup), // Dup to keep ev on the stack
new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnShooting))),

// if (ev.IsAllowed)
// return;
// if (!ev.IsAllowed) return
new(OpCodes.Callvirt, PropertyGetter(typeof(ShootingEventArgs), nameof(ShootingEventArgs.IsAllowed))),
new(OpCodes.Brfalse_S, returnLabel),
});
new(OpCodes.Brtrue_S, continueLabel),
new(OpCodes.Leave_S, returnLabel),

new CodeInstruction(OpCodes.Nop).WithLabels(continueLabel),
};

newInstructions.InsertRange( // noTargetIndex goes first because it's higher then hasTargetIndex so it won't mess it up
noTargetIndex,
patchInstructions1);
newInstructions[noTargetIndex].WithLabels(noTargetLabels);

newInstructions.InsertRange(
hasTargetIndex,
patchInstructions2);

newInstructions[newInstructions.Count - 1].WithLabels(returnLabel);

Expand Down