Esempio n. 1
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
            }));
        }
Esempio n. 2
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
            }));
        }
Esempio n. 3
0
        public override async Task <AssignDeploymentsResponse> AssignDeployments(AssignDeploymentsRequest request,
                                                                                 ServerCallContext context)
        {
            try
            {
                using (var memClient = _matchmakingMemoryStoreClientManager.GetClient())
                {
                    var toUpdate = new List <Entry>();
                    foreach (var assignment in request.Assignments)
                    {
                        Reporter.AssignDeploymentInc(assignment.DeploymentId, assignment.Result);
                        foreach (var memberId in assignment.Party.MemberIds)
                        {
                            var playerJoinRequest = await memClient.GetAsync <PlayerJoinRequest>(memberId);

                            if (playerJoinRequest == null)
                            {
                                continue;
                            }
                            switch (assignment.Result)
                            {
                            case Assignment.Types.Result.Error:
                                playerJoinRequest.State = MatchState.Error;
                                break;

                            case Assignment.Types.Result.Matched:
                                playerJoinRequest.AssignMatch(assignment.DeploymentId, assignment.DeploymentName);
                                break;

                            case Assignment.Types.Result.Requeued:
                                playerJoinRequest.State = MatchState.Requested;
                                break;
                            }

                            toUpdate.Add(playerJoinRequest);
                        }
                    }

                    var toRequeue = new List <PartyJoinRequest>();
                    var toDelete  = new List <PartyJoinRequest>();

                    foreach (var assignment in request.Assignments)
                    {
                        var party            = assignment.Party;
                        var partyJoinRequest = await memClient.GetAsync <PartyJoinRequest>(party.Id);

                        if (partyJoinRequest == null)
                        {
                            // Party join request has been cancelled.
                            continue;
                        }
                        if (assignment.Result == Assignment.Types.Result.Requeued)
                        {
                            partyJoinRequest.RefreshQueueData();
                            toRequeue.Add(partyJoinRequest);
                            toUpdate.Add(partyJoinRequest);
                        }
                        else
                        {
                            // If the matchmaking process for this party has reached a final state, we should delete the
                            // PartyJoinRequest associated to it.
                            toDelete.Add(partyJoinRequest);
                        }
                    }

                    using (var tx = memClient.CreateTransaction())
                    {
                        tx.UpdateAll(toUpdate);
                        tx.EnqueueAll(toRequeue);
                        tx.DeleteAll(toDelete);
                    }
                }
            }
            catch (EntryNotFoundException e)
            {
                Reporter.AssignDeploymentNotFoundInc(e.Id);
                Log.Warning($"Attempted to assign deployment to nonexistent join request {e.Id}.");
                throw new RpcException(new Status(StatusCode.NotFound, "Join request does not exist"));
            }
            catch (TransactionAbortedException)
            {
                Reporter.TransactionAbortedInc("AssignDeployments");
                Log.Warning("Transaction aborted during deployment assignment.");
                throw new RpcException(new Status(StatusCode.Unavailable,
                                                  "assignment aborted due to concurrent modification; safe to retry"));
            }

            return(new AssignDeploymentsResponse());
        }
        public override async Task <AssignDeploymentsResponse> AssignDeployments(AssignDeploymentsRequest request,
                                                                                 ServerCallContext context)
        {
            try
            {
                using (var memClient = _matchmakingMemoryStoreClientManager.GetClient())
                {
                    var toUpdate = new List <Entry>();
                    foreach (var assignment in request.Assignments)
                    {
                        Reporter.AssignDeploymentInc(assignment.DeploymentId, assignment.Result);
                        foreach (var memberId in assignment.Party.MemberIds)
                        {
                            var playerJoinRequest = await memClient.GetAsync <PlayerJoinRequest>(memberId);

                            if (playerJoinRequest == null)
                            {
                                continue;
                            }
                            switch (assignment.Result)
                            {
                            case Assignment.Types.Result.Error:
                                playerJoinRequest.State = MatchState.Error;
                                break;

                            case Assignment.Types.Result.Matched:
                                playerJoinRequest.AssignMatch(assignment.DeploymentId, assignment.DeploymentName);
                                break;

                            case Assignment.Types.Result.Requeued:
                                playerJoinRequest.State = MatchState.Requested;
                                break;
                            }

                            toUpdate.Add(playerJoinRequest);
                        }
                    }

                    var toRequeue = new List <PartyJoinRequest>();
                    var toDelete  = new List <PartyJoinRequest>();

                    foreach (var assignment in request.Assignments)
                    {
                        var party            = assignment.Party;
                        var partyJoinRequest = await memClient.GetAsync <PartyJoinRequest>(party.Id);

                        if (partyJoinRequest == null)
                        {
                            // Party join request has been cancelled.
                            continue;
                        }

                        var eventAttributes = new Dictionary <string, string>
                        {
                            { "partyId", partyJoinRequest.Id },
                            { "matchRequestId", partyJoinRequest.MatchRequestId },
                            { "queueType", partyJoinRequest.Type },
                            { "partyPhase", partyJoinRequest.Party.CurrentPhase.ToString() }
                        };

                        if (assignment.Result == Assignment.Types.Result.Matched)
                        {
                            toDelete.Add(partyJoinRequest);

                            eventAttributes.Add("spatialProjectId", _project);
                            eventAttributes.Add("deploymentName", assignment.DeploymentName);
                            eventAttributes.Add("deploymentId", assignment.DeploymentId);
                            _analytics.Send("party_matched", eventAttributes, partyJoinRequest.Party.LeaderPlayerId);
                        }
                        else if (assignment.Result == Assignment.Types.Result.Requeued)
                        {
                            partyJoinRequest.RefreshQueueData();
                            toRequeue.Add(partyJoinRequest);
                            toUpdate.Add(partyJoinRequest);
                            _analytics.Send("party_requeued", eventAttributes, partyJoinRequest.Party.LeaderPlayerId);
                        }
                        else if (assignment.Result == Assignment.Types.Result.Error)
                        {
                            toDelete.Add(partyJoinRequest);
                            _analytics.Send("party_error", eventAttributes, partyJoinRequest.Party.LeaderPlayerId);
                        }
                        else
                        {
                            toDelete.Add(partyJoinRequest);
                        }
                    }

                    using (var tx = memClient.CreateTransaction())
                    {
                        tx.UpdateAll(toUpdate);
                        tx.EnqueueAll(toRequeue);
                        tx.DeleteAll(toDelete);
                    }

                    foreach (var playerJoinRequest in toUpdate.OfType <PlayerJoinRequest>())
                    {
                        var eventAttributes = new Dictionary <string, string>
                        {
                            { "partyId", playerJoinRequest.PartyId },
                            { "matchRequestId", playerJoinRequest.MatchRequestId },
                            { "queueType", playerJoinRequest.Type },
                            { "playerJoinRequestState", playerJoinRequest.State.ToString() }
                        };

                        switch (playerJoinRequest.State)
                        {
                        case MatchState.Matched:
                            eventAttributes.Add("spatialProjectId", _project);
                            eventAttributes.Add("deploymentName", playerJoinRequest.DeploymentName);
                            eventAttributes.Add("deploymentId", playerJoinRequest.DeploymentId);
                            _analytics.Send("player_matched", eventAttributes, playerJoinRequest.Id);
                            break;

                        case MatchState.Requested:
                            _analytics.Send("player_requeued", eventAttributes, playerJoinRequest.Id);
                            break;

                        case MatchState.Error:
                            _analytics.Send("player_error", eventAttributes, playerJoinRequest.Id);
                            break;
                        }
                    }
                }
            }
            catch (EntryNotFoundException e)
            {
                Reporter.AssignDeploymentNotFoundInc(e.Id);
                Log.Warning($"Attempted to assign deployment to nonexistent join request {e.Id}.");
                throw new RpcException(new Status(StatusCode.NotFound, "Join request does not exist"));
            }
            catch (TransactionAbortedException)
            {
                Reporter.TransactionAbortedInc("AssignDeployments");
                Log.Warning("Transaction aborted during deployment assignment.");
                throw new RpcException(new Status(StatusCode.Unavailable,
                                                  "assignment aborted due to concurrent modification; safe to retry"));
            }

            return(new AssignDeploymentsResponse());
        }
        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
                });
            }
        }
        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);
        }
