public override async Task <GetInviteResponse> GetInvite(GetInviteRequest request, ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);

            if (string.IsNullOrEmpty(request.InviteId))
            {
                throw new RpcException(new Status(StatusCode.InvalidArgument, "Expected non-empty invite id"));
            }

            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                var invite = await memClient.GetAsync <InviteDataModel>(request.InviteId) ??
                             throw new EntryNotFoundException(request.InviteId,
                                                              "No such invite with the given id found");

                if (!invite.PlayerInvolved(playerId))
                {
                    throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                      "The player is not involved in this invite"));
                }

                return(new GetInviteResponse {
                    Invite = ConvertToProto(invite)
                });
            }
        }
예제 #2
0
        public async Task <ActionResult <IEnumerable <MetadataUpdate <string> > > > UpdateAccountMetadata(
            [FromHeader] AuthHeaders headers)
        {
            try
            {
                var body = await Request.Body.ReadAsStringAsync();

                if (!Auth.TryAuthenticate(headers, body, out var error))
                {
                    return(Unauthorized(error));
                }

                var metadata = JsonSerializer.Deserialize <List <MetadataUpdate <string> > >(body);
                if (metadata.Any(x => !Regex.IsMatch(x.Key, "^(tz1|tz2|tz3|KT1)[0-9A-Za-z]{33}$")))
                {
                    return(new BadRequest("body", "Invalid account address"));
                }

                return(Ok(await Metadata.UpdateAccountMetadata(metadata)));
            }
            catch (JsonException)
            {
                return(new BadRequest("body", "Invalid json"));
            }
        }
예제 #3
0
        public override async Task <DeletePartyResponse> DeleteParty(DeletePartyRequest request,
                                                                     ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);

            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                var party = await GetPartyByPlayerId(memClient, playerId) ??
                            throw new RpcException(new Status(StatusCode.NotFound,
                                                              "The player is not a member of any party"));

                if (playerId != party.LeaderPlayerId)
                {
                    throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                      "Cannot delete party: player needs to be the leader of the party"));
                }

                // TODO(iuliaharasim/dom): Move logic specific to party deletion in a separate class.
                var entitiesToDelete = new List <Entry> {
                    party
                };
                entitiesToDelete.AddRange(party.GetMembers());

                try
                {
                    using (var transaction = memClient.CreateTransaction())
                    {
                        transaction.DeleteAll(entitiesToDelete);
                    }
                }
                catch (EntryNotFoundException exception)
                {
                    if (exception.Id.Contains(party.Id))
                    {
                        throw;
                    }

                    // If one of the members has left the party, it is safe to retry this RPC.
                    throw new TransactionAbortedException();
                }

                _analytics.Send("player_cancelled_party", new Dictionary <string, string> {
                    { "partyId", party.Id }
                }, playerId);
                _analytics.Send("party_cancelled", new Dictionary <string, string> {
                    { "partyId", party.Id }
                }, playerId);

                foreach (var m in party.GetMembers())
                {
                    _analytics.Send(
                        "player_left_cancelled_party", new Dictionary <string, string>
                    {
                        { "partyId", party.Id }
                    }, m.Id);
                }
            }

            return(new DeletePartyResponse());
        }
예제 #4
0
        public async Task <ActionResult <IEnumerable <MetadataUpdate <string> > > > UpdateSoftwareMetadata(
            [FromHeader] AuthHeaders headers)
        {
            try
            {
                var body = await Request.Body.ReadAsStringAsync();

                if (!Auth.TryAuthenticate(headers, body, out var error))
                {
                    return(Unauthorized(error));
                }

                var metadata = JsonSerializer.Deserialize <List <MetadataUpdate <string> > >(body);
                if (metadata.Any(x => !Regex.IsMatch(x.Key, "^[0-9a-f]{8}$")))
                {
                    return(BadRequest("Invalid software short hash"));
                }

                return(Ok(await Metadata.UpdateSoftwareMetadata(metadata)));
            }
            catch (JsonException)
            {
                return(new BadRequest("body", "Invalid json"));
            }
        }
