Skip to content

Commit

Permalink
[GUI] Add network interface dropdown (#4597)
Browse files Browse the repository at this point in the history
* Add network adapter dropdown from LDN build

* Ava: Add NetworkInterfaces to SettingsNetworkTab

* Add headless network interface option

* Add network interface dropdown to Avalonia

* Fix handling network interfaces without a gateway address

* gtk: Actually save selected network interface to config

* Increment config version
  • Loading branch information
TSRBerry committed Apr 16, 2023
1 parent 40e87c6 commit 69b6ef7
Show file tree
Hide file tree
Showing 15 changed files with 384 additions and 89 deletions.
10 changes: 9 additions & 1 deletion Ryujinx.Ava/AppHost.cs
Expand Up @@ -177,6 +177,8 @@ internal class AppHost
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;

ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;

_gpuCancellationTokenSource = new CancellationTokenSource();
}

Expand Down Expand Up @@ -383,6 +385,11 @@ private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
});
}

private void UpdateLanInterfaceIdState(object sender, ReactiveEventArgs<string> e)
{
Device.Configuration.MultiplayerLanInterfaceId = e.NewValue;
}

public void Stop()
{
_isActive = false;
Expand Down Expand Up @@ -739,7 +746,8 @@ private void InitializeSwitchInstance()
ConfigurationState.Instance.System.IgnoreMissingServices,
ConfigurationState.Instance.Graphics.AspectRatio,
ConfigurationState.Instance.System.AudioVolume,
ConfigurationState.Instance.System.UseHypervisor);
ConfigurationState.Instance.System.UseHypervisor,
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value);

Device = new Switch(configuration);
}
Expand Down
5 changes: 4 additions & 1 deletion Ryujinx.Ava/Assets/Locales/en_US.json
Expand Up @@ -638,5 +638,8 @@
"SmaaHigh": "SMAA High",
"SmaaUltra": "SMAA Ultra",
"UserEditorTitle" : "Edit User",
"UserEditorTitleCreate" : "Create User"
"UserEditorTitleCreate" : "Create User",
"SettingsTabNetworkInterface": "Network Interface:",
"NetworkInterfaceTooltip": "The network interface used for LAN features",
"NetworkInterfaceDefault": "Default"
}
36 changes: 36 additions & 0 deletions Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs
Expand Up @@ -23,6 +23,7 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Net.NetworkInformation;
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;

namespace Ryujinx.Ava.UI.ViewModels
Expand All @@ -35,6 +36,8 @@ public class SettingsViewModel : BaseModel

private readonly List<string> _validTzRegions;

private readonly Dictionary<string, string> _networkInterfaces;

private float _customResolutionScale;
private int _resolutionScale;
private int _graphicsBackendMultithreadingIndex;
Expand All @@ -50,6 +53,7 @@ public class SettingsViewModel : BaseModel

public event Action CloseWindow;
public event Action SaveSettingsEvent;
private int _networkInterfaceIndex;

public int ResolutionScale
{
Expand Down Expand Up @@ -240,6 +244,11 @@ public float Volume
public AvaloniaList<string> GameDirectories { get; set; }
public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; }

public AvaloniaList<string> NetworkInterfaceList
{
get => new AvaloniaList<string>(_networkInterfaces.Keys);
}

public KeyboardHotkeys KeyboardHotkeys
{
get => _keyboardHotkeys;
Expand All @@ -251,6 +260,16 @@ public KeyboardHotkeys KeyboardHotkeys
}
}

public int NetworkInterfaceIndex
{
get => _networkInterfaceIndex;
set
{
_networkInterfaceIndex = value != -1 ? value : 0;
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[_networkInterfaceIndex]];
}
}

public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
{
_virtualFileSystem = virtualFileSystem;
Expand All @@ -267,8 +286,10 @@ public SettingsViewModel()
TimeZones = new AvaloniaList<TimeZone>();
AvailableGpus = new ObservableCollection<ComboBoxItem>();
_validTzRegions = new List<string>();
_networkInterfaces = new Dictionary<string, string>();

CheckSoundBackends();
PopulateNetworkInterfaces();

if (Program.PreviewerDetached)
{
Expand Down Expand Up @@ -327,6 +348,17 @@ public void LoadTimeZones()
}
}

private void PopulateNetworkInterfaces()
{
_networkInterfaces.Clear();
_networkInterfaces.Add(LocaleManager.Instance[LocaleKeys.NetworkInterfaceDefault], "0");

foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
{
_networkInterfaces.Add(networkInterface.Name, networkInterface.Id);
}
}

public void ValidateAndSetTimeZone(string location)
{
if (_validTzRegions.Contains(location))
Expand Down Expand Up @@ -414,6 +446,8 @@ public void LoadCurrentConfiguration()
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;

NetworkInterfaceIndex = _networkInterfaces.Values.ToList().IndexOf(config.Multiplayer.LanInterfaceId.Value);
}

public void SaveSettings()
Expand Down Expand Up @@ -515,6 +549,8 @@ public void SaveSettings()
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;

config.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[NetworkInterfaceIndex]];

