예제 #1
0
        public async Task <IActionResult> RegisteredZoneCheckin([FromBody] ZoneServerCheckinRequestModel zoneCheckinRequest)
        {
            //This is ok, it means the zone was unregistered before the checkin message was recieved.
            //It doesn't alone indicate a failure.
            if (!await ZoneRepository.ContainsAsync(ClaimsReader.GetAccountIdInt(User)))
            {
                if (Logger.IsEnabled(LogLevel.Warning))
                {
                    Logger.LogWarning($"UserId: {ClaimsReader.GetPlayerAccountId(User)} attempted to checkin ZoneId: {ClaimsReader.GetAccountId(User)} but the zone was not registered.");
                }

                return(Ok());
            }

            ZoneInstanceEntryModel model = await ZoneRepository.RetrieveAsync(ClaimsReader.GetAccountIdInt(User));

            //We don't REMOVE if expired. This should only update checkin time
            //something else should be responsible for removal if expired at some point.
            model.UpdateCheckinTime();
            await ZoneRepository.UpdateAsync(model.ZoneId, model);

            //We don't really have anything to reply to, this shouldn't be called externally.
            //It's basically handling a proxied message queue message.
            return(Ok());
        }
예제 #2
0
		/// <summary>
		/// Indicates if the provided character id is valid for the user in the message context.
		/// </summary>
		/// <param name="characterId">The id to check.</param>
		/// <param name="characterRepository">The character repository service.</param>
		/// <returns>True if the character id is valid/</returns>
		private async Task<bool> IsCharacterIdValidForUser(int characterId, ICharacterRepository characterRepository)
		{
			//We only support positive character ids so if they request a less than 0 it's invalid and likely spoofed
			//or if they request an id they don't own
			//or if it's an not a known character
			return characterId >= 0 &&
				await characterRepository.ContainsAsync(characterId) && 
				(await characterRepository.RetrieveAsync(characterId)).AccountId == ClaimsReader.GetAccountIdInt(User);
		}
예제 #3
0
		public async Task<CharacterSessionDataResponse> GetCharacterSessionData([FromRoute(Name = "id")] int characterId)
		{
			int accountId = ClaimsReader.GetAccountIdInt(User);

			//TODO: Do we want to expose this to non-controlers?
			//First we should validate that the account that is authorized owns the character it is requesting session data from
			return await RetrieveSessionDataIfAvailable(characterId, accountId)
				.ConfigureAwaitFalse();
		}
예제 #4
0
		/// <summary>
		/// Verifies that the provided <see cref="characterId"/>
		/// is owned by the current User claim.
		/// </summary>
		/// <param name="characterId"></param>
		/// <returns></returns>
		public async Task<bool> VerifyCharacterOwnedByAccount(int characterId)
		{
			int accountId = ClaimsReader.GetAccountIdInt(User);

			//TODO: Do we want to expose this to non-controlers?
			//First we should validate that the account that is authorized owns the character it is requesting session data from

			return (await CharacterRepository.CharacterIdsForAccountId(accountId).ConfigureAwaitFalse())
				.Contains(characterId);
		}
예제 #5
0
        private async Task <bool> CheckZoneAuthorizedToModifyCharacterData(int characterId)
        {
            if (!await CharacterSessionRepository.CharacterHasActiveSession(characterId))
            {
                return(false);
            }

            CharacterSessionModel model = await CharacterSessionRepository.RetrieveAsync(characterId);

            //If they aren't in this zone we shouldn't be allowed to save it.
            return(ClaimsReader.GetAccountIdInt(User) == model.ZoneId);
        }
