< Summary

Class:DCLServices.QuestsService.QuestsService
Assembly:QuestsService
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLServices/QuestsService/QuestsService.cs
Covered lines:0
Uncovered lines:74
Coverable lines:74
Total lines:188
Line coverage:0% (0 of 74)
Covered branches:0
Total branches:0
Covered methods:0
Total methods:13
Method coverage:0% (0 of 13)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
QuestsService(...)0%2100%
Subscribe()0%4202000%
StartQuest()0%30500%
AbortQuest()0%42600%
>c__DisplayClass24_0/<<GetDefinition()0%20400%
GetDefinition(...)0%6200%
>c__DisplayClass25_0/<<GetQuestRewards()0%20400%
GetQuestRewards(...)0%6200%
Dispose()0%2100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLServices/QuestsService/QuestsService.cs

#LineLine coverage
 1using Cysharp.Threading.Tasks;
 2using DCL.Helpers;
 3using DCL.Tasks;
 4using Decentraland.Quests;
 5using Google.Protobuf.WellKnownTypes;
 6using System.Collections.Generic;
 7using System.Threading;
 8using DCL;
 9using UnityEngine;
 10using UnityEngine.Networking;
 11using DCLServices.QuestsService;
 12
 13namespace DCLServices.QuestsService
 14{
 15    /* TODO Alex:
 16        - Add service to ServiceLocator
 17        - Find a good place to call QuestsService.SetUserId
 18        All these requirements are only needed to launch quests,
 19        in the meantime the service is ready to be tested by mocking a ClientQuestService (look at the test scene)
 20     */
 21    public class QuestsService : IQuestsService
 22    {
 23        private const bool VERBOSE = false;
 24
 025        public IAsyncEnumerableWithEvent<QuestInstance> QuestStarted => questStarted;
 026        public IAsyncEnumerableWithEvent<QuestInstance> QuestUpdated => questUpdated;
 27
 028        private readonly AsyncEnumerableWithEvent<QuestInstance> questStarted = new ();
 029        private readonly AsyncEnumerableWithEvent<QuestInstance> questUpdated = new ();
 30
 031        public IReadOnlyDictionary<string, QuestInstance> QuestInstances => questInstances;
 032        public IReadOnlyDictionary<string, object> QuestRewards { get; }
 33
 034        internal readonly Dictionary<string, QuestInstance> questInstances = new ();
 35
 36        internal readonly IClientQuestsService clientQuestsService;
 37        internal readonly IQuestRewardsResolver questRewardsResolver;
 38        private Service<IWebRequestController> webRequestController;
 039        internal readonly Dictionary<string, UniTaskCompletionSource<Quest>> definitionCache = new ();
 040        internal readonly Dictionary<string, UniTaskCompletionSource<IReadOnlyList<QuestReward>>> rewardsCache = new ();
 41
 042        internal readonly CancellationTokenSource disposeCts = new ();
 043        internal readonly UniTaskCompletionSource gettingInitialState = new ();
 44
 045        public QuestsService(IClientQuestsService clientQuestsService, IQuestRewardsResolver questRewardsResolver)
 46        {
 047            this.clientQuestsService = clientQuestsService;
 048            this.questRewardsResolver = questRewardsResolver;
 049            Subscribe().Forget();
 050        }
 51
 52        private async UniTaskVoid Subscribe()
 53        {
 54            //Obtain initial state
 055            var allquests = await clientQuestsService.GetAllQuests(new Empty());
 56
 57            if(VERBOSE)
 58                Debug.Log("[QuestsService] Getting all quests");
 59
 060            foreach (QuestInstance questInstance in allquests.Quests.Instances)
 61            {
 62                if(VERBOSE)
 63                    Debug.Log($"[QuestsService]\n{questInstance}");
 064                questInstances[questInstance.Id] = questInstance;
 065                string questId = questInstance.Quest.Id;
 66
 067                if (!definitionCache.TryGetValue(questId, out var completionSource))
 068                    definitionCache[questId] = completionSource = new UniTaskCompletionSource<Quest>();
 69
 070                completionSource.TrySetResult(questInstance.Quest);
 71
 072                questUpdated.Write(questInstance);
 73            }
 74
 75            //Listen to updates
 076            var enumerable = clientQuestsService.Subscribe(new Empty());
 77
 78            if(VERBOSE)
 79                Debug.Log($"[QuestsService] Subscribing");
 80
 081            await foreach (UserUpdate userUpdate in enumerable.WithCancellation(disposeCts.Token))
 82            {
 83                if(VERBOSE)
 84                    Debug.Log($"[QuestsService] Update:\n{userUpdate}");
 085                switch (userUpdate.MessageCase)
 86                {
 87                    case UserUpdate.MessageOneofCase.Subscribed:
 088                        gettingInitialState.TrySetResult();
 089                        break;
 90                    case UserUpdate.MessageOneofCase.QuestStateUpdate:
 091                        if (!questInstances.TryGetValue(userUpdate.QuestStateUpdate.InstanceId, out var questUpdatedInst
 92                        {
 93                            if(VERBOSE)
 94                                Debug.Log($"Received quest update which instance was not received before: {userUpdate.To
 95
 96                            continue;
 97                        }
 098                        questUpdatedInstance.State = userUpdate.QuestStateUpdate.QuestState;
 099                        questUpdated.Write(questUpdatedInstance);
 0100                        break;
 101
 102                    case UserUpdate.MessageOneofCase.NewQuestStarted:
 0103                        var questInstance = userUpdate.NewQuestStarted;
 0104                        questInstances[questInstance.Id] = questInstance;
 105
 0106                        string questId = questInstance.Quest.Id;
 0107                        if (!definitionCache.TryGetValue(questId, out var completionSource))
 0108                            definitionCache[questId] = completionSource = new UniTaskCompletionSource<Quest>();
 109
 0110                        questStarted.Write(questInstance);
 0111                        completionSource.TrySetResult(questInstance.Quest);
 112                        break;
 113                }
 114            }
 0115        }
 116
 117        public async UniTask<StartQuestResponse> StartQuest(string questId)
 118        {
 0119            await gettingInitialState.Task;
 0120            return await clientQuestsService.StartQuest(new StartQuestRequest { QuestId = questId });
 0121        }
 122
 123        public async UniTask<AbortQuestResponse> AbortQuest(string questInstanceId)
 124        {
 0125            await gettingInitialState.Task;
 0126            AbortQuestResponse abortQuestResponse = await clientQuestsService.AbortQuest(new AbortQuestRequest { QuestIn
 127
 0128            if (abortQuestResponse.ResponseCase == AbortQuestResponse.ResponseOneofCase.Accepted)
 0129                questInstances.Remove(questInstanceId);
 0130            return abortQuestResponse;
 0131        }
 132
 133        public UniTask<Quest> GetDefinition(string questId, CancellationToken cancellationToken = default)
 134        {
 135            UniTaskCompletionSource<Quest> definitionCompletionSource;
 136
 137            async UniTask<Quest> RetrieveTask()
 138            {
 0139                GetQuestDefinitionResponse definition = await clientQuestsService.GetQuestDefinition(new GetQuestDefinit
 140
 0141                if (definitionCache.TryGetValue(definition.Quest.Id, out definitionCompletionSource))
 0142                    definitionCache[definition.Quest.Id].TrySetResult(definition.Quest);
 143
 0144                return definition.Quest;
 0145            }
 146
 0147            if (!definitionCache.TryGetValue(questId, out definitionCompletionSource))
 148            {
 0149                definitionCompletionSource = new UniTaskCompletionSource<Quest>();
 0150                definitionCache[questId] = definitionCompletionSource;
 0151                RetrieveTask().Forget();
 152            }
 153
 0154            return definitionCompletionSource.Task.AttachExternalCancellation(cancellationToken);
 155        }
 156
 157        public UniTask<IReadOnlyList<QuestReward>> GetQuestRewards(string questId, CancellationToken cancellationToken =
 158        {
 159            UniTaskCompletionSource<IReadOnlyList<QuestReward>> rewardsCompletionSource;
 160
 161            async UniTask<IReadOnlyList<QuestReward>> RetrieveTask()
 162            {
 0163                IReadOnlyList<QuestReward> response = await questRewardsResolver.ResolveRewards(questId, cancellationTok
 164
 0165                if (rewardsCache.TryGetValue(questId, out rewardsCompletionSource))
 0166                    rewardsCache[questId].TrySetResult(response);
 167
 0168                return response;
 0169            }
 170
 0171            if (!rewardsCache.TryGetValue(questId, out rewardsCompletionSource))
 172            {
 0173                rewardsCompletionSource = new UniTaskCompletionSource<IReadOnlyList<QuestReward>>();
 0174                rewardsCache[questId] = rewardsCompletionSource;
 0175                RetrieveTask().Forget();
 176            }
 177
 0178            return rewardsCompletionSource.Task.AttachExternalCancellation(cancellationToken);
 179        }
 180
 181        public void Dispose()
 182        {
 0183            disposeCts.SafeCancelAndDispose();
 0184            questStarted.Dispose();
 0185            questUpdated.Dispose();
 0186        }
 187    }
 188}