diff options
author | TSRBerry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-16 17:25:20 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-16 15:25:20 +0000 |
commit | 69b6ef7a4ae36994c293e423e1203096c294744c (patch) | |
tree | 3e8de70aee70ae0d28fd2684bea6e6606142d9cf | |
parent | 40e87c634ece65da3f740fcfbb6acb43e5cd71b3 (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.cs | 10 | ||||
-rw-r--r-- | Ryujinx.Ava/Assets/Locales/en_US.json | 5 | ||||
-rw-r--r-- | Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs | 36 | ||||
-rw-r--r-- | Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml | 15 | ||||
-rw-r--r-- | Ryujinx.Common/Utilities/NetworkHelpers.cs | 66 | ||||
-rw-r--r-- | Ryujinx.HLE/HLEConfiguration.cs | 57 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs | 50 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs | 4 | ||||
-rw-r--r-- | Ryujinx.Headless.SDL2/Options.cs | 3 | ||||
-rw-r--r-- | Ryujinx.Headless.SDL2/Program.cs | 3 | ||||
-rw-r--r-- | Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs | 7 | ||||
-rw-r--r-- | Ryujinx.Ui.Common/Configuration/ConfigurationState.cs | 51 | ||||
-rw-r--r-- | Ryujinx/Ui/MainWindow.cs | 3 | ||||
-rw-r--r-- | Ryujinx/Ui/Windows/SettingsWindow.cs | 18 | ||||
-rw-r--r-- | Ryujinx/Ui/Windows/SettingsWindow.glade | 145 |
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> |