예제 #5
0
        public override Task <CreatePartyResponse> CreateParty(CreatePartyRequest request, ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);
            var pit      = AuthHeaders.ExtractPit(context);

            // TODO(iuliaharasim/dom): Move logic specific to party creation in a separate class.
            PartyDataModel party;

            try
            {
                party = new PartyDataModel(playerId, pit, request.MinMembers, request.MaxMembers, request.Metadata);
            }
            catch (ArgumentException exception)
            {
                throw new RpcException(new Status(StatusCode.InvalidArgument, exception.Message));
            }

            var leader = party.GetLeader();

            using (var memClient = _memoryStoreClientManager.GetClient())
                using (var transaction = memClient.CreateTransaction())
                {
                    transaction.CreateAll(new List <Entry> {
                        party, leader
                    });
                }

            return(Task.FromResult(new CreatePartyResponse {
                PartyId = party.Id
            }));
        }
예제 #6
0
        public override async Task <LeavePartyResponse> LeaveParty(LeavePartyRequest request, ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);

            await LeaveParty(playerId);

            return(new LeavePartyResponse());
        }
예제 #7
0
        public bool LoginWithCookie(string cookie)
        {
            AuthHeaders = new AuthHeaders
            {
                Cookie = cookie
            };

            return(true);
        }
예제 #8
0
        public override Task <TResponse> UnaryServerHandler <TRequest, TResponse>(TRequest request,
                                                                                  ServerCallContext context,
                                                                                  UnaryServerMethod <TRequest, TResponse> continuation)
        {
            var validated = context.RequestHeaders.SingleOrDefault(item => item.Key == SecretHeaderKey)?.Value ==
                            _secret;

            AuthHeaders.AddAuthenticatedHeaderToContext(context, validated);
            return(continuation(request, context));
        }
예제 #9
0
        public override async Task <DeleteInviteResponse> DeleteInvite(DeleteInviteRequest request,
                                                                       ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);

            if (string.IsNullOrEmpty(request.InviteId))
            {
                throw new RpcException(new Status(StatusCode.InvalidArgument, "Expected non-empty invite id"));
            }

            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                var invite = await memClient.GetAsync <InviteDataModel>(request.InviteId);

                if (invite == null)
                {
                    return(new DeleteInviteResponse());
                }

                if (!invite.PlayerInvolved(playerId))
                {
                    throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                      "The player is not involved in this invite"));
                }

                var senderInvites = await memClient.GetAsync <PlayerInvites>(invite.SenderId) ??
                                    throw new EntryNotFoundException(playerId, "No invites found for the sender");

                senderInvites.OutboundInviteIds.Remove(invite.Id);

                var receiverInvites = await memClient.GetAsync <PlayerInvites>(invite.ReceiverId) ??
                                      throw new EntryNotFoundException(playerId, "No invites found for the receiver");

                receiverInvites.InboundInviteIds.Remove(invite.Id);

                using (var transaction = memClient.CreateTransaction())
                {
                    transaction.DeleteAll(new List <InviteDataModel> {
                        invite
                    });
                    transaction.UpdateAll(new List <PlayerInvites> {
                        senderInvites, receiverInvites
                    });
                }

                _analytics.Send("player_invite_to_party_revoked", new Dictionary <string, string>
                {
                    { "partyId", invite.PartyId },
                    { "playerIdInviter", playerId },
                    { "inviteId", invite.Id }
                }, invite.ReceiverId);
            }

            return(new DeleteInviteResponse());
        }
예제 #10
0
        public async Task <ActionResult <RawJson> > GetStateMetadata(
            [FromHeader] AuthHeaders headers,
            string section = null)
        {
            if (!Auth.TryAuthenticate(headers, out var error))
            {
                return(Unauthorized(error));
            }

            return(Ok(await Metadata.GetStateMetadata(section)));
        }
예제 #11
0
        public async Task <ActionResult <RawJson> > GetProposalMetadata(
            [FromHeader] AuthHeaders headers,
            [ProtocolHash] string hash,
            string section = null)
        {
            if (!Auth.TryAuthenticate(headers, out var error))
            {
                return(Unauthorized(error));
            }

            return(Ok(await Metadata.GetProposalMetadata(hash, section)));
        }
