internal override void Update()
        {
            if (_steps == ESteps.Builder)
            {
                _simulatePatchManifestPath = EditorSimulateModeHelper.SimulateBuild();
                if (string.IsNullOrEmpty(_simulatePatchManifestPath))
                {
                    _steps = ESteps.Done;
                    Status = EOperationStatus.Failed;
                    Error  = "Simulate build failed, see the detail info on the console window.";
                    return;
                }
                _steps = ESteps.Load;
            }

            if (_steps == ESteps.Load)
            {
                if (File.Exists(_simulatePatchManifestPath) == false)
                {
                    _steps = ESteps.Done;
                    Status = EOperationStatus.Failed;
                    Error  = $"Manifest file not found : {_simulatePatchManifestPath}";
                    return;
                }

                YooLogger.Log($"Load manifest file : {_simulatePatchManifestPath}");
                string jsonContent           = FileUtility.ReadFile(_simulatePatchManifestPath);
                var    simulatePatchManifest = PatchManifest.Deserialize(jsonContent);
                _impl.SetSimulatePatchManifest(simulatePatchManifest);
                _steps = ESteps.Done;
                Status = EOperationStatus.Succeed;
            }
        }
Esempio n. 2
0
        /// <summary>
        /// 清空未被使用的缓存文件
        /// </summary>
        public void ClearUnusedCacheFiles()
        {
            string cacheFolderPath = SandboxHelper.GetCacheFolderPath();

            if (Directory.Exists(cacheFolderPath) == false)
            {
                return;
            }

            DirectoryInfo directoryInfo = new DirectoryInfo(cacheFolderPath);

            foreach (FileInfo fileInfo in directoryInfo.GetFiles())
            {
                bool used = false;
                foreach (var patchBundle in LocalPatchManifest.BundleList)
                {
                    if (fileInfo.Name == patchBundle.Hash)
                    {
                        used = true;
                        break;
                    }
                }
                if (used == false)
                {
                    YooLogger.Log($"Delete unused cache file : {fileInfo.Name}");
                    File.Delete(fileInfo.FullName);
                }
            }
        }
Esempio n. 3
0
        /// <summary>
        /// 主线程等待异步操作完毕
        /// </summary>
        public override void WaitForAsyncComplete()
        {
            _isWaitForAsyncComplete = true;

            int frame = 1000;

            while (true)
            {
                // 保险机制
                // 注意:如果需要从WEB端下载资源,可能会触发保险机制!
                frame--;
                if (frame == 0)
                {
                    if (_isShowWaitForAsyncError == false)
                    {
                        _isShowWaitForAsyncError = true;
                        YooLogger.Error($"WaitForAsyncComplete failed ! BundleName : {MainBundleInfo.BundleName} States : {Status}");
                    }
                    break;
                }

                // 驱动流程
                Update();

                // 完成后退出
                if (IsDone())
                {
                    break;
                }
            }
        }
Esempio n. 4
0
        private static AssetInfo ConvertLocationToAssetInfo(string location, System.Type assetType)
        {
            DebugCheckLocation(location);
            string     assetPath  = _locationServices.ConvertLocationToAssetPath(location);
            PatchAsset patchAsset = _bundleServices.TryGetPatchAsset(assetPath);

            if (patchAsset != null)
            {
                AssetInfo assetInfo = new AssetInfo(patchAsset, assetType);
                return(assetInfo);
            }
            else
            {
                string error;
                if (string.IsNullOrEmpty(location))
                {
                    error = $"The location is null or empty !";
                }
                else
                {
                    error = $"The location is invalid : {location}";
                }
                YooLogger.Error(error);
                AssetInfo assetInfo = new AssetInfo(error);
                return(assetInfo);
            }
        }
Esempio n. 5
0
        /// <summary>
        /// 加载子资源对象
        /// </summary>
        public static SubAssetsOperationHandle LoadSubAssetsAsync(AssetInfo assetInfo)
        {
            if (assetInfo.IsInvalid)
            {
                YooLogger.Warning(assetInfo.Error);
                CompletedProvider completedProvider = new CompletedProvider(assetInfo);
                return(completedProvider.CreateHandle <SubAssetsOperationHandle>());
            }

            ProviderBase provider = TryGetProvider(assetInfo.ProviderGUID);

            if (provider == null)
            {
                if (_simulationOnEditor)
                {
                    provider = new DatabaseSubAssetsProvider(assetInfo);
                }
                else
                {
                    provider = new BundledSubAssetsProvider(assetInfo);
                }
                provider.InitSpawnDebugInfo();
                _providers.Add(provider);
            }
            return(provider.CreateHandle <SubAssetsOperationHandle>());
        }
