Skip to content
This repository was archived by the owner on Jul 8, 2025. It is now read-only.
Closed
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
44 changes: 44 additions & 0 deletions EXILED/Exiled.Events/EventArgs/Player/RoomChangedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// -----------------------------------------------------------------------
// <copyright file="RoomChangedEventArgs.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------
namespace Exiled.Events.EventArgs.Player
{
using Exiled.API.Features;
using Exiled.Events.EventArgs.Interfaces;
using MapGeneration;

/// <summary>
/// Contains the information when a player changes rooms.
/// </summary>
public class RoomChangedEventArgs : IPlayerEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="RoomChangedEventArgs"/> class.
/// </summary>
/// <param name="player">The player whose room has changed.</param>
/// <param name="oldRoom">The room identifier before the change (Can be null on round start).</param>
/// <param name="newRoom">The room identifier after the change.</param>
public RoomChangedEventArgs(ReferenceHub player, RoomIdentifier oldRoom, RoomIdentifier newRoom)
{
Player = Player.Get(player);
OldRoom = Room.Get(oldRoom);
NewRoom = Room.Get(newRoom);
}

/// <inheritdoc/>
public Player Player { get; }

/// <summary>
/// Gets the previous room the player was in.
/// </summary>
public Room OldRoom { get; }

/// <summary>
/// Gets the new room the player entered.
/// </summary>
public Room NewRoom { get; }
}
}
58 changes: 58 additions & 0 deletions EXILED/Exiled.Events/EventArgs/Player/ZoneChangedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// -----------------------------------------------------------------------
// <copyright file="ZoneChangedEventArgs.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.EventArgs.Player
{
using Exiled.API.Enums;
using Exiled.API.Features;
using Exiled.Events.EventArgs.Interfaces;
using MapGeneration;

/// <summary>
/// Contains the information when a player changes zones.
/// </summary>
public class ZoneChangedEventArgs : IPlayerEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="ZoneChangedEventArgs"/> class.
/// </summary>
/// <param name="player">The player whose zone has changed.</param>
/// <param name="oldRoom">The previous room the player was in.</param>
/// <param name="newRoom">The new room the player entered.</param>
public ZoneChangedEventArgs(ReferenceHub player, RoomIdentifier oldRoom, RoomIdentifier newRoom)
{
Player = Player.Get(player);
OldRoom = Room.Get(oldRoom);
NewRoom = Room.Get(newRoom);
OldZone = OldRoom.Zone;
NewZone = NewRoom.Zone;
}

/// <inheritdoc/>
public Player Player { get; }

/// <summary>
/// Gets the previous zone the player was in.
/// </summary>
public ZoneType OldZone { get; }

/// <summary>
/// Gets the new zone the player entered.
/// </summary>
public ZoneType NewZone { get; }

/// <summary>
/// Gets the previous room the player was in.
/// </summary>
public Room OldRoom { get; }

/// <summary>
/// Gets the new room the player entered.
/// </summary>
public Room NewRoom { get; }
}
}
22 changes: 22 additions & 0 deletions EXILED/Exiled.Events/Handlers/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,16 @@ public class Player
/// </summary>
public static Event<ChangingSpectatedPlayerEventArgs> ChangingSpectatedPlayer { get; set; } = new();

/// <summary>
/// Invoked when a <see cref="API.Features.Player"/> changes rooms.
/// </summary>
public static Event<RoomChangedEventArgs> RoomChanged { get; set; } = new();

/// <summary>
/// Invoked when a <see cref="API.Features.Player"/> changes zones.
/// </summary>
public static Event<ZoneChangedEventArgs> ZoneChanged { get; set; } = new();

/// <summary>
/// Invoked before a <see cref="API.Features.Player"/> toggles the NoClip mode.
/// </summary>
Expand Down Expand Up @@ -799,6 +809,18 @@ public class Player
/// <param name="ev">The <see cref="RemovedHandcuffsEventArgs"/> instance.</param>
public static void OnRemovedHandcuffs(RemovedHandcuffsEventArgs ev) => RemovedHandcuffs.InvokeSafely(ev);

/// <summary>
/// Called when a <see cref="API.Features.Player"/> changes rooms.
/// </summary>
/// <param name="ev">The <see cref="RoomChangedEventArgs"/> instance.</param>
public static void OnRoomChanged(RoomChangedEventArgs ev) => RoomChanged.InvokeSafely(ev);

/// <summary>
/// Called when a <see cref="API.Features.Player"/> changes zones.
/// </summary>
/// <param name="ev">The <see cref="ZoneChangedEventArgs"/> instance.</param>
public static void OnZoneChanged(ZoneChangedEventArgs ev) => ZoneChanged.InvokeSafely(ev);

