RPC add modifier with arguments

This commit is contained in:
XtraCube committed 2025-02-02 16:09:06 -05:00
1 parent 51fcc2c88f
commit 9485099ab9
9 files changed
+227 -130

No files matched your search

@@ -3,6 +3,7 @@ using MiraAPI.Example.Options.Roles;
using MiraAPI.Example.Roles;
using MiraAPI.GameOptions;
using MiraAPI.Hud;
using MiraAPI.Modifiers;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using UnityEngine;
+1 -1
View File
@@ -1,6 +1,6 @@
using MiraAPI.Example.Modifiers;
using MiraAPI.Hud;
using MiraAPI.Utilities;
using MiraAPI.Modifiers;
using MiraAPI.Utilities.Assets;
using UnityEngine;
@@ -1,5 +1,4 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
+139
View File
@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using MiraAPI.Networking;
using Reactor.Networking.Attributes;
using Reactor.Utilities;
namespace MiraAPI.Modifiers;
/// <summary>
/// Extension methods for modifiers.
/// </summary>
public static class ModifierExtensions
{
internal static Dictionary<PlayerControl, ModifierComponent> ModifierComponents { get; } = [];
/// <summary>
/// Gets the ModifierComponent for a player.
/// </summary>
/// <param name="player">The PlayerControl object.</param>
/// <returns>A ModifierComponent if there is one, null otherwise.</returns>
public static ModifierComponent? GetModifierComponent(this PlayerControl player)
{
if (ModifierComponents.TryGetValue(player, out var component))
{
return component;
}
component = player.GetComponent<ModifierComponent>();
if (component == null)
{
return null;
}
ModifierComponents[player] = component;
return component;
}
/// <summary>
/// Gets a modifier by its type, or null if the player doesn't have it.
/// </summary>
/// <param name="player">The PlayerControl object.</param>
/// <typeparam name="T">The Type of the Modifier.</typeparam>
/// <returns>The Modifier if it is found, null otherwise.</returns>
public static T? GetModifier<T>(this PlayerControl? player) where T : BaseModifier
{
return player?.GetModifierComponent()?.ActiveModifiers.Find(x => x is T) as T;
}
/// <summary>
/// Checks if a player has a modifier.
/// </summary>
/// <param name="player">The PlayerControl object.</param>
/// <typeparam name="T">The Type of the Modifier.</typeparam>
/// <returns>True if the Modifier is present, false otherwise.</returns>
public static bool HasModifier<T>(this PlayerControl? player) where T : BaseModifier
{
return player?.GetModifierComponent() != null &&
player.GetModifierComponent()!.HasModifier<T>();
}
/// <summary>
/// Checks if a player has a modifier by its ID.
/// </summary>
/// <param name="player">The PlayerControl object.</param>
/// <param name="id">The Modifier ID.</param>
/// <returns>True if the Modifier is present, false otherwise.</returns>
public static bool HasModifier(this PlayerControl? player, uint id)
{
return player?.GetModifierComponent() != null &&
player.GetModifierComponent()!.HasModifier(id);
}
/// <summary>
/// Remote Procedure Call to remove a modifier from a player.
/// </summary>
/// <param name="target">The player to remove the modifier from.</param>
/// <param name="modifierId">The ID of the modifier.</param>
[MethodRpc((uint)MiraRpc.RemoveModifier)]
public static void RpcRemoveModifier(this PlayerControl target, uint modifierId)
{
target.GetModifierComponent()?.RemoveModifier(modifierId);
}
/// <summary>
/// Remote Procedure Call to remove a modifier from a player.
/// </summary>
/// <param name="player">The player to remove the modifier from.</param>
/// <typeparam name="T">The Type of the Modifier.</typeparam>
public static void RpcRemoveModifier<T>(this PlayerControl player) where T : BaseModifier
{
var id = ModifierManager.GetModifierId(typeof(T));
if (id == null)
{
Logger<MiraApiPlugin>.Error($"Cannot add modifier {typeof(T).Name} because it is not registered.");
return;
}
player.RpcRemoveModifier(id.Value);
}
/// <summary>
/// Remote Procedure Call to add a modifier to a player.
/// </summary>
/// <param name="target">The player to add the modifier to.</param>
/// <param name="modifierId">The modifier ID.</param>
[MethodRpc((uint)MiraRpc.AddModifier)]
public static void RpcAddModifier(this PlayerControl target, uint modifierId, params object[] args)
{
var type = ModifierManager.GetModifierType(modifierId);
if (type == null)
{
Logger<MiraApiPlugin>.Error($"Cannot add modifier with id {modifierId} because it is not registered.");
return;
}
var modifier = ModifierFactory.CreateInstance(type, args);
target.GetModifierComponent()?.AddModifier(modifier);
}
/// <summary>
/// Remote Procedure Call to add a modifier to a player.
/// </summary>
/// <param name="player">The player to add the modifier to.</param>
/// <param name="args">The arguments to initialize the modifier constructor with.</param>
/// <typeparam name="T">The modifier Type.</typeparam>
public static void RpcAddModifier<T>(this PlayerControl player, params object[] args) where T : BaseModifier
{
var id = ModifierManager.GetModifierId(typeof(T));
if (id == null)
{
Logger<MiraApiPlugin>.Error($"Cannot add modifier {typeof(T).Name} because it is not registered.");
return;
}
var modifier = ModifierFactory<T>.CreateInstance(args);
player.GetModifierComponent()!.AddModifier(modifier);
}
}
+83
View File
@@ -0,0 +1,83 @@
using System;
using System.Linq;
using System.Linq.Expressions;
namespace MiraAPI.Modifiers;
public static class ModifierFactory
{
private static Func<object[], BaseModifier>? _constructor;
private static Func<object[], BaseModifier> CreateConstructor(Type type)
{
var constructorInfo = type.GetConstructors()
.OrderByDescending(c => c.GetParameters().Length)
.FirstOrDefault() ?? throw new InvalidOperationException($"Type {type} does not have a public constructor.");
var parameters = constructorInfo.GetParameters();
var argsParam = Expression.Parameter(typeof(object[]), "args");
var constructorParams = new Expression[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
{
var paramType = parameters[i].ParameterType;
var paramAccess = Expression.ArrayIndex(argsParam, Expression.Constant(i));
var convertedParam = Expression.Convert(paramAccess, paramType);
constructorParams[i] = convertedParam;
}
var newExpr = Expression.New(constructorInfo, constructorParams);
var lambda = Expression.Lambda<Func<object[], BaseModifier>>(newExpr, argsParam);
return lambda.Compile();
}
public static BaseModifier CreateInstance(Type type, params object[] args)
{
_constructor ??= CreateConstructor(type);
return _constructor(args);
}
}
/// <summary>
/// Factory for creating instances of a modifier. More efficient than using reflection.
/// </summary>
/// <typeparam name="T">The modifier type.</typeparam>
public static class ModifierFactory<T> where T : BaseModifier
{
private static readonly Func<object[], T> Constructor = CreateConstructor();
private static Func<object[], T> CreateConstructor()
{
var constructorInfo = typeof(T).GetConstructors()
.OrderByDescending(c => c.GetParameters().Length)
.FirstOrDefault() ?? throw new InvalidOperationException($"Type {typeof(T)} does not have a public constructor.");
var parameters = constructorInfo.GetParameters();
var argsParam = Expression.Parameter(typeof(object[]), "args");
var constructorParams = new Expression[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
{
var paramType = parameters[i].ParameterType;
var paramAccess = Expression.ArrayIndex(argsParam, Expression.Constant(i));
var convertedParam = Expression.Convert(paramAccess, paramType);
constructorParams[i] = convertedParam;
}
var newExpr = Expression.New(constructorInfo, constructorParams);
var lambda = Expression.Lambda<Func<object[], T>>(newExpr, argsParam);
return lambda.Compile();
}
/// <summary>
/// Creates an instance of the modifier.
/// </summary>
/// <param name="args">Parameters for the constructor.</param>
/// <returns>An instance of the modifier.</returns>
public static T CreateInstance(params object[] args)
{
return Constructor(args);
}
}
@@ -1,7 +1,7 @@
using System.Linq;
using HarmonyLib;
using MiraAPI.Modifiers;
using MiraAPI.Modifiers.Types;
using MiraAPI.Utilities;
namespace MiraAPI.Patches.Modifiers;
+1 -1
View File
@@ -86,6 +86,6 @@ public static class PlayerControlPatches
[HarmonyPatch(nameof(PlayerControl.OnDestroy))]
public static void PlayerControlOnDestroyPrefix(PlayerControl __instance)
{
Utilities.Extensions.ModifierComponents.Remove(__instance);
ModifierExtensions.ModifierComponents.Remove(__instance);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
using HarmonyLib;
using MiraAPI.Modifiers;
using MiraAPI.Roles;
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI.Patches;
-125
View File
@@ -3,10 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using MiraAPI.GameOptions;
using MiraAPI.Modifiers;
using MiraAPI.Networking;
using MiraAPI.Roles;
using Reactor.Networking.Attributes;
using Reactor.Utilities;
using UnityEngine;
@@ -130,30 +128,6 @@ public static class Extensions
opt => opt.OptionBehaviour && opt.OptionBehaviour == optionBehaviour);
}
internal static Dictionary<PlayerControl, ModifierComponent> ModifierComponents { get; } = [];
/// <summary>
/// Gets the ModifierComponent for a player.
/// </summary>
/// <param name="player">The PlayerControl object.</param>
/// <returns>A ModifierComponent if there is one, null otherwise.</returns>
public static ModifierComponent? GetModifierComponent(this PlayerControl player)
{
if (ModifierComponents.TryGetValue(player, out var component))
{
return component;
}
component = player.GetComponent<ModifierComponent>();
if (component == null)
{
return null;
}
ModifierComponents[player] = component;
return component;
}
/// <summary>
/// Randomizes a list.
/// </summary>
@@ -174,105 +148,6 @@ public static class Extensions
return randomizedList;
}
/// <summary>
/// Gets a modifier by its type, or null if the player doesn't have it.
/// </summary>
/// <param name="player">The PlayerControl object.</param>
/// <typeparam name="T">The Type of the Modifier.</typeparam>
/// <returns>The Modifier if it is found, null otherwise.</returns>
public static T? GetModifier<T>(this PlayerControl? player) where T : BaseModifier
{
return player?.GetModifierComponent()?.ActiveModifiers.Find(x => x is T) as T;
}
/// <summary>
/// Checks if a player has a modifier.
/// </summary>
/// <param name="player">The PlayerControl object.</param>
/// <typeparam name="T">The Type of the Modifier.</typeparam>
/// <returns>True if the Modifier is present, false otherwise.</returns>
public static bool HasModifier<T>(this PlayerControl? player) where T : BaseModifier
{
return player?.GetModifierComponent() != null &&
player.GetModifierComponent()!.HasModifier<T>();
}
/// <summary>
/// Checks if a player has a modifier by its ID.
/// </summary>
/// <param name="player">The PlayerControl object.</param>
/// <param name="id">The Modifier ID.</param>
/// <returns>True if the Modifier is present, false otherwise.</returns>
public static bool HasModifier(this PlayerControl? player, uint id)
{
return player?.GetModifierComponent() != null &&
player.GetModifierComponent()!.HasModifier(id);
}
/// <summary>
/// Remote Procedure Call to remove a modifier from a player.
/// </summary>
/// <param name="target">The player to remove the modifier from.</param>
/// <param name="modifierId">The ID of the modifier.</param>
[MethodRpc((uint)MiraRpc.RemoveModifier)]
public static void RpcRemoveModifier(this PlayerControl target, uint modifierId)
{
target.GetModifierComponent()?.RemoveModifier(modifierId);
}
/// <summary>
/// Remote Procedure Call to remove a modifier from a player.
/// </summary>
/// <param name="player">The player to remove the modifier from.</param>
/// <typeparam name="T">The Type of the Modifier.</typeparam>
public static void RpcRemoveModifier<T>(this PlayerControl player) where T : BaseModifier
{
var id = ModifierManager.GetModifierId(typeof(T));
if (id == null)
{
Logger<MiraApiPlugin>.Error($"Cannot add modifier {typeof(T).Name} because it is not registered.");
return;
}
player.RpcRemoveModifier(id.Value);
}
/// <summary>
/// Remote Procedure Call to add a modifier to a player.
/// </summary>
/// <param name="target">The player to add the modifier to.</param>
/// <param name="modifierId">The modifier ID.</param>
[MethodRpc((uint)MiraRpc.AddModifier)]
public static void RpcAddModifier(this PlayerControl target, uint modifierId)
{
var type = ModifierManager.GetModifierType(modifierId);
if (type == null)
{
Logger<MiraApiPlugin>.Error($"Cannot add modifier with id {modifierId} because it is not registered.");
return;
}
target.GetModifierComponent()?.AddModifier(type);
}
/// <summary>
/// Remote Procedure Call to add a modifier to a player.
/// </summary>
/// <param name="player">The player to add the modifier to.</param>
/// <typeparam name="T">The modifier Type.</typeparam>
public static void RpcAddModifier<T>(this PlayerControl player) where T : BaseModifier
{
var id = ModifierManager.GetModifierId(typeof(T));
if (id == null)
{
Logger<MiraApiPlugin>.Error($"Cannot add modifier {typeof(T).Name} because it is not registered.");
return;
}
player.RpcAddModifier(id.Value);
}
/// <summary>
/// Darkens a color by a specified amount.
/// </summary>