예제 #6
0
        public async Task <IActionResult> CreateZoneServerAccount([FromBody][JetBrains.Annotations.NotNull] ZoneServerAccountRegistrationRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            //We want to log this out for information purposes whenever an auth request begins
            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Zone Register Request by UserAccount: {ClaimsReader.GetAccountName(this.User)}:{ClaimsReader.GetAccountIdInt(this.User)} {HttpContext.Connection.RemoteIpAddress}:{HttpContext.Connection.RemotePort}");
            }

            //TODO: Create cryptographically secure random bytes for password and user.
            string zoneUserName = Guid.NewGuid().ToString();
            string zonePassword = Guid.NewGuid().ToString();

            ZoneServerApplicationUser user = new ZoneServerApplicationUser(ClaimsReader.GetAccountIdInt(this.User))
            {
                UserName = zoneUserName,
                Email    = "*****@*****.**",              //TODO: Real email???
            };

            IdentityResult identityResult = await UserManager.CreateAsync(user, zonePassword);

            if (identityResult.Succeeded)
            {
                //Adds the vrgid account owner claim.
                await UserManager.AddClaimAsync(user, new Claim(GladMMOAuthConstants.ACCOUNT_ID_OWNER_CLAIM_NAME, ClaimsReader.GetAccountId(this.User)));

                //Here we inherit each role the user account has to the zoneserver account
                foreach (var claim in User.Claims)
                {
                    if (claim.Type == "role")                                                               //TODO: Is there a constant for this??
                    {
                        if (claim.Value.ToLower() != GladMMOAuthConstants.PLAYERACCOUNT_AUTHORIZATION_ROLE) //DO NOT add the player role.
                        {
                            await UserManager.AddToRoleAsync(user, claim.Value);
                        }
                    }
                }

                //Also add the ZoneServer role
                await UserManager.AddToRoleAsync(user, GladMMOAuthConstants.ZONESERVER_AUTHORIZATION_ROLE);

                //At this point, the account has the PlayFab id claim so it's ready for use.
                return(Json(new ZoneServerAccountRegistrationResponse(user.Id, zoneUserName, zonePassword)));
            }
            else
            {
                return(BadRequest(identityResult.Errors.Aggregate("", (s, error) => $"{s} {error.Code}:{error.Description}")));
            }
        }
예제 #7
0
        public async Task <IActionResult> SetWorldAsUploaded(
            [FromRoute(Name = "id")] long worldId,
            [FromServices] IWorldEntryRepository worldEntryRepository,
            [FromServices] IContentResourceExistenceVerifier contentResourceExistenceVerifier)
        {
            //At this point, the user is telling us they finished uploading the world.
            //They could be lying so we should check that the resource exists AND
            //we should also check that it's an asset bundle and gather some information from the header.

            //First we verify a world exists with this id
            if (!await worldEntryRepository.ContainsAsync(worldId).ConfigureAwaitFalse())
            {
                //TODO: We should say something more specific
                return(BadRequest());
            }

            WorldEntryModel model = await worldEntryRepository.RetrieveAsync(worldId)
                                    .ConfigureAwaitFalse();

            //Check the model is associated with this account. Only 1 account can own a world resource
            if (model.AccountId != ClaimsReader.GetAccountIdInt(User))
            {
                return(Unauthorized());
            }

            //Now that we know the world is in the database and the account making this authorized requests owns it
            //we can now actually check that the resource exists on the storeage system
            //TODO: This relies on some outdated API/deprecated stuff.
            bool resourceExists = await contentResourceExistenceVerifier.VerifyResourceExists(UserContentType.World, model.StorageGuid)
                                  .ConfigureAwaitFalse(); //TODO: Don't hardcore bucket name

            //TODO: Be more descriptive
            if (!resourceExists)
            {
                return(NotFound());
            }

            //Ok, so the user IS the resource owner AND he did upload something, so let's validate the assetbundle header.
            //TODO: Refactor this into an object that does the validation and generates readable data
            //TODO: Actually implement asset bundle validation
            //We haven't implemented this yet, we should do asset bundle parsing and validation
            //This REALLY important to prevent invalid bundles from being uploaded
            //or content that isn't even an asset bundle being uploaded
            //See: https://github.com/HearthSim/UnityPack/wiki/Format-Documentation

            //For now, since it's unimplemented let's just set it validated
            await worldEntryRepository.SetWorldValidated(model.WorldId)
            .ConfigureAwaitFalseVoid();

            return(Ok());
        }
예제 #8
0
        public async Task <CharacterListResponse> GetCharacters()
        {
            int accountId = ClaimsReader.GetAccountIdInt(User);

            //So to check characters we just need to query for the
            //characters with this account id
            int[] characterIds = await CharacterRepository.CharacterIdsForAccountId(accountId);

            if (characterIds.Length == 0)
            {
                return(new CharacterListResponse(CharacterListResponseCode.NoCharactersFoundError));
            }

            //The reason we only provide the IDs is all other character data can be looked up
            //by the client when it needs it. Like name query, visible/character details/look stuff.
            //No reason to send all this data when they may only need names. Which can be queried through the known API
            return(new CharacterListResponse(characterIds));
        }
