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
55 changes: 55 additions & 0 deletions EXILED/Exiled.API/Enums/AspectRatioType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// -----------------------------------------------------------------------
// <copyright file="AspectRatioType.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.API.Enums
{
/// <summary>
/// All available screen aspect ratio types.
/// </summary>
public enum AspectRatioType : byte
{
/// <summary>
/// Unknown aspect ratio.
/// </summary>
Unknown,

/// <summary>
/// 3:2 aspect ratio (common in classic cameras and some tablets).
/// </summary>
Ratio3_2,

/// <summary>
/// 4:3 aspect ratio (standard definition TVs, older monitors).
/// </summary>
Ratio4_3,

/// <summary>
/// 5:4 aspect ratio (some older computer monitors).
/// </summary>
Ratio5_4,

/// <summary>
/// 16:9 aspect ratio (modern widescreen displays, HDTV).
/// </summary>
Ratio16_9,

/// <summary>
/// 16:10 aspect ratio (common in productivity monitors and laptops).
/// </summary>
Ratio16_10,

/// <summary>
/// 21:9 aspect ratio (ultrawide displays).
/// </summary>
Ratio21_9,

/// <summary>
/// 32:9 aspect ratio (super ultrawide displays).
/// </summary>
Ratio32_9,
}
}
54 changes: 54 additions & 0 deletions EXILED/Exiled.API/Extensions/FloatExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// -----------------------------------------------------------------------
// <copyright file="FloatExtensions.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.API.Extensions
{
using System;
using System.Collections.Generic;

using Exiled.API.Enums;

/// <summary>
/// A set of extensions for <see cref="float"/>.
/// </summary>
public static class FloatExtensions
{
private static readonly Dictionary<AspectRatioType, float> AspectRatioReferences = new()
{
{ AspectRatioType.Ratio3_2, 3f / 2f },
{ AspectRatioType.Ratio4_3, 4f / 3f },
{ AspectRatioType.Ratio5_4, 5f / 4f },
{ AspectRatioType.Ratio16_9, 16f / 9f },
{ AspectRatioType.Ratio16_10, 16f / 10f },
{ AspectRatioType.Ratio21_9, 21f / 9f },
{ AspectRatioType.Ratio32_9, 32f / 9f },
};

/// <summary>
/// Gets the closest <see cref="AspectRatioType"/> for a given aspect ratio value.
/// </summary>
/// <param name="ratio">The aspect ratio value to compare.</param>
/// <returns>The closest matching <see cref="AspectRatioType"/>.</returns>
public static AspectRatioType GetAspectRatioLabel(this float ratio)
{
float closestDiff = float.MaxValue;
AspectRatioType closestRatio = AspectRatioType.Unknown;

foreach (KeyValuePair<AspectRatioType, float> kvp in AspectRatioReferences)
{
float diff = Math.Abs(ratio - kvp.Value);
if (diff < closestDiff)
{
closestDiff = diff;
closestRatio = kvp.Key;
}
}

return closestRatio;
}
}
}
5 changes: 5 additions & 0 deletions EXILED/Exiled.API/Features/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,11 @@ public PlayerInfoArea InfoArea
set => ReferenceHub.nicknameSync.Network_playerInfoToShow = value;
}

/// <summary>
/// Gets the player's current aspect ratio type.
/// </summary>
public AspectRatioType AspectRatio => ReferenceHub.aspectRatioSync.AspectRatio.GetAspectRatioLabel();

/// <summary>
/// Gets or sets the player's custom player info string. This string is displayed along with the player's <see cref="InfoArea"/>.
/// </summary>
Expand Down
54 changes: 54 additions & 0 deletions EXILED/Exiled.Events/EventArgs/Player/ChangedRatioEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// -----------------------------------------------------------------------
// <copyright file="ChangedRatioEventArgs.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.Extensions;
using Exiled.API.Features;
using Exiled.Events.EventArgs.Interfaces;

/// <summary>
/// Contains all information after a player's Aspect Ratio changes.
/// </summary>
public class ChangedRatioEventArgs : IPlayerEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="ChangedRatioEventArgs"/> class.
/// </summary>
/// <param name="player">The player who is changed ratio.
/// <inheritdoc cref="Player" />
/// </param>
/// <param name="oldratio">Old aspect ratio of the player.
/// <inheritdoc cref="float" />
/// </param>
/// <param name="newratio">New aspect ratio of the player.
/// <inheritdoc cref="float" />
/// </param>
public ChangedRatioEventArgs(ReferenceHub player, float oldratio, float newratio)
{
Player = Player.Get(player);
OldRatio = oldratio.GetAspectRatioLabel();
NewRatio = newratio.GetAspectRatioLabel();
}

