Skip to content

Commit

Permalink
Reducing allocations and error handling improvements (#2040)
Browse files Browse the repository at this point in the history
  • Loading branch information
Measurity authored May 22, 2023
2 parents 5ab5333 + 57dfce1 commit b0bdcd6
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Buffers;
using System.Threading;
using System.Threading.Tasks;
using LiteNetLib;
Expand Down Expand Up @@ -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<byte>.Shared.Rent(packetDataLength);
try
{
reader.GetBytes(packetData, packetDataLength);
Packet packet = Packet.Deserialize(packetData);
packetReceiver.PacketReceived(packet);
networkDebugger?.PacketReceived(packet, packetDataLength);
}
finally
{
ArrayPool<byte>.Shared.Return(packetData, true);
}
}

private void Connected(NetPeer peer)
Expand Down
65 changes: 48 additions & 17 deletions NitroxModel/Helper/NatHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,77 @@ namespace NitroxModel.Helper;

public static class NatHelper
{
public static async Task<IPAddress> GetExternalIpAsync()
public static async Task<IPAddress> GetExternalIpAsync() => await MonoNatHelper.GetFirstAsync(static async device =>
{
return await MonoNatHelper.GetFirstAsync(async d => await d.GetExternalIPAsync().ConfigureAwait(false)).ConfigureAwait(false);
}
try
{
return await device.GetExternalIPAsync().ConfigureAwait(false);
}
catch (Exception)
{
return null;
}
}).ConfigureAwait(false);

public static async Task<bool> 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<Mapping> 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<bool> AddPortMappingAsync(ushort port, Protocol protocol)
public static async Task<ResultCodes> 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) =>
{
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<EndPoint, INatDevice> discoveredDevices = new();
Expand Down Expand Up @@ -116,15 +145,17 @@ void Handler(object sender, DeviceEventArgs args)
return discoveredDevices.Values;
}

public static async Task<TResult> GetFirstAsync<TResult>(Func<INatDevice, Task<TResult>> predicate)
public static async Task<TResult> GetFirstAsync<TResult>(Func<INatDevice, Task<TResult>> predicate) => await GetFirstAsync(static (device, p) => p(device), predicate);

public static async Task<TResult> GetFirstAsync<TResult, TExtraParam>(Func<INatDevice, TExtraParam, Task<TResult>> predicate, TExtraParam parameter)
{
// Start NAT discovery (if it hasn't started yet).
Task<IEnumerable<INatDevice>> discoverTask = DiscoverAsync();
if (discoverTask.IsCompleted && discoveredDevices.IsEmpty)
{
return default;
}

// Progressively handle devices until first not-null/false result or when discovery times out.
ConcurrentDictionary<EndPoint, INatDevice> handledDevices = new();
do
Expand All @@ -135,20 +166,20 @@ public static async Task<TResult> GetFirstAsync<TResult>(Func<INatDevice, Task<T
await Task.Delay(10).ConfigureAwait(false);
continue;
}

foreach (KeyValuePair<EndPoint, INatDevice> 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;
}
}
}
} while (!discoverTask.IsCompleted);

return default;
}
}
Expand Down
12 changes: 6 additions & 6 deletions NitroxModel/Helper/NetHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
};

Expand Down Expand Up @@ -82,7 +82,7 @@ public static async Task<IPAddress> 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/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
{
Expand Down
39 changes: 25 additions & 14 deletions NitroxServer/Communication/LiteNetLib/LiteNetLibServer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading;
using System.Buffers;
using System.Threading;
using System.Threading.Tasks;
using LiteNetLib;
using LiteNetLib.Utils;
Expand Down Expand Up @@ -64,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;
}
}

Expand Down Expand Up @@ -121,12 +126,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<byte>.Shared.Rent(packetDataLength);
try
{
reader.GetBytes(packetData, packetDataLength);
Packet packet = Packet.Deserialize(packetData);
NitroxConnection connection = GetConnection(peer.Id);
ProcessIncomingData(connection, packet);
}
finally
{
ArrayPool<byte>.Shared.Return(packetData, true);
}
}

private NitroxConnection GetConnection(int remoteIdentifier)
Expand Down

0 comments on commit b0bdcd6

Please sign in to comment.