Merge branch 'master' into translation-it
This commit is contained in:
@@ -207,6 +207,9 @@ namespace Ryujinx.Ava
|
||||
ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState;
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
||||
ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerModeState;
|
||||
ConfigurationState.Instance.Multiplayer.LdnPassphrase.Event += UpdateLdnPassphraseState;
|
||||
ConfigurationState.Instance.Multiplayer.LdnServer.Event += UpdateLdnServerState;
|
||||
ConfigurationState.Instance.Multiplayer.DisableP2p.Event += UpdateDisableP2pState;
|
||||
|
||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||
_gpuDoneEvent = new ManualResetEvent(false);
|
||||
@@ -491,6 +494,21 @@ namespace Ryujinx.Ava
|
||||
Device.Configuration.MultiplayerMode = e.NewValue;
|
||||
}
|
||||
|
||||
private void UpdateLdnPassphraseState(object sender, ReactiveEventArgs<string> e)
|
||||
{
|
||||
Device.Configuration.MultiplayerLdnPassphrase = e.NewValue;
|
||||
}
|
||||
|
||||
private void UpdateLdnServerState(object sender, ReactiveEventArgs<string> e)
|
||||
{
|
||||
Device.Configuration.MultiplayerLdnServer = e.NewValue;
|
||||
}
|
||||
|
||||
private void UpdateDisableP2pState(object sender, ReactiveEventArgs<bool> e)
|
||||
{
|
||||
Device.Configuration.MultiplayerDisableP2p = e.NewValue;
|
||||
}
|
||||
|
||||
public void ToggleVSync()
|
||||
{
|
||||
Device.EnableDeviceVsync = !Device.EnableDeviceVsync;
|
||||
@@ -863,10 +881,11 @@ namespace Ryujinx.Ava
|
||||
ConfigurationState.Instance.Graphics.AspectRatio,
|
||||
ConfigurationState.Instance.System.AudioVolume,
|
||||
ConfigurationState.Instance.System.UseHypervisor,
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId,
|
||||
ConfigurationState.Instance.Multiplayer.Mode
|
||||
)
|
||||
);
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value,
|
||||
ConfigurationState.Instance.Multiplayer.Mode,
|
||||
ConfigurationState.Instance.Multiplayer.DisableP2p,
|
||||
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
|
||||
ConfigurationState.Instance.Multiplayer.LdnServer));
|
||||
}
|
||||
|
||||
private static IHardwareDeviceDriver InitializeAudio()
|
||||
@@ -1050,7 +1069,7 @@ namespace Ryujinx.Ava
|
||||
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld];
|
||||
|
||||
UpdateShaderCount();
|
||||
|
||||
|
||||
if (GraphicsConfig.ResScale != 1)
|
||||
{
|
||||
dockedMode += $" ({GraphicsConfig.ResScale}x)";
|
||||
|
||||
@@ -848,5 +848,17 @@
|
||||
"MultiplayerMode": "Mode:",
|
||||
"MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.",
|
||||
"MultiplayerModeDisabled": "Disabled",
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm"
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm",
|
||||
"MultiplayerModeLdnRyu": "RyuLDN",
|
||||
"MultiplayerDisableP2P": "Disable P2P Network Hosting (may increase latency)",
|
||||
"MultiplayerDisableP2PTooltip": "Disable P2P network hosting, peers will proxy through the master server instead of connecting to you directly.",
|
||||
"LdnPassphrase": "Network Passphrase:",
|
||||
"LdnPassphraseTooltip": "You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputTooltip": "Enter a passphrase in the format Ryujinx-<8 hex chars>. You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputPublic": "(public)",
|
||||
"GenLdnPass": "Generate Random",
|
||||
"GenLdnPassTooltip": "Generates a new passphrase, which can be shared with other players.",
|
||||
"ClearLdnPass": "Clear",
|
||||
"ClearLdnPassTooltip": "Clears the current passphrase, returning to the public network.",
|
||||
"InvalidLdnPassphrase": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\""
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -99,7 +99,7 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
_ => false
|
||||
};
|
||||
|
||||
public static string FormatDynamicValue(LocaleKeys key, params object[] values)
|
||||
public static string FormatDynamicValue(LocaleKeys key, params object[] values)
|
||||
=> Instance.UpdateAndGetDynamicValue(key, values);
|
||||
|
||||
public string UpdateAndGetDynamicValue(LocaleKeys key, params object[] values)
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Ryujinx.Ava
|
||||
Console.Title = $"{App.FullAppName} Console {Version}";
|
||||
|
||||
// Hook unhandled exception and process exit events.
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e)
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e)
|
||||
=> ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating);
|
||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => Exit();
|
||||
|
||||
@@ -224,16 +224,14 @@ namespace Ryujinx.Ava
|
||||
|
||||
private static void PrintSystemInfo()
|
||||
{
|
||||
Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}");
|
||||
Logger.Notice.Print(LogClass.Application, $"{App.FullAppName} Version: {Version}");
|
||||
SystemInfo.Gather().Print();
|
||||
|
||||
var enabledLogLevels = Logger.GetEnabledLevels().ToArray();
|
||||
|
||||
Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {
|
||||
(enabledLogLevels.Length is 0
|
||||
Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(enabledLogLevels.Length is 0
|
||||
? "<None>"
|
||||
: enabledLogLevels.JoinToString(", "))
|
||||
}");
|
||||
: enabledLogLevels.JoinToString(", "))}");
|
||||
|
||||
Logger.Notice.Print(LogClass.Application,
|
||||
AppDataManager.Mode == AppDataManager.LaunchMode.Custom
|
||||
@@ -245,13 +243,13 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
Logger.Log log = Logger.Error ?? Logger.Notice;
|
||||
string message = $"Unhandled exception caught: {ex}";
|
||||
|
||||
|
||||
// ReSharper disable once ConstantConditionalAccessQualifier
|
||||
if (sender?.GetType()?.AsPrettyString() is {} senderName)
|
||||
if (sender?.GetType()?.AsPrettyString() is { } senderName)
|
||||
log.Print(LogClass.Application, message, senderName);
|
||||
else
|
||||
log.PrintMsg(LogClass.Application, message);
|
||||
|
||||
|
||||
if (isTerminating)
|
||||
Exit();
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
public bool DisplayMessageDialog(ControllerAppletUIArgs args)
|
||||
{
|
||||
ManualResetEvent dialogCloseEvent = new(false);
|
||||
|
||||
|
||||
bool okPressed = false;
|
||||
|
||||
if (ConfigurationState.Instance.IgnoreApplet)
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
public AvaloniaDynamicTextInputHandler(MainWindow parent)
|
||||
{
|
||||
_parent = parent;
|
||||
|
||||
|
||||
if (_parent.InputManager.KeyboardDriver is AvaloniaKeyboardDriver avaloniaKeyboardDriver)
|
||||
{
|
||||
avaloniaKeyboardDriver.KeyPressed += AvaloniaDynamicTextInputHandler_KeyPressed;
|
||||
@@ -121,7 +121,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
avaloniaKeyboardDriver.KeyRelease -= AvaloniaDynamicTextInputHandler_KeyRelease;
|
||||
avaloniaKeyboardDriver.TextInput -= AvaloniaDynamicTextInputHandler_TextInput;
|
||||
}
|
||||
|
||||
|
||||
_textChangedSubscription?.Dispose();
|
||||
_selectionStartChangedSubscription?.Dispose();
|
||||
_selectionEndtextChangedSubscription?.Dispose();
|
||||
|
||||
@@ -37,8 +37,8 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
|
||||
public ControllerAppletDialog(MainWindow mainWindow, ControllerAppletUIArgs args)
|
||||
{
|
||||
PlayerCount = args.PlayerCountMin == args.PlayerCountMax
|
||||
? args.PlayerCountMin.ToString()
|
||||
PlayerCount = args.PlayerCountMin == args.PlayerCountMax
|
||||
? args.PlayerCountMin.ToString()
|
||||
: $"{args.PlayerCountMin} - {args.PlayerCountMax}";
|
||||
|
||||
SupportsProController = (args.SupportedStyles & ControllerType.ProController) != 0;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:converters="clr-namespace:Avalonia.Data.Converters;assembly=Avalonia.Base"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
Focusable="True"
|
||||
@@ -110,6 +111,11 @@
|
||||
Text="{Binding FileExtension}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Converter={helpers:MultiplayerInfoConverter}}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="4"
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
|
||||
if (contentManager.GetCurrentFirmwareVersion() != null)
|
||||
Task.Run(() => UserFirmwareAvatarSelectorViewModel.PreloadAvatars(contentManager, virtualFileSystem));
|
||||
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
@@ -60,13 +60,13 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
LoadProfiles();
|
||||
}
|
||||
|
||||
public void Navigate(Type sourcePageType, object parameter)
|
||||
public void Navigate(Type sourcePageType, object parameter)
|
||||
=> ContentFrame.Navigate(sourcePageType, parameter);
|
||||
|
||||
public static async Task Show(
|
||||
AccountManager ownerAccountManager,
|
||||
AccountManager ownerAccountManager,
|
||||
ContentManager ownerContentManager,
|
||||
VirtualFileSystem ownerVirtualFileSystem,
|
||||
VirtualFileSystem ownerVirtualFileSystem,
|
||||
HorizonClient ownerHorizonClient)
|
||||
{
|
||||
var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient);
|
||||
@@ -158,9 +158,9 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
_ = Dispatcher.UIThread.InvokeAsync(async ()
|
||||
=> await ContentDialogHelper.CreateErrorDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]));
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AccountManager.OpenUser(profile.UserId);
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
_key = key;
|
||||
}
|
||||
|
||||
public string this[string key] =>
|
||||
public string this[string key] =>
|
||||
_glyphs.TryGetValue(Enum.Parse<Glyph>(key), out var val)
|
||||
? val
|
||||
? val
|
||||
: string.Empty;
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider) => this[_key];
|
||||
|
||||
44
src/Ryujinx/UI/Helpers/MultiplayerInfoConverter.cs
Normal file
44
src/Ryujinx/UI/Helpers/MultiplayerInfoConverter.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
internal class MultiplayerInfoConverter : MarkupExtension, IValueConverter
|
||||
{
|
||||
private static readonly MultiplayerInfoConverter _instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is ApplicationData applicationData)
|
||||
{
|
||||
if (applicationData.PlayerCount != 0 && applicationData.GameCount != 0)
|
||||
{
|
||||
return $"Hosted Games: {applicationData.GameCount}\nOnline Players: {applicationData.PlayerCount}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,12 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
public static TimeZoneConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> value is TimeZone timeZone
|
||||
? $"{timeZone.UtcDifference} {timeZone.Location} {timeZone.Abbreviation}"
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> value is TimeZone timeZone
|
||||
? $"{timeZone.UtcDifference} {timeZone.Location} {timeZone.Abbreviation}"
|
||||
: null;
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Ryujinx.Ava.UI.Models
|
||||
public string DockedMode { get; }
|
||||
public string FifoStatus { get; }
|
||||
public string GameStatus { get; }
|
||||
|
||||
|
||||
public uint ShaderCount { get; }
|
||||
|
||||
public StatusUpdatedEventArgs(bool vSyncEnabled, string volumeStatus, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, uint shaderCount)
|
||||
|
||||
@@ -117,6 +117,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public ApplicationData ListSelectedApplication;
|
||||
public ApplicationData GridSelectedApplication;
|
||||
|
||||
public IEnumerable<LdnGameData> LastLdnGameData;
|
||||
|
||||
public static readonly Bitmap IconBitmap =
|
||||
new(Assembly.GetAssembly(typeof(ConfigurationState))!.GetManifestResourceStream("Ryujinx.UI.Common.Resources.Logo_Ryujinx.png")!);
|
||||
|
||||
@@ -173,7 +175,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
SwitchToGameControl = switchToGameControl;
|
||||
SetMainContent = setMainContent;
|
||||
TopLevel = topLevel;
|
||||
|
||||
|
||||
#if DEBUG
|
||||
topLevel.AttachDevTools(new KeyGesture(Avalonia.Input.Key.F12, KeyModifiers.Control));
|
||||
#endif
|
||||
@@ -268,7 +270,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool ShowFirmwareStatus => !ShowLoadProgress;
|
||||
|
||||
public bool ShowRightmostSeparator
|
||||
public bool ShowRightmostSeparator
|
||||
{
|
||||
get => _showRightmostSeparator;
|
||||
set
|
||||
@@ -553,7 +555,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string ShaderCountText
|
||||
{
|
||||
get => _shaderCountText;
|
||||
@@ -1021,7 +1023,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
? SortExpressionComparer<ApplicationData>.Ascending(selector)
|
||||
: SortExpressionComparer<ApplicationData>.Descending(selector);
|
||||
|
||||
private IComparer<ApplicationData> GetComparer()
|
||||
private IComparer<ApplicationData> GetComparer()
|
||||
=> SortMode switch
|
||||
{
|
||||
#pragma warning disable IDE0055 // Disable formatting
|
||||
@@ -1251,7 +1253,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private void InitializeGame()
|
||||
{
|
||||
RendererHostControl.WindowCreated += RendererHost_Created;
|
||||
|
||||
|
||||
AppHost.StatusUpdatedEvent += Update_StatusBar;
|
||||
AppHost.AppExit += AppHost_AppExit;
|
||||
|
||||
@@ -1300,9 +1302,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
GameStatusText = args.GameStatus;
|
||||
VolumeStatusText = args.VolumeStatus;
|
||||
FifoStatusText = args.FifoStatus;
|
||||
|
||||
ShaderCountText = (ShowRightmostSeparator = args.ShaderCount > 0)
|
||||
? $"{LocaleManager.Instance[LocaleKeys.CompilingShaders]}: {args.ShaderCount}"
|
||||
|
||||
ShaderCountText = (ShowRightmostSeparator = args.ShaderCount > 0)
|
||||
? $"{LocaleManager.Instance[LocaleKeys.CompilingShaders]}: {args.ShaderCount}"
|
||||
: string.Empty;
|
||||
|
||||
ShowStatusSeparator = true;
|
||||
@@ -1707,7 +1709,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
RendererHostControl.Focus();
|
||||
});
|
||||
|
||||
public static void UpdateGameMetadata(string titleId)
|
||||
public static void UpdateGameMetadata(string titleId)
|
||||
=> ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame());
|
||||
|
||||
public void RefreshFirmwareStatus()
|
||||
|
||||
@@ -25,12 +25,13 @@ using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
public class SettingsViewModel : BaseModel
|
||||
public partial class SettingsViewModel : BaseModel
|
||||
{
|
||||
private readonly VirtualFileSystem _virtualFileSystem;
|
||||
private readonly ContentManager _contentManager;
|
||||
@@ -56,6 +57,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public event Action SaveSettingsEvent;
|
||||
private int _networkInterfaceIndex;
|
||||
private int _multiplayerModeIndex;
|
||||
private string _ldnPassphrase;
|
||||
private string _LdnServer;
|
||||
|
||||
public int ResolutionScale
|
||||
{
|
||||
@@ -180,10 +183,24 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool IsVulkanSelected => GraphicsBackendIndex == 0;
|
||||
public bool UseHypervisor { get; set; }
|
||||
public bool DisableP2P { get; set; }
|
||||
|
||||
public string TimeZone { get; set; }
|
||||
public string ShaderDumpPath { get; set; }
|
||||
|
||||
public string LdnPassphrase
|
||||
{
|
||||
get => _ldnPassphrase;
|
||||
set
|
||||
{
|
||||
_ldnPassphrase = value;
|
||||
IsInvalidLdnPassphraseVisible = !ValidateLdnPassphrase(value);
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(IsInvalidLdnPassphraseVisible));
|
||||
}
|
||||
}
|
||||
|
||||
public int Language { get; set; }
|
||||
public int Region { get; set; }
|
||||
public int FsGlobalAccessLogMode { get; set; }
|
||||
@@ -276,6 +293,21 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex("Ryujinx-[0-9a-f]{8}")]
|
||||
private static partial Regex LdnPassphraseRegex();
|
||||
|
||||
public bool IsInvalidLdnPassphraseVisible { get; set; }
|
||||
|
||||
public string LdnServer
|
||||
{
|
||||
get => _LdnServer;
|
||||
set
|
||||
{
|
||||
_LdnServer = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
||||
{
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
@@ -393,6 +425,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(NetworkInterfaceIndex)));
|
||||
}
|
||||
|
||||
private bool ValidateLdnPassphrase(string passphrase)
|
||||
{
|
||||
return string.IsNullOrEmpty(passphrase) || (passphrase.Length == 16 && LdnPassphraseRegex().IsMatch(passphrase));
|
||||
}
|
||||
|
||||
public void ValidateAndSetTimeZone(string location)
|
||||
{
|
||||
if (_validTzRegions.Contains(location))
|
||||
@@ -497,6 +534,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
|
||||
|
||||
MultiplayerModeIndex = (int)config.Multiplayer.Mode.Value;
|
||||
DisableP2P = config.Multiplayer.DisableP2p.Value;
|
||||
LdnPassphrase = config.Multiplayer.LdnPassphrase.Value;
|
||||
LdnServer = config.Multiplayer.LdnServer.Value;
|
||||
}
|
||||
|
||||
public void SaveSettings()
|
||||
@@ -613,6 +653,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
config.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[NetworkInterfaceIndex]];
|
||||
config.Multiplayer.Mode.Value = (MultiplayerMode)MultiplayerModeIndex;
|
||||
config.Multiplayer.DisableP2p.Value = DisableP2P;
|
||||
config.Multiplayer.LdnPassphrase.Value = LdnPassphrase;
|
||||
config.Multiplayer.LdnServer.Value = LdnServer;
|
||||
|
||||
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
private CheckBox[] GenerateToggleFileTypeItems() =>
|
||||
Enum.GetValues<FileTypes>()
|
||||
.Select(it => (FileName: Enum.GetName(it)!, FileType: it))
|
||||
.Select(it =>
|
||||
new CheckBox
|
||||
.Select(it =>
|
||||
new CheckBox
|
||||
{
|
||||
Content = $".{it.FileName}",
|
||||
IsChecked = it.FileType.GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes),
|
||||
|
||||
@@ -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"
|
||||
@@ -36,11 +36,57 @@
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale MultiplayerModeDisabled}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale MultiplayerModeLdnRyu}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale MultiplayerModeLdnMitm}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
<CheckBox Margin="10,0,0,0" IsChecked="{Binding DisableP2P}">
|
||||
<TextBlock Text="{ext:Locale MultiplayerDisableP2P}"
|
||||
ToolTip.Tip="{ext:Locale MultiplayerDisableP2PTooltip}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{ext:Locale LdnPassphrase}"
|
||||
ToolTip.Tip="{ext:Locale LdnPassphraseTooltip}"
|
||||
Width="200" />
|
||||
<TextBox Name="LdnPassphrase"
|
||||
Text="{Binding LdnPassphrase}"
|
||||
Width="250"
|
||||
MaxLength="16"
|
||||
ToolTip.Tip="{ext:Locale LdnPassphraseInputTooltip}"
|
||||
Watermark="{ext:Locale LdnPassphraseInputPublic}" />
|
||||
<Button
|
||||
Name="GenLdnPassButton"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="10,0,0,0"
|
||||
ToolTip.Tip="{ext:Locale GenLdnPassTooltip}"
|
||||
Click="GenLdnPassButton_OnClick">
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
Text="{ext:Locale GenLdnPass}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="ClearLdnPassButton"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="10,0,0,0"
|
||||
ToolTip.Tip="{ext:Locale ClearLdnPassTooltip}"
|
||||
Click="ClearLdnPassButton_OnClick">
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
Text="{ext:Locale ClearLdnPass}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<TextBlock Margin="10,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Name="InvalidLdnPassphraseBlock"
|
||||
FontStyle="Italic"
|
||||
IsVisible="{Binding IsInvalidLdnPassphraseVisible}"
|
||||
Focusable="False"
|
||||
Text="{ext:Locale InvalidLdnPassphrase}" />
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabNetworkConnection}" />
|
||||
<CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}">
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsNetworkView : UserControl
|
||||
{
|
||||
public SettingsViewModel ViewModel;
|
||||
|
||||
public SettingsNetworkView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void GenLdnPassButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
byte[] code = new byte[4];
|
||||
new Random().NextBytes(code);
|
||||
ViewModel.LdnPassphrase = $"Ryujinx-{BitConverter.ToUInt32(code):x8}";
|
||||
}
|
||||
|
||||
private void ClearLdnPassButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.LdnPassphrase = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +154,36 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
});
|
||||
}
|
||||
|
||||
private void ApplicationLibrary_LdnGameDataReceived(object sender, LdnGameDataReceivedEventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
var ldnGameDataArray = e.LdnData;
|
||||
ViewModel.LastLdnGameData = ldnGameDataArray;
|
||||
foreach (var application in ViewModel.Applications)
|
||||
{
|
||||
UpdateApplicationWithLdnData(application);
|
||||
}
|
||||
ViewModel.RefreshView();
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateApplicationWithLdnData(ApplicationData application)
|
||||
{
|
||||
if (application.ControlHolder.ByteSpan.Length > 0 && ViewModel.LastLdnGameData != null)
|
||||
{
|
||||
IEnumerable<LdnGameData> ldnGameData = ViewModel.LastLdnGameData.Where(game => application.ControlHolder.Value.LocalCommunicationId.Items.Contains(Convert.ToUInt64(game.TitleId, 16)));
|
||||
|
||||
application.PlayerCount = ldnGameData.Sum(game => game.PlayerCount);
|
||||
application.GameCount = ldnGameData.Count();
|
||||
}
|
||||
else
|
||||
{
|
||||
application.PlayerCount = 0;
|
||||
application.GameCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Application_Opened(object sender, ApplicationOpenedEventArgs args)
|
||||
{
|
||||
if (args.Application != null)
|
||||
@@ -450,7 +480,20 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
.Connect()
|
||||
.ObserveOn(SynchronizationContext.Current!)
|
||||
.Bind(ViewModel.Applications)
|
||||
.OnItemAdded(UpdateApplicationWithLdnData)
|
||||
.Subscribe();
|
||||
ApplicationLibrary.LdnGameDataReceived += ApplicationLibrary_LdnGameDataReceived;
|
||||
|
||||
ConfigurationState.Instance.Multiplayer.Mode.Event += (sender, evt) =>
|
||||
{
|
||||
_ = Task.Run(ViewModel.ApplicationLibrary.RefreshLdn);
|
||||
};
|
||||
|
||||
ConfigurationState.Instance.Multiplayer.LdnServer.Event += (sender, evt) =>
|
||||
{
|
||||
_ = Task.Run(ViewModel.ApplicationLibrary.RefreshLdn);
|
||||
};
|
||||
_ = Task.Run(ViewModel.ApplicationLibrary.RefreshLdn);
|
||||
|
||||
ViewModel.RefreshFirmwareStatus();
|
||||
|
||||
@@ -459,7 +502,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
LoadApplications();
|
||||
}
|
||||
|
||||
|
||||
_ = CheckLaunchState();
|
||||
}
|
||||
|
||||
@@ -588,13 +631,26 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
switch (fileType)
|
||||
{
|
||||
case "NSP": ConfigurationState.Instance.UI.ShownFileTypes.NSP.Toggle(); break;
|
||||
case "PFS0": ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Toggle(); break;
|
||||
case "XCI": ConfigurationState.Instance.UI.ShownFileTypes.XCI.Toggle(); break;
|
||||
case "NCA": ConfigurationState.Instance.UI.ShownFileTypes.NCA.Toggle(); break;
|
||||
case "NRO": ConfigurationState.Instance.UI.ShownFileTypes.NRO.Toggle(); break;
|
||||
case "NSO": ConfigurationState.Instance.UI.ShownFileTypes.NSO.Toggle(); break;
|
||||
default: throw new ArgumentOutOfRangeException(fileType);
|
||||
case "NSP":
|
||||
ConfigurationState.Instance.UI.ShownFileTypes.NSP.Toggle();
|
||||
break;
|
||||
case "PFS0":
|
||||
ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Toggle();
|
||||
break;
|
||||
case "XCI":
|
||||
ConfigurationState.Instance.UI.ShownFileTypes.XCI.Toggle();
|
||||
break;
|
||||
case "NCA":
|
||||
ConfigurationState.Instance.UI.ShownFileTypes.NCA.Toggle();
|
||||
break;
|
||||
case "NRO":
|
||||
ConfigurationState.Instance.UI.ShownFileTypes.NRO.Toggle();
|
||||
break;
|
||||
case "NSO":
|
||||
ConfigurationState.Instance.UI.ShownFileTypes.NSO.Toggle();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(fileType);
|
||||
}
|
||||
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
|
||||
@@ -80,6 +80,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
NavPanel.Content = AudioPage;
|
||||
break;
|
||||
case "NetworkPage":
|
||||
NetworkPage.ViewModel = ViewModel;
|
||||
NavPanel.Content = NetworkPage;
|
||||
break;
|
||||
case "LoggingPage":
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
LocaleManager.Instance.LocaleChanged += LocaleChanged;
|
||||
LocaleChanged();
|
||||
|
||||
|
||||
Icon = MainWindowViewModel.IconBitmap;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user