Exemple #1
0
    public async UniTask <List <Level> > LoadFromMetadataFiles(LevelType type, List <string> jsonPaths, bool forceReload = false)
    {
        var lowMemory = false;

        Application.lowMemory += OnLowMemory;
        void OnLowMemory()
        {
            lowMemory = true;
        }

        var loadedCount = 0;
        var tasks       = new List <UniTask>();
        var results     = new List <Level>();
        int index;

        for (index = 0; index < jsonPaths.Count; index++)
        {
            var loadIndex = index;
            async UniTask LoadLevel()
            {
                var timer = new BenchmarkTimer($"Level loader ({loadIndex + 1} / {jsonPaths.Count})")
                {
                    Enabled = false
                };
                var jsonPath = jsonPaths[loadIndex];

                try
                {
                    FileInfo info;
                    try
                    {
                        info = new FileInfo(jsonPath);
                        if (info.Directory == null)
                        {
                            throw new FileNotFoundException(info.ToString());
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.LogWarning(e);
                        Debug.LogWarning($"{jsonPath} could not be read");
                        Debug.LogWarning($"Skipped {loadIndex + 1}/{jsonPaths.Count} from {jsonPath}");
                        return;
                    }

                    var path = info.Directory.FullName + Path.DirectorySeparatorChar;

                    if (!forceReload && loadedPaths.Contains(path))
                    {
                        Debug.LogWarning($"Level from {path} is already loaded");
                        Debug.LogWarning($"Skipped {loadIndex + 1}/{jsonPaths.Count} from {path}");
                        return;
                    }

                    Debug.Log($"Loading {loadIndex + 1}/{jsonPaths.Count} from {path}");

                    if (!File.Exists(jsonPath))
                    {
                        Debug.LogWarning($"level.json not found at {jsonPath}");
                        Debug.LogWarning($"Skipped {loadIndex + 1}/{jsonPaths.Count} from {path}");
                        return;
                    }

                    await UniTask.SwitchToThreadPool();

                    var meta = JsonConvert.DeserializeObject <LevelMeta>(File.ReadAllText(jsonPath));
                    await UniTask.SwitchToMainThread();

                    timer.Time("Deserialization");

                    if (meta == null)
                    {
                        Debug.LogWarning($"Invalid level.json at {jsonPath}");
                        Debug.LogWarning($"Skipped {loadIndex + 1}/{jsonPaths.Count} from {path}");
                        return;
                    }

                    if (type != LevelType.Temp && LoadedLocalLevels.ContainsKey(meta.id))
                    {
                        if (LoadedLocalLevels[meta.id].Type == LevelType.Tier && type == LevelType.User)
                        {
                            Debug.LogWarning($"Community level cannot override tier level");
                            Debug.LogWarning($"Skipped {loadIndex + 1}/{jsonPaths.Count} from {path}");
                            return;
                        }
                        if (LoadedLocalLevels[meta.id].Meta.version > meta.version)
                        {
                            Debug.LogWarning($"Level to load has smaller version than loaded level");
                            Debug.LogWarning($"Skipped {loadIndex + 1}/{jsonPaths.Count} from {path}");
                            return;
                        }
                        loadedPaths.Remove(LoadedLocalLevels[meta.id].Path);
                    }

                    // Sort charts
                    meta.SortCharts();

                    // Reject invalid level meta
                    if (!meta.Validate())
                    {
                        Debug.LogWarning($"Invalid metadata in level.json at {jsonPath}");
                        Debug.LogWarning($"Skipped {loadIndex + 1}/{jsonPaths.Count} from {path}");
                        return;
                    }

                    timer.Time("Validate");

                    var db = Context.Database;
                    await UniTask.SwitchToThreadPool();

                    var level  = Level.FromLocal(path, type, meta, db);
                    var record = level.Record;
                    if (record.AddedDate == DateTimeOffset.MinValue)
                    {
                        record.AddedDate = Context.Library.Levels.ContainsKey(level.Id)
                            ? Context.Library.Levels[level.Id].Date
                            : info.LastWriteTimeUtc;
                        level.SaveRecord();
                    }
                    await UniTask.SwitchToMainThread();

                    timer.Time("LevelRecord");

                    if (type != LevelType.Temp)
                    {
                        LoadedLocalLevels[meta.id] = level;
                        loadedPaths.Add(path);

                        // Generate thumbnail
                        if (!File.Exists(level.Path + CoverThumbnailFilename))
                        {
                            var thumbnailPath = "file://" + level.Path + level.Meta.background.path;

                            if (lowMemory)
                            {
                                // Give up
                                Debug.LogWarning($"Low memory!");
                                Debug.LogWarning($"Skipped {loadIndex + 1}/{jsonPaths.Count} from {jsonPath}");
                                return;
                            }

                            using (var request = UnityWebRequest.Get(thumbnailPath))
                            {
                                await request.SendWebRequest();

                                if (request.isNetworkError || request.isHttpError)
                                {
                                    Debug.LogWarning(request.error);
                                    Debug.LogWarning($"Cannot get background texture from {thumbnailPath}");
                                    Debug.LogWarning(
                                        $"Skipped generating thumbnail for {loadIndex + 1}/{jsonPaths.Count}: {meta.id} ({path})");
                                    return;
                                }

                                var coverTexture = request.downloadHandler.data.ToTexture2D();
                                if (coverTexture == null)
                                {
                                    Debug.LogWarning(request.error);
                                    Debug.LogWarning($"Cannot get background texture from {thumbnailPath}");
                                    Debug.LogWarning(
                                        $"Skipped generating thumbnail for {loadIndex + 1}/{jsonPaths.Count}: {meta.id} ({path})");
                                    return;
                                }

                                if (lowMemory)
                                {
                                    // Give up
                                    Object.Destroy(coverTexture);
                                    Debug.LogWarning($"Low memory!");
                                    Debug.LogWarning($"Skipped {loadIndex + 1}/{jsonPaths.Count} from {jsonPath}");
                                    return;
                                }

                                var croppedTexture = TextureScaler.FitCrop(coverTexture, Context.LevelThumbnailWidth,
                                                                           Context.LevelThumbnailHeight);

                                if (lowMemory)
                                {
                                    // Give up
                                    Object.Destroy(coverTexture);
                                    Object.Destroy(croppedTexture);
                                    Debug.LogWarning($"Low memory!");
                                    Debug.LogWarning($"Skipped {loadIndex + 1}/{jsonPaths.Count} from {jsonPath}");
                                    return;
                                }

                                var bytes = croppedTexture.EncodeToJPG();
                                Object.Destroy(coverTexture);
                                Object.Destroy(croppedTexture);

                                await UniTask.DelayFrame(0); // Reduce load to prevent crash

                                try
                                {
                                    File.WriteAllBytes(level.Path + CoverThumbnailFilename, bytes);
                                    Debug.Log(
                                        $"Thumbnail generated {loadIndex + 1}/{jsonPaths.Count}: {level.Id} ({thumbnailPath})");

                                    await UniTask.DelayFrame(0); // Reduce load to prevent crash
                                }
                                catch (Exception e)
                                {
                                    Debug.LogWarning(e);
                                    Debug.LogWarning($"Could not write to {level.Path + CoverThumbnailFilename}");
                                    Debug.LogWarning(
                                        $"Skipped generating thumbnail for {loadIndex + 1}/{jsonPaths.Count} from {jsonPath}");
                                }
                            }

                            timer.Time("Generate thumbnail");
                        }
                    }

                    results.Add(level);
                    OnLevelLoadProgress.Invoke(meta.id, ++loadedCount, jsonPaths.Count);
                    Debug.Log($"Loaded {loadIndex + 1}/{jsonPaths.Count}: {meta.id} ");
                    timer.Time("OnLevelLoadProgressEvent");
                }
                catch (Exception e)
                {
                    Debug.LogError(e);
                    Debug.LogError($"Unexpected error while loading from {jsonPath}");
                    Debug.LogWarning($"Skipped {loadIndex + 1}/{jsonPaths.Count} from {jsonPath}");
                }

                timer.Time();
            }

            tasks.Add(LoadLevel());
        }

        await UniTask.WhenAll(tasks);

        Application.lowMemory -= OnLowMemory;
        return(results);
    }