예제 #12
0
        public async Task <ActionResult <RawJson> > GetConstantMetadata(
            [FromHeader] AuthHeaders headers,
            [ExpressionHash] string address,
            string section = null)
        {
            if (!Auth.TryAuthenticate(headers, out var error))
            {
                return(Unauthorized(error));
            }

            return(Ok(await Metadata.GetConstantMetadata(address, section)));
        }
        private static string ExtractPlayerIdentityTokenFromContext(ServerCallContext context)
        {
            var pit = context.RequestHeaders.SingleOrDefault(item => item.Key == PlayerIdentityTokenHeaderKey)?.Value;

            if (!AuthHeaders.ValidatePit(pit))
            {
                throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                  "The request header doesn't contain a Player Identity Token."));
            }

            return(pit);
        }
        private static string ExtractPlayerIdentifier(PlayerIdentityToken decodedPit)
        {
            var playerIdentifier = decodedPit.PlayerIdentifier;

            if (!AuthHeaders.ValidatePlayerId(playerIdentifier))
            {
                throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                  "The Player Identity Token doesn't contain a valid player_identifier"));
            }

            return(playerIdentifier);
        }
        public override Task <TResponse> UnaryServerHandler <TRequest, TResponse>(
            TRequest request,
            ServerCallContext context,
            UnaryServerMethod <TRequest, TResponse> continuation)
        {
            var pit              = ExtractPlayerIdentityTokenFromContext(context);
            var decodedPit       = DecodePlayerIdentityToken(pit);
            var playerIdentifier = ExtractPlayerIdentifier(decodedPit);

            AuthHeaders.AddPlayerIdentifierToContext(context, playerIdentifier);

            return(continuation(request, context));
        }
예제 #16
0
        public async Task <ActionResult <IEnumerable <MetadataUpdate <string> > > > GetAccountMetadata(
            [FromHeader] AuthHeaders headers,
            JsonParameter metadata,
            [Min(0)] int offset         = 0,
            [Range(0, 10000)] int limit = 100,
            string section = null)
        {
            if (!Auth.TryAuthenticate(headers, out var error))
            {
                return(Unauthorized(error));
            }

            return(Ok(await Metadata.GetAccountMetadata(metadata, offset, limit, section)));
        }
예제 #17
0
        public override async Task <GetPartyByPlayerIdResponse> GetPartyByPlayerId(GetPartyByPlayerIdRequest request,
                                                                                   ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);

            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                var party = await GetPartyByPlayerId(memClient, playerId) ??
                            throw new RpcException(new Status(StatusCode.NotFound,
                                                              "The player is not a member of any party"));

                return(new GetPartyByPlayerIdResponse {
                    Party = ConvertToProto(party)
                });
            }
        }
예제 #18
0
        // Updates the Party's information, excluding its member list.
        // TODO: Move to FieldMasks.
        public override async Task <UpdatePartyResponse> UpdateParty(UpdatePartyRequest request,
                                                                     ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);

            ValidateUpdatePartyRequest(request);
            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                var updatedParty = request.UpdatedParty;
                var party        = await memClient.GetAsync <PartyDataModel>(updatedParty.Id) ??
                                   throw new RpcException(new Status(StatusCode.NotFound,
                                                                     "There is no such party with the given id"));

                if (party.LeaderPlayerId != playerId)
                {
                    throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                      "The update operation can only be done by the leader of the party"));
                }

                if (!party.UpdatePartyLeader(updatedParty.LeaderPlayerId))
                {
                    throw new RpcException(new Status(StatusCode.FailedPrecondition,
                                                      "The proposed new leader is not a member of the party"));
                }

                if (!party.UpdateMinMaxMembers(updatedParty.MinMembers, updatedParty.MaxMembers))
                {
                    throw new RpcException(new Status(StatusCode.FailedPrecondition,
                                                      "Encountered error while updating the minimum and maximum amount of members"));
                }

                // TODO(iuliaharasim/dom): Move logic specific to updating a party into a separate class.
                party.CurrentPhase = ConvertToDataModel(updatedParty.CurrentPhase);
                party.UpdateMetadata(updatedParty.Metadata);

                using (var transaction = memClient.CreateTransaction())
                {
                    transaction.UpdateAll(new List <Entry> {
                        party
                    });
                }

                return(new UpdatePartyResponse {
                    Party = ConvertToProto(party)
                });
            }
        }
