From e66853fd465af2624fe74fbc24f46508ece70d02 Mon Sep 17 00:00:00 2001 From: hevinci Date: Tue, 1 Mar 2022 10:44:12 +0800 Subject: [PATCH] Initial commit --- Assets/YooAsset.meta | 8 + Assets/YooAsset/Editor.meta | 8 + .../YooAsset/Editor/AssetBundleBrowser.meta | 8 + .../AssetBundleBrowserWindow.cs | 8 + .../AssetBundleBrowserWindow.cs.meta | 11 + .../YooAsset/Editor/AssetBundleBuilder.meta | 8 + .../AssetBundleBuilder/AssetBundleBuilder.cs | 197 ++++ .../AssetBundleBuilder.cs.meta | 11 + .../AssetBundleBuilderHelper.cs | 140 +++ .../AssetBundleBuilderHelper.cs.meta | 11 + .../AssetBundleBuilderTools.cs | 101 ++ .../AssetBundleBuilderTools.cs.meta | 11 + .../AssetBundleBuilderWindow.cs | 158 ++++ .../AssetBundleBuilderWindow.cs.meta | 11 + .../AssetBundleBuilder/BuildAssetInfo.cs | 117 +++ .../AssetBundleBuilder/BuildAssetInfo.cs.meta | 11 + .../AssetBundleBuilder/BuildBundleInfo.cs | 148 +++ .../BuildBundleInfo.cs.meta | 11 + .../AssetBundleBuilder/BuildSystem.meta | 8 + .../BuildSystem/BuildContext.cs | 50 + .../BuildSystem/BuildContext.cs.meta | 11 + .../BuildSystem/BuildRunner.cs | 41 + .../BuildSystem/BuildRunner.cs.meta | 11 + .../BuildSystem/IBuildTask.cs | 8 + .../BuildSystem/IBuildTask.cs.meta | 11 + .../BuildSystem/IContextObject.cs | 7 + .../BuildSystem/IContextObject.cs.meta | 11 + .../Editor/AssetBundleBuilder/BuildTasks.meta | 8 + .../BuildTasks/TaskBuilding.cs | 194 ++++ .../BuildTasks/TaskBuilding.cs.meta | 11 + .../BuildTasks/TaskCopyBuildinFiles.cs | 64 ++ .../BuildTasks/TaskCopyBuildinFiles.cs.meta | 11 + .../BuildTasks/TaskCreatePatchManifest.cs | 155 ++++ .../TaskCreatePatchManifest.cs.meta | 11 + .../BuildTasks/TaskCreatePatchPackage.cs | 83 ++ .../BuildTasks/TaskCreatePatchPackage.cs.meta | 11 + .../BuildTasks/TaskCreateReadme.cs | 205 ++++ .../BuildTasks/TaskCreateReadme.cs.meta | 11 + .../BuildTasks/TaskEncryption.cs | 94 ++ .../BuildTasks/TaskEncryption.cs.meta | 11 + .../BuildTasks/TaskGetBuildMap.cs | 331 +++++++ .../BuildTasks/TaskGetBuildMap.cs.meta | 11 + .../BuildTasks/TaskPrepare.cs | 72 ++ .../BuildTasks/TaskPrepare.cs.meta | 11 + .../AssetBundleBuilder/ECompressOption.cs | 13 + .../ECompressOption.cs.meta | 11 + .../AssetBundleBuilder/IAssetEncrypter.cs | 18 + .../IAssetEncrypter.cs.meta | 11 + .../AssetBundleBuilder/IAssetRedundancy.cs | 11 + .../IAssetRedundancy.cs.meta | 11 + .../YooAsset/Editor/AssetBundleCollector.meta | 8 + .../AssetBundleCollectorSetting.cs | 69 ++ .../AssetBundleCollectorSetting.cs.meta | 11 + .../AssetBundleCollectorSettingData.cs | 478 ++++++++++ .../AssetBundleCollectorSettingData.cs.meta | 11 + .../AssetBundleCollectorWindow.cs | 243 +++++ .../AssetBundleCollectorWindow.cs.meta | 11 + .../AssetBundleCollector/AssetCollectInfo.cs | 30 + .../AssetCollectInfo.cs.meta | 11 + .../CollectorConfigImporter.cs | 100 ++ .../CollectorConfigImporter.cs.meta | 11 + .../AssetBundleCollector/DefaultFilterRule.cs | 53 ++ .../DefaultFilterRule.cs.meta | 11 + .../AssetBundleCollector/DefaultPackRule.cs | 56 ++ .../DefaultPackRule.cs.meta | 11 + .../AssetBundleCollector/IFilterRule.cs | 16 + .../AssetBundleCollector/IFilterRule.cs.meta | 11 + .../Editor/AssetBundleCollector/IPackRule.cs | 14 + .../AssetBundleCollector/IPackRule.cs.meta | 11 + Assets/YooAsset/Editor/EditorDefine.cs | 53 ++ Assets/YooAsset/Editor/EditorDefine.cs.meta | 11 + Assets/YooAsset/Editor/EditorTools.cs | 878 ++++++++++++++++++ Assets/YooAsset/Editor/EditorTools.cs.meta | 11 + .../Editor/ShaderVariantCollector.meta | 8 + .../ShaderVariantCollectionHelper.cs | 112 +++ .../ShaderVariantCollectionHelper.cs.meta | 11 + .../ShaderVariantCollector.cs | 213 +++++ .../ShaderVariantCollector.cs.meta | 11 + .../ShaderVariantCollectorWindow.cs | 58 ++ .../ShaderVariantCollectorWindow.cs.meta | 11 + Assets/YooAsset/Editor/YooAsset.Editor.asmdef | 18 + .../Editor/YooAsset.Editor.asmdef.meta | 7 + LICENSE => Assets/YooAsset/LICENSE | 3 +- Assets/YooAsset/LICENSE.meta | 7 + Assets/YooAsset/README.md | 2 + Assets/YooAsset/README.md.meta | 7 + Assets/YooAsset/Runtime.meta | 8 + Assets/YooAsset/Runtime/AssetSystem.meta | 8 + .../Runtime/AssetSystem/AssetBundleInfo.cs | 149 +++ .../AssetSystem/AssetBundleInfo.cs.meta | 11 + .../AssetSystem/AssetOperationHandle.cs | 185 ++++ .../AssetSystem/AssetOperationHandle.cs.meta | 11 + .../Runtime/AssetSystem/AssetPathHelper.cs | 123 +++ .../AssetSystem/AssetPathHelper.cs.meta | 11 + .../Runtime/AssetSystem/AssetSystem.cs | 326 +++++++ .../Runtime/AssetSystem/AssetSystem.cs.meta | 11 + .../Runtime/AssetSystem/EDecryptMethod.cs | 19 + .../AssetSystem/EDecryptMethod.cs.meta | 11 + .../Runtime/AssetSystem/IBundleServices.cs | 21 + .../AssetSystem/IBundleServices.cs.meta | 11 + .../Runtime/AssetSystem/IDecryptServices.cs | 18 + .../AssetSystem/IDecryptServices.cs.meta | 11 + .../Runtime/AssetSystem/Instance.meta | 8 + .../AssetSystem/Instance/IAssetInstance.cs | 7 + .../Instance/IAssetInstance.cs.meta | 11 + .../AssetSystem/Instance/IAssetParam.cs | 7 + .../AssetSystem/Instance/IAssetParam.cs.meta | 11 + .../AssetSystem/Instance/SceneInstance.cs | 63 ++ .../Instance/SceneInstance.cs.meta | 11 + .../YooAsset/Runtime/AssetSystem/Loader.meta | 8 + .../AssetSystem/Loader/BundleFileLoader.cs | 319 +++++++ .../Loader/BundleFileLoader.cs.meta | 11 + .../AssetSystem/Loader/DependBundleGrouper.cs | 82 ++ .../Loader/DependBundleGrouper.cs.meta | 11 + .../AssetSystem/Loader/ELoaderStates.cs | 17 + .../AssetSystem/Loader/ELoaderStates.cs.meta | 11 + .../Runtime/AssetSystem/Provider.meta | 8 + .../AssetSystem/Provider/AssetProviderBase.cs | 138 +++ .../Provider/AssetProviderBase.cs.meta | 11 + .../Provider/BundledAssetProvider.cs | 105 +++ .../Provider/BundledAssetProvider.cs.meta | 11 + .../AssetSystem/Provider/BundledProvider.cs | 51 + .../Provider/BundledProvider.cs.meta | 11 + .../Provider/BundledSceneProvider.cs | 109 +++ .../Provider/BundledSceneProvider.cs.meta | 11 + .../Provider/BundledSubAssetsProvider.cs | 105 +++ .../Provider/BundledSubAssetsProvider.cs.meta | 11 + .../Provider/DatabaseAssetProvider.cs | 68 ++ .../Provider/DatabaseAssetProvider.cs.meta | 11 + .../Provider/DatabaseSceneProvider.cs | 94 ++ .../Provider/DatabaseSceneProvider.cs.meta | 11 + .../Provider/DatabaseSubAssetsProvider.cs | 75 ++ .../DatabaseSubAssetsProvider.cs.meta | 11 + .../AssetSystem/Provider/EAssetStates.cs | 16 + .../AssetSystem/Provider/EAssetStates.cs.meta | 11 + .../AssetSystem/Provider/IAssetProvider.cs | 114 +++ .../Provider/IAssetProvider.cs.meta | 11 + Assets/YooAsset/Runtime/Logger.meta | 8 + Assets/YooAsset/Runtime/Logger/DebugSummy.cs | 80 ++ .../Runtime/Logger/DebugSummy.cs.meta | 11 + Assets/YooAsset/Runtime/Logger/Logger.cs | 40 + Assets/YooAsset/Runtime/Logger/Logger.cs.meta | 11 + Assets/YooAsset/Runtime/Operation.meta | 8 + .../Runtime/Operation/AsyncOperationBase.cs | 71 ++ .../Operation/AsyncOperationBase.cs.meta | 11 + .../Runtime/Operation/EOperationStatus.cs | 10 + .../Operation/EOperationStatus.cs.meta | 11 + .../Runtime/Operation/OperationUpdater.cs | 29 + .../Operation/OperationUpdater.cs.meta | 11 + Assets/YooAsset/Runtime/PatchSystem.meta | 8 + .../Runtime/PatchSystem/Download.meta | 8 + .../PatchSystem/Download/DownloadSystem.cs | 146 +++ .../Download/DownloadSystem.cs.meta | 11 + .../PatchSystem/Download/EDownloaderStates.cs | 14 + .../Download/EDownloaderStates.cs.meta | 11 + .../PatchSystem/Download/FileDownloader.cs | 234 +++++ .../Download/FileDownloader.cs.meta | 11 + .../PatchSystem/Download/HttpDownloader.cs | 252 +++++ .../Download/HttpDownloader.cs.meta | 11 + .../PatchSystem/Download/PatchDownloader.cs | 175 ++++ .../Download/PatchDownloader.cs.meta | 11 + .../PatchSystem/Download/ThreadSyncContext.cs | 38 + .../Download/ThreadSyncContext.cs.meta | 11 + .../PatchSystem/Download/UnityWebRequester.cs | 113 +++ .../Download/UnityWebRequester.cs.meta | 11 + .../Runtime/PatchSystem/Operations.meta | 8 + .../Operations/InitializationOperation.cs | 197 ++++ .../InitializationOperation.cs.meta | 11 + .../Operations/UpdateManifestOperation.cs | 308 ++++++ .../UpdateManifestOperation.cs.meta | 11 + .../Runtime/PatchSystem/PatchAsset.cs | 23 + .../Runtime/PatchSystem/PatchAsset.cs.meta | 11 + .../Runtime/PatchSystem/PatchBundle.cs | 129 +++ .../Runtime/PatchSystem/PatchBundle.cs.meta | 11 + .../Runtime/PatchSystem/PatchCache.cs | 55 ++ .../Runtime/PatchSystem/PatchCache.cs.meta | 11 + .../Runtime/PatchSystem/PatchHelper.cs | 101 ++ .../Runtime/PatchSystem/PatchHelper.cs.meta | 11 + .../Runtime/PatchSystem/PatchManifest.cs | 162 ++++ .../Runtime/PatchSystem/PatchManifest.cs.meta | 11 + .../Runtime/PatchSystem/PlayMode.meta | 8 + .../PlayMode/EditorPlayModeImpl.cs | 52 ++ .../PlayMode/EditorPlayModeImpl.cs.meta | 11 + .../PatchSystem/PlayMode/HostPlayModeImpl.cs | 252 +++++ .../PlayMode/HostPlayModeImpl.cs.meta | 11 + .../PlayMode/OfflinePlayModeImpl.cs | 70 ++ .../PlayMode/OfflinePlayModeImpl.cs.meta | 11 + Assets/YooAsset/Runtime/ResourceSetting.meta | 8 + .../ResourceSetting/ResourceSetting.cs | 38 + .../ResourceSetting/ResourceSetting.cs.meta | 11 + .../ResourceSetting/ResourceSettingData.cs | 35 + .../ResourceSettingData.cs.meta | 11 + Assets/YooAsset/Runtime/Utility.meta | 8 + .../Runtime/Utility/AssemblyUtility.cs | 140 +++ .../Runtime/Utility/AssemblyUtility.cs.meta | 11 + .../YooAsset/Runtime/Utility/FileUtility.cs | 70 ++ .../Runtime/Utility/FileUtility.cs.meta | 11 + .../YooAsset/Runtime/Utility/HashUtility.cs | 169 ++++ .../Runtime/Utility/HashUtility.cs.meta | 11 + Assets/YooAsset/Runtime/Utility/Misc.meta | 8 + .../Runtime/Utility/Misc/BitMask32.cs | 69 ++ .../Runtime/Utility/Misc/BitMask32.cs.meta | 11 + .../Runtime/Utility/Misc/BitMask64.cs | 69 ++ .../Runtime/Utility/Misc/BitMask64.cs.meta | 11 + .../Runtime/Utility/Misc/CRC32Algorithm.cs | 242 +++++ .../Utility/Misc/CRC32Algorithm.cs.meta | 11 + .../YooAsset/Runtime/Utility/StringUtility.cs | 107 +++ .../Runtime/Utility/StringUtility.cs.meta | 11 + Assets/YooAsset/Runtime/YooAsset.asmdef | 14 + Assets/YooAsset/Runtime/YooAsset.asmdef.meta | 7 + Assets/YooAsset/Runtime/YooAssets.cs | 538 +++++++++++ Assets/YooAsset/Runtime/YooAssets.cs.meta | 11 + Packages/manifest.json | 49 + Packages/packages-lock.json | 435 +++++++++ ProjectSettings/AudioManager.asset | 19 + ProjectSettings/AutoStreamingSettings.asset | 21 + ProjectSettings/ClusterInputManager.asset | 6 + ProjectSettings/DynamicsManager.asset | 36 + ProjectSettings/EditorBuildSettings.asset | 11 + ProjectSettings/EditorSettings.asset | 36 + ProjectSettings/GraphicsSettings.asset | 63 ++ ProjectSettings/InputManager.asset | 487 ++++++++++ ProjectSettings/NavMeshAreas.asset | 91 ++ ProjectSettings/NetworkManager.asset | 8 + ProjectSettings/PackageManagerSettings.asset | 43 + ProjectSettings/Physics2DSettings.asset | 56 ++ ProjectSettings/PresetManager.asset | 7 + ProjectSettings/ProjectSettings.asset | 669 +++++++++++++ ProjectSettings/ProjectVersion.txt | 2 + ProjectSettings/QualitySettings.asset | 236 +++++ ProjectSettings/TagManager.asset | 43 + ProjectSettings/TimeManager.asset | 9 + ProjectSettings/UnityConnectSettings.asset | 37 + ProjectSettings/VFXManager.asset | 12 + ProjectSettings/VersionControlSettings.asset | 8 + ProjectSettings/XRSettings.asset | 10 + README.md | 1 - 237 files changed, 14462 insertions(+), 2 deletions(-) create mode 100644 Assets/YooAsset.meta create mode 100644 Assets/YooAsset/Editor.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBrowser.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs.meta create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs create mode 100644 Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs.meta create mode 100644 Assets/YooAsset/Editor/EditorDefine.cs create mode 100644 Assets/YooAsset/Editor/EditorDefine.cs.meta create mode 100644 Assets/YooAsset/Editor/EditorTools.cs create mode 100644 Assets/YooAsset/Editor/EditorTools.cs.meta create mode 100644 Assets/YooAsset/Editor/ShaderVariantCollector.meta create mode 100644 Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectionHelper.cs create mode 100644 Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectionHelper.cs.meta create mode 100644 Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollector.cs create mode 100644 Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollector.cs.meta create mode 100644 Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectorWindow.cs create mode 100644 Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectorWindow.cs.meta create mode 100644 Assets/YooAsset/Editor/YooAsset.Editor.asmdef create mode 100644 Assets/YooAsset/Editor/YooAsset.Editor.asmdef.meta rename LICENSE => Assets/YooAsset/LICENSE (93%) create mode 100644 Assets/YooAsset/LICENSE.meta create mode 100644 Assets/YooAsset/README.md create mode 100644 Assets/YooAsset/README.md.meta create mode 100644 Assets/YooAsset/Runtime.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/AssetBundleInfo.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/AssetBundleInfo.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/AssetOperationHandle.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/AssetOperationHandle.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/AssetPathHelper.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/AssetPathHelper.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/EDecryptMethod.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/EDecryptMethod.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/IBundleServices.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/IBundleServices.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/IDecryptServices.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/IDecryptServices.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Instance.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetInstance.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetInstance.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetParam.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetParam.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Instance/SceneInstance.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Instance/SceneInstance.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Loader.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Loader/BundleFileLoader.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Loader/BundleFileLoader.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Loader/DependBundleGrouper.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Loader/DependBundleGrouper.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Loader/ELoaderStates.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Loader/ELoaderStates.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/AssetProviderBase.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/AssetProviderBase.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/BundledAssetProvider.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/BundledAssetProvider.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/BundledProvider.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/BundledProvider.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSceneProvider.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSceneProvider.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSubAssetsProvider.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSubAssetsProvider.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseAssetProvider.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseAssetProvider.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSceneProvider.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSceneProvider.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSubAssetsProvider.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSubAssetsProvider.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/EAssetStates.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/EAssetStates.cs.meta create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/IAssetProvider.cs create mode 100644 Assets/YooAsset/Runtime/AssetSystem/Provider/IAssetProvider.cs.meta create mode 100644 Assets/YooAsset/Runtime/Logger.meta create mode 100644 Assets/YooAsset/Runtime/Logger/DebugSummy.cs create mode 100644 Assets/YooAsset/Runtime/Logger/DebugSummy.cs.meta create mode 100644 Assets/YooAsset/Runtime/Logger/Logger.cs create mode 100644 Assets/YooAsset/Runtime/Logger/Logger.cs.meta create mode 100644 Assets/YooAsset/Runtime/Operation.meta create mode 100644 Assets/YooAsset/Runtime/Operation/AsyncOperationBase.cs create mode 100644 Assets/YooAsset/Runtime/Operation/AsyncOperationBase.cs.meta create mode 100644 Assets/YooAsset/Runtime/Operation/EOperationStatus.cs create mode 100644 Assets/YooAsset/Runtime/Operation/EOperationStatus.cs.meta create mode 100644 Assets/YooAsset/Runtime/Operation/OperationUpdater.cs create mode 100644 Assets/YooAsset/Runtime/Operation/OperationUpdater.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/EDownloaderStates.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/EDownloaderStates.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/PatchDownloader.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/PatchDownloader.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebRequester.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebRequester.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Operations.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Operations/InitializationOperation.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Operations/InitializationOperation.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PatchAsset.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PatchAsset.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PatchBundle.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PatchBundle.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PatchCache.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PatchCache.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PatchHelper.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PatchHelper.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PatchManifest.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PatchManifest.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PlayMode.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PlayMode/EditorPlayModeImpl.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PlayMode/EditorPlayModeImpl.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PlayMode/HostPlayModeImpl.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PlayMode/HostPlayModeImpl.cs.meta create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PlayMode/OfflinePlayModeImpl.cs create mode 100644 Assets/YooAsset/Runtime/PatchSystem/PlayMode/OfflinePlayModeImpl.cs.meta create mode 100644 Assets/YooAsset/Runtime/ResourceSetting.meta create mode 100644 Assets/YooAsset/Runtime/ResourceSetting/ResourceSetting.cs create mode 100644 Assets/YooAsset/Runtime/ResourceSetting/ResourceSetting.cs.meta create mode 100644 Assets/YooAsset/Runtime/ResourceSetting/ResourceSettingData.cs create mode 100644 Assets/YooAsset/Runtime/ResourceSetting/ResourceSettingData.cs.meta create mode 100644 Assets/YooAsset/Runtime/Utility.meta create mode 100644 Assets/YooAsset/Runtime/Utility/AssemblyUtility.cs create mode 100644 Assets/YooAsset/Runtime/Utility/AssemblyUtility.cs.meta create mode 100644 Assets/YooAsset/Runtime/Utility/FileUtility.cs create mode 100644 Assets/YooAsset/Runtime/Utility/FileUtility.cs.meta create mode 100644 Assets/YooAsset/Runtime/Utility/HashUtility.cs create mode 100644 Assets/YooAsset/Runtime/Utility/HashUtility.cs.meta create mode 100644 Assets/YooAsset/Runtime/Utility/Misc.meta create mode 100644 Assets/YooAsset/Runtime/Utility/Misc/BitMask32.cs create mode 100644 Assets/YooAsset/Runtime/Utility/Misc/BitMask32.cs.meta create mode 100644 Assets/YooAsset/Runtime/Utility/Misc/BitMask64.cs create mode 100644 Assets/YooAsset/Runtime/Utility/Misc/BitMask64.cs.meta create mode 100644 Assets/YooAsset/Runtime/Utility/Misc/CRC32Algorithm.cs create mode 100644 Assets/YooAsset/Runtime/Utility/Misc/CRC32Algorithm.cs.meta create mode 100644 Assets/YooAsset/Runtime/Utility/StringUtility.cs create mode 100644 Assets/YooAsset/Runtime/Utility/StringUtility.cs.meta create mode 100644 Assets/YooAsset/Runtime/YooAsset.asmdef create mode 100644 Assets/YooAsset/Runtime/YooAsset.asmdef.meta create mode 100644 Assets/YooAsset/Runtime/YooAssets.cs create mode 100644 Assets/YooAsset/Runtime/YooAssets.cs.meta create mode 100644 Packages/manifest.json create mode 100644 Packages/packages-lock.json create mode 100644 ProjectSettings/AudioManager.asset create mode 100644 ProjectSettings/AutoStreamingSettings.asset create mode 100644 ProjectSettings/ClusterInputManager.asset create mode 100644 ProjectSettings/DynamicsManager.asset create mode 100644 ProjectSettings/EditorBuildSettings.asset create mode 100644 ProjectSettings/EditorSettings.asset create mode 100644 ProjectSettings/GraphicsSettings.asset create mode 100644 ProjectSettings/InputManager.asset create mode 100644 ProjectSettings/NavMeshAreas.asset create mode 100644 ProjectSettings/NetworkManager.asset create mode 100644 ProjectSettings/PackageManagerSettings.asset create mode 100644 ProjectSettings/Physics2DSettings.asset create mode 100644 ProjectSettings/PresetManager.asset create mode 100644 ProjectSettings/ProjectSettings.asset create mode 100644 ProjectSettings/ProjectVersion.txt create mode 100644 ProjectSettings/QualitySettings.asset create mode 100644 ProjectSettings/TagManager.asset create mode 100644 ProjectSettings/TimeManager.asset create mode 100644 ProjectSettings/UnityConnectSettings.asset create mode 100644 ProjectSettings/VFXManager.asset create mode 100644 ProjectSettings/VersionControlSettings.asset create mode 100644 ProjectSettings/XRSettings.asset delete mode 100644 README.md diff --git a/Assets/YooAsset.meta b/Assets/YooAsset.meta new file mode 100644 index 0000000..0c86392 --- /dev/null +++ b/Assets/YooAsset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3ac1aba04265f9143957a5b474de973d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor.meta b/Assets/YooAsset/Editor.meta new file mode 100644 index 0000000..22c2889 --- /dev/null +++ b/Assets/YooAsset/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ac093515ae764b94aa07be91d4ba978b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBrowser.meta b/Assets/YooAsset/Editor/AssetBundleBrowser.meta new file mode 100644 index 0000000..19ed378 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBrowser.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 197364393c72852419af32ef010600bf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs b/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs new file mode 100644 index 0000000..7c1fed9 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs @@ -0,0 +1,8 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class AssetBundleBrowserWindow +{ + +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs.meta b/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs.meta new file mode 100644 index 0000000..8d8023c --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d6938e4c868d3f34f97d3bb0dbd9d509 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder.meta b/Assets/YooAsset/Editor/AssetBundleBuilder.meta new file mode 100644 index 0000000..c1b5f4b --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1fdecc5500229d44887425ce619352fc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs new file mode 100644 index 0000000..611d48b --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using YooAsset.Utility; + +namespace YooAsset.Editor +{ + public class AssetBundleBuilder + { + /// + /// 构建参数 + /// + public class BuildParameters + { + /// + /// 是否验证构建结果 + /// + public bool IsVerifyBuildingResult = false; + + /// + /// 输出的根目录 + /// + public string OutputRoot; + + /// + /// 构建的平台 + /// + public BuildTarget BuildTarget; + + /// + /// 构建的版本(资源版本号) + /// + public int BuildVersion; + + /// + /// 是否允许冗余机制 + /// 说明:冗余机制可以帮助我们减少包体数量 + /// + public bool ApplyRedundancy = false; + + /// + /// 是否附加上文件扩展名 + /// + public bool AppendFileExtension = false; + + + /// + /// 压缩选项 + /// + public ECompressOption CompressOption; + + /// + /// 是否强制重新构建整个项目,如果为FALSE则是增量打包 + /// + public bool IsForceRebuild; + + /// + /// 内置资源的标记列表 + /// 注意:分号为分隔符 + /// + public string BuildinTags; + + #region 高级选项 + /// + /// 文件名附加上哈希值 + /// + public bool IsAppendHash = false; + + /// + /// 禁止写入类型树结构(可以降低包体和内存并提高加载效率) + /// + public bool IsDisableWriteTypeTree = false; + + /// + /// 忽略类型树变化 + /// + public bool IsIgnoreTypeTreeChanges = true; + + /// + /// 禁用名称查找资源(可以降内存并提高加载效率) + /// + public bool IsDisableLoadAssetByFileName = false; + #endregion + + + /// + /// 获取内置标记列表 + /// + public List GetBuildinTags() + { + return StringUtility.StringToStringList(BuildinTags, ';'); + } + } + + /// + /// 构建参数环境 + /// + public class BuildParametersContext : IContextObject + { + /// + /// 构建参数 + /// + public BuildParameters Parameters { private set; get; } + + /// + /// 构建管线的输出目录 + /// + public string PipelineOutputDirectory { private set; get; } + + + public BuildParametersContext(BuildParameters parameters) + { + Parameters = parameters; + PipelineOutputDirectory = AssetBundleBuilderHelper.MakePipelineOutputDirectory(parameters.OutputRoot, parameters.BuildTarget); + } + + /// + /// 获取本次构建的补丁目录 + /// + public string GetPackageDirectory() + { + return $"{Parameters.OutputRoot}/{Parameters.BuildTarget}/{Parameters.BuildVersion}"; + } + + /// + /// 获取构建选项 + /// + public BuildAssetBundleOptions GetPipelineBuildOptions() + { + // For the new build system, unity always need BuildAssetBundleOptions.CollectDependencies and BuildAssetBundleOptions.DeterministicAssetBundle + // 除非设置ForceRebuildAssetBundle标记,否则会进行增量打包 + + BuildAssetBundleOptions opt = BuildAssetBundleOptions.None; + opt |= BuildAssetBundleOptions.StrictMode; //Do not allow the build to succeed if any errors are reporting during it. + + if (Parameters.CompressOption == ECompressOption.Uncompressed) + opt |= BuildAssetBundleOptions.UncompressedAssetBundle; + else if (Parameters.CompressOption == ECompressOption.LZ4) + opt |= BuildAssetBundleOptions.ChunkBasedCompression; + + if (Parameters.IsForceRebuild) + opt |= BuildAssetBundleOptions.ForceRebuildAssetBundle; //Force rebuild the asset bundles + if (Parameters.IsAppendHash) + opt |= BuildAssetBundleOptions.AppendHashToAssetBundleName; //Append the hash to the assetBundle name + if (Parameters.IsDisableWriteTypeTree) + opt |= BuildAssetBundleOptions.DisableWriteTypeTree; //Do not include type information within the asset bundle (don't write type tree). + if (Parameters.IsIgnoreTypeTreeChanges) + opt |= BuildAssetBundleOptions.IgnoreTypeTreeChanges; //Ignore the type tree changes when doing the incremental build check. + if (Parameters.IsDisableLoadAssetByFileName) + { + opt |= BuildAssetBundleOptions.DisableLoadAssetByFileName; //Disables Asset Bundle LoadAsset by file name. + opt |= BuildAssetBundleOptions.DisableLoadAssetByFileNameWithExtension; //Disables Asset Bundle LoadAsset by file name with extension. + } + + return opt; + } + } + + + private readonly BuildContext _buildContext = new BuildContext(); + + /// + /// 开始构建 + /// + public bool Run(BuildParameters buildParameters) + { + // 清空旧数据 + _buildContext.ClearAllContext(); + + // 构建参数 + var buildParametersContext = new BuildParametersContext(buildParameters); + _buildContext.SetContextObject(buildParametersContext); + + // 执行构建流程 + List pipeline = new List + { + new TaskPrepare(), //前期准备工作 + new TaskGetBuildMap(), //获取构建列表 + new TaskBuilding(), //开始执行构建 + new TaskEncryption(), //加密资源文件 + new TaskCreatePatchManifest(), //创建清单文件 + new TaskCreateReadme(), //创建说明文件 + new TaskCreatePatchPackage(), //制作补丁包 + new TaskCopyBuildinFiles(), //拷贝内置文件 + }; + + bool succeed = BuildRunner.Run(pipeline, _buildContext); + if (succeed) + Debug.Log($"构建成功!"); + else + Debug.LogWarning($"构建失败!"); + return succeed; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs.meta new file mode 100644 index 0000000..8060114 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de7563040250b4e4a835d1fc90238e38 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs new file mode 100644 index 0000000..98900c4 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs @@ -0,0 +1,140 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEditor; +using YooAsset.Utility; + +namespace YooAsset.Editor +{ + public static class AssetBundleBuilderHelper + { + /// + /// 获取默认的输出根路录 + /// + public static string GetDefaultOutputRoot() + { + string projectPath = EditorTools.GetProjectPath(); + return $"{projectPath}/Bundles"; + } + + /// + /// 获取构建管线的输出目录 + /// + public static string MakePipelineOutputDirectory(string outputRoot, BuildTarget buildTarget) + { + return $"{outputRoot}/{buildTarget}/{ResourceSettingData.Setting.UnityManifestFileName}"; + } + + /// + /// 制作AssetBundle的完整名称 + /// 注意:名称为全部小写并且包含后缀名 + /// + public static string MakeBundleName(string bundleLabel, string bundleVariant) + { + if (string.IsNullOrEmpty(bundleVariant)) + return bundleLabel.ToLower(); + else + return $"{bundleLabel}.{bundleVariant}".ToLower(); + } + + + /// + /// 清空流文件夹 + /// + public static void ClearStreamingAssetsFolder() + { + string streamingPath = Application.dataPath + "/StreamingAssets"; + EditorTools.ClearFolder(streamingPath); + } + + /// + /// 删除流文件夹内无关的文件 + /// 删除.manifest文件和.meta文件 + /// + public static void DeleteStreamingAssetsIgnoreFiles() + { + string streamingPath = Application.dataPath + "/StreamingAssets"; + if (Directory.Exists(streamingPath)) + { + string[] files = Directory.GetFiles(streamingPath, "*.manifest", SearchOption.AllDirectories); + foreach (var file in files) + { + FileInfo info = new FileInfo(file); + info.Delete(); + } + + files = Directory.GetFiles(streamingPath, "*.meta", SearchOption.AllDirectories); + foreach (var item in files) + { + FileInfo info = new FileInfo(item); + info.Delete(); + } + } + } + + + /// + /// 获取所有补丁包版本列表 + /// 注意:列表会按照版本号从小到大排序 + /// + private static List GetPackageVersionList(BuildTarget buildTarget, string outputRoot) + { + List versionList = new List(); + + string parentPath = $"{outputRoot}/{buildTarget}"; + if (Directory.Exists(parentPath) == false) + return versionList; + + // 获取所有补丁包文件夹 + string[] allFolders = Directory.GetDirectories(parentPath); + for (int i = 0; i < allFolders.Length; i++) + { + string folderName = Path.GetFileNameWithoutExtension(allFolders[i]); + if (int.TryParse(folderName, out int version)) + versionList.Add(version); + } + + // 从小到大排序 + versionList.Sort(); + return versionList; + } + + /// + /// 获取当前最大的补丁包版本号 + /// + /// 如果没有任何补丁版本,那么返回-1 + public static int GetMaxPackageVersion(BuildTarget buildTarget, string outputRoot) + { + List versionList = GetPackageVersionList(buildTarget, outputRoot); + if (versionList.Count == 0) + return -1; + return versionList[versionList.Count - 1]; + } + + /// + /// 是否存在任何补丁包版本 + /// + public static bool HasAnyPackageVersion(BuildTarget buildTarget, string outputRoot) + { + List versionList = GetPackageVersionList(buildTarget, outputRoot); + return versionList.Count > 0; + } + + + /// + /// 从输出目录加载补丁清单文件 + /// + public static PatchManifest LoadPatchManifestFile(string fileDirectory) + { + string filePath = $"{fileDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}"; + if (File.Exists(filePath) == false) + { + throw new System.Exception($"Not found patch manifest file : {filePath}"); + } + + string jsonData = FileUtility.ReadFile(filePath); + return PatchManifest.Deserialize(jsonData); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs.meta new file mode 100644 index 0000000..2cbe160 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f48abdec05f0dbe438a83e181fe6bc93 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs new file mode 100644 index 0000000..d37faa4 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs @@ -0,0 +1,101 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.Animations; + +namespace YooAsset.Editor +{ + public static class AssetBundleBuilderTools + { + /// + /// 检测所有损坏的预制体文件 + /// + public static void CheckCorruptionPrefab(List searchDirectorys) + { + if (searchDirectorys.Count == 0) + throw new Exception("路径列表不能为空!"); + + // 获取所有资源列表 + int checkCount = 0; + int invalidCount = 0; + string[] findAssets = EditorTools.FindAssets(EAssetSearchType.Prefab, searchDirectorys.ToArray()); + foreach (string assetPath in findAssets) + { + UnityEngine.Object prefab = AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object)); + if (prefab == null) + { + invalidCount++; + Debug.LogError($"发现损坏预制件:{assetPath}"); + } + EditorTools.DisplayProgressBar("检测预制件文件是否损坏", ++checkCount, findAssets.Length); + } + EditorTools.ClearProgressBar(); + + if (invalidCount == 0) + Debug.Log($"没有发现损坏预制件"); + } + + /// + /// 检测所有动画控制器的冗余状态 + /// + public static void FindRedundantAnimationState(List searchDirectorys) + { + if (searchDirectorys.Count == 0) + throw new Exception("路径列表不能为空!"); + + // 获取所有资源列表 + int checkCount = 0; + int findCount = 0; + string[] findAssets = EditorTools.FindAssets(EAssetSearchType.RuntimeAnimatorController, searchDirectorys.ToArray()); + foreach (string assetPath in findAssets) + { + AnimatorController animator= AssetDatabase.LoadAssetAtPath(assetPath); + if (EditorTools.FindRedundantAnimationState(animator)) + { + findCount++; + Debug.LogWarning($"发现冗余的动画控制器:{assetPath}"); + } + EditorTools.DisplayProgressBar("检测冗余的动画控制器", ++checkCount, findAssets.Length); + } + EditorTools.ClearProgressBar(); + + if (findCount == 0) + Debug.Log($"没有发现冗余的动画控制器"); + else + AssetDatabase.SaveAssets(); + } + + /// + /// 清理所有材质球的冗余属性 + /// + public static void ClearMaterialUnusedProperty(List searchDirectorys) + { + if (searchDirectorys.Count == 0) + throw new Exception("路径列表不能为空!"); + + // 获取所有资源列表 + int checkCount = 0; + int removedCount = 0; + string[] findAssets = EditorTools.FindAssets(EAssetSearchType.Material, searchDirectorys.ToArray()); + foreach (string assetPath in findAssets) + { + Material mat = AssetDatabase.LoadAssetAtPath(assetPath); + if (EditorTools.ClearMaterialUnusedProperty(mat)) + { + removedCount++; + Debug.LogWarning($"材质球已被处理:{assetPath}"); + } + EditorTools.DisplayProgressBar("清理冗余的材质球", ++checkCount, findAssets.Length); + } + EditorTools.ClearProgressBar(); + + if (removedCount == 0) + Debug.Log($"没有发现冗余的材质球"); + else + AssetDatabase.SaveAssets(); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs.meta new file mode 100644 index 0000000..769e38e --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fe50795c51a46884088139b840c1557f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs new file mode 100644 index 0000000..9731285 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs @@ -0,0 +1,158 @@ +using System; +using System.IO; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; + +namespace YooAsset.Editor +{ + public class AssetBundleBuilderWindow : EditorWindow + { + static AssetBundleBuilderWindow _thisInstance; + + [MenuItem("YooAsset/AssetBundle Builder", false, 102)] + static void ShowWindow() + { + if (_thisInstance == null) + { + _thisInstance = EditorWindow.GetWindow(typeof(AssetBundleBuilderWindow), false, "资源包构建工具", true) as AssetBundleBuilderWindow; + _thisInstance.minSize = new Vector2(800, 600); + } + _thisInstance.Show(); + } + + // 构建器 + private readonly AssetBundleBuilder _assetBuilder = new AssetBundleBuilder(); + + // 构建参数 + private int _buildVersion; + private BuildTarget _buildTarget; + private ECompressOption _compressOption = ECompressOption.Uncompressed; + private bool _isAppendExtension = false; + private bool _isForceRebuild = false; + private string _buildinTags = string.Empty; + + // GUI相关 + private bool _isInit = false; + private GUIStyle _centerStyle; + private GUIStyle _leftStyle; + + + private void OnGUI() + { + InitInternal(); + + // 标题 + EditorGUILayout.LabelField("Build setup", _centerStyle); + EditorGUILayout.Space(); + + // 输出路径 + string defaultOutputRoot = AssetBundleBuilderHelper.GetDefaultOutputRoot(); + string pipelineOutputDirectory = AssetBundleBuilderHelper.MakePipelineOutputDirectory(defaultOutputRoot, _buildTarget); + EditorGUILayout.LabelField("Build Output", pipelineOutputDirectory); + + // 构建参数 + _buildVersion = EditorGUILayout.IntField("Build Version", _buildVersion, GUILayout.MaxWidth(250)); + _compressOption = (ECompressOption)EditorGUILayout.EnumPopup("Compression", _compressOption, GUILayout.MaxWidth(250)); + _isAppendExtension = GUILayout.Toggle(_isAppendExtension, "Append Extension", GUILayout.MaxWidth(120)); + _isForceRebuild = GUILayout.Toggle(_isForceRebuild, "Force Rebuild", GUILayout.MaxWidth(120)); + if (_isForceRebuild) + _buildinTags = EditorGUILayout.TextField("Buildin Tags", _buildinTags); + + // 构建按钮 + EditorGUILayout.Space(); + if (GUILayout.Button("Build", GUILayout.MaxHeight(40))) + { + string title; + string content; + if (_isForceRebuild) + { + title = "警告"; + content = "确定开始强制构建吗,这样会删除所有已有构建的文件"; + } + else + { + title = "提示"; + content = "确定开始增量构建吗"; + } + if (EditorUtility.DisplayDialog(title, content, "Yes", "No")) + { + SaveSettingsToPlayerPrefs(); + EditorTools.ClearUnityConsole(); + EditorApplication.delayCall += ExecuteBuild; + } + else + { + Debug.LogWarning("[Build] 打包已经取消"); + } + } + } + private void InitInternal() + { + if (_isInit) + return; + _isInit = true; + + // GUI相关 + _centerStyle = new GUIStyle(GUI.skin.GetStyle("Label")); + _centerStyle.alignment = TextAnchor.UpperCenter; + _leftStyle = new GUIStyle(GUI.skin.GetStyle("Label")); + _leftStyle.alignment = TextAnchor.MiddleLeft; + + // 构建参数 + var appVersion = new Version(Application.version); + _buildVersion = appVersion.Revision; + _buildTarget = EditorUserBuildSettings.activeBuildTarget; + + // 读取配置 + LoadSettingsFromPlayerPrefs(); + } + + /// + /// 执行构建 + /// + private void ExecuteBuild() + { + string defaultOutputRoot = AssetBundleBuilderHelper.GetDefaultOutputRoot(); + AssetBundleBuilder.BuildParameters buildParameters = new AssetBundleBuilder.BuildParameters(); + buildParameters.IsVerifyBuildingResult = true; + buildParameters.OutputRoot = defaultOutputRoot; + buildParameters.BuildTarget = _buildTarget; + buildParameters.BuildVersion = _buildVersion; + buildParameters.CompressOption = _compressOption; + buildParameters.AppendFileExtension = _isAppendExtension; + buildParameters.IsForceRebuild = _isForceRebuild; + buildParameters.BuildinTags = _buildinTags; + _assetBuilder.Run(buildParameters); + } + + #region 配置相关 + private const string StrEditorCompressOption = "StrEditorCompressOption"; + private const string StrEditorIsAppendExtension = "StrEditorIsAppendExtension"; + private const string StrEditorIsForceRebuild = "StrEditorIsForceRebuild"; + private const string StrEditorBuildinTags = "StrEditorBuildinTags"; + + /// + /// 存储配置 + /// + private void SaveSettingsToPlayerPrefs() + { + EditorTools.PlayerSetEnum(StrEditorCompressOption, _compressOption); + EditorPrefs.SetBool(StrEditorIsAppendExtension, _isAppendExtension); + EditorPrefs.SetBool(StrEditorIsForceRebuild, _isForceRebuild); + EditorPrefs.SetString(StrEditorBuildinTags, _buildinTags); + } + + /// + /// 读取配置 + /// + private void LoadSettingsFromPlayerPrefs() + { + _compressOption = EditorTools.PlayerGetEnum(StrEditorCompressOption, ECompressOption.Uncompressed); + _isAppendExtension = EditorPrefs.GetBool(StrEditorIsAppendExtension, false); + _isForceRebuild = EditorPrefs.GetBool(StrEditorIsForceRebuild, false); + _buildinTags = EditorPrefs.GetString(StrEditorBuildinTags, string.Empty); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs.meta new file mode 100644 index 0000000..2e33fe9 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66e43420e95cd0b4bae8803a31e9817b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs new file mode 100644 index 0000000..fa15038 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs @@ -0,0 +1,117 @@ +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset.Editor +{ + /// + /// 构建的资源信息类 + /// + public class BuildAssetInfo + { + /// + /// 资源路径 + /// + public string AssetPath { private set; get; } + + /// + /// 资源包标签 + /// + public string BundleLabel { private set; get; } + + /// + /// 资源包文件格式 + /// + public string BundleVariant { private set; get; } + + /// + /// 是否为原生资源 + /// + public bool IsRawAsset = false; + + /// + /// 是否为主动收集资源 + /// + public bool IsCollectAsset = false; + + /// + /// 资源标记列表 + /// + public List AssetTags = new List(); + + /// + /// 被依赖次数 + /// + public int DependCount = 0; + + /// + /// 依赖的所有资源信息 + /// 注意:包括零依赖资源(零依赖资源的资源包名无效) + /// + public List AllDependAssetInfos { private set; get; } = null; + + + public BuildAssetInfo(string assetPath) + { + AssetPath = assetPath; + } + + /// + /// 设置所有依赖的资源 + /// + public void SetAllDependAssetInfos(List dependAssetInfos) + { + if (AllDependAssetInfos != null) + throw new System.Exception("Should never get here !"); + + AllDependAssetInfos = dependAssetInfos; + } + + /// + /// 设置资源包的标签和文件格式 + /// + public void SetBundleLabelAndVariant(string bundleLabel, string bundleVariant) + { + if (string.IsNullOrEmpty(BundleLabel) == false || string.IsNullOrEmpty(BundleVariant) == false) + throw new System.Exception("Should never get here !"); + + BundleLabel = bundleLabel; + BundleVariant = bundleVariant; + } + + /// + /// 添加资源标记 + /// + public void AddAssetTags(List tags) + { + foreach (var tag in tags) + { + if (AssetTags.Contains(tag) == false) + { + AssetTags.Add(tag); + } + } + } + + /// + /// 获取资源包的完整名称 + /// + public string GetBundleName() + { + if (string.IsNullOrEmpty(BundleLabel) || string.IsNullOrEmpty(BundleVariant)) + throw new System.ArgumentNullException(); + + return AssetBundleBuilderHelper.MakeBundleName(BundleLabel, BundleVariant); + } + + /// + /// 检测资源包名是否有效 + /// + public bool CheckBundleNameValid() + { + if (string.IsNullOrEmpty(BundleLabel) == false && string.IsNullOrEmpty(BundleVariant) == false) + return true; + else + return false; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs.meta new file mode 100644 index 0000000..d7e1149 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 447008dd110b8d746aafbe88c78bee5d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs new file mode 100644 index 0000000..96eff3e --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs @@ -0,0 +1,148 @@ +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; + +namespace YooAsset.Editor +{ + /// + /// 构建的资源包信息类 + /// + public class BuildBundleInfo + { + /// + /// 资源包完整名称 + /// + public string BundleName { private set; get; } + + /// + /// 资源包标签名 + /// + public string BundleLabel { private set; get; } + + /// + /// 资源包文件格式 + /// + public string BundleVariant { private set; get; } + + /// + /// 包含的资源列表 + /// + public readonly List Assets = new List(); + + /// + /// 是否为原生文件 + /// + public bool IsRawFile + { + get + { + foreach (var asset in Assets) + { + if (asset.IsRawAsset) + return true; + } + return false; + } + } + + + public BuildBundleInfo(string bundleLabel, string bundleVariant) + { + BundleLabel = bundleLabel; + BundleVariant = bundleVariant; + BundleName = AssetBundleBuilderHelper.MakeBundleName(bundleLabel, bundleVariant); + } + + /// + /// 是否包含指定资源 + /// + public bool IsContainsAsset(string assetPath) + { + foreach (var assetInfo in Assets) + { + if (assetInfo.AssetPath == assetPath) + { + return true; + } + } + return false; + } + + /// + /// 添加一个打包资源 + /// + public void PackAsset(BuildAssetInfo assetInfo) + { + if (IsContainsAsset(assetInfo.AssetPath)) + throw new System.Exception($"Asset is existed : {assetInfo.AssetPath}"); + + Assets.Add(assetInfo); + } + + /// + /// 获取文件的扩展名 + /// + public string GetAppendExtension() + { + if (IsRawFile) + return $".{ResourceSettingData.Setting.RawFileVariant}"; + else + return $".{ResourceSettingData.Setting.AssetBundleFileVariant}"; + } + + /// + /// 获取资源标记列表 + /// + public string[] GetAssetTags() + { + List result = new List(Assets.Count); + foreach (var assetInfo in Assets) + { + foreach (var assetTag in assetInfo.AssetTags) + { + if (result.Contains(assetTag) == false) + result.Add(assetTag); + } + } + return result.ToArray(); + } + + /// + /// 获取主动收集的资源路径列表 + /// + public string[] GetCollectAssetPaths() + { + return Assets.Where(t => t.IsCollectAsset).Select(t => t.AssetPath).ToArray(); + } + + /// + /// 获取构建的资源路径列表 + /// + public string[] GetBuildinAssetPaths() + { + return Assets.Select(t => t.AssetPath).ToArray(); + } + + /// + /// 获取主动收集的资源信息列表 + /// + public BuildAssetInfo[] GetCollectAssetInfos() + { + return Assets.Where(t => t.IsCollectAsset).ToArray(); + } + + /// + /// 创建AssetBundleBuild类 + /// + public UnityEditor.AssetBundleBuild CreatePipelineBuild() + { + // 注意:我们不在支持AssetBundle的变种机制 + AssetBundleBuild build = new AssetBundleBuild(); + build.assetBundleName = BundleName; + build.assetBundleVariant = string.Empty; + build.assetNames = GetBuildinAssetPaths(); + return build; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs.meta new file mode 100644 index 0000000..d9e3552 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 826a9d7b4de0eba40b5c39b33747c011 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem.meta new file mode 100644 index 0000000..59ef26f --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2875232ad7f87c148b752d432f3a54f2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs new file mode 100644 index 0000000..5d80adf --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset.Editor +{ + public class BuildContext + { + private readonly Dictionary _contextObjects = new Dictionary(); + + /// + /// 清空所有情景对象 + /// + public void ClearAllContext() + { + _contextObjects.Clear(); + } + + /// + /// 设置情景对象 + /// + public void SetContextObject(IContextObject contextObject) + { + if (contextObject == null) + throw new ArgumentNullException("contextObject"); + + var type = contextObject.GetType(); + if (_contextObjects.ContainsKey(type)) + throw new Exception($"Context object {type} is already existed."); + + _contextObjects.Add(type, contextObject); + } + + /// + /// 获取情景对象 + /// + public T GetContextObject() where T : IContextObject + { + var type = typeof(T); + if (_contextObjects.TryGetValue(type, out IContextObject contextObject)) + { + return (T)contextObject; + } + else + { + throw new Exception($"Not found context object : {type}"); + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs.meta new file mode 100644 index 0000000..d2fa01b --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a232601f99c4634ea17fca4979f80ab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs new file mode 100644 index 0000000..f259af3 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace YooAsset.Editor +{ + public class BuildRunner + { + /// + /// 执行构建流程 + /// + /// 如果成功返回TRUE,否则返回FALSE + public static bool Run(List pipeline, BuildContext context) + { + if (pipeline == null) + throw new ArgumentNullException("pipeline"); + if (context == null) + throw new ArgumentNullException("context"); + + bool succeed = true; + for (int i = 0; i < pipeline.Count; i++) + { + IBuildTask task = pipeline[i]; + try + { + task.Run(context); + } + catch (Exception e) + { + Debug.LogError($"Build task {task.GetType().Name} failed : {e}"); + succeed = false; + break; + } + } + + // 返回运行结果 + return succeed; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs.meta new file mode 100644 index 0000000..ad8b244 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 79014124da678684388454d6ea892722 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs new file mode 100644 index 0000000..7158143 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs @@ -0,0 +1,8 @@ + +namespace YooAsset.Editor +{ + public interface IBuildTask + { + void Run(BuildContext context); + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs.meta new file mode 100644 index 0000000..f6b7e53 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8888657c45e12c646a8349bac6bf0326 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs new file mode 100644 index 0000000..9845cce --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs @@ -0,0 +1,7 @@ + +namespace YooAsset.Editor +{ + public interface IContextObject + { + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs.meta new file mode 100644 index 0000000..c51328c --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 151a4acd5ad1c2046be20e080f9bdad4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks.meta new file mode 100644 index 0000000..863df44 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 254f430e0264bd84387878f8d7280e44 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs new file mode 100644 index 0000000..78c945a --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs @@ -0,0 +1,194 @@ +using System; +using System.Linq; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace YooAsset.Editor +{ + public class TaskBuilding : IBuildTask + { + public class UnityManifestContext : IContextObject + { + public AssetBundleManifest UnityManifest; + } + + void IBuildTask.Run(BuildContext context) + { + var buildParametersContext = context.GetContextObject(); + var buildMapContext = context.GetContextObject(); + + Debug.Log($"开始构建......"); + BuildAssetBundleOptions opt = buildParametersContext.GetPipelineBuildOptions(); + AssetBundleManifest unityManifest = BuildPipeline.BuildAssetBundles(buildParametersContext.PipelineOutputDirectory, buildMapContext.GetPipelineBuilds(), opt, buildParametersContext.Parameters.BuildTarget); + if (unityManifest == null) + throw new Exception("构建过程中发生错误!"); + + UnityManifestContext unityManifestContext = new UnityManifestContext(); + unityManifestContext.UnityManifest = unityManifest; + context.SetContextObject(unityManifestContext); + + // 拷贝原生文件 + foreach (var bundleInfo in buildMapContext.BundleInfos) + { + if (bundleInfo.IsRawFile) + { + string dest = $"{buildParametersContext.PipelineOutputDirectory}/{bundleInfo.BundleName}"; + foreach(var buildAsset in bundleInfo.Assets) + { + if(buildAsset.IsRawAsset) + EditorTools.CopyFile(buildAsset.AssetPath, dest, true); + } + } + } + + // 验证构建结果 + if (buildParametersContext.Parameters.IsVerifyBuildingResult) + { + VerifyingBuildingResult(context, unityManifest); + } + } + + /// + /// 验证构建结果 + /// + private void VerifyingBuildingResult(BuildContext context, AssetBundleManifest unityManifest) + { + var buildParameters = context.GetContextObject(); + var buildMapContext = context.GetContextObject(); + string[] buildedBundles = unityManifest.GetAllAssetBundles(); + + // 1. 过滤掉原生Bundle + List expectBundles = new List(buildedBundles.Length); + foreach(var bundleInfo in buildMapContext.BundleInfos) + { + if (bundleInfo.IsRawFile == false) + expectBundles.Add(bundleInfo); + } + + // 2. 验证数量 + if (buildedBundles.Length != expectBundles.Count) + { + Debug.LogWarning($"构建过程中可能存在无效的资源,导致和预期构建的Bundle数量不一致!"); + } + + // 3. 正向验证Bundle + foreach (var bundleName in buildedBundles) + { + if (buildMapContext.IsContainsBundle(bundleName) == false) + { + throw new Exception($"Should never get here !"); + } + } + + // 4. 反向验证Bundle + bool isPass = true; + foreach (var expectBundle in expectBundles) + { + bool isMatch = false; + foreach (var buildedBundle in buildedBundles) + { + if (buildedBundle == expectBundle.BundleName) + { + isMatch = true; + break; + } + } + if (isMatch == false) + { + isPass = false; + Debug.LogWarning($"没有找到预期构建的Bundle文件 : {expectBundle.BundleName}"); + } + } + if(isPass == false) + { + throw new Exception("构建结果验证没有通过,请参考警告日志!"); + } + + // 5. 验证Asset + int progressValue = 0; + foreach (var buildedBundle in buildedBundles) + { + string filePath = $"{buildParameters.PipelineOutputDirectory}/{buildedBundle}"; + string[] allBuildinAssetPaths = GetAssetBundleAllAssets(filePath); + string[] expectBuildinAssetPaths = buildMapContext.GetBuildinAssetPaths(buildedBundle); + if (expectBuildinAssetPaths.Length != allBuildinAssetPaths.Length) + { + Debug.LogWarning($"构建的Bundle文件内的资源对象数量和预期不匹配 : {buildedBundle}"); + isPass = false; + continue; + } + + foreach (var buildinAssetPath in allBuildinAssetPaths) + { + var guid = AssetDatabase.AssetPathToGUID(buildinAssetPath); + if (string.IsNullOrEmpty(guid)) + { + Debug.LogWarning($"无效的资源路径,请检查路径是否带有特殊符号或中文:{buildinAssetPath}"); + isPass = false; + continue; + } + + bool isMatch = false; + foreach (var exceptBuildAssetPath in expectBuildinAssetPaths) + { + var guidExcept = AssetDatabase.AssetPathToGUID(exceptBuildAssetPath); + if (guid == guidExcept) + { + isMatch = true; + break; + } + } + if (isMatch == false) + { + Debug.LogWarning($"在构建的Bundle文件里发现了没有匹配的资源对象:{buildinAssetPath}"); + isPass = false; + continue; + } + } + + EditorTools.DisplayProgressBar("验证构建结果", ++progressValue, buildedBundles.Length); + } + EditorTools.ClearProgressBar(); + if (isPass == false) + { + throw new Exception("构建结果验证没有通过,请参考警告日志!"); + } + + // 卸载所有加载的Bundle + Debug.Log("构建结果验证成功!"); + } + + /// + /// 解析.manifest文件并获取资源列表 + /// + private string[] GetAssetBundleAllAssets(string filePath) + { + string manifestFilePath = $"{filePath}.manifest"; + List assetLines = new List(); + using (StreamReader reader = File.OpenText(manifestFilePath)) + { + string content; + bool findTarget = false; + while (null != (content = reader.ReadLine())) + { + if (content.StartsWith("Dependencies:")) + break; + if (findTarget == false && content.StartsWith("Assets:")) + findTarget = true; + if (findTarget) + { + if (content.StartsWith("- ")) + { + string assetPath = content.TrimStart("- ".ToCharArray()); + assetLines.Add(assetPath); + } + } + } + } + return assetLines.ToArray(); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs.meta new file mode 100644 index 0000000..c4d1e7c --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9466e826c135e994c84961e4b921ca5f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs new file mode 100644 index 0000000..faa917a --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace YooAsset.Editor +{ + /// + /// 拷贝内置文件到StreamingAssets + /// + public class TaskCopyBuildinFiles : IBuildTask + { + void IBuildTask.Run(BuildContext context) + { + // 注意:我们只有在强制重建的时候才会拷贝 + var buildParameters = context.GetContextObject(); + if(buildParameters.Parameters.IsForceRebuild) + { + // 清空流目录 + AssetBundleBuilderHelper.ClearStreamingAssetsFolder(); + + // 拷贝内置文件 + var pipelineOutputDirectory = buildParameters.PipelineOutputDirectory; + CopyBuildinFilesToStreaming(pipelineOutputDirectory); + } + } + + private void CopyBuildinFilesToStreaming(string pipelineOutputDirectory) + { + // 加载补丁清单 + PatchManifest patchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(pipelineOutputDirectory); + + // 拷贝文件列表 + foreach (var patchBundle in patchManifest.BundleList) + { + if (patchBundle.IsBuildin == false) + continue; + + string sourcePath = $"{pipelineOutputDirectory}/{patchBundle.BundleName}"; + string destPath = $"{Application.dataPath}/StreamingAssets/{patchBundle.Hash}"; + Debug.Log($"拷贝内置文件到流目录:{patchBundle.BundleName}"); + EditorTools.CopyFile(sourcePath, destPath, true); + } + + // 拷贝清单文件 + { + string sourcePath = $"{pipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}"; + string destPath = $"{Application.dataPath}/StreamingAssets/{ResourceSettingData.Setting.PatchManifestFileName}"; + EditorTools.CopyFile(sourcePath, destPath, true); + } + + // 拷贝清单哈希文件 + { + string sourcePath = $"{pipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}"; + string destPath = $"{Application.dataPath}/StreamingAssets/{ResourceSettingData.Setting.PatchManifestHashFileName}"; + EditorTools.CopyFile(sourcePath, destPath, true); + } + + // 刷新目录 + AssetDatabase.Refresh(); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs.meta new file mode 100644 index 0000000..6ac8176 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5c77e17c3a3a57548a218f1cd26f5a55 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs new file mode 100644 index 0000000..18a0bbd --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs @@ -0,0 +1,155 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using YooAsset.Utility; + +namespace YooAsset.Editor +{ + /// + /// 创建补丁清单文件 + /// + public class TaskCreatePatchManifest : IBuildTask + { + void IBuildTask.Run(BuildContext context) + { + var buildParameters = context.GetContextObject(); + var encryptionContext = context.GetContextObject(); + var buildMapContext = context.GetContextObject(); + CreatePatchManifestFile(buildParameters, buildMapContext, encryptionContext); + } + + /// + /// 创建补丁清单文件到输出目录 + /// + private void CreatePatchManifestFile(AssetBundleBuilder.BuildParametersContext buildParameters, + TaskGetBuildMap.BuildMapContext buildMapContext, TaskEncryption.EncryptionContext encryptionContext) + { + // 创建新补丁清单 + PatchManifest patchManifest = new PatchManifest(); + patchManifest.ResourceVersion = buildParameters.Parameters.BuildVersion; + patchManifest.BuildinTags = buildParameters.Parameters.BuildinTags; + patchManifest.BundleList = GetAllPatchBundle(buildParameters, buildMapContext, encryptionContext); + patchManifest.AssetList = GetAllPatchAsset(buildMapContext, patchManifest.BundleList); + + // 创建补丁清单文件 + string manifestFilePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}"; + UnityEngine.Debug.Log($"创建补丁清单文件:{manifestFilePath}"); + PatchManifest.Serialize(manifestFilePath, patchManifest); + + // 创建补丁清单哈希文件 + string manifestHashFilePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}"; + string manifestHash = HashUtility.FileMD5(manifestFilePath); + UnityEngine.Debug.Log($"创建补丁清单哈希文件:{manifestHashFilePath}"); + FileUtility.CreateFile(manifestHashFilePath, manifestHash); + } + + /// + /// 获取资源包列表 + /// + private List GetAllPatchBundle(AssetBundleBuilder.BuildParametersContext buildParameters, + TaskGetBuildMap.BuildMapContext buildMapContext, TaskEncryption.EncryptionContext encryptionContext) + { + List result = new List(1000); + + // 内置标记列表 + List buildinTags = buildParameters.Parameters.GetBuildinTags(); + + // 加载旧补丁清单 + PatchManifest oldPatchManifest = null; + if (buildParameters.Parameters.IsForceRebuild == false) + { + oldPatchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory); + } + + foreach (var bundleInfo in buildMapContext.BundleInfos) + { + var bundleName = bundleInfo.BundleName; + string filePath = $"{buildParameters.PipelineOutputDirectory}/{bundleName}"; + string hash = HashUtility.FileMD5(filePath); + string crc = HashUtility.FileCRC32(filePath); + long size = FileUtility.GetFileSize(filePath); + int version = buildParameters.Parameters.BuildVersion; + string[] tags = buildMapContext.GetAssetTags(bundleName); + bool isEncrypted = encryptionContext.IsEncryptFile(bundleName); + bool isBuildin = IsBuildinBundle(tags, buildinTags); + bool isRawFile = bundleInfo.IsRawFile; + + // 附加文件扩展名 + if (buildParameters.Parameters.AppendFileExtension) + { + hash += bundleInfo.GetAppendExtension(); + } + + // 注意:如果文件没有变化使用旧版本号 + if (oldPatchManifest != null && oldPatchManifest.Bundles.TryGetValue(bundleName, out PatchBundle value)) + { + if (value.Hash == hash) + version = value.Version; + } + + PatchBundle patchBundle = new PatchBundle(bundleName, hash, crc, size, version, tags); + patchBundle.SetFlagsValue(isEncrypted, isBuildin, isRawFile); + result.Add(patchBundle); + } + + return result; + } + private bool IsBuildinBundle(string[] bundleTags, List buildinTags) + { + // 注意:没有任何标记的Bundle文件默认为内置文件 + if (bundleTags.Length == 0) + return true; + + foreach (var tag in bundleTags) + { + if (buildinTags.Contains(tag)) + return true; + } + return false; + } + + /// + /// 获取资源列表 + /// + private List GetAllPatchAsset(TaskGetBuildMap.BuildMapContext buildMapContext, List bundleList) + { + List result = new List(1000); + foreach (var bundleInfo in buildMapContext.BundleInfos) + { + var assetInfos = bundleInfo.GetCollectAssetInfos(); + foreach (var assetInfo in assetInfos) + { + PatchAsset patchAsset = new PatchAsset(); + patchAsset.AssetPath = assetInfo.AssetPath; + patchAsset.BundleID = GetAssetBundleID(assetInfo.GetBundleName(), bundleList); + patchAsset.DependIDs = GetAssetBundleDependIDs(assetInfo, bundleList); + result.Add(patchAsset); + } + } + return result; + } + private int[] GetAssetBundleDependIDs(BuildAssetInfo assetInfo, List bundleList) + { + List result = new List(); + foreach (var dependAssetInfo in assetInfo.AllDependAssetInfos) + { + if (dependAssetInfo.CheckBundleNameValid() == false) + continue; + int bundleID = GetAssetBundleID(dependAssetInfo.GetBundleName(), bundleList); + if (result.Contains(bundleID) == false) + result.Add(bundleID); + } + return result.ToArray(); + } + private int GetAssetBundleID(string bundleName, List bundleList) + { + for (int index = 0; index < bundleList.Count; index++) + { + if (bundleList[index].BundleName == bundleName) + return index; + } + throw new Exception($"Not found bundle name : {bundleName}"); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs.meta new file mode 100644 index 0000000..53cf77f --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 566c37f5a59f2e84397a9527981a7310 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs new file mode 100644 index 0000000..a4b8b6f --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs @@ -0,0 +1,83 @@ +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset.Editor +{ + /// + /// 制作补丁包 + /// + public class TaskCreatePatchPackage : IBuildTask + { + void IBuildTask.Run(BuildContext context) + { + var buildParameters = context.GetContextObject(); + CopyPatchFiles(buildParameters); + } + + /// + /// 拷贝补丁文件到补丁包目录 + /// + private void CopyPatchFiles(AssetBundleBuilder.BuildParametersContext buildParameters) + { + string packageDirectory = buildParameters.GetPackageDirectory(); + UnityEngine.Debug.Log($"开始拷贝补丁文件到补丁包目录:{packageDirectory}"); + + // 拷贝Readme文件 + { + string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.ReadmeFileName}"; + string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.ReadmeFileName}"; + EditorTools.CopyFile(sourcePath, destPath, true); + UnityEngine.Debug.Log($"拷贝Readme文件到:{destPath}"); + } + + // 拷贝PatchManifest文件 + { + string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}"; + string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}"; + EditorTools.CopyFile(sourcePath, destPath, true); + UnityEngine.Debug.Log($"拷贝PatchManifest文件到:{destPath}"); + } + + // 拷贝PatchManifest哈希文件 + { + string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}"; + string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}"; + EditorTools.CopyFile(sourcePath, destPath, true); + UnityEngine.Debug.Log($"拷贝PatchManifest哈希文件到:{destPath}"); + } + + // 拷贝UnityManifest序列化文件 + { + string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}"; + string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}"; + EditorTools.CopyFile(sourcePath, destPath, true); + UnityEngine.Debug.Log($"拷贝UnityManifest文件到:{destPath}"); + } + + // 拷贝UnityManifest文本文件 + { + string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}.manifest"; + string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}.manifest"; + EditorTools.CopyFile(sourcePath, destPath, true); + } + + // 拷贝所有补丁文件 + // 注意:拷贝的补丁文件都是需要玩家热更新的文件 + int progressValue = 0; + PatchManifest patchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory); + int patchFileTotalCount = patchManifest.BundleList.Count; + foreach (var patchBundle in patchManifest.BundleList) + { + if (patchBundle.Version == buildParameters.Parameters.BuildVersion) + { + string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{patchBundle.BundleName}"; + string destPath = $"{packageDirectory}/{patchBundle.Hash}"; + EditorTools.CopyFile(sourcePath, destPath, true); + UnityEngine.Debug.Log($"拷贝补丁文件到补丁包:{patchBundle.BundleName}"); + EditorTools.DisplayProgressBar("拷贝补丁文件", ++progressValue, patchFileTotalCount); + } + } + EditorTools.ClearProgressBar(); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs.meta new file mode 100644 index 0000000..0f45229 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d110092f543e3fe40a4d3882e1a718e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs new file mode 100644 index 0000000..14e4108 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs @@ -0,0 +1,205 @@ +using System; +using System.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset.Editor +{ + /// + /// 创建说明文件 + /// + public class TaskCreateReadme : IBuildTask + { + void IBuildTask.Run(BuildContext context) + { + var buildParameters = context.GetContextObject(); + var buildMapContext = context.GetContextObject(); + CreateReadmeFile(buildParameters, buildMapContext); + } + + /// + /// 创建Readme文件到输出目录 + /// + private void CreateReadmeFile(AssetBundleBuilder.BuildParametersContext buildParameters, TaskGetBuildMap.BuildMapContext buildMapContext) + { + PatchManifest patchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory); + + // 删除旧文件 + string filePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.ReadmeFileName}"; + if (File.Exists(filePath)) + File.Delete(filePath); + + UnityEngine.Debug.Log($"创建说明文件:{filePath}"); + + StringBuilder content = new StringBuilder(); + AppendData(content, $"构建时间:{DateTime.Now}"); + AppendData(content, $"构建平台:{buildParameters.Parameters.BuildTarget}"); + AppendData(content, $"构建版本:{buildParameters.Parameters.BuildVersion}"); + AppendData(content, $"冗余机制:{buildParameters.Parameters.ApplyRedundancy}"); + + AppendData(content, ""); + AppendData(content, $"--着色器--"); + AppendData(content, $"IsCollectAllShaders:{AssetBundleCollectorSettingData.Setting.IsCollectAllShaders}"); + AppendData(content, $"ShadersBundleName:{AssetBundleCollectorSettingData.Setting.ShadersBundleName}"); + + AppendData(content, ""); + AppendData(content, $"--配置信息--"); + for (int i = 0; i < AssetBundleCollectorSettingData.Setting.Collectors.Count; i++) + { + AssetBundleCollectorSetting.Collector wrapper = AssetBundleCollectorSettingData.Setting.Collectors[i]; + AppendData(content, wrapper.ToString()); + } + + AppendData(content, ""); + AppendData(content, $"--构建参数--"); + AppendData(content, $"CompressOption:{buildParameters.Parameters.CompressOption}"); + AppendData(content, $"IsForceRebuild:{buildParameters.Parameters.IsForceRebuild}"); + AppendData(content, $"BuildinTags:{buildParameters.Parameters.BuildinTags}"); + AppendData(content, $"IsAppendHash:{buildParameters.Parameters.IsAppendHash}"); + AppendData(content, $"IsDisableWriteTypeTree:{buildParameters.Parameters.IsDisableWriteTypeTree}"); + AppendData(content, $"IsIgnoreTypeTreeChanges:{buildParameters.Parameters.IsIgnoreTypeTreeChanges}"); + AppendData(content, $"IsDisableLoadAssetByFileName : {buildParameters.Parameters.IsDisableLoadAssetByFileName}"); + + AppendData(content, ""); + AppendData(content, $"--构建信息--"); + AppendData(content, $"参与构建的资源总数:{buildMapContext.GetAllAssets().Count}"); + GetBundleFileCountAndTotalSize(patchManifest, out int fileCount1, out long fileTotalSize1); + AppendData(content, $"构建的资源包总数:{fileCount1} 文件总大小:{fileTotalSize1 / (1024 * 1024)}MB"); + GetBuildinFileCountAndTotalSize(patchManifest, out int fileCount2, out long fileTotalSize2); + AppendData(content, $"内置的资源包总数:{fileCount2} 文件总大小:{fileTotalSize2 / (1024 * 1024)}MB"); + GetNotBuildinFileCountAndTotalSize(patchManifest, out int fileCount3, out long fileTotalSize3); + AppendData(content, $"非内置的资源包总数:{fileCount3} 文件总大小:{fileTotalSize3 / (1024 * 1024)}MB"); + GetEncryptedFileCountAndTotalSize(patchManifest, out int fileCount4, out long fileTotalSize4); + AppendData(content, $"加密的资源包总数:{fileCount4} 文件总大小:{fileTotalSize4 / (1024 * 1024)}MB"); + GetRawFileCountAndTotalSize(patchManifest, out int fileCount5, out long fileTotalSize5); + AppendData(content, $"原生的资源包总数:{fileCount5} 文件总大小:{fileTotalSize5 / (1024 * 1024)}MB"); + + AppendData(content, ""); + AppendData(content, $"--冗余列表--"); + for (int i = 0; i < buildMapContext.RedundancyList.Count; i++) + { + string redundancyAssetPath = buildMapContext.RedundancyList[i]; + AppendData(content, redundancyAssetPath); + } + + AppendData(content, ""); + AppendData(content, $"--构建列表--"); + for (int i = 0; i < buildMapContext.BundleInfos.Count; i++) + { + string bundleName = buildMapContext.BundleInfos[i].BundleName; + AppendData(content, bundleName); + } + + AppendData(content, ""); + AppendData(content, $"--内置文件列表--"); + foreach (var patchBundle in patchManifest.BundleList) + { + if (patchBundle.IsBuildin) + { + AppendData(content, patchBundle.BundleName); + } + } + + AppendData(content, ""); + AppendData(content, $"--非内置文件列表--"); + foreach (var patchBundle in patchManifest.BundleList) + { + if (patchBundle.IsBuildin == false) + { + AppendData(content, patchBundle.BundleName); + } + } + + AppendData(content, ""); + AppendData(content, $"--加密文件列表--"); + foreach (var patchBundle in patchManifest.BundleList) + { + if (patchBundle.IsEncrypted) + { + AppendData(content, patchBundle.BundleName); + } + } + + AppendData(content, ""); + AppendData(content, $"--原生文件列表--"); + foreach (var patchBundle in patchManifest.BundleList) + { + if (patchBundle.IsRawFile) + { + AppendData(content, patchBundle.BundleName); + } + } + + // 创建新文件 + File.WriteAllText(filePath, content.ToString(), Encoding.UTF8); + } + private void AppendData(StringBuilder sb, string data) + { + sb.Append(data); + sb.Append("\r\n"); + } + + private void GetBundleFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes) + { + fileCount = patchManifest.BundleList.Count; + fileBytes = 0; + foreach (var patchBundle in patchManifest.BundleList) + { + fileBytes += patchBundle.SizeBytes; + } + } + private void GetBuildinFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes) + { + fileCount = 0; + fileBytes = 0; + foreach (var patchBundle in patchManifest.BundleList) + { + if (patchBundle.IsBuildin) + { + fileCount++; + fileBytes += patchBundle.SizeBytes; + } + } + } + private void GetNotBuildinFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes) + { + fileCount = 0; + fileBytes = 0; + foreach (var patchBundle in patchManifest.BundleList) + { + if (patchBundle.IsBuildin == false) + { + fileCount++; + fileBytes += patchBundle.SizeBytes; + } + } + } + private void GetEncryptedFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes) + { + fileCount = 0; + fileBytes = 0; + foreach (var patchBundle in patchManifest.BundleList) + { + if (patchBundle.IsEncrypted) + { + fileCount++; + fileBytes += patchBundle.SizeBytes; + } + } + } + private void GetRawFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes) + { + fileCount = 0; + fileBytes = 0; + foreach (var patchBundle in patchManifest.BundleList) + { + if (patchBundle.IsRawFile) + { + fileCount++; + fileBytes += patchBundle.SizeBytes; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs.meta new file mode 100644 index 0000000..2687bdc --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1660a082a6449f4429fcca15e4383f0b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs new file mode 100644 index 0000000..7085c57 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs @@ -0,0 +1,94 @@ +using System; +using System.Linq; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using YooAsset.Utility; + +namespace YooAsset.Editor +{ + public class TaskEncryption : IBuildTask + { + public class EncryptionContext : IContextObject + { + public List EncryptList; + + /// + /// 检测是否为加密文件 + /// + public bool IsEncryptFile(string bundleName) + { + return EncryptList.Contains(bundleName); + } + } + + void IBuildTask.Run(BuildContext context) + { + var buildParameters = context.GetContextObject(); + var buildMapContext = context.GetContextObject(); + + var encrypter = CreateAssetEncrypter(); + List encryptList = EncryptFiles(encrypter, buildParameters, buildMapContext); + + EncryptionContext encryptionContext = new EncryptionContext(); + encryptionContext.EncryptList = encryptList; + context.SetContextObject(encryptionContext); + } + + /// + /// 创建加密类 + /// + /// 如果没有定义类型,则返回NULL + private IAssetEncrypter CreateAssetEncrypter() + { + var types = AssemblyUtility.GetAssignableTypes(AssemblyUtility.UnityDefaultAssemblyEditorName, typeof(IAssetEncrypter)); + if (types.Count == 0) + return null; + if (types.Count != 1) + throw new Exception($"Found more {nameof(IAssetEncrypter)} types. We only support one."); + + UnityEngine.Debug.Log($"创建实例类 : {types[0].FullName}"); + return (IAssetEncrypter)Activator.CreateInstance(types[0]); + } + + /// + /// 加密文件 + /// + private List EncryptFiles(IAssetEncrypter encrypter, AssetBundleBuilder.BuildParametersContext buildParameters, TaskGetBuildMap.BuildMapContext buildMapContext) + { + // 加密资源列表 + List encryptList = new List(); + + // 如果没有设置加密类 + if (encrypter == null) + return encryptList; + + UnityEngine.Debug.Log($"开始加密资源文件"); + int progressValue = 0; + foreach (var bundleInfo in buildMapContext.BundleInfos) + { + var bundleName = bundleInfo.BundleName; + string filePath = $"{buildParameters.PipelineOutputDirectory}/{bundleName}"; + if (encrypter.Check(filePath)) + { + encryptList.Add(bundleName); + + // 注意:通过判断文件合法性,规避重复加密一个文件 + byte[] fileData = File.ReadAllBytes(filePath); + if (EditorTools.CheckBundleFileValid(fileData)) + { + byte[] bytes = encrypter.Encrypt(fileData); + File.WriteAllBytes(filePath, bytes); + UnityEngine.Debug.Log($"文件加密完成:{filePath}"); + } + } + + // 进度条 + EditorTools.DisplayProgressBar("加密资源包", ++progressValue, buildMapContext.BundleInfos.Count); + } + EditorTools.ClearProgressBar(); + + return encryptList; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs.meta new file mode 100644 index 0000000..6561a79 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c78d471226b9c8d429a2d962370f480b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs new file mode 100644 index 0000000..fd3163b --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs @@ -0,0 +1,331 @@ +using System; +using System.IO; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using YooAsset.Utility; + +namespace YooAsset.Editor +{ + public class TaskGetBuildMap : IBuildTask + { + public class BuildMapContext : IContextObject + { + /// + /// 资源包列表 + /// + public readonly List BundleInfos = new List(1000); + + /// + /// 冗余的资源列表 + /// + public readonly List RedundancyList = new List(1000); + + + /// + /// 添加一个打包资源 + /// + public void PackAsset(BuildAssetInfo assetInfo) + { + if (TryGetBundleInfo(assetInfo.GetBundleName(), out BuildBundleInfo bundleInfo)) + { + bundleInfo.PackAsset(assetInfo); + } + else + { + BuildBundleInfo newBundleInfo = new BuildBundleInfo(assetInfo.BundleLabel, assetInfo.BundleVariant); + newBundleInfo.PackAsset(assetInfo); + BundleInfos.Add(newBundleInfo); + } + } + + /// + /// 获取所有的打包资源 + /// + public List GetAllAssets() + { + List result = new List(BundleInfos.Count); + foreach (var bundleInfo in BundleInfos) + { + result.AddRange(bundleInfo.Assets); + } + return result; + } + + /// + /// 获取AssetBundle内包含的标记列表 + /// + public string[] GetAssetTags(string bundleFullName) + { + if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo)) + { + return bundleInfo.GetAssetTags(); + } + throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}"); + } + + /// + /// 获取AssetBundle内收集的资源路径列表 + /// + public string[] GetCollectAssetPaths(string bundleFullName) + { + if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo)) + { + return bundleInfo.GetCollectAssetPaths(); + } + throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}"); + } + + /// + /// 获取AssetBundle内构建的资源路径列表 + /// + public string[] GetBuildinAssetPaths(string bundleFullName) + { + if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo)) + { + return bundleInfo.GetBuildinAssetPaths(); + } + throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}"); + } + + /// + /// 获取构建管线里需要的数据 + /// + public UnityEditor.AssetBundleBuild[] GetPipelineBuilds() + { + List builds = new List(BundleInfos.Count); + foreach (var bundleInfo in BundleInfos) + { + if (bundleInfo.IsRawFile == false) + builds.Add(bundleInfo.CreatePipelineBuild()); + } + return builds.ToArray(); + } + + /// + /// 检测是否包含BundleName + /// + public bool IsContainsBundle(string bundleFullName) + { + return TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo); + } + + private bool TryGetBundleInfo(string bundleFullName, out BuildBundleInfo result) + { + foreach (var bundleInfo in BundleInfos) + { + if (bundleInfo.BundleName == bundleFullName) + { + result = bundleInfo; + return true; + } + } + result = null; + return false; + } + } + + + void IBuildTask.Run(BuildContext context) + { + var buildParametersContext = context.GetContextObject(); + BuildMapContext buildMapContext = new BuildMapContext(); + context.SetContextObject(buildMapContext); + SetupBuildMap(buildMapContext, buildParametersContext); + + // 检测构建结果 + CheckBuildMapContent(buildMapContext); + } + + /// + /// 组织构建的资源包 + /// + private void SetupBuildMap(BuildMapContext buildMapContext, AssetBundleBuilder.BuildParametersContext buildParameters) + { + Dictionary buildAssets = new Dictionary(); + + // 1. 获取主动收集的资源 + List allCollectInfos = AssetBundleCollectorSettingData.GetAllCollectAssets(); + + // 2. 对收集的资源进行依赖分析 + int progressValue = 0; + foreach (AssetCollectInfo collectInfo in allCollectInfos) + { + string mainAssetPath = collectInfo.AssetPath; + + // 获取所有依赖资源 + List depends = GetAllDependencies(mainAssetPath); + for (int i = 0; i < depends.Count; i++) + { + string assetPath = depends[i].AssetPath; + + // 如果已经存在,则增加该资源的依赖计数 + if (buildAssets.ContainsKey(assetPath)) + { + buildAssets[assetPath].DependCount++; + } + else + { + buildAssets.Add(assetPath, depends[i]); + } + + // 添加资源标记 + buildAssets[assetPath].AddAssetTags(collectInfo.AssetTags); + + // 注意:检测是否为主动收集资源 + if (assetPath == mainAssetPath) + { + buildAssets[mainAssetPath].IsCollectAsset = true; + buildAssets[mainAssetPath].IsRawAsset = collectInfo.IsRawAsset; + } + } + + // 添加所有的依赖资源列表 + // 注意:不包括自己 + var allDependAssetInfos = new List(depends.Count); + for (int i = 0; i < depends.Count; i++) + { + string assetPath = depends[i].AssetPath; + if (assetPath != mainAssetPath) + allDependAssetInfos.Add(buildAssets[assetPath]); + } + buildAssets[mainAssetPath].SetAllDependAssetInfos(allDependAssetInfos); + + EditorTools.DisplayProgressBar("依赖文件分析", ++progressValue, allCollectInfos.Count); + } + EditorTools.ClearProgressBar(); + + // 3. 移除零依赖的资源 + var redundancy = CreateAssetRedundancy(); + List undependentAssets = new List(); + foreach (KeyValuePair pair in buildAssets) + { + var buildAssetInfo = pair.Value; + if (buildAssetInfo.IsCollectAsset) + continue; + + if (buildAssetInfo.DependCount == 0) + { + undependentAssets.Add(buildAssetInfo); + continue; + } + + // 冗余扩展 + if(redundancy != null) + { + if(redundancy.Check(buildAssetInfo.AssetPath)) + { + undependentAssets.Add(buildAssetInfo); + buildMapContext.RedundancyList.Add(buildAssetInfo.AssetPath); + continue; + } + } + + // 冗余机制 + if (buildParameters.Parameters.ApplyRedundancy) + { + if (AssetBundleCollectorSettingData.HasCollector(buildAssetInfo.AssetPath) == false) + { + undependentAssets.Add(buildAssetInfo); + buildMapContext.RedundancyList.Add(buildAssetInfo.AssetPath); + } + } + } + foreach (var assetInfo in undependentAssets) + { + buildAssets.Remove(assetInfo.AssetPath); + } + + // 4. 设置资源包名 + progressValue = 0; + foreach (KeyValuePair pair in buildAssets) + { + var assetInfo = pair.Value; + var bundleLabel = AssetBundleCollectorSettingData.GetBundleLabel(assetInfo.AssetPath); + if (assetInfo.IsRawAsset) + assetInfo.SetBundleLabelAndVariant(bundleLabel, ResourceSettingData.Setting.RawFileVariant); + else + assetInfo.SetBundleLabelAndVariant(bundleLabel, ResourceSettingData.Setting.AssetBundleFileVariant); + EditorTools.DisplayProgressBar("设置资源包名", ++progressValue, buildAssets.Count); + } + EditorTools.ClearProgressBar(); + + // 4. 构建资源包 + var allAssets = buildAssets.Values.ToList(); + if (allAssets.Count == 0) + throw new Exception("构建的资源列表不能为空"); + UnityEngine.Debug.Log($"构建的资源列表里总共有{allAssets.Count}个资源"); + foreach (var assetInfo in allAssets) + { + buildMapContext.PackAsset(assetInfo); + } + } + + /// + /// 获取指定资源依赖的所有资源列表 + /// 注意:返回列表里已经包括主资源自己 + /// + private List GetAllDependencies(string mainAssetPath) + { + List result = new List(); + string[] depends = AssetDatabase.GetDependencies(mainAssetPath, true); + foreach (string assetPath in depends) + { + if (AssetBundleCollectorSettingData.IsValidateAsset(assetPath)) + { + BuildAssetInfo assetInfo = new BuildAssetInfo(assetPath); + result.Add(assetInfo); + } + } + return result; + } + + /// + /// 检测构建结果 + /// + private void CheckBuildMapContent(BuildMapContext buildMapContext) + { + foreach (var bundleInfo in buildMapContext.BundleInfos) + { + // 注意:原生文件资源包只能包含一个原生文件 + bool isRawFile = bundleInfo.IsRawFile; + if (isRawFile) + { + if (bundleInfo.Assets.Count != 1) + throw new Exception("The bundle does not support multiple raw asset : {bundleInfo.BundleName}"); + continue; + } + + // 注意:原生文件不能被其它资源文件依赖 + foreach (var assetInfo in bundleInfo.Assets) + { + if (assetInfo.AllDependAssetInfos != null) + { + foreach (var dependAssetInfo in assetInfo.AllDependAssetInfos) + { + if (dependAssetInfo.IsRawAsset) + throw new Exception($"{assetInfo.AssetPath} can not depend raw asset : {dependAssetInfo.AssetPath}"); + } + } + } + } + } + + /// + /// 创建冗余类 + /// + /// 如果没有定义类型,则返回NULL + private IAssetRedundancy CreateAssetRedundancy() + { + var types = AssemblyUtility.GetAssignableTypes(AssemblyUtility.UnityDefaultAssemblyEditorName, typeof(IAssetRedundancy)); + if (types.Count == 0) + return null; + if (types.Count != 1) + throw new Exception($"Found more {nameof(IAssetRedundancy)} types. We only support one."); + + UnityEngine.Debug.Log($"创建实例类 : {types[0].FullName}"); + return (IAssetRedundancy)Activator.CreateInstance(types[0]); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs.meta new file mode 100644 index 0000000..9ec942f --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: baee18c5c7b76584b90413bf20fdae9a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs new file mode 100644 index 0000000..8bba665 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs @@ -0,0 +1,72 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; + +namespace YooAsset.Editor +{ + public class TaskPrepare : IBuildTask + { + void IBuildTask.Run(BuildContext context) + { + var buildParameters = context.GetContextObject(); + + // 检测构建平台是否合法 + if (buildParameters.Parameters.BuildTarget == BuildTarget.NoTarget) + throw new Exception("请选择目标平台"); + + // 检测构建版本是否合法 + if (buildParameters.Parameters.BuildVersion <= 0) + throw new Exception("请先设置版本号"); + + // 检测输出目录是否为空 + if (string.IsNullOrEmpty(buildParameters.PipelineOutputDirectory)) + throw new Exception("输出目录不能为空"); + + // 检测资源收集配置文件 + if (AssetBundleCollectorSettingData.GetCollecterCount() == 0) + throw new Exception("配置的资源收集路径为空"); + + // 增量更新时候的必要检测 + if (buildParameters.Parameters.IsForceRebuild == false) + { + // 检测历史版本是否存在 + if (AssetBundleBuilderHelper.HasAnyPackageVersion(buildParameters.Parameters.BuildTarget, buildParameters.Parameters.OutputRoot) == false) + throw new Exception("没有发现任何历史版本,请尝试强制重建"); + + // 检测构建版本是否合法 + int maxPackageVersion = AssetBundleBuilderHelper.GetMaxPackageVersion(buildParameters.Parameters.BuildTarget, buildParameters.Parameters.OutputRoot); + if (buildParameters.Parameters.BuildVersion <= maxPackageVersion) + throw new Exception("构建版本不能小于历史版本"); + + // 检测补丁包是否已经存在 + string packageDirectory = buildParameters.GetPackageDirectory(); + if (Directory.Exists(packageDirectory)) + throw new Exception($"补丁包已经存在:{packageDirectory}"); + + // 检测内置资源标记是否一致 + PatchManifest oldPatchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory); + if (buildParameters.Parameters.BuildinTags != oldPatchManifest.BuildinTags) + throw new Exception($"增量更新时内置资源标记必须一致:{buildParameters.Parameters.BuildinTags} != {oldPatchManifest.BuildinTags}"); + } + + // 如果是强制重建 + if (buildParameters.Parameters.IsForceRebuild) + { + // 删除平台总目录 + string platformDirectory = $"{buildParameters.Parameters.OutputRoot}/{buildParameters.Parameters.BuildTarget}"; + if (EditorTools.DeleteDirectory(platformDirectory)) + { + UnityEngine.Debug.Log($"删除平台总目录:{platformDirectory}"); + } + } + + // 如果输出目录不存在 + if (EditorTools.CreateDirectory(buildParameters.PipelineOutputDirectory)) + { + UnityEngine.Debug.Log($"创建输出目录:{buildParameters.PipelineOutputDirectory}"); + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs.meta new file mode 100644 index 0000000..ba61476 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0dee755cccd608d48b6581ec33c99b39 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs new file mode 100644 index 0000000..759106f --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs @@ -0,0 +1,13 @@ + +namespace YooAsset.Editor +{ + /// + /// AssetBundle压缩选项 + /// + public enum ECompressOption + { + Uncompressed = 0, + LZMA, + LZ4, + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs.meta new file mode 100644 index 0000000..e4fb021 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1e8ac0dfc77bd1b4697db63d52ab4c6e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs new file mode 100644 index 0000000..64f8aac --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs @@ -0,0 +1,18 @@ + +namespace YooAsset.Editor +{ + public interface IAssetEncrypter + { + /// + /// 检测是否需要加密 + /// + bool Check(string filePath); + + /// + /// 加密方法 + /// + /// 要加密的文件数据 + /// 返回加密后的字节数据 + byte[] Encrypt(byte[] fileData); + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs.meta new file mode 100644 index 0000000..3991708 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 04491137351983348959c00ec4ee226a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs new file mode 100644 index 0000000..e49e3f8 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs @@ -0,0 +1,11 @@ + +namespace YooAsset.Editor +{ + public interface IAssetRedundancy + { + /// + /// 检测是否冗余 + /// + bool Check(string filePath); + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs.meta new file mode 100644 index 0000000..9a9b8f5 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d211989e17a617741a1963b8523fe9c7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleCollector.meta b/Assets/YooAsset/Editor/AssetBundleCollector.meta new file mode 100644 index 0000000..9afb783 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0dbd5f557007d574e80c42d4a15421a9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs new file mode 100644 index 0000000..f55d2dc --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using YooAsset.Utility; + +namespace YooAsset.Editor +{ + public class AssetBundleCollectorSetting : ScriptableObject + { + [Serializable] + public class Collector + { + /// + /// 收集的文件夹路径 + /// + public string CollectDirectory = string.Empty; + + /// + /// 打包规则类名 + /// + public string PackRuleName = string.Empty; + + /// + /// 过滤规则类名 + /// + public string FilterRuleName = string.Empty; + + /// + /// 不写入资源路径到清单文件 + /// 注意:对于不依赖于代码加载的收集资源,可以禁止写入资源路径信息到清单文件 + /// + public bool DontWriteAssetPath = false; + + /// + /// 资源标记 + /// + public string AssetTags = string.Empty; + + /// + /// 获取资源标记列表 + /// + public List GetAssetTags() + { + return StringUtility.StringToStringList(AssetTags, ';'); + } + + public override string ToString() + { + return $"Directory : {CollectDirectory} | {PackRuleName} | {FilterRuleName} | {DontWriteAssetPath} | {AssetTags}"; + } + } + + /// + /// 是否收集全路径的着色器 + /// + public bool IsCollectAllShaders = false; + + /// + /// 收集的着色器Bundle名称 + /// + public string ShadersBundleName = "myshaders"; + + /// + /// 收集列表 + /// + public List Collectors = new List(); + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs.meta new file mode 100644 index 0000000..7bdd4a4 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4cc54f0173b27fb40aee29e7eb3364da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs new file mode 100644 index 0000000..cc4b03a --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs @@ -0,0 +1,478 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEditor; +using YooAsset.Utility; + +namespace YooAsset.Editor +{ + public static class AssetBundleCollectorSettingData + { + private static readonly Dictionary _cachePackRuleTypes = new Dictionary(); + private static readonly Dictionary _cachePackRuleInstance = new Dictionary(); + + private static readonly Dictionary _cacheFilterRuleTypes = new Dictionary(); + private static readonly Dictionary _cacheFilterRuleInstance = new Dictionary(); + + + private static AssetBundleCollectorSetting _setting = null; + public static AssetBundleCollectorSetting Setting + { + get + { + if (_setting == null) + LoadSettingData(); + return _setting; + } + } + + public static List GetPackRuleNames() + { + if (_setting == null) + LoadSettingData(); + + List names = new List(); + foreach (var pair in _cachePackRuleTypes) + { + names.Add(pair.Key); + } + return names; + } + public static List GetFilterRuleNames() + { + if (_setting == null) + LoadSettingData(); + + List names = new List(); + foreach (var pair in _cacheFilterRuleTypes) + { + names.Add(pair.Key); + } + return names; + } + public static bool HasPackRuleName(string ruleName) + { + foreach (var pair in _cachePackRuleTypes) + { + if (pair.Key == ruleName) + return true; + } + return false; + } + public static bool HasFilterRuleName(string ruleName) + { + foreach (var pair in _cacheFilterRuleTypes) + { + if (pair.Key == ruleName) + return true; + } + return false; + } + + /// + /// 加载配置文件 + /// + private static void LoadSettingData() + { + // 加载配置文件 + _setting = AssetDatabase.LoadAssetAtPath(EditorDefine.AssetBundleCollectorSettingFilePath); + if (_setting == null) + { + Debug.LogWarning($"Create new {nameof(AssetBundleCollectorSetting)}.asset : {EditorDefine.AssetBundleCollectorSettingFilePath}"); + _setting = ScriptableObject.CreateInstance(); + EditorTools.CreateFileDirectory(EditorDefine.AssetBundleCollectorSettingFilePath); + AssetDatabase.CreateAsset(Setting, EditorDefine.AssetBundleCollectorSettingFilePath); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + else + { + Debug.Log($"Load {nameof(AssetBundleCollectorSetting)}.asset ok"); + } + + // IPackRule + { + // 清空缓存集合 + _cachePackRuleTypes.Clear(); + _cachePackRuleInstance.Clear(); + + // 获取所有类型 + List types = new List(100) + { + typeof(PackExplicit), + typeof(PackDirectory), + typeof(PackRawFile), + }; + var customTypes = AssemblyUtility.GetAssignableTypes(AssemblyUtility.UnityDefaultAssemblyEditorName, typeof(IPackRule)); + types.AddRange(customTypes); + for (int i = 0; i < types.Count; i++) + { + Type type = types[i]; + if (_cachePackRuleTypes.ContainsKey(type.Name) == false) + _cachePackRuleTypes.Add(type.Name, type); + } + } + + // IFilterRule + { + // 清空缓存集合 + _cacheFilterRuleTypes.Clear(); + _cacheFilterRuleInstance.Clear(); + + // 获取所有类型 + List types = new List(100) + { + typeof(CollectAll), + typeof(CollectScene), + typeof(CollectPrefab), + typeof(CollectSprite) + }; + var customTypes = AssemblyUtility.GetAssignableTypes(AssemblyUtility.UnityDefaultAssemblyEditorName, typeof(IFilterRule)); + types.AddRange(customTypes); + for (int i = 0; i < types.Count; i++) + { + Type type = types[i]; + if (_cacheFilterRuleTypes.ContainsKey(type.Name) == false) + _cacheFilterRuleTypes.Add(type.Name, type); + } + } + } + + /// + /// 存储文件 + /// + public static void SaveFile() + { + if (Setting != null) + { + EditorUtility.SetDirty(Setting); + AssetDatabase.SaveAssets(); + } + } + + // 着色器相关 + public static void ModifyShader(bool isCollectAllShaders, string shadersBundleName) + { + if (string.IsNullOrEmpty(shadersBundleName)) + return; + Setting.IsCollectAllShaders = isCollectAllShaders; + Setting.ShadersBundleName = shadersBundleName; + SaveFile(); + } + + // 收集器相关 + public static void ClearAllCollector() + { + Setting.Collectors.Clear(); + SaveFile(); + } + public static void AddCollector(string directory, string packRuleName, string filterRuleName, bool dontWriteAssetPath, string assetTags, bool saveFile = true) + { + // 末尾添加路径分隔符号 + if (directory.EndsWith("/") == false) + directory = $"{directory}/"; + + // 检测收集器路径冲突 + if (CheckConflict(directory)) + return; + + // 检测资源标签 + if (dontWriteAssetPath && string.IsNullOrEmpty(assetTags) == false) + Debug.LogWarning($"Collector {directory} has asset tags : {assetTags}, It is not vliad when enable dontWriteAssetPath."); + + AssetBundleCollectorSetting.Collector element = new AssetBundleCollectorSetting.Collector(); + element.CollectDirectory = directory; + element.PackRuleName = packRuleName; + element.FilterRuleName = filterRuleName; + element.DontWriteAssetPath = dontWriteAssetPath; + element.AssetTags = assetTags; + Setting.Collectors.Add(element); + + if (saveFile) + SaveFile(); + } + public static void RemoveCollector(string directory) + { + for (int i = 0; i < Setting.Collectors.Count; i++) + { + if (Setting.Collectors[i].CollectDirectory == directory) + { + Setting.Collectors.RemoveAt(i); + break; + } + } + SaveFile(); + } + public static void ModifyCollector(string directory, string packRuleName, string filterRuleName, bool dontWriteAssetPath, string assetTags) + { + // 检测资源标签 + if (dontWriteAssetPath && string.IsNullOrEmpty(assetTags) == false) + Debug.LogWarning($"Collector '{directory}' has asset tags '{assetTags}', It is invalid when enable dontWriteAssetPath."); + + for (int i = 0; i < Setting.Collectors.Count; i++) + { + var collector = Setting.Collectors[i]; + if (collector.CollectDirectory == directory) + { + collector.PackRuleName = packRuleName; + collector.FilterRuleName = filterRuleName; + collector.DontWriteAssetPath = dontWriteAssetPath; + collector.AssetTags = assetTags; + break; + } + } + SaveFile(); + } + public static bool IsContainsCollector(string directory) + { + for (int i = 0; i < Setting.Collectors.Count; i++) + { + if (Setting.Collectors[i].CollectDirectory == directory) + return true; + } + return false; + } + public static bool CheckConflict(string directory) + { + if (IsContainsCollector(directory)) + { + Debug.LogError($"Asset collector already existed : {directory}"); + return true; + } + + for (int i = 0; i < Setting.Collectors.Count; i++) + { + var wrapper = Setting.Collectors[i]; + string compareDirectory = wrapper.CollectDirectory; + if (directory.StartsWith(compareDirectory)) + { + Debug.LogError($"New asset collector \"{directory}\" conflict with \"{compareDirectory}\""); + return true; + } + if (compareDirectory.StartsWith(directory)) + { + Debug.LogError($"New asset collector {directory} conflict with {compareDirectory}"); + return true; + } + } + + return false; + } + + /// + /// 获取所有的DLC标记 + /// + public static List GetAllAssetTags() + { + List result = new List(); + for (int i = 0; i < Setting.Collectors.Count; i++) + { + var tags = Setting.Collectors[i].GetAssetTags(); + foreach (var tag in tags) + { + if (result.Contains(tag) == false) + result.Add(tag); + } + } + return result; + } + + /// + /// 获取收集器总数 + /// + public static int GetCollecterCount() + { + return Setting.Collectors.Count; + } + + /// + /// 获取所有的收集路径 + /// + public static List GetAllCollectDirectory() + { + List result = new List(); + for (int i = 0; i < Setting.Collectors.Count; i++) + { + AssetBundleCollectorSetting.Collector collector = Setting.Collectors[i]; + result.Add(collector.CollectDirectory); + } + return result; + } + + /// + /// 获取所有收集的资源 + /// 注意:跳过了不写入资源路径的收集器 + /// + public static List GetAllCollectAssets() + { + Dictionary result = new Dictionary(10000); + for (int i = 0; i < Setting.Collectors.Count; i++) + { + AssetBundleCollectorSetting.Collector collector = Setting.Collectors[i]; + + // 注意:跳过不需要写入资源路径的收集器 + if (collector.DontWriteAssetPath) + continue; + + bool isRawAsset = collector.PackRuleName == nameof(PackRawFile); + string[] findAssets = EditorTools.FindAssets(EAssetSearchType.All, collector.CollectDirectory); + foreach (string assetPath in findAssets) + { + if (IsValidateAsset(assetPath) == false) + continue; + if (IsCollectAsset(assetPath, collector.FilterRuleName) == false) + continue; + + if (result.ContainsKey(assetPath) == false) + { + var assetCollectInfo = new AssetCollectInfo(assetPath, collector.GetAssetTags(), isRawAsset); + result.Add(assetPath, assetCollectInfo); + } + } + } + return result.Values.ToList(); + } + private static bool IsCollectAsset(string assetPath, string filterRuleName) + { + if (Setting.IsCollectAllShaders) + { + Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath); + if (assetType == typeof(UnityEngine.Shader)) + return true; + } + + // 根据规则设置获取标签名称 + IFilterRule filterRuleInstance = GetFilterRuleInstance(filterRuleName); + return filterRuleInstance.IsCollectAsset(assetPath); + } + + /// + /// 检测资源路径是否被收集器覆盖 + /// + public static bool HasCollector(string assetPath) + { + // 如果收集全路径着色器 + if (Setting.IsCollectAllShaders) + { + System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath); + if (assetType == typeof(UnityEngine.Shader)) + return true; + } + + for (int i = 0; i < Setting.Collectors.Count; i++) + { + AssetBundleCollectorSetting.Collector collector = Setting.Collectors[i]; + if (assetPath.StartsWith(collector.CollectDirectory)) + return true; + } + + return false; + } + + /// + /// 检测资源是否有效 + /// + public static bool IsValidateAsset(string assetPath) + { + if (assetPath.StartsWith("Assets/") == false && assetPath.StartsWith("Packages/") == false) + return false; + + if (AssetDatabase.IsValidFolder(assetPath)) + return false; + + // 注意:忽略编辑器下的类型资源 + Type type = AssetDatabase.GetMainAssetTypeAtPath(assetPath); + if (type == typeof(LightingDataAsset)) + return false; + + string ext = System.IO.Path.GetExtension(assetPath); + if (ext == "" || ext == ".dll" || ext == ".cs" || ext == ".js" || ext == ".boo" || ext == ".meta") + return false; + + return true; + } + + /// + /// 获取资源包名 + /// + public static string GetBundleLabel(string assetPath) + { + // 如果收集全路径着色器 + if (Setting.IsCollectAllShaders) + { + System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath); + if (assetType == typeof(UnityEngine.Shader)) + { + return EditorTools.GetRegularPath(Setting.ShadersBundleName); + } + } + + // 获取收集器 + AssetBundleCollectorSetting.Collector findCollector = null; + for (int i = 0; i < Setting.Collectors.Count; i++) + { + AssetBundleCollectorSetting.Collector collector = Setting.Collectors[i]; + if (assetPath.StartsWith(collector.CollectDirectory)) + { + findCollector = collector; + break; + } + } + + // 如果没有找到收集器 + string bundleLabel; + if (findCollector == null) + { + IPackRule defaultInstance = new PackDirectory(); + bundleLabel = defaultInstance.GetAssetBundleLabel(assetPath); + } + else + { + // 根据规则设置获取标签名称 + IPackRule getInstance = GetPackRuleInstance(findCollector.PackRuleName); + bundleLabel = getInstance.GetAssetBundleLabel(assetPath); + } + + // 返回包名 + return EditorTools.GetRegularPath(bundleLabel); + } + + private static IPackRule GetPackRuleInstance(string ruleName) + { + if (_cachePackRuleInstance.TryGetValue(ruleName, out IPackRule instance)) + return instance; + + // 如果不存在创建类的实例 + if (_cachePackRuleTypes.TryGetValue(ruleName, out Type type)) + { + instance = (IPackRule)Activator.CreateInstance(type); + _cachePackRuleInstance.Add(ruleName, instance); + return instance; + } + else + { + throw new Exception($"{nameof(IPackRule)}类型无效:{ruleName}"); + } + } + private static IFilterRule GetFilterRuleInstance(string ruleName) + { + if (_cacheFilterRuleInstance.TryGetValue(ruleName, out IFilterRule instance)) + return instance; + + // 如果不存在创建类的实例 + if (_cacheFilterRuleTypes.TryGetValue(ruleName, out Type type)) + { + instance = (IFilterRule)Activator.CreateInstance(type); + _cacheFilterRuleInstance.Add(ruleName, instance); + return instance; + } + else + { + throw new Exception($"{nameof(IFilterRule)}类型无效:{ruleName}"); + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs.meta new file mode 100644 index 0000000..2bd8874 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5d4d2a4e23c5e0646a07add04fd8e18a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs new file mode 100644 index 0000000..5d80dd4 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs @@ -0,0 +1,243 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; + +namespace YooAsset.Editor +{ + public class AssetBundleCollectorWindow : EditorWindow + { + static AssetBundleCollectorWindow _thisInstance; + + [MenuItem("YooAsset/AssetBundle Collector", false, 101)] + static void ShowWindow() + { + if (_thisInstance == null) + { + _thisInstance = EditorWindow.GetWindow(typeof(AssetBundleCollectorWindow), false, "资源包收集工具", true) as AssetBundleCollectorWindow; + _thisInstance.minSize = new Vector2(800, 600); + } + _thisInstance.Show(); + } + + /// + /// 上次打开的文件夹路径 + /// + private string _lastOpenFolderPath = "Assets/"; + + // GUI相关 + private const float GuiDirecotryMinSize = 300f; + private const float GuiDirecotryMaxSize = 800f; + private const float GuiPackRuleSize = 130f; + private const float GuiFilterRuleSize = 130f; + private const float GuiDontWriteAssetPathSize = 130f; + private const float GuiAssetTagsMinSize = 100f; + private const float GuiAssetTagsMaxSize = 300f; + private const float GuiBtnSize = 40f; + private Vector2 _scrollPos = Vector2.zero; + + // 初始化相关 + private string[] _packRuleArray = null; + private string[] _filterRuleArray = null; + private bool _isInit = false; + + + private void Init() + { + List packRuleNames = AssetBundleCollectorSettingData.GetPackRuleNames(); + _packRuleArray = packRuleNames.ToArray(); + + List filterRuleNames = AssetBundleCollectorSettingData.GetFilterRuleNames(); + _filterRuleArray = filterRuleNames.ToArray(); + } + private int PackRuleNameToIndex(string name) + { + for (int i = 0; i < _packRuleArray.Length; i++) + { + if (_packRuleArray[i] == name) + return i; + } + return 0; + } + private string IndexToPackRuleName(int index) + { + for (int i = 0; i < _packRuleArray.Length; i++) + { + if (i == index) + return _packRuleArray[i]; + } + return string.Empty; + } + private int FilterRuleNameToIndex(string name) + { + for (int i = 0; i < _filterRuleArray.Length; i++) + { + if (_filterRuleArray[i] == name) + return i; + } + return 0; + } + private string IndexToFilterRuleName(int index) + { + for (int i = 0; i < _filterRuleArray.Length; i++) + { + if (i == index) + return _filterRuleArray[i]; + } + return string.Empty; + } + + private void OnGUI() + { + if (_isInit == false) + { + _isInit = true; + Init(); + } + + OnDrawShader(); + OnDrawHeadBar(); + OnDrawCollector(); + } + private void OnDrawShader() + { + bool isCollectAllShader = AssetBundleCollectorSettingData.Setting.IsCollectAllShaders; + string shadersBundleName = AssetBundleCollectorSettingData.Setting.ShadersBundleName; + + EditorGUILayout.Space(); + + bool newToggleValue = EditorGUILayout.Toggle("收集所有着色器", isCollectAllShader); + if (newToggleValue != isCollectAllShader) + { + isCollectAllShader = newToggleValue; + AssetBundleCollectorSettingData.ModifyShader(isCollectAllShader, shadersBundleName); + } + + if (isCollectAllShader) + { + string newTextValue = EditorGUILayout.TextField("AssetBundle名称", shadersBundleName, GUILayout.MaxWidth(300)); + if (newTextValue != shadersBundleName) + { + shadersBundleName = newTextValue; + AssetBundleCollectorSettingData.ModifyShader(isCollectAllShader, shadersBundleName); + } + } + } + private void OnDrawHeadBar() + { + EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Directory", GUILayout.MinWidth(GuiDirecotryMinSize), GUILayout.MaxWidth(GuiDirecotryMaxSize)); + EditorGUILayout.LabelField("PackRule", GUILayout.MinWidth(GuiPackRuleSize), GUILayout.MaxWidth(GuiPackRuleSize)); + EditorGUILayout.LabelField("FilterRule", GUILayout.MinWidth(GuiFilterRuleSize), GUILayout.MaxWidth(GuiFilterRuleSize)); + EditorGUILayout.LabelField("DontWriteAssetPath", GUILayout.MinWidth(GuiDontWriteAssetPathSize), GUILayout.MaxWidth(GuiDontWriteAssetPathSize)); + EditorGUILayout.LabelField("AssetTags", GUILayout.MinWidth(GuiAssetTagsMinSize), GUILayout.MaxWidth(GuiAssetTagsMaxSize)); + EditorGUILayout.LabelField("", GUILayout.MinWidth(GuiBtnSize), GUILayout.MaxWidth(GuiBtnSize)); + EditorGUILayout.EndHorizontal(); + } + private void OnDrawCollector() + { + // 列表显示 + EditorGUILayout.Space(); + _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos); + for (int i = 0; i < AssetBundleCollectorSettingData.Setting.Collectors.Count; i++) + { + var collector = AssetBundleCollectorSettingData.Setting.Collectors[i]; + string directory = collector.CollectDirectory; + string packRuleName = collector.PackRuleName; + string filterRuleName = collector.FilterRuleName; + bool dontWriteAssetPath = collector.DontWriteAssetPath; + string assetTags = collector.AssetTags; + + EditorGUILayout.BeginHorizontal(); + { + // Directory + EditorGUILayout.LabelField(directory, GUILayout.MinWidth(GuiDirecotryMinSize), GUILayout.MaxWidth(GuiDirecotryMaxSize)); + + // IPackRule + { + int index = PackRuleNameToIndex(packRuleName); + int newIndex = EditorGUILayout.Popup(index, _packRuleArray, GUILayout.MinWidth(GuiPackRuleSize), GUILayout.MaxWidth(GuiPackRuleSize)); + if (newIndex != index) + { + packRuleName = IndexToPackRuleName(newIndex); + AssetBundleCollectorSettingData.ModifyCollector(directory, packRuleName, filterRuleName, dontWriteAssetPath, assetTags); + } + } + + // IFilterRule + { + int index = FilterRuleNameToIndex(filterRuleName); + int newIndex = EditorGUILayout.Popup(index, _filterRuleArray, GUILayout.MinWidth(GuiFilterRuleSize), GUILayout.MaxWidth(GuiFilterRuleSize)); + if (newIndex != index) + { + filterRuleName = IndexToFilterRuleName(newIndex); + AssetBundleCollectorSettingData.ModifyCollector(directory, packRuleName, filterRuleName, dontWriteAssetPath, assetTags); + } + } + + // DontWriteAssetPath + { + bool newToggleValue = EditorGUILayout.Toggle(dontWriteAssetPath, GUILayout.MinWidth(GuiDontWriteAssetPathSize), GUILayout.MaxWidth(GuiDontWriteAssetPathSize)); + if (newToggleValue != dontWriteAssetPath) + { + dontWriteAssetPath = newToggleValue; + AssetBundleCollectorSettingData.ModifyCollector(directory, packRuleName, filterRuleName, dontWriteAssetPath, assetTags); + } + } + + // AssetTags + { + if (collector.DontWriteAssetPath) + { + EditorGUILayout.LabelField(assetTags, GUILayout.MinWidth(GuiAssetTagsMinSize), GUILayout.MaxWidth(GuiAssetTagsMaxSize)); + } + else + { + string newTextValue = EditorGUILayout.TextField(assetTags, GUILayout.MinWidth(GuiAssetTagsMinSize), GUILayout.MaxWidth(GuiAssetTagsMaxSize)); + if (newTextValue != assetTags) + { + assetTags = newTextValue; + AssetBundleCollectorSettingData.ModifyCollector(directory, packRuleName, filterRuleName, dontWriteAssetPath, assetTags); + } + } + } + + if (GUILayout.Button("-", GUILayout.MinWidth(GuiBtnSize), GUILayout.MaxWidth(GuiBtnSize))) + { + AssetBundleCollectorSettingData.RemoveCollector(directory); + break; + } + } + EditorGUILayout.EndHorizontal(); + } + EditorGUILayout.EndScrollView(); + + // 添加按钮 + if (GUILayout.Button("+")) + { + string resultPath = EditorTools.OpenFolderPanel("Select Folder", _lastOpenFolderPath); + if (resultPath != null) + { + _lastOpenFolderPath = EditorTools.AbsolutePathToAssetPath(resultPath); + string defaultPackRuleName = nameof(PackExplicit); + string defaultFilterRuleName = nameof(CollectAll); + bool defaultDontWriteAssetPathValue = false; + string defaultAssetTag = string.Empty; + AssetBundleCollectorSettingData.AddCollector(_lastOpenFolderPath, defaultPackRuleName, defaultFilterRuleName, defaultDontWriteAssetPathValue, defaultAssetTag); + } + } + + // 导入配置按钮 + if (GUILayout.Button("Import Config")) + { + string resultPath = EditorTools.OpenFilePath("Select File", "Assets/", "xml"); + if (resultPath != null) + { + CollectorConfigImporter.ImportXmlConfig(resultPath); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs.meta new file mode 100644 index 0000000..78d3ed7 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cfca8094e2a905a4da05586b2d7fa919 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs b/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs new file mode 100644 index 0000000..6758b1d --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs @@ -0,0 +1,30 @@ +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset.Editor +{ + public class AssetCollectInfo + { + /// + /// 资源路径 + /// + public string AssetPath { private set; get; } + + /// + /// 资源标记列表 + /// + public List AssetTags { private set; get; } + + /// + /// 是否为原生资源 + /// + public bool IsRawAsset { private set; get; } + + public AssetCollectInfo(string assetPath, List assetTags, bool isRawAsset) + { + AssetPath = assetPath; + AssetTags = assetTags; + IsRawAsset = isRawAsset; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs.meta new file mode 100644 index 0000000..4e3c4c6 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b44b2cc585bc59c4d879d1edac67f7c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs b/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs new file mode 100644 index 0000000..b26cd66 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs @@ -0,0 +1,100 @@ +using System; +using System.IO; +using System.Xml; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using YooAsset.Utility; + +namespace YooAsset.Editor +{ + public static class CollectorConfigImporter + { + private class CollectWrapper + { + public string CollectDirectory; + public string PackRuleName; + public string FilterRuleName; + public bool DontWriteAssetPath; + public string AssetTags; + + public CollectWrapper(string directory, string packRuleName, string filterRuleName, bool dontWriteAssetPath, string assetTags) + { + CollectDirectory = directory; + PackRuleName = packRuleName; + FilterRuleName = filterRuleName; + DontWriteAssetPath = dontWriteAssetPath; + AssetTags = assetTags; + } + } + + public const string XmlCollector = "Collector"; + public const string XmlDirectory = "Directory"; + public const string XmlPackRule = "PackRule"; + public const string XmlFilterRule = "FilterRule"; + public const string XmlDontWriteAssetPath = "DontWriteAssetPath"; + public const string XmlAssetTags = "AssetTags"; + + public static void ImportXmlConfig(string filePath) + { + if (File.Exists(filePath) == false) + throw new FileNotFoundException(filePath); + + if (Path.GetExtension(filePath) != ".xml") + throw new Exception($"Only support xml : {filePath}"); + + List wrappers = new List(); + + // 加载文件 + XmlDocument xml = new XmlDocument(); + xml.Load(filePath); + + // 解析文件 + XmlElement root = xml.DocumentElement; + XmlNodeList nodeList = root.GetElementsByTagName(XmlCollector); + if (nodeList.Count == 0) + throw new Exception($"Not found any {XmlCollector}"); + foreach (XmlNode node in nodeList) + { + XmlElement collect = node as XmlElement; + string directory = collect.GetAttribute(XmlDirectory); + string packRuleName = collect.GetAttribute(XmlPackRule); + string filterRuleName = collect.GetAttribute(XmlFilterRule); + string dontWriteAssetPath = collect.GetAttribute(XmlDontWriteAssetPath); + string assetTags = collect.GetAttribute(XmlAssetTags); + + if (Directory.Exists(directory) == false) + throw new Exception($"Not found directory : {directory}"); + + if (collect.HasAttribute(XmlPackRule) == false) + throw new Exception($"Not found attribute {XmlPackRule} in collector : {directory}"); + if (collect.HasAttribute(XmlFilterRule) == false) + throw new Exception($"Not found attribute {XmlFilterRule} in collector : {directory}"); + if (collect.HasAttribute(XmlDontWriteAssetPath) == false) + throw new Exception($"Not found attribute {XmlDontWriteAssetPath} in collector : {directory}"); + if (collect.HasAttribute(XmlAssetTags) == false) + throw new Exception($"Not found attribute {XmlAssetTags} in collector : {directory}"); + + if (AssetBundleCollectorSettingData.HasPackRuleName(packRuleName) == false) + throw new Exception($"Invalid {nameof(IPackRule)} class type : {packRuleName}"); + if (AssetBundleCollectorSettingData.HasFilterRuleName(filterRuleName) == false) + throw new Exception($"Invalid {nameof(IFilterRule)} class type : {filterRuleName}"); + + bool dontWriteAssetPathValue = StringUtility.StringToBool(dontWriteAssetPath); + CollectWrapper collectWrapper = new CollectWrapper(directory, packRuleName, filterRuleName, dontWriteAssetPathValue, assetTags); + wrappers.Add(collectWrapper); + } + + // 导入配置数据 + AssetBundleCollectorSettingData.ClearAllCollector(); + foreach (var wrapper in wrappers) + { + AssetBundleCollectorSettingData.AddCollector(wrapper.CollectDirectory, wrapper.PackRuleName, wrapper.FilterRuleName, wrapper.DontWriteAssetPath, wrapper.AssetTags, false); + } + + // 保存配置数据 + AssetBundleCollectorSettingData.SaveFile(); + Debug.Log($"导入配置完毕,一共导入{wrappers.Count}个收集器。"); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs.meta new file mode 100644 index 0000000..241f964 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c35b6554a4bedd049a8868d9249846d7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs new file mode 100644 index 0000000..95adedb --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs @@ -0,0 +1,53 @@ +using UnityEngine; +using UnityEditor; +using System.IO; + +namespace YooAsset.Editor +{ + /// + /// 收集所有资源 + /// + public class CollectAll : IFilterRule + { + public bool IsCollectAsset(string assetPath) + { + return true; + } + } + + /// + /// 只收集场景 + /// + public class CollectScene : IFilterRule + { + public bool IsCollectAsset(string assetPath) + { + return Path.GetExtension(assetPath) == ".unity"; + } + } + + /// + /// 只收集预制体 + /// + public class CollectPrefab : IFilterRule + { + public bool IsCollectAsset(string assetPath) + { + return Path.GetExtension(assetPath) == ".prefab"; + } + } + + /// + /// 只收集精灵类型的资源 + /// + public class CollectSprite : IFilterRule + { + public bool IsCollectAsset(string assetPath) + { + if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(Sprite)) + return true; + else + return false; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs.meta new file mode 100644 index 0000000..df88655 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a74c81d149472fb4c960a1db1fd8accc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs new file mode 100644 index 0000000..9823a8a --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using UnityEditor; +using YooAsset.Utility; + +namespace YooAsset.Editor +{ + /// + /// 以文件路径作为AssetBundle标签名 + /// + public class PackExplicit : IPackRule + { + string IPackRule.GetAssetBundleLabel(string assetPath) + { + return StringUtility.RemoveExtension(assetPath); //"Assets/Config/test.txt" --> "Assets/Config/test" + } + } + + /// + /// 以父文件夹路径作为AssetBundle标签名 + /// 注意:该文件夹下所有资源被打到一个AssetBundle文件里 + /// + public class PackDirectory : IPackRule + { + string IPackRule.GetAssetBundleLabel(string assetPath) + { + return Path.GetDirectoryName(assetPath); //"Assets/Config/test.txt" --> "Assets/Config" + } + } + + /// + /// 原生文件打包模式 + /// 注意:原生文件打包支持:图片,音频,视频,文本 + /// + public class PackRawFile : IPackRule + { + string IPackRule.GetAssetBundleLabel(string assetPath) + { + string extension = StringUtility.RemoveFirstChar(Path.GetExtension(assetPath)); + if (extension == EAssetFileExtension.unity.ToString() || extension == EAssetFileExtension.prefab.ToString() || + extension == EAssetFileExtension.mat.ToString() || extension == EAssetFileExtension.controller.ToString() || + extension == EAssetFileExtension.fbx.ToString() || extension == EAssetFileExtension.anim.ToString() || + extension == EAssetFileExtension.shader.ToString()) + { + throw new Exception($"{nameof(PackRawFile)} is not support file estension : {extension}"); + } + + // 注意:原生文件只支持无依赖关系的资源 + string[] depends = AssetDatabase.GetDependencies(assetPath, true); + if (depends.Length != 1) + throw new Exception($"{nameof(PackRawFile)} is not support estension : {extension}"); + + return StringUtility.RemoveExtension(assetPath); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs.meta new file mode 100644 index 0000000..13b1c8c --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a924dc0a22fc104781bf9aaadd60c29 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs b/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs new file mode 100644 index 0000000..80b5972 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs @@ -0,0 +1,16 @@ + +namespace YooAsset.Editor +{ + /// + /// 资源过滤规则接口 + /// + public interface IFilterRule + { + /// + /// 是否为收集资源 + /// + /// 资源路径 + /// 如果收集该资源返回TRUE + bool IsCollectAsset(string assetPath); + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs.meta new file mode 100644 index 0000000..470ca48 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ffe1385deb0bd9844a514f1c2fd65e62 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs b/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs new file mode 100644 index 0000000..e48376e --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs @@ -0,0 +1,14 @@ + +namespace YooAsset.Editor +{ + /// + /// 资源打包规则接口 + /// + public interface IPackRule + { + /// + /// 获取资源的打包标签 + /// + string GetAssetBundleLabel(string assetPath); + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs.meta new file mode 100644 index 0000000..8094b86 --- /dev/null +++ b/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 577f7c1abcdf0a140958f1d1d44d6f40 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/EditorDefine.cs b/Assets/YooAsset/Editor/EditorDefine.cs new file mode 100644 index 0000000..69443ff --- /dev/null +++ b/Assets/YooAsset/Editor/EditorDefine.cs @@ -0,0 +1,53 @@ + +namespace YooAsset.Editor +{ + public class EditorDefine + { + /// + /// 资源包收集工具的配置文件存储路径 + /// + public const string AssetBundleCollectorSettingFilePath = "Assets/YooAssetSetting/AssetBundleCollectorSetting.asset"; + } + + /// + /// 资源搜索类型 + /// + public enum EAssetSearchType + { + All, + RuntimeAnimatorController, + AnimationClip, + AudioClip, + AudioMixer, + Font, + Material, + Mesh, + Model, + PhysicMaterial, + Prefab, + Scene, + Script, + Shader, + Sprite, + Texture, + VideoClip, + } + + /// + /// 资源文件格式 + /// + public enum EAssetFileExtension + { + prefab, //预制体 + unity, //场景 + fbx, //模型 + anim, //动画 + controller, //控制器 + png, //图片 + jpg, //图片 + mat, //材质球 + shader, //着色器 + ttf, //字体 + cs, //脚本 + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/EditorDefine.cs.meta b/Assets/YooAsset/Editor/EditorDefine.cs.meta new file mode 100644 index 0000000..e79619a --- /dev/null +++ b/Assets/YooAsset/Editor/EditorDefine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e33ff61240a851e4fbaea71d01a460cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/EditorTools.cs b/Assets/YooAsset/Editor/EditorTools.cs new file mode 100644 index 0000000..1be6273 --- /dev/null +++ b/Assets/YooAsset/Editor/EditorTools.cs @@ -0,0 +1,878 @@ +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Linq; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using UnityEngine; +using UnityEditor; +using UnityEditor.Animations; +using YooAsset.Utility; + +namespace YooAsset.Editor +{ + /// + /// 编辑器工具类 + /// + public static class EditorTools + { + #region NGUI + /// + /// Draw a distinctly different looking header label + /// + public static bool DrawHeader(string text) + { + return DrawHeader(text, text, false, true); + } + public static bool DrawHeader(string text, string key, bool forceOn, bool minimalistic) + { + bool state = EditorPrefs.GetBool(key, true); + + if (!minimalistic) GUILayout.Space(3f); + if (!forceOn && !state) GUI.backgroundColor = new Color(0.8f, 0.8f, 0.8f); + GUILayout.BeginHorizontal(); + GUI.changed = false; + + if (minimalistic) + { + if (state) text = "\u25BC" + (char)0x200a + text; + else text = "\u25BA" + (char)0x200a + text; + + GUILayout.BeginHorizontal(); + GUI.contentColor = EditorGUIUtility.isProSkin ? new Color(1f, 1f, 1f, 0.7f) : new Color(0f, 0f, 0f, 0.7f); + if (!GUILayout.Toggle(true, text, "PreToolbar2", GUILayout.MinWidth(20f))) state = !state; + GUI.contentColor = Color.white; + GUILayout.EndHorizontal(); + } + else + { + text = "" + text + ""; + if (state) text = "\u25BC " + text; + else text = "\u25BA " + text; + if (!GUILayout.Toggle(true, text, "dragtab", GUILayout.MinWidth(20f))) state = !state; + } + + if (GUI.changed) EditorPrefs.SetBool(key, state); + + if (!minimalistic) GUILayout.Space(2f); + GUILayout.EndHorizontal(); + GUI.backgroundColor = Color.white; + if (!forceOn && !state) GUILayout.Space(3f); + return state; + } + #endregion + + #region Assembly + /// + /// 调用私有的静态方法 + /// + /// 类的类型 + /// 类里要调用的方法名 + /// 调用方法传入的参数 + public static object InvokeNonPublicStaticMethod(System.Type type, string method, params object[] parameters) + { + var methodInfo = type.GetMethod(method, BindingFlags.NonPublic | BindingFlags.Static); + if (methodInfo == null) + { + UnityEngine.Debug.LogError($"{type.FullName} not found method : {method}"); + return null; + } + return methodInfo.Invoke(null, parameters); + } + + /// + /// 调用公开的静态方法 + /// + /// 类的类型 + /// 类里要调用的方法名 + /// 调用方法传入的参数 + public static object InvokePublicStaticMethod(System.Type type, string method, params object[] parameters) + { + var methodInfo = type.GetMethod(method, BindingFlags.Public | BindingFlags.Static); + if (methodInfo == null) + { + UnityEngine.Debug.LogError($"{type.FullName} not found method : {method}"); + return null; + } + return methodInfo.Invoke(null, parameters); + } + #endregion + + #region EditorUtility + /// + /// 搜集资源 + /// + /// 搜集的资源类型 + /// 指定搜索的文件夹列表 + /// 返回搜集到的资源路径列表 + public static string[] FindAssets(EAssetSearchType searchType, string[] searchInFolders) + { + // 注意:AssetDatabase.FindAssets()不支持末尾带分隔符的文件夹路径 + for (int i = 0; i < searchInFolders.Length; i++) + { + string folderPath = searchInFolders[i]; + searchInFolders[i] = folderPath.TrimEnd('/'); + } + + // 注意:获取指定目录下的所有资源对象(包括子文件夹) + string[] guids; + if (searchType == EAssetSearchType.All) + guids = AssetDatabase.FindAssets(string.Empty, searchInFolders); + else + guids = AssetDatabase.FindAssets($"t:{searchType}", searchInFolders); + + // 注意:AssetDatabase.FindAssets()可能会获取到重复的资源 + List result = new List(); + for (int i = 0; i < guids.Length; i++) + { + string guid = guids[i]; + string assetPath = AssetDatabase.GUIDToAssetPath(guid); + if (result.Contains(assetPath) == false) + { + result.Add(assetPath); + } + } + + // 返回结果 + return result.ToArray(); + } + + /// + /// 搜集资源 + /// + /// 搜集的资源类型 + /// 指定搜索的文件夹 + /// 返回搜集到的资源路径列表 + public static string[] FindAssets(EAssetSearchType searchType, string searchInFolder) + { + return FindAssets(searchType, new string[] { searchInFolder }); + } + + /// + /// 打开搜索面板 + /// + /// 标题名称 + /// 默认搜索路径 + /// 返回选择的文件夹绝对路径,如果无效返回NULL + public static string OpenFolderPanel(string title, string defaultPath) + { + string openPath = EditorUtility.OpenFolderPanel(title, defaultPath, string.Empty); + if (string.IsNullOrEmpty(openPath)) + return null; + + if (openPath.Contains("/Assets") == false) + { + Debug.LogWarning("Please select unity assets folder."); + return null; + } + return openPath; + } + + /// + /// 打开搜索面板 + /// + /// 标题名称 + /// 默认搜索路径 + /// 返回选择的文件绝对路径,如果无效返回NULL + public static string OpenFilePath(string title, string defaultPath, string extension = "") + { + string openPath = EditorUtility.OpenFilePanel(title, defaultPath, extension); + if (string.IsNullOrEmpty(openPath)) + return null; + + if (openPath.Contains("/Assets") == false) + { + Debug.LogWarning("Please select unity assets file."); + return null; + } + return openPath; + } + + /// + /// 显示进度框 + /// + public static void DisplayProgressBar(string tips, int progressValue, int totalValue) + { + EditorUtility.DisplayProgressBar("进度", $"{tips} : {progressValue}/{totalValue}", (float)progressValue / totalValue); + } + + /// + /// 隐藏进度框 + /// + public static void ClearProgressBar() + { + EditorUtility.ClearProgressBar(); + } + #endregion + + #region 编辑器窗口 + public static void FocusUnitySceneWindow() + { + EditorWindow.FocusWindowIfItsOpen(); + } + public static void FocusUnityGameWindow() + { + System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.GameView"); + EditorWindow.GetWindow(T, false, "GameView", true); + } + public static void FocueUnityProjectWindow() + { + System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.ProjectBrowser"); + EditorWindow.GetWindow(T, false, "Project", true); + } + public static void FocusUnityHierarchyWindow() + { + System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.SceneHierarchyWindow"); + EditorWindow.GetWindow(T, false, "Hierarchy", true); + } + public static void FocusUnityInspectorWindow() + { + System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.InspectorWindow"); + EditorWindow.GetWindow(T, false, "Inspector", true); + } + public static void FocusUnityConsoleWindow() + { + System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.ConsoleWindow"); + EditorWindow.GetWindow(T, false, "Console", true); + } + #endregion + + #region 引用关系 + /// + /// 获取场景里的克隆预制体 + /// + public static GameObject GetClonePrefabInScene(GameObject sourcePrefab) + { + GameObject[] findObjects = GameObject.FindObjectsOfType(); + if (findObjects.Length == 0) + return null; + + for (int i = 0; i < findObjects.Length; i++) + { + GameObject findObject = findObjects[i]; + +#if UNITY_2017_4 + // 判断对象是否为一个预制体的引用 + if (PrefabUtility.GetPrefabType(findObject) == PrefabType.PrefabInstance) + { + // 判断是否为同一个预制体 + Object source = PrefabUtility.GetPrefabParent(findObject); + if (source.GetInstanceID() == sourcePrefab.GetInstanceID()) + return findObject; + } +#else + // 判断对象是否为一个预制体的引用 + if (PrefabUtility.GetPrefabInstanceStatus(findObject) == PrefabInstanceStatus.Connected) + { + // 判断是否为同一个预制体 + Object source = PrefabUtility.GetCorrespondingObjectFromSource(findObject); + if (source.GetInstanceID() == sourcePrefab.GetInstanceID()) + return findObject; + } +#endif + } + + return null; //没有找到合适的对象 + } + + /// + /// 查找场景里的引用对象 + /// + public static void FindReferencesInScene(UnityEngine.Object to) + { + var referencedBy = new List(); + + GameObject[] findObjects = GameObject.FindObjectsOfType(); //注意:只能获取激活的GameObject + for (int j = 0; j < findObjects.Length; j++) + { + GameObject findObject = findObjects[j]; + +#if UNITY_2017_4 + // 如果Prefab匹配 + if (PrefabUtility.GetPrefabType(findObject) == PrefabType.PrefabInstance) + { + if (PrefabUtility.GetPrefabParent(findObject) == to) + referencedBy.Add(findObject); + } +#else + // 如果Prefab匹配 + if (PrefabUtility.GetPrefabInstanceStatus(findObject) == PrefabInstanceStatus.Connected) + { + if (PrefabUtility.GetCorrespondingObjectFromSource(findObject) == to) + referencedBy.Add(findObject); + } +#endif + + // 如果组件匹配 + Component[] components = findObject.GetComponents(); + for (int i = 0; i < components.Length; i++) + { + Component c = components[i]; + if (!c) continue; + + SerializedObject so = new SerializedObject(c); + SerializedProperty sp = so.GetIterator(); + while (sp.NextVisible(true)) + { + if (sp.propertyType == SerializedPropertyType.ObjectReference) + { + if (sp.objectReferenceValue != null && sp.objectReferenceValue == to) + referencedBy.Add(c.gameObject); + } + } + } + } + + if (referencedBy.Any()) + Selection.objects = referencedBy.ToArray(); + } + + /// + /// 查找场景里的引用对象 + /// + public static void FindReferencesInPrefabs(UnityEngine.Object to, GameObject[] sourcePrefabs) + { + var referencedBy = new List(); + + for (int j = 0; j < sourcePrefabs.Length; j++) + { + GameObject clonePrefab = GetClonePrefabInScene(sourcePrefabs[j]); + if (clonePrefab == null) + continue; + +#if UNITY_2017_4 + // 如果Prefab匹配 + if (PrefabUtility.GetPrefabParent(clonePrefab) == to) + referencedBy.Add(clonePrefab); +#else + // 如果Prefab匹配 + if (PrefabUtility.GetCorrespondingObjectFromSource(clonePrefab) == to) + referencedBy.Add(clonePrefab); +#endif + + // 如果组件匹配 + Component[] components = clonePrefab.GetComponentsInChildren(true); //GetComponents(); + for (int i = 0; i < components.Length; i++) + { + Component c = components[i]; + if (!c) continue; + + SerializedObject so = new SerializedObject(c); + SerializedProperty sp = so.GetIterator(); + while (sp.NextVisible(true)) + { + if (sp.propertyType == SerializedPropertyType.ObjectReference) + { + if (sp.objectReferenceValue != null && sp.objectReferenceValue == to) + referencedBy.Add(c.gameObject); + } + } + } + } + + if (referencedBy.Any()) + Selection.objects = referencedBy.ToArray(); + } + #endregion + + #region 材质球 + /// + /// 清理无用的材质球属性 + /// + public static bool ClearMaterialUnusedProperty(Material mat) + { + bool removeUnused = false; + SerializedObject so = new SerializedObject(mat); + SerializedProperty sp = so.FindProperty("m_SavedProperties"); + + sp.Next(true); + do + { + if (sp.isArray == false) + continue; + + for (int i = sp.arraySize - 1; i >= 0; --i) + { + var p1 = sp.GetArrayElementAtIndex(i); + if (p1.isArray) + { + for (int ii = p1.arraySize - 1; ii >= 0; --ii) + { + var p2 = p1.GetArrayElementAtIndex(ii); + var val = p2.FindPropertyRelative("first"); + if (mat.HasProperty(val.stringValue) == false) + { + Debug.Log($"Material {mat.name} remove unused property : {val.stringValue}"); + p1.DeleteArrayElementAtIndex(ii); + removeUnused = true; + } + } + } + else + { + var val = p1.FindPropertyRelative("first"); + if (mat.HasProperty(val.stringValue) == false) + { + Debug.Log($"Material {mat.name} remove unused property : {val.stringValue}"); + sp.DeleteArrayElementAtIndex(i); + removeUnused = true; + } + } + } + } + while (sp.Next(false)); + so.ApplyModifiedProperties(); + return removeUnused; + } + #endregion + + #region 动画控制器 + /// + /// 查找动画控制器里冗余的动画状态机 + /// + public static bool FindRedundantAnimationState(AnimatorController animatorController) + { + if (animatorController == null) + return false; + + string assetPath = AssetDatabase.GetAssetPath(animatorController); + + // 查找使用的状态机名称 + List usedStateNames = new List(); + foreach (var layer in animatorController.layers) + { + foreach (var state in layer.stateMachine.states) + { + usedStateNames.Add(state.state.name); + } + } + + List allLines = new List(); + List stateIndexList = new List(); + using (StreamReader reader = File.OpenText(assetPath)) + { + string content; + while (null != (content = reader.ReadLine())) + { + allLines.Add(content); + if (content.StartsWith("AnimatorState:")) + { + stateIndexList.Add(allLines.Count - 1); + } + } + } + + List allStateNames = new List(); + foreach (var index in stateIndexList) + { + for (int i = index; i < allLines.Count; i++) + { + string content = allLines[i]; + content = content.Trim(); + if (content.StartsWith("m_Name")) + { + string[] splits = content.Split(':'); + string name = splits[1].TrimStart(' '); //移除前面的空格 + allStateNames.Add(name); + break; + } + } + } + + bool foundRedundantState = false; + foreach (var stateName in allStateNames) + { + if (usedStateNames.Contains(stateName) == false) + { + Debug.LogWarning($"发现冗余的动画文件:{assetPath}={stateName}"); + foundRedundantState = true; + } + } + return foundRedundantState; + } + #endregion + + #region 控制台 + private static MethodInfo _clearConsoleMethod; + private static MethodInfo ClearConsoleMethod + { + get + { + if (_clearConsoleMethod == null) + { + Assembly assembly = Assembly.GetAssembly(typeof(SceneView)); + System.Type logEntries = assembly.GetType("UnityEditor.LogEntries"); + _clearConsoleMethod = logEntries.GetMethod("Clear"); + } + return _clearConsoleMethod; + } + } + + /// + /// 清空控制台 + /// + public static void ClearUnityConsole() + { + ClearConsoleMethod.Invoke(new object(), null); + } + #endregion + + #region 文件 + /// + /// 测试写入权限 + /// + public static bool HasWriteAccess(string directoryPath) + { + try + { + string tmpFilePath = Path.Combine(directoryPath, Path.GetRandomFileName()); + using (FileStream fs = new FileStream(tmpFilePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite)) + { + StreamWriter writer = new StreamWriter(fs); + writer.Write(0); + } + File.Delete(tmpFilePath); + return true; + } + catch + { + return false; + } + } + + /// + /// 创建文件所在的目录 + /// + /// 文件路径 + public static void CreateFileDirectory(string filePath) + { + string destDirectory = Path.GetDirectoryName(filePath); + CreateDirectory(destDirectory); + } + + /// + /// 创建文件夹 + /// + public static bool CreateDirectory(string directory) + { + if (Directory.Exists(directory) == false) + { + Directory.CreateDirectory(directory); + return true; + } + else + { + return false; + } + } + + /// + /// 删除文件夹及子目录 + /// + public static bool DeleteDirectory(string directory) + { + if (Directory.Exists(directory)) + { + Directory.Delete(directory, true); + return true; + } + else + { + return false; + } + } + + /// + /// 文件重命名 + /// + public static void FileRename(string filePath, string newName) + { + string dirPath = Path.GetDirectoryName(filePath); + string destPath; + if (Path.HasExtension(filePath)) + { + string extentsion = Path.GetExtension(filePath); + destPath = $"{dirPath}/{newName}{extentsion}"; + } + else + { + destPath = $"{dirPath}/{newName}"; + } + FileInfo fileInfo = new FileInfo(filePath); + fileInfo.MoveTo(destPath); + } + + /// + /// 文件移动 + /// + public static void FileMoveTo(string filePath, string destPath) + { + FileInfo fileInfo = new FileInfo(filePath); + fileInfo.MoveTo(destPath); + } + + /// + /// 拷贝文件夹 + /// 注意:包括所有子目录的文件 + /// + public static void CopyDirectory(string sourcePath, string destPath) + { + sourcePath = EditorTools.GetRegularPath(sourcePath); + + // If the destination directory doesn't exist, create it. + if (Directory.Exists(destPath) == false) + Directory.CreateDirectory(destPath); + + string[] fileList = Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories); + foreach (string file in fileList) + { + string temp = EditorTools.GetRegularPath(file); + string savePath = temp.Replace(sourcePath, destPath); + CopyFile(file, savePath, true); + } + } + + /// + /// 拷贝文件 + /// + public static void CopyFile(string sourcePath, string destPath, bool overwrite) + { + if (File.Exists(sourcePath) == false) + throw new FileNotFoundException(sourcePath); + + // 创建目录 + CreateFileDirectory(destPath); + + // 复制文件 + File.Copy(sourcePath, destPath, overwrite); + } + + /// + /// 清空文件夹 + /// + /// 要清理的文件夹路径 + public static void ClearFolder(string directoryPath) + { + if (Directory.Exists(directoryPath) == false) + return; + + // 删除文件 + string[] allFiles = Directory.GetFiles(directoryPath); + for (int i = 0; i < allFiles.Length; i++) + { + File.Delete(allFiles[i]); + } + + // 删除文件夹 + string[] allFolders = Directory.GetDirectories(directoryPath); + for (int i = 0; i < allFolders.Length; i++) + { + Directory.Delete(allFolders[i], true); + } + } + + /// + /// 获取文件字节大小 + /// + public static long GetFileSize(string filePath) + { + FileInfo fileInfo = new FileInfo(filePath); + return fileInfo.Length; + } + + /// + /// 读取文件的所有文本内容 + /// + public static string ReadFileAllText(string filePath) + { + if (File.Exists(filePath) == false) + return string.Empty; + + return File.ReadAllText(filePath, Encoding.UTF8); + } + + /// + /// 读取文本的所有文本内容 + /// + public static string[] ReadFileAllLine(string filePath) + { + if (File.Exists(filePath) == false) + return null; + + return File.ReadAllLines(filePath, Encoding.UTF8); + } + + /// + /// 检测AssetBundle文件是否合法 + /// + public static bool CheckBundleFileValid(byte[] fileData) + { + string signature = ReadStringToNull(fileData, 20); + if (signature == "UnityFS" || signature == "UnityRaw" || signature == "UnityWeb" || signature == "\xFA\xFA\xFA\xFA\xFA\xFA\xFA\xFA") + return true; + else + return false; + } + private static string ReadStringToNull(byte[] data, int maxLength) + { + List bytes = new List(); + for (int i = 0; i < data.Length; i++) + { + if (i >= maxLength) + break; + + byte bt = data[i]; + if (bt == 0) + break; + + bytes.Add(bt); + } + + if (bytes.Count == 0) + return string.Empty; + else + return Encoding.UTF8.GetString(bytes.ToArray()); + } + #endregion + + #region 路径 + /// + /// 获取规范的路径 + /// + public static string GetRegularPath(string path) + { + return path.Replace('\\', '/').Replace("\\", "/"); //替换为Linux路径格式 + } + + /// + /// 获取项目工程路径 + /// + public static string GetProjectPath() + { + string projectPath = Path.GetDirectoryName(Application.dataPath); + return GetRegularPath(projectPath); + } + + /// + /// 转换文件的绝对路径为Unity资源路径 + /// 例如 D:\\YourPorject\\Assets\\Works\\file.txt 替换为 Assets/Works/file.txt + /// + public static string AbsolutePathToAssetPath(string absolutePath) + { + string content = GetRegularPath(absolutePath); + return Substring(content, "Assets", true); + } + + /// + /// 转换Unity资源路径为文件的绝对路径 + /// 例如:Assets/Works/file.txt 替换为 D:\\YourPorject/Assets/Works/file.txt + /// + public static string AssetPathToAbsolutePath(string assetPath) + { + string projectPath = GetProjectPath(); + return $"{projectPath}/{assetPath}"; + } + + /// + /// 递归查找目标文件夹路径 + /// + /// 搜索的根目录 + /// 目标文件夹名称 + /// 返回找到的文件夹路径,如果没有找到返回空字符串 + public static string FindFolder(string root, string folderName) + { + DirectoryInfo rootInfo = new DirectoryInfo(root); + DirectoryInfo[] infoList = rootInfo.GetDirectories(); + for (int i = 0; i < infoList.Length; i++) + { + string fullPath = infoList[i].FullName; + if (infoList[i].Name == folderName) + return fullPath; + + string result = FindFolder(fullPath, folderName); + if (string.IsNullOrEmpty(result) == false) + return result; + } + return string.Empty; + } + #endregion + + #region 字符串 + /// + /// 是否含有中文 + /// + public static bool IncludeChinese(string content) + { + foreach (var c in content) + { + if (c >= 0x4e00 && c <= 0x9fbb) + return true; + } + return false; + } + + /// + /// 是否是数字 + /// + public static bool IsNumber(string content) + { + if (string.IsNullOrEmpty(content)) + return false; + string pattern = @"^\d*$"; + return Regex.IsMatch(content, pattern); + } + + /// + /// 首字母大写 + /// + public static string Capitalize(string content) + { + return content.Substring(0, 1).ToUpper() + (content.Length > 1 ? content.Substring(1).ToLower() : ""); + } + + /// + /// 截取字符串 + /// 获取匹配到的后面内容 + /// + /// 内容 + /// 关键字 + /// 分割的结果里是否包含关键字 + /// 是否使用初始匹配的位置,否则使用末尾匹配的位置 + public static string Substring(string content, string key, bool includeKey, bool firstMatch = true) + { + if (string.IsNullOrEmpty(key)) + return content; + + int startIndex = -1; + if (firstMatch) + startIndex = content.IndexOf(key); //返回子字符串第一次出现位置 + else + startIndex = content.LastIndexOf(key); //返回子字符串最后出现的位置 + + // 如果没有找到匹配的关键字 + if (startIndex == -1) + return content; + + if (includeKey) + return content.Substring(startIndex); + else + return content.Substring(startIndex + key.Length); + } + #endregion + + #region 玩家偏好 + // 枚举 + public static void PlayerSetEnum(string key, T value) + { + string enumName = value.ToString(); + EditorPrefs.SetString(key, enumName); + } + public static T PlayerGetEnum(string key, T defaultValue) + { + string enumName = EditorPrefs.GetString(key, defaultValue.ToString()); + return StringUtility.NameToEnum(enumName); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/EditorTools.cs.meta b/Assets/YooAsset/Editor/EditorTools.cs.meta new file mode 100644 index 0000000..7f89261 --- /dev/null +++ b/Assets/YooAsset/Editor/EditorTools.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d8cbaf07016a5d045a8f463042a64c25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/ShaderVariantCollector.meta b/Assets/YooAsset/Editor/ShaderVariantCollector.meta new file mode 100644 index 0000000..165a9a9 --- /dev/null +++ b/Assets/YooAsset/Editor/ShaderVariantCollector.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dcb9955c15609744a9666bd76f6af3d9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectionHelper.cs b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectionHelper.cs new file mode 100644 index 0000000..b03fe1a --- /dev/null +++ b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectionHelper.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEditor; + +namespace YooAsset.Editor +{ + public static class ShaderVariantCollectionHelper + { + [Serializable] + public class ShaderVariantWrapper + { + /// + /// Shader asset path in editor. + /// + public string AssetPath; + + /// + /// Shader name. + /// + public string ShaderName; + + /// + /// Pass type to use in this variant. + /// + public PassType PassType; + + /// + /// Array of shader keywords to use in this variant. + /// + public string[] Keywords; + + public ShaderVariantWrapper(string assetPath, string shaderName, PassType passType, params string[] keywords) + { + AssetPath = assetPath; + ShaderName = shaderName; + PassType = passType; + Keywords = keywords; + } + } + + [Serializable] + public class ShaderVariantCollectionWrapper + { + /// + /// Number of shaders in this collection + /// + public int ShaderCount; + + /// + /// Number of total varians in this collection + /// + public int VariantCount; + + /// + /// Shader variants list. + /// + public List ShaderVariants = new List(1000); + + public void Add(ShaderVariantWrapper variant) + { + ShaderVariants.Add(variant); + } + } + + + public static ShaderVariantCollectionWrapper Extract(ShaderVariantCollection svc) + { + var result = new ShaderVariantCollectionWrapper(); + using (var so = new SerializedObject(svc)) + { + var shaderArray = so.FindProperty("m_Shaders.Array"); + if (shaderArray != null && shaderArray.isArray) + { + for (int i = 0; i < shaderArray.arraySize; ++i) + { + var shaderRef = shaderArray.FindPropertyRelative($"data[{i}].first"); + var shaderVariantsArray = shaderArray.FindPropertyRelative($"data[{i}].second.variants"); + if (shaderRef != null && shaderRef.propertyType == SerializedPropertyType.ObjectReference && shaderVariantsArray != null && shaderVariantsArray.isArray) + { + var shader = shaderRef.objectReferenceValue as Shader; + if (shader == null) + { + throw new Exception("Invalid shader in ShaderVariantCollection file."); + } + + string shaderAssetPath = AssetDatabase.GetAssetPath(shader); + string shaderName = shader.name; + + // 添加变种信息 + for (int j = 0; j < shaderVariantsArray.arraySize; ++j) + { + var propKeywords = shaderVariantsArray.FindPropertyRelative($"Array.data[{j}].keywords"); + var propPassType = shaderVariantsArray.FindPropertyRelative($"Array.data[{j}].passType"); + if (propKeywords != null && propPassType != null && propKeywords.propertyType == SerializedPropertyType.String) + { + string[] keywords = propKeywords.stringValue.Split(' '); + PassType pathType = (PassType)propPassType.intValue; + result.Add(new ShaderVariantWrapper(shaderAssetPath, shaderName, pathType, keywords)); + } + } + } + } + } + } + return result; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectionHelper.cs.meta b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectionHelper.cs.meta new file mode 100644 index 0000000..301e681 --- /dev/null +++ b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectionHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ab74d4ff4a2805147883de70a1559a0a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollector.cs b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollector.cs new file mode 100644 index 0000000..2cbd67f --- /dev/null +++ b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollector.cs @@ -0,0 +1,213 @@ +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.IO; +using UnityEngine; +using UnityEditor; +using UnityEditor.SceneManagement; + +namespace YooAsset.Editor +{ + [InitializeOnLoad] + public static class ShaderVariantCollector + { + private const float WaitMilliseconds = 1000f; + private static string _saveFilePath; + private static bool _isStarted = false; + private static readonly Stopwatch _elapsedTime = new Stopwatch(); + + static ShaderVariantCollector() + { + EditorApplication.update += EditorUpdate; + } + private static void EditorUpdate() + { + // 注意:一定要延迟保存才会起效 + if (_isStarted && _elapsedTime.ElapsedMilliseconds > WaitMilliseconds) + { + _isStarted = false; + _elapsedTime.Stop(); + + // 保存结果 + SaveCurrentShaderVariantCollection(); + + // 创建说明文件 + CreateReadme(); + } + } + private static void CreateReadme() + { + AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); + + ShaderVariantCollection svc = AssetDatabase.LoadAssetAtPath(_saveFilePath); + if(svc != null) + { + var wrapper = ShaderVariantCollectionHelper.Extract(svc); + string jsonContents = JsonUtility.ToJson(wrapper, true); + string savePath = _saveFilePath.Replace(".shadervariants", ".json"); + File.WriteAllText(savePath, jsonContents); + } + + AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); + } + + /// + /// 开始收集 + /// + public static void Run(string saveFilePath) + { + if (_isStarted) + return; + + if (Path.HasExtension(saveFilePath) == false) + saveFilePath = $"{saveFilePath}.shadervariants"; + if (Path.GetExtension(saveFilePath) != ".shadervariants") + throw new System.Exception("Shader variant file extension is invalid."); + EditorTools.CreateFileDirectory(saveFilePath); + _saveFilePath = saveFilePath; + + // 聚焦到游戏窗口 + EditorTools.FocusUnityGameWindow(); + + // 清空旧数据 + ClearCurrentShaderVariantCollection(); + + // 创建临时测试场景 + CreateTemperScene(); + + // 收集着色器变种 + var materials = GetAllMaterials(); + CollectVariants(materials); + + _isStarted = true; + _elapsedTime.Reset(); + _elapsedTime.Start(); + + UnityEngine.Debug.LogWarning("已经启动着色器变种收集工作,该工具只支持在编辑器下人工操作!"); + } + + private static void CreateTemperScene() + { + // 创建临时场景 + EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects); + } + private static List GetAllMaterials() + { + int progressValue = 0; + List allAssets = new List(1000); + + // 获取所有打包的资源 + List allCollectInfos = AssetBundleCollectorSettingData.GetAllCollectAssets(); + List collectAssets = allCollectInfos.Select(t => t.AssetPath).ToList(); + foreach (var assetPath in collectAssets) + { + string[] depends = AssetDatabase.GetDependencies(assetPath, true); + foreach (var depend in depends) + { + if (allAssets.Contains(depend) == false) + allAssets.Add(depend); + } + EditorTools.DisplayProgressBar("获取所有打包资源", ++progressValue, collectAssets.Count); + } + EditorTools.ClearProgressBar(); + + // 搜集所有材质球 + progressValue = 0; + var shaderDic = new Dictionary>(100); + foreach (var assetPath in allAssets) + { + System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath); + if (assetType == typeof(UnityEngine.Material)) + { + var material = AssetDatabase.LoadAssetAtPath(assetPath); + var shader = material.shader; + if (shader == null) + continue; + + if (shaderDic.ContainsKey(shader) == false) + { + shaderDic.Add(shader, new List()); + } + if (shaderDic[shader].Contains(material) == false) + { + shaderDic[shader].Add(material); + } + } + EditorTools.DisplayProgressBar("搜集所有材质球", ++progressValue, allAssets.Count); + } + EditorTools.ClearProgressBar(); + + // 返回结果 + var materials = new List(1000); + foreach (var valuePair in shaderDic) + { + materials.AddRange(valuePair.Value); + } + return materials; + } + private static void CollectVariants(List materials) + { + Camera camera = Camera.main; + if(camera == null) + throw new System.Exception("Not found main camera."); + + // 设置主相机 + float aspect = camera.aspect; + int totalMaterials = materials.Count; + float height = Mathf.Sqrt(totalMaterials / aspect) + 1; + float width = Mathf.Sqrt(totalMaterials / aspect) * aspect + 1; + float halfHeight = Mathf.CeilToInt(height / 2f); + float halfWidth = Mathf.CeilToInt(width / 2f); + camera.orthographic = true; + camera.orthographicSize = halfHeight; + camera.transform.position = new Vector3(0f, 0f, -10f); + + // 创建测试球体 + int xMax = (int)(width - 1); + int x = 0, y = 0; + int progressValue = 0; + for (int i = 0; i < materials.Count; i++) + { + var material = materials[i]; + var position = new Vector3(x - halfWidth + 1f, y - halfHeight + 1f, 0f); + CreateSphere(material, position, i); + if (x == xMax) + { + x = 0; + y++; + } + else + { + x++; + } + EditorTools.DisplayProgressBar("测试所有材质球", ++progressValue, materials.Count); + } + EditorTools.ClearProgressBar(); + } + private static void CreateSphere(Material material, Vector3 position, int index) + { + var go = GameObject.CreatePrimitive(PrimitiveType.Sphere); + go.GetComponent().material = material; + go.transform.position = position; + go.name = $"Sphere_{index}|{material.name}"; + } + + private static void ClearCurrentShaderVariantCollection() + { + EditorTools.InvokeNonPublicStaticMethod(typeof(ShaderUtil), "ClearCurrentShaderVariantCollection"); + } + private static void SaveCurrentShaderVariantCollection() + { + EditorTools.InvokeNonPublicStaticMethod(typeof(ShaderUtil), "SaveCurrentShaderVariantCollection", _saveFilePath); + } + public static int GetCurrentShaderVariantCollectionShaderCount() + { + return (int)EditorTools.InvokeNonPublicStaticMethod(typeof(ShaderUtil), "GetCurrentShaderVariantCollectionShaderCount"); + } + public static int GetCurrentShaderVariantCollectionVariantCount() + { + return (int)EditorTools.InvokeNonPublicStaticMethod(typeof(ShaderUtil), "GetCurrentShaderVariantCollectionVariantCount"); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollector.cs.meta b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollector.cs.meta new file mode 100644 index 0000000..79df03e --- /dev/null +++ b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 21b4cc6bf4c0c064d8e2687024e24c86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectorWindow.cs b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectorWindow.cs new file mode 100644 index 0000000..6887753 --- /dev/null +++ b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectorWindow.cs @@ -0,0 +1,58 @@ +using UnityEngine; +using UnityEditor; + +namespace YooAsset.Editor +{ + public class ShaderVariantCollectionWindow : EditorWindow + { + static ShaderVariantCollectionWindow _thisInstance; + + [MenuItem("YooAsset/ShaderVariant Collector", false, 203)] + static void ShowWindow() + { + if (_thisInstance == null) + { + _thisInstance = EditorWindow.GetWindow(typeof(ShaderVariantCollectionWindow), false, "着色器变种收集工具", true) as ShaderVariantCollectionWindow; + _thisInstance.minSize = new Vector2(800, 600); + } + _thisInstance.Show(); + } + + private string _saveFilePath = "Assets/MyShaderVariants.shadervariants"; + private ShaderVariantCollection _selectSVC; + + private void OnGUI() + { + EditorGUILayout.Space(); + _saveFilePath = EditorGUILayout.TextField("收集文件保存路径", _saveFilePath); + + int currentShaderCount = ShaderVariantCollector.GetCurrentShaderVariantCollectionShaderCount(); + int currentVariantCount = ShaderVariantCollector.GetCurrentShaderVariantCollectionVariantCount(); + EditorGUILayout.LabelField($"CurrentShaderCount : {currentShaderCount}"); + EditorGUILayout.LabelField($"CurrentVariantCount : {currentVariantCount}"); + + // 搜集变种 + EditorGUILayout.Space(); + if (GUILayout.Button("搜集变种", GUILayout.MaxWidth(80))) + { + ShaderVariantCollector.Run(_saveFilePath); + } + + // 查询 + EditorGUILayout.Space(); + if (GUILayout.Button("查询", GUILayout.MaxWidth(80))) + { + string resultPath = EditorTools.OpenFilePath("Select File", "Assets/", "shadervariants"); + if (string.IsNullOrEmpty(resultPath)) + return; + string assetPath = EditorTools.AbsolutePathToAssetPath(resultPath); + _selectSVC = AssetDatabase.LoadAssetAtPath(assetPath); + } + if (_selectSVC != null) + { + EditorGUILayout.LabelField($"ShaderCount : {_selectSVC.shaderCount}"); + EditorGUILayout.LabelField($"VariantCount : {_selectSVC.variantCount}"); + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectorWindow.cs.meta b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectorWindow.cs.meta new file mode 100644 index 0000000..6d9150f --- /dev/null +++ b/Assets/YooAsset/Editor/ShaderVariantCollector/ShaderVariantCollectorWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70401cc80b9807e46bd8283e01b4302f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Editor/YooAsset.Editor.asmdef b/Assets/YooAsset/Editor/YooAsset.Editor.asmdef new file mode 100644 index 0000000..9f1d318 --- /dev/null +++ b/Assets/YooAsset/Editor/YooAsset.Editor.asmdef @@ -0,0 +1,18 @@ +{ + "name": "YooAsset.Editor", + "rootNamespace": "", + "references": [ + "YooAsset" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/YooAsset/Editor/YooAsset.Editor.asmdef.meta b/Assets/YooAsset/Editor/YooAsset.Editor.asmdef.meta new file mode 100644 index 0000000..0d68d26 --- /dev/null +++ b/Assets/YooAsset/Editor/YooAsset.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4d1926c9df5b052469a1c63448b7609a +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LICENSE b/Assets/YooAsset/LICENSE similarity index 93% rename from LICENSE rename to Assets/YooAsset/LICENSE index b2c4f96..552c4f2 100644 --- a/LICENSE +++ b/Assets/YooAsset/LICENSE @@ -1,6 +1,7 @@ MIT License -Copyright (c) 2022 tuyoogame +Copyright (c) 2018-2021 何冠峰 +Copyright (c) 2021-2022 TuYoo Games Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Assets/YooAsset/LICENSE.meta b/Assets/YooAsset/LICENSE.meta new file mode 100644 index 0000000..5af4ff3 --- /dev/null +++ b/Assets/YooAsset/LICENSE.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 685cb35952719f9499a0cdb255604729 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/README.md b/Assets/YooAsset/README.md new file mode 100644 index 0000000..1bb093b --- /dev/null +++ b/Assets/YooAsset/README.md @@ -0,0 +1,2 @@ +# YooAsset +Unity assets system diff --git a/Assets/YooAsset/README.md.meta b/Assets/YooAsset/README.md.meta new file mode 100644 index 0000000..f586a23 --- /dev/null +++ b/Assets/YooAsset/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: cf18b110959e74849b675ca29f77c7dd +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime.meta b/Assets/YooAsset/Runtime.meta new file mode 100644 index 0000000..d4aa9cc --- /dev/null +++ b/Assets/YooAsset/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 40472229e23f3314f9a7123ad2d6e1e4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem.meta b/Assets/YooAsset/Runtime/AssetSystem.meta new file mode 100644 index 0000000..ee3b448 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8e6b180f448258247a506a5d0ea4922e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/AssetBundleInfo.cs b/Assets/YooAsset/Runtime/AssetSystem/AssetBundleInfo.cs new file mode 100644 index 0000000..fcebb35 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/AssetBundleInfo.cs @@ -0,0 +1,149 @@ + +namespace YooAsset +{ + public class AssetBundleInfo + { + private readonly PatchBundle _patchBundle; + + /// + /// 资源包名称 + /// + public string BundleName { private set; get; } + + /// + /// 本地存储的路径 + /// + public string LocalPath { private set; get; } + + /// + /// 远端下载地址 + /// + public string RemoteMainURL { private set; get; } + + /// + /// 远端下载备用地址 + /// + public string RemoteFallbackURL { private set; get; } + + /// + /// 文件哈希值 + /// + public string Hash + { + get + { + if (_patchBundle == null) + return string.Empty; + else + return _patchBundle.Hash; + } + } + + /// + /// 校验的CRC + /// + public string CRC + { + get + { + if (_patchBundle == null) + return string.Empty; + else + return _patchBundle.CRC; + } + } + + /// + /// 文件大小 + /// + public long SizeBytes + { + get + { + if (_patchBundle == null) + return 0; + else + return _patchBundle.SizeBytes; + } + } + + /// + /// 资源版本 + /// + public int Version + { + get + { + if (_patchBundle == null) + return 0; + else + return _patchBundle.Version; + } + } + + /// + /// 是否为加密文件 + /// + public bool IsEncrypted + { + get + { + if (_patchBundle == null) + return false; + else + return _patchBundle.IsEncrypted; + } + } + + /// + /// 是否为原生文件 + /// + public bool IsRawFile + { + get + { + if (_patchBundle == null) + return false; + else + return _patchBundle.IsRawFile; + } + } + + + private AssetBundleInfo() + { + } + internal AssetBundleInfo(PatchBundle patchBundle, string localPath, string mainURL, string fallbackURL) + { + _patchBundle = patchBundle; + BundleName = patchBundle.BundleName; + LocalPath = localPath; + RemoteMainURL = mainURL; + RemoteFallbackURL = fallbackURL; + } + internal AssetBundleInfo(PatchBundle patchBundle, string localPath) + { + _patchBundle = patchBundle; + BundleName = patchBundle.BundleName; + LocalPath = localPath; + RemoteMainURL = string.Empty; + RemoteFallbackURL = string.Empty; + } + internal AssetBundleInfo(string bundleName, string localPath) + { + _patchBundle = null; + BundleName = bundleName; + LocalPath = localPath; + RemoteMainURL = string.Empty; + RemoteFallbackURL = string.Empty; + } + + /// + /// 是否为JAR包内文件 + /// + public bool IsBuildinJarFile() + { + return LocalPath.StartsWith("jar:"); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/AssetBundleInfo.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/AssetBundleInfo.cs.meta new file mode 100644 index 0000000..9fa49ba --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/AssetBundleInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2939a2b9862f05545969cc5fcb378bd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/AssetOperationHandle.cs b/Assets/YooAsset/Runtime/AssetSystem/AssetOperationHandle.cs new file mode 100644 index 0000000..bb26d9b --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/AssetOperationHandle.cs @@ -0,0 +1,185 @@ +using System.Collections; +using UnityEngine; + +namespace YooAsset +{ + public struct AssetOperationHandle : IEnumerator + { + private IAssetProvider _provider; + + internal AssetOperationHandle(IAssetProvider provider) + { + _provider = provider; + } + + /// + /// 句柄是否有效(AssetFileLoader销毁会导致所有句柄失效) + /// + public bool IsValid + { + get + { + return _provider != null && _provider.IsValid; + } + } + + /// + /// 当前的加载状态 + /// + public EAssetStates States + { + get + { + if (IsValid == false) + return EAssetStates.None; + return _provider.States; + } + } + + /// + /// 加载进度 + /// + public float Progress + { + get + { + if (IsValid == false) + return 0; + return _provider.Progress; + } + } + + /// + /// 是否加载完毕 + /// + public bool IsDone + { + get + { + if (IsValid == false) + return false; + return _provider.IsDone; + } + } + + /// + /// 完成委托 + /// + public event System.Action Completed + { + add + { + if (IsValid == false) + throw new System.Exception($"{nameof(AssetOperationHandle)} is invalid"); + if (_provider.IsDone) + value.Invoke(this); + else + _provider.Callback += value; + } + remove + { + if (IsValid == false) + throw new System.Exception($"{nameof(AssetOperationHandle)} is invalid"); + _provider.Callback -= value; + } + } + + /// + /// 资源对象 + /// + public UnityEngine.Object AssetObject + { + get + { + if (IsValid == false) + return null; + return _provider.AssetObject; + } + } + + /// + /// 资源对象集合 + /// + public UnityEngine.Object[] AllAssets + { + get + { + if (IsValid == false) + return null; + return _provider.AllAssets; + } + } + + /// + /// 扩展的实例对象 + /// + public IAssetInstance AssetInstance + { + get + { + if (IsValid == false) + return null; + return _provider.AssetInstance; + } + } + + /// + /// 初始化的游戏对象(只限于请求的资源对象类型为GameObject) + /// + public GameObject InstantiateObject + { + get + { + if (IsValid == false) + return null; + if (_provider.AssetObject == null) + return null; + return UnityEngine.Object.Instantiate(_provider.AssetObject as GameObject); + } + } + + /// + /// 释放资源句柄 + /// + public void Release() + { + if (IsValid == false) + return; + _provider.Release(); + _provider = null; + } + + /// + /// 等待异步执行完毕 + /// + public void WaitForAsyncComplete() + { + if (IsValid == false) + return; + _provider.WaitForAsyncComplete(); + } + + #region 异步操作相关 + /// + /// 异步操作任务 + /// + public System.Threading.Tasks.Task Task + { + get { return _provider.Task; } + } + + // 协程相关 + bool IEnumerator.MoveNext() + { + return !IsDone; + } + void IEnumerator.Reset() + { + } + object IEnumerator.Current + { + get { return AssetObject; } + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/AssetOperationHandle.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/AssetOperationHandle.cs.meta new file mode 100644 index 0000000..cea76b3 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/AssetOperationHandle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6b766125ab790154ca132adb5c77333d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/AssetPathHelper.cs b/Assets/YooAsset/Runtime/AssetSystem/AssetPathHelper.cs new file mode 100644 index 0000000..f967c93 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/AssetPathHelper.cs @@ -0,0 +1,123 @@ +using System.IO; +using YooAsset.Utility; + +namespace YooAsset +{ + internal static class AssetPathHelper + { + /// + /// 获取规范化的路径 + /// + public static string GetRegularPath(string path) + { + return path.Replace('\\', '/').Replace("\\", "/"); //替换为Linux路径格式 + } + + /// + /// 获取文件所在的目录路径(Linux格式) + /// + public static string GetDirectory(string filePath) + { + string directory = Path.GetDirectoryName(filePath); + return GetRegularPath(directory); + } + + /// + /// 获取基于流文件夹的加载路径 + /// + public static string MakeStreamingLoadPath(string path) + { + return StringUtility.Format("{0}/{1}", UnityEngine.Application.streamingAssetsPath, path); + } + + /// + /// 获取基于沙盒文件夹的加载路径 + /// + public static string MakePersistentLoadPath(string path) + { + string root = MakePersistentRootPath(); + return StringUtility.Format("{0}/{1}", root, path); + } + + /// + /// 获取沙盒文件夹路径 + /// + public static string MakePersistentRootPath() + { +#if UNITY_EDITOR + // 注意:为了方便调试查看,编辑器下把存储目录放到项目里 + string projectPath = GetDirectory(UnityEngine.Application.dataPath); + return StringUtility.Format("{0}/Sandbox", projectPath); +#else + return StringUtility.Format("{0}/Sandbox", UnityEngine.Application.persistentDataPath); +#endif + } + + /// + /// 获取网络资源加载路径 + /// + public static string ConvertToWWWPath(string path) + { + // 注意:WWW加载方式,必须要在路径前面加file:// +#if UNITY_EDITOR + return StringUtility.Format("file:///{0}", path); +#elif UNITY_IPHONE + return StringUtility.Format("file://{0}", path); +#elif UNITY_ANDROID + return path; +#elif UNITY_STANDALONE + return StringUtility.Format("file:///{0}", path); +#endif + } + + /// + /// 合并资源路径 + /// + internal static string CombineAssetPath(string root, string location) + { + if (string.IsNullOrEmpty(root)) + return location; + else + return $"{root}/{location}"; + } + + /// + /// 获取AssetDatabase的加载路径 + /// + internal static string FindDatabaseAssetPath(string filePath) + { +#if UNITY_EDITOR + if (File.Exists(filePath)) + return filePath; + + // AssetDatabase加载资源需要提供文件后缀格式,然而资源定位地址并没有文件格式信息。 + // 所以我们通过查找该文件所在文件夹内同名的首个文件来确定AssetDatabase的加载路径。 + // 注意:AssetDatabase.FindAssets() 返回文件内包括递归文件夹内所有资源的GUID + string fileName = Path.GetFileName(filePath); + string directory = GetDirectory(filePath); + string[] guids = UnityEditor.AssetDatabase.FindAssets(string.Empty, new[] { directory }); + for (int i = 0; i < guids.Length; i++) + { + string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(guids[i]); + + if (UnityEditor.AssetDatabase.IsValidFolder(assetPath)) + continue; + + string assetDirectory = GetDirectory(assetPath); + if (assetDirectory != directory) + continue; + + string assetName = Path.GetFileNameWithoutExtension(assetPath); + if (assetName == fileName) + return assetPath; + } + + // 没有找到同名的资源文件 + Logger.Warning($"Not found asset : {filePath}"); + return filePath; +#else + throw new System.NotImplementedException(); +#endif + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/AssetPathHelper.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/AssetPathHelper.cs.meta new file mode 100644 index 0000000..fe3433d --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/AssetPathHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9581e7c53fe081749ab849ec6e2be3d6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs b/Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs new file mode 100644 index 0000000..c29ef37 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace YooAsset +{ + internal static class AssetSystem + { + private static readonly List _loaders = new List(1000); + private static readonly List _providers = new List(1000); + + /// + /// 在编辑器下模拟运行 + /// + public static bool SimulationOnEditor { private set; get; } + + /// + /// 运行时的最大加载个数 + /// + public static int AssetLoadingMaxNumber { private set; get; } + + public static IDecryptServices DecryptServices { private set; get; } + public static IBundleServices BundleServices { private set; get; } + + + /// + /// 初始化资源系统 + /// 注意:在使用AssetSystem之前需要初始化 + /// + public static void Initialize(bool simulationOnEditor, int assetLoadingMaxNumber, IDecryptServices decryptServices, IBundleServices bundleServices) + { + SimulationOnEditor = simulationOnEditor; + AssetLoadingMaxNumber = assetLoadingMaxNumber; + DecryptServices = decryptServices; + BundleServices = bundleServices; + } + + /// + /// 轮询更新 + /// + public static void UpdatePoll() + { + // 更新加载器 + foreach (var loader in _loaders) + { + loader.Update(); + } + + // 更新资源提供者 + // 注意:循环更新的时候,可能会扩展列表 + // 注意:不能限制场景对象的加载 + int loadingCount = 0; + for (int i = 0; i < _providers.Count; i++) + { + var provider = _providers[i]; + if (provider.IsSceneProvider()) + { + provider.Update(); + } + else + { + if (loadingCount < AssetLoadingMaxNumber) + provider.Update(); + + if (provider.IsDone == false) + loadingCount++; + } + } + + // 注意:需要立刻卸载场景 + if (SimulationOnEditor) + { + for (int i = _providers.Count - 1; i >= 0; i--) + { + AssetProviderBase provider = _providers[i]; + if (provider.IsSceneProvider() && provider.CanDestroy()) + { + provider.Destory(); + _providers.RemoveAt(i); + } + } + } + else + { + for (int i = _loaders.Count - 1; i >= 0; i--) + { + BundleFileLoader loader = _loaders[i]; + if (loader.IsSceneLoader()) + { + loader.TryDestroyAllProviders(); + } + } + for (int i = _loaders.Count - 1; i >= 0; i--) + { + BundleFileLoader loader = _loaders[i]; + if (loader.IsSceneLoader() && loader.CanDestroy()) + { + loader.Destroy(false); + _loaders.RemoveAt(i); + } + } + } + } + + /// + /// 资源回收(卸载引用计数为零的资源) + /// + public static void UnloadUnusedAssets() + { + if (SimulationOnEditor) + { + for (int i = _providers.Count - 1; i >= 0; i--) + { + if (_providers[i].CanDestroy()) + { + _providers[i].Destory(); + _providers.RemoveAt(i); + } + } + } + else + { + for (int i = _loaders.Count - 1; i >= 0; i--) + { + BundleFileLoader loader = _loaders[i]; + loader.TryDestroyAllProviders(); + } + for (int i = _loaders.Count - 1; i >= 0; i--) + { + BundleFileLoader loader = _loaders[i]; + if (loader.CanDestroy()) + { + loader.Destroy(false); + _loaders.RemoveAt(i); + } + } + } + } + + /// + /// 强制回收所有资源 + /// + public static void ForceUnloadAllAssets() + { + foreach (var provider in _providers) + { + provider.Destory(); + } + _providers.Clear(); + + foreach (var loader in _loaders) + { + loader.Destroy(true); + } + _loaders.Clear(); + + // 注意:调用底层接口释放所有资源 + Resources.UnloadUnusedAssets(); + } + + /// + /// 异步加载场景 + /// + /// 场景名称 + public static AssetOperationHandle LoadSceneAsync(string scenePath, SceneInstanceParam instanceParam) + { + AssetProviderBase provider = TryGetAssetProvider(scenePath); + if (provider == null) + { + if (SimulationOnEditor) + provider = new DatabaseSceneProvider(scenePath, instanceParam); + else + provider = new BundledSceneProvider(scenePath, instanceParam); + _providers.Add(provider); + } + + // 引用计数增加 + provider.Reference(); + return provider.Handle; + } + + /// + /// 异步加载资源对象 + /// + /// 资源路径 + /// 资源类型 + public static AssetOperationHandle LoadAssetAsync(string assetPath, System.Type assetType) + { + AssetProviderBase provider = TryGetAssetProvider(assetPath); + if (provider == null) + { + if (SimulationOnEditor) + provider = new DatabaseAssetProvider(assetPath, assetType); + else + provider = new BundledAssetProvider(assetPath, assetType); + _providers.Add(provider); + } + + // 引用计数增加 + provider.Reference(); + return provider.Handle; + } + + /// + /// 异步加载所有子资源对象 + /// + /// 资源路径 + /// 资源类型、 + public static AssetOperationHandle LoadSubAssetsAsync(string assetPath, System.Type assetType) + { + AssetProviderBase provider = TryGetAssetProvider(assetPath); + if (provider == null) + { + if (SimulationOnEditor) + provider = new DatabaseSubAssetsProvider(assetPath, assetType); + else + provider = new BundledSubAssetsProvider(assetPath, assetType); + _providers.Add(provider); + } + + // 引用计数增加 + provider.Reference(); + return provider.Handle; + } + + + internal static BundleFileLoader CreateOwnerBundleLoader(string assetPath) + { + string bundleName = BundleServices.GetAssetBundleName(assetPath); + AssetBundleInfo bundleInfo = BundleServices.GetAssetBundleInfo(bundleName); + return CreateBundleFileLoaderInternal(bundleInfo); + } + internal static List CreateDependBundleLoaders(string assetPath) + { + List result = new List(); + string[] depends = BundleServices.GetAllDependencies(assetPath); + if (depends != null) + { + foreach (var dependBundleName in depends) + { + AssetBundleInfo dependBundleInfo = BundleServices.GetAssetBundleInfo(dependBundleName); + BundleFileLoader dependLoader = CreateBundleFileLoaderInternal(dependBundleInfo); + result.Add(dependLoader); + } + } + return result; + } + internal static void RemoveBundleProviders(List providers) + { + foreach (var provider in providers) + { + _providers.Remove(provider); + } + } + + private static BundleFileLoader CreateBundleFileLoaderInternal(AssetBundleInfo bundleInfo) + { + // 如果加载器已经存在 + BundleFileLoader loader = TryGetBundleFileLoader(bundleInfo.BundleName); + if (loader != null) + return loader; + + // 新增下载需求 + loader = new BundleFileLoader(bundleInfo); + _loaders.Add(loader); + return loader; + } + private static BundleFileLoader TryGetBundleFileLoader(string bundleName) + { + BundleFileLoader loader = null; + for (int i = 0; i < _loaders.Count; i++) + { + BundleFileLoader temp = _loaders[i]; + if (temp.BundleInfo.BundleName.Equals(bundleName)) + { + loader = temp; + break; + } + } + return loader; + } + private static AssetProviderBase TryGetAssetProvider(string assetPath) + { + AssetProviderBase provider = null; + for (int i = 0; i < _providers.Count; i++) + { + AssetProviderBase temp = _providers[i]; + if (temp.AssetPath.Equals(assetPath)) + { + provider = temp; + break; + } + } + return provider; + } + + #region 调试专属方法 + internal static void GetDebugSummy(DebugSummy summy) + { + summy.ClearAll(); + summy.BundleCount = _loaders.Count; + summy.AssetCount = _providers.Count; + + foreach (var provider in _providers) + { + DebugSummy.ProviderInfo providerInfo = new DebugSummy.ProviderInfo(); + providerInfo.AssetPath = provider.AssetPath; + providerInfo.RefCount = provider.RefCount; + providerInfo.States = provider.States; + providerInfo.BundleInfos.Clear(); + summy.ProviderInfos.Add(providerInfo); + + if (provider is BundledProvider) + { + BundledProvider temp = provider as BundledProvider; + temp.GetBundleDebugInfos(providerInfo.BundleInfos); + } + } + + // 重新排序 + summy.ProviderInfos.Sort(); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs.meta new file mode 100644 index 0000000..a1a3e25 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3ecc5913758e8234fbba5ef2fd192f86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/EDecryptMethod.cs b/Assets/YooAsset/Runtime/AssetSystem/EDecryptMethod.cs new file mode 100644 index 0000000..0c4c1d7 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/EDecryptMethod.cs @@ -0,0 +1,19 @@ + +namespace YooAsset +{ + /// + /// 解密方法 + /// + public enum EDecryptMethod + { + /// + /// 获取解密的数据偏移 + /// + GetDecryptOffset, + + /// + /// 获取解密的字节数据 + /// + GetDecryptBinary, + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/EDecryptMethod.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/EDecryptMethod.cs.meta new file mode 100644 index 0000000..ea68fc6 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/EDecryptMethod.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a446add12cd4554a9bd178a7ef2db55 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/IBundleServices.cs b/Assets/YooAsset/Runtime/AssetSystem/IBundleServices.cs new file mode 100644 index 0000000..06a5b2d --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/IBundleServices.cs @@ -0,0 +1,21 @@ + +namespace YooAsset +{ + public interface IBundleServices + { + /// + /// 获取AssetBundle的信息 + /// + AssetBundleInfo GetAssetBundleInfo(string bundleName); + + /// + /// 获取资源所属的资源包名称 + /// + string GetAssetBundleName(string assetPath); + + /// + /// 获取资源依赖的所有AssetBundle列表 + /// + string[] GetAllDependencies(string assetPath); + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/IBundleServices.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/IBundleServices.cs.meta new file mode 100644 index 0000000..c0796aa --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/IBundleServices.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70420213c551a2b4b8cf014067699b07 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/IDecryptServices.cs b/Assets/YooAsset/Runtime/AssetSystem/IDecryptServices.cs new file mode 100644 index 0000000..3e2b453 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/IDecryptServices.cs @@ -0,0 +1,18 @@ + +namespace YooAsset +{ + public interface IDecryptServices + { + EDecryptMethod DecryptType { get; } + + /// + /// 获取解密的数据偏移 + /// + ulong GetDecryptOffset(AssetBundleInfo bundleInfo); + + /// + /// 获取解密的字节数据 + /// + byte[] GetDecryptBinary(AssetBundleInfo bundleInfo); + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/IDecryptServices.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/IDecryptServices.cs.meta new file mode 100644 index 0000000..ea5b4e0 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/IDecryptServices.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3444518ef1b082a46a9855fef4f69c86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Instance.meta b/Assets/YooAsset/Runtime/AssetSystem/Instance.meta new file mode 100644 index 0000000..06a9b1d --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Instance.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a9b18d6c47ddf9b4296619d3d50ff826 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetInstance.cs b/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetInstance.cs new file mode 100644 index 0000000..eac555e --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetInstance.cs @@ -0,0 +1,7 @@ + +namespace YooAsset +{ + public interface IAssetInstance + { + } +} diff --git a/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetInstance.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetInstance.cs.meta new file mode 100644 index 0000000..ecd0841 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetInstance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1d0cfc6a2d5b1f4d9b9ac8f93682497 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetParam.cs b/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetParam.cs new file mode 100644 index 0000000..367e529 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetParam.cs @@ -0,0 +1,7 @@ + +namespace YooAsset +{ + public interface IAssetParam + { + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetParam.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetParam.cs.meta new file mode 100644 index 0000000..aa37de7 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Instance/IAssetParam.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a8995b1e1e511714b948f7fa6275028e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Instance/SceneInstance.cs b/Assets/YooAsset/Runtime/AssetSystem/Instance/SceneInstance.cs new file mode 100644 index 0000000..7d908bf --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Instance/SceneInstance.cs @@ -0,0 +1,63 @@ +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace YooAsset +{ + /// + /// 扩展的场景实例对象 + /// + public class SceneInstance : IAssetInstance + { + private readonly AsyncOperation _asyncOp; + + public SceneInstance(AsyncOperation op) + { + _asyncOp = op; + } + + /// + /// UnityEngine场景对象 + /// + public UnityEngine.SceneManagement.Scene Scene { internal set; get; } + + /// + /// 激活场景 + /// + public bool Activate() + { + if (Scene == null) + return false; + + if (Scene.IsValid() && Scene.isLoaded) + { + return SceneManager.SetActiveScene(Scene); + } + else + { + Logger.Warning($"Scene is invalid or not loaded : {Scene.name}"); + return false; + } + } + } + + /// + /// 加载场景实例对象需要提供的参数类 + /// + public class SceneInstanceParam : IAssetParam + { + /// + /// 加载模式 + /// + public LoadSceneMode LoadMode { set; get; } + + /// + /// 物理模式 + /// + //public LocalPhysicsMode PhysicsMode { set; get;} + + /// + /// 加载完毕时是否主动激活 + /// + public bool ActivateOnLoad { set; get; } + } +} diff --git a/Assets/YooAsset/Runtime/AssetSystem/Instance/SceneInstance.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Instance/SceneInstance.cs.meta new file mode 100644 index 0000000..9568688 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Instance/SceneInstance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ab6e7cef318cff4684ed733679bca0e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader.meta b/Assets/YooAsset/Runtime/AssetSystem/Loader.meta new file mode 100644 index 0000000..9f69f01 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 58462a7dcef164e43878a037395d4417 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader/BundleFileLoader.cs b/Assets/YooAsset/Runtime/AssetSystem/Loader/BundleFileLoader.cs new file mode 100644 index 0000000..96288ce --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader/BundleFileLoader.cs @@ -0,0 +1,319 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace YooAsset +{ + internal class BundleFileLoader + { + /// + /// 资源文件信息 + /// + public AssetBundleInfo BundleInfo { private set; get; } + + /// + /// 引用计数 + /// + public int RefCount { private set; get; } + + /// + /// 加载状态 + /// + public ELoaderStates States { private set; get; } + + /// + /// 是否已经销毁 + /// + public bool IsDestroyed { private set; get; } = false; + + private readonly List _providers = new List(100); + private bool _isWaitForAsyncComplete = false; + private bool _isShowWaitForAsyncError = false; + private FileDownloader _fileDownloader; + private AssetBundleCreateRequest _cacheRequest; + internal AssetBundle CacheBundle { private set; get; } + + + public BundleFileLoader(AssetBundleInfo bundleInfo) + { + BundleInfo = bundleInfo; + RefCount = 0; + States = ELoaderStates.None; + } + + /// + /// 是否为场景加载器 + /// + public bool IsSceneLoader() + { + foreach (var provider in _providers) + { + if (provider is BundledSceneProvider) + return true; + } + return false; + } + + /// + /// 添加附属的资源提供者 + /// + public void AddProvider(AssetProviderBase provider) + { + if (_providers.Contains(provider) == false) + _providers.Add(provider); + } + + /// + /// 引用(引用计数递加) + /// + public void Reference() + { + RefCount++; + } + + /// + /// 释放(引用计数递减) + /// + public void Release() + { + RefCount--; + } + + /// + /// 轮询更新 + /// + public void Update() + { + // 如果资源文件加载完毕 + if (IsDone()) + return; + + if (States == ELoaderStates.None) + { + // 检测加载地址是否为空 + if (string.IsNullOrEmpty(BundleInfo.LocalPath)) + { + States = ELoaderStates.Fail; + return; + } + + if (string.IsNullOrEmpty(BundleInfo.RemoteMainURL)) + States = ELoaderStates.LoadFile; + else + States = ELoaderStates.Download; + } + + // 1. 从服务器下载 + if (States == ELoaderStates.Download) + { + int failedTryAgain = int.MaxValue; + _fileDownloader = DownloadSystem.BeginDownload(BundleInfo, failedTryAgain); + States = ELoaderStates.CheckDownload; + } + + // 2. 检测服务器下载结果 + if (States == ELoaderStates.CheckDownload) + { + if (_fileDownloader.IsDone() == false) + return; + + if (_fileDownloader.HasError()) + { + _fileDownloader.ReportError(); + States = ELoaderStates.Fail; + } + else + { + States = ELoaderStates.LoadFile; + } + } + + // 3. 加载AssetBundle + if (States == ELoaderStates.LoadFile) + { +#if UNITY_EDITOR + // 注意:Unity2017.4编辑器模式下,如果AssetBundle文件不存在会导致编辑器崩溃,这里做了预判。 + if (System.IO.File.Exists(BundleInfo.LocalPath) == false) + { + Logger.Warning($"Not found assetBundle file : {BundleInfo.LocalPath}"); + States = ELoaderStates.Fail; + return; + } +#endif + + // Load assetBundle file + if (BundleInfo.IsEncrypted) + { + if (AssetSystem.DecryptServices == null) + throw new Exception($"{nameof(BundleFileLoader)} need IDecryptServices : {BundleInfo.BundleName}"); + + EDecryptMethod decryptType = AssetSystem.DecryptServices.DecryptType; + if (decryptType == EDecryptMethod.GetDecryptOffset) + { + ulong offset = AssetSystem.DecryptServices.GetDecryptOffset(BundleInfo); + if (_isWaitForAsyncComplete) + CacheBundle = AssetBundle.LoadFromFile(BundleInfo.LocalPath, 0, offset); + else + _cacheRequest = AssetBundle.LoadFromFileAsync(BundleInfo.LocalPath, 0, offset); + } + else if (decryptType == EDecryptMethod.GetDecryptBinary) + { + byte[] binary = AssetSystem.DecryptServices.GetDecryptBinary(BundleInfo); + if (_isWaitForAsyncComplete) + CacheBundle = AssetBundle.LoadFromMemory(binary); + else + _cacheRequest = AssetBundle.LoadFromMemoryAsync(binary); + } + else + { + throw new NotImplementedException($"{decryptType}"); + } + } + else + { + if (_isWaitForAsyncComplete) + CacheBundle = AssetBundle.LoadFromFile(BundleInfo.LocalPath); + else + _cacheRequest = AssetBundle.LoadFromFileAsync(BundleInfo.LocalPath); + } + States = ELoaderStates.CheckFile; + } + + // 4. 检测AssetBundle加载结果 + if (States == ELoaderStates.CheckFile) + { + if (_cacheRequest != null) + { + if (_isWaitForAsyncComplete) + { + // 强制挂起主线程(注意:该操作会很耗时) + Logger.Warning("Suspend the main thread to load unity bundle."); + CacheBundle = _cacheRequest.assetBundle; + } + else + { + if (_cacheRequest.isDone == false) + return; + CacheBundle = _cacheRequest.assetBundle; + } + } + + // Check error + if (CacheBundle == null) + { + Logger.Error($"Failed to load assetBundle file : {BundleInfo.BundleName}"); + States = ELoaderStates.Fail; + } + else + { + States = ELoaderStates.Success; + } + } + } + + /// + /// 销毁 + /// + public void Destroy(bool forceDestroy) + { + IsDestroyed = true; + + // Check fatal + if (forceDestroy == false) + { + if (RefCount > 0) + throw new Exception($"Bundle file loader ref is not zero : {BundleInfo.BundleName}"); + if (IsDone() == false) + throw new Exception($"Bundle file loader is not done : {BundleInfo.BundleName}"); + } + + if (CacheBundle != null) + { + CacheBundle.Unload(true); + CacheBundle = null; + } + } + + /// + /// 是否完毕(无论成功或失败) + /// + public bool IsDone() + { + return States == ELoaderStates.Success || States == ELoaderStates.Fail; + } + + /// + /// 是否可以销毁 + /// + public bool CanDestroy() + { + if (IsDone() == false) + return false; + + return RefCount <= 0; + } + + /// + /// 在满足条件的前提下,销毁所有资源提供者 + /// + public void TryDestroyAllProviders() + { + if (IsDone() == false) + return; + + // 注意:必须等待所有Provider可以销毁的时候,才可以释放Bundle文件。 + foreach (var provider in _providers) + { + if (provider.CanDestroy() == false) + return; + } + + // 除了自己没有其它引用 + if (RefCount > _providers.Count) + return; + + // 销毁所有Providers + foreach (var provider in _providers) + { + provider.Destory(); + } + + // 从列表里移除Providers + AssetSystem.RemoveBundleProviders(_providers); + _providers.Clear(); + } + + /// + /// 主线程等待异步操作完毕 + /// + public void WaitForAsyncComplete() + { + _isWaitForAsyncComplete = true; + + int frame = 1000; + while (true) + { + // 保险机制 + // 注意:如果需要从WEB端下载资源,可能会触发保险机制! + frame--; + if (frame == 0) + { + if (_isShowWaitForAsyncError == false) + { + _isShowWaitForAsyncError = true; + Logger.Error($"WaitForAsyncComplete failed ! BundleName : {BundleInfo.BundleName} States : {States}"); + } + break; + } + + // 驱动流程 + Update(); + + // 完成后退出 + if (IsDone()) + break; + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader/BundleFileLoader.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Loader/BundleFileLoader.cs.meta new file mode 100644 index 0000000..a88ab9c --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader/BundleFileLoader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 64d0a51f59c2a964eaf97b87db47428f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader/DependBundleGrouper.cs b/Assets/YooAsset/Runtime/AssetSystem/Loader/DependBundleGrouper.cs new file mode 100644 index 0000000..0dcaf63 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader/DependBundleGrouper.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset +{ + internal class DependBundleGrouper + { + /// + /// 依赖的资源包加载器列表 + /// + private readonly List _dependBundles; + + public DependBundleGrouper(string assetPath) + { + _dependBundles = AssetSystem.CreateDependBundleLoaders(assetPath); + } + + /// + /// 是否已经完成(无论成功或失败) + /// + public bool IsDone() + { + foreach (var loader in _dependBundles) + { + if (loader.IsDone() == false) + return false; + } + return true; + } + + /// + /// 主线程等待异步操作完毕 + /// + public void WaitForAsyncComplete() + { + foreach (var loader in _dependBundles) + { + if (loader.IsDone() == false) + loader.WaitForAsyncComplete(); + } + } + + /// + /// 增加引用计数 + /// + public void Reference() + { + foreach (var loader in _dependBundles) + { + loader.Reference(); + } + } + + /// + /// 减少引用计数 + /// + public void Release() + { + foreach (var loader in _dependBundles) + { + loader.Release(); + } + } + + /// + /// 获取资源包的调试信息列表 + /// + internal void GetBundleDebugInfos(List output) + { + foreach (var loader in _dependBundles) + { + var debugInfo = new DebugSummy.BundleInfo(); + debugInfo.BundleName = loader.BundleInfo.BundleName; + debugInfo.Version = loader.BundleInfo.Version; + debugInfo.RefCount = loader.RefCount; + debugInfo.States = loader.States; + output.Add(debugInfo); + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader/DependBundleGrouper.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Loader/DependBundleGrouper.cs.meta new file mode 100644 index 0000000..d770c0d --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader/DependBundleGrouper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b7eeccc96e4c31c45bd105b0dbc3fd6e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader/ELoaderStates.cs b/Assets/YooAsset/Runtime/AssetSystem/Loader/ELoaderStates.cs new file mode 100644 index 0000000..d9b91fe --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader/ELoaderStates.cs @@ -0,0 +1,17 @@ + +namespace YooAsset +{ + /// + /// 文件加载器状态 + /// + public enum ELoaderStates + { + None = 0, + Download, + CheckDownload, + LoadFile, + CheckFile, + Success, + Fail, + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader/ELoaderStates.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Loader/ELoaderStates.cs.meta new file mode 100644 index 0000000..3674a8a --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader/ELoaderStates.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 90f00fe388279034aaee9c89c07e2ef1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider.meta b/Assets/YooAsset/Runtime/AssetSystem/Provider.meta new file mode 100644 index 0000000..ec9d60f --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c64025c73372d9d4cab02c9cf9436e55 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/AssetProviderBase.cs b/Assets/YooAsset/Runtime/AssetSystem/Provider/AssetProviderBase.cs new file mode 100644 index 0000000..6ee0582 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/AssetProviderBase.cs @@ -0,0 +1,138 @@ + +namespace YooAsset +{ + internal abstract class AssetProviderBase : IAssetProvider + { + protected bool IsWaitForAsyncComplete { private set; get; } = false; + + public string AssetPath { private set; get; } + public string AssetName { private set; get; } + public System.Type AssetType { private set; get; } + public UnityEngine.Object AssetObject { protected set; get; } + public UnityEngine.Object[] AllAssets { protected set; get; } + public IAssetInstance AssetInstance { protected set; get; } + public EAssetStates States { protected set; get; } + public int RefCount { private set; get; } + public AssetOperationHandle Handle { private set; get; } + public System.Action Callback { set; get; } + public bool IsDestroyed { private set; get; } = false; + public bool IsDone + { + get + { + return States == EAssetStates.Success || States == EAssetStates.Fail; + } + } + public bool IsValid + { + get + { + return IsDestroyed == false; + } + } + public virtual float Progress + { + get + { + return 0; + } + } + + + public AssetProviderBase(string assetPath, System.Type assetType) + { + AssetPath = assetPath; + AssetName = System.IO.Path.GetFileName(assetPath); + AssetType = assetType; + States = EAssetStates.None; + Handle = new AssetOperationHandle(this); + } + + public abstract void Update(); + public virtual void Destory() + { + IsDestroyed = true; + } + + public void Reference() + { + RefCount++; + } + public void Release() + { + if (RefCount <= 0) + Logger.Warning("Asset provider reference count is already zero. There may be resource leaks !"); + + RefCount--; + } + public bool CanDestroy() + { + if (IsDone == false) + return false; + + return RefCount <= 0; + } + + /// + /// 是否为场景提供者 + /// + public bool IsSceneProvider() + { + if (this is BundledSceneProvider || this is DatabaseSceneProvider) + return true; + else + return false; + } + + /// + /// 等待异步执行完毕 + /// + public virtual void WaitForAsyncComplete() + { + IsWaitForAsyncComplete = true; + + // 注意:主动轮询更新完成同步加载 + Update(); + + // 验证结果 + if (IsDone == false) + { + Logger.Warning($"WaitForAsyncComplete failed to loading : {AssetPath}"); + } + } + + /// + /// 异步操作任务 + /// + System.Threading.Tasks.Task IAssetProvider.Task + { + get + { + var handle = WaitHandle; + return System.Threading.Tasks.Task.Factory.StartNew(o => + { + handle.WaitOne(); + return AssetObject as object; + }, this); + } + } + + // 异步操作相关 + private System.Threading.EventWaitHandle _waitHandle; + private System.Threading.WaitHandle WaitHandle + { + get + { + if (_waitHandle == null) + _waitHandle = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.ManualReset); + _waitHandle.Reset(); + return _waitHandle; + } + } + protected void InvokeCompletion() + { + Callback?.Invoke(Handle); + _waitHandle?.Set(); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/AssetProviderBase.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Provider/AssetProviderBase.cs.meta new file mode 100644 index 0000000..38b0327 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/AssetProviderBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02cc2ce3da5a88b48942ad96e6061aad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledAssetProvider.cs b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledAssetProvider.cs new file mode 100644 index 0000000..a645d80 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledAssetProvider.cs @@ -0,0 +1,105 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace YooAsset +{ + internal sealed class BundledAssetProvider : BundledProvider + { + private AssetBundleRequest _cacheRequest; + public override float Progress + { + get + { + if (_cacheRequest == null) + return 0; + return _cacheRequest.progress; + } + } + + public BundledAssetProvider(string assetPath, System.Type assetType) + : base(assetPath, assetType) + { + } + public override void Update() + { + if (IsDone) + return; + + if (States == EAssetStates.None) + { + States = EAssetStates.CheckBundle; + } + + // 1. 检测资源包 + if (States == EAssetStates.CheckBundle) + { + if (IsWaitForAsyncComplete) + { + DependBundles.WaitForAsyncComplete(); + OwnerBundle.WaitForAsyncComplete(); + } + + if (DependBundles.IsDone() == false) + return; + if (OwnerBundle.IsDone() == false) + return; + + if (OwnerBundle.CacheBundle == null) + { + States = EAssetStates.Fail; + InvokeCompletion(); + } + else + { + States = EAssetStates.Loading; + } + } + + // 2. 加载资源对象 + if (States == EAssetStates.Loading) + { + if (IsWaitForAsyncComplete) + { + if (AssetType == null) + AssetObject = OwnerBundle.CacheBundle.LoadAsset(AssetName); + else + AssetObject = OwnerBundle.CacheBundle.LoadAsset(AssetName, AssetType); + } + else + { + if (AssetType == null) + _cacheRequest = OwnerBundle.CacheBundle.LoadAssetAsync(AssetName); + else + _cacheRequest = OwnerBundle.CacheBundle.LoadAssetAsync(AssetName, AssetType); + } + States = EAssetStates.Checking; + } + + // 3. 检测加载结果 + if (States == EAssetStates.Checking) + { + if (_cacheRequest != null) + { + if (IsWaitForAsyncComplete) + { + // 强制挂起主线程(注意:该操作会很耗时) + Logger.Warning("Suspend the main thread to load unity asset."); + AssetObject = _cacheRequest.asset; + } + else + { + if (_cacheRequest.isDone == false) + return; + AssetObject = _cacheRequest.asset; + } + } + + States = AssetObject == null ? EAssetStates.Fail : EAssetStates.Success; + if (States == EAssetStates.Fail) + Logger.Warning($"Failed to load asset : {AssetName} from bundle : {OwnerBundle.BundleInfo.BundleName}"); + InvokeCompletion(); + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledAssetProvider.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledAssetProvider.cs.meta new file mode 100644 index 0000000..3e28b3f --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledAssetProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ca5e4bf0c3efe6742bb57b494487be52 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledProvider.cs b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledProvider.cs new file mode 100644 index 0000000..9747f36 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledProvider.cs @@ -0,0 +1,51 @@ +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset +{ + internal abstract class BundledProvider : AssetProviderBase + { + protected BundleFileLoader OwnerBundle { private set; get; } + protected DependBundleGrouper DependBundles { private set; get; } + + public BundledProvider(string assetPath, System.Type assetType) : base(assetPath, assetType) + { + OwnerBundle = AssetSystem.CreateOwnerBundleLoader(assetPath); + OwnerBundle.Reference(); + OwnerBundle.AddProvider(this); + DependBundles = new DependBundleGrouper(assetPath); + DependBundles.Reference(); + } + public override void Destory() + { + base.Destory(); + + // 释放资源包 + if (OwnerBundle != null) + { + OwnerBundle.Release(); + OwnerBundle = null; + } + if (DependBundles != null) + { + DependBundles.Release(); + DependBundles = null; + } + } + + /// + /// 获取资源包的调试信息列表 + /// + internal void GetBundleDebugInfos(List output) + { + var ownerInfo = new DebugSummy.BundleInfo(); + ownerInfo.BundleName = OwnerBundle.BundleInfo.BundleName; + ownerInfo.Version = OwnerBundle.BundleInfo.Version; + ownerInfo.RefCount = OwnerBundle.RefCount; + ownerInfo.States = OwnerBundle.States; + output.Add(ownerInfo); + + DependBundles.GetBundleDebugInfos(output); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledProvider.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledProvider.cs.meta new file mode 100644 index 0000000..1fd4db3 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4eacb00a7e3873740b599112dfcf0123 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSceneProvider.cs b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSceneProvider.cs new file mode 100644 index 0000000..0908aa4 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSceneProvider.cs @@ -0,0 +1,109 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace YooAsset +{ + internal sealed class BundledSceneProvider : BundledProvider + { + private SceneInstanceParam _param; + private AsyncOperation _asyncOp; + public override float Progress + { + get + { + if (_asyncOp == null) + return 0; + return _asyncOp.progress; + } + } + + public BundledSceneProvider(string scenePath, SceneInstanceParam param) + : base(scenePath, null) + { + _param = param; + } + public override void Update() + { + if (IsDone) + return; + + if (States == EAssetStates.None) + { + States = EAssetStates.CheckBundle; + } + + // 1. 检测资源包 + if (States == EAssetStates.CheckBundle) + { + if (DependBundles.IsDone() == false) + return; + if (OwnerBundle.IsDone() == false) + return; + + if (OwnerBundle.CacheBundle == null) + { + States = EAssetStates.Fail; + InvokeCompletion(); + } + else + { + States = EAssetStates.Loading; + } + } + + // 2. 加载场景 + if (States == EAssetStates.Loading) + { + _asyncOp = SceneManager.LoadSceneAsync(AssetName, _param.LoadMode); + if (_asyncOp != null) + { + _asyncOp.allowSceneActivation = true; + States = EAssetStates.Checking; + } + else + { + Logger.Warning($"Failed to load scene : {AssetName}"); + States = EAssetStates.Fail; + InvokeCompletion(); + } + } + + // 3. 检测加载结果 + if (States == EAssetStates.Checking) + { + if (_asyncOp.isDone) + { + SceneInstance instance = new SceneInstance(_asyncOp); + instance.Scene = SceneManager.GetSceneByName(AssetName); + AssetInstance = instance; + if (_param.ActivateOnLoad) + instance.Activate(); + + States = instance.Scene.IsValid() ? EAssetStates.Success : EAssetStates.Fail; + InvokeCompletion(); + } + } + } + public override void Destory() + { + base.Destory(); + + // 卸载附加场景(异步方式卸载) + if (_param.LoadMode == LoadSceneMode.Additive) + { + var instance = AssetInstance as SceneInstance; + if (instance != null && instance.Scene != null) + { + if (instance.Scene.IsValid() && instance.Scene.isLoaded) + SceneManager.UnloadSceneAsync(instance.Scene); + } + } + } + public override void WaitForAsyncComplete() + { + throw new System.Exception($"Unity scene is not support {nameof(WaitForAsyncComplete)}."); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSceneProvider.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSceneProvider.cs.meta new file mode 100644 index 0000000..b857bef --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSceneProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 38b1b77cff590ca4e808c5068c9bf88b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSubAssetsProvider.cs b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSubAssetsProvider.cs new file mode 100644 index 0000000..e486c0d --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSubAssetsProvider.cs @@ -0,0 +1,105 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace YooAsset +{ + internal sealed class BundledSubAssetsProvider : BundledProvider + { + private AssetBundleRequest _cacheRequest; + public override float Progress + { + get + { + if (_cacheRequest == null) + return 0; + return _cacheRequest.progress; + } + } + + public BundledSubAssetsProvider(string assetPath, System.Type assetType) + : base(assetPath, assetType) + { + } + public override void Update() + { + if (IsDone) + return; + + if (States == EAssetStates.None) + { + States = EAssetStates.CheckBundle; + } + + // 1. 检测资源包 + if (States == EAssetStates.CheckBundle) + { + if (IsWaitForAsyncComplete) + { + DependBundles.WaitForAsyncComplete(); + OwnerBundle.WaitForAsyncComplete(); + } + + if (DependBundles.IsDone() == false) + return; + if (OwnerBundle.IsDone() == false) + return; + + if (OwnerBundle.CacheBundle == null) + { + States = EAssetStates.Fail; + InvokeCompletion(); + } + else + { + States = EAssetStates.Loading; + } + } + + // 2. 加载资源对象 + if (States == EAssetStates.Loading) + { + if (IsWaitForAsyncComplete) + { + if (AssetType == null) + AllAssets = OwnerBundle.CacheBundle.LoadAssetWithSubAssets(AssetName); + else + AllAssets = OwnerBundle.CacheBundle.LoadAssetWithSubAssets(AssetName, AssetType); + } + else + { + if (AssetType == null) + _cacheRequest = OwnerBundle.CacheBundle.LoadAssetWithSubAssetsAsync(AssetName); + else + _cacheRequest = OwnerBundle.CacheBundle.LoadAssetWithSubAssetsAsync(AssetName, AssetType); + } + States = EAssetStates.Checking; + } + + // 3. 检测加载结果 + if (States == EAssetStates.Checking) + { + if (_cacheRequest != null) + { + if (IsWaitForAsyncComplete) + { + // 强制挂起主线程(注意:该操作会很耗时) + Logger.Warning("Suspend the main thread to load unity asset."); + AllAssets = _cacheRequest.allAssets; + } + else + { + if (_cacheRequest.isDone == false) + return; + AllAssets = _cacheRequest.allAssets; + } + } + + States = AllAssets == null ? EAssetStates.Fail : EAssetStates.Success; + if (States == EAssetStates.Fail) + Logger.Warning($"Failed to load sub assets : {AssetName} from bundle : {OwnerBundle.BundleInfo.BundleName}"); + InvokeCompletion(); + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSubAssetsProvider.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSubAssetsProvider.cs.meta new file mode 100644 index 0000000..36e1c3d --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/BundledSubAssetsProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b3763bf52bde23b41a756f0d015cb30d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseAssetProvider.cs b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseAssetProvider.cs new file mode 100644 index 0000000..966ad20 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseAssetProvider.cs @@ -0,0 +1,68 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace YooAsset +{ + internal sealed class DatabaseAssetProvider : AssetProviderBase + { + public override float Progress + { + get + { + if (IsDone) + return 100f; + else + return 0; + } + } + + public DatabaseAssetProvider(string assetPath, System.Type assetType) + : base(assetPath, assetType) + { + } + public override void Update() + { +#if UNITY_EDITOR + if (IsDone) + return; + + if (States == EAssetStates.None) + { + // 检测资源文件是否存在 + string guid = UnityEditor.AssetDatabase.AssetPathToGUID(AssetPath); + if (string.IsNullOrEmpty(guid)) + { + States = EAssetStates.Fail; + InvokeCompletion(); + return; + } + else + { + States = EAssetStates.Loading; + } + + // 注意:模拟异步加载效果提前返回 + if (IsWaitForAsyncComplete == false) + return; + } + + // 1. 加载资源对象 + if (States == EAssetStates.Loading) + { + AssetObject = UnityEditor.AssetDatabase.LoadAssetAtPath(AssetPath, AssetType); + States = EAssetStates.Checking; + } + + // 2. 检测加载结果 + if (States == EAssetStates.Checking) + { + States = AssetObject == null ? EAssetStates.Fail : EAssetStates.Success; + if (States == EAssetStates.Fail) + Logger.Warning($"Failed to load asset object : {AssetPath}"); + InvokeCompletion(); + } +#endif + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseAssetProvider.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseAssetProvider.cs.meta new file mode 100644 index 0000000..39d07f1 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseAssetProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d4022dd2ea39af5458fb1d61ec997347 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSceneProvider.cs b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSceneProvider.cs new file mode 100644 index 0000000..375d263 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSceneProvider.cs @@ -0,0 +1,94 @@ +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace YooAsset +{ + internal sealed class DatabaseSceneProvider : AssetProviderBase + { + private SceneInstanceParam _param; + private AsyncOperation _asyncOp; + public override float Progress + { + get + { + if (_asyncOp == null) + return 0; + return _asyncOp.progress; + } + } + + public DatabaseSceneProvider(string scenePath, SceneInstanceParam param) + : base(scenePath, null) + { + _param = param; + } + public override void Update() + { +#if UNITY_EDITOR + if (IsDone) + return; + + if (States == EAssetStates.None) + { + States = EAssetStates.Loading; + } + + // 1. 加载资源对象 + if (States == EAssetStates.Loading) + { + LoadSceneParameters loadSceneParameters = new LoadSceneParameters(); + loadSceneParameters.loadSceneMode = _param.LoadMode; + _asyncOp = UnityEditor.SceneManagement.EditorSceneManager.LoadSceneAsyncInPlayMode(AssetPath, loadSceneParameters); + if (_asyncOp != null) + { + _asyncOp.allowSceneActivation = true; + States = EAssetStates.Checking; + } + else + { + Logger.Warning($"Failed to load scene : {AssetName}"); + States = EAssetStates.Fail; + InvokeCompletion(); + } + } + + // 2. 检测加载结果 + if (States == EAssetStates.Checking) + { + if (_asyncOp.isDone) + { + SceneInstance instance = new SceneInstance(_asyncOp); + instance.Scene = SceneManager.GetSceneAt(SceneManager.sceneCount - 1); + AssetInstance = instance; + if(_param.ActivateOnLoad) + instance.Activate(); + + States = instance.Scene.IsValid() ? EAssetStates.Success : EAssetStates.Fail; + InvokeCompletion(); + } + } +#endif + } + public override void Destory() + { +#if UNITY_EDITOR + base.Destory(); + + // 卸载附加场景(异步方式卸载) + if (_param.LoadMode == LoadSceneMode.Additive) + { + var instance = AssetInstance as SceneInstance; + if(instance != null && instance.Scene != null) + { + if (instance.Scene.IsValid() && instance.Scene.isLoaded) + SceneManager.UnloadSceneAsync(instance.Scene); + } + } +#endif + } + public override void WaitForAsyncComplete() + { + throw new System.Exception($"Unity scene is not support {nameof(WaitForAsyncComplete)}."); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSceneProvider.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSceneProvider.cs.meta new file mode 100644 index 0000000..d45070d --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSceneProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8252a639423064f498ed22f14912adae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSubAssetsProvider.cs b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSubAssetsProvider.cs new file mode 100644 index 0000000..118264d --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSubAssetsProvider.cs @@ -0,0 +1,75 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace YooAsset +{ + internal sealed class DatabaseSubAssetsProvider : AssetProviderBase + { + public override float Progress + { + get + { + if (IsDone) + return 100f; + else + return 0; + } + } + + public DatabaseSubAssetsProvider(string assetPath, System.Type assetType) + : base(assetPath, assetType) + { + } + public override void Update() + { +#if UNITY_EDITOR + if (IsDone) + return; + + if (States == EAssetStates.None) + { + // 检测资源文件是否存在 + string guid = UnityEditor.AssetDatabase.AssetPathToGUID(AssetPath); + if (string.IsNullOrEmpty(guid)) + { + States = EAssetStates.Fail; + InvokeCompletion(); + return; + } + else + { + States = EAssetStates.Loading; + } + + // 注意:模拟异步加载效果提前返回 + if (IsWaitForAsyncComplete == false) + return; + } + + // 1. 加载资源对象 + if (States == EAssetStates.Loading) + { + var findAssets = UnityEditor.AssetDatabase.LoadAllAssetsAtPath(AssetPath); + List result = new List(findAssets.Length); + foreach (var findObj in findAssets) + { + if (findObj.GetType() == AssetType) + result.Add(findObj); + } + AllAssets = result.ToArray(); + States = EAssetStates.Checking; + } + + // 2. 检测加载结果 + if (States == EAssetStates.Checking) + { + States = AllAssets == null ? EAssetStates.Fail : EAssetStates.Success; + if (States == EAssetStates.Fail) + Logger.Warning($"Failed to load all asset object : {AssetPath}"); + InvokeCompletion(); + } +#endif + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSubAssetsProvider.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSubAssetsProvider.cs.meta new file mode 100644 index 0000000..f229f8a --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/DatabaseSubAssetsProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3837962b901d7ba4abf02a9991ac4c4a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/EAssetStates.cs b/Assets/YooAsset/Runtime/AssetSystem/Provider/EAssetStates.cs new file mode 100644 index 0000000..5551491 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/EAssetStates.cs @@ -0,0 +1,16 @@ + +namespace YooAsset +{ + /// + /// 资源加载状态 + /// + public enum EAssetStates + { + None = 0, + CheckBundle, + Loading, + Checking, + Success, + Fail, + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/EAssetStates.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Provider/EAssetStates.cs.meta new file mode 100644 index 0000000..2ba38a7 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/EAssetStates.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aa6c0fe63326c604bb699be16747c1e2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/IAssetProvider.cs b/Assets/YooAsset/Runtime/AssetSystem/Provider/IAssetProvider.cs new file mode 100644 index 0000000..41da080 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/IAssetProvider.cs @@ -0,0 +1,114 @@ + +namespace YooAsset +{ + /// + /// 资源提供者 + /// + internal interface IAssetProvider + { + /// + /// 资源路径 + /// + string AssetPath { get; } + + /// + /// 资源对象的名称 + /// + string AssetName { get; } + + /// + /// 资源对象的类型 + /// + System.Type AssetType { get; } + + /// + /// 获取的资源对象 + /// + UnityEngine.Object AssetObject { get; } + + /// + /// 获取的资源对象集合 + /// + UnityEngine.Object[] AllAssets { get; } + + /// + /// 扩展的实例对象 + /// + IAssetInstance AssetInstance { get; } + + /// + /// 当前的加载状态 + /// + EAssetStates States { get; } + + /// + /// 引用计数 + /// + int RefCount { get; } + + /// + /// 资源操作句柄 + /// + AssetOperationHandle Handle { get; } + + /// + /// 用户请求的回调 + /// + System.Action Callback { set; get; } + + /// + /// 是否已经销毁 + /// + bool IsDestroyed { get; } + + /// + /// 是否完毕(成功或失败) + /// + bool IsDone { get; } + + /// + /// 是否有效(AssetFileLoader销毁会导致Provider无效) + /// + bool IsValid { get; } + + /// + /// 加载进度 + /// + float Progress { get; } + + /// + /// 轮询更新方法 + /// + void Update(); + + /// + /// 销毁资源对象 + /// + void Destory(); + + /// + /// 引用计数递加 + /// + void Reference(); + + /// + /// 引用计数递减 + /// + void Release(); + + /// + /// 是否可以销毁 + /// + bool CanDestroy(); + + /// + /// 等待异步执行完毕 + /// + void WaitForAsyncComplete(); + + /// + /// 异步操作任务 + /// + System.Threading.Tasks.Task Task { get; } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/IAssetProvider.cs.meta b/Assets/YooAsset/Runtime/AssetSystem/Provider/IAssetProvider.cs.meta new file mode 100644 index 0000000..5de2355 --- /dev/null +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/IAssetProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 54f7b01043e68994189cc954e55813aa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Logger.meta b/Assets/YooAsset/Runtime/Logger.meta new file mode 100644 index 0000000..92cf8b9 --- /dev/null +++ b/Assets/YooAsset/Runtime/Logger.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f3763af65a2f76945bd4a6adc720ec07 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Logger/DebugSummy.cs b/Assets/YooAsset/Runtime/Logger/DebugSummy.cs new file mode 100644 index 0000000..f87e4af --- /dev/null +++ b/Assets/YooAsset/Runtime/Logger/DebugSummy.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset +{ + public class DebugSummy + { + /// + /// 资源包调试信息 + /// + public class BundleInfo + { + /// + /// 资源包名称 + /// + public string BundleName { set; get; } + + /// + /// 资源版本 + /// + public int Version { set; get; } + + /// + /// 引用计数 + /// + public int RefCount { set; get; } + + /// + /// 加载状态 + /// + public ELoaderStates States { set; get; } + } + + /// + /// 资源加载对象调试信息 + /// + public class ProviderInfo : IComparer, IComparable + { + /// + /// 资源对象路径 + /// + public string AssetPath { set; get; } + + /// + /// 引用计数 + /// + public int RefCount { set; get; } + + /// + /// 加载状态 + /// + public EAssetStates States { set; get; } + + /// + /// 依赖的资源包列表 + /// + public readonly List BundleInfos = new List(); + + public int CompareTo(ProviderInfo other) + { + return Compare(this, other); + } + public int Compare(ProviderInfo a, ProviderInfo b) + { + return string.CompareOrdinal(a.AssetPath, b.AssetPath); + } + } + + + public readonly List ProviderInfos = new List(1000); + public int BundleCount { set; get; } + public int AssetCount { set; get; } + + public void ClearAll() + { + ProviderInfos.Clear(); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Logger/DebugSummy.cs.meta b/Assets/YooAsset/Runtime/Logger/DebugSummy.cs.meta new file mode 100644 index 0000000..2a05eed --- /dev/null +++ b/Assets/YooAsset/Runtime/Logger/DebugSummy.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 04568a15f0fdb58438568dac56c588c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Logger/Logger.cs b/Assets/YooAsset/Runtime/Logger/Logger.cs new file mode 100644 index 0000000..ae27c91 --- /dev/null +++ b/Assets/YooAsset/Runtime/Logger/Logger.cs @@ -0,0 +1,40 @@ +using System.Diagnostics; + +namespace YooAsset +{ + internal static class Logger + { + /// + /// 日志 + /// + [Conditional("DEBUG")] + public static void Log(string info) + { + UnityEngine.Debug.Log(info); + } + + /// + /// 警告 + /// + public static void Warning(string info) + { + UnityEngine.Debug.LogWarning(info); + } + + /// + /// 错误 + /// + public static void Error(string info) + { + UnityEngine.Debug.LogError(info); + } + + /// + /// 异常 + /// + public static void Exception(System.Exception exception) + { + UnityEngine.Debug.LogException(exception); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Logger/Logger.cs.meta b/Assets/YooAsset/Runtime/Logger/Logger.cs.meta new file mode 100644 index 0000000..b29bd42 --- /dev/null +++ b/Assets/YooAsset/Runtime/Logger/Logger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2ffcef42f3747ab43908595778accc29 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Operation.meta b/Assets/YooAsset/Runtime/Operation.meta new file mode 100644 index 0000000..4f54bd9 --- /dev/null +++ b/Assets/YooAsset/Runtime/Operation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c94cb69081e4d394e80f6fca6da4366f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Operation/AsyncOperationBase.cs b/Assets/YooAsset/Runtime/Operation/AsyncOperationBase.cs new file mode 100644 index 0000000..21ecb83 --- /dev/null +++ b/Assets/YooAsset/Runtime/Operation/AsyncOperationBase.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset +{ + public abstract class AsyncOperationBase : IEnumerator + { + /// + /// 状态 + /// + public EOperationStatus Status { get; protected set; } = EOperationStatus.None; + + /// + /// 错误信息 + /// + public string Error { get; protected set; } = string.Empty; + + /// + /// 是否已经完成 + /// + public bool IsDone + { + get + { + return Status == EOperationStatus.Failed || Status == EOperationStatus.Succeed; + } + } + + /// + /// 用户请求的回调 + /// + private Action _callback; + + /// + /// 完成事件 + /// + public event Action Completed + { + add + { + if (IsDone) + value.Invoke(this); + else + _callback += value; + } + remove + { + _callback -= value; + } + } + + internal abstract void Start(); + internal abstract void Update(); + internal void Finish() + { + _callback?.Invoke(this); + } + + #region 异步相关 + public bool MoveNext() + { + return !IsDone; + } + public void Reset() + { + } + public object Current => null; + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Operation/AsyncOperationBase.cs.meta b/Assets/YooAsset/Runtime/Operation/AsyncOperationBase.cs.meta new file mode 100644 index 0000000..cae4e49 --- /dev/null +++ b/Assets/YooAsset/Runtime/Operation/AsyncOperationBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e928a1c08d679ad44bcb435678667fd7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Operation/EOperationStatus.cs b/Assets/YooAsset/Runtime/Operation/EOperationStatus.cs new file mode 100644 index 0000000..5cecac5 --- /dev/null +++ b/Assets/YooAsset/Runtime/Operation/EOperationStatus.cs @@ -0,0 +1,10 @@ + +namespace YooAsset +{ + public enum EOperationStatus + { + None, + Succeed, + Failed + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Operation/EOperationStatus.cs.meta b/Assets/YooAsset/Runtime/Operation/EOperationStatus.cs.meta new file mode 100644 index 0000000..ad348b1 --- /dev/null +++ b/Assets/YooAsset/Runtime/Operation/EOperationStatus.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b0b0ed13c7433847a91c0ffd8306125 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Operation/OperationUpdater.cs b/Assets/YooAsset/Runtime/Operation/OperationUpdater.cs new file mode 100644 index 0000000..e15a79e --- /dev/null +++ b/Assets/YooAsset/Runtime/Operation/OperationUpdater.cs @@ -0,0 +1,29 @@ +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset +{ + internal class OperationUpdater + { + private static readonly List _operations = new List(100); + + public static void ProcessOperaiton(AsyncOperationBase operationBase) + { + _operations.Add(operationBase); + operationBase.Start(); + } + + public static void Update() + { + for (int i = _operations.Count - 1; i >= 0; i--) + { + _operations[i].Update(); + if (_operations[i].IsDone) + { + _operations[i].Finish(); + _operations.RemoveAt(i); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Operation/OperationUpdater.cs.meta b/Assets/YooAsset/Runtime/Operation/OperationUpdater.cs.meta new file mode 100644 index 0000000..cd12ac9 --- /dev/null +++ b/Assets/YooAsset/Runtime/Operation/OperationUpdater.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b20b2efe14ab7e8498edb9819d24a172 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem.meta b/Assets/YooAsset/Runtime/PatchSystem.meta new file mode 100644 index 0000000..3983b1d --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5ecf79384c5577a49a9908067c7ca646 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download.meta b/Assets/YooAsset/Runtime/PatchSystem/Download.meta new file mode 100644 index 0000000..89a2ea1 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 715017c3b0bb2a3429d8f021b0d9a2b2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs b/Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs new file mode 100644 index 0000000..1147f9a --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs @@ -0,0 +1,146 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using YooAsset.Utility; + +namespace YooAsset +{ + /// + /// 1. 保证每一时刻资源文件只存在一个下载器 + /// 2. 保证下载器下载完成后立刻验证并缓存 + /// 3. 保证资源文件不会被重复下载 + /// + internal static class DownloadSystem + { + private static readonly Dictionary _downloaderDic = new Dictionary(); + private static readonly List _removeList = new List(100); + private static readonly Dictionary _cachedHashList = new Dictionary(1000); + + + /// + /// 更新所有下载器 + /// + public static void Update() + { + // 更新下载器 + _removeList.Clear(); + foreach (var valuePair in _downloaderDic) + { + var downloader = valuePair.Value; + downloader.Update(); + if (downloader.IsDone()) + _removeList.Add(valuePair.Key); + } + + // 移除下载器 + foreach (var key in _removeList) + { + _downloaderDic.Remove(key); + } + } + + /// + /// 开始下载资源文件 + /// 注意:只有第一次请求的参数才是有效的 + /// + public static FileDownloader BeginDownload(AssetBundleInfo bundleInfo, int failedTryAgain, int timeout = 60) + { + // 查询存在的下载器 + if (_downloaderDic.TryGetValue(bundleInfo.Hash, out var downloader)) + { + return downloader; + } + + // 如果资源已经缓存 + if(ContainsVerifyFile(bundleInfo.Hash)) + { + var newDownloader = new FileDownloader(bundleInfo); + newDownloader.SetDone(); + return newDownloader; + } + + // 创建新的下载器 + { + Logger.Log($"Beginning to download file : {bundleInfo.BundleName} URL : {bundleInfo.RemoteMainURL}"); + FileUtility.CreateFileDirectory(bundleInfo.LocalPath); + var newDownloader = new FileDownloader(bundleInfo); + newDownloader.SendRequest(failedTryAgain, timeout); + _downloaderDic.Add(bundleInfo.Hash, newDownloader); + return newDownloader; + } + } + + /// + /// 获取下载器的总数 + /// + public static int GetDownloaderTotalCount() + { + return _downloaderDic.Count; + } + + /// + /// 查询是否为验证文件 + /// 注意:被收录的文件完整性是绝对有效的 + /// + public static bool ContainsVerifyFile(string hash) + { + if (_cachedHashList.ContainsKey(hash)) + { + string filePath = PatchHelper.MakeSandboxCacheFilePath(hash); + if (File.Exists(filePath)) + { + return true; + } + else + { + string bundleName = _cachedHashList[hash]; + _cachedHashList.Remove(hash); + Logger.Error($"Cache file is missing : {bundleName} Hash : {hash}"); + return false; + } + } + else + { + return false; + } + } + + /// + /// 缓存验证过的文件 + /// + public static void CacheVerifyFile(string hash, string bundleName) + { + if (_cachedHashList.ContainsKey(hash) == false) + { + Logger.Log($"Cache verify file : {bundleName} Hash : {hash}"); + _cachedHashList.Add(hash, bundleName); + } + } + + // 验证文件完整性 + public static bool CheckContentIntegrity(AssetBundleInfo bundleInfo) + { + return CheckContentIntegrity(bundleInfo.LocalPath, bundleInfo.SizeBytes, bundleInfo.CRC); + } + public static bool CheckContentIntegrity(PatchBundle patchBundle) + { + string filePath = PatchHelper.MakeSandboxCacheFilePath(patchBundle.Hash); + return CheckContentIntegrity(filePath, patchBundle.SizeBytes, patchBundle.CRC); + } + public static bool CheckContentIntegrity(string filePath, long size, string crc) + { + if (File.Exists(filePath) == false) + return false; + + // 先验证文件大小 + long fileSize = FileUtility.GetFileSize(filePath); + if (fileSize != size) + return false; + + // 再验证文件CRC + string fileCRC = HashUtility.FileCRC32(filePath); + return fileCRC == crc; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs.meta new file mode 100644 index 0000000..8b9112e --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d19eae1f43ecf6347925a06730a6c846 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/EDownloaderStates.cs b/Assets/YooAsset/Runtime/PatchSystem/Download/EDownloaderStates.cs new file mode 100644 index 0000000..a296a8f --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/EDownloaderStates.cs @@ -0,0 +1,14 @@ + +namespace YooAsset +{ + /// + /// 下载状态 + /// + public enum EDownloaderStates + { + None, + Loading, + Failed, + Succeed, + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/EDownloaderStates.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/Download/EDownloaderStates.cs.meta new file mode 100644 index 0000000..5fe3ef7 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/EDownloaderStates.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb052d4c6ef18f54d9407cb48eafdfb9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs b/Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs new file mode 100644 index 0000000..8cb2f22 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEngine.Networking; + +namespace YooAsset +{ + internal class FileDownloader + { + private enum ESteps + { + None, + CreateDownload, + CheckDownload, + TryAgain, + Succeed, + Failed, + } + + public AssetBundleInfo BundleInfo { private set; get; } + private UnityWebRequest _webRequest; + private UnityWebRequestAsyncOperation _operationHandle; + + private ESteps _steps = ESteps.None; + private string _lastError = string.Empty; + + private int _timeout; + private int _failedTryAgain; + private int _requestCount; + private string _requestURL; + + // 重置变量 + private bool _isAbort = false; + private ulong _latestDownloadBytes; + private float _latestDownloadRealtime; + private float _tryAgainTimer; + + /// + /// 下载进度(0-100f) + /// + public float DownloadProgress { private set; get; } + + /// + /// 已经下载的总字节数 + /// + public ulong DownloadedBytes { private set; get; } + + + internal FileDownloader(AssetBundleInfo bundleInfo) + { + BundleInfo = bundleInfo; + } + internal void SendRequest(int failedTryAgain, int timeout) + { + if (string.IsNullOrEmpty(BundleInfo.LocalPath)) + throw new ArgumentNullException(); + + if (_steps == ESteps.None) + { + _failedTryAgain = failedTryAgain; + _timeout = timeout; + _steps = ESteps.CreateDownload; + } + } + internal void Update() + { + if (_steps == ESteps.None) + return; + if (_steps == ESteps.Failed || _steps == ESteps.Succeed) + return; + + // 创建下载器 + if (_steps == ESteps.CreateDownload) + { + // 重置变量 + DownloadProgress = 0f; + DownloadedBytes = 0; + _isAbort = false; + _latestDownloadBytes = 0; + _latestDownloadRealtime = Time.realtimeSinceStartup; + _tryAgainTimer = 0f; + + _requestCount++; + _requestURL = GetRequestURL(); + _webRequest = new UnityWebRequest(_requestURL, UnityWebRequest.kHttpVerbGET); + DownloadHandlerFile handler = new DownloadHandlerFile(BundleInfo.LocalPath); + handler.removeFileOnAbort = true; + _webRequest.downloadHandler = handler; + _webRequest.disposeDownloadHandlerOnDispose = true; + _operationHandle = _webRequest.SendWebRequest(); + _steps = ESteps.CheckDownload; + } + + // 检测下载结果 + if (_steps == ESteps.CheckDownload) + { + DownloadProgress = _webRequest.downloadProgress * 100f; + DownloadedBytes = _webRequest.downloadedBytes; + if (_operationHandle.isDone == false) + { + CheckTimeout(); + return; + } + + // 检查网络错误 + bool isError = false; +#if UNITY_2020_3_OR_NEWER + if (_webRequest.result != UnityWebRequest.Result.Success) + { + isError = true; + _lastError = _webRequest.error; + } +#else + if (_webRequest.isNetworkError || _webRequest.isHttpError) + { + isError = true; + _lastError = _webRequest.error; + } +#endif + + // 检查文件完整性 + if (isError == false) + { + // 注意:如果文件验证失败需要删除文件 + if (DownloadSystem.CheckContentIntegrity(BundleInfo) == false) + { + isError = true; + _lastError = $"Verification failed"; + if (File.Exists(BundleInfo.LocalPath)) + File.Delete(BundleInfo.LocalPath); + } + } + + if (isError) + { + ReportError(); + if (_failedTryAgain > 0) + _steps = ESteps.TryAgain; + else + _steps = ESteps.Failed; + } + else + { + _steps = ESteps.Succeed; + DownloadSystem.CacheVerifyFile(BundleInfo.Hash, BundleInfo.BundleName); + } + + // 释放下载器 + DisposeWebRequest(); + } + + // 重新尝试下载 + if (_steps == ESteps.TryAgain) + { + _tryAgainTimer += Time.unscaledDeltaTime; + if (_tryAgainTimer > 0.5f) + { + _failedTryAgain--; + _steps = ESteps.CreateDownload; + Logger.Warning($"Try again download : {_requestURL}"); + } + } + } + internal void SetDone() + { + _steps = ESteps.Succeed; + } + + private string GetRequestURL() + { + // 轮流返回请求地址 + if (_requestCount % 2 == 0) + return BundleInfo.RemoteFallbackURL; + else + return BundleInfo.RemoteMainURL; + } + private void CheckTimeout() + { + // 注意:在连续时间段内无新增下载数据及判定为超时 + if (_isAbort == false) + { + if (_latestDownloadBytes != DownloadedBytes) + { + _latestDownloadBytes = DownloadedBytes; + _latestDownloadRealtime = Time.realtimeSinceStartup; + } + + float offset = Time.realtimeSinceStartup - _latestDownloadRealtime; + if (offset > _timeout) + { + Logger.Warning($"Web file request timeout : {_requestURL}"); + _webRequest.Abort(); + _isAbort = true; + } + } + } + private void DisposeWebRequest() + { + if (_webRequest != null) + { + _webRequest.Dispose(); + _webRequest = null; + _operationHandle = null; + } + } + + /// + /// 检测下载器是否已经完成(无论成功或失败) + /// + public bool IsDone() + { + return _steps == ESteps.Succeed || _steps == ESteps.Failed; + } + + /// + /// 下载过程是否发生错误 + /// + /// + public bool HasError() + { + return _steps == ESteps.Failed; + } + + /// + /// 报告错误信息 + /// + public void ReportError() + { + Logger.Error($"Failed to download : {_requestURL} Error : {_lastError}"); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs.meta new file mode 100644 index 0000000..5438603 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4c6e1a8bc8d5e664395395daa772ddd7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs b/Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs new file mode 100644 index 0000000..3252c81 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Security; +using System.Threading; + +namespace YooAsset +{ + internal class HttpDownloader + { + private enum ESteps + { + None, + CreateDownload, + CheckDownload, + Succeed, + Failed, + } + + public AssetBundleInfo BundleInfo { private set; get; } + private ESteps _steps = ESteps.None; + + // 线程 + private bool _threadOver = false; + private bool _threadResult = false; + private string _threadError = string.Empty; + private Thread _thread; + + // 保留参数 + private int _timeout; + private int _failedTryAgain; + private int _requestCount; + + // 下载结果 + private string _downloadError = string.Empty; + private float _downloadProgress = 0f; + private long _downloadBytes = 0; + + /// + /// 下载进度(0-100f) + /// + public float DownloadProgress + { + get + { + return _downloadProgress; + } + } + + /// + /// 已经下载的总字节数 + /// + public long DownloadedBytes + { + get + { + return _downloadBytes; + } + } + + + internal void SendRequest(int failedTryAgain, int timeout) + { + } + internal void Update() + { + if (_steps == ESteps.None) + return; + if (_steps == ESteps.Succeed || _steps == ESteps.Failed) + return; + + if(_steps == ESteps.CreateDownload) + { + _downloadError = string.Empty; + _downloadProgress = 0f; + _downloadBytes = 0; + + _threadOver = false; + _threadResult = false; + _threadError = string.Empty; + _thread = new Thread(ThreadRun); + _thread.IsBackground = true; + _thread.Start(); + _steps = ESteps.CheckDownload; + } + + if(_steps == ESteps.CheckDownload) + { + if (_threadOver == false) + return; + + if(_thread != null) + { + _thread.Abort(); + _thread = null; + } + + _downloadError = _threadError; + if (_threadResult) + { + DownloadSystem.CacheVerifyFile(BundleInfo.Hash, BundleInfo.BundleName); + _steps = ESteps.Succeed; + } + else + { + // 失败后重新尝试 + if(_failedTryAgain > 0) + { + _failedTryAgain--; + _steps = ESteps.CreateDownload; + } + else + { + _steps = ESteps.Failed; + } + } + } + } + internal void SetDone() + { + _steps = ESteps.Succeed; + } + + /// + /// 检测下载器是否已经完成(无论成功或失败) + /// + public bool IsDone() + { + return _steps == ESteps.Succeed || _steps == ESteps.Failed; + } + + /// + /// 下载过程是否发生错误 + /// + public bool HasError() + { + return _steps == ESteps.Failed; + } + + /// + /// 报告错误信息 + /// + public void ReportError() + { + Logger.Error(_downloadError); + } + + + #region 多线程下载 + public const int BufferSize = 1042 * 4; + private void ThreadRun() + { + string url = GetRequestURL(); + string savePath = BundleInfo.LocalPath; + long fileTotalSize = BundleInfo.SizeBytes; + + FileStream fileStream = null; + Stream webStream = null; + HttpWebResponse fileResponse = null; + + try + { + // 创建文件流 + fileStream = new FileStream(savePath, FileMode.OpenOrCreate, FileAccess.Write); + long fileLength = fileStream.Length; + + // 创建HTTP下载请求 + HttpWebRequest fileRequest = WebRequest.Create(url) as HttpWebRequest; + fileRequest.Timeout = _timeout; + fileRequest.ReadWriteTimeout = _timeout; + fileRequest.ProtocolVersion = HttpVersion.Version10; + if (fileLength > 0) + { + // 注意:设置远端请求文件的起始位置 + fileRequest.AddRange(fileLength); + // 注意:设置本地文件流的起始位置 + fileStream.Seek(fileLength, SeekOrigin.Begin); + } + + // 读取下载数据并保存到文件 + fileResponse = fileRequest.GetResponse() as HttpWebResponse; + webStream = fileResponse.GetResponseStream(); + byte[] buffer = new byte[BufferSize]; + while (true) + { + int length = webStream.Read(buffer, 0, buffer.Length); + if (length <= 0) + break; + + fileStream.Write(buffer, 0, length); + + // 计算下载进度 + // 注意:原子操作保证数据安全 + fileLength += length; + float progress = (fileLength / fileTotalSize) * 100f; + _downloadProgress = progress; + _downloadBytes = fileLength; + } + + // 验证下载文件完整性 + bool verfiyResult = DownloadSystem.CheckContentIntegrity(savePath, BundleInfo.SizeBytes, BundleInfo.CRC); + if(verfiyResult) + { + _threadResult = true; + } + else + { + _threadResult = false; + _threadError = $"Verify file content failed : {BundleInfo.Hash}"; + } + } + catch (Exception e) + { + _threadResult = false; + _threadError = e.Message; + } + finally + { + if (webStream != null) + { + webStream.Close(); + webStream.Dispose(); + } + + if (fileResponse != null) + { + fileResponse.Close(); + } + + if (fileStream != null) + { + fileStream.Close(); + fileStream.Dispose(); + } + + _threadOver = true; + } + } + private string GetRequestURL() + { + // 轮流返回请求地址 + _requestCount++; + if (_requestCount % 2 == 0) + return BundleInfo.RemoteFallbackURL; + else + return BundleInfo.RemoteMainURL; + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs.meta new file mode 100644 index 0000000..b2fdeed --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 057a358d2d0c92b47add59dac8bef783 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/PatchDownloader.cs b/Assets/YooAsset/Runtime/PatchSystem/Download/PatchDownloader.cs new file mode 100644 index 0000000..645cdf4 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/PatchDownloader.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset +{ + /// + /// 补丁下载器 + /// + public class PatchDownloader : IEnumerator + { + private const int MAX_LOADER_COUNT = 64; + + public delegate void OnDownloadOver(bool isSucceed); + public delegate void OnDownloadProgress(int totalDownloadCount, int currentDownloadCoun, long totalDownloadBytes, long currentDownloadBytes); + public delegate void OnPatchFileDownloadFailed(string fileName); + + private readonly HostPlayModeImpl _playModeImpl; + private readonly int _fileLoadingMaxNumber; + private readonly int _failedTryAgain; + private readonly List _downloadList; + private readonly List _loadFailedList = new List(); + private readonly List _downloaders = new List(); + private readonly List _removeList = new List(MAX_LOADER_COUNT); + + // 数据相关 + public EDownloaderStates DownloadStates { private set; get; } + public int TotalDownloadCount { private set; get; } + public long TotalDownloadBytes { private set; get; } + public int CurrentDownloadCount { private set; get; } + public long CurrentDownloadBytes { private set; get; } + private long _lastDownloadBytes = 0; + private int _lastDownloadCount = 0; + + // 委托相关 + public OnDownloadOver OnDownloadOverCallback { set; get; } + public OnDownloadProgress OnDownloadProgressCallback { set; get; } + public OnPatchFileDownloadFailed OnPatchFileDownloadFailedCallback { set; get; } + + + private PatchDownloader() + { + } + internal PatchDownloader(HostPlayModeImpl playModeImpl, List downloadList, int fileLoadingMaxNumber, int failedTryAgain) + { + _playModeImpl = playModeImpl; + _downloadList = downloadList; + _fileLoadingMaxNumber = UnityEngine.Mathf.Clamp(fileLoadingMaxNumber, 1, MAX_LOADER_COUNT); ; + _failedTryAgain = failedTryAgain; + + DownloadStates = EDownloaderStates.None; + TotalDownloadCount = downloadList.Count; + foreach (var patchBundle in downloadList) + { + TotalDownloadBytes += patchBundle.SizeBytes; + } + } + + /// + /// 是否完毕,无论成功或失败 + /// + public bool IsDone() + { + return DownloadStates == EDownloaderStates.Failed || DownloadStates == EDownloaderStates.Succeed; + } + + /// + /// 开始下载 + /// + public void Download() + { + if (DownloadStates != EDownloaderStates.None) + { + Logger.Warning($"{nameof(PatchDownloader)} is already running."); + return; + } + + Logger.Log($"Begine to download : {TotalDownloadCount} files and {TotalDownloadBytes} bytes"); + DownloadStates = EDownloaderStates.Loading; + } + + /// + /// 更新下载器 + /// + public void Update() + { + if (DownloadStates != EDownloaderStates.Loading) + return; + + // 检测下载器结果 + _removeList.Clear(); + long downloadBytes = CurrentDownloadBytes; + foreach (var downloader in _downloaders) + { + downloadBytes += (long)downloader.DownloadedBytes; + if (downloader.IsDone() == false) + continue; + + AssetBundleInfo bundleInfo = downloader.BundleInfo; + + // 检测是否下载失败 + if (downloader.HasError()) + { + downloader.ReportError(); + _removeList.Add(downloader); + _loadFailedList.Add(bundleInfo); + continue; + } + + // 下载成功 + _removeList.Add(downloader); + CurrentDownloadCount++; + CurrentDownloadBytes += bundleInfo.SizeBytes; + } + + // 移除已经完成的下载器(无论成功或失败) + foreach (var loader in _removeList) + { + _downloaders.Remove(loader); + } + + // 如果下载进度发生变化 + if (_lastDownloadBytes != downloadBytes || _lastDownloadCount != CurrentDownloadCount) + { + _lastDownloadBytes = downloadBytes; + _lastDownloadCount = CurrentDownloadCount; + OnDownloadProgressCallback?.Invoke(TotalDownloadCount, _lastDownloadCount, TotalDownloadBytes, _lastDownloadBytes); + } + + // 动态创建新的下载器到最大数量限制 + // 注意:如果期间有下载失败的文件,暂停动态创建下载器 + if (_downloadList.Count > 0 && _loadFailedList.Count == 0) + { + if (_downloaders.Count < _fileLoadingMaxNumber) + { + int index = _downloadList.Count - 1; + var operation = DownloadSystem.BeginDownload(_downloadList[index], _failedTryAgain); + _downloaders.Add(operation); + _downloadList.RemoveAt(index); + } + } + + // 下载结算 + if (_downloaders.Count == 0) + { + if (_loadFailedList.Count > 0) + { + DownloadStates = EDownloaderStates.Failed; + OnPatchFileDownloadFailedCallback?.Invoke(_loadFailedList[0].BundleName); + OnDownloadOverCallback?.Invoke(false); + } + else + { + // 结算成功 + DownloadStates = EDownloaderStates.Succeed; + OnDownloadOverCallback?.Invoke(true); + } + } + } + + #region 异步相关 + bool IEnumerator.MoveNext() + { + return !IsDone(); + } + void IEnumerator.Reset() + { + } + object IEnumerator.Current + { + get { return null; } + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/PatchDownloader.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/Download/PatchDownloader.cs.meta new file mode 100644 index 0000000..4574bfc --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/PatchDownloader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e6483a640bdc429459305f148a863397 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs b/Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs new file mode 100644 index 0000000..fb5f5c5 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Threading; + +namespace YooAsset +{ + /// + /// 同步其它线程里的回调到主线程里 + /// 注意:Unity3D中需要设置Scripting Runtime Version为.NET4.6 + /// + internal sealed class ThreadSyncContext : SynchronizationContext + { + private readonly ConcurrentQueue _safeQueue = new ConcurrentQueue(); + + /// + /// 更新同步队列 + /// + public void Update() + { + while (true) + { + if (_safeQueue.TryDequeue(out Action action) == false) + return; + action.Invoke(); + } + } + + /// + /// 向同步队列里投递一个回调方法 + /// + public override void Post(SendOrPostCallback callback, object state) + { + Action action = new Action(() => { callback(state); }); + _safeQueue.Enqueue(action); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs.meta new file mode 100644 index 0000000..fbe09c1 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c8ce2c52a3e9964fa50a9c031e4e593 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebRequester.cs b/Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebRequester.cs new file mode 100644 index 0000000..0e11dcc --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebRequester.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine.Networking; + +namespace YooAsset +{ + /// + /// 下载器 + /// 说明:UnityWebRequest(UWR) supports reading streaming assets since 2017.1 + /// + internal class UnityWebRequester + { + protected UnityWebRequest _webRequest; + protected UnityWebRequestAsyncOperation _operationHandle; + + /// + /// 请求URL地址 + /// + public string URL { private set; get; } + + + /// + /// 发送GET请求 + /// + /// 超时:从请求开始计时 + public void SendRequest(string url, int timeout = 0) + { + if (_webRequest == null) + { + URL = url; + _webRequest = new UnityWebRequest(URL, UnityWebRequest.kHttpVerbGET); + DownloadHandlerBuffer handler = new DownloadHandlerBuffer(); + _webRequest.downloadHandler = handler; + _webRequest.disposeDownloadHandlerOnDispose = true; + _webRequest.timeout = timeout; + _operationHandle = _webRequest.SendWebRequest(); + } + } + + /// + /// 获取下载的字节数据 + /// + public byte[] GetData() + { + if (_webRequest != null && IsDone()) + return _webRequest.downloadHandler.data; + else + return null; + } + + /// + /// 获取下载的文本数据 + /// + public string GetText() + { + if (_webRequest != null && IsDone()) + return _webRequest.downloadHandler.text; + else + return null; + } + + /// + /// 释放下载器 + /// + public void Dispose() + { + if (_webRequest != null) + { + _webRequest.Dispose(); + _webRequest = null; + _operationHandle = null; + } + } + + /// + /// 是否完毕(无论成功失败) + /// + public bool IsDone() + { + if (_operationHandle == null) + return false; + return _operationHandle.isDone; + } + + /// + /// 下载是否发生错误 + /// + public bool HasError() + { +#if UNITY_2020_3_OR_NEWER + return _webRequest.result != UnityWebRequest.Result.Success; +#else + if (_webRequest.isNetworkError || _webRequest.isHttpError) + return true; + else + return false; +#endif + } + + /// + /// 获取错误信息 + /// + public string GetError() + { + if (_webRequest != null) + { + return $"URL : {URL} Error : {_webRequest.error}"; + } + return string.Empty; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebRequester.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebRequester.cs.meta new file mode 100644 index 0000000..1e4cc2d --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebRequester.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b7557eb146572de49a1ec9b3f3c0b706 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Operations.meta b/Assets/YooAsset/Runtime/PatchSystem/Operations.meta new file mode 100644 index 0000000..269e890 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Operations.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4812fd895ef2d7e4a97956be2c6f8170 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Operations/InitializationOperation.cs b/Assets/YooAsset/Runtime/PatchSystem/Operations/InitializationOperation.cs new file mode 100644 index 0000000..19562d4 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Operations/InitializationOperation.cs @@ -0,0 +1,197 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; + +namespace YooAsset +{ + /// + /// 初始化操作 + /// + public abstract class InitializationOperation : AsyncOperationBase + { + } + + /// + /// 编辑器下模拟运行的初始化操作 + /// + internal class EditorModeInitializationOperation : InitializationOperation + { + internal override void Start() + { + Status = EOperationStatus.Succeed; + } + internal override void Update() + { + } + } + + /// + /// 离线模式的初始化操作 + /// + internal class OfflinePlayModeInitializationOperation : InitializationOperation + { + private enum ESteps + { + Idle, + LoadAppManifest, + CheckAppManifest, + Done, + } + + private OfflinePlayModeImpl _impl; + private ESteps _steps = ESteps.Idle; + private UnityWebRequester _downloader; + private string _downloadURL; + + internal OfflinePlayModeInitializationOperation(OfflinePlayModeImpl impl) + { + _impl = impl; + } + internal override void Start() + { + _steps = ESteps.LoadAppManifest; + } + internal override void Update() + { + if (_steps == ESteps.Idle) + return; + + if (_steps == ESteps.LoadAppManifest) + { + string filePath = AssetPathHelper.MakeStreamingLoadPath(ResourceSettingData.Setting.PatchManifestFileName); + _downloadURL = AssetPathHelper.ConvertToWWWPath(filePath); + _downloader = new UnityWebRequester(); + _downloader.SendRequest(_downloadURL); + _steps = ESteps.CheckAppManifest; + } + + if (_steps == ESteps.CheckAppManifest) + { + if (_downloader.IsDone() == false) + return; + + if (_downloader.HasError()) + { + Error = _downloader.GetError(); + Status = EOperationStatus.Failed; + _downloader.Dispose(); + _steps = ESteps.Done; + throw new System.Exception($"Fatal error : Failed load application patch manifest file : {_downloadURL}"); + } + + // 解析APP里的补丁清单 + _impl.AppPatchManifest = PatchManifest.Deserialize(_downloader.GetText()); + _downloader.Dispose(); + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + } + } + } + + /// + /// 网络模式的初始化操作 + /// + internal class HostPlayModeInitializationOperation : InitializationOperation + { + private enum ESteps + { + Idle, + InitCache, + LoadAppManifest, + CheckAppManifest, + LoadSandboxManifest, + Done, + } + + private HostPlayModeImpl _impl; + private ESteps _steps = ESteps.Idle; + private UnityWebRequester _downloader; + private string _downloadURL; + + internal HostPlayModeInitializationOperation(HostPlayModeImpl impl) + { + _impl = impl; + } + internal override void Start() + { + _steps = ESteps.InitCache; + } + internal override void Update() + { + if (_steps == ESteps.Idle) + return; + + if (_steps == ESteps.InitCache) + { + // 每次启动时比对APP版本号是否一致 + PatchCache cache = PatchCache.LoadCache(); + if (cache.CacheAppVersion != Application.version) + { + Logger.Warning($"Cache is dirty ! Cache app version is {cache.CacheAppVersion}, Current app version is {Application.version}"); + + // 注意:在覆盖安装的时候,会保留APP沙盒目录,可以选择清空缓存目录 + if (_impl.ClearCacheWhenDirty) + { + Logger.Warning("Clear cache files."); + PatchHelper.DeleteSandboxCacheFolder(); + } + + // 删除清单文件 + PatchHelper.DeleteSandboxPatchManifestFile(); + // 更新缓存文件 + PatchCache.UpdateCache(); + } + _steps = ESteps.LoadAppManifest; + } + + if (_steps == ESteps.LoadAppManifest) + { + // 加载APP内的补丁清单 + Logger.Log($"Load application patch manifest."); + string filePath = AssetPathHelper.MakeStreamingLoadPath(ResourceSettingData.Setting.PatchManifestFileName); + _downloadURL = AssetPathHelper.ConvertToWWWPath(filePath); + _downloader = new UnityWebRequester(); + _downloader.SendRequest(_downloadURL); + _steps = ESteps.CheckAppManifest; + } + + if (_steps == ESteps.CheckAppManifest) + { + if (_downloader.IsDone() == false) + return; + + if (_downloader.HasError()) + { + Error = _downloader.GetError(); + Status = EOperationStatus.Failed; + _downloader.Dispose(); + _steps = ESteps.Done; + throw new System.Exception($"Fatal error : Failed load application patch manifest file : {_downloadURL}"); + } + + // 解析补丁清单 + string jsonData = _downloader.GetText(); + _impl.AppPatchManifest = PatchManifest.Deserialize(jsonData); + _impl.LocalPatchManifest = _impl.AppPatchManifest; + _downloader.Dispose(); + _steps = ESteps.LoadSandboxManifest; + } + + if (_steps == ESteps.LoadSandboxManifest) + { + // 加载沙盒内的补丁清单 + if (PatchHelper.CheckSandboxPatchManifestFileExist()) + { + Logger.Log($"Load sandbox patch manifest."); + string filePath = AssetPathHelper.MakePersistentLoadPath(ResourceSettingData.Setting.PatchManifestFileName); + string jsonData = File.ReadAllText(filePath); + _impl.LocalPatchManifest = PatchManifest.Deserialize(jsonData); + } + + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/Operations/InitializationOperation.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/Operations/InitializationOperation.cs.meta new file mode 100644 index 0000000..4aaf2c0 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Operations/InitializationOperation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a44ad228b117d0047ab80e7a442459b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs b/Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs new file mode 100644 index 0000000..3c51f7c --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Threading; + +namespace YooAsset +{ + /// + /// 更新清单操作 + /// + public abstract class UpdateManifestOperation : AsyncOperationBase + { + } + + /// + /// 编辑器下模拟运行的更新清单操作 + /// + internal class EditorModeUpdateManifestOperation : UpdateManifestOperation + { + internal override void Start() + { + Status = EOperationStatus.Succeed; + } + internal override void Update() + { + } + } + + /// + /// 离线模式的更新清单操作 + /// + internal class OfflinePlayModeUpdateManifestOperation : UpdateManifestOperation + { + internal override void Start() + { + Status = EOperationStatus.Succeed; + } + internal override void Update() + { + } + } + + /// + /// 网络模式的更新清单操作 + /// + internal class HostPlayModeUpdateManifestOperation : UpdateManifestOperation + { + private enum ESteps + { + Idle, + LoadWebManifestHash, + CheckWebManifestHash, + LoadWebManifest, + CheckWebManifest, + InitPrepareCache, + UpdatePrepareCache, + Done, + } + + private static int RequestCount = 0; + + private readonly HostPlayModeImpl _impl; + private readonly int _updateResourceVersion; + private readonly int _timeout; + private ESteps _steps = ESteps.Idle; + private UnityWebRequester _downloaderHash; + private UnityWebRequester _downloaderManifest; + private float _verifyTime; + + public HostPlayModeUpdateManifestOperation(HostPlayModeImpl impl, int updateResourceVersion, int timeout) + { + _impl = impl; + _updateResourceVersion = updateResourceVersion; + _timeout = timeout; + } + internal override void Start() + { + RequestCount++; + _steps = ESteps.LoadWebManifestHash; + + if (_impl.IgnoreResourceVersion && _updateResourceVersion > 0) + { + Logger.Warning($"Update resource version {_updateResourceVersion} is invalid when ignore resource version."); + } + else + { + Logger.Log($"Update patch manifest : update resource version is {_updateResourceVersion}"); + } + } + internal override void Update() + { + if (_steps == ESteps.Idle) + return; + + if (_steps == ESteps.LoadWebManifestHash) + { + string webURL = GetPatchManifestRequestURL(_updateResourceVersion, ResourceSettingData.Setting.PatchManifestHashFileName); + Logger.Log($"Beginning to request patch manifest hash : {webURL}"); + _downloaderHash = new UnityWebRequester(); + _downloaderHash.SendRequest(webURL, _timeout); + _steps = ESteps.CheckWebManifestHash; + } + + if (_steps == ESteps.CheckWebManifestHash) + { + if (_downloaderHash.IsDone() == false) + return; + + // Check fatal + if (_downloaderHash.HasError()) + { + Error = _downloaderHash.GetError(); + Status = EOperationStatus.Failed; + _downloaderHash.Dispose(); + _steps = ESteps.Done; + return; + } + + // 获取补丁清单文件的哈希值 + string webManifestHash = _downloaderHash.GetText(); + _downloaderHash.Dispose(); + + // 如果补丁清单文件的哈希值相同 + string currentFileHash = PatchHelper.GetSandboxPatchManifestFileHash(); + if (currentFileHash == webManifestHash) + { + Logger.Log($"Patch manifest file hash is not change : {webManifestHash}"); + _steps = ESteps.InitPrepareCache; + } + else + { + Logger.Log($"Patch manifest hash is change : {webManifestHash} -> {currentFileHash}"); + _steps = ESteps.LoadWebManifest; + } + } + + if (_steps == ESteps.LoadWebManifest) + { + string webURL = GetPatchManifestRequestURL(_updateResourceVersion, ResourceSettingData.Setting.PatchManifestFileName); + Logger.Log($"Beginning to request patch manifest : {webURL}"); + _downloaderManifest = new UnityWebRequester(); + _downloaderManifest.SendRequest(webURL, _timeout); + _steps = ESteps.CheckWebManifest; + } + + if (_steps == ESteps.CheckWebManifest) + { + if (_downloaderManifest.IsDone() == false) + return; + + // Check fatal + if (_downloaderManifest.HasError()) + { + Error = _downloaderManifest.GetError(); + Status = EOperationStatus.Failed; + _downloaderManifest.Dispose(); + _steps = ESteps.Done; + return; + } + + // 解析补丁清单 + ParseAndSaveRemotePatchManifest(_downloaderManifest.GetText()); + _downloaderManifest.Dispose(); + _steps = ESteps.InitPrepareCache; + } + + if (_steps == ESteps.InitPrepareCache) + { + InitPrepareCache(); + _verifyTime = UnityEngine.Time.realtimeSinceStartup; + _steps = ESteps.UpdatePrepareCache; + } + + if (_steps == ESteps.UpdatePrepareCache) + { + if (UpdatePrepareCache()) + { + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyTime; + Logger.Log($"Verify files total time : {costTime}"); + } + } + } + + private string GetPatchManifestRequestURL(int updateResourceVersion, string fileName) + { + string url; + + // 轮流返回请求地址 + if (RequestCount % 2 == 0) + url = _impl.GetPatchDownloadFallbackURL(updateResourceVersion, fileName); + else + url = _impl.GetPatchDownloadMainURL(updateResourceVersion, fileName); + + // 注意:在URL末尾添加时间戳 + if (_impl.IgnoreResourceVersion) + url = $"{url}?{System.DateTime.UtcNow.Ticks}"; + + return url; + } + private void ParseAndSaveRemotePatchManifest(string content) + { + _impl.LocalPatchManifest = PatchManifest.Deserialize(content); + + // 注意:这里会覆盖掉沙盒内的补丁清单文件 + Logger.Log("Save remote patch manifest file."); + string savePath = AssetPathHelper.MakePersistentLoadPath(ResourceSettingData.Setting.PatchManifestFileName); + PatchManifest.Serialize(savePath, _impl.LocalPatchManifest); + } + + #region 多线程相关 + private class ThreadInfo + { + public bool Result = false; + public string FilePath { private set; get; } + public PatchBundle Bundle { private set; get; } + public ThreadInfo(string filePath, PatchBundle bundle) + { + FilePath = filePath; + Bundle = bundle; + } + } + + private readonly List _cacheList = new List(1000); + private readonly List _verifyList = new List(100); + private readonly ThreadSyncContext _syncContext = new ThreadSyncContext(); + private const int VerifyMaxCount = 32; + + private void InitPrepareCache() + { + // 遍历所有文件然后验证并缓存合法文件 + foreach (var patchBundle in _impl.LocalPatchManifest.BundleList) + { + // 忽略缓存文件 + if (DownloadSystem.ContainsVerifyFile(patchBundle.Hash)) + continue; + + // 忽略APP资源 + // 注意:如果是APP资源并且哈希值相同,则不需要下载 + if (_impl.AppPatchManifest.Bundles.TryGetValue(patchBundle.BundleName, out PatchBundle appPatchBundle)) + { + if (appPatchBundle.IsBuildin && appPatchBundle.Hash == patchBundle.Hash) + continue; + } + + // 查看文件是否存在 + string filePath = PatchHelper.MakeSandboxCacheFilePath(patchBundle.Hash); + if (File.Exists(filePath) == false) + continue; + + _cacheList.Add(patchBundle); + } + } + private bool UpdatePrepareCache() + { + _syncContext.Update(); + + if (_cacheList.Count == 0 && _verifyList.Count == 0) + return true; + + if (_verifyList.Count >= VerifyMaxCount) + return false; + + for (int i = _cacheList.Count - 1; i >= 0; i--) + { + if (_verifyList.Count >= VerifyMaxCount) + break; + + var patchBundle = _cacheList[i]; + if (RunThread(patchBundle)) + { + _cacheList.RemoveAt(i); + _verifyList.Add(patchBundle); + } + else + { + Logger.Warning("Failed to run verify thread."); + break; + } + } + + return false; + } + private bool RunThread(PatchBundle patchBundle) + { + string filePath = PatchHelper.MakeSandboxCacheFilePath(patchBundle.Hash); + ThreadInfo info = new ThreadInfo(filePath, patchBundle); + return ThreadPool.QueueUserWorkItem(new WaitCallback(VerifyFile), info); + } + private void VerifyFile(object infoObj) + { + // 验证沙盒内的文件 + ThreadInfo info = (ThreadInfo)infoObj; + info.Result = DownloadSystem.CheckContentIntegrity(info.FilePath, info.Bundle.SizeBytes, info.Bundle.CRC); + _syncContext.Post(VerifyCallback, info); + } + private void VerifyCallback(object obj) + { + ThreadInfo info = (ThreadInfo)obj; + if (info.Result) + DownloadSystem.CacheVerifyFile(info.Bundle.Hash, info.Bundle.BundleName); + _verifyList.Remove(info.Bundle); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs.meta new file mode 100644 index 0000000..01b749f --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 250c5da7ab03e724f8c328de5238e433 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/PatchAsset.cs b/Assets/YooAsset/Runtime/PatchSystem/PatchAsset.cs new file mode 100644 index 0000000..8d5535a --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PatchAsset.cs @@ -0,0 +1,23 @@ +using System; + +namespace YooAsset +{ + [Serializable] + public class PatchAsset + { + /// + /// 资源路径 + /// + public string AssetPath; + + /// + /// 所属资源包ID + /// + public int BundleID; + + /// + /// 依赖的资源包ID列表 + /// + public int[] DependIDs; + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/PatchAsset.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/PatchAsset.cs.meta new file mode 100644 index 0000000..d98a146 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PatchAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: df359cc7c21634d41b5d05f09ca59cf2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/PatchBundle.cs b/Assets/YooAsset/Runtime/PatchSystem/PatchBundle.cs new file mode 100644 index 0000000..896aab1 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PatchBundle.cs @@ -0,0 +1,129 @@ +using System; +using System.Linq; +using YooAsset.Utility; + +namespace YooAsset +{ + [Serializable] + public class PatchBundle + { + /// + /// 资源包名称 + /// + public string BundleName; + + /// + /// 文件哈希值 + /// + public string Hash; + + /// + /// 文件校验码 + /// + public string CRC; + + /// + /// 文件大小(字节数) + /// + public long SizeBytes; + + /// + /// 文件版本 + /// + public int Version; + + /// + /// Tags + /// + public string[] Tags; + + /// + /// Flags + /// + public int Flags; + + + /// + /// 是否为加密文件 + /// + public bool IsEncrypted { private set; get; } + + /// + /// 是否为内置文件 + /// + public bool IsBuildin { private set; get; } + + /// + /// 是否为原生文件 + /// + public bool IsRawFile { private set; get; } + + + + public PatchBundle(string bundleName, string hash, string crc, long sizeBytes, int version, string[] tags) + { + BundleName = bundleName; + Hash = hash; + CRC = crc; + SizeBytes = sizeBytes; + Version = version; + Tags = tags; + } + + /// + /// 设置Flags + /// + public void SetFlagsValue(bool isEncrypted, bool isBuildin, bool isRawFile) + { + IsEncrypted = isEncrypted; + IsBuildin = isBuildin; + IsRawFile = isRawFile; + + BitMask32 mask = new BitMask32(0); + if (isEncrypted) mask.Open(0); + if (isBuildin) mask.Open(1); + if (isRawFile) mask.Open(2); + Flags = mask; + } + + /// + /// 解析Flags + /// + public void ParseFlagsValue() + { + BitMask32 value = Flags; + IsEncrypted = value.Test(0); + IsBuildin = value.Test(1); + IsRawFile = value.Test(2); + } + + /// + /// 是否包含Tag + /// + public bool HasTag(string[] tags) + { + if (tags == null || tags.Length == 0) + return false; + if (Tags == null || Tags.Length == 0) + return false; + + foreach (var tag in tags) + { + if (Tags.Contains(tag)) + return true; + } + return false; + } + + /// + /// 是否为纯内置资源(不带任何Tag的资源) + /// + public bool IsPureBuildin() + { + if (Tags == null || Tags.Length == 0) + return true; + else + return false; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/PatchBundle.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/PatchBundle.cs.meta new file mode 100644 index 0000000..be67a80 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PatchBundle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70ff08c6c9b0f5e4c993ec4c91954467 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/PatchCache.cs b/Assets/YooAsset/Runtime/PatchSystem/PatchCache.cs new file mode 100644 index 0000000..52446ec --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PatchCache.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using YooAsset.Utility; + +namespace YooAsset +{ + [Serializable] + internal sealed class PatchCache + { + /// + /// 缓存的APP内置版本 + /// + public string CacheAppVersion = string.Empty; + + /// + /// 读取缓存文件 + /// 注意:如果文件不存在则创建新的缓存文件 + /// + public static PatchCache LoadCache() + { + if (PatchHelper.CheckSandboxCacheFileExist()) + { + Logger.Log("Load patch cache from disk."); + string filePath = PatchHelper.GetSandboxCacheFilePath(); + string jsonData = FileUtility.ReadFile(filePath); + return JsonUtility.FromJson(jsonData); + } + else + { + Logger.Log($"Create patch cache to disk : {Application.version}"); + PatchCache cache = new PatchCache(); + cache.CacheAppVersion = Application.version; + string filePath = PatchHelper.GetSandboxCacheFilePath(); + string jsonData = JsonUtility.ToJson(cache); + FileUtility.CreateFile(filePath, jsonData); + return cache; + } + } + + /// + /// 更新缓存文件 + /// + public static void UpdateCache() + { + Logger.Log($"Update patch cache to disk : {Application.version}"); + PatchCache cache = new PatchCache(); + cache.CacheAppVersion = Application.version; + string filePath = PatchHelper.GetSandboxCacheFilePath(); + string jsonData = JsonUtility.ToJson(cache); + FileUtility.CreateFile(filePath, jsonData); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/PatchCache.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/PatchCache.cs.meta new file mode 100644 index 0000000..c660875 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PatchCache.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 37a7daecdb1361140b44ba4724e8866e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/PatchHelper.cs b/Assets/YooAsset/Runtime/PatchSystem/PatchHelper.cs new file mode 100644 index 0000000..221f134 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PatchHelper.cs @@ -0,0 +1,101 @@ +using System.IO; +using System.Text; +using YooAsset.Utility; + +namespace YooAsset +{ + internal static class PatchHelper + { + private const string StrCacheFileName = "Cache.bytes"; + private const string StrCacheFolderName = "CacheFiles"; + + /// + /// 清空沙盒目录 + /// + public static void ClearSandbox() + { + string directoryPath = AssetPathHelper.MakePersistentLoadPath(string.Empty); + if (Directory.Exists(directoryPath)) + Directory.Delete(directoryPath, true); + } + + /// + /// 删除沙盒内补丁清单文件 + /// + public static void DeleteSandboxPatchManifestFile() + { + string filePath = AssetPathHelper.MakePersistentLoadPath(ResourceSettingData.Setting.PatchManifestFileName); + if (File.Exists(filePath)) + File.Delete(filePath); + } + + /// + /// 删除沙盒内的缓存文件 + /// + public static void DeleteSandboxCacheFile() + { + string filePath = GetSandboxCacheFilePath(); + if (File.Exists(filePath)) + File.Delete(filePath); + } + + /// + /// 删除沙盒内的缓存文件夹 + /// + public static void DeleteSandboxCacheFolder() + { + string directoryPath = AssetPathHelper.MakePersistentLoadPath(StrCacheFolderName); + if (Directory.Exists(directoryPath)) + Directory.Delete(directoryPath, true); + } + + + /// + /// 获取沙盒内缓存文件的路径 + /// + public static string GetSandboxCacheFilePath() + { + return AssetPathHelper.MakePersistentLoadPath(StrCacheFileName); + } + + /// + /// 检测沙盒内缓存文件是否存在 + /// + public static bool CheckSandboxCacheFileExist() + { + string filePath = GetSandboxCacheFilePath(); + return File.Exists(filePath); + } + + /// + /// 检测沙盒内补丁清单文件是否存在 + /// + public static bool CheckSandboxPatchManifestFileExist() + { + string filePath = AssetPathHelper.MakePersistentLoadPath(ResourceSettingData.Setting.PatchManifestFileName); + return File.Exists(filePath); + } + + /// + /// 获取沙盒内补丁清单文件的哈希值 + /// 注意:如果沙盒内补丁清单文件不存在,返回空字符串 + /// + /// + public static string GetSandboxPatchManifestFileHash() + { + string filePath = AssetPathHelper.MakePersistentLoadPath(ResourceSettingData.Setting.PatchManifestFileName); + if (File.Exists(filePath)) + return HashUtility.FileMD5(filePath); + else + return string.Empty; + } + + /// + /// 获取缓存文件的存储路径 + /// + public static string MakeSandboxCacheFilePath(string fileName) + { + return AssetPathHelper.MakePersistentLoadPath($"{StrCacheFolderName}/{fileName}"); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/PatchHelper.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/PatchHelper.cs.meta new file mode 100644 index 0000000..03ad6d1 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PatchHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 887179b2bb92ca04988d1091b3b8f8e2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/PatchManifest.cs b/Assets/YooAsset/Runtime/PatchSystem/PatchManifest.cs new file mode 100644 index 0000000..9a5060d --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PatchManifest.cs @@ -0,0 +1,162 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using YooAsset.Utility; + +namespace YooAsset +{ + /// + /// 补丁清单文件 + /// + [Serializable] + public class PatchManifest + { + /// + /// 资源版本号 + /// + public int ResourceVersion; + + /// + /// 内置资源的标记列表 + /// + public string BuildinTags; + + /// + /// 资源列表(主动收集的资源列表) + /// + public List AssetList = new List(); + + /// + /// 资源包列表 + /// + public List BundleList = new List(); + + + /// + /// 资源包集合(提供BundleName获取PatchBundle) + /// + [NonSerialized] + public readonly Dictionary Bundles = new Dictionary(); + + /// + /// 资源映射集合(提供AssetPath获取PatchAsset) + /// + [NonSerialized] + public readonly Dictionary Assets = new Dictionary(); + + + /// + /// 获取内置资源标记列表 + /// + public string[] GetBuildinTags() + { + return StringUtility.StringToStringList(BuildinTags, ';').ToArray(); + } + + /// + /// 获取资源依赖列表 + /// + public string[] GetAllDependencies(string assetPath) + { + if (Assets.TryGetValue(assetPath, out PatchAsset patchAsset)) + { + List result = new List(patchAsset.DependIDs.Length); + foreach (var dependID in patchAsset.DependIDs) + { + if (dependID >= 0 && dependID < BundleList.Count) + { + var dependPatchBundle = BundleList[dependID]; + result.Add(dependPatchBundle.BundleName); + } + else + { + throw new Exception($"Invalid depend id : {dependID} Asset path : {assetPath}"); + } + } + return result.ToArray(); + } + else + { + Logger.Warning($"Not found asset path in patch manifest : {assetPath}"); + return new string[] { }; + } + } + + /// + /// 获取资源包名称 + /// + public string GetAssetBundleName(string assetPath) + { + if (Assets.TryGetValue(assetPath, out PatchAsset patchAsset)) + { + int bundleID = patchAsset.BundleID; + if (bundleID >= 0 && bundleID < BundleList.Count) + { + var patchBundle = BundleList[bundleID]; + return patchBundle.BundleName; + } + else + { + throw new Exception($"Invalid depend id : {bundleID} Asset path : {assetPath}"); + } + } + else + { + Logger.Warning($"Not found asset path in patch manifest : {assetPath}"); + return string.Empty; + } + } + + + /// + /// 序列化 + /// + public static void Serialize(string savePath, PatchManifest patchManifest) + { + string json = JsonUtility.ToJson(patchManifest); + FileUtility.CreateFile(savePath, json); + } + + /// + /// 反序列化 + /// + public static PatchManifest Deserialize(string jsonData) + { + PatchManifest patchManifest = JsonUtility.FromJson(jsonData); + + // BundleList + foreach (var patchBundle in patchManifest.BundleList) + { + patchBundle.ParseFlagsValue(); + patchManifest.Bundles.Add(patchBundle.BundleName, patchBundle); + } + + // AssetList + foreach (var patchAsset in patchManifest.AssetList) + { + string assetPath = patchAsset.AssetPath; + + // 添加原始路径 + // 注意:我们不允许原始路径存在重名 + if (patchManifest.Assets.ContainsKey(assetPath)) + throw new Exception($"Asset path have existed : {assetPath}"); + else + patchManifest.Assets.Add(assetPath, patchAsset); + + // 添加去掉后缀名的路径 + if (Path.HasExtension(assetPath)) + { + string assetPathWithoutExtension = StringUtility.RemoveExtension(assetPath); + if (patchManifest.Assets.ContainsKey(assetPathWithoutExtension)) + Logger.Warning($"Asset path have existed : {assetPathWithoutExtension}"); + else + patchManifest.Assets.Add(assetPathWithoutExtension, patchAsset); + } + } + + return patchManifest; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/PatchManifest.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/PatchManifest.cs.meta new file mode 100644 index 0000000..1111ce6 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PatchManifest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0bb3469358a90d41a3a4b284a3ddc8f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/PlayMode.meta b/Assets/YooAsset/Runtime/PatchSystem/PlayMode.meta new file mode 100644 index 0000000..64d721b --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PlayMode.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e9d6cb1ce5d510645866ad7c122abfab +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/PlayMode/EditorPlayModeImpl.cs b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/EditorPlayModeImpl.cs new file mode 100644 index 0000000..218d528 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/EditorPlayModeImpl.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset +{ + internal class EditorPlayModeImpl : IBundleServices + { + /// + /// 异步初始化 + /// + public InitializationOperation InitializeAsync() + { + var operation = new EditorModeInitializationOperation(); + OperationUpdater.ProcessOperaiton(operation); + return operation; + } + + /// + /// 获取资源版本号 + /// + public int GetResourceVersion() + { + return 0; + } + + /// + /// 获取内置资源标记列表 + /// + public string[] GetManifestBuildinTags() + { + return new string[0]; + } + + #region IBundleServices接口 + AssetBundleInfo IBundleServices.GetAssetBundleInfo(string bundleName) + { + Logger.Warning($"Editor play mode can not get asset bundle info."); + AssetBundleInfo bundleInfo = new AssetBundleInfo(bundleName, bundleName); + return bundleInfo; + } + string IBundleServices.GetAssetBundleName(string assetPath) + { + return assetPath; + } + string[] IBundleServices.GetAllDependencies(string assetPath) + { + return new string[] { }; + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/PlayMode/EditorPlayModeImpl.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/EditorPlayModeImpl.cs.meta new file mode 100644 index 0000000..305eea0 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/EditorPlayModeImpl.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2fb1b9a2a91f1af4a86acfcfac424e0b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/PlayMode/HostPlayModeImpl.cs b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/HostPlayModeImpl.cs new file mode 100644 index 0000000..651d6f8 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/HostPlayModeImpl.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset +{ + internal class HostPlayModeImpl : IBundleServices + { + // 补丁清单 + internal PatchManifest AppPatchManifest; + internal PatchManifest LocalPatchManifest; + + // 参数相关 + internal bool ClearCacheWhenDirty { private set; get; } + internal bool IgnoreResourceVersion { private set; get; } + private string _defaultHostServer; + private string _fallbackHostServer; + + /// + /// 异步初始化 + /// + public InitializationOperation InitializeAsync(bool clearCacheWhenDirty, bool ignoreResourceVersion, + string defaultHostServer, string fallbackHostServer) + { + ClearCacheWhenDirty = clearCacheWhenDirty; + IgnoreResourceVersion = ignoreResourceVersion; + _defaultHostServer = defaultHostServer; + _fallbackHostServer = fallbackHostServer; + + var operation = new HostPlayModeInitializationOperation(this); + OperationUpdater.ProcessOperaiton(operation); + return operation; + } + + /// + /// 异步更新补丁清单 + /// + public UpdateManifestOperation UpdatePatchManifestAsync(int updateResourceVersion, int timeout) + { + var operation = new HostPlayModeUpdateManifestOperation(this, updateResourceVersion, timeout); + OperationUpdater.ProcessOperaiton(operation); + return operation; + } + + /// + /// 获取资源版本号 + /// + public int GetResourceVersion() + { + if (LocalPatchManifest == null) + return 0; + return LocalPatchManifest.ResourceVersion; + } + + /// + /// 获取内置资源标记列表 + /// + public string[] GetManifestBuildinTags() + { + if (LocalPatchManifest == null) + return new string[0]; + return LocalPatchManifest.GetBuildinTags(); + } + + /// + /// 创建补丁下载器 + /// + public PatchDownloader CreateDLCDownloader(string[] dlcTags, int fileLoadingMaxNumber, int failedTryAgain) + { + List downloadList = GetDLCDownloadList(dlcTags); + PatchDownloader downlader = new PatchDownloader(this, downloadList, fileLoadingMaxNumber, failedTryAgain); + return downlader; + } + private List GetDLCDownloadList(string[] dlcTags) + { + List downloadList = new List(1000); + foreach (var patchBundle in LocalPatchManifest.BundleList) + { + // 忽略缓存文件 + 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; + } + + // 如果是纯内置资源,则统一下载 + // 注意:可能是新增的或者变化的内置资源 + // 注意:可能是由热更资源转换的内置资源 + if (patchBundle.IsPureBuildin()) + { + downloadList.Add(patchBundle); + } + else + { + // 查询DLC资源 + if (patchBundle.HasTag(dlcTags)) + { + downloadList.Add(patchBundle); + } + } + } + + return ConvertToDownloadList(downloadList); + } + + /// + /// 创建补丁下载器 + /// + public PatchDownloader CreateBundleDownloader(List assetPaths, int fileLoadingMaxNumber, int failedTryAgain) + { + List downloadList = GetBundleDownloadList(assetPaths); + PatchDownloader downlader = new PatchDownloader(this, downloadList, fileLoadingMaxNumber, failedTryAgain); + return downlader; + } + private List GetBundleDownloadList(List assetPaths) + { + // 获取资源对象的资源包和所有依赖资源包 + List checkList = new List(); + foreach (var assetPath in assetPaths) + { + string mainBundleName = LocalPatchManifest.GetAssetBundleName(assetPath); + if (string.IsNullOrEmpty(mainBundleName) == false) + { + if (LocalPatchManifest.Bundles.TryGetValue(mainBundleName, out PatchBundle mainBundle)) + { + if (checkList.Contains(mainBundle) == false) + checkList.Add(mainBundle); + } + } + + string[] dependBundleNames = LocalPatchManifest.GetAllDependencies(assetPath); + foreach (var dependBundleName in dependBundleNames) + { + if (LocalPatchManifest.Bundles.TryGetValue(dependBundleName, out PatchBundle dependBundle)) + { + if (checkList.Contains(dependBundle) == false) + checkList.Add(dependBundle); + } + } + } + + List downloadList = new List(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); + } + + // WEB相关 + internal string GetPatchDownloadMainURL(int resourceVersion, string fileName) + { + if (IgnoreResourceVersion) + return $"{_defaultHostServer}/{fileName}"; + else + return $"{_defaultHostServer}/{resourceVersion}/{fileName}"; + } + internal string GetPatchDownloadFallbackURL(int resourceVersion, string fileName) + { + if (IgnoreResourceVersion) + return $"{_fallbackHostServer}/{fileName}"; + else + return $"{_fallbackHostServer}/{resourceVersion}/{fileName}"; + } + + // 下载相关 + private AssetBundleInfo ConvertToDownloadInfo(PatchBundle patchBundle) + { + // 注意:资源版本号只用于确定下载路径 + string sandboxPath = PatchHelper.MakeSandboxCacheFilePath(patchBundle.Hash); + string remoteMainURL = GetPatchDownloadMainURL(patchBundle.Version, patchBundle.Hash); + string remoteFallbackURL = GetPatchDownloadFallbackURL(patchBundle.Version, patchBundle.Hash); + AssetBundleInfo bundleInfo = new AssetBundleInfo(patchBundle, sandboxPath, remoteMainURL, remoteFallbackURL); + return bundleInfo; + } + private List ConvertToDownloadList(List downloadList) + { + List result = new List(downloadList.Count); + foreach (var patchBundle in downloadList) + { + var bundleInfo = ConvertToDownloadInfo(patchBundle); + result.Add(bundleInfo); + } + return result; + } + + #region IBundleServices接口 + AssetBundleInfo IBundleServices.GetAssetBundleInfo(string bundleName) + { + if (string.IsNullOrEmpty(bundleName)) + return new AssetBundleInfo(string.Empty, string.Empty); + + if (LocalPatchManifest.Bundles.TryGetValue(bundleName, out PatchBundle patchBundle)) + { + // 查询APP资源 + if (AppPatchManifest.Bundles.TryGetValue(bundleName, out PatchBundle appPatchBundle)) + { + if (appPatchBundle.IsBuildin && appPatchBundle.Hash == patchBundle.Hash) + { + string appLoadPath = AssetPathHelper.MakeStreamingLoadPath(appPatchBundle.Hash); + AssetBundleInfo bundleInfo = new AssetBundleInfo(appPatchBundle, appLoadPath); + return bundleInfo; + } + } + + // 查询沙盒资源 + if (DownloadSystem.ContainsVerifyFile(patchBundle.Hash)) + { + string sandboxLoadPath = PatchHelper.MakeSandboxCacheFilePath(patchBundle.Hash); + AssetBundleInfo bundleInfo = new AssetBundleInfo(patchBundle, sandboxLoadPath); + return bundleInfo; + } + + // 从服务端下载 + return ConvertToDownloadInfo(patchBundle); + } + else + { + Logger.Warning($"Not found bundle in patch manifest : {bundleName}"); + AssetBundleInfo bundleInfo = new AssetBundleInfo(bundleName, string.Empty); + return bundleInfo; + } + } + string IBundleServices.GetAssetBundleName(string assetPath) + { + return LocalPatchManifest.GetAssetBundleName(assetPath); + } + string[] IBundleServices.GetAllDependencies(string assetPath) + { + return LocalPatchManifest.GetAllDependencies(assetPath); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/PlayMode/HostPlayModeImpl.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/HostPlayModeImpl.cs.meta new file mode 100644 index 0000000..1d8ae52 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/HostPlayModeImpl.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 84c10fd3507a1c24a9043aebb72db5f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/PlayMode/OfflinePlayModeImpl.cs b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/OfflinePlayModeImpl.cs new file mode 100644 index 0000000..66e7ba2 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/OfflinePlayModeImpl.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset +{ + internal class OfflinePlayModeImpl : IBundleServices + { + internal PatchManifest AppPatchManifest; + + /// + /// 异步初始化 + /// + public InitializationOperation InitializeAsync() + { + var operation = new OfflinePlayModeInitializationOperation(this); + OperationUpdater.ProcessOperaiton(operation); + return operation; + } + + /// + /// 获取资源版本号 + /// + public int GetResourceVersion() + { + if (AppPatchManifest == null) + return 0; + return AppPatchManifest.ResourceVersion; + } + + /// + /// 获取内置资源标记列表 + /// + public string[] GetManifestBuildinTags() + { + if (AppPatchManifest == null) + return new string[0]; + return AppPatchManifest.GetBuildinTags(); + } + + #region IBundleServices接口 + AssetBundleInfo IBundleServices.GetAssetBundleInfo(string bundleName) + { + if (string.IsNullOrEmpty(bundleName)) + return new AssetBundleInfo(string.Empty, string.Empty); + + if (AppPatchManifest.Bundles.TryGetValue(bundleName, out PatchBundle patchBundle)) + { + string localPath = AssetPathHelper.MakeStreamingLoadPath(patchBundle.Hash); + AssetBundleInfo bundleInfo = new AssetBundleInfo(patchBundle, localPath); + return bundleInfo; + } + else + { + Logger.Warning($"Not found bundle in patch manifest : {bundleName}"); + AssetBundleInfo bundleInfo = new AssetBundleInfo(bundleName, string.Empty); + return bundleInfo; + } + } + string IBundleServices.GetAssetBundleName(string assetPath) + { + return AppPatchManifest.GetAssetBundleName(assetPath); + } + string[] IBundleServices.GetAllDependencies(string assetPath) + { + return AppPatchManifest.GetAllDependencies(assetPath); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/PlayMode/OfflinePlayModeImpl.cs.meta b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/OfflinePlayModeImpl.cs.meta new file mode 100644 index 0000000..115c1a4 --- /dev/null +++ b/Assets/YooAsset/Runtime/PatchSystem/PlayMode/OfflinePlayModeImpl.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc10550f17aaeb14795135a51444de1c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/ResourceSetting.meta b/Assets/YooAsset/Runtime/ResourceSetting.meta new file mode 100644 index 0000000..2133210 --- /dev/null +++ b/Assets/YooAsset/Runtime/ResourceSetting.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ff07a5253f139bf40856fa1ed6a6bb5b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/ResourceSetting/ResourceSetting.cs b/Assets/YooAsset/Runtime/ResourceSetting/ResourceSetting.cs new file mode 100644 index 0000000..750d592 --- /dev/null +++ b/Assets/YooAsset/Runtime/ResourceSetting/ResourceSetting.cs @@ -0,0 +1,38 @@ +using UnityEngine; + +namespace YooAsset +{ + [CreateAssetMenu(fileName = "YooAssetSetting", menuName = "YooAsset/Create Setting")] + public class ResourceSetting : ScriptableObject + { + /// + /// AssetBundle文件的后缀名 + /// + public string AssetBundleFileVariant = "bundle"; + + /// + /// 原生文件的后缀名 + /// + public string RawFileVariant = "rawfile"; + + /// + /// 构建输出的补丁清单文件名称 + /// + public string PatchManifestFileName = "PatchManifest.bytes"; + + /// + /// 构建输出的补丁清单哈希文件名称 + /// + public string PatchManifestHashFileName = "PatchManifestHash.bytes"; + + /// + /// 构建输出的Unity清单文件名称 + /// + public string UnityManifestFileName = "UnityManifest"; + + /// + /// 构建输出的说明文件 + /// + public string ReadmeFileName = "readme.txt"; + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/ResourceSetting/ResourceSetting.cs.meta b/Assets/YooAsset/Runtime/ResourceSetting/ResourceSetting.cs.meta new file mode 100644 index 0000000..9a2b069 --- /dev/null +++ b/Assets/YooAsset/Runtime/ResourceSetting/ResourceSetting.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5bd1afdce9715f84eb4cbc901922afc2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/ResourceSetting/ResourceSettingData.cs b/Assets/YooAsset/Runtime/ResourceSetting/ResourceSettingData.cs new file mode 100644 index 0000000..80df203 --- /dev/null +++ b/Assets/YooAsset/Runtime/ResourceSetting/ResourceSettingData.cs @@ -0,0 +1,35 @@ +using UnityEngine; + +namespace YooAsset +{ + public static class ResourceSettingData + { + private static ResourceSetting _setting = null; + public static ResourceSetting Setting + { + get + { + if (_setting == null) + LoadSettingData(); + return _setting; + } + } + + /// + /// 加载配置文件 + /// + private static void LoadSettingData() + { + _setting = Resources.Load("YooAssetSetting"); + if (_setting == null) + { + Debug.Log("Use YooAsset default resource setting."); + _setting = ScriptableObject.CreateInstance(); + } + else + { + Debug.Log("Use YooAsset custom resource setting."); + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/ResourceSetting/ResourceSettingData.cs.meta b/Assets/YooAsset/Runtime/ResourceSetting/ResourceSettingData.cs.meta new file mode 100644 index 0000000..59cecac --- /dev/null +++ b/Assets/YooAsset/Runtime/ResourceSetting/ResourceSettingData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3c6da38a89e37124c907c4814295a432 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Utility.meta b/Assets/YooAsset/Runtime/Utility.meta new file mode 100644 index 0000000..f696a3f --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 770292fd53451ac40b45c59a4ef241a9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Utility/AssemblyUtility.cs b/Assets/YooAsset/Runtime/Utility/AssemblyUtility.cs new file mode 100644 index 0000000..bd23935 --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/AssemblyUtility.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; + +namespace YooAsset.Utility +{ + public static class AssemblyUtility + { + public const string YooAssetAssemblyName = "YooAsset"; + public const string YooAssetAssemblyEditorName = "YooAsset.Editor"; + public const string UnityDefaultAssemblyName = "Assembly-CSharp"; + public const string UnityDefaultAssemblyEditorName = "Assembly-CSharp-Editor"; + + + private static readonly Dictionary> _cache = new Dictionary>(); + + static AssemblyUtility() + { + _cache.Clear(); + } + + /// + /// 获取程序集 + /// + public static Assembly GetAssembly(string assemblyName) + { + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly assembly in assemblies) + { + if (assembly.GetName().Name == assemblyName) + return assembly; + } + return null; + } + + /// + /// 获取程序集里的所有类型 + /// + private static List GetTypes(string assemblyName) + { + if (_cache.ContainsKey(assemblyName)) + return _cache[assemblyName]; + + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly assembly in assemblies) + { + if (assembly.GetName().Name == assemblyName) + { + List types = assembly.GetTypes().ToList(); + _cache.Add(assemblyName, types); + return types; + } + } + + // 注意:如果没有找到程序集返回空列表 + UnityEngine.Debug.LogWarning($"Not found assembly : {assemblyName}"); + return new List(); + } + + /// + /// 获取带继承关系的所有类的类型 + /// 父类类型 + /// + public static List GetAssignableTypes(string assemblyName, System.Type parentType) + { + List result = new List(); + List cacheTypes = GetTypes(assemblyName); + for (int i = 0; i < cacheTypes.Count; i++) + { + Type type = cacheTypes[i]; + + // 判断继承关系 + if (parentType.IsAssignableFrom(type)) + { + if (type.Name == parentType.Name) + continue; + result.Add(type); + } + } + return result; + } + + /// + /// 获取带属性标签的所有类的类型 + /// 属性类型 + /// + public static List GetAttributeTypes(string assemblyName, System.Type attributeType) + { + List result = new List(); + List cacheTypes = GetTypes(assemblyName); + for (int i = 0; i < cacheTypes.Count; i++) + { + System.Type type = cacheTypes[i]; + + // 判断属性标签 + if (Attribute.IsDefined(type, attributeType)) + { + result.Add(type); + } + } + return result; + } + + /// + /// 获取带继承关系和属性标签的所有类的类型 + /// + /// 父类类型 + /// 属性类型 + public static List GetAssignableAttributeTypes(string assemblyName, System.Type parentType, System.Type attributeType, bool checkError = true) + { + List result = new List(); + List cacheTypes = GetTypes(assemblyName); + for (int i = 0; i < cacheTypes.Count; i++) + { + Type type = cacheTypes[i]; + + // 判断属性标签 + if (Attribute.IsDefined(type, attributeType)) + { + // 判断继承关系 + if (parentType.IsAssignableFrom(type)) + { + if (type.Name == parentType.Name) + continue; + result.Add(type); + } + else + { + if(checkError) + throw new Exception($"class {type} must inherit from {parentType}."); + } + } + } + return result; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Utility/AssemblyUtility.cs.meta b/Assets/YooAsset/Runtime/Utility/AssemblyUtility.cs.meta new file mode 100644 index 0000000..856fd93 --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/AssemblyUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0b559b6cd6f58a04497ef21b68d5dde8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Utility/FileUtility.cs b/Assets/YooAsset/Runtime/Utility/FileUtility.cs new file mode 100644 index 0000000..4488d0c --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/FileUtility.cs @@ -0,0 +1,70 @@ +using System; +using System.Text; +using System.IO; + +namespace YooAsset.Utility +{ + public static class FileUtility + { + /// + /// 读取文件 + /// + public static string ReadFile(string filePath) + { + if (File.Exists(filePath) == false) + return string.Empty; + return File.ReadAllText(filePath, Encoding.UTF8); + } + + /// + /// 创建文件 + /// + public static void CreateFile(string filePath, string content) + { + // 删除旧文件 + if (File.Exists(filePath)) + File.Delete(filePath); + + // 创建文件夹路径 + CreateFileDirectory(filePath); + + // 创建新文件 + byte[] bytes = Encoding.UTF8.GetBytes(content); + using (FileStream fs = File.Create(filePath)) + { + fs.Write(bytes, 0, bytes.Length); + fs.Flush(); + fs.Close(); + } + } + + /// + /// 创建文件的文件夹路径 + /// + public static void CreateFileDirectory(string filePath) + { + // 获取文件的文件夹路径 + string directory = Path.GetDirectoryName(filePath); + CreateDirectory(directory); + } + + /// + /// 创建文件夹路径 + /// + public static void CreateDirectory(string directory) + { + // If the directory doesn't exist, create it. + if (Directory.Exists(directory) == false) + Directory.CreateDirectory(directory); + } + + /// + /// 获取文件大小(字节数) + /// + public static long GetFileSize(string filePath) + { + FileInfo fileInfo = new FileInfo(filePath); + return fileInfo.Length; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Utility/FileUtility.cs.meta b/Assets/YooAsset/Runtime/Utility/FileUtility.cs.meta new file mode 100644 index 0000000..835d687 --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/FileUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dfc7a106fdf9d90428c38ec09d35a6f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Utility/HashUtility.cs b/Assets/YooAsset/Runtime/Utility/HashUtility.cs new file mode 100644 index 0000000..1f1b7a3 --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/HashUtility.cs @@ -0,0 +1,169 @@ +using System; +using System.Text; +using System.IO; +using System.Security.Cryptography; + +namespace YooAsset.Utility +{ + public static class HashUtility + { + private static string ToString(byte[] hashBytes) + { + string result = BitConverter.ToString(hashBytes); + result = result.Replace("-", ""); + return result.ToLower(); + } + + #region SHA1 + /// + /// 获取字符串的Hash值 + /// + public static string StringSHA1(string str) + { + byte[] buffer = Encoding.UTF8.GetBytes(str); + return BytesSHA1(buffer); + } + + /// + /// 获取文件的Hash值 + /// + public static string FileSHA1(string filePath) + { + try + { + using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + return StreamSHA1(fs); + } + } + catch (Exception e) + { + Logger.Exception(e); + return string.Empty; + } + } + + /// + /// 获取数据流的Hash值 + /// + public static string StreamSHA1(Stream stream) + { + // 说明:创建的是SHA1类的实例,生成的是160位的散列码 + HashAlgorithm hash = HashAlgorithm.Create(); + byte[] hashBytes = hash.ComputeHash(stream); + return ToString(hashBytes); + } + + /// + /// 获取字节数组的Hash值 + /// + public static string BytesSHA1(byte[] buffer) + { + // 说明:创建的是SHA1类的实例,生成的是160位的散列码 + HashAlgorithm hash = HashAlgorithm.Create(); + byte[] hashBytes = hash.ComputeHash(buffer); + return ToString(hashBytes); + } + #endregion + + #region MD5 + /// + /// 获取字符串的MD5 + /// + public static string StringMD5(string str) + { + byte[] buffer = Encoding.UTF8.GetBytes(str); + return BytesMD5(buffer); + } + + /// + /// 获取文件的MD5 + /// + public static string FileMD5(string filePath) + { + try + { + using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + return StreamMD5(fs); + } + } + catch (Exception e) + { + Logger.Exception(e); + return string.Empty; + } + } + + /// + /// 获取数据流的MD5 + /// + public static string StreamMD5(Stream stream) + { + MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); + byte[] hashBytes = provider.ComputeHash(stream); + return ToString(hashBytes); + } + + /// + /// 获取字节数组的MD5 + /// + public static string BytesMD5(byte[] buffer) + { + MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); + byte[] hashBytes = provider.ComputeHash(buffer); + return ToString(hashBytes); + } + #endregion + + #region CRC32 + /// + /// 获取字符串的CRC32 + /// + public static string StringCRC32(string str) + { + byte[] buffer = Encoding.UTF8.GetBytes(str); + return BytesCRC32(buffer); + } + + /// + /// 获取文件的CRC32 + /// + 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) + { + Logger.Exception(e); + return string.Empty; + } + } + + /// + /// 获取数据流的CRC32 + /// + public static string StreamCRC32(Stream stream) + { + CRC32Algorithm hash = new CRC32Algorithm(); + byte[] hashBytes = hash.ComputeHash(stream); + return ToString(hashBytes); + } + + /// + /// 获取字节数组的CRC32 + /// + public static string BytesCRC32(byte[] buffer) + { + CRC32Algorithm hash = new CRC32Algorithm(); + byte[] hashBytes = hash.ComputeHash(buffer); + return ToString(hashBytes); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Utility/HashUtility.cs.meta b/Assets/YooAsset/Runtime/Utility/HashUtility.cs.meta new file mode 100644 index 0000000..ca9d47f --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/HashUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 43b9656e0b5241e49afcd9d671f5deac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Utility/Misc.meta b/Assets/YooAsset/Runtime/Utility/Misc.meta new file mode 100644 index 0000000..76ecb2d --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/Misc.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 778d989dab3088b499eb457a9b7cf854 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Utility/Misc/BitMask32.cs b/Assets/YooAsset/Runtime/Utility/Misc/BitMask32.cs new file mode 100644 index 0000000..d28359e --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/Misc/BitMask32.cs @@ -0,0 +1,69 @@ +using System; + +namespace YooAsset.Utility +{ + public struct BitMask32 + { + private int _mask; + + public static implicit operator int(BitMask32 mask) { return mask._mask; } + public static implicit operator BitMask32(int mask) { return new BitMask32(mask); } + + public BitMask32(int mask) + { + _mask = mask; + } + + /// + /// 打开位 + /// + public void Open(int bit) + { + if (bit < 0 || bit > 31) + throw new ArgumentOutOfRangeException(); + else + _mask |= 1 << bit; + } + + /// + /// 关闭位 + /// + public void Close(int bit) + { + if (bit < 0 || bit > 31) + throw new ArgumentOutOfRangeException(); + else + _mask &= ~(1 << bit); + } + + /// + /// 位取反 + /// + public void Reverse(int bit) + { + if (bit < 0 || bit > 31) + throw new ArgumentOutOfRangeException(); + else + _mask ^= 1 << bit; + } + + /// + /// 所有位取反 + /// + public void Inverse() + { + _mask = ~_mask; + } + + /// + /// 比对位值 + /// + public bool Test(int bit) + { + if (bit < 0 || bit > 31) + throw new ArgumentOutOfRangeException(); + else + return (_mask & (1 << bit)) != 0; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Utility/Misc/BitMask32.cs.meta b/Assets/YooAsset/Runtime/Utility/Misc/BitMask32.cs.meta new file mode 100644 index 0000000..18d1885 --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/Misc/BitMask32.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 60c7594328ef976408edadfdf2b9aa3d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Utility/Misc/BitMask64.cs b/Assets/YooAsset/Runtime/Utility/Misc/BitMask64.cs new file mode 100644 index 0000000..0ad9e31 --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/Misc/BitMask64.cs @@ -0,0 +1,69 @@ +using System; + +namespace YooAsset.Utility +{ + public struct BitMask64 + { + private long _mask; + + public static implicit operator long(BitMask64 mask) { return mask._mask; } + public static implicit operator BitMask64(long mask) { return new BitMask64(mask); } + + public BitMask64(long mask) + { + _mask = mask; + } + + /// + /// 打开位 + /// + public void Open(int bit) + { + if (bit < 0 || bit > 63) + throw new ArgumentOutOfRangeException(); + else + _mask |= 1L << bit; + } + + /// + /// 关闭位 + /// + public void Close(int bit) + { + if (bit < 0 || bit > 63) + throw new ArgumentOutOfRangeException(); + else + _mask &= ~(1L << bit); + } + + /// + /// 位取反 + /// + public void Reverse(int bit) + { + if (bit < 0 || bit > 63) + throw new ArgumentOutOfRangeException(); + else + _mask ^= 1L << bit; + } + + /// + /// 所有位取反 + /// + public void Inverse() + { + _mask = ~_mask; + } + + /// + /// 比对位值 + /// + public bool Test(int bit) + { + if (bit < 0 || bit > 63) + throw new ArgumentOutOfRangeException(); + else + return (_mask & (1L << bit)) != 0; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Utility/Misc/BitMask64.cs.meta b/Assets/YooAsset/Runtime/Utility/Misc/BitMask64.cs.meta new file mode 100644 index 0000000..5d62c53 --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/Misc/BitMask64.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6b03684bc5163694ab3983243512b4cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Utility/Misc/CRC32Algorithm.cs b/Assets/YooAsset/Runtime/Utility/Misc/CRC32Algorithm.cs new file mode 100644 index 0000000..acb554f --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/Misc/CRC32Algorithm.cs @@ -0,0 +1,242 @@ +using System; +using System.Security.Cryptography; + +namespace YooAsset.Utility +{ + internal class SafeProxy + { + private const uint Poly = 0xedb88320u; + private readonly uint[] _table = new uint[16 * 256]; + + internal SafeProxy() + { + Init(Poly); + } + public void Init(uint poly) + { + var table = _table; + for (uint i = 0; i < 256; i++) + { + uint res = i; + for (int t = 0; t < 16; t++) + { + for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? poly ^ (res >> 1) : (res >> 1); + table[(t * 256) + i] = res; + } + } + } + public uint Append(uint crc, byte[] input, int offset, int length) + { + uint crcLocal = uint.MaxValue ^ crc; + + uint[] table = _table; + while (length >= 16) + { + var a = table[(3 * 256) + input[offset + 12]] + ^ table[(2 * 256) + input[offset + 13]] + ^ table[(1 * 256) + input[offset + 14]] + ^ table[(0 * 256) + input[offset + 15]]; + + var b = table[(7 * 256) + input[offset + 8]] + ^ table[(6 * 256) + input[offset + 9]] + ^ table[(5 * 256) + input[offset + 10]] + ^ table[(4 * 256) + input[offset + 11]]; + + var c = table[(11 * 256) + input[offset + 4]] + ^ table[(10 * 256) + input[offset + 5]] + ^ table[(9 * 256) + input[offset + 6]] + ^ table[(8 * 256) + input[offset + 7]]; + + var d = table[(15 * 256) + ((byte)crcLocal ^ input[offset])] + ^ table[(14 * 256) + ((byte)(crcLocal >> 8) ^ input[offset + 1])] + ^ table[(13 * 256) + ((byte)(crcLocal >> 16) ^ input[offset + 2])] + ^ table[(12 * 256) + ((crcLocal >> 24) ^ input[offset + 3])]; + + crcLocal = d ^ c ^ b ^ a; + offset += 16; + length -= 16; + } + + while (--length >= 0) + crcLocal = table[(byte)(crcLocal ^ input[offset++])] ^ crcLocal >> 8; + + return crcLocal ^ uint.MaxValue; + } + } + + /// + /// This is .NET safe implementation of Crc32 algorithm. + /// Implementation of CRC-32. + /// This class supports several convenient static methods returning the CRC as UInt32. + /// + internal class CRC32Algorithm : HashAlgorithm + { + private uint _currentCrc; + + /// + /// Initializes a new instance of the class. + /// + public CRC32Algorithm() + { +#if !NETCORE13 + HashSizeValue = 32; +#endif + } + + /// + /// Resets internal state of the algorithm. Used internally. + /// + public override void Initialize() + { + _currentCrc = 0; + } + + /// + /// Appends CRC-32 from given buffer + /// + protected override void HashCore(byte[] input, int offset, int length) + { + _currentCrc = AppendInternal(_currentCrc, input, offset, length); + } + + /// + /// Computes CRC-32 from + /// + protected override byte[] HashFinal() + { + if(BitConverter.IsLittleEndian) + return new[] { (byte)_currentCrc, (byte)(_currentCrc >> 8), (byte)(_currentCrc >> 16), (byte)(_currentCrc >> 24) }; + else + return new[] { (byte)(_currentCrc >> 24), (byte)(_currentCrc >> 16), (byte)(_currentCrc >> 8), (byte)_currentCrc }; + } + + + /// + /// Computes CRC-32 from multiple buffers. + /// Call this method multiple times to chain multiple buffers. + /// + /// + /// Initial CRC value for the algorithm. It is zero for the first buffer. + /// Subsequent buffers should have their initial value set to CRC value returned by previous call to this method. + /// + /// Input buffer with data to be checksummed. + /// Offset of the input data within the buffer. + /// Length of the input data in the buffer. + /// Accumulated CRC-32 of all buffers processed so far. + public static uint Append(uint initial, byte[] input, int offset, int length) + { + if (input == null) + throw new ArgumentNullException("input"); + if (offset < 0 || length < 0 || offset + length > input.Length) + throw new ArgumentOutOfRangeException("length"); + return AppendInternal(initial, input, offset, length); + } + + /// + /// Computes CRC-32 from multiple buffers. + /// Call this method multiple times to chain multiple buffers. + /// + /// + /// Initial CRC value for the algorithm. It is zero for the first buffer. + /// Subsequent buffers should have their initial value set to CRC value returned by previous call to this method. + /// + /// Input buffer containing data to be checksummed. + /// Accumulated CRC-32 of all buffers processed so far. + public static uint Append(uint initial, byte[] input) + { + if (input == null) + throw new ArgumentNullException(); + return AppendInternal(initial, input, 0, input.Length); + } + + /// + /// Computes CRC-32 from input buffer. + /// + /// Input buffer with data to be checksummed. + /// Offset of the input data within the buffer. + /// Length of the input data in the buffer. + /// CRC-32 of the data in the buffer. + public static uint Compute(byte[] input, int offset, int length) + { + return Append(0, input, offset, length); + } + + /// + /// Computes CRC-32 from input buffer. + /// + /// Input buffer containing data to be checksummed. + /// CRC-32 of the buffer. + public static uint Compute(byte[] input) + { + return Append(0, input); + } + + /// + /// Computes CRC-32 from input buffer and writes it after end of data (buffer should have 4 bytes reserved space for it). Can be used in conjunction with + /// + /// Input buffer with data to be checksummed. + /// Offset of the input data within the buffer. + /// Length of the input data in the buffer. + /// CRC-32 of the data in the buffer. + public static uint ComputeAndWriteToEnd(byte[] input, int offset, int length) + { + if (length + 4 > input.Length) + throw new ArgumentOutOfRangeException("length", "Length of data should be less than array length - 4 bytes of CRC data"); + var crc = Append(0, input, offset, length); + var r = offset + length; + input[r] = (byte)crc; + input[r + 1] = (byte)(crc >> 8); + input[r + 2] = (byte)(crc >> 16); + input[r + 3] = (byte)(crc >> 24); + return crc; + } + + /// + /// Computes CRC-32 from input buffer - 4 bytes and writes it as last 4 bytes of buffer. Can be used in conjunction with + /// + /// Input buffer with data to be checksummed. + /// CRC-32 of the data in the buffer. + public static uint ComputeAndWriteToEnd(byte[] input) + { + if (input.Length < 4) + throw new ArgumentOutOfRangeException("input", "Input array should be 4 bytes at least"); + return ComputeAndWriteToEnd(input, 0, input.Length - 4); + } + + /// + /// Validates correctness of CRC-32 data in source buffer with assumption that CRC-32 data located at end of buffer in reverse bytes order. Can be used in conjunction with + /// + /// Input buffer with data to be checksummed. + /// Offset of the input data within the buffer. + /// Length of the input data in the buffer with CRC-32 bytes. + /// Is checksum valid. + public static bool IsValidWithCrcAtEnd(byte[] input, int offset, int lengthWithCrc) + { + return Append(0, input, offset, lengthWithCrc) == 0x2144DF1C; + } + + /// + /// Validates correctness of CRC-32 data in source buffer with assumption that CRC-32 data located at end of buffer in reverse bytes order. Can be used in conjunction with + /// + /// Input buffer with data to be checksummed. + /// Is checksum valid. + public static bool IsValidWithCrcAtEnd(byte[] input) + { + if (input.Length < 4) + throw new ArgumentOutOfRangeException("input", "Input array should be 4 bytes at least"); + return Append(0, input, 0, input.Length) == 0x2144DF1C; + } + + + private static readonly SafeProxy _proxy = new SafeProxy(); + private static uint AppendInternal(uint initial, byte[] input, int offset, int length) + { + if (length > 0) + { + return _proxy.Append(initial, input, offset, length); + } + else + return initial; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Utility/Misc/CRC32Algorithm.cs.meta b/Assets/YooAsset/Runtime/Utility/Misc/CRC32Algorithm.cs.meta new file mode 100644 index 0000000..bb12053 --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/Misc/CRC32Algorithm.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e5eb0b1129d88a744973666a66a1902c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/Utility/StringUtility.cs b/Assets/YooAsset/Runtime/Utility/StringUtility.cs new file mode 100644 index 0000000..7b3c203 --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/StringUtility.cs @@ -0,0 +1,107 @@ +using System; +using System.Text; +using System.IO; +using System.Collections.Generic; + +namespace YooAsset.Utility +{ + public static class StringUtility + { + [ThreadStatic] + private static StringBuilder _cacheBuilder = new StringBuilder(1024); + + public static string Format(string format, object arg0) + { + if (string.IsNullOrEmpty(format)) + throw new ArgumentNullException(); + + _cacheBuilder.Length = 0; + _cacheBuilder.AppendFormat(format, arg0); + return _cacheBuilder.ToString(); + } + public static string Format(string format, object arg0, object arg1) + { + if (string.IsNullOrEmpty(format)) + throw new ArgumentNullException(); + + _cacheBuilder.Length = 0; + _cacheBuilder.AppendFormat(format, arg0, arg1); + return _cacheBuilder.ToString(); + } + public static string Format(string format, object arg0, object arg1, object arg2) + { + if (string.IsNullOrEmpty(format)) + throw new ArgumentNullException(); + + _cacheBuilder.Length = 0; + _cacheBuilder.AppendFormat(format, arg0, arg1, arg2); + return _cacheBuilder.ToString(); + } + public static string Format(string format, params object[] args) + { + if (string.IsNullOrEmpty(format)) + throw new ArgumentNullException(); + + if (args == null) + throw new ArgumentNullException(); + + _cacheBuilder.Length = 0; + _cacheBuilder.AppendFormat(format, args); + return _cacheBuilder.ToString(); + } + + public static List StringToStringList(string str, char separator) + { + List result = new List(); + if (!String.IsNullOrEmpty(str)) + { + string[] splits = str.Split(separator); + foreach (string split in splits) + { + if (!String.IsNullOrEmpty(split)) + { + result.Add(split); + } + } + } + return result; + } + public static bool StringToBool(string str) + { + int value = (int)Convert.ChangeType(str, typeof(int)); + return value > 0; + } + public static T NameToEnum(string name) + { + if (Enum.IsDefined(typeof(T), name) == false) + { + throw new ArgumentException($"Enum {typeof(T)} is not defined name {name}"); + } + return (T)Enum.Parse(typeof(T), name); + } + + public static string RemoveFirstChar(string str) + { + if (string.IsNullOrEmpty(str)) + return str; + return str.Substring(1); + } + public static string RemoveLastChar(string str) + { + if (string.IsNullOrEmpty(str)) + return str; + return str.Substring(0, str.Length - 1); + } + public static string RemoveExtension(string str) + { + if (string.IsNullOrEmpty(str)) + return str; + + int index = str.LastIndexOf("."); + if (index == -1) + return str; + else + return str.Remove(index); //"assets/config/test.unity3d" --> "assets/config/test" + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/Utility/StringUtility.cs.meta b/Assets/YooAsset/Runtime/Utility/StringUtility.cs.meta new file mode 100644 index 0000000..78b6e49 --- /dev/null +++ b/Assets/YooAsset/Runtime/Utility/StringUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ce33ab90add0ab4aa7063977e23284e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/YooAsset.asmdef b/Assets/YooAsset/Runtime/YooAsset.asmdef new file mode 100644 index 0000000..f3b53a8 --- /dev/null +++ b/Assets/YooAsset/Runtime/YooAsset.asmdef @@ -0,0 +1,14 @@ +{ + "name": "YooAsset", + "rootNamespace": "", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/YooAsset.asmdef.meta b/Assets/YooAsset/Runtime/YooAsset.asmdef.meta new file mode 100644 index 0000000..5b2b5b0 --- /dev/null +++ b/Assets/YooAsset/Runtime/YooAsset.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e34a5702dd353724aa315fb8011f08c3 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/YooAssets.cs b/Assets/YooAsset/Runtime/YooAssets.cs new file mode 100644 index 0000000..e04d8dd --- /dev/null +++ b/Assets/YooAsset/Runtime/YooAssets.cs @@ -0,0 +1,538 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset +{ + public static class YooAssets + { + /// + /// 运行模式 + /// + private enum EPlayMode + { + /// + /// 编辑器下模拟运行模式 + /// + EditorPlayMode, + + /// + /// 离线模式 + /// + OfflinePlayMode, + + /// + /// 网络模式 + /// + HostPlayMode, + } + + public abstract class CreateParameters + { + /// + /// 资源定位的根路径 + /// 例如:Assets/MyResource + /// + public string LocationRoot; + + /// + /// 文件解密接口 + /// + public IDecryptServices DecryptServices = null; + + /// + /// 资源系统自动释放零引用资源的间隔秒数 + /// 注意:如果小于等于零代表不自动释放,可以使用YooAssets.UnloadUnusedAssets接口主动释放 + /// + public float AutoReleaseInterval = -1; + + /// + /// 资源加载的最大数量 + /// + public int AssetLoadingMaxNumber = int.MaxValue; + } + + /// + /// 编辑器下模拟运行(只支持在编辑器下运行) + /// + public class EditorPlayModeParameters : CreateParameters + { + } + + /// + /// 离线模式(本地打包运行模式) + /// + public class OfflinePlayModeParameters : CreateParameters + { + } + + /// + /// 网络模式(网络打包运行模式) + /// + public class HostPlayModeParameters : CreateParameters + { + /// + /// 当缓存池被污染的时候清理缓存池 + /// + public bool ClearCacheWhenDirty; + + /// + /// 忽略资源版本号 + /// + public bool IgnoreResourceVersion; + + /// + /// 默认的资源服务器下载地址 + /// + public string DefaultHostServer; + + /// + /// 备用的资源服务器下载地址 + /// + public string FallbackHostServer; + } + + + private static string _locationRoot; + private static EPlayMode _playMode; + private static IBundleServices _bundleServices; + private static EditorPlayModeImpl _editorPlayModeImpl; + private static OfflinePlayModeImpl _offlinePlayModeImpl; + private static HostPlayModeImpl _hostPlayModeImpl; + + private static float _releaseTimer; + private static float _releaseCD = -1f; + + + /// + /// 异步初始化 + /// + public static InitializationOperation InitializeAsync(CreateParameters parameters) + { + if (parameters == null) + throw new Exception($"YooAsset create parameters is invalid."); + +#if !UNITY_EDITOR + if (parameters is EditorPlayModeParameters) + throw new Exception($"Editor play mode only support unity editor."); +#endif + + // 检测创建参数 + if (parameters.AssetLoadingMaxNumber < 3) + { + parameters.AssetLoadingMaxNumber = 3; + Logger.Warning($"{nameof(parameters.AssetLoadingMaxNumber)} minimum is 3"); + } + + // 创建间隔计时器 + if (parameters.AutoReleaseInterval > 0) + { + _releaseCD = parameters.AutoReleaseInterval; + } + + if (string.IsNullOrEmpty(parameters.LocationRoot) == false) + _locationRoot = AssetPathHelper.GetRegularPath(parameters.LocationRoot); + + // 运行模式 + if (parameters is EditorPlayModeParameters) + _playMode = EPlayMode.EditorPlayMode; + else if (parameters is OfflinePlayModeParameters) + _playMode = EPlayMode.OfflinePlayMode; + else if (parameters is HostPlayModeParameters) + _playMode = EPlayMode.HostPlayMode; + else + throw new NotImplementedException(); + + // 初始化 + if (_playMode == EPlayMode.EditorPlayMode) + { + _editorPlayModeImpl = new EditorPlayModeImpl(); + _bundleServices = _editorPlayModeImpl; + AssetSystem.Initialize(true, parameters.AssetLoadingMaxNumber, parameters.DecryptServices, _bundleServices); + return _editorPlayModeImpl.InitializeAsync(); + } + else if (_playMode == EPlayMode.OfflinePlayMode) + { + _offlinePlayModeImpl = new OfflinePlayModeImpl(); + _bundleServices = _offlinePlayModeImpl; + AssetSystem.Initialize(false, parameters.AssetLoadingMaxNumber, parameters.DecryptServices, _bundleServices); + return _offlinePlayModeImpl.InitializeAsync(); + } + else if (_playMode == EPlayMode.HostPlayMode) + { + _hostPlayModeImpl = new HostPlayModeImpl(); + _bundleServices = _hostPlayModeImpl; + AssetSystem.Initialize(false, parameters.AssetLoadingMaxNumber, parameters.DecryptServices, _bundleServices); + var hostPlayModeParameters = parameters as HostPlayModeParameters; + return _hostPlayModeImpl.InitializeAsync( + hostPlayModeParameters.ClearCacheWhenDirty, + hostPlayModeParameters.IgnoreResourceVersion, + hostPlayModeParameters.DefaultHostServer, + hostPlayModeParameters.FallbackHostServer); + } + else + { + throw new NotImplementedException(); + } + } + + /// + /// 向网络端请求并更新补丁清单 + /// + /// 更新的资源版本号 + /// 超时时间 + public static UpdateManifestOperation UpdateManifestAsync(int updateResourceVersion, int timeout) + { + if (_playMode == EPlayMode.EditorPlayMode) + { + var operation = new EditorModeUpdateManifestOperation(); + OperationUpdater.ProcessOperaiton(operation); + return operation; + } + else if (_playMode == EPlayMode.OfflinePlayMode) + { + var operation = new OfflinePlayModeUpdateManifestOperation(); + OperationUpdater.ProcessOperaiton(operation); + return operation; + } + else if (_playMode == EPlayMode.HostPlayMode) + { + if (_hostPlayModeImpl == null) + throw new Exception("YooAsset is not initialized."); + return _hostPlayModeImpl.UpdatePatchManifestAsync(updateResourceVersion, timeout); + } + else + { + throw new NotImplementedException(); + } + } + + + + /// + /// 更新资源系统 + /// + public static void Update() + { + // 更新异步请求操作 + OperationUpdater.Update(); + + // 更新下载管理系统 + DownloadSystem.Update(); + + // 轮询更新资源系统 + AssetSystem.UpdatePoll(); + + // 自动释放零引用资源 + if (_releaseCD > 0) + { + _releaseTimer += UnityEngine.Time.unscaledDeltaTime; + if (_releaseTimer >= _releaseCD) + { + _releaseTimer = 0f; + AssetSystem.UnloadUnusedAssets(); + } + } + } + + /// + /// 获取资源版本号 + /// + public static int GetResourceVersion() + { + if (_playMode == EPlayMode.EditorPlayMode) + { + if (_editorPlayModeImpl == null) + throw new Exception("YooAsset is not initialized."); + return _editorPlayModeImpl.GetResourceVersion(); + } + else if (_playMode == EPlayMode.OfflinePlayMode) + { + if (_offlinePlayModeImpl == null) + throw new Exception("YooAsset is not initialized."); + return _offlinePlayModeImpl.GetResourceVersion(); + } + else if (_playMode == EPlayMode.HostPlayMode) + { + if (_hostPlayModeImpl == null) + throw new Exception("YooAsset is not initialized."); + return _hostPlayModeImpl.GetResourceVersion(); + } + else + { + throw new NotImplementedException(); + } + } + + /// + /// 获取内置资源标记列表 + /// + public static string[] GetManifestBuildinTags() + { + if (_playMode == EPlayMode.EditorPlayMode) + { + if (_editorPlayModeImpl == null) + throw new Exception("YooAsset is not initialized."); + return _editorPlayModeImpl.GetManifestBuildinTags(); + } + else if (_playMode == EPlayMode.OfflinePlayMode) + { + if (_offlinePlayModeImpl == null) + throw new Exception("YooAsset is not initialized."); + return _offlinePlayModeImpl.GetManifestBuildinTags(); + } + else if (_playMode == EPlayMode.HostPlayMode) + { + if (_hostPlayModeImpl == null) + throw new Exception("YooAsset is not initialized."); + return _hostPlayModeImpl.GetManifestBuildinTags(); + } + else + { + throw new NotImplementedException(); + } + } + + /// + /// 获取资源包信息 + /// + public static AssetBundleInfo GetAssetBundleInfo(string location) + { + string assetPath = ConvertLocationToAssetPath(location); + string bundleName = _bundleServices.GetAssetBundleName(assetPath); + return _bundleServices.GetAssetBundleInfo(bundleName); + } + + /// + /// 资源回收(卸载引用计数为零的资源) + /// + public static void UnloadUnusedAssets() + { + AssetSystem.UpdatePoll(); + AssetSystem.UnloadUnusedAssets(); + } + + /// + /// 强制回收所有资源 + /// + public static void ForceUnloadAllAssets() + { + AssetSystem.ForceUnloadAllAssets(); + } + + /// + /// 获取调试汇总信息 + /// + public static void GetDebugSummy(DebugSummy summy) + { + if (summy == null) + Logger.Error($"{nameof(DebugSummy)} is null"); + + AssetSystem.GetDebugSummy(summy); + } + + + #region 资源加载接口 + /// + /// 同步加载资源对象 + /// + /// 资源对象相对路径 + public static AssetOperationHandle LoadAssetSync(string location) where TObject : class + { + return LoadAssetInternal(location, typeof(TObject), true); + } + public static AssetOperationHandle LoadAssetSync(System.Type type, string location) + { + return LoadAssetInternal(location, type, true); + } + + /// + /// 同步加载子资源对象集合 + /// + /// 资源对象相对路径 + public static AssetOperationHandle LoadSubAssetsSync(string location) + { + return LoadSubAssetsInternal(location, typeof(TObject), true); + } + public static AssetOperationHandle LoadSubAssetsSync(System.Type type, string location) + { + return LoadSubAssetsInternal(location, type, true); + } + + /// + /// 异步加载场景 + /// + public static AssetOperationHandle LoadSceneAsync(string location, SceneInstanceParam instanceParam) + { + string scenePath = ConvertLocationToAssetPath(location); + var handle = AssetSystem.LoadSceneAsync(scenePath, instanceParam); + return handle; + } + + /// + /// 异步加载资源对象 + /// + /// 资源对象相对路径 + public static AssetOperationHandle LoadAssetAsync(string location) + { + return LoadAssetInternal(location, typeof(TObject), false); + } + public static AssetOperationHandle LoadAssetAsync(System.Type type, string location) + { + return LoadAssetInternal(location, type, false); + } + + /// + /// 异步加载子资源对象集合 + /// + /// 资源对象相对路径 + public static AssetOperationHandle LoadSubAssetsAsync(string location) + { + return LoadSubAssetsInternal(location, typeof(TObject), false); + } + public static AssetOperationHandle LoadSubAssetsAsync(System.Type type, string location) + { + return LoadSubAssetsInternal(location, type, false); + } + + private static AssetOperationHandle LoadAssetInternal(string location, System.Type assetType, bool waitForAsyncComplete) + { + string assetPath = ConvertLocationToAssetPath(location); + var handle = AssetSystem.LoadAssetAsync(assetPath, assetType); + if (waitForAsyncComplete) + handle.WaitForAsyncComplete(); + return handle; + } + private static AssetOperationHandle LoadSubAssetsInternal(string location, System.Type assetType, bool waitForAsyncComplete) + { + string assetPath = ConvertLocationToAssetPath(location); + var handle = AssetSystem.LoadSubAssetsAsync(assetPath, assetType); + if (waitForAsyncComplete) + handle.WaitForAsyncComplete(); + return handle; + } + #endregion + + #region 资源下载接口 + /// + /// 创建补丁下载器 + /// + /// DLC标记 + /// 同时下载的最大文件数 + /// 下载失败的重试次数 + public static PatchDownloader CreateDLCDownloader(string dlcTag, int downloadingMaxNumber, int failedTryAgain) + { + return CreateDLCDownloader(new string[] { dlcTag }, downloadingMaxNumber, failedTryAgain); + } + + /// + /// 创建补丁下载器 + /// + /// DLC标记列表 + /// 同时下载的最大文件数 + /// 下载失败的重试次数 + public static PatchDownloader CreateDLCDownloader(string[] dlcTags, int downloadingMaxNumber, int failedTryAgain) + { + if (_playMode == EPlayMode.EditorPlayMode) + { + List downloadList = new List(); + PatchDownloader downlader = new PatchDownloader(null, downloadList, downloadingMaxNumber, failedTryAgain); + return downlader; + } + else if (_playMode == EPlayMode.OfflinePlayMode) + { + List downloadList = new List(); + PatchDownloader downlader = new PatchDownloader(null, downloadList, downloadingMaxNumber, failedTryAgain); + return downlader; + } + else if (_playMode == EPlayMode.HostPlayMode) + { + if (_hostPlayModeImpl == null) + throw new Exception("YooAsset is not initialized."); + return _hostPlayModeImpl.CreateDLCDownloader(dlcTags, downloadingMaxNumber, failedTryAgain); + } + else + { + throw new NotImplementedException(); + } + } + + /// + /// 创建补丁下载器 + /// + /// 资源列表 + /// 同时下载的最大文件数 + /// 下载失败的重试次数 + public static PatchDownloader CreateBundleDownloader(string[] locations, int downloadingMaxNumber, int failedTryAgain) + { + if (_playMode == EPlayMode.EditorPlayMode) + { + List downloadList = new List(); + PatchDownloader downlader = new PatchDownloader(null, downloadList, downloadingMaxNumber, failedTryAgain); + return downlader; + } + else if (_playMode == EPlayMode.OfflinePlayMode) + { + List downloadList = new List(); + PatchDownloader downlader = new PatchDownloader(null, downloadList, downloadingMaxNumber, failedTryAgain); + return downlader; + } + else if (_playMode == EPlayMode.HostPlayMode) + { + if (_hostPlayModeImpl == null) + throw new Exception("YooAsset is not initialized."); + + List assetPaths = new List(locations.Length); + foreach (var location in locations) + { + string assetPath = ConvertLocationToAssetPath(location); + assetPaths.Add(assetPath); + } + return _hostPlayModeImpl.CreateBundleDownloader(assetPaths, downloadingMaxNumber, failedTryAgain); + } + else + { + throw new NotImplementedException(); + } + } + #endregion + + #region 沙盒相关 + /// + /// 清空沙盒目录 + /// 注意:可以使用该方法修复我们本地的客户端 + /// + public static void ClearSandbox() + { + Logger.Warning("Clear sandbox."); + PatchHelper.ClearSandbox(); + } + + /// + /// 获取沙盒文件夹的路径 + /// + public static string GetSandboxRoot() + { + return AssetPathHelper.MakePersistentRootPath(); + } + #endregion + + #region 内部方法 + /// + /// 定位地址转换为资源路径 + /// + private static string ConvertLocationToAssetPath(string location) + { + if (_playMode == EPlayMode.EditorPlayMode) + { + string filePath = AssetPathHelper.CombineAssetPath(_locationRoot, location); + return AssetPathHelper.FindDatabaseAssetPath(filePath); + } + else + { + return AssetPathHelper.CombineAssetPath(_locationRoot, location); + } + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/YooAssets.cs.meta b/Assets/YooAsset/Runtime/YooAssets.cs.meta new file mode 100644 index 0000000..946a79d --- /dev/null +++ b/Assets/YooAsset/Runtime/YooAssets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bbc02342251289b46a6fedc192aaa9b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/manifest.json b/Packages/manifest.json new file mode 100644 index 0000000..9c20be9 --- /dev/null +++ b/Packages/manifest.json @@ -0,0 +1,49 @@ +{ + "dependencies": { + "com.unity.2d.animation": "5.0.8", + "com.unity.2d.pixel-perfect": "4.0.1", + "com.unity.2d.psdimporter": "4.1.1", + "com.unity.2d.sprite": "1.0.0", + "com.unity.2d.spriteshape": "5.1.5", + "com.unity.2d.tilemap": "1.0.0", + "com.unity.addressables": "1.18.19", + "com.unity.ide.visualstudio": "2.0.14", + "com.unity.ide.vscode": "1.2.4", + "com.unity.test-framework": "1.1.29", + "com.unity.textmeshpro": "3.0.6", + "com.unity.timeline": "1.4.8", + "com.unity.ugui": "1.0.0", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.autostreaming": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json new file mode 100644 index 0000000..c009809 --- /dev/null +++ b/Packages/packages-lock.json @@ -0,0 +1,435 @@ +{ + "dependencies": { + "com.unity.2d.animation": { + "version": "5.0.8", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.2d.common": "4.0.3", + "com.unity.mathematics": "1.1.0", + "com.unity.2d.sprite": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.uielements": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.common": { + "version": "4.0.3", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.2d.sprite": "1.0.0", + "com.unity.modules.uielements": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.path": { + "version": "4.0.2", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.pixel-perfect": { + "version": "4.0.1", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.psdimporter": { + "version": "4.1.1", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.2d.common": "4.0.3", + "com.unity.2d.animation": "5.0.8", + "com.unity.2d.sprite": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.sprite": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.2d.spriteshape": { + "version": "5.1.5", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.mathematics": "1.1.0", + "com.unity.2d.common": "4.0.3", + "com.unity.2d.path": "4.0.2", + "com.unity.modules.physics2d": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.tilemap": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.addressables": { + "version": "1.18.19", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.scriptablebuildpipeline": "1.19.2", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.ext.nunit": { + "version": "1.0.6", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.cn" + }, + "com.unity.ide.visualstudio": { + "version": "2.0.14", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.9" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.ide.vscode": { + "version": "1.2.4", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.cn" + }, + "com.unity.mathematics": { + "version": "1.1.0", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.cn" + }, + "com.unity.scriptablebuildpipeline": { + "version": "1.19.2", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.cn" + }, + "com.unity.test-framework": { + "version": "1.1.29", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.textmeshpro": { + "version": "3.0.6", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.timeline": { + "version": "1.4.8", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.director": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.ugui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0" + } + }, + "com.unity.modules.ai": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.androidjni": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.animation": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.assetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.audio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.autostreaming": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0" + } + }, + "com.unity.modules.cloth": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.director": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.animation": "1.0.0" + } + }, + "com.unity.modules.imageconversion": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.imgui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.jsonserialize": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.particlesystem": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics2d": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.screencapture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.subsystems": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.terrain": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.terrainphysics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.terrain": "1.0.0" + } + }, + "com.unity.modules.tilemap": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics2d": "1.0.0" + } + }, + "com.unity.modules.ui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.uielements": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.uielementsnative": "1.0.0" + } + }, + "com.unity.modules.uielementsnative": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.umbra": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unityanalytics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.unitywebrequest": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unitywebrequestassetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestaudio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.audio": "1.0.0" + } + }, + "com.unity.modules.unitywebrequesttexture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestwww": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.vehicles": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.video": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.vr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } + }, + "com.unity.modules.wind": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.xr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.subsystems": "1.0.0" + } + } + } +} diff --git a/ProjectSettings/AudioManager.asset b/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..27287fe --- /dev/null +++ b/ProjectSettings/AudioManager.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 1024 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 + m_RequestedDSPBufferSize: 0 diff --git a/ProjectSettings/AutoStreamingSettings.asset b/ProjectSettings/AutoStreamingSettings.asset new file mode 100644 index 0000000..d3e071e --- /dev/null +++ b/ProjectSettings/AutoStreamingSettings.asset @@ -0,0 +1,21 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1200 &1 +AutoStreamingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + mSearchMode: 15 + mCustomSearchFile: + mTextureSearchString: + mMeshSearchString: + mTextures: [] + mAudios: [] + mMeshes: [] + mScenes: [] + mConfigCCD: + useCCD: 0 + cosKey: + projectGuid: + bucketUuid: + bucketName: + badgeName: diff --git a/ProjectSettings/ClusterInputManager.asset b/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..e7886b2 --- /dev/null +++ b/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/ProjectSettings/DynamicsManager.asset b/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..fc90ab9 --- /dev/null +++ b/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0.1 + m_ClothInterCollisionStiffness: 0.2 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 0 + m_ReuseCollisionCallbacks: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ClothGravity: {x: 0, y: -9.81, z: 0} + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 + m_FrictionType: 0 + m_EnableEnhancedDeterminism: 0 + m_EnableUnifiedHeightmaps: 1 + m_SolverType: 0 + m_DefaultMaxAngularSpeed: 50 diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..82ab0f5 --- /dev/null +++ b/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: + - enabled: 1 + path: Assets/Scenes/SampleScene.unity + guid: 2cda990e2423bbf4892e6590ba056729 + m_configObjects: {} diff --git a/ProjectSettings/EditorSettings.asset b/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..b1ef674 --- /dev/null +++ b/ProjectSettings/EditorSettings.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 10 + m_ExternalVersionControlSupport: Visible Meta Files + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 0 + m_DefaultBehaviorMode: 1 + m_PrefabRegularEnvironment: {fileID: 0} + m_PrefabUIEnvironment: {fileID: 0} + m_SpritePackerMode: 4 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref;rsp + m_ProjectGenerationRootNamespace: + m_CollabEditorSettings: + inProgressEnabled: 1 + m_EnableTextureStreamingInEditMode: 1 + m_EnableTextureStreamingInPlayMode: 1 + m_AsyncShaderCompilation: 1 + m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptions: 3 + m_ShowLightmapResolutionOverlay: 1 + m_UseLegacyProbeSampleCount: 0 + m_SerializeInlineMappingsOnOneLine: 1 + m_AssetPipelineMode: 1 + m_CacheServerMode: 0 + m_CacheServerEndpoint: + m_CacheServerNamespacePrefix: default + m_CacheServerEnableDownload: 1 + m_CacheServerEnableUpload: 1 diff --git a/ProjectSettings/GraphicsSettings.asset b/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..36bc22d --- /dev/null +++ b/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,63 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_VideoShadersIncludeMode: 2 + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 + m_LogWhenShaderIsCompiled: 0 diff --git a/ProjectSettings/InputManager.asset b/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..b16147e --- /dev/null +++ b/ProjectSettings/InputManager.asset @@ -0,0 +1,487 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: joystick button 8 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: backspace + altNegativeButton: + altPositiveButton: joystick button 9 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Reset + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Next + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page down + altNegativeButton: + altPositiveButton: joystick button 5 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Previous + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page up + altNegativeButton: + altPositiveButton: joystick button 4 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Validate + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Persistent + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: right shift + altNegativeButton: + altPositiveButton: joystick button 2 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Multiplier + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: joystick button 3 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 6 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 5 + joyNum: 0 diff --git a/ProjectSettings/NavMeshAreas.asset b/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..3b0b7c3 --- /dev/null +++ b/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid diff --git a/ProjectSettings/NetworkManager.asset b/ProjectSettings/NetworkManager.asset new file mode 100644 index 0000000..5dc6a83 --- /dev/null +++ b/ProjectSettings/NetworkManager.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!149 &1 +NetworkManager: + m_ObjectHideFlags: 0 + m_DebugLevel: 0 + m_Sendrate: 15 + m_AssetToPrefab: {} diff --git a/ProjectSettings/PackageManagerSettings.asset b/ProjectSettings/PackageManagerSettings.asset new file mode 100644 index 0000000..bc93024 --- /dev/null +++ b/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_EnablePreviewPackages: 0 + m_EnablePackageDependencies: 0 + m_AdvancedSettingsExpanded: 1 + m_ScopedRegistriesSettingsExpanded: 1 + oneTimeWarningShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.cn + m_Scopes: [] + m_IsDefault: 1 + m_Capabilities: 7 + m_UserSelectedRegistryName: + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_ErrorMessage: + m_Original: + m_Id: + m_Name: + m_Url: + m_Scopes: [] + m_IsDefault: 0 + m_Capabilities: 0 + m_Modified: 0 + m_Name: + m_Url: + m_Scopes: + - + m_SelectedScopeIndex: 0 diff --git a/ProjectSettings/Physics2DSettings.asset b/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..6cfcdda --- /dev/null +++ b/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,56 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_JobOptions: + serializedVersion: 2 + useMultithreading: 0 + useConsistencySorting: 0 + m_InterpolationPosesPerJob: 100 + m_NewContactsPerJob: 30 + m_CollideContactsPerJob: 100 + m_ClearFlagsPerJob: 200 + m_ClearBodyForcesPerJob: 200 + m_SyncDiscreteFixturesPerJob: 50 + m_SyncContinuousFixturesPerJob: 50 + m_FindNearestContactsPerJob: 100 + m_UpdateTriggerContactsPerJob: 100 + m_IslandSolverCostThreshold: 100 + m_IslandSolverBodyCostScale: 1 + m_IslandSolverContactCostScale: 10 + m_IslandSolverJointCostScale: 10 + m_IslandSolverBodiesPerJob: 50 + m_IslandSolverContactsPerJob: 50 + m_SimulationMode: 0 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_CallbacksOnDisable: 1 + m_ReuseCollisionCallbacks: 1 + m_AutoSyncTransforms: 0 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/ProjectSettings/PresetManager.asset b/ProjectSettings/PresetManager.asset new file mode 100644 index 0000000..67a94da --- /dev/null +++ b/ProjectSettings/PresetManager.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_DefaultPresets: {} diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..d64b1cb --- /dev/null +++ b/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,669 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 22 + productGUID: 4a4a7a13133dc924198729793da75e8f + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: YooAsset + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_ShowUnitySplashAds: 0 + m_AdsAndroidGameId: + m_AdsIosGameId: + m_ShowSplashAdsSlogan: 0 + m_SloganImage: {fileID: 0} + m_SloganHeight: 150 + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1920 + defaultScreenHeight: 1080 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + mipStripping: 0 + numberOfMipsStripped: 0 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + iosUseCustomAppBackgroundBehavior: 0 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 1 + androidUseSwappy: 1 + androidBlitType: 0 + androidResizableWindow: 0 + androidDefaultWindowWidth: 1920 + androidDefaultWindowHeight: 1080 + androidMinimumWindowWidth: 400 + androidMinimumWindowHeight: 300 + androidFullscreenMode: 1 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 0 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + autoStreaming: 0 + useAnimationStreaming: 0 + useFontStreaming: 0 + autoStreamingId: + instantGameAppId: + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + useFlipModelSwapchain: 1 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + stadiaPresentMode: 0 + stadiaTargetFramerate: 0 + vulkanNumSwapchainBuffers: 3 + vulkanEnableSetSRGBWrite: 0 + vulkanEnablePreTransform: 0 + vulkanEnableLateAcquireNextImage: 0 + vulkanEnableCommandBufferRecycling: 1 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + vrSettings: + enable360StereoCapture: 0 + isWsaHolographicRemotingEnabled: 0 + enableFrameTimingStats: 0 + useHDRDisplay: 0 + D3DHDRBitDepth: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: {} + buildNumber: + Standalone: 0 + iPhone: 0 + tvOS: 0 + overrideDefaultApplicationIdentifier: 0 + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 19 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + VertexChannelCompressionMask: 4054 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: 11.0 + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 11.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSLaunchScreenCustomStoryboardPath: + iOSLaunchScreeniPadCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + shaderPrecisionModel: 0 + clonedFromGUID: 10ad67313f4034357812315f3c407484 + templatePackageId: com.unity.template.2d@5.0.0 + templateDefaultScene: Assets/Scenes/SampleScene.unity + useCustomMainManifest: 0 + useCustomLauncherManifest: 0 + useCustomMainGradleTemplate: 0 + useCustomLauncherGradleManifest: 0 + useCustomBaseGradleTemplate: 0 + useCustomGradlePropertiesTemplate: 0 + useCustomProguardFile: 0 + AndroidTargetArchitectures: 1 + AndroidTargetDevices: 0 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 0 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + androidUseCustomKeystore: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + chromeosInputEmulation: 1 + AndroidMinifyWithR8: 0 + AndroidMinifyRelease: 0 + AndroidMinifyDebug: 0 + AndroidValidateAppBundleSize: 1 + AndroidAppBundleSizeToValidate: 150 + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetSecurityBuild: [] + m_BuildTargetGraphicsJobs: + - m_BuildTarget: MacStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: Switch + m_GraphicsJobs: 0 + - m_BuildTarget: MetroSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AppleTVSupport + m_GraphicsJobs: 0 + - m_BuildTarget: BJMSupport + m_GraphicsJobs: 0 + - m_BuildTarget: LinuxStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: PS4Player + m_GraphicsJobs: 0 + - m_BuildTarget: iOSSupport + m_GraphicsJobs: 0 + - m_BuildTarget: WindowsStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobs: 0 + - m_BuildTarget: LuminSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AndroidPlayer + m_GraphicsJobs: 0 + - m_BuildTarget: WebGLSupport + m_GraphicsJobs: 0 + m_BuildTargetGraphicsJobMode: [] + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: AndroidPlayer + m_APIs: 150000000b000000 + m_Automatic: 0 + - m_BuildTarget: iOSSupport + m_APIs: 10000000 + m_Automatic: 1 + m_BuildTargetVRSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + openGLRequireES32: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + m_BuildTargetNormalMapEncoding: [] + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + bluetoothUsageDescription: + switchNMETAOverride: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchUseGOLDLinker: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 22 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + switchUseNewStyleFilepaths: 0 + switchUseMicroSleepForYield: 1 + switchMicroSleepForYieldTime: 25 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 2 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + playerPrefsMaxSize: 32768 + ps4Passcode: bi9UOuSpM2Tlh01vOzwvSikHFswuzleh + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4UseLowGarlicFragmentationMode: 1 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 2 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + ps4attribVROutputEnabled: 0 + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + blurSplashScreenBackground: 1 + spritePackerPolicy: + webGLMemorySize: 32 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 1 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 0 + webGLWasmArithmeticExceptions: 0 + webGLLinkerTarget: 1 + webGLThreadsSupport: 0 + webGLDecompressionFallback: 0 + scriptingDefineSymbols: {} + additionalCompilerArguments: {} + platformArchitecture: {} + scriptingBackend: {} + il2cppCompilerConfiguration: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + suppressCommonWarnings: 1 + allowUnsafeCode: 0 + useDeterministicCompilation: 1 + useReferenceAssemblies: 1 + enableRoslynAnalyzers: 1 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + gcIncremental: 1 + assemblyVersionValidation: 1 + gcWBarrierValidation: 0 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: 2D_BuiltInRenderer + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: 2D_BuiltInRenderer + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnhancedXboxCompatibilityMode: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: + vrEditorSettings: {} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_SignPackage: 1 + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + apiCompatibilityLevel: 6 + activeInputHandler: 0 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + qualitySettingsNames: [] + projectName: + organizationId: + cloudEnabled: 0 + legacyClampBlendShapeWeights: 0 + virtualTexturingSupportEnabled: 0 diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..998706b --- /dev/null +++ b/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2020.3.21f1c1 +m_EditorVersionWithRevision: 2020.3.21f1c1 (ae728190a658) diff --git a/ProjectSettings/QualitySettings.asset b/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..d24eb10 --- /dev/null +++ b/ProjectSettings/QualitySettings.asset @@ -0,0 +1,236 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 255 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + Lumin: 5 + Nintendo Switch: 5 + PS4: 5 + Stadia: 5 + Standalone: 5 + WebGL: 3 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..1c92a78 --- /dev/null +++ b/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/ProjectSettings/TimeManager.asset b/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..558a017 --- /dev/null +++ b/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..b2f8bbe --- /dev/null +++ b/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,37 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 1 + m_Enabled: 0 + m_TestMode: 0 + m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events + m_EventUrl: https://cdp.cloud.unity3d.com/v1/events + m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_DashboardUrl: https://dashboard.unity3d.com + m_CNEventUrl: https://cdp.cloud.unity.cn/v1/events + m_CNConfigUrl: https://cdp.cloud.unity.cn/config + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity.cn + m_Enabled: 0 + m_LogBufferSize: 10 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 1 + m_TestMode: 0 + m_InitializeOnStartup: 1 + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/ProjectSettings/VFXManager.asset b/ProjectSettings/VFXManager.asset new file mode 100644 index 0000000..3a95c98 --- /dev/null +++ b/ProjectSettings/VFXManager.asset @@ -0,0 +1,12 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_StripUpdateShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 diff --git a/ProjectSettings/VersionControlSettings.asset b/ProjectSettings/VersionControlSettings.asset new file mode 100644 index 0000000..dca2881 --- /dev/null +++ b/ProjectSettings/VersionControlSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!890905787 &1 +VersionControlSettings: + m_ObjectHideFlags: 0 + m_Mode: Visible Meta Files + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/ProjectSettings/XRSettings.asset b/ProjectSettings/XRSettings.asset new file mode 100644 index 0000000..482590c --- /dev/null +++ b/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 4eb9f5e..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# YooAsset \ No newline at end of file