diff --git a/Exiled.Events/EventArgs/Server/RoundStartingEventArgs.cs b/Exiled.Events/EventArgs/Server/RoundStartingEventArgs.cs new file mode 100644 index 0000000000..9b1d54e457 --- /dev/null +++ b/Exiled.Events/EventArgs/Server/RoundStartingEventArgs.cs @@ -0,0 +1,31 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Server +{ + /// + /// Contains all information before the start of the round. + /// + public class RoundStartingEventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public RoundStartingEventArgs(bool isAllowed = true) + { + IsAllowed = isAllowed; + } + + /// + /// Gets or sets a value indicating whether the round can start or not. + /// + public bool IsAllowed { get; set; } + } +} diff --git a/Exiled.Events/Handlers/Server.cs b/Exiled.Events/Handlers/Server.cs index 1b3d0481a9..2ee721cdd1 100644 --- a/Exiled.Events/Handlers/Server.cs +++ b/Exiled.Events/Handlers/Server.cs @@ -103,6 +103,11 @@ public static class Server /// public static Event ReloadedPermissions { get; set; } = new(); + /// + /// Invoked before the round starts. + /// + public static Event RoundStarting { get; set; } = new(); + /// /// Called before waiting for players. /// @@ -195,5 +200,11 @@ public static class Server /// /// The instance. public static void OnSelectingRespawnTeam(SelectingRespawnTeamEventArgs ev) => SelectingRespawnTeam.InvokeSafely(ev); + + /// + /// Called before round started. + /// + /// The instance. + public static void OnRoundStarting(RoundStartingEventArgs ev) => RoundStarting.InvokeSafely(ev); } -} \ No newline at end of file +} diff --git a/Exiled.Events/Patches/Events/Server/RoundStarting.cs b/Exiled.Events/Patches/Events/Server/RoundStarting.cs new file mode 100644 index 0000000000..b064e49b5f --- /dev/null +++ b/Exiled.Events/Patches/Events/Server/RoundStarting.cs @@ -0,0 +1,78 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Server +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Server; + using Exiled.Events.Handlers; + + using GameCore; + + using HarmonyLib; + + using static HarmonyLib.AccessTools; + + /// + /// Patch the . + /// Adds the event. + /// + [EventPatch(typeof(Server), nameof(Server.RoundStarting))] + [HarmonyPatch(typeof(RoundStart), nameof(RoundStart.NetworkTimer), MethodType.Setter)] + internal static class RoundStarting + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label ret = generator.DefineLabel(); + Label contlabel = generator.DefineLabel(); + + newInstructions[newInstructions.Count - 1].labels.Add(ret); + LocalBuilder ev = generator.DeclareLocal(typeof(RoundStartingEventArgs)); + + newInstructions.InsertRange( + 0, + new CodeInstruction[] + { + // Getting a old value + new CodeInstruction(OpCodes.Ldarg_1), + + // Getting a new value + new CodeInstruction(OpCodes.Ldc_I4, -1), + + // If the value is not equal, jump + new CodeInstruction(OpCodes.Bne_Un, contlabel), + + // RoundStartingEventArgs ev = new + new CodeInstruction(OpCodes.Newobj, GetDeclaredConstructors(typeof(RoundStartingEventArgs))[0]), + new CodeInstruction(OpCodes.Dup), + new CodeInstruction(OpCodes.Stloc_S, ev.LocalIndex), + + // Handlers.Server.OnRoundStarting(ev) + new CodeInstruction(OpCodes.Call, Method(typeof(Server), nameof(Server.OnRoundStarting))), + new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex), + + // If isallowed = false + new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(RoundStartingEventArgs), nameof(RoundStartingEventArgs.IsAllowed))), + new CodeInstruction(OpCodes.Brfalse_S, ret), + + // Empty opcode for jump + new CodeInstruction(OpCodes.Nop).WithLabels(contlabel), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +}