// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Linq; namespace Epic.OnlineServices { public sealed partial class Helper { /// /// Adds a callback to the wrapper. /// /// The generated client data address. /// The client data of the callback. /// The public delegate of the callback. /// The private delegate of the callback. /// Any delegates passed in with input structs. internal static void AddCallback(out IntPtr clientDataAddress, object clientData, Delegate publicDelegate, Delegate privateDelegate, params Delegate[] structDelegates) { lock (s_Callbacks) { clientDataAddress = AddClientData(clientData); s_Callbacks.Add(clientDataAddress, new DelegateHolder(publicDelegate, privateDelegate, structDelegates)); } } /// /// Removes a callback from the wrapper. /// /// The client data address of the callback. private static void RemoveCallback(IntPtr clientDataAddress) { lock (s_Callbacks) { s_Callbacks.Remove(clientDataAddress); RemoveClientData(clientDataAddress); } } /// /// Tries to get the callback associated with the given internal callback info, and then removes it from the wrapper if applicable. /// Single-use callbacks will be cleaned up by this function. /// /// The internal callback info type. /// The callback type. /// The callback info type. /// The internal callback info. /// The callback associated with the internal callback info. /// The callback info. /// Whether the callback was successfully retrieved. internal static bool TryGetAndRemoveCallback(ref TCallbackInfoInternal callbackInfoInternal, out TCallback callback, out TCallbackInfo callbackInfo) where TCallbackInfoInternal : struct, ICallbackInfoInternal, IGettable where TCallback : class where TCallbackInfo : struct, ICallbackInfo { IntPtr clientDataAddress; Get(ref callbackInfoInternal, out callbackInfo, out clientDataAddress); callback = null; lock (s_Callbacks) { DelegateHolder delegateHolder; if (s_Callbacks.TryGetValue(clientDataAddress, out delegateHolder)) { callback = delegateHolder.Public as TCallback; if (callback != null) { // If this delegate was added with an AddNotify, we should only ever remove it on RemoveNotify. if (delegateHolder.NotificationId.HasValue) { } // If the operation is complete, it's safe to remove. else if (callbackInfo.GetResultCode().HasValue && Common.IsOperationComplete(callbackInfo.GetResultCode().Value)) { RemoveCallback(clientDataAddress); } return true; } } } return false; } /// /// Tries to get the struct callback associated with the given internal callback info. /// /// The internal callback info type. /// The callback type. /// The callback info type. /// The internal callback info. /// The callback associated with the internal callback info. /// The callback info. /// Whether the callback was successfully retrieved. internal static bool TryGetStructCallback(ref TCallbackInfoInternal callbackInfoInternal, out TCallback callback, out TCallbackInfo callbackInfo) where TCallbackInfoInternal : struct, ICallbackInfoInternal, IGettable where TCallback : class where TCallbackInfo : struct { IntPtr clientDataAddress; Get(ref callbackInfoInternal, out callbackInfo, out clientDataAddress); callback = null; lock (s_Callbacks) { DelegateHolder delegateHolder; if (s_Callbacks.TryGetValue(clientDataAddress, out delegateHolder)) { callback = delegateHolder.StructDelegates.FirstOrDefault(structDelegate => structDelegate.GetType() == typeof(TCallback)) as TCallback; if (callback != null) { return true; } } } return false; } /// /// Removes a callback from the wrapper by an associated notification id. /// /// The notification id associated with the callback. internal static void RemoveCallbackByNotificationId(ulong notificationId) { lock (s_Callbacks) { var clientDataAddress = s_Callbacks.SingleOrDefault(pair => pair.Value.NotificationId.HasValue && pair.Value.NotificationId == notificationId); RemoveCallback(clientDataAddress.Key); } } /// /// Adds a static callback to the wrapper. /// /// The key of the callback. /// The public delegate of the callback. /// The private delegate of the callback internal static void AddStaticCallback(string key, Delegate publicDelegate, Delegate privateDelegate) { lock (s_StaticCallbacks) { s_StaticCallbacks.Remove(key); s_StaticCallbacks.Add(key, new DelegateHolder(publicDelegate, privateDelegate)); } } /// /// Tries to get the static callback associated with the given key. /// /// The callback type. /// The key of the callback. /// The callback associated with the key. /// Whether the callback was successfully retrieved. internal static bool TryGetStaticCallback(string key, out TCallback callback) where TCallback : class { callback = null; lock (s_StaticCallbacks) { DelegateHolder delegateHolder; if (s_StaticCallbacks.TryGetValue(key, out delegateHolder)) { callback = delegateHolder.Public as TCallback; if (callback != null) { return true; } } } return false; } /// /// Assigns a notification id to a callback by client data address associated with the callback. /// /// The client data address associated with the callback. /// The notification id to assign. internal static void AssignNotificationIdToCallback(IntPtr clientDataAddress, ulong notificationId) { if (notificationId == 0) { RemoveCallback(clientDataAddress); return; } lock (s_Callbacks) { DelegateHolder delegateHolder; if (s_Callbacks.TryGetValue(clientDataAddress, out delegateHolder)) { delegateHolder.NotificationId = notificationId; } } } /// /// Adds client data to the wrapper. /// /// The client data to add. /// The address of the added client data. private static IntPtr AddClientData(object clientData) { lock (s_ClientDatas) { long clientDataId = ++s_LastClientDataId; IntPtr clientDataAddress = new IntPtr(clientDataId); s_ClientDatas.Add(clientDataAddress, clientData); return clientDataAddress; } } /// /// Removes a client data from the wrapper. /// /// The address of the client data to remove. private static void RemoveClientData(IntPtr clientDataAddress) { lock (s_ClientDatas) { s_ClientDatas.Remove(clientDataAddress); } } /// /// Gets client data by its address. /// /// The address of the client data. /// Th client data associated with the address. private static object GetClientData(IntPtr clientDataAddress) { lock (s_ClientDatas) { object clientData; s_ClientDatas.TryGetValue(clientDataAddress, out clientData); return clientData; } } } }