Skip to content
Draft
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
94 changes: 62 additions & 32 deletions EXILED/Exiled.Events/Patches/Events/Player/Spawning.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ namespace Exiled.Events.Patches.Events.Player

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

using HarmonyLib;

using MEC;

using PlayerRoles.FirstPersonControl;
using PlayerRoles.FirstPersonControl.Spawnpoints;

using UnityEngine;

using static HarmonyLib.AccessTools;
Expand All @@ -31,54 +37,78 @@ namespace Exiled.Events.Patches.Events.Player
[HarmonyPatch(typeof(RoleSpawnpointManager), nameof(RoleSpawnpointManager.SetPosition))]
internal static class Spawning
{
/// <summary>
/// fghdv.
/// </summary>
/// <param name="fpcRole">dfs.</param>
/// <param name="horizontalRotation">sdf.</param>
internal static void ApplyRotation(IFpcRole fpcRole, float horizontalRotation)
{
if (fpcRole == null || fpcRole.FpcModule == null)
return;

Timing.RunCoroutine(WaitForMouseLookAndApply(fpcRole.FpcModule, horizontalRotation));
}

private static IEnumerator<float> WaitForMouseLookAndApply(FirstPersonMovementModule fpcModule, float rotation)
{
while (fpcModule != null)
{
yield return Timing.WaitUntilFalse(() => fpcModule.MouseLook == null);

yield return Timing.WaitForOneFrame;
fpcModule.ServerOverrideRotation(new Vector2(0f, rotation));

yield break;
}
}

private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Pool.Get(instructions);

int offset = -1;
LocalBuilder ev = generator.DeclareLocal(typeof(SpawningEventArgs));

// Locate the call to `Transform.position` setter to determine where to insert new instructions.
int offset = -1;
int index = newInstructions.FindIndex(instr => instr.Calls(PropertySetter(typeof(Transform), nameof(Transform.position)))) + offset;

// Declare the `SpawningEventArgs` local variable.
LocalBuilder ev = generator.DeclareLocal(typeof(SpawningEventArgs));

newInstructions.InsertRange(
index,
new[]
newInstructions.InsertRange(index, new CodeInstruction[]
{
// Load `ReferenceHub` (argument 0) and get `Player`.
new CodeInstruction(OpCodes.Ldarg_0), // Load `hub` (first argument passed to the method).
new CodeInstruction(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), // Call Player.Get(hub) to get the Player instance.
// Player.Get(hub);
new(OpCodes.Ldarg_0),
new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),

// Load `position` (local variable 2).
new CodeInstruction(OpCodes.Ldloc_2),
// spawnLocation
new(OpCodes.Ldloc_2),

// Load `rotation` (local variable 3).
new CodeInstruction(OpCodes.Ldloc_3),
// horizontalRotation
new(OpCodes.Ldloc_3),

// Load `newRole` (argument 1).
new CodeInstruction(OpCodes.Ldarg_1), // Load `newRole` from argument 1.
// newRole
new(OpCodes.Ldarg_1),

// Create a new instance of `SpawningEventArgs`.
new CodeInstruction(OpCodes.Newobj, GetDeclaredConstructors(typeof(SpawningEventArgs))[0]),
// SpawningEventArg ev = new SpawningEventArgs(player, spawnLocation, horizontalRotation, newRole);
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(SpawningEventArgs))[0]),
new(OpCodes.Dup),
new(OpCodes.Stloc, ev.LocalIndex),

// Duplicate the object to store it and pass it around.
new CodeInstruction(OpCodes.Dup), // Duplicate the `SpawningEventArgs` object.
new CodeInstruction(OpCodes.Stloc, ev.LocalIndex), // Store the duplicated object in a local variable.
// Handlers.Player.OnSpawning(ev);
new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnSpawning))),

// Call `Handlers.Player.OnSpawning`.
new CodeInstruction(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnSpawning))),
// spawnLocation = ev.Position;
new(OpCodes.Ldloc, ev.LocalIndex),
new(OpCodes.Call, PropertyGetter(typeof(SpawningEventArgs), nameof(SpawningEventArgs.Position))),
new(OpCodes.Stloc_2),

// Modify `position` from `ev.Position`.
new CodeInstruction(OpCodes.Ldloc, ev.LocalIndex), // Load the `SpawningEventArgs` object stored in the local variable.
new CodeInstruction(OpCodes.Call, PropertyGetter(typeof(SpawningEventArgs), nameof(SpawningEventArgs.Position))), // Get the `Position` property from `SpawningEventArgs`.
new CodeInstruction(OpCodes.Stloc_2), // Store the position value back in the local variable 2 (`position`).
// horizontalRotation = ev.HorizontalRotation;
new(OpCodes.Ldloc, ev.LocalIndex),
new(OpCodes.Call, PropertyGetter(typeof(SpawningEventArgs), nameof(SpawningEventArgs.HorizontalRotation))),
new(OpCodes.Stloc_3),

// Modify `rotation` from `ev.HorizontalRotation`.
new CodeInstruction(OpCodes.Ldloc, ev.LocalIndex), // Load the `SpawningEventArgs` object again.
new CodeInstruction(OpCodes.Call, PropertyGetter(typeof(SpawningEventArgs), nameof(SpawningEventArgs.HorizontalRotation))), // Get the `HorizontalRotation` property from `SpawningEventArgs`.
new CodeInstruction(OpCodes.Stloc_3), // Store the rotation value back in the local variable 3 (`rotation`).
// ApplyRotation(fpcRole, horizontalRotation)
new(OpCodes.Ldloc_0),
new(OpCodes.Ldloc_3),
new(OpCodes.Call, Method(typeof(Spawning), nameof(ApplyRotation))),
});

for (int z = 0; z < newInstructions.Count; z++)
Expand Down
Loading