aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTSRBerry <20988865+TSRBerry@users.noreply.github.com>2023-04-16 17:25:20 +0200
committerGitHub <noreply@github.com>2023-04-16 15:25:20 +0000
commit69b6ef7a4ae36994c293e423e1203096c294744c (patch)
tree3e8de70aee70ae0d28fd2684bea6e6606142d9cf
parent40e87c634ece65da3f740fcfbb6acb43e5cd71b3 (diff)
[GUI] Add network interface dropdown (#4597)1.1.717
* 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
-rw-r--r--Ryujinx.Ava/AppHost.cs10
-rw-r--r--Ryujinx.Ava/Assets/Locales/en_US.json5
-rw-r--r--Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs36
-rw-r--r--Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml15
-rw-r--r--Ryujinx.Common/Utilities/NetworkHelpers.cs66
-rw-r--r--Ryujinx.HLE/HLEConfiguration.cs57
-rw-r--r--Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs50
-rw-r--r--Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs4
-rw-r--r--Ryujinx.Headless.SDL2/Options.cs3
-rw-r--r--Ryujinx.Headless.SDL2/Program.cs3
-rw-r--r--Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs7
-rw-r--r--Ryujinx.Ui.Common/Configuration/ConfigurationState.cs51
-rw-r--r--Ryujinx/Ui/MainWindow.cs3
-rw-r--r--Ryujinx/Ui/Windows/SettingsWindow.cs18
-rw-r--r--Ryujinx/Ui/Windows/SettingsWindow.glade145
15 files changed, 384 insertions, 89 deletions
diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs
index ae9e8e53..957a1c9d 100644
--- a/Ryujinx.Ava/AppHost.cs
+++ b/Ryujinx.Ava/AppHost.cs
@@ -177,6 +177,8 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
+ ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
+
_gpuCancellationTokenSource = new CancellationTokenSource();
}
@@ -383,6 +385,11 @@ namespace Ryujinx.Ava
});
}
+ private void UpdateLanInterfaceIdState(object sender, ReactiveEventArgs<string> e)
+ {
+ Device.Configuration.MultiplayerLanInterfaceId = e.NewValue;
+ }
+
public void Stop()
{
_isActive = false;
@@ -739,7 +746,8 @@ namespace Ryujinx.Ava
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);
}
diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json
index b2d6b43d..3a4bfc65 100644
--- a/Ryujinx.Ava/Assets/Locales/en_US.json
+++ b/Ryujinx.Ava/Assets/Locales/en_US.json
@@ -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"
}
diff --git a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs
index cbba7fb9..232c9d43 100644
--- a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs
+++ b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs
@@ -23,6 +23,7 @@ using System.Collections.Generic;
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
@@ -35,6 +36,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private readonly List<string> _validTzRegions;
+ private readonly Dictionary<string, string> _networkInterfaces;
+
private float _customResolutionScale;
private int _resolutionScale;
private int _graphicsBackendMultithreadingIndex;
@@ -50,6 +53,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public event Action CloseWindow;
public event Action SaveSettingsEvent;
+ private int _networkInterfaceIndex;
public int ResolutionScale
{
@@ -240,6 +244,11 @@ namespace Ryujinx.Ava.UI.ViewModels
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;
@@ -251,6 +260,16 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
+ 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;
@@ -267,8 +286,10 @@ namespace Ryujinx.Ava.UI.ViewModels
TimeZones = new AvaloniaList<TimeZone>();
AvailableGpus = new ObservableCollection<ComboBoxItem>();
_validTzRegions = new List<string>();
+ _networkInterfaces = new Dictionary<string, string>();
CheckSoundBackends();
+ PopulateNetworkInterfaces();
if (Program.PreviewerDetached)
{
@@ -327,6 +348,17 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
+ 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))
@@ -414,6 +446,8 @@ namespace Ryujinx.Ava.UI.ViewModels
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()
@@ -515,6 +549,8 @@ namespace Ryujinx.Ava.UI.ViewModels
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();
diff --git a/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml b/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml
index 8efd367d..ab8a7f6d 100644
--- a/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml
+++ b/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"
@@ -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> \ No newline at end of file
+</UserControl>
diff --git a/Ryujinx.Common/Utilities/NetworkHelpers.cs b/Ryujinx.Common/Utilities/NetworkHelpers.cs
new file mode 100644
index 00000000..b2f6f45d
--- /dev/null
+++ b/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);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HLEConfiguration.cs b/Ryujinx.HLE/HLEConfiguration.cs
index e21157f9..df8dea6d 100644
--- a/Ryujinx.HLE/HLEConfiguration.cs
+++ b/Ryujinx.HLE/HLEConfiguration.cs
@@ -154,6 +154,11 @@ namespace Ryujinx.HLE
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>
public Action RefreshInputConfig { internal get; set; }
@@ -181,32 +186,34 @@ namespace Ryujinx.HLE
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;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs
index fd711040..e9712e92 100644
--- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs
+++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs
@@ -6,7 +6,6 @@ using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types;
using System;
using System.Net.NetworkInformation;
using System.Runtime.CompilerServices;
-using System.Text;
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
{
@@ -16,6 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
private IPInterfaceProperties _targetPropertiesCache = null;
private UnicastIPAddressInformation _targetAddressInfoCache = null;
+ private string _cacheChosenInterface = null;
public IGeneralService()
{
@@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
{
ulong networkProfileDataPosition = context.Request.RecvListBuff[0].Position;
- (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface();
+ (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context);
if (interfaceProperties == null || unicastAddress == null)
{
@@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
// GetCurrentIpAddress() -> nn::nifm::IpV4Address
public ResultCode GetCurrentIpAddress(ServiceCtx context)
{
- (_, UnicastIPAddressInformation unicastAddress) = GetLocalInterface();
+ (_, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context);
if (unicastAddress == null)
{
@@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
// GetCurrentIpConfigInfo() -> (nn::nifm::IpAddressSetting, nn::nifm::DnsSetting)
public ResultCode GetCurrentIpConfigInfo(ServiceCtx context)
{
- (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface();
+ (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context);
if (interfaceProperties == null || unicastAddress == null)
{
@@ -163,51 +163,23 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
return ResultCode.Success;
}
- private (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface()
+ private (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(ServiceCtx context)
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
return (null, null);
}
- if (_targetPropertiesCache != null && _targetAddressInfoCache != null)
- {
- return (_targetPropertiesCache, _targetAddressInfoCache);
- }
-
- IPInterfaceProperties targetProperties = null;
- UnicastIPAddressInformation targetAddressInfo = null;
+ string chosenInterface = context.Device.Configuration.MultiplayerLanInterfaceId;
- NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
-
- foreach (NetworkInterface adapter in interfaces)
+ if (_targetPropertiesCache == null || _targetAddressInfoCache == null || _cacheChosenInterface != chosenInterface)
{
- // Ignore loopback and non IPv4 capable interface.
- if (targetProperties == null && adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4))
- {
- IPInterfaceProperties properties = adapter.GetIPProperties();
-
- if (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)
- {
- targetProperties = properties;
- targetAddressInfo = info;
-
- break;
- }
- }
- }
- }
- }
+ _cacheChosenInterface = chosenInterface;
- _targetPropertiesCache = targetProperties;
- _targetAddressInfoCache = targetAddressInfo;
+ (_targetPropertiesCache, _targetAddressInfoCache) = NetworkHelpers.GetLocalInterface(chosenInterface);
+ }
- return (targetProperties, targetAddressInfo);
+ return (_targetPropertiesCache, _targetAddressInfoCache);
}
private void LocalInterfaceCacheHandler(object sender, EventArgs e)
diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs
index 30667b92..59c1f6a7 100644
--- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs
+++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs
@@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
IsDhcpEnabled = OperatingSystem.IsMacOS() || interfaceProperties.DhcpServerAddresses.Count != 0;
Address = new IpV4Address(unicastIPAddressInformation.Address);
IPv4Mask = new IpV4Address(unicastIPAddressInformation.IPv4Mask);
- GatewayAddress = new IpV4Address(interfaceProperties.GatewayAddresses[0].Address);
+ GatewayAddress = (interfaceProperties.GatewayAddresses.Count == 0) ? new IpV4Address() : new IpV4Address(interfaceProperties.GatewayAddresses[0].Address);
}
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx.Headless.SDL2/Options.cs b/Ryujinx.Headless.SDL2/Options.cs
index 5138a053..982d0990 100644
--- a/Ryujinx.Headless.SDL2/Options.cs
+++ b/Ryujinx.Headless.SDL2/Options.cs
@@ -132,6 +132,9 @@ namespace Ryujinx.Headless.SDL2
[Option("use-hypervisor", Required = false, Default = true, HelpText = "Uses Hypervisor over JIT if available.")]
public bool UseHypervisor { get; set; }
+ [Option("lan-interface-id", Required = false, Default = "0", HelpText = "GUID for the network interface used by LAN.")]
+ public string MultiplayerLanInterfaceId { get; set; }
+
// Logging
[Option("disable-file-logging", Required = false, Default = false, HelpText = "Disables logging to a file on disk.")]
diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs
index 35cc74f4..b0bdb97f 100644
--- a/Ryujinx.Headless.SDL2/Program.cs
+++ b/Ryujinx.Headless.SDL2/Program.cs
@@ -548,7 +548,8 @@ namespace Ryujinx.Headless.SDL2
options.IgnoreMissingServices,
options.AspectRatio,
options.AudioVolume,
- options.UseHypervisor);
+ options.UseHypervisor,
+ options.MultiplayerLanInterfaceId);
return new Switch(configuration);
}
diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs
index cb9adc86..c9e7f80e 100644
--- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs
+++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration
/// <summary>
/// The current version of the file format
/// </summary>
- public const int CurrentVersion = 45;
+ public const int CurrentVersion = 46;
/// <summary>
/// Version of the configuration file format
@@ -351,6 +351,11 @@ namespace Ryujinx.Ui.Common.Configuration
public string PreferredGpu { get; set; }
/// <summary>
+ /// GUID for the network interface used by LAN (or 0 for default)
+ /// </summary>
+ public string MultiplayerLanInterfaceId { get; set; }
+
+ /// <summary>
/// Uses Hypervisor over JIT if available
/// </summary>
public bool UseHypervisor { get; set; }
diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs
index 12cc1b8f..3a68cc26 100644
--- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs
+++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs
@@ -518,6 +518,22 @@ namespace Ryujinx.Ui.Common.Configuration
}
/// <summary>
+ /// Multiplayer configuration section
+ /// </summary>
+ public class MultiplayerSection
+ {
+ /// <summary>
+ /// GUID for the network interface used by LAN (or 0 for default)
+ /// </summary>
+ public ReactiveObject<string> LanInterfaceId { get; private set; }
+
+ public MultiplayerSection()
+ {
+ LanInterfaceId = new ReactiveObject<string>();
+ }
+ }
+
+ /// <summary>
/// The default configuration instance
/// </summary>
public static ConfigurationState Instance { get; private set; }
@@ -548,6 +564,11 @@ namespace Ryujinx.Ui.Common.Configuration
public HidSection Hid { get; private set; }
/// <summary>
+ /// The Multiplayer section
+ /// </summary>
+ public MultiplayerSection Multiplayer { get; private set; }
+
+ /// <summary>
/// Enables or disables Discord Rich Presence
/// </summary>
public ReactiveObject<bool> EnableDiscordIntegration { get; private set; }
@@ -574,6 +595,7 @@ namespace Ryujinx.Ui.Common.Configuration
System = new SystemSection();
Graphics = new GraphicsSection();
Hid = new HidSection();
+ Multiplayer = new MultiplayerSection();
EnableDiscordIntegration = new ReactiveObject<bool>();
CheckUpdatesOnStart = new ReactiveObject<bool>();
ShowConfirmExit = new ReactiveObject<bool>();
@@ -674,7 +696,8 @@ namespace Ryujinx.Ui.Common.Configuration
ControllerConfig = new List<JsonObject>(),
InputConfig = Hid.InputConfig,
GraphicsBackend = Graphics.GraphicsBackend,
- PreferredGpu = Graphics.PreferredGpu
+ PreferredGpu = Graphics.PreferredGpu,
+ MultiplayerLanInterfaceId = Multiplayer.LanInterfaceId
};
return configurationFile;
@@ -727,6 +750,7 @@ namespace Ryujinx.Ui.Common.Configuration
System.ExpandRam.Value = false;
System.IgnoreMissingServices.Value = false;
System.UseHypervisor.Value = true;
+ Multiplayer.LanInterfaceId.Value = "0";
Ui.GuiColumns.FavColumn.Value = true;
Ui.GuiColumns.IconColumn.Value = true;
Ui.GuiColumns.AppColumn.Value = true;
@@ -1308,6 +1332,15 @@ namespace Ryujinx.Ui.Common.Configuration
configurationFileUpdated = true;
}
+ if (configurationFileFormat.Version < 46)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 45.");
+
+ configurationFileFormat.MultiplayerLanInterfaceId = "0";
+
+ configurationFileUpdated = true;
+ }
+
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.ResScale.Value = configurationFileFormat.ResScale;
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
@@ -1366,12 +1399,12 @@ namespace Ryujinx.Ui.Common.Configuration
Ui.ColumnSort.SortColumnId.Value = configurationFileFormat.ColumnSort.SortColumnId;
Ui.ColumnSort.SortAscending.Value = configurationFileFormat.ColumnSort.SortAscending;
Ui.GameDirs.Value = configurationFileFormat.GameDirs;
- Ui.ShownFileTypes.NSP.Value = configurationFileFormat.ShownFileTypes.NSP;
- Ui.ShownFileTypes.PFS0.Value = configurationFileFormat.ShownFileTypes.PFS0;
- Ui.ShownFileTypes.XCI.Value = configurationFileFormat.ShownFileTypes.XCI;
- Ui.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA;
- Ui.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO;
- Ui.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO;
+ Ui.ShownFileTypes.NSP.Value = configurationFileFormat.ShownFileTypes.NSP;
+ Ui.ShownFileTypes.PFS0.Value = configurationFileFormat.ShownFileTypes.PFS0;
+ Ui.ShownFileTypes.XCI.Value = configurationFileFormat.ShownFileTypes.XCI;
+ Ui.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA;
+ Ui.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO;
+ Ui.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO;
Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme;
Ui.LanguageCode.Value = configurationFileFormat.LanguageCode;
Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath;
@@ -1393,6 +1426,8 @@ namespace Ryujinx.Ui.Common.Configuration
Hid.InputConfig.Value = new List<InputConfig>();
}
+ Multiplayer.LanInterfaceId.Value = configurationFileFormat.MultiplayerLanInterfaceId;
+
if (configurationFileUpdated)
{
ToFileFormat().SaveConfig(configurationFilePath);
@@ -1418,4 +1453,4 @@ namespace Ryujinx.Ui.Common.Configuration
Instance = new ConfigurationState();
}
}
-} \ No newline at end of file
+}
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index 3e513dd2..bf96e18a 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -598,7 +598,8 @@ namespace Ryujinx.Ui
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);
_emulationContext = new HLE.Switch(configuration);
}
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs
index f049da50..27080bda 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.cs
+++ b/Ryujinx/Ui/Windows/SettingsWindow.cs
@@ -17,6 +17,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.Net.NetworkInformation;
using System.Reflection;
using System.Threading.Tasks;
using GUI = Gtk.Builder.ObjectAttribute;
@@ -84,6 +85,7 @@ namespace Ryujinx.Ui.Windows
[GUI] Adjustment _systemTimeDaySpinAdjustment;
[GUI] Adjustment _systemTimeHourSpinAdjustment;
[GUI] Adjustment _systemTimeMinuteSpinAdjustment;
+ [GUI] ComboBoxText _multiLanSelect;
[GUI] CheckButton _custThemeToggle;
[GUI] Entry _custThemePath;
[GUI] ToggleButton _browseThemePath;
@@ -348,6 +350,8 @@ namespace Ryujinx.Ui.Windows
UpdatePreferredGpuComboBox();
_graphicsBackend.Changed += (sender, e) => UpdatePreferredGpuComboBox();
+ PopulateNetworkInterfaces();
+ _multiLanSelect.SetActiveId(ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value);
_custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath;
_resScaleText.Buffer.Text = ConfigurationState.Instance.Graphics.ResScaleCustom.Value.ToString();
@@ -490,6 +494,19 @@ namespace Ryujinx.Ui.Windows
}
}
+ private void PopulateNetworkInterfaces()
+ {
+ NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
+
+ foreach (NetworkInterface nif in interfaces)
+ {
+ string guid = nif.Id;
+ string name = nif.Name;
+
+ _multiLanSelect.Append(guid, name);
+ }
+ }
+
private void UpdateSystemTimeSpinners()
{
//Bind system time events
@@ -616,6 +633,7 @@ namespace Ryujinx.Ui.Windows
ConfigurationState.Instance.Graphics.AntiAliasing.Value = Enum.Parse<AntiAliasing>(_antiAliasing.ActiveId);
ConfigurationState.Instance.Graphics.ScalingFilter.Value = Enum.Parse<ScalingFilter>(_scalingFilter.ActiveId);
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value = (int)_scalingFilterLevel.Value;
+ ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _multiLanSelect.ActiveId;
_previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value;
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/Ryujinx/Ui/Windows/SettingsWindow.glade
index c19c1db9..8ae6ea72 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.glade
+++ b/Ryujinx/Ui/Windows/SettingsWindow.glade
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.38.2 -->
+<!-- Generated with glade 3.40.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkAdjustment" id="_fsLogSpinAdjustment">
@@ -7,6 +7,12 @@
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
+ <object class="GtkAdjustment" id="_scalingFilterLevel">
+ <property name="upper">101</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">5</property>
+ <property name="page-size">1</property>
+ </object>
<object class="GtkAdjustment" id="_systemTimeDaySpinAdjustment">
<property name="lower">1</property>
<property name="upper">31</property>
@@ -40,13 +46,6 @@
<property name="inline-completion">True</property>
<property name="inline-selection">True</property>
</object>
- <object class="GtkAdjustment" id="_scalingFilterLevel">
- <property name="lower">0</property>
- <property name="upper">101</property>
- <property name="step-increment">1</property>
- <property name="page-increment">5</property>
- <property name="page-size">1</property>
- </object>
<object class="GtkWindow" id="_settingsWin">
<property name="can-focus">False</property>
<property name="title" translatable="yes">Ryujinx - Settings</property>
@@ -2862,6 +2861,136 @@
<property name="tab-fill">False</property>
</packing>
</child>
+ <child>
+ <object class="GtkBox" id="TabMultiplayer">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-left">5</property>
+ <property name="margin-right">10</property>
+ <property name="margin-top">5</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="CatLAN">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="valign">start</property>
+ <property name="margin-left">5</property>
+ <property name="margin-right">5</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin-bottom">5</property>
+ <property name="label" translatable="yes">LAN Mode</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="LANOptions">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="valign">start</property>
+ <property name="margin-left">10</property>
+ <property name="margin-right">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="NetworkInterfaceBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="tooltip-text" translatable="yes">The network interface used for LAN features</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">Network Interface:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="_multiLanSelect">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="tooltip-text" translatable="yes">The network interface used for LAN features</property>
+ <property name="active-id">0</property>
+ <items>
+ <item id="0" translatable="yes">Default</item>
+ </items>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin-bottom">5</property>
+ <property name="label" translatable="yes">To use LAN functionality in games, Enable Guest Internet Access must be checked in System.</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Multiplayer</property>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ <property name="tab-fill">False</property>
+ </packing>
+ </child>
</object>
</child>
</object>