예제 #9
0
        public async Task <IActionResult> UpdateUploadedContent(
            [FromRoute(Name = "id")] long contentId,
            [FromServices] ICustomContentRepository <TContentType> contentEntryRepository,
            [FromServices] IStorageUrlBuilder urlBuilder)
        {
            //Content must exist.
            if (!await contentEntryRepository.ContainsAsync(contentId))
            {
                return(BuildFailedResponseModel(ContentUploadResponseCode.InvalidRequest));
            }

            //Unlike creation, we just load an existing one.
            TContentType content = await contentEntryRepository.RetrieveAsync(contentId)
                                   .ConfigureAwaitFalse();

            //WE MUST MAKE SURE THE AUTHORIZED USER OWNS THE CONTENT!!
            if (ClaimsReader.GetAccountIdInt(User) != content.AccountId)
            {
                return(BuildFailedResponseModel(ContentUploadResponseCode.AuthorizationFailed));
            }

            try
            {
                IActionResult response = await GenerateUploadTokenResponse(urlBuilder, content);

                //This is where we update the content versioning.
                content.Version += 1;
                await contentEntryRepository.UpdateAsync(content.ContentId, content)
                .ConfigureAwaitFalseVoid();

                return(response);
            }
            catch (Exception e)
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Failed to update content. Reason: {e.Message}");
                }

                return(BuildFailedResponseModel(ContentUploadResponseCode.InvalidRequest));
            }
        }
예제 #10
0
        public async Task <IActionResult> UpdateCharacterLocation(
            [FromRoute(Name = "id")] int characterId,
            [FromBody] ZoneServerCharacterLocationSaveRequest saveRequest,
            [NotNull][FromServices] ICharacterLocationRepository locationRepository,
            [NotNull][FromServices] IZoneServerRepository zoneRepository)
        {
            if (locationRepository == null)
            {
                throw new ArgumentNullException(nameof(locationRepository));
            }
            if (zoneRepository == null)
            {
                throw new ArgumentNullException(nameof(zoneRepository));
            }

            //TODO: For HTTP callers we should maybe include better information. Though with message queue we can't respond.
            if (!await CheckZoneAuthorizedToModifyCharacterData(characterId))
            {
                return(Forbid());
            }

            //TODO: This could fail if we unregistered. More gracefully handle that case.
            //Get world, we need it for location
            ZoneInstanceEntryModel zoneEntry = await zoneRepository.RetrieveAsync(ClaimsReader.GetAccountIdInt(User));

            //If world was deleted then they won't have location.
            //TODO: Is this the best way to deal with this?
            if (await locationRepository.ContainsAsync(characterId).ConfigureAwaitFalse())
            {
                await locationRepository.UpdateAsync(characterId, BuildCharacterLocationFromSave(characterId, saveRequest, zoneEntry.WorldId))
                .ConfigureAwaitFalseVoid();
            }
            else
            {
                await locationRepository.TryCreateAsync(BuildCharacterLocationFromSave(characterId, saveRequest, zoneEntry.WorldId))
                .ConfigureAwaitFalse();
            }

            return(Ok());
        }
예제 #11
0
        public async Task <IActionResult> TryRegisterZoneServer([FromBody] ZoneServerRegistrationRequest request,
                                                                [FromServices][NotNull] IWorldDataServiceClient worldDataClient)
        {
            if (worldDataClient == null)
            {
                throw new ArgumentNullException(nameof(worldDataClient));
            }

            if (!ModelState.IsValid)
            {
                return(BuildFailedResponseModel(ZoneServerRegistrationResponseCode.GeneralServerError));
            }

            //This should really ever happen in normal circumstances.
            if (await ZoneRepository.ContainsAsync(ClaimsReader.GetAccountIdInt(User)))
            {
                if (Logger.IsEnabled(LogLevel.Warning))
                {
                    Logger.LogWarning($"UserId: {ClaimsReader.GetPlayerAccountId(User)} attempted to register ZoneId: {ClaimsReader.GetAccountId(User)} multiple times.");
                }

                return(BuildFailedResponseModel(ZoneServerRegistrationResponseCode.ZoneAlreadyRegistered));
            }

            //Check world exists
            if (!await worldDataClient.CheckWorldExistsAsync(request.WorldId))
            {
                return(BuildFailedResponseModel(ZoneServerRegistrationResponseCode.WorldRequestedNotFound));
            }

            //TODO: Should we check world rights ownership here?
            bool registerResult = await ZoneRepository.TryCreateAsync(new ZoneInstanceEntryModel(ClaimsReader.GetAccountIdInt(User), HttpContext.Connection.RemoteIpAddress.ToString(), request.NetworkPort, request.WorldId));

            if (!registerResult)
            {
                return(BuildFailedResponseModel(ZoneServerRegistrationResponseCode.GeneralServerError));
            }

            return(BuildSuccessfulResponseModel(new ZoneServerRegistrationResponse(ClaimsReader.GetAccountIdInt(User))));
        }