예제 #19
0
        public override Task <CreatePartyResponse> CreateParty(CreatePartyRequest request, ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);
            var pit      = AuthHeaders.ExtractPit(context);

            // TODO(iuliaharasim/dom): Move logic specific to party creation in a separate class.
            PartyDataModel party;

            try
            {
                party = new PartyDataModel(playerId, pit, request.MinMembers, request.MaxMembers, request.Metadata);
            }
            catch (ArgumentException exception)
            {
                throw new RpcException(new Status(StatusCode.InvalidArgument, exception.Message));
            }

            var leader = party.GetLeader();

            using (var memClient = _memoryStoreClientManager.GetClient())
                using (var transaction = memClient.CreateTransaction())
                {
                    transaction.CreateAll(new List <Entry> {
                        party, leader
                    });
                }

            var eventAttributes = new Dictionary <string, string>
            {
                { "partyId", party.Id }
            };

            string[] eventTypes = { "player_created_party", "player_joined_party", "party_created" };
            foreach (string eventType in eventTypes)
            {
                if (eventType == "party_created")
                {
                    eventAttributes.Add("partyPhase", party.CurrentPhase.ToString());
                }
                _analytics.Send(eventType, eventAttributes, playerId);
            }

            return(Task.FromResult(new CreatePartyResponse {
                PartyId = party.Id
            }));
        }
예제 #20
0
        // Updates the metadata and current status. Sender, receiver and party id are ignored.
        // TODO: consider moving to FieldMasks.
        public override async Task <UpdateInviteResponse> UpdateInvite(UpdateInviteRequest request,
                                                                       ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);

            var updatedInvite = request.UpdatedInvite ??
                                throw new RpcException(new Status(StatusCode.InvalidArgument,
                                                                  "Expected non-empty updated invite"));

            if (string.IsNullOrEmpty(updatedInvite.Id))
            {
                throw new RpcException(new Status(StatusCode.InvalidArgument,
                                                  "Expected updated invite with non-empty id"));
            }

            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                var invite = await memClient.GetAsync <InviteDataModel>(updatedInvite.Id) ??
                             throw new EntryNotFoundException(updatedInvite.Id,
                                                              "No such invite with the given id found");

                if (!invite.PlayerInvolved(playerId))
                {
                    throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                      "The player is not involved in this invite"));
                }

                invite.CurrentStatus = ConvertToDataModel(updatedInvite.CurrentStatus);
                invite.UpdateMetadata(updatedInvite.Metadata);

                using (var transaction = memClient.CreateTransaction())
                {
                    transaction.UpdateAll(new List <Entry> {
                        invite
                    });
                }

                return(new UpdateInviteResponse {
                    Invite = ConvertToProto(invite)
                });
            }
        }
예제 #21
0
        public async Task <ActionResult <MetadataUpdate> > UpdateStateMetadata(
            [FromHeader] AuthHeaders headers)
        {
            try
            {
                var body = await Request.Body.ReadAsStringAsync();

                if (!Auth.TryAuthenticate(headers, body, out var error))
                {
                    return(Unauthorized(error));
                }

                var metadata = JsonSerializer.Deserialize <MetadataUpdate>(body);
                return(Ok(await Metadata.UpdateStateMetadata(metadata)));
            }
            catch (JsonException)
            {
                return(new BadRequest("body", "Invalid json"));
            }
        }
        public override async Task <ListAllInvitesResponse> ListAllInvites(ListAllInvitesRequest request,
                                                                           ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);

            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                var playerInvites = await memClient.GetAsync <PlayerInvites>(playerId);

                if (playerInvites == null)
                {
                    return(new ListAllInvitesResponse());
                }

                var response = new ListAllInvitesResponse();
                foreach (var id in playerInvites.OutboundInviteIds)
                {
                    var invite = await memClient.GetAsync <InviteDataModel>(id) ??
                                 throw new RpcException(new Status(StatusCode.Unavailable,
                                                                   "Concurrent modification. Safe to retry"));

                    response.OutboundInvites.Add(ConvertToProto(invite));
                }

                foreach (var id in playerInvites.InboundInviteIds)
                {
                    var invite = await memClient.GetAsync <InviteDataModel>(id) ??
                                 throw new RpcException(new Status(StatusCode.Unavailable,
                                                                   "Concurrent modification. Safe to retry"));

                    response.InboundInvites.Add(ConvertToProto(invite));
                }

                return(response);
            }
        }
