AnotherReplayReader/PlayerIdentity.cs

185 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.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, TResult>(TArg arg) => throw new NotImplementedException();
public Task<TResult?> InvokeAsync<TArg, TResult>(TArg arg)
{
var url = arg as string ?? throw new NotImplementedException();
return Network.HttpGetJson<TResult>(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<IPlayerIdentityService> _serviceSource = new();
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 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<IpAndPlayer>().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<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 async Task<bool> UpdateIpTable(uint ip, string idText)
{
var service = await _serviceSource.Task.ConfigureAwait(false);
return await service.UpdateIpTable(ip, idText).ConfigureAwait(false);
}
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)
{
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<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 { }
}
}
}