Skip to content
This repository has been archived by the owner on Jan 19, 2025. It is now read-only.

Commit

Permalink
Merge branch 'role-api' of https://github.com/Misfiy/EXILED into pr/2483
Browse files Browse the repository at this point in the history
  • Loading branch information
louis1706 committed Mar 24, 2024
2 parents 9c0bc89 + 7186f14 commit a1730a4
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 61 deletions.
19 changes: 19 additions & 0 deletions Exiled.API/Extensions/CollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,25 @@ namespace Exiled.API.Extensions
/// </summary>
public static class CollectionExtensions
{
/// <summary>
/// Removes elements from the enumerable that satisfy the specified condition.
/// </summary>
/// <typeparam name="T">The type of elements in the enumerable.</typeparam>
/// <param name="enumerable">The enumerable to remove elements from.</param>
/// <param name="func">The condition used to determine which elements to remove.</param>
/// <returns>A new enumerable with elements removed based on the specified condition.</returns>
public static IEnumerable<T> RemoveSpecified<T>(this IEnumerable<T> enumerable, Func<T, bool> func)
{
List<T> collection = enumerable.ToList();

foreach (T item in collection.Where(func).ToList())
{
collection.Remove(item);
}

return collection;
}

/// <summary>
/// Gets a random item from an <see cref="IEnumerable{T}"/>.
/// </summary>
Expand Down
3 changes: 2 additions & 1 deletion Exiled.API/Extensions/MirrorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@ public static void ChangeAppearance(this Player player, RoleTypeId type, IEnumer
else
fpc = playerfpc;

fpc.FpcModule.MouseLook.GetSyncValues(0, out ushort value, out ushort _);
ushort value = 0;
fpc?.FpcModule.MouseLook.GetSyncValues(0, out value, out ushort _);
writer.WriteRelativePosition(player.RelativePosition);
writer.WriteUShort(value);
}
Expand Down
14 changes: 12 additions & 2 deletions Exiled.API/Extensions/RoleExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ public static class RoleExtensions
/// <returns>The <see cref="PlayerRoleBase"/>.</returns>
public static bool TryGetRoleBase(this RoleTypeId roleType, out PlayerRoleBase roleBase) => PlayerRoleLoader.TryGetRoleTemplate(roleType, out roleBase);

/// <summary>
/// Tries to get the base <see cref="PlayerRoleBase"/> of the given <see cref="RoleTypeId"/>.
/// </summary>
/// <param name="roleType">The <see cref="RoleTypeId"/>.</param>
/// <param name="roleBase">The <see cref="PlayerRoleBase"/> to return.</param>
/// <typeparam name="T">The type to cast the <see cref="PlayerRoleBase"/> to.</typeparam>
/// <returns>The <see cref="PlayerRoleBase"/>.</returns>
public static bool TryGetRoleBase<T>(this RoleTypeId roleType, out T roleBase)
where T : PlayerRoleBase => PlayerRoleLoader.TryGetRoleTemplate(roleType, out roleBase);

/// <summary>
/// Gets the <see cref="LeadingTeam"/>.
/// </summary>
Expand Down Expand Up @@ -120,11 +130,11 @@ public static class RoleExtensions
/// <returns>Returns a <see cref="SpawnLocation"/> representing the spawn, or <see langword="null"/> if no spawns were found.</returns>
public static SpawnLocation GetRandomSpawnLocation(this RoleTypeId roleType)
{
if (roleType.GetRoleBase() is IFpcRole fpcRole &&
if (roleType.TryGetRoleBase(out FpcStandardRoleBase fpcRole) &&
fpcRole.SpawnpointHandler != null &&
fpcRole.SpawnpointHandler.TryGetSpawnpoint(out Vector3 position, out float horizontalRotation))
{
return new SpawnLocation(roleType, position, horizontalRotation);
return new(roleType, position, horizontalRotation);
}

return null;
Expand Down
2 changes: 1 addition & 1 deletion Exiled.API/Features/Doors/Door.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ internal Door(DoorVariant door, List<Room> rooms)
/// <summary>
/// Gets a value indicating whether or not the door is currently moving.
/// </summary>
public virtual bool IsMoving => ExactState is not(0 or 1);
public virtual bool IsMoving => !(IsFullyOpen || IsFullyClosed);

/// <summary>
/// Gets a value indicating the precise state of the door, from <c>0-1</c>. A value of <c>0</c> indicates the door is fully closed, while a value of <c>1</c> indicates the door is fully open. Values in-between represent the door's animation progress.
Expand Down
2 changes: 1 addition & 1 deletion Exiled.API/Features/Doors/Gate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public Gate(PryableDoor door, List<Room> room)
/// <summary>
/// Gets a value indicating whether the door is fully open.
/// </summary>
public override bool IsFullyOpen => base.IsFullyOpen;
public override bool IsFullyOpen => base.IsFullyOpen || (Base is Timed173PryableDoor && ExactState is 0.5845918f);

/// <summary>
/// Gets a value indicating whether or not the door is currently moving.
Expand Down
49 changes: 30 additions & 19 deletions Exiled.API/Features/Roles/Role.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ namespace Exiled.API.Features.Roles
using Extensions;
using PlayerRoles;
using PlayerRoles.PlayableScps.Scp049.Zombies;
using PlayerRoles.RoleAssign;
using UnityEngine;
using YamlDotNet.Serialization.TypeInspectors;

using FilmmakerGameRole = PlayerRoles.Filmmaker.FilmmakerRole;
using HumanGameRole = PlayerRoles.HumanRole;
Expand Down Expand Up @@ -52,14 +52,24 @@ protected Role(PlayerRoleBase baseRole)
}

/// <summary>
/// Gets a random human <see cref="RoleTypeId"/>.
/// Gets an array of all <see cref="RoleTypeId"/>.
/// </summary>
public static RoleTypeId RandomHuman => Enum.GetValues(typeof(RoleTypeId)).ToArray<RoleTypeId>().Shuffle().FirstOrDefault(role => role.IsHuman());
public static IEnumerable<RoleTypeId> AllRoles => Enum.GetValues(typeof(RoleTypeId)).ToArray<RoleTypeId>();

/// <summary>
/// Gets a random human <see cref="RoleTypeId"/>.
/// Gets a shuffled list of all possible <see cref="RoleTypeId"/>.
/// </summary>
public static RoleTypeId RandomScp => Enum.GetValues(typeof(RoleTypeId)).ToArray<RoleTypeId>().Shuffle().FirstOrDefault(role => RoleExtensions.GetTeam(role) == Team.SCPs);
public static IEnumerable<RoleTypeId> ShuffledAllRoles => AllRoles.Shuffle();

/// <summary>
/// Gets the next Scp to spawn according to NW logic.
/// </summary>
public static RoleTypeId NextScpSpawn => ScpSpawner.NextScp;

/// <summary>
/// Gets the next Human to spawn according to NW logic.
/// </summary>
public static RoleTypeId NextHumanSpawn => HumanSpawner.NextHumanRoleToSpawn;

/// <inheritdoc/>
public override GameObject GameObject => Base.gameObject;
Expand Down Expand Up @@ -97,7 +107,7 @@ protected Role(PlayerRoleBase baseRole)
/// <summary>
/// Gets the <see cref="Enums.Side"/> of this <see cref="Role"/>.
/// </summary>
public Side Side => Base.Team.GetSide();
public Side Side => Team.GetSide();

/// <summary>
/// Gets the <see cref="UnityEngine.Color"/> of this <see cref="Role"/>.
Expand Down Expand Up @@ -189,27 +199,28 @@ protected Role(PlayerRoleBase baseRole)
/// <returns><see langword="true"/> if the values are not equal.</returns>
public static bool operator !=(RoleTypeId type, Role role) => role != type;

/// <summary>
/// Gets a random <see cref="RoleTypeId"/> from the specified <see cref="Team"/>.
/// </summary>
/// <param name="team">The team to get a random of.</param>
/// <returns>A random role from the specified team.</returns>
public static RoleTypeId GetRandom(Team team) => ShuffledAllRoles.FirstOrDefault(r => RoleExtensions.GetTeam(r) == team);

/// <summary>
/// Gets a random <see cref="RoleTypeId"/>.
/// </summary>
/// <param name="includeNonPlayableRoles">Specifies whether non-playable roles should be included.</param>
/// <param name="except">An optional collection of role types to exclude.</param>
/// <returns>A random <see cref="RoleTypeId"/>.</returns>
public static RoleTypeId Random(bool includeNonPlayableRoles = false, IEnumerable<RoleTypeId> except = null)
public static RoleTypeId GetRandom(bool includeNonPlayableRoles = false, IEnumerable<RoleTypeId> except = null)
{
RoleTypeId[] roles = Enum.GetValues(typeof(RoleTypeId)).ToArray<RoleTypeId>();

if (!includeNonPlayableRoles)
{
IEnumerable<RoleTypeId> exceptRoles = roles.Except(new RoleTypeId[] { RoleTypeId.Filmmaker, RoleTypeId.None, RoleTypeId.Overwatch, RoleTypeId.Spectator });

if (except is not null)
exceptRoles = exceptRoles.Except(except);

return exceptRoles.Shuffle().First();
}
IEnumerable<RoleTypeId> roles = except is null
? includeNonPlayableRoles
? AllRoles
: AllRoles.RemoveSpecified(r => RoleExtensions.GetTeam(r) == Team.Dead)
: Enum.GetValues(typeof(RoleTypeId)).ToArray<RoleTypeId>().Except(except);

return includeNonPlayableRoles && except is not null ? roles.Except(except).Shuffle().First() : roles.Shuffle().First();
return roles.Shuffle().FirstOrDefault();
}

/// <inheritdoc/>
Expand Down
19 changes: 18 additions & 1 deletion Exiled.Events/Features/Event.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,14 +188,31 @@ internal void InvokeAsync()

foreach (CustomAsyncEventHandler handler in InnerAsyncEvent.GetInvocationList().Cast<CustomAsyncEventHandler>())
{
Timing.RunCoroutine(SafeCoroutineEnumerator(handler(), handler));
}
}

/// <summary>
/// Runs the coroutine manualy so exceptions can be caught and logged.
/// </summary>
private IEnumerator<float> SafeCoroutineEnumerator(IEnumerator<float> coroutine, CustomAsyncEventHandler handler)
{
while (true)
{
float current;
try
{
Timing.RunCoroutine(handler());
if (!coroutine.MoveNext())
break;
current = coroutine.Current;
}
catch (Exception ex)
{
Log.Error($"Method \"{handler.Method.Name}\" of the class \"{handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}");
yield break;
}

yield return current;
}
}
}
Expand Down
19 changes: 18 additions & 1 deletion Exiled.Events/Features/Event{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,31 @@ internal void InvokeAsync(T arg)

foreach (CustomAsyncEventHandler<T> handler in InnerAsyncEvent.GetInvocationList().Cast<CustomAsyncEventHandler<T>>())
{
Timing.RunCoroutine(SafeCoroutineEnumerator(handler(arg), handler));
}
}

