Compare commits

...

4 Commits

5 changed files with 134 additions and 49 deletions

View File

@@ -110,24 +110,10 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
_broadcastAddress = config.ProxyIp | (~config.ProxySubnetMask);
}
public async Task<ushort> NatPunch()
private async Task<ushort> NatPunchForDevice(NatDevice device)
{
NatDiscoverer discoverer = new();
CancellationTokenSource cts = new(5000);
NatDevice device;
try
{
device = await discoverer.DiscoverDeviceAsync(PortMapper.Upnp, cts);
}
catch (NatDeviceNotFoundException)
{
return 0;
}
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Attempting to map port using {device.ToString()}");
_publicPort = PublicPortBase;
for (int i = 0; i < PublicPortRange; i++)
{
try
@@ -138,12 +124,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
break;
}
catch (MappingException)
catch (MappingException ex)
{
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Failed to map port {_publicPort}: {ex.Message}");
_publicPort++;
}
catch (Exception)
catch (Exception ex)
{
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Failed to map port {_publicPort}: {ex.GetType().Name}: {ex.Message}");
return 0;
}
@@ -155,17 +143,52 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
if (_publicPort != 0)
{
_natDevice = device;
_ = Executor.ExecuteAfterDelayAsync(
PortLeaseRenew.Seconds(),
PortLeaseRenew.Seconds(),
_disposedCancellation.Token,
RefreshLease);
}
_natDevice = device;
return _publicPort;
}
public async Task<ushort> NatPunch()
{
NatDiscoverer discoverer = new();
CancellationTokenSource cts = new(500);
NatDevice[] devices;
try
{
devices = (await discoverer.DiscoverDevicesAsync(PortMapper.Upnp, cts)).ToArray();
}
catch (Exception ex)
{
Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Failed to discover UPnP devices: {ex.Message}");
return 0;
}
if (devices.Length == 0)
{
Logger.Error?.PrintMsg(LogClass.ServiceLdn, "No UPnP devices found.");
return 0;
}
foreach (var device in devices)
{
ushort port = await NatPunchForDevice(device);
if (port != 0)
{
return port;
}
}
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Failed to map port using any device");
_publicPort = 0;
return 0;
}
// Proxy handlers
private void RouteMessage(P2pProxySession sender, ref ProxyInfo info, Action<P2pProxySession> action)

View File

@@ -55,9 +55,21 @@
Tag="{Binding AppData.IdString}"
Text="{Binding AppData.LocalizedStatus}"
Foreground="{Binding AppData.PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
ToolTip.Tip="{Binding AppData.LocalizedStatusTooltip}"
TextAlignment="Start"
TextWrapping="Wrap" />
TextWrapping="Wrap">
<ToolTip.Tip>
<StackPanel Orientation="Vertical">
<TextBlock
Text="{Binding AppData.LocalizedStatusTooltip}" />
<Separator
Margin="0, 10, 0, 10"
IsVisible="{Binding AppData.HasCompatibilityLabels}" />
<TextBlock
IsVisible="{Binding AppData.HasCompatibilityLabels}"
Text="{Binding AppData.FormattedCompatibilityLabels}" />
</StackPanel>
</ToolTip.Tip>
</TextBlock>
<Button.Styles>
<Style Selector="Button">
<Setter Property="MinWidth"

View File

@@ -93,8 +93,19 @@
IsVisible="{Binding HasPlayabilityInfo}"
Background="{DynamicResource AppListBackgroundColor}"
Margin="-1, 0, 0, 0"
Padding="0"
ToolTip.Tip="{Binding LocalizedStatusTooltip}">
Padding="0">
<ToolTip.Tip>
<StackPanel Orientation="Vertical">
<TextBlock
Text="{Binding LocalizedStatusTooltip}" />
<Separator
Margin="0, 10, 0, 10"
IsVisible="{Binding HasCompatibilityLabels}" />
<TextBlock
IsVisible="{Binding HasCompatibilityLabels}"
Text="{Binding FormattedCompatibilityLabels}" />
</StackPanel>
</ToolTip.Tip>
<TextBlock
Margin="1.5"
Tag="{Binding IdString}"

View File

