Compare commits

..

4 Commits

Author SHA1 Message Date
Vladimir Sokolov e75ce17137 Merge branch 'master' into Master_PR 2025-02-21 10:38:39 +10:00
Vladimir Sokolov 0d9a4fe48c Merge branch 'master' into Master_PR 2025-02-19 14:11:13 +10:00
Vladimir Sokolov 6c13e94c93 Merge branch 'master' into Master_PR 2025-02-19 09:36:44 +10:00
Vova 3e69afd110 Fix: Input page is saved only when input is changed
This is an attempt to decouple "saving gamepad settings" from the rest of the emulator settings. Should fix the issue where the gamepad was not detected when starting the emulator and the game (usually after saving settings with the gamepad turned off)
2025-02-18 19:28:13 +10:00
14 changed files with 71 additions and 50 deletions
-9
View File
@@ -1,9 +0,0 @@
namespace Ryujinx.Common
{
public static class SharedConstants
{
public const string DefaultLanPlayHost = "ryuldn.vudjun.com";
public const short LanPlayPort = 30456;
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
}
}
@@ -23,6 +23,9 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
class IUserLocalCommunicationService : IpcService, IDisposable class IUserLocalCommunicationService : IpcService, IDisposable
{ {
public static string DefaultLanPlayHost = "ryuldn.vudjun.com";
public static short LanPlayPort = 30456;
public INetworkClient NetworkClient { get; private set; } public INetworkClient NetworkClient { get; private set; }
private const int NifmRequestID = 90; private const int NifmRequestID = 90;
@@ -1089,18 +1092,20 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
case MultiplayerMode.LdnRyu: case MultiplayerMode.LdnRyu:
try try
{ {
string ldnServer = context.Device.Configuration.MultiplayerLdnServer string ldnServer = context.Device.Configuration.MultiplayerLdnServer;
?? throw new InvalidOperationException("Cannot initialize RyuLDN with a null Multiplayer server."); if (string.IsNullOrEmpty(ldnServer))
{
ldnServer = DefaultLanPlayHost;
}
if (!IPAddress.TryParse(ldnServer, out IPAddress ipAddress)) if (!IPAddress.TryParse(ldnServer, out IPAddress ipAddress))
{ {
ipAddress = Dns.GetHostEntry(ldnServer).AddressList[0]; ipAddress = Dns.GetHostEntry(ldnServer).AddressList[0];
} }
NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), SharedConstants.LanPlayPort, context.Device.Configuration); NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), LanPlayPort, context.Device.Configuration);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate RyuLDN server. Defaulting to stubbed wireless."); Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate LdnRyu server. Defaulting to stubbed wireless.");
Logger.Error?.Print(LogClass.ServiceLdn, ex.Message); Logger.Error?.Print(LogClass.ServiceLdn, ex.Message);
NetworkClient = new LdnDisabledClient(); NetworkClient = new LdnDisabledClient();
} }
+1 -1
View File
@@ -111,7 +111,7 @@ namespace Ryujinx.Input.SDL2
byte blue = packedRgb > 0 ? (byte)(packedRgb % 256) : (byte)0; byte blue = packedRgb > 0 ? (byte)(packedRgb % 256) : (byte)0;
if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0) if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0)
Logger.Debug?.Print(LogClass.Hid, "LED setting failed; probably in the middle of disconnecting."); Logger.Error?.Print(LogClass.Hid, "LED setting failed; probably in the middle of disconnecting.");
} }
private GamepadFeaturesFlag GetFeaturesFlag() private GamepadFeaturesFlag GetFeaturesFlag()
+1 -1
View File
@@ -951,7 +951,7 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Multiplayer.Mode, ConfigurationState.Instance.Multiplayer.Mode,
ConfigurationState.Instance.Multiplayer.DisableP2p, ConfigurationState.Instance.Multiplayer.DisableP2p,
ConfigurationState.Instance.Multiplayer.LdnPassphrase, ConfigurationState.Instance.Multiplayer.LdnPassphrase,
ConfigurationState.Instance.Multiplayer.GetLdnServer(), ConfigurationState.Instance.Multiplayer.LdnServer,
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value, ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value,
ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null)); ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null));
} }
+1 -1
View File
@@ -24073,4 +24073,4 @@
} }
} }
] ]
} }
@@ -52,6 +52,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
[ObservableProperty] private object _configViewModel; [ObservableProperty] private object _configViewModel;
[ObservableProperty] private string _profileName; [ObservableProperty] private string _profileName;
private bool _isLoaded; private bool _isLoaded;
public bool InitInputPage { get; set; }
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
@@ -92,6 +93,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense"); public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
public bool IsModified { get; set; } public bool IsModified { get; set; }
public bool IsInputConfigChanged { get; set; }
public event Action NotifyChangesEvent; public event Action NotifyChangesEvent;
public PlayerIndex PlayerIdChoose public PlayerIndex PlayerIdChoose
@@ -121,13 +124,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
_isLoaded = false; _isLoaded = false;
LoadConfiguration(); LoadConfiguration();
LoadDevice(); LoadDevice();
LoadProfiles(); LoadProfiles();
_isLoaded = true; _isLoaded = true;
OnPropertyChanged(); OnPropertyChanged();
} }
} }
@@ -296,7 +298,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig)); ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig));
} }
}
IsInputConfigChanged |= InitInputPage; // If the field has been changed, the control settings will be overwritten
InitInputPage = true; // initialization variable
}
public void LoadDevice() public void LoadDevice()
{ {
@@ -817,6 +822,13 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
IsModified = false; IsModified = false;
if (!IsInputConfigChanged)
{
return; //If the input settings were not touched, then do nothing
}
IsInputConfigChanged = false; // Input settings have been changed
List<InputConfig> newConfig = []; List<InputConfig> newConfig = [];
newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value); newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
@@ -65,6 +65,9 @@ namespace Ryujinx.Ava.UI.Views.Input
if (!float.IsNaN(_changeSlider) && _changeSlider != (float)check.Value) if (!float.IsNaN(_changeSlider) && _changeSlider != (float)check.Value)
{ {
(DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true; (DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true;
FlagInputConfigChanged();
_changeSlider = (float)check.Value; _changeSlider = (float)check.Value;
} }
} }
@@ -75,6 +78,9 @@ namespace Ryujinx.Ava.UI.Views.Input
if (sender is CheckBox { IsPointerOver: true }) if (sender is CheckBox { IsPointerOver: true })
{ {
(DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true; (DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true;
FlagInputConfigChanged();
_currentAssigner?.Cancel(); _currentAssigner?.Cancel();
_currentAssigner = null; _currentAssigner = null;
} }
@@ -102,6 +108,8 @@ namespace Ryujinx.Ava.UI.Views.Input
PointerPressed += MouseClick; PointerPressed += MouseClick;
FlagInputConfigChanged();
ControllerInputViewModel viewModel = (DataContext as ControllerInputViewModel); ControllerInputViewModel viewModel = (DataContext as ControllerInputViewModel);
IKeyboard keyboard = IKeyboard keyboard =
@@ -208,6 +216,11 @@ namespace Ryujinx.Ava.UI.Views.Input
} }
} }
private void FlagInputConfigChanged()
{
(DataContext as ControllerInputViewModel)!.ParentModel.IsInputConfigChanged = true;
}
private void MouseClick(object sender, PointerPressedEventArgs e) private void MouseClick(object sender, PointerPressedEventArgs e)
{ {
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed; bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
@@ -239,7 +252,6 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
gamepad?.ClearLed(); gamepad?.ClearLed();
} }
_currentAssigner?.Cancel(); _currentAssigner?.Cancel();
_currentAssigner = null; _currentAssigner = null;
} }
@@ -48,6 +48,7 @@ namespace Ryujinx.Ava.UI.Views.Input
if (result == UserResult.Yes) if (result == UserResult.Yes)
{ {
ViewModel.InitInputPage = false;
ViewModel.Save(); ViewModel.Save();
} }
@@ -60,6 +60,8 @@ namespace Ryujinx.Ava.UI.Views.Input
PointerPressed += MouseClick; PointerPressed += MouseClick;
FlagInputConfigChanged();
if (DataContext is not KeyboardInputViewModel viewModel) if (DataContext is not KeyboardInputViewModel viewModel)
return; return;
@@ -184,6 +186,11 @@ namespace Ryujinx.Ava.UI.Views.Input
} }
} }
private void FlagInputConfigChanged()
{
(DataContext as KeyboardInputViewModel)!.ParentModel.IsInputConfigChanged = true;
}
private void MouseClick(object sender, PointerPressedEventArgs e) private void MouseClick(object sender, PointerPressedEventArgs e)
{ {
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed; bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
@@ -148,11 +148,8 @@ namespace Ryujinx.Ava.UI.Windows
{ {
if ((firmwarePath.ExistsAsFile && firmwarePath.Extension is "xci" or "zip") || if ((firmwarePath.ExistsAsFile && firmwarePath.Extension is "xci" or "zip") ||
firmwarePath.ExistsAsDirectory) firmwarePath.ExistsAsDirectory)
{
await Dispatcher.UIThread.InvokeAsync(() => await Dispatcher.UIThread.InvokeAsync(() =>
ViewModel.HandleFirmwareInstallation(firmwarePath)); ViewModel.HandleFirmwareInstallation(firmwarePath));
CommandLineState.FirmwareToInstallPathArg = null;
}
else else
Logger.Notice.Print(LogClass.UI, "Invalid firmware type provided. Path must be a directory, or a .zip or .xci file."); Logger.Notice.Print(LogClass.UI, "Invalid firmware type provided. Path must be a directory, or a .zip or .xci file.");
} }
@@ -42,6 +42,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
{ {
public class ApplicationLibrary public class ApplicationLibrary
{ {
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
public Language DesiredLanguage { get; set; } public Language DesiredLanguage { get; set; }
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated; public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
public event Action<LdnGameDataReceivedEventArgs> LdnGameDataReceived; public event Action<LdnGameDataReceivedEventArgs> LdnGameDataReceived;
@@ -825,6 +826,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
public async Task RefreshLdn() public async Task RefreshLdn()
{ {
if (ConfigurationState.Instance.Multiplayer.Mode == MultiplayerMode.LdnRyu) if (ConfigurationState.Instance.Multiplayer.Mode == MultiplayerMode.LdnRyu)
{ {
try try
@@ -832,22 +834,33 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
string ldnWebHost = ConfigurationState.Instance.Multiplayer.LdnServer; string ldnWebHost = ConfigurationState.Instance.Multiplayer.LdnServer;
if (string.IsNullOrEmpty(ldnWebHost)) if (string.IsNullOrEmpty(ldnWebHost))
{ {
ldnWebHost = SharedConstants.DefaultLanPlayWebHost; ldnWebHost = DefaultLanPlayWebHost;
} }
IEnumerable<LdnGameData> ldnGameDataArray = Array.Empty<LdnGameData>();
using HttpClient httpClient = new(); using HttpClient httpClient = new();
string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games"); string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
LdnGameData[] ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData).ToArray(); ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData);
LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs(ldnGameDataArray)); LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs
return; {
LdnData = ldnGameDataArray
});
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}"); Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}");
LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs
{
LdnData = Array.Empty<LdnGameData>()
});
} }
} }
else
LdnGameDataReceived?.Invoke(LdnGameDataReceivedEventArgs.Empty); {
LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs
{
LdnData = Array.Empty<LdnGameData>()
});
}
} }
// Replace the currently stored DLC state for the game with the provided DLC state. // Replace the currently stored DLC state for the game with the provided DLC state.
@@ -5,14 +5,6 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
{ {
public class LdnGameDataReceivedEventArgs : EventArgs public class LdnGameDataReceivedEventArgs : EventArgs
{ {
public static new readonly LdnGameDataReceivedEventArgs Empty = new(null); public IEnumerable<LdnGameData> LdnData { get; set; }
public LdnGameDataReceivedEventArgs(LdnGameData[] ldnData)
{
LdnData = ldnData ?? [];
}
public LdnGameData[] LdnData { get; set; }
} }
} }
+1 -1
View File
@@ -14,7 +14,7 @@ namespace Ryujinx.Ava.Utilities
public static string OverrideBackendThreading { get; private set; } public static string OverrideBackendThreading { get; private set; }
public static string OverrideHideCursor { get; private set; } public static string OverrideHideCursor { get; private set; }
public static string BaseDirPathArg { get; private set; } public static string BaseDirPathArg { get; private set; }
public static FilePath FirmwareToInstallPathArg { get; set; } public static FilePath FirmwareToInstallPathArg { get; private set; }
public static string Profile { get; private set; } public static string Profile { get; private set; }
public static string LaunchPathArg { get; private set; } public static string LaunchPathArg { get; private set; }
public static string LaunchApplicationId { get; private set; } public static string LaunchApplicationId { get; private set; }
@@ -1,6 +1,5 @@
using ARMeilleure; using ARMeilleure;
using Gommon; using Gommon;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Configuration.System; using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities.Configuration.UI; using Ryujinx.Ava.Utilities.Configuration.UI;
using Ryujinx.Common; using Ryujinx.Common;
@@ -648,14 +647,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// </summary> /// </summary>
public ReactiveObject<string> LdnServer { get; private set; } public ReactiveObject<string> LdnServer { get; private set; }
public string GetLdnServer()
{
string ldnServer = LdnServer;
return string.IsNullOrEmpty(ldnServer)
? SharedConstants.DefaultLanPlayHost
: ldnServer;
}
public MultiplayerSection() public MultiplayerSection()
{ {
LanInterfaceId = new ReactiveObject<string>(); LanInterfaceId = new ReactiveObject<string>();