From 45f1ed2a75c3bee213bea94aab97112aa7a9b59c Mon Sep 17 00:00:00 2001 From: Measurity Date: Mon, 15 May 2023 19:10:21 +0200 Subject: [PATCH 1/5] Removed closure captures in NatHelper.cs --- NitroxModel/Helper/NatHelper.cs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/NitroxModel/Helper/NatHelper.cs b/NitroxModel/Helper/NatHelper.cs index 87ec2961ab..ee5668e5c9 100644 --- a/NitroxModel/Helper/NatHelper.cs +++ b/NitroxModel/Helper/NatHelper.cs @@ -11,46 +11,42 @@ namespace NitroxModel.Helper; public static class NatHelper { - public static async Task GetExternalIpAsync() - { - return await MonoNatHelper.GetFirstAsync(async d => await d.GetExternalIPAsync().ConfigureAwait(false)).ConfigureAwait(false); - } + public static async Task GetExternalIpAsync() => await MonoNatHelper.GetFirstAsync(static async device => await device.GetExternalIPAsync().ConfigureAwait(false)).ConfigureAwait(false); public static async Task DeletePortMappingAsync(ushort port, Protocol protocol) { - Mapping mapping = new(protocol, port, port); - return await MonoNatHelper.GetFirstAsync(async d => + return await MonoNatHelper.GetFirstAsync(static async (device, mapping) => { try { - return await d.DeletePortMapAsync(mapping).ConfigureAwait(false) != null; + return await device.DeletePortMapAsync(mapping).ConfigureAwait(false) != null; } catch (MappingException) { return false; } - }).ConfigureAwait(false); + }, new Mapping(protocol, port, port)).ConfigureAwait(false); } public static async Task GetPortMappingAsync(ushort port, Protocol protocol) { - return await MonoNatHelper.GetFirstAsync(async d => + return await MonoNatHelper.GetFirstAsync(static async (device, protocolAndPort) => { try { - return await d.GetSpecificMappingAsync(protocol, port).ConfigureAwait(false); + return await device.GetSpecificMappingAsync(protocolAndPort.protocol, protocolAndPort.port).ConfigureAwait(false); } catch (Exception) { return null; } - }).ConfigureAwait(false); + }, (port, protocol)).ConfigureAwait(false); } public static async Task AddPortMappingAsync(ushort port, Protocol protocol) { Mapping mapping = new(protocol, port, port); - return await MonoNatHelper.GetFirstAsync(async d => await d.CreatePortMapAsync(mapping).ConfigureAwait(false) != null).ConfigureAwait(false); + return await MonoNatHelper.GetFirstAsync(static async (device, mapping) => await device.CreatePortMapAsync(mapping).ConfigureAwait(false) != null, mapping).ConfigureAwait(false); } private static class MonoNatHelper @@ -116,7 +112,9 @@ void Handler(object sender, DeviceEventArgs args) return discoveredDevices.Values; } - public static async Task GetFirstAsync(Func> predicate) + public static async Task GetFirstAsync(Func> predicate) => await GetFirstAsync(static (device, p) => p(device), predicate); + + public static async Task GetFirstAsync(Func> predicate, TExtraParam parameter) { // Start NAT discovery (if it hasn't started yet). Task> discoverTask = DiscoverAsync(); @@ -124,7 +122,7 @@ public static async Task GetFirstAsync(Func handledDevices = new(); do @@ -135,12 +133,12 @@ public static async Task GetFirstAsync(Func pair in unhandledDevices) { if (handledDevices.TryAdd(pair.Key, pair.Value)) { - TResult result = await predicate(pair.Value); + TResult result = await predicate(pair.Value, parameter); if (result is true or not null) { return result; @@ -148,7 +146,7 @@ public static async Task GetFirstAsync(Func Date: Mon, 15 May 2023 19:14:25 +0200 Subject: [PATCH 2/5] Reduced byte array allocations when packets are received --- .../LiteNetLib/LiteNetLibClient.cs | 19 +++++++++++------ .../LiteNetLib/LiteNetLibServer.cs | 21 ++++++++++++------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/NitroxClient/Communication/NetworkingLayer/LiteNetLib/LiteNetLibClient.cs b/NitroxClient/Communication/NetworkingLayer/LiteNetLib/LiteNetLibClient.cs index f4f688fe90..ec81024360 100644 --- a/NitroxClient/Communication/NetworkingLayer/LiteNetLib/LiteNetLibClient.cs +++ b/NitroxClient/Communication/NetworkingLayer/LiteNetLib/LiteNetLibClient.cs @@ -1,3 +1,4 @@ +using System.Buffers; using System.Threading; using System.Threading.Tasks; using LiteNetLib; @@ -82,12 +83,18 @@ public void Stop() private void ReceivedNetworkData(NetPeer peer, NetDataReader reader, byte channel, DeliveryMethod deliveryMethod) { int packetDataLength = reader.GetInt(); - byte[] packetData = new byte[packetDataLength]; - reader.GetBytes(packetData, packetDataLength); - - Packet packet = Packet.Deserialize(packetData); - packetReceiver.PacketReceived(packet); - networkDebugger?.PacketReceived(packet, packetDataLength); + byte[] packetData = ArrayPool.Shared.Rent(packetDataLength); + try + { + reader.GetBytes(packetData, packetDataLength); + Packet packet = Packet.Deserialize(packetData); + packetReceiver.PacketReceived(packet); + networkDebugger?.PacketReceived(packet, packetDataLength); + } + finally + { + ArrayPool.Shared.Return(packetData, true); + } } private void Connected(NetPeer peer) diff --git a/NitroxServer/Communication/LiteNetLib/LiteNetLibServer.cs b/NitroxServer/Communication/LiteNetLib/LiteNetLibServer.cs index d92553cf51..df465a06d6 100644 --- a/NitroxServer/Communication/LiteNetLib/LiteNetLibServer.cs +++ b/NitroxServer/Communication/LiteNetLib/LiteNetLibServer.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System.Buffers; +using System.Threading; using System.Threading.Tasks; using LiteNetLib; using LiteNetLib.Utils; @@ -121,12 +122,18 @@ private void PeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) private void NetworkDataReceived(NetPeer peer, NetDataReader reader, byte channel, DeliveryMethod deliveryMethod) { int packetDataLength = reader.GetInt(); - byte[] packetData = new byte[packetDataLength]; - reader.GetBytes(packetData, packetDataLength); - - Packet packet = Packet.Deserialize(packetData); - NitroxConnection connection = GetConnection(peer.Id); - ProcessIncomingData(connection, packet); + byte[] packetData = ArrayPool.Shared.Rent(packetDataLength); + try + { + reader.GetBytes(packetData, packetDataLength); + Packet packet = Packet.Deserialize(packetData); + NitroxConnection connection = GetConnection(peer.Id); + ProcessIncomingData(connection, packet); + } + finally + { + ArrayPool.Shared.Return(packetData, true); + } } private NitroxConnection GetConnection(int remoteIdentifier) From 9cb0da74fa298368bfb247271ab6477c690cee96 Mon Sep 17 00:00:00 2001 From: Measurity Date: Mon, 15 May 2023 19:44:29 +0200 Subject: [PATCH 3/5] Added error handling for NatHelper.AddPortMappingAsync() Added more context to error messages when port forwarding fails --- NitroxModel/Helper/NatHelper.cs | 39 +++++++++++++++++-- .../LiteNetLib/LiteNetLibServer.cs | 18 +++++---- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/NitroxModel/Helper/NatHelper.cs b/NitroxModel/Helper/NatHelper.cs index ee5668e5c9..ce6eb67bcc 100644 --- a/NitroxModel/Helper/NatHelper.cs +++ b/NitroxModel/Helper/NatHelper.cs @@ -11,7 +11,17 @@ namespace NitroxModel.Helper; public static class NatHelper { - public static async Task GetExternalIpAsync() => await MonoNatHelper.GetFirstAsync(static async device => await device.GetExternalIPAsync().ConfigureAwait(false)).ConfigureAwait(false); + public static async Task GetExternalIpAsync() => await MonoNatHelper.GetFirstAsync(static async device => + { + try + { + return await device.GetExternalIPAsync().ConfigureAwait(false); + } + catch (Exception) + { + return null; + } + }).ConfigureAwait(false); public static async Task DeletePortMappingAsync(ushort port, Protocol protocol) { @@ -43,12 +53,35 @@ public static async Task GetPortMappingAsync(ushort port, Protocol prot }, (port, protocol)).ConfigureAwait(false); } - public static async Task AddPortMappingAsync(ushort port, Protocol protocol) + public static async Task AddPortMappingAsync(ushort port, Protocol protocol) { Mapping mapping = new(protocol, port, port); - return await MonoNatHelper.GetFirstAsync(static async (device, mapping) => await device.CreatePortMapAsync(mapping).ConfigureAwait(false) != null, mapping).ConfigureAwait(false); + return await MonoNatHelper.GetFirstAsync(static async (device, mapping) => + { + try + { + return await device.CreatePortMapAsync(mapping).ConfigureAwait(false) != null ? ResultCodes.SUCCESS : ResultCodes.UNKNOWN_ERROR; + } + catch (MappingException ex) + { + return ExceptionToCode(ex); + } + }, mapping).ConfigureAwait(false); + } + + public enum ResultCodes + { + SUCCESS, + CONFLICT_IN_MAPPING_ENTRY, + UNKNOWN_ERROR } + private static ResultCodes ExceptionToCode(MappingException exception) => exception.ErrorCode switch + { + ErrorCode.ConflictInMappingEntry => ResultCodes.CONFLICT_IN_MAPPING_ENTRY, + _ => ResultCodes.UNKNOWN_ERROR + }; + private static class MonoNatHelper { private static readonly ConcurrentDictionary discoveredDevices = new(); diff --git a/NitroxServer/Communication/LiteNetLib/LiteNetLibServer.cs b/NitroxServer/Communication/LiteNetLib/LiteNetLibServer.cs index df465a06d6..e139c893f5 100644 --- a/NitroxServer/Communication/LiteNetLib/LiteNetLibServer.cs +++ b/NitroxServer/Communication/LiteNetLib/LiteNetLibServer.cs @@ -65,14 +65,18 @@ private async Task PortForwardAsync(ushort port) return; } - bool isMapped = await NatHelper.AddPortMappingAsync(port, Protocol.Udp); - if (isMapped) + NatHelper.ResultCodes mappingResult = await NatHelper.AddPortMappingAsync(port, Protocol.Udp); + switch (mappingResult) { - Log.Info($"Server port {port} UDP has been automatically opened on your router (port is closed when server closes)"); - } - else - { - Log.Warn($"Failed to automatically port forward {port} UDP through UPnP. If using Hamachi or manually port-forwarding, please disregard this warning. To disable this feature you can go into the server settings."); + case NatHelper.ResultCodes.SUCCESS: + Log.Info($"Server port {port} UDP has been automatically opened on your router (port is closed when server closes)"); + break; + case NatHelper.ResultCodes.CONFLICT_IN_MAPPING_ENTRY: + Log.Warn($"Port forward for {port} UDP failed. It appears to already be port forwarded or it conflicts with another port forward rule."); + break; + case NatHelper.ResultCodes.UNKNOWN_ERROR: + Log.Warn($"Failed to port forward {port} UDP through UPnP. If using Hamachi or you've manually port-forwarded, please disregard this warning. To disable this feature you can go into the server settings."); + break; } } From 9dd0da8356137e925c6e042f6dfa6019f6a9cdf4 Mon Sep 17 00:00:00 2001 From: Measurity Date: Mon, 15 May 2023 19:45:52 +0200 Subject: [PATCH 4/5] Removed compiled flag in regex for parsing IP from site content The compiled flag slows things down unnecessarily since the regex is not used many times. --- NitroxModel/Helper/NetHelper.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/NitroxModel/Helper/NetHelper.cs b/NitroxModel/Helper/NetHelper.cs index a8f0aa3d02..6320fefba5 100644 --- a/NitroxModel/Helper/NetHelper.cs +++ b/NitroxModel/Helper/NetHelper.cs @@ -16,11 +16,11 @@ public static class NetHelper { private static readonly string[] privateNetworks = { - "10.0.0.0/8", - "127.0.0.0/8", - "172.16.0.0/12", - "192.0.0.0/24 ", - "192.168.0.0/16", + "10.0.0.0/8", + "127.0.0.0/8", + "172.16.0.0/12", + "192.0.0.0/24 ", + "192.168.0.0/16", "198.18.0.0/15", }; @@ -82,7 +82,7 @@ public static async Task GetWanIpAsync() #if RELEASE if (ip == null || ip.IsPrivate()) { - Regex regex = new(@"(?:[0-2]??[0-9]{1,2}\.){3}[0-2]??[0-9]+", RegexOptions.Compiled); + Regex regex = new(@"(?:[0-2]??[0-9]{1,2}\.){3}[0-2]??[0-9]+"); string[] sites = { "https://ipv4.icanhazip.com/", From 57dfce1bad3d254d4fffb24c9b95c26ac237c262 Mon Sep 17 00:00:00 2001 From: Measurity Date: Mon, 15 May 2023 19:47:43 +0200 Subject: [PATCH 5/5] Removed unused variable in GameSettings_SerializeInputSettings_Patch --- .../Persistent/GameSettings_SerializeInputSettings_Patch.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/NitroxPatcher/Patches/Persistent/GameSettings_SerializeInputSettings_Patch.cs b/NitroxPatcher/Patches/Persistent/GameSettings_SerializeInputSettings_Patch.cs index e5b5dd0089..56c0553bf4 100644 --- a/NitroxPatcher/Patches/Persistent/GameSettings_SerializeInputSettings_Patch.cs +++ b/NitroxPatcher/Patches/Persistent/GameSettings_SerializeInputSettings_Patch.cs @@ -16,7 +16,6 @@ public static void Postfix(GameSettings.ISerializer serializer) { ClientConfig cfg = ClientConfig.Load(NitroxUser.AppDataPath); KeyBindingManager keyBindingManager = new(); - string serializerFormat = "Input/Binding/{0}/{1}/{2}"; foreach (GameInput.BindingSet bindingSet in Enum.GetValues(typeof(GameInput.BindingSet))) {