예제 #23
0
 public bool TryAuthenticate(AuthHeaders headers, out string error)
 {
     error = null;
     return(true);
 }
        public override async Task <Empty> DeleteOperation(DeleteOperationRequest request, ServerCallContext context)
        {
            var playerIdentity = AuthHeaders.ExtractPlayerId(context);

            if (!string.Equals(request.Name, playerIdentity))
            {
                throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                  "Deleting another player's operation is forbidden."));
            }

            Log.Information($"Requested cancellation for the party of player identifier {request.Name}.");
            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                var party = await GetPartyOfMember(memClient, request.Name);

                if (party == null)
                {
                    throw new RpcException(new Status(StatusCode.NotFound,
                                                      "The player making this call is not a member of any party"));
                }

                if (party.LeaderPlayerId != request.Name)
                {
                    throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                      "Only the leader can delete a matchmaking join request"));
                }

                try
                {
                    var partyJoinRequest = await memClient.GetAsync <PartyJoinRequest>(party.Id) ??
                                           throw new EntryNotFoundException(party.Id);

                    var toDelete = new List <Entry> {
                        partyJoinRequest
                    };
                    foreach (var(member, _) in partyJoinRequest.Party.MemberIdToPit)
                    {
                        toDelete.Add(await memClient.GetAsync <PlayerJoinRequest>(member) ??
                                     throw new EntryNotFoundException(member));
                    }

                    party.CurrentPhase = PartyDataModel.Phase.Forming;

                    using (var tx = memClient.CreateTransaction())
                    {
                        tx.UpdateAll(party.Yield());
                        tx.RemoveAllFromQueue(partyJoinRequest.Yield());
                        tx.DeleteAll(toDelete);
                    }

                    Reporter.CancelOperationInc();
                    return(new Empty());
                }
                catch (EntryNotFoundException exception)
                {
                    Log.Warning($"Delete for {request.Name} failed.");
                    if (exception.Id.Contains(party.Id))
                    {
                        Reporter.CancelOperationNotFoundInc();
                        throw new RpcException(new Status(StatusCode.NotFound,
                                                          "requested party is not in matchmaking"));
                    }

                    throw new RpcException(new Status(StatusCode.Internal,
                                                      $"could not find join request for player {exception.Id}"));
                }
                catch (TransactionAbortedException)
                {
                    Reporter.TransactionAbortedInc("DeleteOperation");
                    Log.Warning("Transaction for operation deletion was aborted");
                    throw new RpcException(new Status(StatusCode.Unavailable,
                                                      "deletion aborted due to concurrent modification; safe to retry"));
                }
            }
        }
        public override async Task <Operation> GetOperation(GetOperationRequest request, ServerCallContext context)
        {
            var playerIdentity = AuthHeaders.ExtractPlayerId(context);

            if (!string.Equals(request.Name, playerIdentity))
            {
                throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                  "Fetching another player's operation is forbidden."));
            }

            PlayerJoinRequest joinRequest;

            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                try
                {
                    joinRequest = await memClient.GetAsync <PlayerJoinRequest>(request.Name) ??
                                  throw new EntryNotFoundException(request.Name);

                    if (joinRequest.IsComplete())
                    {
                        using (var tx = memClient.CreateTransaction())
                        {
                            tx.DeleteAll(joinRequest.Yield());
                        }
                    }
                }
                catch (EntryNotFoundException e)
                {
                    Reporter.OperationStateNotFoundInc();
                    Log.Warning($"Join request for {e.Id} does not exist");
                    throw new RpcException(new Status(StatusCode.NotFound, "requested player does not exist"));
                }
                catch (TransactionAbortedException)
                {
                    Reporter.TransactionAbortedInc("GetOperation");
                    Log.Warning("Transaction for operation deletion was aborted");
                    throw new RpcException(new Status(StatusCode.Unavailable,
                                                      "deletion aborted due to concurrent modification; safe to retry"));
                }
            }

            var op = new Operation
            {
                Name = joinRequest.PlayerIdentity,
                Done = joinRequest.IsComplete()
            };

            if (!op.Done)
            {
                Reporter.OperationStateInc(MatchState.Requested);
                return(op);
            }

            switch (joinRequest.State)
            {
            case MatchState.Matched:
                op.Response = CreateJoinResponse(joinRequest);
                break;

            case MatchState.Error:
                op.Error = new Google.Rpc.Status
                {
                    Code    = (int)Google.Rpc.Code.Unknown,
                    Message = "the join request encountered an error"
                };
                break;
            }

            Reporter.OperationStateInc(joinRequest.State);
            Log.Information($"Join request for {op.Name} done in state {joinRequest.State}.");
            return(op);
        }
