/// <summary>
        /// Async Find
        /// </summary>
        /// <param name="search">LobbySearch</param>
        /// <param name="localUserId">Login user id</param>
        /// <returns>Task</returns>
        public static async UniTask <LobbySearchFindCallbackInfo> Find(this LobbySearch search, ProductUserId localUserId)
        {
            var op = new LobbySearchFindOptions
            {
                LocalUserId = localUserId
            };
            LobbySearchFindCallbackInfo info = null;

            search.Find(op, null, e =>
            {
                info = e;
            });

            while (info == null || info.ResultCode == Result.OperationWillRetry)
            {
                if (info != null)
                {
                    Debug.LogError($"error {DebugTools.GetClassMethodName()}:{info.ResultCode}");
                    info = null;
                }
                await UniTask.NextFrame();
            }

            if (info.ResultCode == Result.Success)
            {
                return(info);
            }
            Debug.LogError($"error {DebugTools.GetClassMethodName()}:{info.ResultCode}");
            return(null);
        }
        /// <summary>
        /// Async Login
        /// </summary>
        /// <param name="type">Login type</param>
        /// <param name="id">ID</param>
        /// <param name="token">Token</param>
        /// <returns>Task</returns>
        public static async UniTask <LoginCallbackInfo> Login(this ConnectInterface conn, string token, ExternalCredentialType type)
        {
            var op = new LoginOptions
            {
                Credentials = new Credentials
                {
                    Token = token,
                    Type  = type,
                },
            };
            LoginCallbackInfo info = null;

            conn.Login(op, null, e =>
            {
                info = e;
            });

            while (info == null)
            {
                await UniTask.NextFrame();
            }

            if (info.ResultCode == Result.Success)
            {
                return(info);
            }
            Debug.LogError($"error {DebugTools.GetClassMethodName()}:{info.ResultCode}");
            return(null);
        }
        /// <summary>
        /// Async Login
        /// </summary>
        /// <param name="auth">AuthInterface</param>
        /// <param name="type">Login type</param>
        /// <param name="id">ID</param>
        /// <param name="token">Token</param>
        /// <returns>Task</returns>
        public static async UniTask <LoginCallbackInfo> Login(this AuthInterface auth, LoginCredentialType type, string id, string token)
        {
            var op = new LoginOptions();

            op.Credentials = new Credentials
            {
                Type  = type,
                Id    = id,
                Token = token,
            };

            LoginCallbackInfo info = null;

            auth.Login(op, null, e =>
            {
                info = e;
            });

            while (info == null)
            {
                await UniTask.NextFrame();
            }

            if (info.ResultCode == Result.Success)
            {
                return(info);
            }
            Debug.LogError($"error {DebugTools.GetClassMethodName()}:{info.ResultCode}");
            return(null);
        }
        /// <summary>
        /// Async CreateLobby
        /// </summary>
        /// <param name="lobby">LobbyInterface</param>
        /// <param name="localUserId">Login user id</param>
        /// <param name="maxLobbyMembers">Max member count</param>
        /// <param name="permissionLevel">Public settings, etc.</param>
        /// <returns>Task</returns>
        public static async UniTask <CreateLobbyCallbackInfo> CreateLobby(this LobbyInterface lobby, ProductUserId localUserId, uint maxLobbyMembers, LobbyPermissionLevel permissionLevel)
        {
            var lobbyOp = new CreateLobbyOptions
            {
                LocalUserId     = localUserId,
                MaxLobbyMembers = maxLobbyMembers,
                PermissionLevel = permissionLevel,
            };
            CreateLobbyCallbackInfo info = null;

            lobby.CreateLobby(lobbyOp, null, e =>
            {
                info = e;
            });
            while (info == null)
            {
                await UniTask.NextFrame();
            }

            if (info.ResultCode == Result.Success)
            {
                return(info);
            }
            Debug.LogError($"error {DebugTools.GetClassMethodName()}:{info.ResultCode}");
            return(null);
        }
        /// <summary>
        /// Async JoinLobby
        /// </summary>
        /// <param name="lobby">LobbyInterface</param>
        /// <param name="lobbyDetailsHandle">Lobby detail</param>
        /// <param name="localUserId">Login user id</param>
        /// <returns>Task</returns>
        public static async UniTask <JoinLobbyCallbackInfo> JoinLobby(this LobbyInterface lobby, LobbyDetails lobbyDetailsHandle, ProductUserId localUserId)
        {
            var joinOp = new JoinLobbyOptions
            {
                LobbyDetailsHandle = lobbyDetailsHandle,
                LocalUserId        = localUserId
            };
            JoinLobbyCallbackInfo info = null;

            lobby.JoinLobby(joinOp, null, e =>
            {
                info = e;
            });
            while (info == null)
            {
                await UniTask.NextFrame();
            }

            if (info.ResultCode == Result.Success)
            {
                return(info);
            }
            Debug.LogError($"error {DebugTools.GetClassMethodName()}:{info.ResultCode}");
            return(null);
        }
        /// <summary>
        /// Short CopyUserAuthToken
        /// </summary>
        /// <param name="auth">AuthInterface</param>
        /// <param name="localUserId">Login user id</param>
        /// <returns>Token</returns>
        public static Token CopyUserAuthToken(this AuthInterface auth, EpicAccountId localUserId)
        {
            var result = auth.CopyUserAuthToken(new CopyUserAuthTokenOptions(), localUserId, out Token token);

            if (result == Result.Success)
            {
                return(token);
            }
            Debug.LogError($"error {DebugTools.GetClassMethodName()}:{result}");
            return(null);
        }
        /// <summary>
        /// Short SetLobbyId
        /// </summary>
        /// <param name="search">LobbySearch</param>
        /// <param name="lobbyId">Lobby id</param>
        public static void SetLobbyId(this LobbySearch search, string lobbyId)
        {
            var op = new LobbySearchSetLobbyIdOptions
            {
                LobbyId = lobbyId
            };
            var result = search.SetLobbyId(op);

            if (result != Result.Success)
            {
                Debug.LogError($"error {DebugTools.GetClassMethodName()}:{result}");
            }
        }
        /// <summary>
        /// Short GetSearchResultCount
        /// </summary>
        /// <param name="search">LobbySearch</param>
        /// <param name="localUserId">Login user id</param>
        public static LobbyDetails CopySearchResultByIndex(this LobbySearch search, uint lobbyIndex)
        {
            var resOp = new LobbySearchCopySearchResultByIndexOptions
            {
                LobbyIndex = lobbyIndex
            };
            var result = search.CopySearchResultByIndex(resOp, out LobbyDetails detail);

            if (result == Result.Success)
            {
                return(detail);
            }
            Debug.LogError($"error {DebugTools.GetClassMethodName()}:{result}");
            return(null);
        }
        /// <summary>
        /// Short ReceivePacket
        /// </summary>
        /// <param name="p2p">P2PInterface</param>
        /// <param name="localUserId">Login user id</param>
        /// <param name="maxDataSizeBytes">Maximum received data size</param>
        /// <param name="requestedChannel">Channel id</param>
        public static (ProductUserId, SocketId, byte, byte[], uint) ReceivePacket(this P2PInterface p2p, ProductUserId localUserId, uint maxDataSizeBytes, byte requestedChannel)
        {
            var op = new ReceivePacketOptions
            {
                LocalUserId      = localUserId,
                MaxDataSizeBytes = maxDataSizeBytes,
                RequestedChannel = requestedChannel
            };

            // * An error will occur if the array is null.
            byte[] rawData = new byte[maxDataSizeBytes];
            var    result  = p2p.ReceivePacket(op, out ProductUserId remoteUserId, out SocketId socketId, out byte channel, ref rawData, out uint outBytesWritten);

            if (result != Result.Success)
            {
                Debug.LogError($"error {DebugTools.GetClassMethodName()}:{result}");
                return(default);
        /// <summary>
        /// Short UpdateLobbyModification
        /// </summary>
        /// <param name="lobby">LobbyInterface</param>
        /// <param name="maxResults">Max search count</param>
        /// <returns>Lobby search instance</returns>
        public static LobbySearch CreateLobbySearch(this LobbyInterface lobby, uint maxResults)
        {
            var lobbyOp = new CreateLobbySearchOptions
            {
                MaxResults = maxResults
            };

            LobbySearch search;
            var         result = lobby.CreateLobbySearch(lobbyOp, out search);

            if (result == Result.Success)
            {
                return(search);
            }
            Debug.LogError($"error {DebugTools.GetClassMethodName()}:{result}");
            return(null);
        }
        /// <summary>
        /// Short SetParameter
        /// </summary>
        /// <param name="search">LobbySearch</param>
        /// <param name="key">Key</param>
        /// <param name="value">Value</param>
        /// <param name="comparisonOp">Compare option</param>
        public static void SetParameter(this LobbySearch search, string key, AttributeDataValue value, ComparisonOp comparisonOp)
        {
            var attr = new AttributeData();

            attr.Key   = key;
            attr.Value = value;
            var paramOp = new LobbySearchSetParameterOptions
            {
                Parameter    = attr,
                ComparisonOp = comparisonOp,
            };
            var result = search.SetParameter(paramOp);

            if (result != Result.Success)
            {
                Debug.LogError($"error {DebugTools.GetClassMethodName()}:{result}");
            }
        }
        /// <summary>
        /// Short UpdateLobbyModification
        /// </summary>
        /// <param name="lobby">LobbyInterface</param>
        /// <param name="localUserId">Login user id</param>
        /// <param name="LobbyId">Lobby id</param>
        /// <returns>Handle</returns>
        public static LobbyModification UpdateLobbyModification(this LobbyInterface lobby, ProductUserId localUserId, string lobbyId)
        {
            var modOp = new UpdateLobbyModificationOptions
            {
                LocalUserId = localUserId,
                LobbyId     = lobbyId
            };

            LobbyModification handle;
            var result = lobby.UpdateLobbyModification(modOp, out handle);

            if (result == Result.Success)
            {
                return(handle);
            }
            Debug.LogError($"error {DebugTools.GetClassMethodName()}:{result}");
            return(null);
        }
        /// <summary>
        /// Short AcceptConnection
        /// </summary>
        /// <param name="p2p">P2PInterface</param>
        /// <param name="localUserId">Self user id</param>
        /// <param name="remoteUserId">Send from user id</param>
        /// <param name="socketName">Socket name</param>
        public static void AcceptConnection(this P2PInterface p2p, ProductUserId localUserId, ProductUserId remoteUserId, string socketName)
        {
            var accOp = new AcceptConnectionOptions
            {
                LocalUserId  = localUserId,
                RemoteUserId = remoteUserId,
                SocketId     = new SocketId
                {
                    SocketName = socketName
                },
            };

            var result = p2p.AcceptConnection(accOp);

            if (result != Result.Success)
            {
                Debug.LogError($"error {DebugTools.GetClassMethodName()}:{result}");
            }
        }
        /// <summary>
        /// Short SendPacket
        /// </summary>
        /// <param name="p2p">P2PInterface</param>
        /// <param name="socketName">Socket name</param>
        /// <param name="remoteUserId">Send to user id</param>
        /// <param name="localUserId">Login user id</param>
        /// <param name="channel">Channel id</param>
        /// <param name="data">data</param>
        public static void SendPacket(this P2PInterface p2p, string socketName, ProductUserId remoteUserId, ProductUserId localUserId, byte channel, byte[] data)
        {
            var op = new SendPacketOptions
            {
                SocketId = new SocketId
                {
                    SocketName = socketName
                },
                RemoteUserId = remoteUserId,
                LocalUserId  = localUserId,
                Channel      = channel,
                Data         = data
            };
            var result = p2p.SendPacket(op);

            if (result != Result.Success)
            {
                Debug.LogError($"error {DebugTools.GetClassMethodName()}:{result}");
            }
        }