Validate auth ticket event exposed to other parts of Torch and plugins
This commit is contained in:
@@ -1,11 +1,12 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
|
||||||
namespace Torch.API.Event
|
namespace Torch.API.Event
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manager class responsible for registration of event handlers.
|
/// Manager class responsible for registration of event handlers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IEventManager
|
public interface IEventManager : IManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers all event handler methods contained in the given instance
|
/// Registers all event handler methods contained in the given instance
|
||||||
|
@@ -25,8 +25,7 @@ namespace Torch.Server.Managers
|
|||||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
[ReflectedGetter(Name = "m_members")]
|
[ReflectedGetter(Name = "m_members")] private static Func<MyDedicatedServerBase, List<ulong>> _members;
|
||||||
private static Func<MyDedicatedServerBase, List<ulong>> _members;
|
|
||||||
[ReflectedGetter(Name = "m_waitingForGroup")]
|
[ReflectedGetter(Name = "m_waitingForGroup")]
|
||||||
private static Func<MyDedicatedServerBase, HashSet<ulong>> _waitingForGroup;
|
private static Func<MyDedicatedServerBase, HashSet<ulong>> _waitingForGroup;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
@@ -37,7 +36,9 @@ namespace Torch.Server.Managers
|
|||||||
private Dictionary<ulong, ulong> _gameOwnerIds = new Dictionary<ulong, ulong>();
|
private Dictionary<ulong, ulong> _gameOwnerIds = new Dictionary<ulong, ulong>();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MultiplayerManagerDedicated(ITorchBase torch) : base(torch) { }
|
public MultiplayerManagerDedicated(ITorchBase torch) : base(torch)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void KickPlayer(ulong steamId) => Torch.Invoke(() => MyMultiplayer.Static.KickClient(steamId));
|
public void KickPlayer(ulong steamId) => Torch.Invoke(() => MyMultiplayer.Static.KickClient(steamId));
|
||||||
@@ -54,7 +55,8 @@ namespace Torch.Server.Managers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsBanned(ulong steamId) => _isClientBanned.Invoke(MyMultiplayer.Static, steamId) || MySandboxGame.ConfigDedicated.Banned.Contains(steamId);
|
public bool IsBanned(ulong steamId) => _isClientBanned.Invoke(MyMultiplayer.Static, steamId) ||
|
||||||
|
MySandboxGame.ConfigDedicated.Banned.Contains(steamId);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Attach()
|
public override void Attach()
|
||||||
@@ -62,8 +64,10 @@ namespace Torch.Server.Managers
|
|||||||
base.Attach();
|
base.Attach();
|
||||||
_gameServerValidateAuthTicketReplacer = _gameServerValidateAuthTicketFactory.Invoke();
|
_gameServerValidateAuthTicketReplacer = _gameServerValidateAuthTicketFactory.Invoke();
|
||||||
_gameServerUserGroupStatusReplacer = _gameServerUserGroupStatusFactory.Invoke();
|
_gameServerUserGroupStatusReplacer = _gameServerUserGroupStatusFactory.Invoke();
|
||||||
_gameServerValidateAuthTicketReplacer.Replace(new Action<ulong, JoinResult, ulong>(ValidateAuthTicketResponse), MyGameService.GameServer);
|
_gameServerValidateAuthTicketReplacer.Replace(
|
||||||
_gameServerUserGroupStatusReplacer.Replace(new Action<ulong, ulong, bool, bool>(UserGroupStatusResponse), MyGameService.GameServer);
|
new Action<ulong, JoinResult, ulong>(ValidateAuthTicketResponse), MyGameService.GameServer);
|
||||||
|
_gameServerUserGroupStatusReplacer.Replace(new Action<ulong, ulong, bool, bool>(UserGroupStatusResponse),
|
||||||
|
MyGameService.GameServer);
|
||||||
_log.Info("Inserted steam authentication intercept");
|
_log.Info("Inserted steam authentication intercept");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,15 +84,20 @@ namespace Torch.Server.Managers
|
|||||||
|
|
||||||
|
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
[ReflectedEventReplace(typeof(MySteamGameServer), nameof(MySteamGameServer.ValidateAuthTicketResponse), typeof(MyDedicatedServerBase), "GameServer_ValidateAuthTicketResponse")]
|
[ReflectedEventReplace(typeof(MySteamGameServer), nameof(MySteamGameServer.ValidateAuthTicketResponse),
|
||||||
|
typeof(MyDedicatedServerBase), "GameServer_ValidateAuthTicketResponse")]
|
||||||
private static Func<ReflectedEventReplacer> _gameServerValidateAuthTicketFactory;
|
private static Func<ReflectedEventReplacer> _gameServerValidateAuthTicketFactory;
|
||||||
[ReflectedEventReplace(typeof(MySteamGameServer), nameof(MySteamGameServer.UserGroupStatusResponse), typeof(MyDedicatedServerBase), "GameServer_UserGroupStatus")]
|
|
||||||
|
[ReflectedEventReplace(typeof(MySteamGameServer), nameof(MySteamGameServer.UserGroupStatusResponse),
|
||||||
|
typeof(MyDedicatedServerBase), "GameServer_UserGroupStatus")]
|
||||||
private static Func<ReflectedEventReplacer> _gameServerUserGroupStatusFactory;
|
private static Func<ReflectedEventReplacer> _gameServerUserGroupStatusFactory;
|
||||||
|
|
||||||
private ReflectedEventReplacer _gameServerValidateAuthTicketReplacer;
|
private ReflectedEventReplacer _gameServerValidateAuthTicketReplacer;
|
||||||
private ReflectedEventReplacer _gameServerUserGroupStatusReplacer;
|
private ReflectedEventReplacer _gameServerUserGroupStatusReplacer;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
|
|
||||||
#region CustomAuth
|
#region CustomAuth
|
||||||
|
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
[ReflectedStaticMethod(Type = typeof(MyDedicatedServerBase), Name = "ConvertSteamIDFrom64")]
|
[ReflectedStaticMethod(Type = typeof(MyDedicatedServerBase), Name = "ConvertSteamIDFrom64")]
|
||||||
private static Func<ulong, string> _convertSteamIDFrom64;
|
private static Func<ulong, string> _convertSteamIDFrom64;
|
||||||
@@ -96,78 +105,130 @@ namespace Torch.Server.Managers
|
|||||||
[ReflectedStaticMethod(Type = typeof(MyGameService), Name = "GetServerAccountType")]
|
[ReflectedStaticMethod(Type = typeof(MyGameService), Name = "GetServerAccountType")]
|
||||||
private static Func<ulong, MyGameServiceAccountType> _getServerAccountType;
|
private static Func<ulong, MyGameServiceAccountType> _getServerAccountType;
|
||||||
|
|
||||||
[ReflectedMethod(Name = "UserAccepted")]
|
[ReflectedMethod(Name = "UserAccepted")] private static Action<MyDedicatedServerBase, ulong> _userAcceptedImpl;
|
||||||
private static Action<MyDedicatedServerBase, ulong> _userAcceptedImpl;
|
|
||||||
|
|
||||||
[ReflectedMethod(Name = "UserRejected")]
|
[ReflectedMethod(Name = "UserRejected")]
|
||||||
private static Action<MyDedicatedServerBase, ulong, JoinResult> _userRejected;
|
private static Action<MyDedicatedServerBase, ulong, JoinResult> _userRejected;
|
||||||
[ReflectedMethod(Name = "IsClientBanned")]
|
|
||||||
private static Func<MyMultiplayerBase, ulong, bool> _isClientBanned;
|
[ReflectedMethod(Name = "IsClientBanned")] private static Func<MyMultiplayerBase, ulong, bool> _isClientBanned;
|
||||||
[ReflectedMethod(Name = "IsClientKicked")]
|
[ReflectedMethod(Name = "IsClientKicked")] private static Func<MyMultiplayerBase, ulong, bool> _isClientKicked;
|
||||||
private static Func<MyMultiplayerBase, ulong, bool> _isClientKicked;
|
|
||||||
[ReflectedMethod(Name = "RaiseClientKicked")]
|
[ReflectedMethod(Name = "RaiseClientKicked")]
|
||||||
private static Action<MyMultiplayerBase, ulong> _raiseClientKicked;
|
private static Action<MyMultiplayerBase, ulong> _raiseClientKicked;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
private const int _waitListSize = 32;
|
||||||
|
private readonly List<WaitingForGroup> _waitingForGroupLocal = new List<WaitingForGroup>(_waitListSize);
|
||||||
|
|
||||||
|
private struct WaitingForGroup
|
||||||
|
{
|
||||||
|
public readonly ulong SteamId;
|
||||||
|
public readonly JoinResult Response;
|
||||||
|
public readonly ulong SteamOwner;
|
||||||
|
|
||||||
|
public WaitingForGroup(ulong id, JoinResult response, ulong owner)
|
||||||
|
{
|
||||||
|
SteamId = id;
|
||||||
|
Response = response;
|
||||||
|
SteamOwner = owner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Largely copied from SE
|
//Largely copied from SE
|
||||||
private void ValidateAuthTicketResponse(ulong steamID, JoinResult response, ulong steamOwner)
|
private void ValidateAuthTicketResponse(ulong steamID, JoinResult response, ulong steamOwner)
|
||||||
{
|
{
|
||||||
_log.Debug($"ValidateAuthTicketResponse(user={steamID}, response={response}, owner={steamOwner}");
|
_log.Debug($"ValidateAuthTicketResponse(user={steamID}, response={response}, owner={steamOwner}");
|
||||||
if (IsBanned(steamOwner))
|
if (MySandboxGame.ConfigDedicated.GroupID == 0uL)
|
||||||
|
RunEvent(new ValidateAuthTicketEvent(steamID, steamOwner, response, 0, true, false));
|
||||||
|
else if (_getServerAccountType(MySandboxGame.ConfigDedicated.GroupID) != MyGameServiceAccountType.Clan)
|
||||||
|
UserRejected(steamID, JoinResult.GroupIdInvalid);
|
||||||
|
else if (MyGameService.GameServer.RequestGroupStatus(steamID, MySandboxGame.ConfigDedicated.GroupID))
|
||||||
|
lock (_waitingForGroupLocal)
|
||||||
{
|
{
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.BannedByAdmins);
|
if (_waitingForGroupLocal.Count >= _waitListSize)
|
||||||
_raiseClientKicked.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID);
|
_waitingForGroupLocal.RemoveAt(0);
|
||||||
|
_waitingForGroupLocal.Add(new WaitingForGroup(steamID, response, steamOwner));
|
||||||
}
|
}
|
||||||
else if (_isClientKicked.Invoke(MyMultiplayer.Static, steamOwner))
|
else
|
||||||
{
|
UserRejected(steamID, JoinResult.SteamServersOffline);
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.KickedRecently);
|
|
||||||
_raiseClientKicked.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID);
|
|
||||||
}
|
}
|
||||||
if (response != JoinResult.OK)
|
|
||||||
|
private void RunEvent(ValidateAuthTicketEvent info)
|
||||||
{
|
{
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, response);
|
MultiplayerManagerDedicatedEventShim.RaiseValidateAuthTicket(ref info);
|
||||||
|
|
||||||
|
if (info.FutureVerdict == null)
|
||||||
|
{
|
||||||
|
if (IsBanned(info.SteamOwner) || IsBanned(info.SteamID))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.BannedByAdmins);
|
||||||
|
else if (_isClientKicked(MyMultiplayer.Static, info.SteamID) ||
|
||||||
|
_isClientKicked(MyMultiplayer.Static, info.SteamOwner))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.KickedRecently);
|
||||||
|
else if (info.SteamResponse != JoinResult.OK)
|
||||||
|
CommitVerdict(info.SteamID, info.SteamResponse);
|
||||||
|
else if (MyMultiplayer.Static.MemberLimit > 0 &&
|
||||||
|
MyMultiplayer.Static.MemberCount + 1 > MyMultiplayer.Static.MemberLimit)
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.ServerFull);
|
||||||
|
else if (MySandboxGame.ConfigDedicated.GroupID == 0uL ||
|
||||||
|
MySandboxGame.ConfigDedicated.Administrators.Contains(info.SteamID.ToString()) ||
|
||||||
|
MySandboxGame.ConfigDedicated.Administrators.Contains(_convertSteamIDFrom64(info.SteamID)))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.OK);
|
||||||
|
else if (MySandboxGame.ConfigDedicated.GroupID == info.Group && (info.Member || info.Officer))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.OK);
|
||||||
|
else
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.NotInGroup);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (MyMultiplayer.Static.MemberLimit > 0 && _members.Invoke((MyDedicatedServerBase)MyMultiplayer.Static).Count - 1 >= MyMultiplayer.Static.MemberLimit)
|
|
||||||
|
info.FutureVerdict.ContinueWith((task) =>
|
||||||
{
|
{
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.ServerFull);
|
JoinResult verdict;
|
||||||
return;
|
if (task.IsFaulted)
|
||||||
}
|
|
||||||
if (MySandboxGame.ConfigDedicated.GroupID == 0uL ||
|
|
||||||
MySandboxGame.ConfigDedicated.Administrators.Contains(steamID.ToString()) ||
|
|
||||||
MySandboxGame.ConfigDedicated.Administrators.Contains(_convertSteamIDFrom64(steamID)))
|
|
||||||
{
|
{
|
||||||
this.UserAccepted(steamID);
|
_log.Error(task.Exception, $"Future validation verdict faulted");
|
||||||
return;
|
verdict = JoinResult.TicketCanceled;
|
||||||
}
|
}
|
||||||
if (_getServerAccountType(MySandboxGame.ConfigDedicated.GroupID) != MyGameServiceAccountType.Clan)
|
else
|
||||||
|
verdict = task.Result;
|
||||||
|
Torch.Invoke(() => { CommitVerdict(info.SteamID, verdict); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CommitVerdict(ulong steamId, JoinResult verdict)
|
||||||
{
|
{
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.GroupIdInvalid);
|
if (verdict == JoinResult.OK)
|
||||||
return;
|
UserAccepted(steamId);
|
||||||
}
|
else
|
||||||
if (MyGameService.GameServer.RequestGroupStatus(steamID, MySandboxGame.ConfigDedicated.GroupID))
|
UserRejected(steamId, verdict);
|
||||||
{
|
|
||||||
_waitingForGroup.Invoke((MyDedicatedServerBase)MyMultiplayer.Static).Add(steamID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.SteamServersOffline);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UserGroupStatusResponse(ulong userId, ulong groupId, bool member, bool officer)
|
private void UserGroupStatusResponse(ulong userId, ulong groupId, bool member, bool officer)
|
||||||
{
|
{
|
||||||
if (groupId == MySandboxGame.ConfigDedicated.GroupID && _waitingForGroup.Invoke((MyDedicatedServerBase)MyMultiplayer.Static).Remove(userId))
|
lock (_waitingForGroupLocal)
|
||||||
|
for (var j = 0; j < _waitingForGroupLocal.Count; j++)
|
||||||
{
|
{
|
||||||
if (member || officer)
|
var wait = _waitingForGroupLocal[j];
|
||||||
UserAccepted(userId);
|
if (wait.SteamId == userId)
|
||||||
else
|
{
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, userId, JoinResult.NotInGroup);
|
RunEvent(new ValidateAuthTicketEvent(wait.SteamId, wait.SteamOwner, wait.Response, groupId,
|
||||||
|
member, officer));
|
||||||
|
_waitingForGroupLocal.RemoveAt(j);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UserRejected(ulong steamId, JoinResult reason)
|
||||||
|
{
|
||||||
|
_userRejected.Invoke((MyDedicatedServerBase) MyMultiplayer.Static, steamId, reason);
|
||||||
|
}
|
||||||
|
|
||||||
private void UserAccepted(ulong steamId)
|
private void UserAccepted(ulong steamId)
|
||||||
{
|
{
|
||||||
_userAcceptedImpl.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamId);
|
_userAcceptedImpl.Invoke((MyDedicatedServerBase) MyMultiplayer.Static, steamId);
|
||||||
base.RaiseClientJoined(steamId);
|
base.RaiseClientJoined(steamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,95 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NLog;
|
||||||
|
using Sandbox;
|
||||||
|
using Torch.API.Event;
|
||||||
|
using Torch.Event;
|
||||||
|
using VRage.Network;
|
||||||
|
|
||||||
|
namespace Torch.Server.Managers
|
||||||
|
{
|
||||||
|
[EventShim]
|
||||||
|
internal static class MultiplayerManagerDedicatedEventShim
|
||||||
|
{
|
||||||
|
private static readonly EventList<ValidateAuthTicketEvent> _eventValidateAuthTicket =
|
||||||
|
new EventList<ValidateAuthTicketEvent>();
|
||||||
|
|
||||||
|
|
||||||
|
internal static void RaiseValidateAuthTicket(ref ValidateAuthTicketEvent info)
|
||||||
|
{
|
||||||
|
_eventValidateAuthTicket?.RaiseEvent(ref info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// class AdminsCanJoinExample : IEventHandler
|
||||||
|
// {
|
||||||
|
// [EventHandler(SkipCancelled = false)]
|
||||||
|
// public void Handle(ref ValidateAuthTicketEvent info)
|
||||||
|
// {
|
||||||
|
// if (MySandboxGame.ConfigDedicated.Administrators.Contains(info.SteamID.ToString())
|
||||||
|
// || (info.ServerGroup == MySandboxGame.ConfigDedicated.GroupID && info.Officer))
|
||||||
|
// info.Verdict = JoinResult.OK;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that occurs when a player tries to connect to the server.
|
||||||
|
/// Use these values to choose a <see cref="ValidateAuthTicketEvent.Verdict"/>,
|
||||||
|
/// or leave it unset to allow the default logic to handle the request.
|
||||||
|
/// </summary>
|
||||||
|
public struct ValidateAuthTicketEvent : IEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// SteamID of the player
|
||||||
|
/// </summary>
|
||||||
|
public readonly ulong SteamID;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SteamID of the game owner
|
||||||
|
/// </summary>
|
||||||
|
public readonly ulong SteamOwner;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The response from steam
|
||||||
|
/// </summary>
|
||||||
|
public readonly JoinResult SteamResponse;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ID of the queried group, or <c>0</c> if no group.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ulong Group;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this person a member of <see cref="Group"/>. If no group this is true.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool Member;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this person an officer of <see cref="Group"/>. If no group this is false.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool Officer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A future verdict on this authorization request. If null, let the default logic choose.
|
||||||
|
/// </summary>
|
||||||
|
public Task<JoinResult> FutureVerdict;
|
||||||
|
|
||||||
|
internal ValidateAuthTicketEvent(ulong steamId, ulong steamOwner, JoinResult steamResponse,
|
||||||
|
ulong serverGroup, bool member, bool officer)
|
||||||
|
{
|
||||||
|
SteamID = steamId;
|
||||||
|
SteamOwner = steamOwner;
|
||||||
|
SteamResponse = steamResponse;
|
||||||
|
Group = serverGroup;
|
||||||
|
Member = member;
|
||||||
|
Officer = officer;
|
||||||
|
FutureVerdict = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Cancelled => FutureVerdict != null;
|
||||||
|
}
|
||||||
|
}
|
@@ -198,6 +198,7 @@
|
|||||||
<Compile Include="Managers\EntityControlManager.cs" />
|
<Compile Include="Managers\EntityControlManager.cs" />
|
||||||
<Compile Include="Managers\MultiplayerManagerDedicated.cs" />
|
<Compile Include="Managers\MultiplayerManagerDedicated.cs" />
|
||||||
<Compile Include="Managers\InstanceManager.cs" />
|
<Compile Include="Managers\InstanceManager.cs" />
|
||||||
|
<Compile Include="Managers\MultiplayerManagerDedicatedEventShim.cs" />
|
||||||
<Compile Include="NativeMethods.cs" />
|
<Compile Include="NativeMethods.cs" />
|
||||||
<Compile Include="Initializer.cs" />
|
<Compile Include="Initializer.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
@@ -38,7 +38,9 @@ namespace Torch.Collections
|
|||||||
|
|
||||||
~MtObservableCollection()
|
~MtObservableCollection()
|
||||||
{
|
{
|
||||||
_flushEventQueue.Dispose();
|
Timer queue = _flushEventQueue;
|
||||||
|
_flushEventQueue = null;
|
||||||
|
queue?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -208,10 +210,10 @@ namespace Torch.Collections
|
|||||||
return;
|
return;
|
||||||
_collectionEventQueue.Enqueue(e);
|
_collectionEventQueue.Enqueue(e);
|
||||||
// In half a second, flush the events
|
// In half a second, flush the events
|
||||||
_flushEventQueue.Change(500, -1);
|
_flushEventQueue?.Change(500, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Timer _flushEventQueue;
|
private Timer _flushEventQueue;
|
||||||
|
|
||||||
private readonly Queue<NotifyCollectionChangedEventArgs> _collectionEventQueue =
|
private readonly Queue<NotifyCollectionChangedEventArgs> _collectionEventQueue =
|
||||||
new Queue<NotifyCollectionChangedEventArgs>();
|
new Queue<NotifyCollectionChangedEventArgs>();
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -36,7 +37,7 @@ namespace Torch.Event
|
|||||||
_log.Warn($"Registering type {type.FullName} as an event dispatch type, even though it isn't declared singleton");
|
_log.Warn($"Registering type {type.FullName} as an event dispatch type, even though it isn't declared singleton");
|
||||||
var listsFound = 0;
|
var listsFound = 0;
|
||||||
RuntimeHelpers.RunClassConstructor(type.TypeHandle);
|
RuntimeHelpers.RunClassConstructor(type.TypeHandle);
|
||||||
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
|
||||||
if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(EventList<>))
|
if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(EventList<>))
|
||||||
{
|
{
|
||||||
Type eventType = field.FieldType.GenericTypeArguments[0];
|
Type eventType = field.FieldType.GenericTypeArguments[0];
|
||||||
@@ -70,7 +71,7 @@ namespace Torch.Event
|
|||||||
ParameterInfo[] ps = x.GetParameters();
|
ParameterInfo[] ps = x.GetParameters();
|
||||||
if (ps.Length != 1)
|
if (ps.Length != 1)
|
||||||
return false;
|
return false;
|
||||||
return ps[0].ParameterType.IsByRef && typeof(IEvent).IsAssignableFrom(ps[0].ParameterType);
|
return ps[0].ParameterType.IsByRef && typeof(IEvent).IsAssignableFrom(ps[0].ParameterType.GetElementType());
|
||||||
});
|
});
|
||||||
return exploreType.BaseType != null ? enumerable.Concat(EventHandlers(exploreType.BaseType)) : enumerable;
|
return exploreType.BaseType != null ? enumerable.Concat(EventHandlers(exploreType.BaseType)) : enumerable;
|
||||||
}
|
}
|
||||||
@@ -78,9 +79,12 @@ namespace Torch.Event
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
private static void RegisterHandlerInternal(IEventHandler instance)
|
private static void RegisterHandlerInternal(IEventHandler instance)
|
||||||
{
|
{
|
||||||
|
var foundHandler = false;
|
||||||
foreach (MethodInfo handler in EventHandlers(instance.GetType()))
|
foreach (MethodInfo handler in EventHandlers(instance.GetType()))
|
||||||
{
|
{
|
||||||
Type eventType = handler.GetParameters()[0].ParameterType;
|
Type eventType = handler.GetParameters()[0].ParameterType.GetElementType();
|
||||||
|
Debug.Assert(eventType != null);
|
||||||
|
foundHandler = true;
|
||||||
if (eventType.IsInterface)
|
if (eventType.IsInterface)
|
||||||
{
|
{
|
||||||
var foundList = false;
|
var foundList = false;
|
||||||
@@ -100,6 +104,9 @@ namespace Torch.Event
|
|||||||
}
|
}
|
||||||
_log.Error($"Unable to find event handler list for event type {eventType.FullName}");
|
_log.Error($"Unable to find event handler list for event type {eventType.FullName}");
|
||||||
}
|
}
|
||||||
|
if (!foundHandler)
|
||||||
|
_log.Warn($"Found no handlers in {instance.GetType().FullName} or base types");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -14,7 +14,7 @@ namespace Torch.Event
|
|||||||
/// Event shims should be singleton, and have one (or more) fields that are of type <see cref="EventList{T}"/>.
|
/// Event shims should be singleton, and have one (or more) fields that are of type <see cref="EventList{T}"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
internal class EventShimAttribute : Attribute
|
public class EventShimAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user