using AnotherReplayReader.PluginSystem; using AnotherReplayReader.Utils; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text.Json; using System.Threading.Tasks; using static System.Text.Json.JsonSerializer; namespace AnotherReplayReader { internal class PlayerIdentity { private class NetworkSerializer : IGenericCallable { public TResult Invoke(TArg arg) => throw new NotImplementedException(); public Task InvokeAsync(TArg arg) { var url = arg as string ?? throw new NotImplementedException(); return Network.HttpGetJson(url); } } private const string StoredKey = "pt"; private const string IvKey = "jw"; private static readonly JsonSerializerOptions _jsonOptions = new() { PropertyNameCaseInsensitive = true }; private readonly object _lock = new(); private readonly Cache _cache; private readonly TaskCompletionSource _serviceSource = new(); private IReadOnlyDictionary? _list; public bool IsUsable => Lock.Run(_lock, () => _list is not null); public PlayerIdentity(Cache cache) { _cache = cache; Task.Run(FetchLocal); Fetch(); } public void LoadPlugin(IPlugin plugin) { var service = plugin.CreatePlayerIdentityService(Network.UrlEncode, new NetworkSerializer()); _serviceSource.TrySetResult(service); } public Task Fetch() { return Task.Run(async () => { try { if (await Auth.Id is null) { return; } var service = await _serviceSource.Task.ConfigureAwait(false); var list = await service.Fetch().ConfigureAwait(false); var converted = list?.ToDictionary(x => x.Ip, x => x.Id); lock (_lock) { _list = converted; } if (list is null || await Auth.IdAsKey() is not { } encryptKey) { _cache.Set(StoredKey, null); return; } using var memory = new MemoryStream(); using var aes = Aes.Create(); using (var encryptor = aes.CreateEncryptor(encryptKey, aes.IV)) using (var decryptorStream = new CryptoStream(memory, encryptor, CryptoStreamMode.Write)) { await SerializeAsync(decryptorStream, list, _jsonOptions).ConfigureAwait(false); } memory.Flush(); _cache.SetValues((StoredKey, Convert.ToBase64String(memory.ToArray())), (IvKey, Convert.ToBase64String(aes.IV))); } catch { } }); } public async Task UpdateIpTable(uint ip, string idText) { var service = await _serviceSource.Task.ConfigureAwait(false); return await service.UpdateIpTable(ip, idText).ConfigureAwait(false); } public List AsSortedList() { using var locker = new Lock(_lock); return _list? .Select((kv) => new IpAndPlayer { Ip = kv.Key, Id = kv.Value }) .OrderBy(x => x.Ip) .ToList() ?? new(0); } public string? GetRealName(uint ip) { using var locker = new Lock(_lock); if (_list is null) { return null; } if (ip is 0 || !_list.TryGetValue(ip, out var name)) { return string.Empty; } return name; } public string FormatRealName(uint ip) { var name = GetRealName(ip); if (name is null) { return string.Empty; } if (string.IsNullOrEmpty(name)) { name = IpAndPlayer.SimpleIPToString(ip); } return $"({name})"; } public string? QueryRealNameAndIP(uint ip) { var name = GetRealName(ip); if (name is null) { return null; } var ipText = IpAndPlayer.SimpleIPToString(ip); if (string.IsNullOrEmpty(name)) { return ipText; } return $"{name},{ipText}"; } private async Task FetchLocal() { try { await _cache.Initialization; var stored = _cache.GetOrDefault(StoredKey, string.Empty); var iv = Convert.FromBase64String(_cache.GetOrDefault(IvKey, string.Empty)); if (string.IsNullOrWhiteSpace(stored) || await Auth.IdAsKey() is not { } key) { return; } using var aes = Aes.Create(); using var decryptor = aes.CreateDecryptor(key, iv); using var memory = new MemoryStream(Convert.FromBase64String(stored)); using var decryptorStream = new CryptoStream(memory, decryptor, CryptoStreamMode.Read); var cachedTable = await DeserializeAsync>(decryptorStream, _jsonOptions).ConfigureAwait(false); if (cachedTable is null) { return; } var converted = cachedTable.ToDictionary(x => x.Ip, x => x.Id); converted[0] = "【没有网络连接,正在使用上次保存的数据】"; lock (_lock) { _list ??= converted; } } catch { } } } }