Add the ability to set a custom LDN server

This commit is contained in:
Vudjun 2024-11-04 18:32:14 +00:00
parent 8f16da8655
commit 825de3d238
10 changed files with 84 additions and 11 deletions

View File

@ -174,6 +174,11 @@ namespace Ryujinx.HLE
/// </summary> /// </summary>
public string MultiplayerLdnPassphrase { internal get; set; } public string MultiplayerLdnPassphrase { internal get; set; }
/// <summary>
/// LDN Server
/// </summary>
public string MultiplayerLdnServer { internal get; set; }
/// <summary> /// <summary>
/// An action called when HLE force a refresh of output after docked mode changed. /// An action called when HLE force a refresh of output after docked mode changed.
/// </summary> /// </summary>
@ -206,7 +211,8 @@ namespace Ryujinx.HLE
string multiplayerLanInterfaceId, string multiplayerLanInterfaceId,
MultiplayerMode multiplayerMode, MultiplayerMode multiplayerMode,
bool multiplayerDisableP2p, bool multiplayerDisableP2p,
string multiplayerLdnPassphrase) string multiplayerLdnPassphrase,
string multiplayerLdnServer)
{ {
VirtualFileSystem = virtualFileSystem; VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager; LibHacHorizonManager = libHacHorizonManager;
@ -236,6 +242,7 @@ namespace Ryujinx.HLE
MultiplayerMode = multiplayerMode; MultiplayerMode = multiplayerMode;
MultiplayerDisableP2p = multiplayerDisableP2p; MultiplayerDisableP2p = multiplayerDisableP2p;
MultiplayerLdnPassphrase = multiplayerLdnPassphrase; MultiplayerLdnPassphrase = multiplayerLdnPassphrase;
MultiplayerLdnServer = multiplayerLdnServer;
} }
} }
} }

View File

@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
class IUserLocalCommunicationService : IpcService, IDisposable class IUserLocalCommunicationService : IpcService, IDisposable
{ {
public static string LanPlayHost = "ryuldn.vudjun.com"; public static string DefaultLanPlayHost = "ryuldn.vudjun.com";
public static short LanPlayPort = 30456; public static short LanPlayPort = 30456;
public INetworkClient NetworkClient { get; private set; } public INetworkClient NetworkClient { get; private set; }
@ -1092,15 +1092,21 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
case MultiplayerMode.LdnRyu: case MultiplayerMode.LdnRyu:
try try
{ {
if (!IPAddress.TryParse(LanPlayHost, out IPAddress ipAddress)) string ldnServer = context.Device.Configuration.MultiplayerLdnServer;
if (string.IsNullOrEmpty(ldnServer))
{ {
ipAddress = Dns.GetHostEntry(LanPlayHost).AddressList[0]; ldnServer = DefaultLanPlayHost;
}
if (!IPAddress.TryParse(ldnServer, out IPAddress ipAddress))
{
ipAddress = Dns.GetHostEntry(ldnServer).AddressList[0];
} }
NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), LanPlayPort, context.Device.Configuration); NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), LanPlayPort, context.Device.Configuration);
} }
catch (Exception) catch (Exception ex)
{ {
Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate LdnRyu 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);
NetworkClient = new LdnDisabledClient(); NetworkClient = new LdnDisabledClient();
} }
break; break;

View File