Esempio n. 7
0
        public override async Task <Operation> Join(JoinRequestProto request, ServerCallContext context)
        {
            // TODO(dom): refactor this later.
            var playerIdentifier = AuthHeaders.ExtractPlayerId(context);

            using (var memClient = _memoryStoreClientManager.GetClient())
            {
                try
                {
                    var party = await GetPartyForMember(memClient, playerIdentifier) ??
                                throw new EntryNotFoundException(playerIdentifier);

                    if (party.LeaderPlayerId != playerIdentifier)
                    {
                        throw new RpcException(new Status(StatusCode.PermissionDenied,
                                                          "Only the leader can start matchmaking"));
                    }

                    if (!party.SufficientMembers())
                    {
                        throw new RpcException(new Status(StatusCode.FailedPrecondition,
                                                          "There are not enough members in the party to start matchmaking"));
                    }

                    party.CurrentPhase = PartyDataModel.Phase.Matchmaking;

                    var matchmakingMetadata = new Dictionary <string, string>(request.Metadata);
                    var partyJoinRequest    = new PartyJoinRequest(party, request.MatchmakingType, matchmakingMetadata);
                    var entriesToCreate     = new List <Entry> {
                        partyJoinRequest
                    };
                    entriesToCreate.AddRange(CreateJoinRequestsForEachMember(party, request.MatchmakingType,
                                                                             matchmakingMetadata));

                    using (var tx = memClient.CreateTransaction())
                    {
                        tx.UpdateAll(party.Yield());
                        tx.CreateAll(entriesToCreate);
                        tx.EnqueueAll(partyJoinRequest.Yield());
                    }
                }
                catch (EntryNotFoundException)
                {
                    Log.Information("Player is not a member of any party");
                    throw new RpcException(new Status(StatusCode.NotFound, "player is not a member of any party"));
                }
                catch (EntryAlreadyExistsException e)
                {
                    Reporter.JoinRequestQueuedInc();
                    Log.Information($"Party already queued: {e.Id}.");
                    throw new RpcException(new Status(StatusCode.AlreadyExists,
                                                      "party is already queued for matchmaking"));
                }
                catch (TransactionAbortedException)
                {
                    Reporter.TransactionAbortedInc("Join");
                    Log.Warning("Transaction for join was aborted");
                    throw new RpcException(new Status(StatusCode.Unavailable,
                                                      "join aborted due to concurrent modification; safe to retry"));
                }
            }

            Reporter.JoinRequestInc();
            Log.Information($"Created party join request for the party associated to player ${playerIdentifier}.");
            return(new Operation
            {
                Name = playerIdentifier
            });
        }