/// <inheritdoc /> public async Task <IPrefabContentResourceHandle> LoadAvatarPrefabAsync(long avatarId) { //If it's already available, we can just return immediately if (IsAvatarResourceAvailable(avatarId)) { return(TryLoadAvatarPrefab(avatarId)); } ContentDownloadURLResponse downloadUrlResponse = await ContentClient.RequestAvatarDownloadUrl(avatarId, AuthTokenRepo.RetrieveWithType()) .ConfigureAwait(false); //TODO: Handle failure TaskCompletionSource <IPrefabContentResourceHandle> completionSource = new TaskCompletionSource <IPrefabContentResourceHandle>(); //Asset bundle requests can sadly only happen on the main thread, so we must join the main thread. await new UnityYieldAwaitable(); //TODO: We should handle caching, versioning and etc here. UnityWebRequestAsyncOperation asyncOperation = UnityWebRequestAssetBundle.GetAssetBundle(downloadUrlResponse.DownloadURL, 0).SendWebRequest(); //TODO: We should render these operations to the loading screen UI. asyncOperation.completed += operation => { //When we first get back on the main thread, the main concern //is that this resource manager may be from the last scene //and that the client may have moved on //to avoid this issues we check disposal state //and do nothing, otherwise if we check AFTER then we just have to release the assetbundle immediately anyway. if (isDisposed) { //Just tell anyone awaiting this that it is canceled. They should handle that case, not us. completionSource.SetCanceled(); return; } //GetContent will throw if the assetbundle has already been loaded. //So to prevent this from occuring due to multiple requests for the //content async we will check, on this main thread, via a write lock. lock (SyncObj) { //We're on the main thread again. So, we should check if another //request already got the bundle if (IsAvatarResourceAvailable(avatarId)) { completionSource.SetResult(TryLoadAvatarPrefab(avatarId)); return; } //otherwise, we still don't have it so we should initialize it. this.ResourceHandleCache[avatarId] = new ReferenceCountedPrefabContentResourceHandle(DownloadHandlerAssetBundle.GetContent(asyncOperation.webRequest)); completionSource.SetResult(TryLoadAvatarPrefab(avatarId)); //we assume this will work now. } }; return(await completionSource.Task .ConfigureAwait(false)); }
//TODO: Refactor this behind its own object to provide download URL for character. /// <inheritdoc /> public async Task OnGameInitialized() { if (Logger.IsInfoEnabled) { Logger.Info("About to start downloading map data."); } //When we start the loading screen for the game //To know what world we should load we should CharacterSessionDataResponse characterSessionData = await CharacterService.GetCharacterSessionData(LocalCharacterData.CharacterId, AuthTokenRepo.RetrieveWithType()) .ConfigureAwait(false); if (!characterSessionData.isSuccessful) { Logger.Error($"Failed to query Character Session Data: {characterSessionData.ResultCode}:{(int)characterSessionData.ResultCode}"); return; } //TODO: Handle failure ProjectVersionStage.AssertAlpha(); //TODO: Handle throwing/error //We need to know the world the zone is it, so we can request a download URL for it. long worldId = await ZoneDataService.GetZoneWorld(characterSessionData.ZoneId) .ConfigureAwait(false); //With the worldid we can get the download URL. ContentDownloadURLResponse urlDownloadResponse = await ContentService.RequestWorldDownloadUrl(worldId, AuthTokenRepo.RetrieveWithType()) .ConfigureAwait(false); //TODO: Handle failure if (urlDownloadResponse.isSuccessful) { if (Logger.IsInfoEnabled) { Logger.Info($"Download URL: {urlDownloadResponse.DownloadURL}"); } //Can't do web request not on the main thread, sadly. await new UnityYieldAwaitable(); //TODO: Do we need to be on the main unity3d thread UnityWebRequestAsyncOperation asyncOperation = UnityWebRequestAssetBundle.GetAssetBundle(urlDownloadResponse.DownloadURL, 0).SendWebRequest(); //TODO: We should render these operations to the loading screen UI. asyncOperation.completed += operation => { AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(asyncOperation.webRequest); string[] paths = bundle.GetAllScenePaths(); foreach (string p in paths) { Debug.Log($"Found Scene in Bundle: {p}"); } AsyncOperation sceneAsync = SceneManager.LoadSceneAsync(System.IO.Path.GetFileNameWithoutExtension(paths.First())); sceneAsync.completed += operation1 => { //When the scene is finished loading we should cleanup the asset bundle //Don't clean up the WHOLE BUNDLE, just the compressed downloaded data bundle.Unload(false); //TODO: We need a way/system to reference the bundle later so it can be cleaned up inbetween scene loads. }; sceneAsync.allowSceneActivation = true; }; } }