예제 #12
0
		public async Task<CharacterSessionEnterResponse> SetCharacterSessionData([FromRoute(Name = "charid")] int characterId, [FromBody] int zoneId)
		{
			if(!await VerifyCharacterOwnedByAccount(characterId))
			{
				//TODO: Return not authed in JSON
				return new CharacterSessionEnterResponse(CharacterSessionEnterResponseCode.InvalidCharacterIdError);
			}

			//This case is actually pretty likely, they will likely be trying to move to another server
			//before their active session is cleaned up. Retry logic will be required to get past this.
			if(await CharacterSessionRepository.AccountHasActiveSession(ClaimsReader.GetAccountIdInt(User)))
			{
				//TODO: Return JSON that says active session
				return new CharacterSessionEnterResponse(CharacterSessionEnterResponseCode.AccountAlreadyHasCharacterSession);
			}

			//We can't check if it contains and then update, because that will there is a data race with
			//zoneserver deregisteration and cascading delteing.
			//So we only check for removal, and then remove and create

			//Don't try to delete the claimed session, we should try to delete the current session data. Since they want to change zones
			//and we consider that a new session
			if(!await CharacterSessionRepository.TryDeleteAsync(characterId))
			{
				//TODO: This could fail, potentially removed during race. But it's ok. It's ok that it's gone but may want to log it
			}

			//TODO: Refactor
			if(!await CharacterSessionRepository.TryCreateAsync(new CharacterSessionModel(characterId, zoneId)))
			{
				//Not sure what it wrong, no way to know really.
				return new CharacterSessionEnterResponse(CharacterSessionEnterResponseCode.GeneralServerError);
			}
				
			//It passed, they're allowed to join this zone.
			return new CharacterSessionEnterResponse(zoneId);
		}
예제 #13
0
        public async Task <IActionResult> CreateCharacter([FromRoute] string name,
                                                          [FromServices][NotNull] IPlayfabCharacterClient playfabCharacterClient,
                                                          [FromServices][NotNull] ICharacterAppearanceRepository characterAppearanceRepository,
                                                          [FromServices][NotNull] ICharacterDataRepository characterDataRepository)
        {
            if (playfabCharacterClient == null)
            {
                throw new ArgumentNullException(nameof(playfabCharacterClient));
            }
            if (characterAppearanceRepository == null)
            {
                throw new ArgumentNullException(nameof(characterAppearanceRepository));
            }
            if (characterDataRepository == null)
            {
                throw new ArgumentNullException(nameof(characterDataRepository));
            }
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentException("Value cannot be null or whitespace.", nameof(name));
            }

            int accountId = ClaimsReader.GetAccountIdInt(User);

            bool nameIsAvailable = await ValidateNameAvailability(name);

            if (!nameIsAvailable)
            {
                return(BadRequest(new CharacterCreationResponse(CharacterCreationResponseCode.NameUnavailableError)));
            }

            string playfabId = ClaimsReader.GetPlayfabId(User);

            //Now, we actually need to create the character on PlayFab first. It's better to have an orphaned character on PlayFab
            //than to have a character without a PlayFab equivalent.
            PlayFabResultModel <GladMMOPlayFabGrantCharacterToUserResult> playFabResultModel = await playfabCharacterClient.GrantCharacterToUser(new GladMMOPlayFabGrantCharacterToUserRequest(name, "test", playfabId));

            //TODO: Better error handling
            if (playFabResultModel.ResultCode != HttpStatusCode.OK)
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"PlayFab CharacterCreation Erorr: {playFabResultModel.ResultCode}:{playFabResultModel.ResultStatus}");
                }

                return(BadRequest(new CharacterCreationResponse(CharacterCreationResponseCode.GeneralServerError)));
            }

            CharacterEntryModel characterEntryModel = new CharacterEntryModel(accountId, name, playfabId, playFabResultModel.Data.CharacterId);

            //TODO: We need a transition around the creation of the below entries.
            ProjectVersionStage.AssertBeta();
            //TODO: Don't expose the database table model
            //Otherwise we should try to create. There is a race condition here that can cause it to still fail
            //since others could create a character with this name before we finish after checking
            bool result = await CharacterRepository.TryCreateAsync(characterEntryModel);

            //TODO: Also needs to be apart of the transaction
            if (result)
            {
                await characterDataRepository.TryCreateAsync(new CharacterDataModel(characterEntryModel.CharacterId, 0));

                await characterAppearanceRepository.TryCreateAsync(new CharacterAppearanceModel(characterEntryModel.CharacterId, 9));                 //Default is 9 right now.
            }

            return(Json(new CharacterCreationResponse(CharacterCreationResponseCode.Success)));
        }
