using System.Net.NetworkInformation;
using Microsoft.Win32;
using SpdUp.Core;
namespace SpdUp.Services;
///
/// Network tweaks for gaming: DNS, TCP settings, latency checks.
/// All changes are reversible via Restore methods.
///
public sealed class NetworkService
{
private readonly Dictionary _previousRegValues = new();
///
/// Measure round-trip ping to a host.
///
public static async Task PingAsync(string host = "8.8.8.8", int timeoutMs = 2000)
{
try
{
using var ping = new Ping();
var reply = await ping.SendPingAsync(host, timeoutMs);
return reply.Status == IPStatus.Success ? reply.RoundtripTime : -1;
}
catch
{
return -1;
}
}
///
/// Apply TCP gaming optimisations via the registry (Nagle, TCP ack frequency, etc.).
///
public void ApplyTcpOptimizations()
{
try
{
const string tcpKey = @"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters";
using var key = Registry.LocalMachine.OpenSubKey(tcpKey, writable: true);
if (key is null) return;
SetRegistryValueWithBackup(key, tcpKey, "TcpAckFrequency", 1, RegistryValueKind.DWord);
SetRegistryValueWithBackup(key, tcpKey, "TCPNoDelay", 1, RegistryValueKind.DWord);
SetRegistryValueWithBackup(key, tcpKey, "TcpDelAckTicks", 0, RegistryValueKind.DWord);
// Also per-interface
const string ifacesKey = @"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces";
using var ifacesRoot = Registry.LocalMachine.OpenSubKey(ifacesKey);
if (ifacesRoot is not null)
{
foreach (var subKeyName in ifacesRoot.GetSubKeyNames())
{
using var ifaceKey = ifacesRoot.OpenSubKey(subKeyName, writable: true);
if (ifaceKey is null) continue;
var fullPath = $"{ifacesKey}\\{subKeyName}";
SetRegistryValueWithBackup(ifaceKey, fullPath, "TcpAckFrequency", 1, RegistryValueKind.DWord);
SetRegistryValueWithBackup(ifaceKey, fullPath, "TCPNoDelay", 1, RegistryValueKind.DWord);
}
}
Logger.Info("TCP gaming optimizations applied.");
}
catch (Exception ex)
{
Logger.Error("Failed to apply TCP optimizations", ex);
}
}
///
/// Restore all TCP settings to their previous values.
///
public void RestoreTcpSettings()
{
try
{
foreach (var kvp in _previousRegValues)
{
var parts = kvp.Key.Split('|');
if (parts.Length != 2) continue;
using var key = Registry.LocalMachine.OpenSubKey(parts[0], writable: true);
if (key is null) continue;
if (kvp.Value is null)
key.DeleteValue(parts[1], throwOnMissingValue: false);
else
key.SetValue(parts[1], kvp.Value);
}
_previousRegValues.Clear();
Logger.Info("TCP settings restored.");
}
catch (Exception ex)
{
Logger.Error("Failed to restore TCP settings", ex);
}
}
private void SetRegistryValueWithBackup(RegistryKey key, string keyPath, string valueName, object newValue, RegistryValueKind kind)
{
var backupKey = $"{keyPath}|{valueName}";
if (!_previousRegValues.ContainsKey(backupKey))
_previousRegValues[backupKey] = key.GetValue(valueName); // null if not present
key.SetValue(valueName, newValue, kind);
}
}