/// <summary>
/// Runs the coroutine manualy so exceptions can be caught and logged.
/// </summary>
private IEnumerator<float> SafeCoroutineEnumerator(IEnumerator<float> coroutine, CustomAsyncEventHandler<T> handler)
{
while (true)
{
float current;
try
{
Timing.RunCoroutine(handler(arg));
if (!coroutine.MoveNext())
break;
current = coroutine.Current;
}
catch (Exception ex)
{
Log.Error($"Method \"{handler.Method.Name}\" of the class \"{handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}");
yield break;
}

yield return current;
}
}
}
Expand Down
9 changes: 0 additions & 9 deletions Exiled.Events/Patches/Events/Player/InteractingDoor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstructi
0,
new CodeInstruction[]
{
new(OpCodes.Ldarg_0),
new(OpCodes.Call, Method(typeof(InteractingDoor), nameof(InteractingDoor.CanStateChange))),
new(OpCodes.Brfalse_S, retLabel),

// InteractingDoorEventArgs ev = new(Player.Get(ply), __instance, false);
new(OpCodes.Ldarg_1),
new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
Expand Down Expand Up @@ -115,10 +111,5 @@ private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstructi

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

private static bool CanStateChange(DoorVariant variant)
{
return !(variant.GetExactState() > 0f && variant.GetExactState() < 1f);
}
}
}
47 changes: 21 additions & 26 deletions Exiled.Events/Patches/Events/Player/Spawning.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ namespace Exiled.Events.Patches.Events.Player
using System.Reflection;

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

