Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GUI] Add network interface dropdown #4597

Merged
merged 7 commits into from Apr 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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;
}
}
}