AnotherReplayReader/PlayerIdentity.cs
2021-10-19 23:13:28 +02:00

188 lines
6.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 { }
}
}
}