ryubing/src/Ryujinx/UI/Models/LdnGameModel.cs

181 lines
7.6 KiB
C#

using Gommon;
using Humanizer;
using LibHac.Ns;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common.Utilities;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Models
{
public record LdnGameModel
{
public string Id { get; private init; }
public bool IsPublic { get; private init; }
public short PlayerCount { get; private init; }
public short MaxPlayerCount { get; private init; }
public TitleTuple Title { get; private init; }
public ConnectionType ConnectionType { get; private init; }
public bool IsJoinable { get; private init; }
public ushort SceneId { get; private init; }
public string[] Players { get; private init; }
public string PlayersLabel =>
LocaleManager.GetFormatted(LocaleKeys.LdnGameListPlayersAndPlayerCount, PlayerCount, MaxPlayerCount);
public string FormattedPlayers =>
Players.Chunk(4)
.Select(x => x.FormatCollection(s => s, prefix: " ", separator: ", "))
.JoinToString("\n ");
public DateTimeOffset CreatedAt { get; init; }
public string FormattedCreatedAt
=> LocaleManager.GetFormatted(LocaleKeys.LdnGameListCreatedAt, CreatedAt.Humanize());
public string CreatedAtToolTip => CreatedAt.DateTime.ToString(CultureInfo.CurrentUICulture);
public LocaleKeys ConnectionTypeLocaleKey => ConnectionType switch
{
ConnectionType.MasterServerProxy => LocaleKeys.LdnGameListConnectionTypeMasterServerProxy,
ConnectionType.PeerToPeer => LocaleKeys.LdnGameListConnectionTypeP2P,
_ => throw new ArgumentOutOfRangeException(nameof(ConnectionType),
$"Expected either 'P2P' or 'Master Server Proxy' ConnectionType; got '{ConnectionType}'")
};
public LocaleKeys ConnectionTypeToolTipLocaleKey => ConnectionType switch
{
ConnectionType.MasterServerProxy => LocaleKeys.LdnGameListConnectionTypeMasterServerProxyToolTip,
ConnectionType.PeerToPeer => LocaleKeys.LdnGameListConnectionTypeP2PToolTip,
_ => throw new ArgumentOutOfRangeException(nameof(ConnectionType),
$"Expected either 'P2P' or 'Master Server Proxy' ConnectionType; got '{ConnectionType}'")
};
public record struct TitleTuple
{
public required string Name { get; init; }
public required string Id { get; init; }
public required string Version { get; init; }
}
public static Array GetArrayForApp(
LdnGameModel[] receivedData,
ref ApplicationControlProperty acp,
bool onlyJoinable = true,
bool onlyPublic = true)
{
LibHac.Common.FixedArrays.Array8<ulong> communicationId = acp.LocalCommunicationId;
return new Array(receivedData.Where(game =>
communicationId.AsReadOnlySpan().Contains(game.Title.Id.ToULong())
), onlyJoinable, onlyPublic);
}
public class Array : IEnumerable<LdnGameModel>
{
private readonly LdnGameModel[] _ldnDatas;
internal Array(IEnumerable<LdnGameModel> receivedData, bool onlyJoinable = false, bool onlyPublic = false)
{
if (onlyJoinable)
receivedData = receivedData.Where(x => x.IsJoinable);
if (onlyPublic)
receivedData = receivedData.Where(x => x.IsPublic);
_ldnDatas = receivedData.ToArray();
}
public int PlayerCount => _ldnDatas.Sum(it => it.PlayerCount);
public int GameCount => _ldnDatas.Length;
public IEnumerator<LdnGameModel> GetEnumerator() => (_ldnDatas as IEnumerable<LdnGameModel>).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _ldnDatas.GetEnumerator();
}
public static async Task<IEnumerable<LdnGameModel>> GetAllAsync(HttpClient client = null)
=> LdnGameJsonModel.ParseArray(await GetAllAsyncRequestImpl(client))
.Select(FromJson);
private static async Task<string> GetAllAsyncRequestImpl(HttpClient client = null)
{
string ldnWebHost = ConfigurationState.Instance.Multiplayer.GetLdnWebServer();
LocaleManager.Associate(LocaleKeys.LdnGameListRefreshToolTip, ldnWebHost);
try
{
if (client != null)
return await client.GetStringAsync($"https://{ldnWebHost}/api/public_games");
using HttpClient httpClient = new();
return await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
}
catch
{
return "[]";
}
}
private static LdnGameModel FromJson(LdnGameJsonModel json) =>
new()
{
Id = json.Id,
IsPublic = json.IsPublic,
PlayerCount = json.PlayerCount,
MaxPlayerCount = json.MaxPlayerCount,
Title = new TitleTuple { Name = json.TitleName, Id = json.TitleId, Version = json.TitleVersion },
ConnectionType = json.ConnectionType switch
{
"P2P" => ConnectionType.PeerToPeer,
"Master Server Proxy" => ConnectionType.MasterServerProxy,
_ => throw new ArgumentOutOfRangeException(nameof(json),
$"Expected either 'P2P' or 'Master Server Proxy' ConnectionType; got '{json.ConnectionType}'")
},
IsJoinable = json.Joinability is "Joinable",
SceneId = json.SceneId,
Players = json.Players,
CreatedAt = DateTimeOffset.FromUnixTimeMilliseconds(json.CreatedAtUnixTimestamp).ToLocalTime()
};
}
public class LdnGameJsonModel
{
[JsonPropertyName("id")] public string Id { get; set; }
[JsonPropertyName("is_public")] public bool IsPublic { get; set; }
[JsonPropertyName("player_count")] public short PlayerCount { get; set; }
[JsonPropertyName("max_player_count")] public short MaxPlayerCount { get; set; }
[JsonPropertyName("game_name")] public string TitleName { get; set; }
[JsonPropertyName("title_id")] public string TitleId { get; set; }
[JsonPropertyName("title_version")] public string TitleVersion { get; set; }
[JsonPropertyName("mode")] public string ConnectionType { get; set; }
[JsonPropertyName("status")] public string Joinability { get; set; }
[JsonPropertyName("scene_id")] public ushort SceneId { get; set; }
[JsonPropertyName("players")] public string[] Players { get; set; }
[JsonPropertyName("created_at")] public long CreatedAtUnixTimestamp { get; set; }
public static LdnGameJsonModel Parse(string value)
=> JsonHelper.Deserialize(value, LdnGameJsonModelSerializerContext.Default.LdnGameJsonModel);
public static LdnGameJsonModel[] ParseArray(string value)
=> JsonHelper.Deserialize(value, LdnGameJsonModelSerializerContext.Default.LdnGameJsonModelArray);
}
public enum ConnectionType
{
PeerToPeer,
MasterServerProxy
}
[JsonSerializable(typeof(LdnGameJsonModel[]))]
partial class LdnGameJsonModelSerializerContext : JsonSerializerContext;
}