예제 #26
0
        public override async Task <KickOutPlayerResponse> KickOutPlayer(KickOutPlayerRequest request,
                                                                         ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);

            if (string.IsNullOrEmpty(request.EvictedPlayerId))
            {
                throw new RpcException(new Status(StatusCode.InvalidArgument,
                                                  "LeaveParty requires a non-empty evicted player id"));
            }

            if (playerId == request.EvictedPlayerId)
            {
                await LeaveParty(request.EvictedPlayerId);

                return(new KickOutPlayerResponse());
            }

            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                var initiatorTask = memClient.GetAsync <Member>(playerId);
                var evictedTask   = memClient.GetAsync <Member>(request.EvictedPlayerId);
                Task.WaitAll(initiatorTask, evictedTask);

                var initiator = initiatorTask.Result ?? throw new RpcException(new Status(StatusCode.NotFound,
                                                                                          "The initiator player is not a member of any party"));

                // If the evicted has already left the party, we should return early.
                var evicted = evictedTask.Result;
                if (evicted == null)
                {
                    return(new KickOutPlayerResponse());
                }

                var party = await memClient.GetAsync <PartyDataModel>(initiator.PartyId) ??
                            throw new RpcException(new Status(StatusCode.NotFound,
                                                              "The party no longer exists"));

                if (party.LeaderPlayerId != initiator.Id)
                {
                    throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                      "The initiator is not the leader of the party"));
                }

                if (initiator.PartyId != evicted.PartyId)
                {
                    throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                      "The players are not members of the same party"));
                }

                // TODO(iuliaharasim/dom): Move logic specific to removing a player from a party into a separate class.
                // If false, the player has already been removed from the party so we should terminate early.
                if (!party.RemovePlayerFromParty(evicted.Id))
                {
                    return(new KickOutPlayerResponse());
                }

                using (var transaction = memClient.CreateTransaction())
                {
                    transaction.DeleteAll(new List <Entry> {
                        evicted
                    });
                    transaction.UpdateAll(new List <Entry> {
                        party
                    });
                }

                _analytics.Send("player_kicked_from_party", new Dictionary <string, string>
                {
                    { "partyId", party.Id },
                    { "playerIdKicker", playerId }
                }, evicted.Id);
            }

            return(new KickOutPlayerResponse());
        }
        public override async Task <CreateInviteResponse> CreateInvite(CreateInviteRequest request, ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);

            if (string.IsNullOrEmpty(request.ReceiverPlayerId))
            {
                throw new RpcException(
                          new Status(StatusCode.InvalidArgument, "Expected a non-empty receiver player id"));
            }

            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                var party = await GetPartyByPlayerId(memClient, playerId);

                // This extra check is necessary because the player might have meanwhile left the party (between the
                // Get<Member> and Get<PartyDataModel> calls).
                if (party?.GetMember(playerId) == null)
                {
                    throw new RpcException(new Status(StatusCode.FailedPrecondition,
                                                      "The player creating this invite is not a member of any party"));
                }

                if (party.GetMember(request.ReceiverPlayerId) != null)
                {
                    throw new RpcException(new Status(StatusCode.FailedPrecondition,
                                                      "The receiving player is already a member of the party"));
                }

                var entitiesToCreate = new List <Entry>();
                var entitiesToUpdate = new List <Entry>();

                var invite = new InviteDataModel(playerId, request.ReceiverPlayerId, party.Id, request.Metadata);
                entitiesToCreate.Add(invite);

                var senderPlayerInvites = await memClient.GetAsync <PlayerInvites>(playerId);

                if (senderPlayerInvites == null)
                {
                    senderPlayerInvites = new PlayerInvites(playerId);
                    entitiesToCreate.Add(senderPlayerInvites);
                }
                else
                {
                    entitiesToUpdate.Add(senderPlayerInvites);
                }

                senderPlayerInvites.OutboundInviteIds.Add(invite.Id);

                var receiverPlayerInvites = await memClient.GetAsync <PlayerInvites>(request.ReceiverPlayerId);

                if (receiverPlayerInvites == null)
                {
                    receiverPlayerInvites = new PlayerInvites(request.ReceiverPlayerId);
                    entitiesToCreate.Add(receiverPlayerInvites);
                }
                else
                {
                    entitiesToUpdate.Add(receiverPlayerInvites);
                }

                receiverPlayerInvites.InboundInviteIds.Add(invite.Id);

                using (var transaction = memClient.CreateTransaction())
                {
                    transaction.CreateAll(entitiesToCreate);
                    transaction.UpdateAll(entitiesToUpdate);
                }

                return(new CreateInviteResponse {
                    InviteId = invite.Id
                });
            }
        }
