131 lines
4.9 KiB
C#
131 lines
4.9 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Reflection;
|
|
using Lagrange.Core.Common;
|
|
using Lagrange.Core.Internal.Event;
|
|
using Lagrange.Core.Internal.Packets;
|
|
using Lagrange.Core.Internal.Service;
|
|
using Lagrange.Core.Utility.Extension;
|
|
|
|
namespace Lagrange.Core.Internal.Context;
|
|
|
|
/// <summary>
|
|
/// <para>Manage the service and packet translation of the Bot</para>
|
|
/// <para>Instantiate the Service by <see cref="System.Reflection"/> and store such</para>
|
|
/// <para>Translate the event into <see cref="ProtocolEvent"/>, you may manually dispatch the packet to <see cref="PacketContext"/></para>
|
|
/// </summary>
|
|
internal class ServiceContext : ContextBase
|
|
{
|
|
private const string Tag = nameof(ServiceContext);
|
|
|
|
private readonly SequenceProvider _sequenceProvider;
|
|
private readonly Dictionary<string, IService> _services;
|
|
private readonly Dictionary<Type, List<(ServiceAttribute Attribute, IService Instance)>> _servicesEventType;
|
|
|
|
public ServiceContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device)
|
|
: base(collection, keystore, appInfo, device)
|
|
{
|
|
_sequenceProvider = new SequenceProvider();
|
|
_services = new Dictionary<string, IService>();
|
|
_servicesEventType = new Dictionary<Type, List<(ServiceAttribute, IService)>>();
|
|
|
|
RegisterServices();
|
|
}
|
|
|
|
private void RegisterServices()
|
|
{
|
|
var assembly = Assembly.GetExecutingAssembly();
|
|
foreach (var type in assembly.GetDerivedTypes<ProtocolEvent>())
|
|
{
|
|
_servicesEventType[type] = new List<(ServiceAttribute, IService)>();
|
|
}
|
|
|
|
foreach (var type in assembly.GetTypeByAttributes<ServiceAttribute>(out _))
|
|
{
|
|
var serviceAttribute = type.GetCustomAttribute<ServiceAttribute>();
|
|
|
|
if (serviceAttribute != null)
|
|
{
|
|
var service = (IService)type.CreateInstance();
|
|
_services[serviceAttribute.Command] = service;
|
|
|
|
foreach (var attribute in type.GetCustomAttributes<EventSubscribeAttribute>())
|
|
{
|
|
_servicesEventType[attribute.EventType].Add((serviceAttribute, service));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resolve the outgoing packet by the event
|
|
/// </summary>
|
|
public List<SsoPacket> ResolvePacketByEvent(ProtocolEvent protocolEvent)
|
|
{
|
|
var result = new List<SsoPacket>();
|
|
if (!_servicesEventType.TryGetValue(protocolEvent.GetType(), out var serviceList)) return result; // 没找到 滚蛋吧
|
|
|
|
foreach (var (attribute, instance) in serviceList)
|
|
{
|
|
bool success = instance.Build(protocolEvent, Keystore, AppInfo, DeviceInfo, out var binary, out var extraPackets);
|
|
|
|
if (success && binary != null)
|
|
{
|
|
result.Add(new SsoPacket(attribute.PacketType, attribute.Command, (uint)_sequenceProvider.GetNewSequence(), binary.ToArray()));
|
|
|
|
if (extraPackets is { } extra)
|
|
{
|
|
var packets = extra.Select(e => new SsoPacket(attribute.PacketType, attribute.Command, (uint)_sequenceProvider.GetNewSequence(), e.ToArray()));
|
|
result.AddRange(packets);
|
|
}
|
|
|
|
Collection.Log.LogDebug(Tag, $"Outgoing SSOFrame: {attribute.Command}");
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resolve the incoming event by the packet
|
|
/// </summary>
|
|
public List<ProtocolEvent> ResolveEventByPacket(SsoPacket packet)
|
|
{
|
|
var result = new List<ProtocolEvent>();
|
|
|
|
if (!_services.TryGetValue(packet.Command, out var service))
|
|
{
|
|
Collection.Log.LogWarning(Tag, $"Unsupported SSOFrame Received: {packet.Command}");
|
|
Collection.Log.LogDebug(Tag, $"Unsuccessful SSOFrame Payload: {packet.Payload.Hex()}");
|
|
return result; // 没找到 滚蛋吧
|
|
}
|
|
|
|
bool success = service.Parse(packet.Payload, Keystore, AppInfo, DeviceInfo, out var @event, out var extraEvents);
|
|
|
|
if (success)
|
|
{
|
|
if (@event != null) result.Add(@event);
|
|
if (extraEvents != null) result.AddRange(extraEvents);
|
|
|
|
Collection.Log.LogDebug(Tag, $"Incoming SSOFrame: {packet.Command}");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public int GetNewSequence() => _sequenceProvider.GetNewSequence();
|
|
|
|
private class SequenceProvider
|
|
{
|
|
private readonly ConcurrentDictionary<string, int> _sessionSequence = new();
|
|
|
|
private int _sequence = Random.Shared.Next(5000000, 9900000);
|
|
|
|
public int GetNewSequence()
|
|
{
|
|
Interlocked.CompareExchange(ref _sequence, 5000000, 9900000);
|
|
return Interlocked.Increment(ref _sequence);
|
|
}
|
|
|
|
public int RegisterSession(string sessionId) => _sessionSequence.GetOrAdd(sessionId, GetNewSequence());
|
|
}
|
|
} |