diff --git a/EXILED/Exiled.Events/EventArgs/Server/RoundStartingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Server/RoundStartingEventArgs.cs new file mode 100644 index 000000000..b97f309ed --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Server/RoundStartingEventArgs.cs @@ -0,0 +1,62 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Server +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before the start of a round. + /// + public class RoundStartingEventArgs : IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// + public RoundStartingEventArgs(short timeLeft, short originalTimeLeft, int topPlayer, int playerCount) + { + TimeLeft = timeLeft; + OriginalTimeLeft = originalTimeLeft; + TopPlayer = topPlayer; + PlayerCount = playerCount; + IsAllowed = TimeLeft == -1; + } + + /// + /// Gets or sets the time before the start of the Round. + /// + public int TimeLeft { get; set; } + + /// + /// Gets or sets the time before the start of the Round. + /// + public int OriginalTimeLeft { get; set; } + + /// + /// Gets or sets the maximum number of Player on the server since restart. + /// + public int TopPlayer { get; set; } + + /// + /// Gets the number of Player. + /// + public int PlayerCount { get; } + + /// + public bool IsAllowed { get; set; } + } +} diff --git a/EXILED/Exiled.Events/Handlers/Server.cs b/EXILED/Exiled.Events/Handlers/Server.cs index 80453625b..caef937bf 100644 --- a/EXILED/Exiled.Events/Handlers/Server.cs +++ b/EXILED/Exiled.Events/Handlers/Server.cs @@ -32,6 +32,11 @@ public static class Server /// public static Event RoundStarted { get; set; } = new(); + /// + /// Invoked after the start of a new round. + /// + public static Event RoundStarting { get; set; } = new(); + /// /// Invoked after all players have spawned at the start of a new round. /// @@ -137,6 +142,12 @@ public static class Server /// public static void OnWaitingForPlayers() => WaitingForPlayers.InvokeSafely(); + /// + /// Called before the start of a new round. + /// + /// The instance. + public static void OnRoundStarting(RoundStartingEventArgs ev) => RoundStarting.InvokeSafely(ev); + /// /// Called after the start of a new round. /// diff --git a/EXILED/Exiled.Events/Patches/Events/Server/RoundStarting.cs b/EXILED/Exiled.Events/Patches/Events/Server/RoundStarting.cs new file mode 100644 index 000000000..ec5e68b95 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Server/RoundStarting.cs @@ -0,0 +1,118 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Server +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.EventArgs.Server; + using HarmonyLib; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Adds the event. + /// + [HarmonyPatch] + internal class RoundStarting + { + #pragma warning disable SA1600 // Elements should be documented + public static Type PrivateType { get; internal set; } + + private static MethodInfo TargetMethod() + { + PrivateType = typeof(CharacterClassManager).GetNestedTypes(all) + .FirstOrDefault(currentType => currentType.Name.Contains("Init")); + if (PrivateType == null) + throw new Exception("State machine type for Init not found."); + MethodInfo moveNextMethod = PrivateType.GetMethod("MoveNext", all); + + if (moveNextMethod == null) + throw new Exception("MoveNext method not found in the state machine type."); + return moveNextMethod; + } + + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + const string TimeLeft = "5__3"; + const string OriginalTimeLeft = "5__2"; + const string TopPlayer = "5__4"; + + LocalBuilder ev = generator.DeclareLocal(typeof(RoundStartingEventArgs)); + int offset = -4; + int index = newInstructions.FindLastIndex(x => x.Calls(Method(typeof(CharacterClassManager), nameof(CharacterClassManager.ForceRoundStart)))) + offset; + + List