From 2b71ed34e4dfa1ca51bc839b3cdd5a06b655110a Mon Sep 17 00:00:00 2001 From: Jay Malhotra <5047192+SapiensAnatis@users.noreply.github.com> Date: Sat, 6 Apr 2024 16:29:36 +0100 Subject: [PATCH] Fix story mission softlock (#753) A player got into a state where they were unable to progress the main campaign as their client was refusing to call `/mission/unlock_main_story_group`. Upon further investigation it seems the cause of this is that we are always sending `current_main_story_mission` from `/mission/get_mission_list` with data, even if it relates to main story missions that are already completed. This lead to the missions showing as completed on their screen, but without any endeavours to claim and with the quest remaining locked. Fix this by filtering out the current main story mission to those that are not claimed. We will therefore send an empty `current_main_story_mission` if none is in progress. This fixes the issue, and lines up with what I have seen in my captures from my main account on the official servers, which had completed the campaign. Also includes a misc fix to user impersionation as I used it while troubleshooting and noticed that it was broken after the global query filter changes :P --- .../Features/Missions/MissionTest.cs | 64 +++++++++++++++++++ .../Features/GraphQL/MutationBase.cs | 14 ++-- .../Features/Missions/MissionService.cs | 1 + 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/DragaliaAPI/DragaliaAPI.Integration.Test/Features/Missions/MissionTest.cs b/DragaliaAPI/DragaliaAPI.Integration.Test/Features/Missions/MissionTest.cs index 7e0cb2024..5d22bec01 100644 --- a/DragaliaAPI/DragaliaAPI.Integration.Test/Features/Missions/MissionTest.cs +++ b/DragaliaAPI/DragaliaAPI.Integration.Test/Features/Missions/MissionTest.cs @@ -572,4 +572,68 @@ await this.Client.PostMsgpack("mission/get_missio .And.Contain(x => x.PeriodMissionId == expectedMission.Id) .And.Contain(x => x.PeriodMissionId == otherExpectedMission.Id); } + + [Fact] + public async Task GetMissionList_DoesNotReturnPreviousMainStoryMissions() + { + IEnumerable completedMissions = MasterAsset + .MissionMainStoryData.Enumerable.Where(x => x.MissionMainStoryGroupId == 1) + .Select(x => new DbPlayerMission() + { + Id = x.Id, + GroupId = 1, + ViewerId = this.ViewerId, + Type = MissionType.MainStory, + State = MissionState.Claimed + }); + + await this.AddRangeToDatabase(completedMissions); + + DragaliaResponse response = + await this.Client.PostMsgpack( + "mission/get_mission_list" + ); + + response + .Data.CurrentMainStoryMission.Should() + .BeEquivalentTo(new CurrentMainStoryMission()); + } + + [Fact] + public async Task GetMissionList_ReturnsCurrentMainStoryMissions() + { + IEnumerable inProgressMissions = MasterAsset + .MissionMainStoryData.Enumerable.Where(x => x.MissionMainStoryGroupId == 1) + .Select(x => new DbPlayerMission() + { + Id = x.Id, + GroupId = 1, + ViewerId = this.ViewerId, + Type = MissionType.MainStory, + State = MissionState.InProgress, + }); + + await this.AddRangeToDatabase(inProgressMissions); + + DragaliaResponse response = + await this.Client.PostMsgpack( + "mission/get_mission_list" + ); + + response + .Data.CurrentMainStoryMission.Should() + .BeEquivalentTo( + new CurrentMainStoryMission() + { + MainStoryMissionGroupId = 1, + MainStoryMissionStateList = MasterAsset + .MissionMainStoryData.Enumerable.Where(x => x.MissionMainStoryGroupId == 1) + .Select(x => new AtgenMainStoryMissionStateList() + { + MainStoryMissionId = x.Id, + State = (int)MissionState.InProgress, + }) + } + ); + } } diff --git a/DragaliaAPI/DragaliaAPI/Features/GraphQL/MutationBase.cs b/DragaliaAPI/DragaliaAPI/Features/GraphQL/MutationBase.cs index cc3279194..cab01d06d 100644 --- a/DragaliaAPI/DragaliaAPI/Features/GraphQL/MutationBase.cs +++ b/DragaliaAPI/DragaliaAPI/Features/GraphQL/MutationBase.cs @@ -2,6 +2,7 @@ using DragaliaAPI.Database; using DragaliaAPI.Database.Entities; using DragaliaAPI.Shared.PlayerDetails; +using Microsoft.EntityFrameworkCore; namespace DragaliaAPI.Features.GraphQL; @@ -24,11 +25,9 @@ protected IDisposable StartUserImpersonation( Func, IQueryable>? include = null ) { - IDisposable context = this.identityService.StartUserImpersonation(viewerId, null); - - IQueryable query = this.apiContext.Players.Where(x => - x.UserData != null && x.UserData.ViewerId == viewerId - ); + IQueryable query = this + .apiContext.Players.IgnoreQueryFilters() + .Where(x => x.UserData != null && x.UserData.ViewerId == viewerId); if (include is not null) query = include.Invoke(query); @@ -40,6 +39,11 @@ protected IDisposable StartUserImpersonation( this.Player = player; + IDisposable context = this.identityService.StartUserImpersonation( + viewerId, + player.AccountId + ); + return context; } } diff --git a/DragaliaAPI/DragaliaAPI/Features/Missions/MissionService.cs b/DragaliaAPI/DragaliaAPI/Features/Missions/MissionService.cs index 11df7901b..89205a665 100644 --- a/DragaliaAPI/DragaliaAPI/Features/Missions/MissionService.cs +++ b/DragaliaAPI/DragaliaAPI/Features/Missions/MissionService.cs @@ -439,6 +439,7 @@ public async Task GetCurrentMainStoryMission() { int? mainStoryMissionGroupId = await this .missionRepository.GetMissionsByType(MissionType.MainStory) + .Where(x => x.State < MissionState.Claimed) .MaxAsync(x => x.GroupId); if (mainStoryMissionGroupId == null)