config.ToFileFormat().SaveConfig(Program.ConfigurationPath);

MainWindow.UpdateGraphicsConfig();
Expand Down
15 changes: 13 additions & 2 deletions Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml
@@ -1,4 +1,4 @@
<UserControl
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsNetworkView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Expand Down Expand Up @@ -29,7 +29,18 @@
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
</CheckBox>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabNetworkInterface}"
ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
Width="200" />
<ComboBox SelectedIndex="{Binding NetworkInterfaceIndex}"
ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
HorizontalContentAlignment="Left"
Items="{Binding NetworkInterfaceList}"
Width="250" />
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>
</UserControl>
66 changes: 66 additions & 0 deletions Ryujinx.Common/Utilities/NetworkHelpers.cs
@@ -0,0 +1,66 @@
using System.Net.NetworkInformation;

namespace Ryujinx.Common.Utilities
{
public static class NetworkHelpers
{
private static (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(NetworkInterface adapter, bool isPreferred)
{
IPInterfaceProperties properties = adapter.GetIPProperties();

if (isPreferred || (properties.GatewayAddresses.Count > 0 && properties.DnsAddresses.Count > 0))
{
foreach (UnicastIPAddressInformation info in properties.UnicastAddresses)
{
// Only accept an IPv4 address
if (info.Address.GetAddressBytes().Length == 4)
{
return (properties, info);
}
}
}

return (null, null);
}

public static (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(string lanInterfaceId = "0")
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
return (null, null);
}

IPInterfaceProperties targetProperties = null;
UnicastIPAddressInformation targetAddressInfo = null;

NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();

string guid = lanInterfaceId;
bool hasPreference = guid != "0";

foreach (NetworkInterface adapter in interfaces)
{
bool isPreferred = adapter.Id == guid;

// Ignore loopback and non IPv4 capable interface.
if (isPreferred || (targetProperties == null && adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4)))
{
(IPInterfaceProperties properties, UnicastIPAddressInformation info) = GetLocalInterface(adapter, isPreferred);

if (properties != null)
{
targetProperties = properties;
targetAddressInfo = info;

if (isPreferred || !hasPreference)
{
break;
}
}
}
}

return (targetProperties, targetAddressInfo);
}
}
}
57 changes: 32 additions & 25 deletions Ryujinx.HLE/HLEConfiguration.cs
Expand Up @@ -153,6 +153,11 @@ public class HLEConfiguration
/// </summary>
internal readonly bool UseHypervisor;

/// <summary>
/// Multiplayer LAN Interface ID (device GUID)
/// </summary>
public string MultiplayerLanInterfaceId { internal get; set; }

/// <summary>
/// An action called when HLE force a refresh of output after docked mode changed.
/// </summary>
Expand Down Expand Up @@ -181,32 +186,34 @@ public class HLEConfiguration
bool ignoreMissingServices,
AspectRatio aspectRatio,
float audioVolume,
bool useHypervisor)
bool useHypervisor,
string multiplayerLanInterfaceId)
{
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
AccountManager = accountManager;
ContentManager = contentManager;
UserChannelPersistence = userChannelPersistence;
GpuRenderer = gpuRenderer;
AudioDeviceDriver = audioDeviceDriver;
MemoryConfiguration = memoryConfiguration;
HostUiHandler = hostUiHandler;
SystemLanguage = systemLanguage;
Region = region;
EnableVsync = enableVsync;
EnableDockedMode = enableDockedMode;
EnablePtc = enablePtc;
EnableInternetAccess = enableInternetAccess;
FsIntegrityCheckLevel = fsIntegrityCheckLevel;
FsGlobalAccessLogMode = fsGlobalAccessLogMode;
SystemTimeOffset = systemTimeOffset;
TimeZone = timeZone;
MemoryManagerMode = memoryManagerMode;
IgnoreMissingServices = ignoreMissingServices;
AspectRatio = aspectRatio;
AudioVolume = audioVolume;
UseHypervisor = useHypervisor;
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
AccountManager = accountManager;
ContentManager = contentManager;
UserChannelPersistence = userChannelPersistence;
GpuRenderer = gpuRenderer;
AudioDeviceDriver = audioDeviceDriver;
MemoryConfiguration = memoryConfiguration;
HostUiHandler = hostUiHandler;
SystemLanguage = systemLanguage;
Region = region;
EnableVsync = enableVsync;
EnableDockedMode = enableDockedMode;
EnablePtc = enablePtc;
EnableInternetAccess = enableInternetAccess;
FsIntegrityCheckLevel = fsIntegrityCheckLevel;
FsGlobalAccessLogMode = fsGlobalAccessLogMode;
SystemTimeOffset = systemTimeOffset;
TimeZone = timeZone;
MemoryManagerMode = memoryManagerMode;
IgnoreMissingServices = ignoreMissingServices;
AspectRatio = aspectRatio;
AudioVolume = audioVolume;
UseHypervisor = useHypervisor;
MultiplayerLanInterfaceId = multiplayerLanInterfaceId;
}
}
}

0 comments on commit 69b6ef7

Please sign in to comment.