예제 #14
0
		public async Task<CharacterSessionEnterResponse> EnterSession([FromRoute(Name = "id")] int characterId,
			[FromServices] ICharacterLocationRepository characterLocationRepository,
			[FromServices] IZoneServerRepository zoneServerRepository)
		{
			if(!await IsCharacterIdValidForUser(characterId, CharacterRepository))
				return new CharacterSessionEnterResponse(CharacterSessionEnterResponseCode.InvalidCharacterIdError);

			int accountId = ClaimsReader.GetAccountIdInt(User);

			//This checks to see if the account, not just the character, has an active session.
			//We do this before we check anything to reject quick even though the query behind this
			//may be abit more expensive
			//As a note, this checks (or should) CLAIMED SESSIONS. So, it won't prevent multiple session entries for an account
			//This is good because we actually use the left over session data to re-enter the instances on disconnect.
			if(await CharacterSessionRepository.AccountHasActiveSession(accountId))
				return new CharacterSessionEnterResponse(CharacterSessionEnterResponseCode.AccountAlreadyHasCharacterSession);

			//They may have a session entry already, which is ok. So long as they don't have an active claimed session
			//which the above query checks for.
			bool hasSession = await CharacterSessionRepository.ContainsAsync(characterId);

			//We need to check active or not
			if (hasSession)
			{
				//It's possible that the session no longer matches the character's
				//persisted location. We should check their location and put them in the correct zone.

				//If it's active we can just retrieve the data and send them off on their way
				CharacterSessionModel sessionModel = await CharacterSessionRepository.RetrieveAsync(characterId, true);

				if (await characterLocationRepository.ContainsAsync(characterId))
				{
					CharacterLocationModel locationModel = await characterLocationRepository.RetrieveAsync(characterId);
					//They have a location, verify it matches the session
					if (locationModel.WorldId != sessionModel.ZoneEntry.WorldId)
					{
						//The location world and the session's world do not match, so remove the session.
						await CharacterSessionRepository.TryDeleteAsync(sessionModel.CharacterId);
					}
					else
						return new CharacterSessionEnterResponse(sessionModel.ZoneId);
				}
				else
					//TODO: Handle case when we have an inactive session that can be claimed
					return new CharacterSessionEnterResponse(sessionModel.ZoneId);
			}

			//If we didn't return above then we should be in a state where the below can handle this now.

			try
			{
				int targetSessionZoneId = 0;

				//TO know what zone we should connect to we need to check potential
				//character location.
				if (await characterLocationRepository.ContainsAsync(characterId))
				{
					CharacterLocationModel locationModel = await characterLocationRepository.RetrieveAsync(characterId);

					//We have no session so we need to find a zone that matches this server.
					ZoneInstanceEntryModel firstWithWorldId = await zoneServerRepository.FindFirstWithWorldId(locationModel.WorldId);

					//TODO: Should we request one be created for the user??
					//There is NO instance available for this world.
					if (firstWithWorldId == null)
					{
						//Location is basically invalid since there is no running world
						await characterLocationRepository.TryDeleteAsync(locationModel.CharacterId);
					}
					else
					{
						targetSessionZoneId = firstWithWorldId.ZoneId;
					}
				}

				//Try to get into any zone
				if (targetSessionZoneId == 0)
				{
					ZoneInstanceEntryModel entryModel = await zoneServerRepository.AnyAsync();

					if (entryModel != null)
						targetSessionZoneId = entryModel.ZoneId;
				}

				//Still zero means literally no zone servers are available.
				if(targetSessionZoneId == 0)
					return new CharacterSessionEnterResponse(CharacterSessionEnterResponseCode.GeneralServerError);

				if(await CharacterSessionRepository.TryCreateAsync(new CharacterSessionModel(characterId, targetSessionZoneId)))
					return new CharacterSessionEnterResponse(targetSessionZoneId);
				else
					return new CharacterSessionEnterResponse(CharacterSessionEnterResponseCode.GeneralServerError);

			}
			catch (Exception e)
			{
				if(Logger.IsEnabled(LogLevel.Error))
					Logger.LogError($"Character with ID: {characterId} failed to create session. Potentially no default world assigned or World with session was deleted and orphaned. Reason: {e.Message}");
				throw;
			}
		}