Esempio n. 6
0
        /// <summary>
        /// 异步卸载子场景
        /// </summary>
        public UnloadSceneOperation UnloadAsync()
        {
            // 如果句柄无效
            if (IsValid == false)
            {
                string error     = $"{nameof(SceneOperationHandle)} is invalid.";
                var    operation = new UnloadSceneOperation(error);
                OperationSystem.StartOperaiton(operation);
                return(operation);
            }

            // 如果是主场景
            if (IsMainScene())
            {
                string error = $"Cannot unload main scene. Use {nameof(YooAssets.LoadSceneAsync)} method to change the main scene !";
                YooLogger.Error(error);
                var operation = new UnloadSceneOperation(error);
                OperationSystem.StartOperaiton(operation);
                return(operation);
            }

            // 卸载子场景
            Scene sceneObject = SceneObject;

            AssetSystem.UnloadSubScene(Provider);
            {
                var operation = new UnloadSceneOperation(sceneObject);
                OperationSystem.StartOperaiton(operation);
                return(operation);
            }
        }
Esempio n. 7
0
        private static RawFileOperation GetRawFileInternal(AssetInfo assetInfo, string copyPath)
        {
            if (assetInfo.IsInvalid)
            {
                YooLogger.Warning(assetInfo.Error);
                RawFileOperation operation = new CompletedRawFileOperation(assetInfo.Error, copyPath);
                OperationSystem.StartOperaiton(operation);
                return(operation);
            }

            BundleInfo bundleInfo = _bundleServices.GetBundleInfo(assetInfo);

            if (_playMode == EPlayMode.EditorSimulateMode)
            {
                RawFileOperation operation = new EditorPlayModeRawFileOperation(bundleInfo, copyPath);
                OperationSystem.StartOperaiton(operation);
                return(operation);
            }
            else if (_playMode == EPlayMode.OfflinePlayMode)
            {
                RawFileOperation operation = new OfflinePlayModeRawFileOperation(bundleInfo, copyPath);
                OperationSystem.StartOperaiton(operation);
                return(operation);
            }
            else if (_playMode == EPlayMode.HostPlayMode)
            {
                RawFileOperation operation = new HostPlayModeRawFileOperation(bundleInfo, copyPath);
                OperationSystem.StartOperaiton(operation);
                return(operation);
            }
            else
            {
                throw new NotImplementedException();
            }
        }
Esempio n. 8
0
        private List <BundleInfo> GetDownloadListByPaths(AssetInfo[] assetInfos)
        {
            // 获取资源对象的资源包和所有依赖资源包
            List <PatchBundle> checkList = new List <PatchBundle>();

            foreach (var assetInfo in assetInfos)
            {
                if (assetInfo.IsInvalid)
                {
                    YooLogger.Warning(assetInfo.Error);
                    continue;
                }

                string mainBundleName = LocalPatchManifest.GetBundleName(assetInfo.AssetPath);
                if (LocalPatchManifest.Bundles.TryGetValue(mainBundleName, out PatchBundle mainBundle))
                {
                    if (checkList.Contains(mainBundle) == false)
                    {
                        checkList.Add(mainBundle);
                    }
                }

                string[] dependBundleNames = LocalPatchManifest.GetAllDependencies(assetInfo.AssetPath);
                foreach (var dependBundleName in dependBundleNames)
                {
                    if (LocalPatchManifest.Bundles.TryGetValue(dependBundleName, out PatchBundle dependBundle))
                    {
                        if (checkList.Contains(dependBundle) == false)
                        {
                            checkList.Add(dependBundle);
                        }
                    }
                }
            }

            List <PatchBundle> downloadList = new List <PatchBundle>(1000);

            foreach (var patchBundle in checkList)
            {
                // 忽略缓存文件
                if (DownloadSystem.ContainsVerifyFile(patchBundle.Hash))
                {
                    continue;
                }

                // 忽略APP资源
                // 注意:如果是APP资源并且哈希值相同,则不需要下载
                if (AppPatchManifest.Bundles.TryGetValue(patchBundle.BundleName, out PatchBundle appPatchBundle))
                {
                    if (appPatchBundle.IsBuildin && appPatchBundle.Hash == patchBundle.Hash)
                    {
                        continue;
                    }
                }

                downloadList.Add(patchBundle);
            }

            return(ConvertToDownloadList(downloadList));
        }
