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()); }
/// <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); }
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(); }
/// <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); }
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); }
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}"))); } }
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()); }
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)); }
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)); } }
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()); }
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)))); }
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); }
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))); }
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; } }
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))); }
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))); }
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)); } }
protected override WorldEntryModel GenerateNewModel() { int userId = ClaimsReader.GetAccountIdInt(User); return(new WorldEntryModel(userId, HttpContext.Connection.RemoteIpAddress.ToString(), Guid.NewGuid())); }
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))); }