From 6ba6709b50929a3d1c34e28ac0e4f4c6f08fe4b5 Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Sun, 16 Jun 2024 22:22:15 -0400 Subject: [PATCH] Added methods to maintain nat punch port open --- .../Coop/Components/FikaServerStunQuery.cs | 59 ++++++++++++++++ Fika.Core/Coop/GameMode/CoopGame.cs | 10 +++ Fika.Core/Coop/Utils/FikaBackendUtils.cs | 3 +- Fika.Core/Coop/Utils/NetManagerUtils.cs | 46 +++++++++++++ Fika.Core/Networking/FikaPingingClient.cs | 69 ++++++++++++------- Fika.Core/Networking/FikaServer.cs | 51 +++++++++----- .../Networking/Models/CreateMatchRequest.cs | 6 +- Fika.Core/Networking/Models/SetHostRequest.cs | 6 +- .../Networking/NatPunch/FikaNatPunchClient.cs | 8 ++- .../Networking/NatPunch/FikaNatPunchServer.cs | 24 +++++-- Fika.Core/UI/Custom/MatchMakerUIScript.cs | 19 +++-- 11 files changed, 239 insertions(+), 62 deletions(-) create mode 100644 Fika.Core/Coop/Components/FikaServerStunQuery.cs diff --git a/Fika.Core/Coop/Components/FikaServerStunQuery.cs b/Fika.Core/Coop/Components/FikaServerStunQuery.cs new file mode 100644 index 00000000..28c3a982 --- /dev/null +++ b/Fika.Core/Coop/Components/FikaServerStunQuery.cs @@ -0,0 +1,59 @@ +using System.Collections; +using UnityEngine; +using Fika.Core.Networking.NatPunch; +using Comfort.Common; +using Fika.Core.Networking; + +namespace Fika.Core.Coop.Components +{ + public class FikaServerStunQuery : MonoBehaviour + { + private Coroutine stunQueryRoutine; + + public void StartServerStunQueryRoutine() + { + stunQueryRoutine = StartCoroutine(StunQuery()); + } + + public void StopServerStunQueryRoutine() + { + if (stunQueryRoutine != null) + { + StopCoroutine(stunQueryRoutine); + stunQueryRoutine = null; + } + } + + private IEnumerator StunQuery() + { + while (true) + { + var fikaServer = Singleton.Instance; + + while (fikaServer == null || fikaServer.NetServer == null || fikaServer.NatPunchServer == null) + yield return null; + + fikaServer.NetServer.Stop(); + + var localPort = 0; + var currentStunIpEndPoint = fikaServer.NatPunchServer.StunIpEndpoint; + + if (currentStunIpEndPoint != null) + localPort = currentStunIpEndPoint.Local.Port; + + var stunIpEndPoint = NatPunchUtils.CreateStunEndPoint(localPort); + + fikaServer.NatPunchServer.StunIpEndpoint = stunIpEndPoint; + + fikaServer.NetServer.Start(stunIpEndPoint.Local.Port); + + yield return new WaitForSeconds(60); + } + } + + private void OnDestroy() + { + StopServerStunQueryRoutine(); + } + } +} diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index 165a98f4..77e568fa 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -858,8 +858,18 @@ public override async Task vmethod_2(int playerId, Vector3 position BackendConfigAbstractClass.Config.CharacterController.ClientPlayerMode, getSensitivity, getAimingSensitivity, new GClass1456(), isServer ? 0 : 1000, statisticsManager); + if(FikaBackendUtils.IsHostNatPunch) + { + NetManagerUtils.DestroyPingingClient(); + } + await NetManagerUtils.InitNetManager(isServer); + if (FikaPlugin.NatPunch.Value) + { + NetManagerUtils.StartServerStunQuery(); + } + if (!CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) { Logger.LogError($"{nameof(vmethod_2)}:Unable to find {nameof(CoopHandler)}"); diff --git a/Fika.Core/Coop/Utils/FikaBackendUtils.cs b/Fika.Core/Coop/Utils/FikaBackendUtils.cs index 28b55b5b..07781e22 100644 --- a/Fika.Core/Coop/Utils/FikaBackendUtils.cs +++ b/Fika.Core/Coop/Utils/FikaBackendUtils.cs @@ -30,6 +30,7 @@ public static class FikaBackendUtils public static string RemoteIp; public static int RemotePort; public static int LocalPort = 0; + public static bool IsHostNatPunch = false; private static string groupId; public static MatchmakerTimeHasCome.GClass3187 ScreenController; @@ -76,7 +77,7 @@ public static bool JoinMatch(string profileId, string serverId, out CreateMatch public static void CreateMatch(string profileId, string hostUsername, RaidSettings raidSettings) { long timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); - var body = new CreateMatch(profileId, hostUsername, timestamp, raidSettings, HostExpectedNumberOfPlayers, raidSettings.Side, raidSettings.SelectedDateTime, FikaPlugin.NatPunch.Value); + var body = new CreateMatch(profileId, hostUsername, timestamp, raidSettings, HostExpectedNumberOfPlayers, raidSettings.Side, raidSettings.SelectedDateTime); FikaRequestHandler.RaidCreate(body); diff --git a/Fika.Core/Coop/Utils/NetManagerUtils.cs b/Fika.Core/Coop/Utils/NetManagerUtils.cs index d5fa68cd..57e8279c 100644 --- a/Fika.Core/Coop/Utils/NetManagerUtils.cs +++ b/Fika.Core/Coop/Utils/NetManagerUtils.cs @@ -36,6 +36,20 @@ public static void CreateNetManager(bool isServer) } } + public static void CreatePingingClient() + { + if (FikaGameObject == null) + { + FikaGameObject = new GameObject("FikaGameObject"); + Object.DontDestroyOnLoad(FikaGameObject); + logger.LogInfo("FikaGameObject has been created!"); + } + + FikaPingingClient pingingClient = FikaGameObject.AddComponent(); + Singleton.Create(pingingClient); + logger.LogInfo("FikaPingingClient has started!"); + } + public static void DestroyNetManager(bool isServer) { if (FikaGameObject != null) @@ -55,6 +69,17 @@ public static void DestroyNetManager(bool isServer) } } + public static void DestroyPingingClient() + { + if(FikaGameObject != null) + { + Singleton.Instance.NetClient.Stop(); + Singleton.Instance.StopKeepAliveRoutine(); + Singleton.TryRelease(Singleton.Instance); + logger.LogInfo("Destroyed FikaPingingClient"); + } + } + public static Task InitNetManager(bool isServer) { if (FikaGameObject != null) @@ -121,5 +146,26 @@ public static void StopPinger() } } } + + public static void StartServerStunQuery() + { + if (FikaGameObject != null) + { + FikaServerStunQuery fikaServerStunQuery = FikaGameObject.AddComponent(); + fikaServerStunQuery.StartServerStunQueryRoutine(); + } + } + + public static void StopServerStunQuery() + { + if (FikaGameObject != null) + { + FikaServerStunQuery fikaServerStunQuery = FikaGameObject.GetComponent(); + if (fikaServerStunQuery != null) + { + Object.Destroy(fikaServerStunQuery); + } + } + } } } \ No newline at end of file diff --git a/Fika.Core/Networking/FikaPingingClient.cs b/Fika.Core/Networking/FikaPingingClient.cs index 3249c33a..5176a74c 100644 --- a/Fika.Core/Networking/FikaPingingClient.cs +++ b/Fika.Core/Networking/FikaPingingClient.cs @@ -8,24 +8,26 @@ using LiteNetLib; using LiteNetLib.Utils; using SPT.Common.Http; +using System.Collections; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; +using UnityEngine; namespace Fika.Core.Networking { - internal class FikaPingingClient(string serverId) : INetEventListener + public class FikaPingingClient : MonoBehaviour, INetEventListener { public NetManager NetClient; - private readonly ManualLogSource _logger = Logger.CreateLogSource("Fika.PingingClient"); - private readonly string serverId = serverId; + private readonly ManualLogSource _logger = BepInEx.Logging.Logger.CreateLogSource("Fika.PingingClient"); private IPEndPoint remoteEndPoint; private IPEndPoint localEndPoint; private IPEndPoint remoteStunEndPoint; private int localPort = 0; public bool Received = false; + private Coroutine keepAliveRoutine; - public bool Init() + public bool Init(string serverId) { NetClient = new(this) { @@ -63,6 +65,8 @@ public bool Init() if (result.NatPunch) { + FikaBackendUtils.IsHostNatPunch = true; + var localStunEndPoint = NatPunchUtils.CreateStunEndPoint(); var natPunchTask = Task.Run(async () => @@ -97,15 +101,10 @@ public bool Init() return true; } - public void PingEndPoint() + public void PingEndPoint(string message) { - if (Received) - { - return; - } - NetDataWriter writer = new(); - writer.Put("fika.hello"); + writer.Put(message); NetClient.SendUnconnectedMessage(writer, remoteEndPoint); if (localEndPoint != null) @@ -119,6 +118,28 @@ public void PingEndPoint() } } + public void StartKeepAliveRoutine() + { + keepAliveRoutine = StartCoroutine(KeepAlive()); + } + + public void StopKeepAliveRoutine() + { + if(keepAliveRoutine != null) + StopCoroutine(keepAliveRoutine); + } + + public IEnumerator KeepAlive() + { + while(true) + { + PingEndPoint("fika.keepalive"); + NetClient.PollEvents(); + + yield return new WaitForSeconds(1.0f); + } + } + public void OnConnectionRequest(ConnectionRequest request) { // Do nothing @@ -141,24 +162,24 @@ public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelN public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) { - if (Received) - { - return; - } _logger.LogInfo("Received response from server, parsing..."); if (reader.TryGetString(out string result)) { - if (result == "fika.hello") - { - Received = true; - FikaBackendUtils.RemoteIp = remoteEndPoint.Address.ToString(); - FikaBackendUtils.RemotePort = remoteEndPoint.Port; - FikaBackendUtils.LocalPort = localPort; - } - else + switch(result) { - _logger.LogError("Data was not as expected"); + case "fika.hello": + Received = true; + FikaBackendUtils.RemoteIp = remoteEndPoint.Address.ToString(); + FikaBackendUtils.RemotePort = remoteEndPoint.Port; + FikaBackendUtils.LocalPort = localPort; + break; + case "fika.keepalive": + // Do nothing + break; + default: + _logger.LogError("Data was not as expected"); + break; } } else diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 2714e954..19bbaf1d 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -113,9 +113,8 @@ public async Task Init() UseNativeSockets = FikaPlugin.NativeSockets.Value, EnableStatistics = true }; - - if (FikaPlugin.UseUPnP.Value) + if (FikaPlugin.UseUPnP.Value && !FikaPlugin.NatPunch.Value) { bool upnpFailed = false; @@ -161,17 +160,13 @@ public async Task Init() if (FikaPlugin.NatPunch.Value) { - var stunIpEndPoint = NatPunchUtils.CreateStunEndPoint(); - - NatPunchServer = new FikaNatPunchServer(_netServer, stunIpEndPoint); + NatPunchServer = new FikaNatPunchServer(_netServer); NatPunchServer.Connect(); if(!NatPunchServer.Connected) { Singleton.Instance.ShowErrorScreen("Network Error", "Error when trying to connect to FikaNatPunchRelayService. Please ensure FikaNatPunchRelayService is enabled and the port is open."); } - - _netServer.Start(NatPunchServer.StunIpEndPoint.Local.Port); } else { @@ -186,7 +181,15 @@ public async Task Init() } logger.LogInfo("Started Fika Server"); - NotificationManagerClass.DisplayMessageNotification($"Server started on port {_netServer.LocalPort}.", + + string serverStartedMessage; + + if (FikaPlugin.NatPunch.Value) + serverStartedMessage = "Server started with Nat Punching enabled."; + else + serverStartedMessage = $"Server started on port {_netServer.LocalPort}."; + + NotificationManagerClass.DisplayMessageNotification(serverStartedMessage, EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); string[] Ips = []; @@ -206,7 +209,7 @@ public async Task Init() iconType: EFT.Communications.ENotificationIconType.Alert); } - SetHostRequest body = new(Ips, Port); + SetHostRequest body = new(Ips, Port, FikaPlugin.NatPunch.Value); FikaRequestHandler.UpdateSetHost(body); FikaEventDispatcher.DispatchEvent(new FikaServerCreatedEvent(this)); @@ -780,16 +783,28 @@ public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketRead { if (reader.TryGetString(out string data)) { - if (data == "fika.hello") - { - NetDataWriter resp = new(); - resp.Put(data); - _netServer.SendUnconnectedMessage(resp, remoteEndPoint); - logger.LogInfo("PingingRequest: Correct ping query, sending response"); - } - else + NetDataWriter resp; + + switch (data) { - logger.LogError("PingingRequest: Data was not as expected"); + case "fika.hello": + resp = new(); + resp.Put(data); + _netServer.SendUnconnectedMessage(resp, remoteEndPoint); + logger.LogInfo("PingingRequest: Correct ping query, sending response"); + break; + + case "fika.keepalive": + resp = new(); + resp.Put(data); + _netServer.SendUnconnectedMessage(resp, remoteEndPoint); + NetManagerUtils.StopServerStunQuery(); + EFT.UI.ConsoleScreen.Log("received fika.keepalive"); + break; + + default: + logger.LogError("PingingRequest: Data was not as expected"); + break; } } else diff --git a/Fika.Core/Networking/Models/CreateMatchRequest.cs b/Fika.Core/Networking/Models/CreateMatchRequest.cs index e250cd70..0ffb2d7d 100644 --- a/Fika.Core/Networking/Models/CreateMatchRequest.cs +++ b/Fika.Core/Networking/Models/CreateMatchRequest.cs @@ -36,10 +36,7 @@ public struct CreateMatch [DataMember(Name = "time")] public EDateTime Time; - [DataMember(Name = "natPunch")] - public bool NatPunch; - - public CreateMatch(string serverId, string hostUsername, long timestamp, RaidSettings settings, int expectedNumberOfPlayers, ESideType side, EDateTime time, bool natPunch) + public CreateMatch(string serverId, string hostUsername, long timestamp, RaidSettings settings, int expectedNumberOfPlayers, ESideType side, EDateTime time) { ServerId = serverId; HostUsername = hostUsername; @@ -50,7 +47,6 @@ public CreateMatch(string serverId, string hostUsername, long timestamp, RaidSet FikaVersion = Assembly.GetExecutingAssembly().GetName().Version; Side = side; Time = time; - NatPunch = natPunch; } } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/SetHostRequest.cs b/Fika.Core/Networking/Models/SetHostRequest.cs index 57b9203b..df68b130 100644 --- a/Fika.Core/Networking/Models/SetHostRequest.cs +++ b/Fika.Core/Networking/Models/SetHostRequest.cs @@ -15,11 +15,15 @@ public struct SetHostRequest [DataMember(Name = "port")] public int Port; - public SetHostRequest(string[] ips, int port) + [DataMember(Name = "natPunch")] + public bool NatPunch; + + public SetHostRequest(string[] ips, int port, bool natPunch) { ServerId = CoopHandler.GetServerId(); Ips = ips; Port = port; + NatPunch = natPunch; } } } \ No newline at end of file diff --git a/Fika.Core/Networking/NatPunch/FikaNatPunchClient.cs b/Fika.Core/Networking/NatPunch/FikaNatPunchClient.cs index f10e2111..375af2c9 100644 --- a/Fika.Core/Networking/NatPunch/FikaNatPunchClient.cs +++ b/Fika.Core/Networking/NatPunch/FikaNatPunchClient.cs @@ -23,8 +23,6 @@ public bool Connected private WebSocket _webSocket; private TaskCompletionSource _receiveTaskCompletion; - public StunIpEndPoint StunIpEndPoint { get; set; } - public FikaNatPunchClient() { Host = $"ws:{RequestHandler.Host.Split(':')[1]}:{FikaPlugin.NatPunchPort.Value}"; @@ -40,6 +38,7 @@ public FikaNatPunchClient() _webSocket.OnOpen += WebSocket_OnOpen; _webSocket.OnError += WebSocket_OnError; _webSocket.OnMessage += WebSocket_OnMessage; + _webSocket.OnClose += WebSocket_OnClose; } public void Connect() @@ -85,6 +84,11 @@ private void WebSocket_OnError(object sender, ErrorEventArgs e) _webSocket.Close(); } + private void WebSocket_OnClose(object sender, CloseEventArgs e) + { + EFT.UI.ConsoleScreen.Log($"Disconnected from FikaNatPunchService as client"); + } + private void Send(T1 o) { var data = JsonConvert.SerializeObject(o); diff --git a/Fika.Core/Networking/NatPunch/FikaNatPunchServer.cs b/Fika.Core/Networking/NatPunch/FikaNatPunchServer.cs index be178c0f..0a35d166 100644 --- a/Fika.Core/Networking/NatPunch/FikaNatPunchServer.cs +++ b/Fika.Core/Networking/NatPunch/FikaNatPunchServer.cs @@ -21,12 +21,20 @@ public bool Connected return _webSocket.ReadyState == WebSocketState.Open ? true : false; } } - public StunIpEndPoint StunIpEndPoint { get; set; } + private StunIpEndPoint _stunIpEndPoint; + public StunIpEndPoint StunIpEndpoint { + get { + return _stunIpEndPoint; + } + set { + _stunIpEndPoint = value; + } + } private WebSocket _webSocket; private NetManager _netManager; - public FikaNatPunchServer(NetManager netManager, StunIpEndPoint stunIpEndPoint) + public FikaNatPunchServer(NetManager netManager) { Host = $"ws:{RequestHandler.Host.Split(':')[1]}:{FikaPlugin.NatPunchPort.Value}"; SessionId = RequestHandler.SessionId; @@ -41,10 +49,9 @@ public FikaNatPunchServer(NetManager netManager, StunIpEndPoint stunIpEndPoint) _webSocket.OnOpen += WebSocket_OnOpen; _webSocket.OnError += WebSocket_OnError; _webSocket.OnMessage += WebSocket_OnMessage; + _webSocket.OnClose += WebSocket_OnClose; _netManager = netManager; - - StunIpEndPoint = stunIpEndPoint; } public void Connect() @@ -79,6 +86,11 @@ private void WebSocket_OnError(object sender, ErrorEventArgs e) _webSocket.Close(); } + private void WebSocket_OnClose(object sender, CloseEventArgs e) + { + EFT.UI.ConsoleScreen.Log($"Disconnected from FikaNatPunchService as server"); + } + private void ProcessMessage(string data) { var msgObj = GetRequestObject(data); @@ -89,13 +101,13 @@ private void ProcessMessage(string data) case "GetHostStunRequest": var getHostStunRequest = (GetHostStunRequest)msgObj; - if (StunIpEndPoint != null) + if (_stunIpEndPoint != null) { IPEndPoint clientIpEndPoint = new IPEndPoint(IPAddress.Parse(getHostStunRequest.StunIp), getHostStunRequest.StunPort); NatPunchUtils.PunchNat(_netManager, clientIpEndPoint); - SendHostStun(getHostStunRequest.SessionId, StunIpEndPoint); + SendHostStun(getHostStunRequest.SessionId, _stunIpEndPoint); } break; } diff --git a/Fika.Core/UI/Custom/MatchMakerUIScript.cs b/Fika.Core/UI/Custom/MatchMakerUIScript.cs index d3d80942..1509b2d9 100644 --- a/Fika.Core/UI/Custom/MatchMakerUIScript.cs +++ b/Fika.Core/UI/Custom/MatchMakerUIScript.cs @@ -209,8 +209,11 @@ private IEnumerator JoinMatch(string profileId, string serverId, Button button) NotificationManagerClass.DisplayMessageNotification("Connecting to session...", iconType: EFT.Communications.ENotificationIconType.EntryPoint); - FikaPingingClient pingingClient = new(serverId); - if (pingingClient.Init()) + NetManagerUtils.CreatePingingClient(); + + var pingingClient = Singleton.Instance; + + if (pingingClient.Init(serverId)) { int attempts = 0; bool success; @@ -221,7 +224,7 @@ private IEnumerator JoinMatch(string profileId, string serverId, Button button) { attempts++; - pingingClient.PingEndPoint(); + pingingClient.PingEndPoint("fika.hello"); pingingClient.NetClient.PollEvents(); success = pingingClient.Received; @@ -249,8 +252,14 @@ private IEnumerator JoinMatch(string profileId, string serverId, Button button) ConsoleScreen.Log("ERROR"); } - pingingClient.NetClient?.Stop(); - pingingClient = null; + if(FikaBackendUtils.IsHostNatPunch) + { + pingingClient.StartKeepAliveRoutine(); + } + else + { + NetManagerUtils.DestroyPingingClient(); + } if (FikaBackendUtils.JoinMatch(profileId, serverId, out CreateMatch result, out string errorMessage)) {