@ -44,6 +44,7 @@ namespace Ryujinx.UI.App.Common
{ {
public class ApplicationLibrary public class ApplicationLibrary
{ {
public static 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 EventHandler<LdnGameDataReceivedEventArgs> LdnGameDataReceived; public event EventHandler<LdnGameDataReceivedEventArgs> LdnGameDataReceived;
@ -788,9 +789,14 @@ namespace Ryujinx.UI.App.Common
{ {
try try
{ {
string ldnWebHost = ConfigurationState.Instance.Multiplayer.LdnServer;
if (string.IsNullOrEmpty(ldnWebHost))
{
ldnWebHost = DefaultLanPlayWebHost;
}
IEnumerable<LdnGameData> ldnGameDataArray = Array.Empty<LdnGameData>(); IEnumerable<LdnGameData> ldnGameDataArray = Array.Empty<LdnGameData>();
using HttpClient httpClient = new HttpClient(); using HttpClient httpClient = new HttpClient();
string ldnGameDataArrayString = await httpClient.GetStringAsync("https://ryuldnweb.vudjun.com/api/public_games"); string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData); ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData);
var evt = new LdnGameDataReceivedEventArgs var evt = new LdnGameDataReceivedEventArgs
{ {
@ -798,9 +804,9 @@ namespace Ryujinx.UI.App.Common
}; };
LdnGameDataReceived?.Invoke(null, evt); LdnGameDataReceived?.Invoke(null, evt);
} }
catch 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."); 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(null, new LdnGameDataReceivedEventArgs() LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs()
{ {
LdnData = Array.Empty<LdnGameData>() LdnData = Array.Empty<LdnGameData>()

View File

@ -402,6 +402,11 @@ namespace Ryujinx.UI.Common.Configuration
/// </summary> /// </summary>
public string MultiplayerLdnPassphrase { get; set; } public string MultiplayerLdnPassphrase { get; set; }
/// <summary>
/// Custom LDN Server
/// </summary>
public string LdnServer { get; set; }
/// <summary> /// <summary>
/// Uses Hypervisor over JIT if available /// Uses Hypervisor over JIT if available
/// </summary> /// </summary>

View File

@ -584,6 +584,11 @@ namespace Ryujinx.UI.Common.Configuration
/// </summary> /// </summary>
public ReactiveObject<string> LdnPassphrase { get; private set; } public ReactiveObject<string> LdnPassphrase { get; private set; }
/// <summary>
/// Custom LDN server
/// </summary>
public ReactiveObject<string> LdnServer { get; private set; }
public MultiplayerSection() public MultiplayerSection()
{ {
LanInterfaceId = new ReactiveObject<string>(); LanInterfaceId = new ReactiveObject<string>();
@ -592,6 +597,7 @@ namespace Ryujinx.UI.Common.Configuration
DisableP2p = new ReactiveObject<bool>(); DisableP2p = new ReactiveObject<bool>();
DisableP2p.Event += static (_, e) => LogValueChange(e, nameof(DisableP2p)); DisableP2p.Event += static (_, e) => LogValueChange(e, nameof(DisableP2p));
LdnPassphrase = new ReactiveObject<string>(); LdnPassphrase = new ReactiveObject<string>();
LdnServer = new ReactiveObject<string>();
} }
} }
@ -801,6 +807,7 @@ namespace Ryujinx.UI.Common.Configuration
MultiplayerMode = Multiplayer.Mode, MultiplayerMode = Multiplayer.Mode,
MultiplayerDisableP2p = Multiplayer.DisableP2p, MultiplayerDisableP2p = Multiplayer.DisableP2p,
MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase, MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase,
LdnServer = Multiplayer.LdnServer,
}; };
return configurationFile; return configurationFile;
@ -862,6 +869,7 @@ namespace Ryujinx.UI.Common.Configuration
Multiplayer.Mode.Value = MultiplayerMode.Disabled; Multiplayer.Mode.Value = MultiplayerMode.Disabled;
Multiplayer.DisableP2p.Value = false; Multiplayer.DisableP2p.Value = false;
Multiplayer.LdnPassphrase.Value = ""; Multiplayer.LdnPassphrase.Value = "";
Multiplayer.LdnServer.Value = "";
UI.GuiColumns.FavColumn.Value = true; UI.GuiColumns.FavColumn.Value = true;
UI.GuiColumns.IconColumn.Value = true; UI.GuiColumns.IconColumn.Value = true;
UI.GuiColumns.AppColumn.Value = true; UI.GuiColumns.AppColumn.Value = true;
@ -1656,6 +1664,7 @@ namespace Ryujinx.UI.Common.Configuration
Multiplayer.Mode.Value = configurationFileFormat.MultiplayerMode; Multiplayer.Mode.Value = configurationFileFormat.MultiplayerMode;
Multiplayer.DisableP2p.Value = configurationFileFormat.MultiplayerDisableP2p; Multiplayer.DisableP2p.Value = configurationFileFormat.MultiplayerDisableP2p;
Multiplayer.LdnPassphrase.Value = configurationFileFormat.MultiplayerLdnPassphrase; Multiplayer.LdnPassphrase.Value = configurationFileFormat.MultiplayerLdnPassphrase;
Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer;
if (configurationFileUpdated) if (configurationFileUpdated)
{ {

View File

@ -208,6 +208,7 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerModeState; ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerModeState;
ConfigurationState.Instance.Multiplayer.LdnPassphrase.Event += UpdateLdnPassphraseState; ConfigurationState.Instance.Multiplayer.LdnPassphrase.Event += UpdateLdnPassphraseState;
ConfigurationState.Instance.Multiplayer.LdnServer.Event += UpdateLdnServerState;
ConfigurationState.Instance.Multiplayer.DisableP2p.Event += UpdateDisableP2pState; ConfigurationState.Instance.Multiplayer.DisableP2p.Event += UpdateDisableP2pState;
_gpuCancellationTokenSource = new CancellationTokenSource(); _gpuCancellationTokenSource = new CancellationTokenSource();
@ -498,6 +499,11 @@ namespace Ryujinx.Ava
Device.Configuration.MultiplayerLdnPassphrase = e.NewValue; 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) private void UpdateDisableP2pState(object sender, ReactiveEventArgs<bool> e)
{ {
Device.Configuration.MultiplayerDisableP2p = e.NewValue; Device.Configuration.MultiplayerDisableP2p = e.NewValue;
@ -878,7 +884,8 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value,
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.LdnServer));
} }
private static IHardwareDeviceDriver InitializeAudio() private static IHardwareDeviceDriver InitializeAudio()

View File

@ -819,5 +819,9 @@
"GenLdnPassTooltip": "Generates a new passphrase, which can be shared with other players.", "GenLdnPassTooltip": "Generates a new passphrase, which can be shared with other players.",
"ClearLdnPass": "Clear", "ClearLdnPass": "Clear",
"ClearLdnPassTooltip": "Clears the current passphrase, returning to the public network.", "ClearLdnPassTooltip": "Clears the current passphrase, returning to the public network.",
"InvalidLdnPassphrase": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\"" "InvalidLdnPassphrase": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\"",
"LdnServer": "Custom LDN Server:",
"LdnServerTooltip": "The LDN server to use for LDN connections. Leave blank to use the default server.",
"LdnServerInputTooltip": "Enter the URL of the LDN server to use.",
"LdnServerInputDefault": "(default)"
} }