Esempio n. 9
0
        /// <summary>
        /// 开始下载资源文件
        /// 注意:只有第一次请求的参数才是有效的
        /// </summary>
        public static DownloaderBase BeginDownload(BundleInfo bundleInfo, int failedTryAgain, int timeout = 60)
        {
            // 查询存在的下载器
            if (_downloaderDic.TryGetValue(bundleInfo.Hash, out var downloader))
            {
                return(downloader);
            }

            // 如果资源已经缓存
            if (ContainsVerifyFile(bundleInfo.Hash))
            {
                var tempDownloader = new TempDownloader(bundleInfo);
                return(tempDownloader);
            }

            // 创建新的下载器
            {
                YooLogger.Log($"Beginning to download file : {bundleInfo.BundleName} URL : {bundleInfo.RemoteMainURL}");
                FileUtility.CreateFileDirectory(bundleInfo.GetCacheLoadPath());
                DownloaderBase newDownloader;
                if (bundleInfo.SizeBytes >= _breakpointResumeFileSize)
                {
                    newDownloader = new HttpDownloader(bundleInfo);
                }
                else
                {
                    newDownloader = new FileDownloader(bundleInfo);
                }
                newDownloader.SendRequest(failedTryAgain, timeout);
                _downloaderDic.Add(bundleInfo.Hash, newDownloader);
                return(newDownloader);
            }
        }
Esempio n. 10
0
 /// <summary>
 /// 缓存验证过的文件
 /// </summary>
 public static void CacheVerifyFile(string hash, string bundleName)
 {
     if (_cachedHashList.ContainsKey(hash) == false)
     {
         YooLogger.Log($"Cache verify file : {bundleName} Hash : {hash}");
         _cachedHashList.Add(hash, bundleName);
     }
 }
Esempio n. 11
0
        /// <summary>
        /// 加载沙盒内的补丁清单
        /// 注意:在加载本地补丁清单之前,已经验证过文件的哈希值
        /// </summary>
        private void LoadSandboxPatchManifest(int updateResourceVersion)
        {
            YooLogger.Log("Load sandbox patch manifest file.");
            string filePath             = PathHelper.MakePersistentLoadPath(YooAssetSettingsData.GetPatchManifestFileName(updateResourceVersion));
            string jsonData             = File.ReadAllText(filePath);
            var    sandboxPatchManifest = PatchManifest.Deserialize(jsonData);

            _impl.SetLocalPatchManifest(sandboxPatchManifest);
        }
Esempio n. 12
0
        /// <summary>
        /// 获取调试信息
        /// </summary>
        internal static void GetDebugReport(DebugReport report)
        {
            if (report == null)
            {
                YooLogger.Error($"{nameof(DebugReport)} is null");
            }

            AssetSystem.GetDebugReport(report);
        }
Esempio n. 13
0
        public override void Update()
        {
#if UNITY_EDITOR
            if (IsDone)
            {
                return;
            }

            if (Status == EStatus.None)
            {
                Status = EStatus.Loading;
            }

            // 1. 加载资源对象
            if (Status == EStatus.Loading)
            {
                LoadSceneParameters loadSceneParameters = new LoadSceneParameters();
                loadSceneParameters.loadSceneMode = SceneMode;
                _asyncOp = UnityEditor.SceneManagement.EditorSceneManager.LoadSceneAsyncInPlayMode(MainAssetInfo.AssetPath, loadSceneParameters);
                if (_asyncOp != null)
                {
                    _asyncOp.allowSceneActivation = true;
                    _asyncOp.priority             = _priority;
                    Status = EStatus.Checking;
                }
                else
                {
                    Status    = EStatus.Fail;
                    LastError = $"Failed to load scene : {MainAssetInfo.AssetPath}";
                    YooLogger.Error(LastError);
                    InvokeCompletion();
                }
            }

            // 2. 检测加载结果
            if (Status == EStatus.Checking)
            {
                if (_asyncOp.isDone)
                {
                    SceneObject = SceneManager.GetSceneAt(SceneManager.sceneCount - 1);
                    if (SceneObject.IsValid() && _activateOnLoad)
                    {
                        SceneManager.SetActiveScene(SceneObject);
                    }

                    Status = SceneObject.IsValid() ? EStatus.Success : EStatus.Fail;
                    if (Status == EStatus.Fail)
                    {
                        LastError = $"The loaded scene is invalid : {MainAssetInfo.AssetPath}";
                        YooLogger.Error(LastError);
                    }
                    InvokeCompletion();
                }
            }
#endif
        }