@@ -1,3 +1,4 @@
using Gommon;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
@@ -14,6 +15,7 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using System;
using System.IO;
using System.Text;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.AppLibrary
@@ -32,33 +34,12 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
set
{
_id = value;
PlayabilityStatus = CompatibilityCsv.GetStatus(Id);
Compatibility = CompatibilityCsv.Find(Id);
}
}
public string Developer { get; set; } = "Unknown";
public string Version { get; set; } = "0";
public bool HasPlayabilityInfo => PlayabilityStatus != null;
public string LocalizedStatus =>
PlayabilityStatus.HasValue
? LocaleManager.Instance[PlayabilityStatus!.Value]
: string.Empty;
public LocaleKeys? PlayabilityStatus { get; set; }
public string LocalizedStatusTooltip =>
PlayabilityStatus.HasValue
#pragma warning disable CS8509 // It is exhaustive for any value this property can contain.
? LocaleManager.Instance[PlayabilityStatus!.Value switch
#pragma warning restore CS8509
{
LocaleKeys.CompatibilityListPlayable => LocaleKeys.CompatibilityListPlayableTooltip,
LocaleKeys.CompatibilityListIngame => LocaleKeys.CompatibilityListIngameTooltip,
LocaleKeys.CompatibilityListMenus => LocaleKeys.CompatibilityListMenusTooltip,
LocaleKeys.CompatibilityListBoots => LocaleKeys.CompatibilityListBootsTooltip,
LocaleKeys.CompatibilityListNothing => LocaleKeys.CompatibilityListNothingTooltip,
}]
: string.Empty;
public int PlayerCount { get; set; }
public int GameCount { get; set; }
@@ -83,6 +64,53 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n");
public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize);
public Optional<CompatibilityEntry> Compatibility { get; private set; }
public bool HasPlayabilityInfo => Compatibility.HasValue;
public string LocalizedStatus => Compatibility.Convert(x => x.LocalizedStatus);
public bool HasCompatibilityLabels => !FormattedCompatibilityLabels.Equals(string.Empty);
public string FormattedCompatibilityLabels
=> Compatibility.Convert(x => x.FormattedIssueLabels).OrElse(string.Empty);
public LocaleKeys? PlayabilityStatus => Compatibility.Convert(x => x.Status).OrElse(null);
public string CompatibilityToolTip
{
get
{
StringBuilder sb = new(LocalizedStatusTooltip);
string formattedCompatibilityLabels = FormattedCompatibilityLabels;
if (!string.IsNullOrEmpty(formattedCompatibilityLabels))
{
sb.AppendLine()
.AppendLine()
.Append(formattedCompatibilityLabels);
}
return sb.ToString();
}
}
public string LocalizedStatusTooltip =>
Compatibility.Convert(x =>
#pragma warning disable CS8509 It is exhaustive for all possible values this can contain.
LocaleManager.Instance[x.Status switch
#pragma warning restore CS8509
{
LocaleKeys.CompatibilityListPlayable => LocaleKeys.CompatibilityListPlayableTooltip,
LocaleKeys.CompatibilityListIngame => LocaleKeys.CompatibilityListIngameTooltip,
LocaleKeys.CompatibilityListMenus => LocaleKeys.CompatibilityListMenusTooltip,
LocaleKeys.CompatibilityListBoots => LocaleKeys.CompatibilityListBootsTooltip,
LocaleKeys.CompatibilityListNothing => LocaleKeys.CompatibilityListNothingTooltip,
}]
).OrElse(string.Empty);
[JsonIgnore] public string IdString => Id.ToString("x16");

View File

@@ -60,10 +60,21 @@ namespace Ryujinx.Ava.Utilities.Compat
}
}
public static CompatibilityEntry Find(string titleId)
=> Entries.FirstOrDefault(x => x.TitleId.HasValue && x.TitleId.Value.EqualsIgnoreCase(titleId));
public static CompatibilityEntry Find(ulong titleId)
=> Find(titleId.ToString("X16"));
public static LocaleKeys? GetStatus(string titleId)
=> Entries.FirstOrDefault(x => x.TitleId.HasValue && x.TitleId.Value.EqualsIgnoreCase(titleId))?.Status;
=> Find(titleId)?.Status;
public static LocaleKeys? GetStatus(ulong titleId) => GetStatus(titleId.ToString("X16"));
public static string GetLabels(string titleId)
=> Find(titleId)?.FormattedIssueLabels;
public static string GetLabels(ulong titleId) => GetLabels(titleId.ToString("X16"));
}
public class CompatibilityEntry