예제 #28
0
        public override async Task <JoinPartyResponse> JoinParty(JoinPartyRequest request, ServerCallContext context)
        {
            var playerId = AuthHeaders.ExtractPlayerId(context);

            var pit = AuthHeaders.ExtractPit(context);

            if (string.IsNullOrEmpty(request.PartyId))
            {
                throw new RpcException(new Status(StatusCode.InvalidArgument,
                                                  "JoinParty requires a non-empty party id"));
            }

            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                var partyToJoin = await memClient.GetAsync <PartyDataModel>(request.PartyId) ??
                                  throw new RpcException(new Status(StatusCode.NotFound, "The party doesn't exist"));

                var member = await memClient.GetAsync <Member>(playerId);

                if (member != null && member.PartyId != request.PartyId)
                {
                    throw new RpcException(new Status(StatusCode.AlreadyExists,
                                                      "The player is a member of another party"));
                }

                var playerInvites = await memClient.GetAsync <PlayerInvites>(playerId);

                if (playerInvites == null)
                {
                    throw new RpcException(new Status(StatusCode.FailedPrecondition,
                                                      "The player is not invited to this party"));
                }

                var invites = (await Task.WhenAll(playerInvites.InboundInviteIds
                                                  .Select(invite => memClient.GetAsync <Invite>(invite))))
                              .Where(invite =>
                {
                    if (invite == null)
                    {
                        Log.Logger.Warning("Failed to fetch an invite for {player}", playerId);
                    }

                    return(invite != null);
                }).ToList();

                var invited = invites
                              .Any(invite => invite.CurrentStatus == Invite.Status.Pending && invite.ReceiverId == playerId);

                if (!invited)
                {
                    throw new RpcException(new Status(StatusCode.FailedPrecondition,
                                                      "The player is not invited to this party"));
                }


                if (partyToJoin.CurrentPhase != PartyPhaseDataModel.Forming)
                {
                    throw new RpcException(new Status(StatusCode.FailedPrecondition,
                                                      "The party is no longer in the Forming phase"));
                }

                // TODO(iuliaharasim/dom): Move logic specific to joining a party into a separate class.
                try
                {
                    var added = partyToJoin.AddPlayerToParty(playerId, pit);
                    // If false, the player already joined the party so we should terminate early.
                    if (!added)
                    {
                        return(new JoinPartyResponse {
                            Party = ConvertToProto(partyToJoin)
                        });
                    }
                }
                catch (Exception exception)
                {
                    throw new RpcException(new Status(StatusCode.FailedPrecondition, exception.Message));
                }

                using (var transaction = memClient.CreateTransaction())
                {
                    transaction.CreateAll(new List <Entry> {
                        partyToJoin.GetMember(playerId)
                    });
                    transaction.UpdateAll(new List <Entry> {
                        partyToJoin
                    });
                }

                _analytics.Send("player_joined_party", new Dictionary <string, object>
                {
                    { "partyId", partyToJoin.Id },
                    {
                        "invites", invites.Select(invite => new Dictionary <string, string>
                        {
                            { "inviteId", invite.Id },
                            { "playerIdInviter", invite.SenderId }
                        })
                    }
                }, playerId);

                return(new JoinPartyResponse {
                    Party = ConvertToProto(partyToJoin)
                });
            }
        }
 protected override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 {
     AuthHeaders.Add(request.Headers.Authorization);
     return(Task.FromResult(new HttpResponseMessage()));
 }
예제 #30
0
 public YoutubeMusicClient(ILogger logger = null)
 {
     // init to default values w/o a cookie
     AuthHeaders = new AuthHeaders();
     Logger      = logger;
 }