/// <summary>
/// Gets the player who is changed ratio.
/// </summary>
public Player Player { get; }

/// <summary>
/// Gets the players old ratio.
/// </summary>
public AspectRatioType OldRatio { get; }

/// <summary>
/// Gets the players new ratio.
/// </summary>
public AspectRatioType NewRatio { get; }
}
}
11 changes: 11 additions & 0 deletions EXILED/Exiled.Events/Handlers/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ public class Player
/// </summary>
public static Event<CancelledItemUseEventArgs> CancelledItemUse { get; set; } = new();

/// <summary>
/// Invoked after a <see cref="API.Features.Player"/>'s aspect ratio has changed.
/// </summary>
public static Event<ChangedRatioEventArgs> ChangedRatio { get; set; } = new();

/// <summary>
/// Invoked after a <see cref="API.Features.Player"/> interacted with something.
/// </summary>
Expand Down Expand Up @@ -684,6 +689,12 @@ public class Player
/// <param name="ev">The <see cref="CancelledItemUseEventArgs"/> instance.</param>
public static void OnCancelledItemUse(CancelledItemUseEventArgs ev) => CancelledItemUse.InvokeSafely(ev);

/// <summary>
/// Called after a <see cref="API.Features.Player"/>'s aspect ratio changes.
/// </summary>
/// <param name="ev">The <see cref="ChangedRatioEventArgs"/> instance.</param>
public static void OnChangedRatio(ChangedRatioEventArgs ev) => ChangedRatio.InvokeSafely(ev);

/// <summary>
/// Called after a <see cref="API.Features.Player"/> interacted with something.
/// </summary>
Expand Down
85 changes: 85 additions & 0 deletions EXILED/Exiled.Events/Patches/Events/Player/ChangedAspectRatio.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// -----------------------------------------------------------------------
// <copyright file="ChangedAspectRatio.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 API.Features.Pools;
using CentralAuth;
using Exiled.Events.EventArgs.Player;
using HarmonyLib;
using UnityEngine;

using static HarmonyLib.AccessTools;

/// <summary>
/// Patches <see cref="AspectRatioSync.UserCode_CmdSetAspectRatio__Single" />.
/// Adds the <see cref="Handlers.Player.ChangedRatio" /> event.
/// </summary>
[HarmonyPatch(typeof(AspectRatioSync), nameof(AspectRatioSync.UserCode_CmdSetAspectRatio__Single))]
internal class ChangedAspectRatio
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Pool.Get(instructions);

LocalBuilder oldratio = generator.DeclareLocal(typeof(float));
LocalBuilder hub = generator.DeclareLocal(typeof(ReferenceHub));

Label retLabel = generator.DefineLabel();

newInstructions.InsertRange(0, new CodeInstruction[]
{
// float OldRatio = this.AspectRatio;
new(OpCodes.Ldarg_0),
new(OpCodes.Callvirt, PropertyGetter(typeof(AspectRatioSync), nameof(AspectRatioSync.AspectRatio))),
new(OpCodes.Stloc_S, oldratio.LocalIndex),
});

int index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ret);
newInstructions[index].WithLabels(retLabel);

newInstructions.InsertRange(index, new CodeInstruction[]
{
// ReferenceHub hub = this.GetComponent<ReferenceHub>();
new(OpCodes.Ldarg_0),
new(OpCodes.Call, Method(typeof(Component), nameof(Component.GetComponent)).MakeGenericMethod(typeof(ReferenceHub))),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, hub.LocalIndex),

// if (hub.authManager._targetInstanceMode != ClientInstanceMode.ReadyClient) return;
new(OpCodes.Ldfld, Field(typeof(ReferenceHub), nameof(ReferenceHub.authManager))),
new(OpCodes.Ldfld, Field(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager._targetInstanceMode))),
new(OpCodes.Ldc_I4, 1),
new(OpCodes.Bne_Un_S, retLabel),

// hub
new(OpCodes.Ldloc, hub.LocalIndex),

// OldRatio
new(OpCodes.Ldloc, oldratio.LocalIndex),

// this.AspectRatio
new(OpCodes.Ldarg_0),
new(OpCodes.Call, PropertyGetter(typeof(AspectRatioSync), nameof(AspectRatioSync.AspectRatio))),

// ChangedRatioEventArgs ev = new ChangedRatioEventArgs(ReferenceHub, float, float)
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ChangedRatioEventArgs))[0]),

// Handlers.Player.OnChangedRatio(ev);
new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnChangedRatio))),
});

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

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