예제 #15
0
        public async Task <IActionResult> RequestContentDownloadURL(
            [FromRoute(Name = "id")] long contentId,
            [FromServices] ICustomContentRepository <TContentType> contentEntryRepository,
            [FromServices] IStorageUrlBuilder urlBuilder,
            [FromServices] IContentDownloadAuthroizationValidator downloadAuthorizer)
        {
            if (contentEntryRepository == null)
            {
                throw new ArgumentNullException(nameof(contentEntryRepository));
            }

            //TODO: We want to rate limit access to this API
            //TODO: We should use both app logging but also another logging service that always gets hit

            //TODO: Consolidate this shared logic between controllers
            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Recieved {nameof(RequestContentDownloadURL)} request from {ClaimsReader.GetAccountName(User)}:{ClaimsReader.GetAccountId(User)}.");
            }

            //TODO: We should probably check the flags of content to see if it's private (IE hidden from user). Or if it's unlisted or removed.
            //It's possible a user is requesting a content that doesn't exist
            //Could be malicious or it could have been deleted for whatever reason
            if (!await contentEntryRepository.ContainsAsync(contentId).ConfigureAwaitFalse())
            {
                return(Json(new ContentDownloadURLResponse(ContentDownloadURLResponseCode.NoContentId)));
            }

            //TODO: Refactor this into a validation dependency
            //Now we need to do some validation to determine if they should even be downloading this content
            //we do not want people downloading a content they have no business of going to
            int userId = ClaimsReader.GetAccountIdInt(User);

            //TODO: Need to NOT call it world content
            if (!await downloadAuthorizer.CanUserAccessWorldContet(userId, contentId))
            {
                return(Json(new ContentDownloadURLResponse(ContentDownloadURLResponseCode.AuthorizationFailed)));
            }

            TContentType contentEntry = await contentEntryRepository.RetrieveAsync(contentId);

            //We can get the URL from the urlbuilder if we provide the content storage GUID
            string downloadUrl = await urlBuilder.BuildRetrivalUrl(ContentType, contentEntry.StorageGuid);

            //TODO: Should we be validating S3 availability?
            if (String.IsNullOrEmpty(downloadUrl))
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Failed to create content upload URL for {ClaimsReader.GetAccountName(User)}:{ClaimsReader.GetAccountId(User)} with ID: {contentId}.");
                }

                return(Json(new ContentDownloadURLResponse(ContentDownloadURLResponseCode.ContentDownloadServiceUnavailable)));
            }

            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Success. Sending {ClaimsReader.GetAccountName(User)} URL: {downloadUrl}");
            }

            return(Json(new ContentDownloadURLResponse(downloadUrl, contentEntry.Version)));
        }
예제 #16
0
        public async Task <IActionResult> GetCharacterGuildList([NotNull][FromServices] IGuildCharacterMembershipRepository guildCharacterMembershipRepository,
                                                                [FromServices] ISocialServiceToGameServiceClient socialToGameClient)
        {
            if (guildCharacterMembershipRepository == null)
            {
                throw new ArgumentNullException(nameof(guildCharacterMembershipRepository));
            }

            CharacterSessionDataResponse session = await socialToGameClient.GetCharacterSessionDataByAccount(ClaimsReader.GetAccountIdInt(User));

            if (!session.isSuccessful)
            {
                return(BuildFailedResponseModel(CharacterGuildMembershipStatusResponseCode.GeneralServerError));
            }

            //No guild check
            if (!await guildCharacterMembershipRepository.ContainsAsync(session.CharacterId))
            {
                return(BuildFailedResponseModel(CharacterGuildMembershipStatusResponseCode.NoGuild));
            }

            var playerGuildMembership = await guildCharacterMembershipRepository.RetrieveAsync(session.CharacterId);

            int[] roster = await guildCharacterMembershipRepository.GetEntireGuildRosterAsync(playerGuildMembership.GuildId);

            return(BuildSuccessfulResponseModel(new CharacterGuildListResponseModel(roster)));
        }