Esempio n. 14
0
 /// <summary>
 /// 销毁所有下载器
 /// </summary>
 public static void DestroyAll()
 {
     foreach (var valuePair in _downloaderDic)
     {
         var downloader = valuePair.Value;
         downloader.Abort();
     }
     _downloaderDic.Clear();
     YooLogger.Log("DownloadSystem destroy all !");
 }
Esempio n. 15
0
        /// <summary>
        /// 更新缓存文件
        /// </summary>
        public static void UpdateCache()
        {
            YooLogger.Log($"Update cache data to disk : {Application.version}");
            CacheData cacheData = new CacheData();

            cacheData.CacheAppVersion = Application.version;
            string filePath = GetCacheDataFilePath();
            string jsonData = JsonUtility.ToJson(cacheData);

            FileUtility.CreateFile(filePath, jsonData);
        }
        internal override void Update()
        {
            if (_steps == ESteps.None || _steps == ESteps.Done)
            {
                return;
            }

            if (_steps == ESteps.InitCache)
            {
                // 每次启动时比对APP版本号是否一致
                CacheData cacheData = CacheData.LoadCache();
                if (cacheData.CacheAppVersion != Application.version)
                {
                    YooLogger.Warning($"Cache is dirty ! Cache application version is {cacheData.CacheAppVersion}, Current application version is {Application.version}");

                    // 注意:在覆盖安装的时候,会保留APP沙盒目录,可以选择清空缓存目录
                    if (_impl.ClearCacheWhenDirty)
                    {
                        YooLogger.Warning("Clear cache files.");
                        SandboxHelper.DeleteCacheFolder();
                    }

                    // 更新缓存文件
                    CacheData.UpdateCache();
                }
                _steps = ESteps.Update;
            }

            if (_steps == ESteps.Update)
            {
                _appManifestLoader.Update();
                Progress = _appManifestLoader.Progress();
                if (_appManifestLoader.IsDone() == false)
                {
                    return;
                }

                if (_appManifestLoader.Result == null)
                {
                    _steps = ESteps.Done;
                    Status = EOperationStatus.Failed;
                    Error  = _appManifestLoader.Error;
                }
                else
                {
                    _steps = ESteps.Done;
                    Status = EOperationStatus.Succeed;
                    _impl.SetAppPatchManifest(_appManifestLoader.Result);
                    _impl.SetLocalPatchManifest(_appManifestLoader.Result);
                }
            }
        }
Esempio n. 17
0
        /// <summary>
        /// 等待异步执行完毕
        /// </summary>
        public void WaitForAsyncComplete()
        {
            IsWaitForAsyncComplete = true;

            // 注意:主动轮询更新完成同步加载
            Update();

            // 验证结果
            if (IsDone == false)
            {
                YooLogger.Warning($"WaitForAsyncComplete failed to loading : {MainAssetInfo.AssetPath}");
            }
        }
 /// <summary>
 /// 加载配置文件
 /// </summary>
 private static void LoadSettingData()
 {
     _setting = Resources.Load <YooAssetSettings>("YooAssetSettings");
     if (_setting == null)
     {
         YooLogger.Log("YooAsset use default settings.");
         _setting = ScriptableObject.CreateInstance <YooAssetSettings>();
     }
     else
     {
         YooLogger.Log("YooAsset use user settings.");
     }
 }
Esempio n. 19
0
 /// <summary>
 /// 解析远端请求的补丁清单
 /// </summary>
 private bool ParseRemotePatchManifest(string content)
 {
     try
     {
         _remotePatchManifest = PatchManifest.Deserialize(content);
         return(true);
     }
     catch (Exception e)
     {
         YooLogger.Warning(e.ToString());
         return(false);
     }
 }