using HarmonyLib;

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

using UnityEngine;
Expand All @@ -27,8 +25,8 @@ namespace Exiled.Events.Patches.Events.Player
/// <summary>
/// Patches <see cref="RoleSpawnpointManager.Init"/> delegate.
/// Adds the <see cref="Handlers.Player.Spawning"/> event.
/// Fix for spawning in void.
/// </summary>
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.Spawning))]
[HarmonyPatch]
internal static class Spawning
{
Expand All @@ -39,36 +37,33 @@ private static MethodInfo TargetMethod()

private static bool Prefix(ReferenceHub hub, PlayerRoleBase prevRole, PlayerRoleBase newRole)
{
if (newRole.ServerSpawnReason != RoleChangeReason.Destroyed && Player.TryGet(hub, out Player player))
{
Vector3 oldPosition = hub.transform.position;
float oldRotation = (prevRole as IFpcRole)?.FpcModule.MouseLook.CurrentVertical ?? 0;

if (newRole is IFpcRole fpcRole)
{
if (newRole.ServerSpawnFlags.HasFlag(RoleSpawnFlags.UseSpawnpoint) && fpcRole.SpawnpointHandler != null && fpcRole.SpawnpointHandler.TryGetSpawnpoint(out Vector3 position, out float horizontalRot))
{
oldPosition = position;
oldRotation = horizontalRot;
}
if (newRole.ServerSpawnReason == RoleChangeReason.Destroyed || Player.TryGet(hub, out Player player))
return true;

SpawningEventArgs ev = new(player, oldPosition, oldRotation, prevRole);
Vector3 oldPosition = hub.transform.position;
float oldRotation = (prevRole as IFpcRole)?.FpcModule.MouseLook.CurrentVertical ?? 0;

Handlers.Player.OnSpawning(ev);

hub.transform.position = ev.Position;
fpcRole.FpcModule.MouseLook.CurrentHorizontal = ev.HorizontalRotation;
hub.connectionToClient.Send(new FpcOverrideMessage(ev.Position, ev.HorizontalRotation), 0);
}
else
if (newRole is IFpcRole fpcRole)
{
if (newRole.ServerSpawnFlags.HasFlag(RoleSpawnFlags.UseSpawnpoint) && fpcRole.SpawnpointHandler != null && fpcRole.SpawnpointHandler.TryGetSpawnpoint(out Vector3 position, out float horizontalRot))
{
Handlers.Player.OnSpawning(new(player, oldPosition, oldRotation, prevRole));
oldPosition = position;
oldRotation = horizontalRot;
}

return false;
SpawningEventArgs ev = new(player, oldPosition, oldRotation, prevRole);

Handlers.Player.OnSpawning(ev);

player.Position = ev.Position;
fpcRole.FpcModule.MouseLook.CurrentHorizontal = ev.HorizontalRotation;
}
else
{
Handlers.Player.OnSpawning(new(player, oldPosition, oldRotation, prevRole));
}

return true;
return false;
}
}
}

0 comments on commit a1730a4

Please sign in to comment.