async public Task<JoinRoomResponse> JoinRoom(JoinRoomS2S request)
        {
            if (_roomMembers.Count >= MaxRoomSize)
                return new JoinRoomResponse { RetCode = JoinRoomRetCode.RoomIsFull };
            else
            {
                if (_roomMembers.Any(x => x.UserName == request.UserName))
                    return new JoinRoomResponse { RetCode = JoinRoomRetCode.NameIsTaken };

                try
                {

                    var joinNotification = new RoomEvent { Text = "User joined", UserName = request.UserName };
                    _callbackChannel.OnRoomEvent(joinNotification);

                    _roomMembers.Add(new RoomMember { UserName = request.UserName });

                    return new JoinRoomResponse { RetCode = JoinRoomRetCode.Ok };
                }
                catch (Exception)
                {
                    return new JoinRoomResponse() { RetCode = JoinRoomRetCode.Retry };
                }
            }
        }
        async public Task<JoinRoomResponse> Process(JoinRoomS2S request, IOperationContext context)
        {
            if (_roomMembers.Count >= MaxRoomSize)
                return new JoinRoomResponse { RetCode = JoinRoomRetCode.RoomIsFull };
            else
            {
                if (_roomMembers.Any(x => x.UserName == request.UserName))
                    return new JoinRoomResponse { RetCode = JoinRoomRetCode.NameIsTaken };

                _state = await _stateManager.GetOrAddAsync<IReliableDictionary<string, DateTime>>(ActorRef.Key.Id);

                try
                {
                    var callbackChannel = await _node.ConnectToCallbackChannel(request.CallbackChannelRef);

                    callbackChannel.DisconnectedEvent
                        .Subscribe(_ =>
                            Fiber.Process(() =>
                            {
                                _roomMembers.RemoveAll(x => x.UserName == request.UserName);

                                var leaveNotification = new RoomEvent { Text = "User left", UserName = request.UserName };
                                foreach (var roomMember in _roomMembers)
                                {
                                    roomMember.CallbackChannel.Send(leaveNotification);
                                }

                                if (_roomMembers.Count == 0)
                                    Dispose();

                            }));

                    var joinNotification = new RoomEvent { Text = "User joined", UserName = request.UserName };
                    foreach (var roomMember in _roomMembers)
                    {
                        roomMember.CallbackChannel.Send(joinNotification);
                    }

                    _roomMembers.Add(new RoomMember { UserName = request.UserName, CallbackChannel = callbackChannel });

                    return new JoinRoomResponse { RetCode = JoinRoomRetCode.Ok };
                }
                catch (Exception)
                {
                    return new JoinRoomResponse() { RetCode = JoinRoomRetCode.Retry };
                }
            }
        }
        async Task<JoinRoomResponse> OnRoomJoin(JoinRoom msg, IOperationContext context)
        {
            var room = await _rooms.GetActor(new ActorKey(msg.RoomName));

            var callbackRef = _callbacksNode.ExposeAsCallbackChannel(this);
            var joinRoomOnServer = new JoinRoomS2S()
            {
                CallbackChannelRef = callbackRef,
                RoomName = msg.RoomName,
                UserName = msg.UserName
            };
            var reply = await room.SendAsync<JoinRoomResponse>(joinRoomOnServer, context);
            if (reply.RetCode == JoinRoomRetCode.Ok)
            {
                _room = room;
                _userName = msg.UserName;
            }

            return reply;
        }
        async Task<JoinRoomResponse> OnRoomJoin(JoinRoom msg, IOperationContext context)
        {
            var room = ActorProxy.Create<ISFRoomActor>(new ActorId(msg.RoomName));

            await room.SubscribeAsync(this);

            var joinRoomOnServer = new JoinRoomS2S()
            {
                RoomName = msg.RoomName,
                UserName = msg.UserName
            };
            var reply = await room.JoinRoom(joinRoomOnServer);
            if (reply.RetCode == JoinRoomRetCode.Ok)
            {
                _room = room;
                _userName = msg.UserName;
            }

            return reply;
        }