188 lines
6.1 KiB
C#
188 lines
6.1 KiB
C#
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<uint, string>? _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<List<IpAndPlayer>>(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<string?>(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<IpAndPlayer> 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<List<IpAndPlayer>>(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 { }
|
||
}
|
||
}
|
||
}
|