diff --git a/Reactor.Example/ExamplePlugin.cs b/Reactor.Example/ExamplePlugin.cs index c3f1e4c..0b53f18 100644 --- a/Reactor.Example/ExamplePlugin.cs +++ b/Reactor.Example/ExamplePlugin.cs @@ -1,6 +1,8 @@ using System; using BepInEx; using BepInEx.Unity.IL2CPP; +using Il2CppInterop.Runtime.Attributes; +using Reactor.Extensions; using Reactor.Networking; using Reactor.Networking.MethodRpc; using UnityEngine; @@ -21,31 +23,52 @@ public partial class ExamplePlugin : BasePlugin [RegisterInIl2Cpp] public class ExampleComponent : MonoBehaviour { + [HideFromIl2Cpp] + public DragWindow TestWindow { get; } + public ExampleComponent(IntPtr ptr) : base(ptr) { + TestWindow = new DragWindow(new Rect(60, 20, 0, 0), "Example", () => + { + if (AmongUsClient.Instance && PlayerControl.LocalPlayer) + { + if (GUILayout.Button("Send ExampleRpc")) + { + var name = PlayerControl.LocalPlayer.Data.PlayerName; + Rpc.Instance.Send(new ExampleRpc.Data($"Send: from {name}"), ackCallback: () => + { + Logger.Info("Got an acknowledgement for example rpc"); + }); + + if (!AmongUsClient.Instance.AmHost) + { + Rpc.Instance.SendTo(AmongUsClient.Instance.HostId, new ExampleRpc.Data($"SendTo: from {name} to host")); + } + } + + if (GUILayout.Button("Send MethodExampleRpc")) + { + RpcSay(PlayerControl.LocalPlayer, "Hello from method rpc", Random.value, PlayerControl.LocalPlayer); + } + } + }) + { + Enabled = false, + }; } private void Update() { - if (AmongUsClient.Instance && PlayerControl.LocalPlayer) + if (Input.GetKeyDown(KeyCode.F2)) { - if (Input.GetKeyDown(KeyCode.F3)) - { - var name = PlayerControl.LocalPlayer.Data.PlayerName; - Rpc.Instance.Send(new ExampleRpc.Data($"Send: from {name}")); - - if (!AmongUsClient.Instance.AmHost) - { - Rpc.Instance.SendTo(AmongUsClient.Instance.HostId, new ExampleRpc.Data($"SendTo: from {name} to host")); - } - } - - if (Input.GetKeyDown(KeyCode.F4)) - { - RpcSay(PlayerControl.LocalPlayer, "Hello from method rpc", Random.value, PlayerControl.LocalPlayer); - } + TestWindow.Enabled = !TestWindow.Enabled; } } + + private void OnGUI() + { + TestWindow.OnGUI(); + } } [MethodRpc((uint) CustomRpcCalls.MethodRpcExample)] diff --git a/Reactor/Networking/CustomRpc.cs b/Reactor/Networking/CustomRpc.cs index bdebf50..9419046 100644 --- a/Reactor/Networking/CustomRpc.cs +++ b/Reactor/Networking/CustomRpc.cs @@ -33,14 +33,14 @@ public abstract class CustomRpc : UnsafeCustomR Handle((TInnerNetObject) innerNetObject, (TData?) data); } - public void Send(InnerNetObject netObject, TData data, bool immediately = false) + public void Send(InnerNetObject netObject, TData data, bool immediately = false, Action? ackCallback = null) { - UnsafeSend(netObject, data, immediately); + UnsafeSend(netObject, data, immediately, ackCallback: ackCallback); } - public void SendTo(InnerNetObject netObject, int targetId, TData data) + public void SendTo(InnerNetObject netObject, int targetId, TData data, Action? ackCallback = null) { - UnsafeSend(netObject, data, true, targetId); + UnsafeSend(netObject, data, true, targetId, ackCallback); } } @@ -69,13 +69,13 @@ public abstract class CustomRpc : UnsafeCustomRpc wher Handle((TInnerNetObject) innerNetObject); } - public void Send(InnerNetObject netObject, bool immediately = false) + public void Send(InnerNetObject netObject, bool immediately = false, Action? ackCallback = null) { - UnsafeSend(netObject, immediately); + UnsafeSend(netObject, immediately, ackCallback: ackCallback); } - public void SendTo(InnerNetObject netObject, int targetId) + public void SendTo(InnerNetObject netObject, int targetId, Action? ackCallback = null) { - UnsafeSend(netObject, null, true, targetId); + UnsafeSend(netObject, null, true, targetId, ackCallback); } } diff --git a/Reactor/Networking/PlayerCustomRpc.cs b/Reactor/Networking/PlayerCustomRpc.cs index 24aca17..dbda2d7 100644 --- a/Reactor/Networking/PlayerCustomRpc.cs +++ b/Reactor/Networking/PlayerCustomRpc.cs @@ -1,3 +1,4 @@ +using System; using BepInEx.Unity.IL2CPP; namespace Reactor.Networking; @@ -8,14 +9,14 @@ public abstract class PlayerCustomRpc : CustomRpc : CustomRpc AckCallbacks { get; } = new(Il2CppEqualityComparer.Instance); + + [HarmonyPatch(typeof(UdpConnection), nameof(UdpConnection.Send))] + private static class MessageAckPatch + { + public static bool Prefix(UdpConnection __instance, MessageWriter msg) + { + if (__instance._state != ConnectionState.Connected) return true; + + if (msg.SendOption == SendOption.Reliable && AckCallbacks.TryRemove(msg, out var ackCallback)) + { + var buffer = new Il2CppStructArray(msg.Length); + Buffer.BlockCopy(new Il2CppSystem.Array(msg.Buffer.Pointer), 0, new Il2CppSystem.Array(buffer.Pointer), 0, msg.Length); + + __instance.ResetKeepAliveTimer(); + + __instance.AttachReliableID(buffer, 1, ackCallback); + __instance.WriteBytesToConnection(buffer, buffer.Length); + + return false; + } + + return true; + } + } }