/// <summary>
/// Called before a <see cref="API.Features.Player"/> escapes.
/// </summary>
Expand Down
117 changes: 117 additions & 0 deletions EXILED/Exiled.Events/Patches/Events/Player/ChangedRoomZone.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// -----------------------------------------------------------------------
// <copyright file="ChangedRoomZone.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.Patches.Events.Player
{
using System.Collections.Generic;
using System.Reflection.Emit;

using Exiled.API.Features.Pools;
using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Player;
using Exiled.Events.Handlers;
using HarmonyLib;
using MapGeneration;
using UnityEngine;

using static HarmonyLib.AccessTools;

/// <summary>
/// Patches <see cref="CurrentRoomPlayerCache.ValidateCache"/> to add the <see cref="Player.RoomChanged"/> and <see cref="Player.ZoneChanged"/> events.
/// </summary>
[EventPatch(typeof(Player), nameof(Player.RoomChanged))]
[EventPatch(typeof(Player), nameof(Player.ZoneChanged))]
[HarmonyPatch(typeof(CurrentRoomPlayerCache), nameof(CurrentRoomPlayerCache.ValidateCache))]
internal class ChangedRoomZone
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Pool.Get(instructions);

Label returnLabel = generator.DefineLabel();

LocalBuilder oldRoom = generator.DeclareLocal(typeof(RoomIdentifier));
LocalBuilder newRoom = generator.DeclareLocal(typeof(RoomIdentifier));

int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Ldloca_S);

newInstructions.InsertRange(index, new CodeInstruction[]
{
// oldRoom = this._lastDetected
new(OpCodes.Ldarg_0),
new(OpCodes.Ldfld, Field(typeof(CurrentRoomPlayerCache), nameof(CurrentRoomPlayerCache._lastDetected))),
new(OpCodes.Stloc_S, oldRoom),
});

int lastIndex = newInstructions.Count - 1;

newInstructions[lastIndex].WithLabels(returnLabel);

newInstructions.InsertRange(lastIndex, new CodeInstruction[]
{
// newRoom = this._lastDetected
new(OpCodes.Ldarg_0),
new(OpCodes.Ldfld, Field(typeof(CurrentRoomPlayerCache), nameof(CurrentRoomPlayerCache._lastDetected))),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, newRoom),

new(OpCodes.Ldloc_S, oldRoom),

// if (oldRoom == newRoom) return;
new(OpCodes.Call, Method(typeof(object), nameof(object.Equals), new[] { typeof(object), typeof(object) })),
new(OpCodes.Brtrue_S, returnLabel),

// ReferenceHub hub = this._roleManager.gameObject.GetComponent<ReferenceHub>();
new(OpCodes.Ldarg_0),
new(OpCodes.Ldfld, Field(typeof(CurrentRoomPlayerCache), nameof(CurrentRoomPlayerCache._roleManager))),
new(OpCodes.Call, Method(typeof(Component), nameof(Component.GetComponent)).MakeGenericMethod(typeof(ReferenceHub))),

// oldRoom
new(OpCodes.Ldloc_S, oldRoom),

// newRoom
new(OpCodes.Ldloc_S, newRoom),

// Handlers.Player.OnRoomChanged(new RoomChangedEventArgs(hub, oldRoom, newRoom));
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RoomChangedEventArgs))[0]),
new(OpCodes.Call, Method(typeof(Player), nameof(Player.OnRoomChanged))),

// oldRoom.Zone
new(OpCodes.Ldloc_S, oldRoom),
new(OpCodes.Ldfld, Field(typeof(RoomIdentifier), nameof(RoomIdentifier.Zone))),

// newRoom.Zone
new(OpCodes.Ldloc_S, newRoom),
new(OpCodes.Ldfld, Field(typeof(RoomIdentifier), nameof(RoomIdentifier.Zone))),

// if (oldRoom.Zone == newRoom.Zone) return;
new(OpCodes.Ceq),
new(OpCodes.Brtrue_S, returnLabel),

// ReferenceHub hub = this._roleManager.gameObject.GetComponent<ReferenceHub>();
new(OpCodes.Ldarg_0),
new(OpCodes.Ldfld, Field(typeof(CurrentRoomPlayerCache), nameof(CurrentRoomPlayerCache._roleManager))),
new(OpCodes.Call, Method(typeof(Component), nameof(Component.GetComponent)).MakeGenericMethod(typeof(ReferenceHub))),

// oldRoom
new(OpCodes.Ldloc_S, oldRoom),

// newRoom
new(OpCodes.Ldloc_S, newRoom),

// Handlers.Player.OnZoneChanged(new ZoneChangedEventArgs(hub, oldRoom, newRoom));
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ZoneChangedEventArgs))[0]),
new(OpCodes.Call, Method(typeof(Player), nameof(Player.OnZoneChanged))),
});

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

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