Esempio n. 20
0
        internal override void Update()
        {
            if (_steps == ESteps.None || _steps == ESteps.Done)
            {
                return;
            }

            if (_steps == ESteps.LoadWebManifest)
            {
                string webURL = GetPatchManifestRequestURL(YooAssetSettingsData.GetPatchManifestFileName(_resourceVersion));
                YooLogger.Log($"Beginning to request patch manifest : {webURL}");
                _downloader = new UnityWebDataRequester();
                _downloader.SendRequest(webURL, _timeout);
                _steps = ESteps.CheckWebManifest;
            }

            if (_steps == ESteps.CheckWebManifest)
            {
                Progress = _downloader.Progress();
                if (_downloader.IsDone() == false)
                {
                    return;
                }

                // Check error
                if (_downloader.HasError())
                {
                    _steps = ESteps.Done;
                    Status = EOperationStatus.Failed;
                    Error  = _downloader.GetError();
                }
                else
                {
                    // 解析补丁清单
                    if (ParseRemotePatchManifest(_downloader.GetText()))
                    {
                        _steps = ESteps.Done;
                        Status = EOperationStatus.Succeed;
                    }
                    else
                    {
                        _steps = ESteps.Done;
                        Status = EOperationStatus.Failed;
                        Error  = $"URL : {_downloader.URL} Error : remote patch manifest content is invalid";
                    }
                }
                _downloader.Dispose();
            }
        }
Esempio n. 21
0
        /// <summary>
        /// 释放操作句柄
        /// </summary>
        public void ReleaseHandle(OperationHandleBase handle)
        {
            if (RefCount <= 0)
            {
                YooLogger.Warning("Asset provider reference count is already zero. There may be resource leaks !");
            }

            if (_handles.Remove(handle) == false)
            {
                throw new System.Exception("Should never get here !");
            }

            // 引用计数减少
            RefCount--;
        }
Esempio n. 22
0
 /// <summary>
 /// 创建包裹下载器
 /// </summary>
 public override PackageDownloaderOperation CreatePackageDownloader(int downloadingMaxNumber, int failedTryAgain)
 {
     if (Status == EOperationStatus.Succeed)
     {
         List <BundleInfo> downloadList = GetDownloadList();
         var operation = new PackageDownloaderOperation(downloadList, downloadingMaxNumber, failedTryAgain);
         return(operation);
     }
     else
     {
         YooLogger.Error($"{nameof(UpdatePackageOperation)} status is failed !");
         var operation = new PackageDownloaderOperation(null, downloadingMaxNumber, failedTryAgain);
         return(operation);
     }
 }
Esempio n. 23
0
 /// <summary>
 /// 获取文件的CRC32
 /// </summary>
 public static string FileCRC32(string filePath)
 {
     try
     {
         using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
         {
             return(StreamCRC32(fs));
         }
     }
     catch (Exception e)
     {
         YooLogger.Exception(e);
         return(string.Empty);
     }
 }
Esempio n. 24
0
        internal override void Update()
        {
            if (_steps == ESteps.None || _steps == ESteps.Done)
            {
                return;
            }

            if (_steps == ESteps.LoadStaticVersion)
            {
                string webURL = GetStaticVersionRequestURL(YooAssetSettings.VersionFileName);
                YooLogger.Log($"Beginning to request static version : {webURL}");
                _downloader = new UnityWebDataRequester();
                _downloader.SendRequest(webURL, _timeout);
                _steps = ESteps.CheckStaticVersion;
            }

            if (_steps == ESteps.CheckStaticVersion)
            {
                Progress = _downloader.Progress();
                if (_downloader.IsDone() == false)
                {
                    return;
                }

                if (_downloader.HasError())
                {
                    _steps = ESteps.Done;
                    Status = EOperationStatus.Failed;
                    Error  = _downloader.GetError();
                }
                else
                {
                    if (int.TryParse(_downloader.GetText(), out int value))
                    {
                        ResourceVersion = value;
                        _steps          = ESteps.Done;
                        Status          = EOperationStatus.Succeed;
                    }
                    else
                    {
                        _steps = ESteps.Done;
                        Status = EOperationStatus.Failed;
                        Error  = $"URL : {_downloader.URL} Error : static version content is invalid.";
                    }
                }
                _downloader.Dispose();
            }
        }