예제 #17
0
        public async Task <IActionResult> TryAddFriend([FromRoute(Name = "name")][JetBrains.Annotations.NotNull] string characterFriendName,
                                                       [FromServices][JetBrains.Annotations.NotNull] ICharacterFriendRepository friendsRepository,
                                                       [FromServices][JetBrains.Annotations.NotNull] ISocialServiceToGameServiceClient socialServiceClient,
                                                       [FromServices] INameQueryService nameQueryService)
        {
            if (friendsRepository == null)
            {
                throw new ArgumentNullException(nameof(friendsRepository));
            }
            if (socialServiceClient == null)
            {
                throw new ArgumentNullException(nameof(socialServiceClient));
            }
            if (string.IsNullOrEmpty(characterFriendName))
            {
                throw new ArgumentException("Value cannot be null or empty.", nameof(characterFriendName));
            }

            //Find the character
            CharacterSessionDataResponse response = await socialServiceClient.GetCharacterSessionDataByAccount(ClaimsReader.GetAccountIdInt(User));

            if (response.ResultCode == CharacterSessionDataResponseCode.NoSessionAvailable)
            {
                return(BadRequest());
            }

            var nameReverseQueryResponse = await nameQueryService.RetrievePlayerGuidAsync(characterFriendName);

            //Handle known failure cases first.
            switch (nameReverseQueryResponse.ResultCode)
            {
            case NameQueryResponseCode.UnknownIdError:
                return(BuildFailedResponseModel(CharacterFriendAddResponseCode.CharacterNotFound));

            case NameQueryResponseCode.GeneralServerError:
                return(BuildFailedResponseModel(CharacterFriendAddResponseCode.GeneralServerError));
            }

            //If the player is trying to add himself, just say not found
            if (nameReverseQueryResponse.Result.EntityId == response.CharacterId)
            {
                return(BuildFailedResponseModel(CharacterFriendAddResponseCode.CharacterNotFound));
            }

            //Ok, reverse namequery is a success
            //now we must check some stuff

            //Already friends check
            if (await friendsRepository.IsFriendshipPresentAsync(response.CharacterId, nameReverseQueryResponse.Result.EntityId))
            {
                return(BuildFailedResponseModel(CharacterFriendAddResponseCode.AlreadyFriends));
            }

            if (await friendsRepository.TryCreateAsync(new CharacterFriendModel(response.CharacterId, nameReverseQueryResponse.Result.EntityId)))
            {
                //This is a success, let's tell them about who they added.
                return(BuildSuccessfulResponseModel(new CharacterFriendAddResponseModel(nameReverseQueryResponse.Result)));
            }
            else
            {
                return(BuildFailedResponseModel(CharacterFriendAddResponseCode.GeneralServerError));
            }
        }
예제 #18
0
        protected override WorldEntryModel GenerateNewModel()
        {
            int userId = ClaimsReader.GetAccountIdInt(User);

            return(new WorldEntryModel(userId, HttpContext.Connection.RemoteIpAddress.ToString(), Guid.NewGuid()));
        }
예제 #19
0
        public async Task <IActionResult> GetCharacterFriends([FromServices][JetBrains.Annotations.NotNull] ICharacterFriendRepository friendsRepository,
                                                              [FromServices][JetBrains.Annotations.NotNull] ISocialServiceToGameServiceClient socialServiceClient)
        {
            if (friendsRepository == null)
            {
                throw new ArgumentNullException(nameof(friendsRepository));
            }
            if (socialServiceClient == null)
            {
                throw new ArgumentNullException(nameof(socialServiceClient));
            }

            //Find the character
            CharacterSessionDataResponse response = await socialServiceClient.GetCharacterSessionDataByAccount(ClaimsReader.GetAccountIdInt(User));

            if (response.ResultCode == CharacterSessionDataResponseCode.NoSessionAvailable)
            {
                return(Json(new CharacterFriendListResponseModel(Array.Empty <int>())));
            }

            int[] friendsCharacterIds = await friendsRepository.GetCharactersFriendsList(response.CharacterId);

            return(Json(new CharacterFriendListResponseModel(friendsCharacterIds)));
        }