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

feat: Added SendingCommand event to Player #163

Open
wants to merge 18 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 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
@@ -0,0 +1,70 @@
// -----------------------------------------------------------------------
// <copyright file="SendingValidRACommandEventArgs.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 CommandSystem;
using Exiled.API.Features;
using Exiled.API.Features.Pickups;
using Exiled.Events.EventArgs.Interfaces;

using RemoteAdmin;

/// <summary>
/// Contains all information before a player sends a command.
/// </summary>
public class SendingValidRACommandEventArgs : IPlayerEvent, IDeniableEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="SendingValidRACommandEventArgs" /> class.
/// </summary>
/// <param name="player">
/// <inheritdoc cref="Player" />
/// </param>
/// <param name="command">
/// <inheritdoc cref="Command" />
/// </param>
/// <param name="query">
/// <inheritdoc cref="Query" />
/// </param>
/// <param name="response">
/// <inheritdoc cref="Response" />
/// </param>
public SendingValidRACommandEventArgs(Player player, ICommand command, string query, string response)
{
Player = player;
Command = command;
Query = query;
Response = response;
}

/// <summary>
/// Gets or sets a value indicating whether the player can send a command.
/// </summary>
public bool IsAllowed { get; set; }

/// <summary>
/// Gets or sets the response of the command. If this value is null, the response will stay unchanged.
/// </summary>
public string Response { get; set; }

/// <summary>
/// Gets the player who is sending the command.
/// </summary>
public Player Player { get; }

/// <summary>
/// Gets the command query.
/// </summary>
public string Query { get; }

/// <summary>
/// Gets the command interface.
/// </summary>
public ICommand Command { get; }
}
}
11 changes: 11 additions & 0 deletions EXILED/Exiled.Events/Handlers/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,11 @@ public class Player
/// </summary>
public static Event<ChangingNicknameEventArgs> ChangingNickname { get; set; } = new();

/// <summary>
/// Invoked before a <see cref="API.Features.Player"/> sended command.
Mar1ki marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public static Event<SendingValidRACommandEventArgs> SendingValidRACommand { get; set; } = new();

/// <summary>
/// Called before reserved slot is resolved for a <see cref="API.Features.Player"/>.
/// </summary>
Expand Down Expand Up @@ -1164,6 +1169,12 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item
/// <param name="ev">The <see cref="ChangingNicknameEventArgs"/> instance.</param>
public static void OnChangingNickname(ChangingNicknameEventArgs ev) => ChangingNickname.InvokeSafely(ev);

/// <summary>
/// Called before a <see cref="Player"/>'s sended command.
Mar1ki marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="ev">The <see cref="SendingValidRACommandEventArgs"/> instance.</param>
public static void OnSendingValidRACommand(SendingValidRACommandEventArgs ev) => SendingValidRACommand.InvokeSafely(ev);

/// <summary>
/// Called before pre-authenticating a <see cref="API.Features.Player"/>.
/// </summary>
Expand Down
131 changes: 131 additions & 0 deletions EXILED/Exiled.Events/Patches/Events/Player/SendingValidRACommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// -----------------------------------------------------------------------
// <copyright file="SendingValidRACommand.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.Emit;

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

using HarmonyLib;

using RemoteAdmin;

using static HarmonyLib.AccessTools;

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

Label setptroperresp = generator.DefineLabel();

Label ret = generator.DefineLabel();
newInstructions[newInstructions.Count - 1].WithLabels(ret);
LocalBuilder ev = generator.DeclareLocal(typeof(SendingValidRACommandEventArgs));
int offset = 2;
int index = newInstructions.FindIndex(instruction => instruction.Calls(Method(typeof(CommandHandler), nameof(CommandHandler.TryGetCommand)))) + offset;

Label contlabel = generator.DefineLabel();
newInstructions[index].WithLabels(contlabel);

int sendreplyidx = newInstructions.FindIndex(instructions => instructions.Calls(Method(typeof(string), nameof(string.IsNullOrEmpty)))) + offset;

Label sendreply = generator.DefineLabel();
newInstructions[sendreplyidx].WithLabels(sendreply);

newInstructions.InsertRange(
index,
new CodeInstruction[]
{
// sender
new (OpCodes.Ldarg_1),

// Player.get(sender)
new (OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new Type[] { typeof(CommandSender) })),

// command
new (OpCodes.Ldloc_1),

// query
new (OpCodes.Ldarg_0),

// response
new (OpCodes.Ldloc_S, 6),

// new SendingCommandEventArgs
new (OpCodes.Newobj, GetDeclaredConstructors(typeof(SendingValidRACommandEventArgs))[0]),
new (OpCodes.Dup),
new (OpCodes.Stloc_S, ev.LocalIndex),

// OnSendingCommad(ev)
new (OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnSendingValidRACommand))),

// if ev.IsAllowed cont
new (OpCodes.Ldloc_S, ev.LocalIndex),
new (OpCodes.Callvirt, PropertyGetter(typeof(SendingValidRACommandEventArgs), nameof(SendingValidRACommandEventArgs.IsAllowed))),
new (OpCodes.Brtrue_S, contlabel),

// if ev.Response.IsNullOrEmpty rets
new (OpCodes.Ldloc_S, ev.LocalIndex),
new (OpCodes.Callvirt, PropertyGetter(typeof (SendingValidRACommandEventArgs), nameof(SendingValidRACommandEventArgs.Response))),
new (OpCodes.Call, Method(typeof(string), nameof(string.IsNullOrEmpty))),
new (OpCodes.Brtrue_S, setptroperresp),

// response = ev.Response
new (OpCodes.Ldloc_S, ev.LocalIndex),
new (OpCodes.Callvirt, PropertyGetter(typeof (SendingValidRACommandEventArgs), nameof(SendingValidRACommandEventArgs.Response))),
new (OpCodes.Stloc_S, 6),

// goto sendreply
new (OpCodes.Br, sendreply),

// response = "The Command Execution Was Pervented Ny Plugin."
Mar1ki marked this conversation as resolved.
Show resolved Hide resolved
new CodeInstruction(OpCodes.Ldstr, "The Command Execution Was Prevented By Plugin.").WithLabels(setptroperresp),
new (OpCodes.Stloc_S, 6),
new (OpCodes.Br, sendreply),
});
offset = -4;
index = newInstructions.FindIndex(instruction => instruction.Calls(Method(typeof(string), nameof(string.ToUpperInvariant)))) + offset;
Label skip = generator.DefineLabel();
newInstructions[index].WithLabels(skip);
newInstructions.InsertRange(
index,
new CodeInstruction[]
{
// if ev.Response.IsNullOrEmpty skip
new (OpCodes.Ldloc_S, ev.LocalIndex),
new (OpCodes.Callvirt, PropertyGetter(typeof (SendingValidRACommandEventArgs), nameof(SendingValidRACommandEventArgs.Response))),
new (OpCodes.Call, Method(typeof(string), nameof(string.IsNullOrEmpty))),
new (OpCodes.Brtrue_S, skip),

// response = ev.Response
new (OpCodes.Ldloc_S, ev.LocalIndex),
new (OpCodes.Callvirt, PropertyGetter(typeof (SendingValidRACommandEventArgs), nameof(SendingValidRACommandEventArgs.Response))),
new (OpCodes.Stloc_S, 6),
});

for (int z = 0; z < newInstructions.Count; z++)
yield return newInstructions[z];

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