Esempio n. 25
0
        /// <summary>
        /// 激活场景
        /// </summary>
        public bool ActivateScene()
        {
            if (IsValid == false)
            {
                return(false);
            }

            if (SceneObject.IsValid() && SceneObject.isLoaded)
            {
                return(SceneManager.SetActiveScene(SceneObject));
            }
            else
            {
                YooLogger.Warning($"Scene is invalid or not loaded : {SceneObject.name}");
                return(false);
            }
        }
Esempio n. 26
0
        /// <summary>
        /// 解析并保存远端请求的补丁清单
        /// </summary>
        private bool ParseAndSaveRemotePatchManifest(int updateResourceVersion, string content)
        {
            try
            {
                var remotePatchManifest = PatchManifest.Deserialize(content);
                _impl.SetLocalPatchManifest(remotePatchManifest);

                YooLogger.Log("Save remote patch manifest file.");
                string savePath = PathHelper.MakePersistentLoadPath(YooAssetSettingsData.GetPatchManifestFileName(updateResourceVersion));
                PatchManifest.Serialize(savePath, remotePatchManifest);
                return(true);
            }
            catch (Exception e)
            {
                YooLogger.Error(e.ToString());
                return(false);
            }
        }
Esempio n. 27
0
        /// <summary>
        /// 获取子资源对象
        /// </summary>
        /// <typeparam name="TObject">子资源对象类型</typeparam>
        /// <param name="assetName">子资源对象名称</param>
        public TObject GetSubAssetObject <TObject>(string assetName) where TObject : UnityEngine.Object
        {
            if (IsValid == false)
            {
                return(null);
            }

            foreach (var assetObject in Provider.AllAssetObjects)
            {
                if (assetObject.name == assetName)
                {
                    return(assetObject as TObject);
                }
            }

            YooLogger.Warning($"Not found sub asset object : {assetName}");
            return(null);
        }
Esempio n. 28
0
        /// <summary>
        /// 加载场景
        /// </summary>
        public static SceneOperationHandle LoadSceneAsync(AssetInfo assetInfo, LoadSceneMode sceneMode, bool activateOnLoad, int priority)
        {
            if (assetInfo.IsInvalid)
            {
                YooLogger.Warning(assetInfo.Error);
                CompletedProvider completedProvider = new CompletedProvider(assetInfo);
                return(completedProvider.CreateHandle <SceneOperationHandle>());
            }

            // 注意:场景句柄永远保持唯一
            string providerGUID = assetInfo.ProviderGUID;

            if (_sceneHandles.ContainsKey(providerGUID))
            {
                return(_sceneHandles[providerGUID]);
            }

            // 如果加载的是主场景,则卸载所有缓存的场景
            if (sceneMode == LoadSceneMode.Single)
            {
                UnloadAllScene();
            }

            ProviderBase provider = TryGetProvider(providerGUID);

            if (provider == null)
            {
                if (_simulationOnEditor)
                {
                    provider = new DatabaseSceneProvider(assetInfo, sceneMode, activateOnLoad, priority);
                }
                else
                {
                    provider = new BundledSceneProvider(assetInfo, sceneMode, activateOnLoad, priority);
                }
                provider.InitSpawnDebugInfo();
                _providers.Add(provider);
            }

            var handle = provider.CreateHandle <SceneOperationHandle>();

            _sceneHandles.Add(providerGUID, handle);
            return(handle);
        }
Esempio n. 29
0
        /// <summary>
        /// 是否需要从远端更新下载
        /// </summary>
        /// <param name="location">资源的定位地址</param>
        public static bool IsNeedDownloadFromRemote(AssetInfo assetInfo)
        {
            DebugCheckInitialize();
            if (assetInfo.IsInvalid)
            {
                YooLogger.Warning(assetInfo.Error);
                return(false);
            }

            BundleInfo bundleInfo = _bundleServices.GetBundleInfo(assetInfo);

            if (bundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromRemote)
            {
                return(true);
            }
            else
            {
                return(false);
            }
        }
Esempio n. 30
0
        private static void DebugCheckLocation(string location)
        {
            if (string.IsNullOrEmpty(location) == false)
            {
                // 检查路径末尾是否有空格
                int index = location.LastIndexOf(" ");
                if (index != -1)
                {
                    if (location.Length == index + 1)
                    {
                        YooLogger.Warning($"Found blank character in location : \"{location}\"");
                    }
                }

                if (location.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0)
                {
                    YooLogger.Warning($"Found illegal character in location : \"{location}\"");
                }
            }
        }