diff --git a/MiraAPI/Modifiers/ModifierData.cs b/MiraAPI/Modifiers/ModifierData.cs new file mode 100644 index 0000000..425e063 --- /dev/null +++ b/MiraAPI/Modifiers/ModifierData.cs @@ -0,0 +1,21 @@ +using System; + +namespace MiraAPI.Modifiers; + +/// +/// Modifier data for networking. +/// +/// The Type of the modifier. +/// Parameters for constructor. +public readonly struct ModifierData(Type type, object[] args) +{ + /// + /// Gets the type of the modifier. + /// + public Type Type { get; } = type; + + /// + /// Gets the parameters for the constructor. + /// + public object[] Args { get; } = args; +} diff --git a/MiraAPI/Modifiers/ModifierExtensions.cs b/MiraAPI/Modifiers/ModifierExtensions.cs index 83dfd4c..d6ce5ff 100644 --- a/MiraAPI/Modifiers/ModifierExtensions.cs +++ b/MiraAPI/Modifiers/ModifierExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using MiraAPI.Networking; using Reactor.Networking.Attributes; +using Reactor.Networking.Rpc; using Reactor.Utilities; namespace MiraAPI.Modifiers; @@ -104,7 +105,7 @@ public static class ModifierExtensions /// /// The player to add the modifier to. /// The modifier ID. - [MethodRpc((uint)MiraRpc.AddModifier)] + /// The arguments to initialize the modifier constructor with. public static void RpcAddModifier(this PlayerControl target, uint modifierId, params object[] args) { var type = ModifierManager.GetModifierType(modifierId); @@ -114,8 +115,7 @@ public static class ModifierExtensions return; } - var modifier = ModifierFactory.CreateInstance(type, args); - target.GetModifierComponent()?.AddModifier(modifier); + Rpc.Instance.Send(target, new ModifierData(type, args)); } /// @@ -133,7 +133,6 @@ public static class ModifierExtensions return; } - var modifier = ModifierFactory.CreateInstance(args); - player.GetModifierComponent()!.AddModifier(modifier); + player.RpcAddModifier(id.Value, args); } } diff --git a/MiraAPI/Networking/AddModifierRpc.cs b/MiraAPI/Networking/AddModifierRpc.cs new file mode 100644 index 0000000..3546d4f --- /dev/null +++ b/MiraAPI/Networking/AddModifierRpc.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Hazel; +using MiraAPI.Modifiers; +using Reactor.Networking.Attributes; +using Reactor.Networking.Rpc; +using Reactor.Networking.Serialization; + +namespace MiraAPI.Networking; + +/// +/// Remote procedure call for adding a modifier. +/// +/// Mira plugin. +/// RPC ID. +[RegisterCustomRpc((uint)MiraRpc.AddModifier)] +public class AddModifierRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) +{ + private static readonly Dictionary ParameterCache = []; + + /// + public override RpcLocalHandling LocalHandling => RpcLocalHandling.Before; + + /// + public override void Write(MessageWriter writer, ModifierData data) + { + var modId = ModifierManager.GetModifierId(data.Type); + writer.WritePacked((uint)modId!); + MessageSerializer.Serialize(writer, data.Args); + } + + /// + public override ModifierData Read(MessageReader reader) + { + var modId = reader.ReadPackedUInt32(); + var modifier = ModifierManager.GetModifierType(modId)!; + + if (!ParameterCache.TryGetValue(modifier, out var paramTypes)) + { + paramTypes = modifier.GetConstructors().OrderBy(x => x.GetParameters().Length).First().GetParameters(); + ParameterCache[modifier] = paramTypes; + } + + var objects = new object[paramTypes.Length]; + foreach (var paramType in paramTypes) + { + objects[paramType.Position] = reader.Deserialize(paramType.ParameterType); + } + + return new ModifierData(modifier, objects); + } + + /// + public override void Handle(PlayerControl innerNetObject, ModifierData data) + { + var modifier = ModifierFactory.CreateInstance(data.Type, data.Args); + innerNetObject.GetModifierComponent()?.AddModifier(modifier); + } +}