diff --git a/Source/AkiSupport/Custom/BotDifficultyPatch.cs b/Source/AkiSupport/Custom/BotDifficultyPatch.cs index 2b28cb82..d709d33b 100644 --- a/Source/AkiSupport/Custom/BotDifficultyPatch.cs +++ b/Source/AkiSupport/Custom/BotDifficultyPatch.cs @@ -1,8 +1,8 @@ using EFT; using StayInTarkov.Networking; +using System; using System.Linq; using System.Reflection; -using System.Threading.Tasks; namespace StayInTarkov.AkiSupport.Custom { @@ -24,7 +24,14 @@ protected override MethodBase GetTargetMethod() [PatchPrefix] private static bool PatchPrefix(ref string __result, BotDifficulty botDifficulty, WildSpawnType role) { - __result = AkiBackendCommunication.Instance.GetJsonBLOCKING($"/singleplayer/settings/bot/difficulty/{role}/{botDifficulty}"); + try + { + __result = AkiBackendCommunication.Instance.GetJsonBLOCKING($"/singleplayer/settings/bot/difficulty/{role}/{botDifficulty}", 15000); + } + catch (Exception ex) + { + Logger.LogError($"Could not fetch bot difficulty: {ex}"); + } return string.IsNullOrWhiteSpace(__result); } } diff --git a/Source/AkiSupport/Custom/CoreDifficultyPatch.cs b/Source/AkiSupport/Custom/CoreDifficultyPatch.cs index 2783fccd..c961d182 100644 --- a/Source/AkiSupport/Custom/CoreDifficultyPatch.cs +++ b/Source/AkiSupport/Custom/CoreDifficultyPatch.cs @@ -1,4 +1,5 @@ using StayInTarkov.Networking; +using System; using System.Linq; using System.Reflection; @@ -22,7 +23,14 @@ protected override MethodBase GetTargetMethod() [PatchPrefix] private static bool PatchPrefix(ref string __result) { - __result = AkiBackendCommunication.Instance.GetJsonBLOCKING("/singleplayer/settings/bot/difficulty/core/core"); + try + { + __result = AkiBackendCommunication.Instance.GetJsonBLOCKING("/singleplayer/settings/bot/difficulty/core/core", 15000); + } + catch (Exception ex) + { + Logger.LogError($"Could not fetch bot core difficulty: {ex}"); + } return string.IsNullOrWhiteSpace(__result); } } diff --git a/Source/Coop/CoopPatches.cs b/Source/Coop/CoopPatches.cs index a72579ec..e0d30bf2 100644 --- a/Source/Coop/CoopPatches.cs +++ b/Source/Coop/CoopPatches.cs @@ -114,8 +114,6 @@ internal static void LeftGameDestroyEverything() GameObject.DestroyImmediate(coopGameComponent); } - AkiBackendCommunication.Instance.WebSocketClose(); - EnableDisablePatches(); } } diff --git a/Source/Networking/AkiBackendCommunication.cs b/Source/Networking/AkiBackendCommunication.cs index 3b205cac..64faf9b9 100644 --- a/Source/Networking/AkiBackendCommunication.cs +++ b/Source/Networking/AkiBackendCommunication.cs @@ -10,11 +10,11 @@ using StayInTarkov.ThirdParty; using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -25,44 +25,10 @@ namespace StayInTarkov.Networking public class AkiBackendCommunication : IDisposable { public const int DEFAULT_TIMEOUT_MS = 9999; - public const int DEFAULT_TIMEOUT_LONG_MS = 9999; - public const string PACKET_TAG_METHOD = "m"; - public const string PACKET_TAG_SERVERID = "serverId"; - public const string PACKET_TAG_DATA = "data"; - private string m_Session; + public string ProfileId; - public string ProfileId - { - get - { - return m_Session; - } - set { m_Session = value; } - } - - - - private string m_RemoteEndPoint; - - public string RemoteEndPoint - { - get - { - if (string.IsNullOrEmpty(m_RemoteEndPoint)) - m_RemoteEndPoint = StayInTarkovHelperConstants.GetBackendUrl(); - - // Remove ending slash on URI for SIT.Manager.Avalonia - if(m_RemoteEndPoint.EndsWith("/")) - m_RemoteEndPoint = m_RemoteEndPoint.Substring(0, m_RemoteEndPoint.Length - 1); - - return m_RemoteEndPoint; - - } - set { m_RemoteEndPoint = value; } - } - - private Dictionary? m_RequestHeaders = null; + public string RemoteEndPoint; private static AkiBackendCommunication? m_Instance; public static AkiBackendCommunication Instance @@ -76,11 +42,11 @@ public static AkiBackendCommunication Instance } } - public HttpClient HttpClient { get; set; } + private readonly HttpClient _httpClient; protected ManualLogSource Logger; - public WebSocketSharp.WebSocket WebSocket { get; private set; } + public WebSocketSharp.WebSocket? WebSocket { get; private set; } public long BytesSent = 0; public long BytesReceived = 0; @@ -90,85 +56,81 @@ public static AkiBackendCommunication Instance public static int PING_LIMIT_HIGH { get; } = 125; public static int PING_LIMIT_MID { get; } = 100; public static bool IsLocal; + public bool HighPingMode; + private Task? _periodicallySendPingTask; - protected AkiBackendCommunication(ManualLogSource logger = null) + protected AkiBackendCommunication(ManualLogSource? logger = null) { // disable SSL encryption ServicePointManager.Expect100Continue = true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; - if (logger != null) - Logger = logger; - else - Logger = BepInEx.Logging.Logger.CreateLogSource("Request"); - - if (string.IsNullOrEmpty(RemoteEndPoint)) - RemoteEndPoint = StayInTarkovHelperConstants.GetBackendUrl(); - - IsLocal = RemoteEndPoint.Contains("127.0.0.1") - || RemoteEndPoint.Contains("localhost"); - - GetHeaders(); - ConnectToAkiBackend(); - PeriodicallySendPing(); - //PeriodicallySendPooledData(); + Logger = logger ?? BepInEx.Logging.Logger.CreateLogSource("Request"); - var processor = StayInTarkovPlugin.Instance.GetOrAddComponent(); - Singleton.Create(processor); - Comfort.Common.Singleton.Instance.OnLatencyUpdated += OnLatencyUpdated; + foreach (string arg in Environment.GetCommandLineArgs()) + { + if (arg.Contains("-token=")) + { + ProfileId = arg.Replace("-token=", string.Empty); + break; + } + } + if (ProfileId == null) + { + throw new ArgumentNullException("could not find valid profile id for AkiBackend"); + } - HttpClient = new HttpClient(); - foreach (var item in GetHeaders()) + RemoteEndPoint = StayInTarkovHelperConstants.GetBackendUrl(); + if (RemoteEndPoint == null) { - HttpClient.DefaultRequestHeaders.Add(item.Key, item.Value); + throw new ArgumentNullException("could not find valid remote endpoint for AkiBackend"); } - HttpClient.MaxResponseContentBufferSize = long.MaxValue; - HttpClient.Timeout = new TimeSpan(0, 0, 0, 0, 1000); + RemoteEndPoint = RemoteEndPoint.TrimEnd('/'); + IsLocal = RemoteEndPoint.Contains("127.0.0.1") || RemoteEndPoint.Contains("localhost"); + + _httpClient = new HttpClient(new HttpClientHandler + { + UseCookies = false // do not use CookieContainer, let us manage cookies thru headers ourselves + }); + _httpClient.DefaultRequestHeaders.Add("Cookie", $"PHPSESSID={ProfileId}"); + _httpClient.DefaultRequestHeaders.Add("SessionId", ProfileId); + _httpClient.DefaultRequestHeaders.AcceptEncoding.TryParseAdd("deflate"); + _httpClient.MaxResponseContentBufferSize = long.MaxValue; + _httpClient.Timeout = new TimeSpan(0, 0, 0, 0, 60000); - HighPingMode = PluginConfigSettings.Instance.CoopSettings.ForceHighPingMode; + PeriodicallySendPing(); + SITGameServerClientDataProcessing.OnLatencyUpdated += OnLatencyUpdated; + HighPingMode = PluginConfigSettings.Instance!.CoopSettings.ForceHighPingMode; } - private void ConnectToAkiBackend() + private SITGameComponent? _gameComp = null; + private string? _profileId; + + public void WebSocketCreate(SITGameComponent gameComp, string profileId) { - PooledJsonToPostToUrl.Add(new KeyValuePair("/coop/connect", "{}")); - } + if (WebSocket != null) + { + throw new InvalidOperationException("in-raid lifecycle violation, WebSocket already exists"); + } - private Profile MyProfile { get; set; } + _gameComp = gameComp; + _profileId = profileId; - //private HashSet WebSocketPreviousReceived { get; set; } + var webSocketPort = PluginConfigSettings.Instance?.CoopSettings.SITWebSocketPort; + var wsUrl = $"{StayInTarkovHelperConstants.GetREALWSURL()}:{webSocketPort}/{_profileId}?"; + Logger.LogDebug($"WebSocketCreate: {wsUrl}"); - public void WebSocketCreate(Profile profile) - { - MyProfile = profile; - - Logger.LogDebug("WebSocketCreate"); - //if (WebSocket != null && WebSocket.ReadyState != WebSocketSharp.WebSocketState.Closed) - //{ - // Logger.LogDebug("WebSocketCreate:WebSocket already exits"); - // return; - //} - - Logger.LogDebug("Request Instance is connecting to WebSocket"); - - var webSocketPort = PluginConfigSettings.Instance.CoopSettings.SITWebSocketPort; - var wsUrl = $"{StayInTarkovHelperConstants.GetREALWSURL()}:{webSocketPort}/{profile.ProfileId}?"; - Logger.LogDebug(webSocketPort); - Logger.LogDebug(StayInTarkovHelperConstants.GetREALWSURL()); - Logger.LogDebug(wsUrl); - - //WebSocketPreviousReceived = new HashSet(); - WebSocket = new WebSocketSharp.WebSocket(wsUrl); - WebSocket.WaitTime = TimeSpan.FromMinutes(1); - WebSocket.EmitOnPing = true; + WebSocket = new WebSocketSharp.WebSocket(wsUrl) + { + WaitTime = TimeSpan.FromMinutes(1), + EmitOnPing = true + }; WebSocket.Connect(); - // --- - // Start up after initial Send WebSocket.OnError += WebSocket_OnError; WebSocket.OnMessage += WebSocket_OnMessage; - // --- } public void WebSocketClose() @@ -181,6 +143,9 @@ public void WebSocketClose() WebSocket.Close(WebSocketSharp.CloseStatusCode.Normal); WebSocket = null; } + + _profileId = null; + _gameComp = null; } public async void PingAsync() @@ -214,22 +179,30 @@ public void PostDownWebSocketImmediately(byte[] packet) private void WebSocket_OnError(object sender, WebSocketSharp.ErrorEventArgs e) { - Logger.LogError($"{nameof(WebSocket_OnError)}: {e.Message} {Environment.NewLine}"); - Logger.LogError($"{nameof(WebSocket_OnError)}: {e.Exception}"); + Logger.LogError($"{nameof(WebSocket_OnError)}: {e.Message} {e.Exception}"); WebSocket_OnError(); WebSocketClose(); - WebSocketCreate(MyProfile); + + if (_gameComp == null || _profileId == null) + { + Logger.LogError($"unexpected in-raid lifecycle violation {_gameComp} {_profileId}"); + return; + } + + WebSocketCreate(_gameComp, _profileId); } private void WebSocket_OnError() { - Logger.LogError($"Your PC has failed to connect and send data to the WebSocket with the port {PluginConfigSettings.Instance.CoopSettings.SITWebSocketPort} on the Server {StayInTarkovHelperConstants.GetBackendUrl()}! Application will now close."); + Logger.LogError($"Your PC has failed to connect and send data to the WebSocket with the port {PluginConfigSettings.Instance?.CoopSettings.SITWebSocketPort} on the Server {StayInTarkovHelperConstants.GetBackendUrl()}! Application will now close."); if (Singleton.Instantiated) { Singleton.Instance.Stop(Singleton.Instance.MainPlayer.ProfileId, Singleton.Instance.MyExitStatus, Singleton.Instance.MyExitLocation); } else + { Application.Quit(); + } } private void WebSocket_OnMessage(object sender, WebSocketSharp.MessageEventArgs e) @@ -238,18 +211,16 @@ private void WebSocket_OnMessage(object sender, WebSocketSharp.MessageEventArgs return; Interlocked.Add(ref BytesReceived, e.RawData.Length); - GC.AddMemoryPressure(e.RawData.Length); var d = e.RawData; if (d.Length >= 3 && d[0] != '{' && !(d[0] == 'S' && d[1] == 'I' && d[2] == 'T')) { - Singleton.Instance.ProcessFlatBuffer(d); + SITGameServerClientDataProcessing.ProcessFlatBuffer(_gameComp, d); } else { - Singleton.Instance.ProcessPacketBytes(e.RawData); + SITGameServerClientDataProcessing.ProcessPacketBytes(_gameComp, e.RawData); } - GC.RemoveMemoryPressure(e.RawData.Length); } public static AkiBackendCommunication GetRequestInstance(bool createInstance = false, ManualLogSource logger = null) @@ -262,193 +233,9 @@ public static AkiBackendCommunication GetRequestInstance(bool createInstance = f return Instance; } - public bool HighPingMode { get; set; } - - public BlockingCollection PooledBytesToPost { get; } = new BlockingCollection(); - public BlockingCollection>> PooledDictionariesToPost { get; } = new(); - public BlockingCollection>> PooledDictionaryCollectionToPost { get; } = new(); - - public BlockingCollection> PooledJsonToPostToUrl { get; } = new(); - - //public void SendDataToPool(string url, string serializedData) - //{ - // PooledJsonToPostToUrl.Add(new(url, serializedData)); - //} - - //public void SendDataToPool(string serializedData) - //{ - // if (WebSocket != null && WebSocket.ReadyState == WebSocketSharp.WebSocketState.Open) - // WebSocket.Send(serializedData); - //} - - private HashSet _previousPooledData = new HashSet(); - - //public void SendDataToPool(byte[] serializedData) - //{ - - // if (DEBUGPACKETS) - // { - // Logger.LogDebug(nameof(SendDataToPool)); - // Logger.LogDebug(Encoding.UTF8.GetString(serializedData)); - // } - // //if (_previousPooledData.Contains(Encoding.UTF8.GetString(serializedData))) - // // return; - - // //_previousPooledData.Add(Encoding.UTF8.GetString(serializedData)); - // PooledBytesToPost.Add(serializedData); - - // //if (HighPingMode) - // //{ - // // PooledBytesToPost.Add(serializedData); - // //} - // //else - // //{ - // // if (WebSocket != null && WebSocket.ReadyState == WebSocketSharp.WebSocketState.Open) - // // WebSocket.Send(serializedData); - // //} - //} - - //public void SendDataToPool(string url, Dictionary data) - //{ - // PooledDictionariesToPost.Add(new(url, data)); - //} - - //public void SendListDataToPool(string url, List> data) - //{ - // PooledDictionaryCollectionToPost.Add(data); - //} - - //private Task PeriodicallySendPooledDataTask; - - //private void PeriodicallySendPooledData() - //{ - // //PatchConstants.Logger.LogDebug($"PeriodicallySendPooledData()"); - - // PeriodicallySendPooledDataTask = Task.Run(async () => - // { - // int awaitPeriod = 1; - // //GCHelpers.EnableGC(); - // //GCHelpers.ClearGarbage(); - // //PatchConstants.Logger.LogDebug($"PeriodicallySendPooledData():In Async Task"); - - // //while (m_Instance != null) - // Stopwatch swPing = new(); - - // while (true) - // { - // if (WebSocket == null) - // { - // await Task.Delay(awaitPeriod); - // continue; - // } - - // // If there is nothing to post. Then delay 1ms (to avoid mem leak) and continue. - // if - // ( - // !PooledBytesToPost.Any() - // && !PooledDictionariesToPost.Any() - // && !PooledDictionaryCollectionToPost.Any() - // && !PooledJsonToPostToUrl.Any() - // ) - // { - // swPing.Restart(); - // await Task.Delay(awaitPeriod); - // continue; - // } - - // // This would the most common delivery from the Client - // // Pooled up bytes will now send to the Web Socket - // while (PooledBytesToPost.Any()) - // { - // //await Task.Delay(awaitPeriod); - // if (WebSocket != null) - // { - // if (WebSocket.ReadyState == WebSocketSharp.WebSocketState.Open) - // { - // while (PooledBytesToPost.TryTake(out var bytes)) - // { - // //Logger.LogDebug($"Sending bytes of {bytes.Length}b in length"); - // if (DEBUGPACKETS) - // { - // Logger.LogDebug($"SENT:{Encoding.UTF8.GetString(bytes)}"); - // } - - // WebSocket.Send(bytes); - // } - // } - // else - // { - // WebSocket_OnError(); - // } - // } - // } - // //await Task.Delay(100); - // while (PooledDictionariesToPost.Any()) - // { - // await Task.Delay(awaitPeriod); - - // KeyValuePair> d; - // if (PooledDictionariesToPost.TryTake(out d)) - // { - - // var url = d.Key; - // var json = JsonConvert.SerializeObject(d.Value); - // //var json = d.Value.ToJson(); - // if (WebSocket != null) - // { - // if (WebSocket.ReadyState == WebSocketSharp.WebSocketState.Open) - // { - // WebSocket.Send(json); - // } - // else - // { - // WebSocket_OnError(); - // } - // } - // } - // } - - // if (PooledDictionaryCollectionToPost.TryTake(out var d2)) - // { - // var json = JsonConvert.SerializeObject(d2); - // if (WebSocket != null) - // { - // if (WebSocket.ReadyState == WebSocketSharp.WebSocketState.Open) - // { - // WebSocket.Send(json); - // } - // else - // { - // StayInTarkovHelperConstants.Logger.LogError($"WS:Periodic Send:PooledDictionaryCollectionToPost:Failed!"); - // } - // } - // json = null; - // } - - // while (PooledJsonToPostToUrl.Any()) - // { - // await Task.Delay(awaitPeriod); - - // if (PooledJsonToPostToUrl.TryTake(out var kvp)) - // { - // _ = await PostJsonAsync(kvp.Key, kvp.Value, timeout: 1000, debug: true); - // } - // } - - // if (PostPingSmooth.Any() && PostPingSmooth.Count > 30) - // PostPingSmooth.TryDequeue(out _); - - // PostPingSmooth.Enqueue((int)swPing.ElapsedMilliseconds - awaitPeriod); - // PostPing = (int)Math.Round(PostPingSmooth.Average()); - // } - // }); - //} - - private Task PeriodicallySendPingTask { get; set; } - private void PeriodicallySendPing() { - PeriodicallySendPingTask = Task.Run(async () => + _periodicallySendPingTask = Task.Run(async () => { int awaitPeriod = 2000; while (true) @@ -492,145 +279,78 @@ private void OnLatencyUpdated(ushort latencyMs) Ping = (ushort)(ServerPingSmooth.Count > 0 ? Math.Round(ServerPingSmooth.Average()) : 1); } - private Dictionary GetHeaders() - { - if (m_RequestHeaders != null && m_RequestHeaders.Count > 0) - return m_RequestHeaders; - - string[] args = Environment.GetCommandLineArgs(); - - foreach (string arg in args) - { - if (arg.Contains("-token=")) - { - ProfileId = arg.Replace("-token=", string.Empty); - m_RequestHeaders = new Dictionary() - { - { "Cookie", $"PHPSESSID={ProfileId}" }, - { "SessionId", ProfileId } - }; - break; - } - } - return m_RequestHeaders; - } - - /// - /// Send request to the server and get Stream of data back - /// - /// String url endpoint example: /start - /// POST or GET - /// string json data - /// Should use compression gzip? - /// Stream or null - private async Task asyncRequestFromPath(string path, string method = "GET", string? data = null, int timeout = 9999, bool debug = false) + private async Task AsyncRequestFromPath(string path, HttpMethod method, string? data = null, int timeout = DEFAULT_TIMEOUT_MS, bool debug = false) { if (!Uri.IsWellFormedUriString(path, UriKind.Absolute)) { path = RemoteEndPoint + path; } - return await asyncRequest(new Uri(path), method, data, timeout, debug); + return await AsyncRequest(new Uri(path), method, data, timeout, debug); } - private async Task asyncRequest(Uri uri, string method = "GET", string? data = null, int timeout = 9999, bool debug = false) + private async Task AsyncRequest(Uri uri, HttpMethod method, string? data = null, int timeout = DEFAULT_TIMEOUT_MS, bool debug = false) { - var compress = true; - using (HttpClientHandler handler = new HttpClientHandler()) + HttpRequestMessage req = new(method, uri); + + if (method == HttpMethod.Post) { - using (HttpClient httpClient = new HttpClient(handler)) + if (!debug) { - handler.UseCookies = true; - handler.CookieContainer = new CookieContainer(); - httpClient.Timeout = TimeSpan.FromMilliseconds(timeout); - Uri baseAddress = new Uri(RemoteEndPoint); - foreach (var item in GetHeaders()) - { - if (item.Key == "Cookie") - { - string[] pairs = item.Value.Split(';'); - var keyValuePairs = pairs - .Select(p => p.Split(new[] { '=' }, 2)) - .Where(kvp => kvp.Length == 2) - .ToDictionary(kvp => kvp[0], kvp => kvp[1]); - foreach (var kvp in keyValuePairs) - { - handler.CookieContainer.Add(baseAddress, new Cookie(kvp.Key, kvp.Value)); - } - } - else - { - httpClient.DefaultRequestHeaders.TryAddWithoutValidation(item.Key, item.Value); - } - } - if (!debug && method == "POST") - { - httpClient.DefaultRequestHeaders.AcceptEncoding.TryParseAdd("deflate"); - } - - HttpContent? byteContent = null; - if (method.Equals("POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(data)) - { - if (debug) - { - compress = false; - httpClient.DefaultRequestHeaders.Add("debug", "1"); - } - var inputDataBytes = Encoding.UTF8.GetBytes(data); - var bytes = compress ? Zlib.Compress(inputDataBytes, ZlibCompression.Normal) : inputDataBytes; - byteContent = new ByteArrayContent(bytes); - byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - if (compress) - { - byteContent.Headers.ContentEncoding.Add("deflate"); - } - } + var bytes = Zlib.Compress(Encoding.UTF8.GetBytes(data), ZlibCompression.Normal); + var byteContent = new ByteArrayContent(bytes); + byteContent.Headers.ContentEncoding.Add("deflate"); + byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + req.Content = byteContent; + } + else + { + var byteContent = new ByteArrayContent(Encoding.UTF8.GetBytes(data)); + byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + req.Content = byteContent; + } + } - HttpResponseMessage response; - if (method.Equals("POST", StringComparison.OrdinalIgnoreCase)) - { - response = await httpClient.PostAsync(uri, byteContent); - } - else - { - response = await httpClient.GetAsync(uri); - } + if (debug) + { + req.Headers.Add("debug", "1"); + } - if (response.IsSuccessStatusCode) - { - var bytes = await response.Content.ReadAsByteArrayAsync(); + using var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromMilliseconds(timeout)); + var response = await _httpClient.SendAsync(req, cts.Token); - if (Zlib.IsCompressed(bytes)) - { - bytes = Zlib.Decompress(bytes); - } + if (response.IsSuccessStatusCode) + { + var bytes = await response.Content.ReadAsByteArrayAsync(); - return bytes; - } - else - { - StayInTarkovHelperConstants.Logger.LogError($"Unable to send api request to server.Status code" + response.StatusCode); - return null; - } + if (Zlib.IsCompressed(bytes)) + { + bytes = Zlib.Decompress(bytes); } + return bytes; + } + else + { + throw new Exception($"Failed HTTP request {method} {uri} -> {response.StatusCode}"); } } public async Task GetBundleData(string url, int timeout = 60000) { - return await asyncRequestFromPath(url, "GET", data: null, timeout); + return await AsyncRequestFromPath(url, HttpMethod.Get, data: null, timeout); } - public async Task GetJsonAsync(string url) + public async Task GetJsonAsync(string url, int timeout = DEFAULT_TIMEOUT_MS) { - var bytes = await asyncRequestFromPath(url, "GET"); + var bytes = await AsyncRequestFromPath(url, HttpMethod.Get, data: null, timeout); return Encoding.UTF8.GetString(bytes); } - public string GetJsonBLOCKING(string url) + public string GetJsonBLOCKING(string url, int timeout = DEFAULT_TIMEOUT_MS) { - return Task.Run(() => GetJsonAsync(url)).GetAwaiter().GetResult(); + return Task.Run(() => GetJsonAsync(url, timeout)).GetAwaiter().GetResult(); } public async Task PostJsonAsync(string url, string data, int timeout = DEFAULT_TIMEOUT_MS, int retryAttempts = 5, bool debug = false) @@ -647,7 +367,7 @@ public async Task PostJsonAsync(string url, string data, int timeout = D url = "/" + url; } - var bytes = await asyncRequestFromPath(url, "POST", data, timeout, debug); + var bytes = await AsyncRequestFromPath(url, HttpMethod.Post, data, timeout, debug); return Encoding.UTF8.GetString(bytes); } catch (Exception ex) @@ -659,13 +379,6 @@ public async Task PostJsonAsync(string url, string data, int timeout = D throw new Exception($"Unable to communicate with Aki Server {url} to post json data: {data}"); } - /// - /// Retrieves data asyncronously and parses response JSON to the desired type - /// - /// Desired type to Deserialize to - /// URL to call - /// data to send - /// public async Task PostJsonAsync(string url, string data, int timeout = DEFAULT_TIMEOUT_MS, int retryAttempts = 5, bool debug = false) { var rsp = await PostJsonAsync(url, data, timeout, retryAttempts, debug); @@ -680,9 +393,7 @@ internal string PostJsonBLOCKING(string url, string data, int timeout = DEFAULT_ public void Dispose() { - ProfileId = null; - RemoteEndPoint = null; - Singleton.Instance.OnLatencyUpdated -= OnLatencyUpdated; + SITGameServerClientDataProcessing.OnLatencyUpdated -= OnLatencyUpdated; } } } diff --git a/Source/Networking/GameClientTCPRelay.cs b/Source/Networking/GameClientTCPRelay.cs index 0698e9e5..0ed127ac 100644 --- a/Source/Networking/GameClientTCPRelay.cs +++ b/Source/Networking/GameClientTCPRelay.cs @@ -1,4 +1,5 @@ -using StayInTarkov.Coop.Matchmaker; +using StayInTarkov.Coop.Components.CoopGameComponents; +using StayInTarkov.Coop.Matchmaker; using StayInTarkov.Coop.NetworkPacket; using System; using System.Collections.Concurrent; @@ -27,12 +28,15 @@ public ushort Ping void Awake() { - AkiBackendCommunication.Instance.WebSocketClose(); - AkiBackendCommunication.Instance.WebSocketCreate(SITMatchmaking.Profile); } void Start() { + AkiBackendCommunication.Instance.WebSocketCreate(GetComponent(), SITMatchmaking.Profile.Id); + } + void OnDestroy() + { + AkiBackendCommunication.Instance.WebSocketClose(); } void Update() diff --git a/Source/Networking/GameClientUDP.cs b/Source/Networking/GameClientUDP.cs index 716ab244..b57b5aed 100644 --- a/Source/Networking/GameClientUDP.cs +++ b/Source/Networking/GameClientUDP.cs @@ -180,12 +180,12 @@ void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, by { if (channelNumber == SITGameServerClientDataProcessing.FLATBUFFER_CHANNEL_NUM) { - Singleton.Instance.ProcessFlatBuffer(reader.GetRemainingBytes()); + SITGameServerClientDataProcessing.ProcessFlatBuffer(GetComponent(), reader.GetRemainingBytes()); } else { var bytes = reader.GetRemainingBytes(); - Singleton.Instance.ProcessPacketBytes(bytes); + SITGameServerClientDataProcessing.ProcessPacketBytes(GetComponent(), bytes); } } diff --git a/Source/Networking/SITGameServerClientDataProcessing.cs b/Source/Networking/SITGameServerClientDataProcessing.cs index 671c3c46..a33da9b5 100644 --- a/Source/Networking/SITGameServerClientDataProcessing.cs +++ b/Source/Networking/SITGameServerClientDataProcessing.cs @@ -23,51 +23,29 @@ namespace StayInTarkov.Networking { - public class SITGameServerClientDataProcessing : NetworkBehaviour + public static class SITGameServerClientDataProcessing { public const byte FLATBUFFER_CHANNEL_NUM = 1; - public event Action OnLatencyUpdated; - private SITGameComponent SITGameComponent { get; set; } + public static event Action OnLatencyUpdated; - public ManualLogSource Logger { get; set; } + public static ManualLogSource Logger { get; set; } - void Awake() + static SITGameServerClientDataProcessing() { Logger = BepInEx.Logging.Logger.CreateLogSource($"{nameof(SITGameServerClientDataProcessing)}"); } - void Update() - { - if (Singleton.Instantiated) - { - if ((Singleton.Instance as MonoBehaviour).TryGetComponent(out var comp)) - { - SITGameComponent = comp; - } - else - { - SITGameComponent = null; - } - } - } - - public void ProcessFlatBuffer(byte[] data) + public static void ProcessFlatBuffer(SITGameComponent gameComp, byte[] data) { var buf = new ByteBuffer(data); var packet = StayInTarkov.FlatBuffers.Packet.GetRootAsPacket(buf); - if (SITGameComponent == null) - { - Logger.LogError($"{nameof(ProcessFlatBuffer)}. game component is null"); - return; - } - switch (packet.PacketType) { case AnyPacket.player_state: { var key = packet.Packet_Asplayer_state().ProfileId; - if (SITGameComponent.Players.ContainsKey(key) && SITGameComponent.Players[key] is CoopPlayerClient client) + if (gameComp.Players.ContainsKey(key) && gameComp.Players[key] is CoopPlayerClient client) { client.ReceivePlayerStatePacket(packet.Packet_Asplayer_state()); } @@ -81,7 +59,7 @@ public void ProcessFlatBuffer(byte[] data) } } - public void ProcessPacketBytes(byte[] data) + public static void ProcessPacketBytes(SITGameComponent gameComp, byte[] data) { try { @@ -97,14 +75,16 @@ public void ProcessPacketBytes(byte[] data) return; } - if (SITGameComponent == null) + if (gameComp == null) return; ISITPacket sitPacket = null; - ProcessSITPacket(data, out sitPacket); + ProcessSITPacket(gameComp, data, out sitPacket); if (sitPacket != null) - SITGameComponent.ActionPacketHandler.ActionSITPackets.Add(sitPacket); + { + gameComp.ActionPacketHandler.ActionSITPackets.Add(sitPacket); + } else { #if DEBUG @@ -121,7 +101,7 @@ public void ProcessPacketBytes(byte[] data) } } - public void ProcessSITPacket(byte[] data, out ISITPacket packet) + public static void ProcessSITPacket(SITGameComponent gameComp, byte[] data, out ISITPacket packet) { packet = null; @@ -141,9 +121,9 @@ public void ProcessSITPacket(byte[] data, out ISITPacket packet) var serverId = stringData.Substring(3, 24); // If the serverId is not the same as the one we are connected to. Return; - if (serverId != SITGameComponent.ServerId) + if (serverId != gameComp.ServerId) { - Logger.LogError($"{nameof(ProcessSITPacket)}. {serverId} does not equal {SITGameComponent.ServerId}"); + Logger.LogError($"{nameof(ProcessSITPacket)}. {serverId} does not equal {gameComp.ServerId}"); return; } @@ -154,7 +134,7 @@ public void ProcessSITPacket(byte[] data, out ISITPacket packet) packet = DeserializeIntoPacket(data, packet, bp); } - private ISITPacket DeserializeIntoPacket(byte[] data, ISITPacket packet, BasePacket bp) + private static ISITPacket DeserializeIntoPacket(byte[] data, ISITPacket packet, BasePacket bp) { var sitPacketType = StayInTarkovHelperConstants