View File

@ -58,6 +58,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private int _networkInterfaceIndex; private int _networkInterfaceIndex;
private int _multiplayerModeIndex; private int _multiplayerModeIndex;
private string _ldnPassphrase; private string _ldnPassphrase;
private string _LdnServer;
public int ResolutionScale public int ResolutionScale
{ {
@ -297,6 +298,16 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsInvalidLdnPassphraseVisible { get; set; } public bool IsInvalidLdnPassphraseVisible { get; set; }
public string LdnServer
{
get => _LdnServer;
set
{
_LdnServer = value;
OnPropertyChanged();
}
}
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this() public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
{ {
_virtualFileSystem = virtualFileSystem; _virtualFileSystem = virtualFileSystem;
@ -525,6 +536,7 @@ namespace Ryujinx.Ava.UI.ViewModels
MultiplayerModeIndex = (int)config.Multiplayer.Mode.Value; MultiplayerModeIndex = (int)config.Multiplayer.Mode.Value;
DisableP2P = config.Multiplayer.DisableP2p.Value; DisableP2P = config.Multiplayer.DisableP2p.Value;
LdnPassphrase = config.Multiplayer.LdnPassphrase.Value; LdnPassphrase = config.Multiplayer.LdnPassphrase.Value;
LdnServer = config.Multiplayer.LdnServer.Value;
} }
public void SaveSettings() public void SaveSettings()
@ -643,6 +655,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.Multiplayer.Mode.Value = (MultiplayerMode)MultiplayerModeIndex; config.Multiplayer.Mode.Value = (MultiplayerMode)MultiplayerModeIndex;
config.Multiplayer.DisableP2p.Value = DisableP2P; config.Multiplayer.DisableP2p.Value = DisableP2P;
config.Multiplayer.LdnPassphrase.Value = LdnPassphrase; config.Multiplayer.LdnPassphrase.Value = LdnPassphrase;
config.Multiplayer.LdnServer.Value = LdnServer;
config.ToFileFormat().SaveConfig(Program.ConfigurationPath); config.ToFileFormat().SaveConfig(Program.ConfigurationPath);

View File

@ -1,4 +1,4 @@
<UserControl <UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsNetworkView" x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsNetworkView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@ -87,6 +87,17 @@
IsVisible="{Binding IsInvalidLdnPassphraseVisible}" IsVisible="{Binding IsInvalidLdnPassphraseVisible}"
Focusable="False" Focusable="False"
Text="{ext:Locale InvalidLdnPassphrase}" /> Text="{ext:Locale InvalidLdnPassphrase}" />
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{ext:Locale LdnServer}"
ToolTip.Tip="{ext:Locale LdnServerTooltip}"
Width="200" />
<TextBox Name="LdnServer"
Text="{Binding LdnServer}"
Width="250"
ToolTip.Tip="{ext:Locale LdnServerInputTooltip}"
Watermark="{ext:Locale LdnServerInputDefault}" />
</StackPanel>
<Separator Height="1" /> <Separator Height="1" />
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabNetworkConnection}" /> <TextBlock Classes="h1" Text="{ext:Locale SettingsTabNetworkConnection}" />
<CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}"> <CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}">

View File

@ -501,6 +501,11 @@ namespace Ryujinx.Ava.UI.Windows
{ {
_ = Task.Run(ViewModel.ApplicationLibrary.RefreshLdn); _ = Task.Run(ViewModel.ApplicationLibrary.RefreshLdn);
}; };
ConfigurationState.Instance.Multiplayer.LdnServer.Event += (sender, evt) =>
{
_ = Task.Run(ViewModel.ApplicationLibrary.RefreshLdn);
};
_ = Task.Run(ViewModel.ApplicationLibrary.RefreshLdn); _ = Task.Run(ViewModel.ApplicationLibrary.RefreshLdn);
ViewModel.RefreshFirmwareStatus(); ViewModel.RefreshFirmwareStatus();