using AnotherReplayReader.Utils; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using System.Web; using static System.Text.Json.JsonSerializer; namespace AnotherReplayReader { internal sealed class IpAndPlayer { public static string SimpleIPToString(uint ip) { return $"{ip / 256 / 256 / 256}.{ip / 256 / 256 % 256}.{ip / 256 % 256}.{ip % 256}"; } [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)] public uint Ip { get => _ip; set { _ip = value; IpString = SimpleIPToString(_ip); } } public string IpString { get; private set; } = "0.0.0.0"; public string Id { get => _id; set { _id = value; _pinyin = _id.ToPinyin(); } } public string? PinyinId => _pinyin; private uint _ip; private string _id = string.Empty; private string? _pinyin; } internal class PlayerIdentity { 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 IReadOnlyDictionary? _list; public bool IsUsable => Lock.Run(_lock, () => _list is not null); public PlayerIdentity(Cache cache) { _cache = cache; Task.Run(FetchLocal); Fetch(); } public Task Fetch() { return Task.Run(async () => { try { var key = HttpUtility.UrlEncode(Auth.GetKey()); var request = WebRequest.Create($"https://lanyi.altervista.org/playertable/playertable.php?do=getTable&key={key}"); using var response = await request.GetResponseAsync().ConfigureAwait(false); using var stream = response.GetResponseStream(); var list = await DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); var converted = list?.ToDictionary(x => x.Ip, x => x.Id); lock (_lock) { _list = converted; } if (list is null || 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 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) { if (ip == 0) { return string.Empty; } using var locker = new Lock(_lock); if (_list is null || !_list.TryGetValue(ip, out var name)) { return string.Empty; } return name; } public string FormatRealName(uint ip) { var name = GetRealName(ip); if (string.IsNullOrEmpty(name)) { name = IpAndPlayer.SimpleIPToString(ip); } return $"({name})"; } public string QueryRealNameAndIP(uint ip) { var ipText = IpAndPlayer.SimpleIPToString(ip); var name = GetRealName(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) || 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 { } } } }