diff options
Diffstat (limited to 'Ryujinx.HLE/HOS/Services/Friend/INotificationService.cs')
-rw-r--r-- | Ryujinx.HLE/HOS/Services/Friend/INotificationService.cs | 151 |
1 files changed, 143 insertions, 8 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Friend/INotificationService.cs b/Ryujinx.HLE/HOS/Services/Friend/INotificationService.cs index 8b684e6b..68893efe 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/INotificationService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/INotificationService.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; @@ -5,37 +6,55 @@ using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; +using static Ryujinx.HLE.HOS.ErrorCode; + namespace Ryujinx.HLE.HOS.Services.Friend { - class INotificationService : IpcService + class INotificationService : IpcService, IDisposable { - private UInt128 _userId; + private readonly UInt128 _userId; + private readonly FriendServicePermissionLevel _permissionLevel; + + private readonly object _lock = new object(); private KEvent _notificationEvent; private int _notificationEventHandle = 0; + + private LinkedList<NotificationInfo> _notifications; + + private bool _hasNewFriendRequest; + private bool _hasFriendListUpdate; + private Dictionary<int, ServiceProcessRequest> _commands; public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands; - public INotificationService(UInt128 userId) + public INotificationService(ServiceCtx context, UInt128 userId, FriendServicePermissionLevel permissionLevel) { _commands = new Dictionary<int, ServiceProcessRequest> { { 0, GetEvent }, // 2.0.0+ - //{ 1, Clear }, // 2.0.0+ - //{ 2, Pop }, // 2.0.0+ + { 1, Clear }, // 2.0.0+ + { 2, Pop }, // 2.0.0+ }; - _userId = userId; + _userId = userId; + _permissionLevel = permissionLevel; + _notifications = new LinkedList<NotificationInfo>(); + _notificationEvent = new KEvent(context.Device.System); + + _hasNewFriendRequest = false; + _hasFriendListUpdate = false; + + NotificationEventHandler.Instance.RegisterNotificationService(this); } + // nn::friends::detail::ipc::INotificationService::GetEvent() -> handle<copy> public long GetEvent(ServiceCtx context) { if (_notificationEventHandle == 0) { - _notificationEvent = new KEvent(context.Device.System); - if (context.Process.HandleTable.GenerateHandle(_notificationEvent.ReadableEvent, out _notificationEventHandle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); @@ -46,5 +65,121 @@ namespace Ryujinx.HLE.HOS.Services.Friend return 0; } + + // nn::friends::detail::ipc::INotificationService::Clear() + public long Clear(ServiceCtx context) + { + lock (_lock) + { + _hasNewFriendRequest = false; + _hasFriendListUpdate = false; + + _notifications.Clear(); + } + + return 0; + } + + // nn::friends::detail::ipc::INotificationService::Pop() -> nn::friends::detail::ipc::SizedNotificationInfo + public long Pop(ServiceCtx context) + { + lock (_lock) + { + if (_notifications.Count >= 1) + { + NotificationInfo notificationInfo = _notifications.First.Value; + _notifications.RemoveFirst(); + + if (notificationInfo.Type == NotificationEventType.FriendListUpdate) + { + _hasFriendListUpdate = false; + } + else if (notificationInfo.Type == NotificationEventType.NewFriendRequest) + { + _hasNewFriendRequest = false; + } + + context.ResponseData.WriteStruct(notificationInfo); + + return 0; + } + } + + return MakeError(ErrorModule.Friends, FriendError.NotificationQueueEmpty); + } + + public void SignalFriendListUpdate(UInt128 targetId) + { + lock (_lock) + { + if (_userId == targetId) + { + if (!_hasFriendListUpdate) + { + NotificationInfo friendListNotification = new NotificationInfo(); + + if (_notifications.Count != 0) + { + friendListNotification = _notifications.First.Value; + _notifications.RemoveFirst(); + } + + friendListNotification.Type = NotificationEventType.FriendListUpdate; + _hasFriendListUpdate = true; + + if (_hasNewFriendRequest) + { + NotificationInfo newFriendRequestNotification = new NotificationInfo(); + + if (_notifications.Count != 0) + { + newFriendRequestNotification = _notifications.First.Value; + _notifications.RemoveFirst(); + } + + newFriendRequestNotification.Type = NotificationEventType.NewFriendRequest; + _notifications.AddFirst(newFriendRequestNotification); + } + + // We defer this to make sure we are on top of the queue. + _notifications.AddFirst(friendListNotification); + } + + _notificationEvent.ReadableEvent.Signal(); + } + } + } + + public void SignalNewFriendRequest(UInt128 targetId) + { + lock (_lock) + { + if ((_permissionLevel & FriendServicePermissionLevel.OverlayMask) != 0 && _userId == targetId) + { + if (!_hasNewFriendRequest) + { + if (_notifications.Count == 100) + { + SignalFriendListUpdate(targetId); + } + + NotificationInfo newFriendRequestNotification = new NotificationInfo + { + Type = NotificationEventType.NewFriendRequest + }; + + _notifications.AddLast(newFriendRequestNotification); + _hasNewFriendRequest = true; + } + + _notificationEvent.ReadableEvent.Signal(); + } + } + } + + public void Dispose() + { + NotificationEventHandler.Instance.UnregisterNotificationService(this); + } } }
\ No newline at end of file |