diff --git a/OneSignalExample/Assets/AssetStoreTools/AS_Checklist.asset b/OneSignalExample/Assets/AssetStoreTools/AS_Checklist.asset deleted file mode 100644 index 4f8ab8d3b..000000000 --- a/OneSignalExample/Assets/AssetStoreTools/AS_Checklist.asset +++ /dev/null @@ -1,435 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &-8947285676733080522 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 13 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &-8755971793223368065 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 0 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &-8661936208335236000 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 6 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &-7871847419538706788 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 5 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &-6698765338948244159 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 15 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &-6339410273520006441 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 19 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &-6061428815975448629 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 11 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &-5679275496968565740 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 3 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &-5198808791204941492 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 17 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &-2134886366345951030 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 18 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &-981265310457203865 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 2 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: -479656862, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: AS_Checklist - m_EditorClassIdentifier: - Checks: - - {fileID: -8755971793223368065} - - {fileID: 803030733214631059} - - {fileID: -981265310457203865} - - {fileID: -5679275496968565740} - - {fileID: 6856344075226757343} - - {fileID: -7871847419538706788} - - {fileID: -8661936208335236000} - - {fileID: 8038474888795199677} - - {fileID: 4351420400993694493} - - {fileID: 4762057310115538168} - - {fileID: 3269503747262216792} - - {fileID: -6061428815975448629} - - {fileID: 52070677570945434} - - {fileID: -8947285676733080522} - - {fileID: 3958176054584142399} - - {fileID: -6698765338948244159} - - {fileID: 877433876370547074} - - {fileID: -5198808791204941492} - - {fileID: -2134886366345951030} - - {fileID: -6339410273520006441} ---- !u!114 &52070677570945434 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 12 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &803030733214631059 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 1 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &877433876370547074 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 16 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &3269503747262216792 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 10 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &3958176054584142399 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 14 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &4351420400993694493 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 8 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &4762057310115538168 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 9 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &6856344075226757343 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 4 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 ---- !u!114 &8038474888795199677 -MonoBehaviour: - m_ObjectHideFlags: 0 - 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: 1783199433, guid: db89eddc1931309408191e6d4644d3c7, type: 3} - m_Name: - m_EditorClassIdentifier: - Type: 7 - AssetPaths: [] - Status: 0 - Active: 1 - Foldout: 0 - FoldoutMessage: 1 - FoldoutPaths: 1 - Failed: 0 diff --git a/OneSignalExample/Assets/AssetStoreTools/Editor/AssetStoreTools.dll b/OneSignalExample/Assets/AssetStoreTools/Editor/AssetStoreTools.dll deleted file mode 100644 index c836772fc..000000000 Binary files a/OneSignalExample/Assets/AssetStoreTools/Editor/AssetStoreTools.dll and /dev/null differ diff --git a/OneSignalExample/Assets/AssetStoreTools/Editor/AssetStoreTools.dll.meta b/OneSignalExample/Assets/AssetStoreTools/Editor/AssetStoreTools.dll.meta deleted file mode 100644 index a139fbdd1..000000000 --- a/OneSignalExample/Assets/AssetStoreTools/Editor/AssetStoreTools.dll.meta +++ /dev/null @@ -1,33 +0,0 @@ -fileFormatVersion: 2 -guid: db89eddc1931309408191e6d4644d3c7 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 1 - settings: - DefaultValueInitialized: true - - first: - Windows Store Apps: WindowsStoreApps - second: - enabled: 0 - settings: - CPU: AnyCPU - userData: - assetBundleName: - assetBundleVariant: diff --git a/OneSignalExample/Assets/OneSignal/Attribution/OneSignal.UnityPackage.Attribution.asmdef b/OneSignalExample/Assets/OneSignal/Attribution/OneSignal.UnityPackage.Attribution.asmdef index 52bfc8966..2812d2289 100644 --- a/OneSignalExample/Assets/OneSignal/Attribution/OneSignal.UnityPackage.Attribution.asmdef +++ b/OneSignalExample/Assets/OneSignal/Attribution/OneSignal.UnityPackage.Attribution.asmdef @@ -21,4 +21,4 @@ } ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/OneSignalExample/Assets/OneSignal/Editor/OneSignal.UnityPackage.Editor.asmdef b/OneSignalExample/Assets/OneSignal/Editor/OneSignal.UnityPackage.Editor.asmdef index f5eb7ee78..9ef5b5ba8 100644 --- a/OneSignalExample/Assets/OneSignal/Editor/OneSignal.UnityPackage.Editor.asmdef +++ b/OneSignalExample/Assets/OneSignal/Editor/OneSignal.UnityPackage.Editor.asmdef @@ -21,4 +21,4 @@ } ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/OneSignalExample/Assets/OneSignal/Example/OneSignal.UnityPackage.Example.asmdef b/OneSignalExample/Assets/OneSignal/Example/OneSignal.UnityPackage.Example.asmdef index 3d7ab0170..c9cd4a43f 100644 --- a/OneSignalExample/Assets/OneSignal/Example/OneSignal.UnityPackage.Example.asmdef +++ b/OneSignalExample/Assets/OneSignal/Example/OneSignal.UnityPackage.Example.asmdef @@ -23,4 +23,4 @@ } ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/CHANGELOG.md b/OneSignalExample/Packages/com.unity.asset-store-tools/CHANGELOG.md new file mode 100644 index 000000000..2c7b5b11c --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/CHANGELOG.md @@ -0,0 +1,279 @@ +# Changelog +All notable changes to this package will be documented in this file. + +## [11.4.3] - 2024-08-01 + +### Validator Changes +- Hotfix: Remove non-ascii characters from the demo scene validation + +## [11.4.2] - 2024-07-30 + +### Validator Changes +- Check for nested .unitypackage files in the demo scene validation +- Prevent normal map test from erroring when misc importer types are detected +- Remove Templates category from the uncompressed images requirement list + +## [11.4.1] - 2024-05-10 + +### Exporter Changes +- Fixed an issue with bundled plugin folder contents not being exported + +### Other +- Miscellaneous internal changes + +## [11.4.0] - 2024-01-23 + +### Uploader Changes +- Added prevention of uploading packages larger than 6 GB +- Added a prompt to allow automatically generating meta files within hidden folders +- Fixed some obsolete API usage warnings in newer Unity versions + +### Validator Changes +- Added validation tests for: + - Animation Clip take names + - Model import logs + - Uncompressed Package size +- Updated the fail severity of Audio Clipping validation test +- Updated the Demo Scene test to treat default scenes with custom skyboxes as valid demo scenes +- Fixed some obsolete API usage warnings in newer Unity versions + +### Other +- Added an option to check for Asset Store Publishing Tools updates + +## [11.3.1] - 2023-08-14 + +### Uploader Changes +- Added the option to select indirect package dependencies from the project (e.g. Mathematics package installed by the Burst package) + +### Validator Changes +- Updated the Texture Dimensions test to ignore 'Sprite' and 'Editor GUI' texture types + +### Exporter Changes +- Updated exporter to ignore the 'ProjectSettings/ProjectVersion.txt' asset when exporting 'Complete Project' category packages + +## [11.3.0] - 2023-07-04 + +### Uploader Changes + +- Added the option to validate a pre-exported package +- Added the option to export a .unitypackage file without uploading +- Updated the dependency selection UI + +### Validator Changes + +- Added the option to validate several asset paths at once + - Note: when validating package that is comprised of several folders (e.g. Assets/MyPackage + + Assets/StreamingAssets + Assets/WebGLTemplates), please select all applicable paths that would be included in the package +- Added several new validation tests for: + - File Menu Names + - Compressed files + - Model Types + - Texture Dimensions + - Particle Systems + - Normal Map Textures + - Audio Clipping + - Path Lengths + - Script Compilation +- Updated validation test severities based on package category +- Updated validation tests to each have their own test logic class +- Updated validation tests to be displayed in alphabetical order +- Fixed several issues with the namespace check test +- Fixed scenes in Samples~ folders not being taken into account for the sample scene check test +- Other internal changes + +### Exporter Changes + +- Package exporter is now a separate module (similar to Uploader and Validator) +- Fixed hidden folders being included when exporting package content + - Note: this prevents an issue with the Unity Editor, where exported hidden folders would appear in the Project window + as empty folders when imported, despite having content on disk. Content nested within hidden folders is still collected, + provided it contains unique .meta files + +## [11.2.2] - 2023-02-23 + +### Validator Changes + +- Updated the 'LOD Setup' test to address some issues + - Added additional checks for LOD renderers (inactive renderer check, LOD Group reference check, relative hierarchy position to LOD Group check) + - LOD Group Component is no longer required to be on the root of the Prefab + - Updated the test result message interface when invalid Prefabs are found + +## [11.2.1] - 2023-01-17 + +### Uploader Changes + +- Added a more informative error when exporting content with clashing guid meta files in hidden folders +- Fixed a compilation issue for Unity 2020.1 and 2020.2 +- Fixed a rare error condition when queueing multiple package uploads in quick succession +- Fixed Asset Store Uploader state not being properly reset if the uploading process fails + +### Validator Changes + +- Updated the Asset Store Validator description +- Fixed a rare memory overflow issue when performing package validation + +## [11.2.0] - 2022-11-03 + +### Uploader Changes + +- Uploader will now use the custom package exporter by default + - An option to use the legacy (native) exporter can be found in the Asset Store Publishing Tools' settings window +- When exporting from the Assets folder, package dependencies can now be selected individually instead of being a choice between 'All' or 'None' + - This option is only available with the custom exporter +- Changed the way the Uploader reports completed uploading tasks + - Modal pop-up has been replaced by a new UI view state + - Added an option to the Asset Store Publishing Tools' Settings to display the pop-up after a completed upload +- Changed exported .unitypackage files to have distinguishable file names +- Fixed the Uploader window indefinitely stalling at 100% upload progress when a response from the Asset Store server is not received +- Fixed native package exporter producing broken packages when the export path contained hidden folders +- Fixed an issue with high CPU usage when uploading packages +- Fixed Asset Store Publishing Tools' settings not being saved between Editor sessions on macOS +- Other minor changes and tweaks + +### Validator Changes + +- Added two new tests: + - 'Types have namespaces': checks whether scripts and native libraries under the validated path are nested under a namespace + - 'Consistent line endings': checks whether scripts under the validated path have consistent line endings. This is similar to the warning from the Unity Editor compilation pipeline when a script contains both Windows and UNIX line endings. +- Improved 'Reset Prefabs' test to display and be more informative about prefabs with unusually low transform values +- Improved 'SpeedTree asset inclusion' test to search for '.st' files +- Improved 'Documentation inclusion' test to treat '.md' files as valid documentation files +- Improved 'Lossy audio file inclusion' test to treat '.aif' and '.aiff' files as valid non-lossy audio files +- Improved 'Lossy audio file inclusion' test to search the project for non-lossy variants of existing lossy audio files +- Removed 'Duplicate animation names' test +- Tweaked validation severities for several tests +- Other minor changes and tweaks + +## [11.1.0] - 2022-09-14 + +### Uploader Changes + +- Package Publisher Portal links can now be opened for all packages regardless of package status +- External Dependency Manager can now be selected as a 'Special Folder' if found in the root Assets folder + +### Validator Changes + +- Added category selection for the Validator + - Categories help determine the outcome of package validation more accurately. For example, documentation is not crucial for art packages, but is required for tooling packages. +- Added a list of prefabs with missing mesh references to 'Meshes have Prefabs' test when the test fails +- Corrected the message for a passing 'Shader compilation errors' test +- Improved the floating point precision accuracy of 'Reset Prefabs' test +- Fixed 'Missing Components in Assets' test checking all project folders instead of only the set path +- Fixed 'Prefabs for meshes' test not checking meshes in certain paths +- Fixed 'Reset Prefabs' test failing because of Prefabs with a Rect Transform Component +- Fixed 'Reset Prefabs' test ignoring Transform rotation +- Fixed test description text overlapping in some cases +- Other minor changes and tweaks + +## [11.0.2] - 2022-08-09 + +- Corrected some namespaces which were causing issues when deriving classes from Editor class + +## [11.0.1] - 2022-08-05 + +### Uploader Changes + +- Added Settings window (Asset Store Tools > Settings) +- Added Soft/Junction Symlink support (enable through Settings) +- Added workflow and path selection serialization (workflow saved locally, paths locally and online) +- No more logs when using the `-nullable` compiler option (thanks @alfish) +- Some API refactoring in preparation for CLI support +- Other minor fixes/improvements + +**Note:** when updating Asset Store Tools from the Package Manager, don't forget to remove the old version from the project (V11.0.0) before importing the new one (V11.0.1) + + +## [11.0.0] - 2022-07-20 + +### Uploader changes + +- UI has been reworked using UI Toolkit +- New login window, allowing to login using Unity Cloud Services +- Improved top bar, including search and sorting +- Draft packages moved to the top +- Added category, size, and last modified date next to the package +- Added a link to the publishing portal next to the package +- New uploading flow: “Pre-exported .unitypackage” +- Previous uploading flow (folder selection) has been renamed to “From Assets Folder” +- Dependencies check has been renamed to “Include Package Manifest” for clarity +- Special Folders can now be selected and uploaded together with the package’s main folder (i.e. StreamingAssets, Plugins) +- You can now upload to multiple packages at the same time without waiting for the first one to finish +- Package can now be validated in the Uploading window by pressing the “Validate” button +- Added refresh and logout buttons to the bottom toolbar for easier access +- Packages caching - package information will no longer be redownloaded every time you open the Uploader window during the same Editor session +- (Experimental) Custom exporter - will export your package ~2 times faster, but may miss some asset previews in the final product. To enable it - click three dots on the top left side of the window and enable “Use Custom Exporting” + + +### Validator changes + +- UI has been reworked using UI Toolkit +- New tests based on the new guidelines +- Updated tests’ titles, descriptions, and error reporting + +## [5.0.5] - 2021-11-04 + +- Fixed namespace issues + +## [5.0.4] - 2020-07-28 + +- Fixed issues with Unity 2020.1 + +## [5.0.3] - 2020-05-07 + +- Remove "Remove Standard Assets" check + +## [5.0.2] - 2020-04-21 + +- Enable auto login with Unity account +- Upload package with thread + +## [5.0.1] - 2020-03-23 + +- Fix domain resolve issue + +## [5.0.0] - 2019-10-09 + +- Added "Package Validator" tool +- Added Help window +- Added logout confirmation popup +- Updated toolbar menu layout +- Removed "Mass Labeler" tool +- Updated layout of Login and Package Upload windows +- Error messages are now more elaborate and user-friendly +- Removed deprecated "Main Assets" step from the Package Upload window +- Package Upload window now has a step for including package manager dependencies +- Tooltips are now added to each upload process step + + +## [4.1.0] - 2018-05-14 + +- Made Tool compatible with 2017.1 + +## [4.0.7] - 2017-07-10 + +- Tweaked menu items. + +## [4.0.6] - 2016-07-15 + +- Improved error messages. + +## [4.0.5] - 2016-03-17 + +- Enabling upload of fbm files. + +## [4.0.4] - 2015-11-16 + +- Login improvements + +## [4.0.3] - 2015-11-16 + +- Prepare the Tools for Unity 5.3 + +## [4.0.2] - 2015-10-23 + +- Fixed issue where Upload button would not work for some projects. +- Fixed issues for publishers that only had one package. + +## [4.0.0] - 2015-09-01 + +- Replaced Package Manager with Package Upload. Package management is now handled by Publisher Administration \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/CHANGELOG.md.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/CHANGELOG.md.meta new file mode 100644 index 000000000..0db1ee3e5 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/CHANGELOG.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 06607220dbd46414e8f66bf9c5e3eb79 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Assets/AssetStoreTools/Editor.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor.meta similarity index 61% rename from OneSignalExample/Assets/AssetStoreTools/Editor.meta rename to OneSignalExample/Packages/com.unity.asset-store-tools/Editor.meta index a2b3ce682..8521ad694 100644 --- a/OneSignalExample/Assets/AssetStoreTools/Editor.meta +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor.meta @@ -1,8 +1,6 @@ fileFormatVersion: 2 -guid: cf7638d43c18cec40b4b9312969b5378 +guid: 166da5c6fc70e814a8262463903b2714 folderAsset: yes -timeCreated: 1588842340 -licenseType: Store DefaultImporter: externalObjects: {} userData: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs new file mode 100644 index 000000000..96f0282fe --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("Unity.AssetStoreTools.Editor.Tests.asmdef")] +[assembly: InternalsVisibleTo("ab-builder")] +[assembly: InternalsVisibleTo("Inspector-Editor")] diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs.meta new file mode 100644 index 000000000..972ae52b3 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ccfd7faf75ab3c74a98015e772288d86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs new file mode 100644 index 000000000..fc8742dba --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs @@ -0,0 +1,60 @@ +using UnityEditor; +using UnityEngine; +using System; +using AssetStoreTools.Uploader; +using AssetStoreTools.Validator; +using AssetStoreTools.Utility; + +namespace AssetStoreTools +{ + internal class AssetStoreTools : EditorWindow + { + [MenuItem("Asset Store Tools/Asset Store Uploader", false, 0)] + public static void ShowAssetStoreToolsUploader() + { + Type inspectorType = Type.GetType("UnityEditor.InspectorWindow,UnityEditor.dll"); + var wnd = GetWindow(inspectorType); + wnd.Show(); + } + + + [MenuItem("Asset Store Tools/Asset Store Validator", false, 1)] + public static void ShowAssetStoreToolsValidator() + { + Type inspectorType = Type.GetType("UnityEditor.InspectorWindow,UnityEditor.dll"); + var wnd = GetWindow(typeof(AssetStoreUploader), inspectorType); + wnd.Show(); + } + + [MenuItem("Asset Store Tools/Publisher Portal", false, 20)] + public static void OpenPublisherPortal() + { + Application.OpenURL("https://publisher.unity.com/"); + } + + [MenuItem("Asset Store Tools/Submission Guidelines", false, 21)] + public static void OpenSubmissionGuidelines() + { + Application.OpenURL("https://assetstore.unity.com/publishing/submission-guidelines/"); + } + + [MenuItem("Asset Store Tools/Provide Feedback", false, 22)] + public static void OpenFeedback() + { + Application.OpenURL("https://forum.unity.com/threads/new-asset-store-tools-version-coming-july-20th-2022.1310939/"); + } + + [MenuItem("Asset Store Tools/Check for Updates", false, 45)] + public static void OpenUpdateChecker() + { + var wnd = GetWindowWithRect(new Rect(Screen.width / 2, Screen.height / 2, 400, 150), true); + wnd.Show(); + } + + [MenuItem("Asset Store Tools/Settings", false, 50)] + public static void OpenSettings() + { + ASToolsPreferencesProvider.OpenSettings(); + } + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs.meta new file mode 100644 index 000000000..9452bb059 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6060eef206afc844caaa1732538e8890 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs new file mode 100644 index 000000000..062a12581 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs @@ -0,0 +1,21 @@ +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools +{ + internal abstract class AssetStoreToolsWindow : EditorWindow + { + protected abstract string WindowTitle { get; } + + protected virtual void Init() + { + titleContent = new GUIContent(WindowTitle); + } + + private void OnEnable() + { + Init(); + } + + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs.meta new file mode 100644 index 000000000..2fe87e578 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c1057a05baaa45942808573065c02a03 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Assets/AssetStoreTools.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter.meta similarity index 61% rename from OneSignalExample/Assets/AssetStoreTools.meta rename to OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter.meta index 4dae05ea2..4e1143b83 100644 --- a/OneSignalExample/Assets/AssetStoreTools.meta +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter.meta @@ -1,8 +1,6 @@ fileFormatVersion: 2 -guid: 8466fccc5fcd4d74ea48c2a9411d446d +guid: 5f5ca981958937a43997a9f365759edf folderAsset: yes -timeCreated: 1588842340 -licenseType: Store DefaultImporter: externalObjects: {} userData: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs new file mode 100644 index 000000000..56c7d0eb2 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs @@ -0,0 +1,16 @@ +using AssetStoreTools.Utility; + +namespace AssetStoreTools.Exporter +{ + internal class ExportResult + { + public bool Success; + public string ExportedPath; + public ASError Error; + + public static implicit operator bool(ExportResult value) + { + return value != null && value.Success; + } + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs.meta new file mode 100644 index 000000000..a990a5098 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ce99a618d1e211444b53f18bb3444f75 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs new file mode 100644 index 000000000..74f7838e1 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs @@ -0,0 +1,18 @@ +namespace AssetStoreTools.Exporter +{ + public abstract class ExporterSettings + { + public string[] ExportPaths; + public string OutputFilename; + } + + public class DefaultExporterSettings : ExporterSettings + { + public string[] Dependencies; + } + + public class LegacyExporterSettings : ExporterSettings + { + public bool IncludeDependencies; + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs.meta new file mode 100644 index 000000000..24d41f792 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 399b115514c617d47a00b8c0a5e430fd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs new file mode 100644 index 000000000..42f0efb67 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs @@ -0,0 +1,137 @@ +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; + +namespace AssetStoreTools.Exporter +{ + internal abstract class PackageExporter + { + protected const string ProgressBarTitle = "Exporting Package"; + protected const string ProgressBarStepSavingAssets = "Saving Assets..."; + protected const string ProgressBarStepGatheringFiles = "Gathering files..."; + protected const string ProgressBarStepCompressingPackage = "Compressing package..."; + + private static readonly string[] PluginFolderExtensions = { "androidlib", "bundle", "plugin", "framework", "xcframework" }; + + public static async Task ExportPackage(ExporterSettings exportSettings) + { + if (!IsSettingsValid(exportSettings, out Exception e)) + return new ExportResult() { Success = false, Error = ASError.GetGenericError(e) }; + + switch (exportSettings) + { + case LegacyExporterSettings legacySettings: + return await PackageExporterLegacy.ExportPackage(legacySettings); + case DefaultExporterSettings defaultSettings: + return PackageExporterDefault.ExportPackage(defaultSettings); + default: + return new ExportResult() { Success = false, Error = ASError.GetGenericError(new ArgumentException("Unrecognized ExportSettings type was provided")) }; + } + } + + private static bool IsSettingsValid(ExporterSettings settings, out Exception e) + { + e = null; + + if (settings == null) + e = new ArgumentException("Package Exporting failed: ExporterSettings cannot be null"); + else if (settings.ExportPaths == null || settings.ExportPaths.Length == 0) + e = new ArgumentException("Package Exporting failed: received an invalid export paths array"); + else if (string.IsNullOrEmpty(settings.OutputFilename)) + e = new ArgumentException("Package Exporting failed: received an invalid output path"); + else if (settings.OutputFilename.EndsWith("/") || settings.OutputFilename.EndsWith("\\")) + e = new ArgumentException("Package Exporting failed: output path must be a valid filename and not end with a directory separator character"); + + return e == null; + } + + protected string[] GetAssetPaths(string rootPath) + { + // To-do: slight optimization is possible in the future by having a list of excluded folders/file extensions + List paths = new List(); + + // Add files within given directory + var filePaths = Directory.GetFiles(rootPath).Select(p => p.Replace('\\', '/')).ToArray(); + paths.AddRange(filePaths); + + // Add directories within given directory + var directoryPaths = Directory.GetDirectories(rootPath).Select(p => p.Replace('\\', '/')).ToArray(); + foreach (var nestedDirectory in directoryPaths) + paths.AddRange(GetAssetPaths(nestedDirectory)); + + // Add the given directory itself if it is not empty + if (filePaths.Length > 0 || directoryPaths.Length > 0) + paths.Add(rootPath); + + return paths.ToArray(); + } + + protected string GetAssetGuid(string assetPath, bool generateForPlugin, bool hiddenSearch) + { + // Skip meta files as they do not have guids + if (assetPath.EndsWith(".meta")) + return string.Empty; + + // Skip hidden assets. They normally do not have meta files, but + // have been observed to retain them in the past due to a Unity bug + if (assetPath.EndsWith("~")) + return string.Empty; + + // Skip ProjectVersion.txt file specifically as it may introduce + // project compatibility issues when imported + if (string.Compare(assetPath, "ProjectSettings/ProjectVersion.txt", StringComparison.OrdinalIgnoreCase) == 0) + return string.Empty; + + // Attempt retrieving guid from the Asset Database first + var guid = AssetDatabase.AssetPathToGUID(assetPath); + if (guid != string.Empty) + return guid; + + // Some special folders (e.g. SomeName.framework) do not have meta files inside them. + // Their contents should be exported with any arbitrary GUID so that Unity Importer could pick them up + if (generateForPlugin && PathBelongsToPlugin(assetPath)) + return GUID.Generate().ToString(); + + // Files in hidden folders (e.g. Samples~) are not part of the Asset Database, + // therefore GUIDs need to be scraped from the .meta file. + // Note: only do this for custom exporter since the native exporter + // will not be able to retrieve the asset path from a hidden folder + if (hiddenSearch) + { + // To-do: handle hidden folders without meta files + var metaPath = $"{assetPath}.meta"; + + if (!File.Exists(metaPath)) + return string.Empty; + + using (StreamReader reader = new StreamReader(metaPath)) + { + string line; + while ((line = reader.ReadLine()) != string.Empty) + { + if (!line.StartsWith("guid:")) + continue; + var metaGuid = line.Substring("guid:".Length).Trim(); + return metaGuid; + } + } + } + + return string.Empty; + } + + private bool PathBelongsToPlugin(string assetPath) + { + return PluginFolderExtensions.Any(extension => assetPath.ToLower().Contains($".{extension}/")); + } + + protected virtual void PostExportCleanup() + { + EditorUtility.ClearProgressBar(); + } + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs.meta new file mode 100644 index 000000000..66906b4dc --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 52ef11a59e545544fafaa99a5fa6cce9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs new file mode 100644 index 000000000..85d8d9c8b --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs @@ -0,0 +1,321 @@ +using AssetStoreTools.Uploader.Utility; +using AssetStoreTools.Utility; +using AssetStoreTools.Utility.Json; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Exporter +{ + internal class PackageExporterDefault : PackageExporter + { + private const string TemporaryExportPathName = "CustomExport"; + + private DefaultExporterSettings _exportSettings; + + private PackageExporterDefault(DefaultExporterSettings exportSettings) + { + _exportSettings = exportSettings; + } + + public static ExportResult ExportPackage(DefaultExporterSettings exportSettings) + { + var exporter = new PackageExporterDefault(exportSettings); + return exporter.ExportPackage(); + } + + private ExportResult ExportPackage() + { + ASDebug.Log("Using custom package exporter"); + + // Save assets before exporting + EditorUtility.DisplayProgressBar(ProgressBarTitle, ProgressBarStepSavingAssets, 0.1f); + AssetDatabase.SaveAssets(); + + try + { + // Create a temporary export path + var temporaryExportPath = GetTemporaryExportPath(); + if (!Directory.Exists(temporaryExportPath)) + Directory.CreateDirectory(temporaryExportPath); + + // Construct an unzipped package structure + CreateTempPackageStructure(temporaryExportPath); + + // Build a .unitypackage file from the temporary folder + CreateUnityPackage(temporaryExportPath, _exportSettings.OutputFilename); + + EditorUtility.RevealInFinder(_exportSettings.OutputFilename); + + ASDebug.Log($"Package file has been created at {_exportSettings.OutputFilename}"); + return new ExportResult() { Success = true, ExportedPath = _exportSettings.OutputFilename }; + } + catch (Exception e) + { + return new ExportResult() { Success = false, Error = ASError.GetGenericError(e) }; + } + finally + { + PostExportCleanup(); + } + } + + private string GetTemporaryExportPath() + { + return $"{AssetStoreCache.TempCachePath}/{TemporaryExportPathName}"; + } + + private void CreateTempPackageStructure(string tempOutputPath) + { + EditorUtility.DisplayProgressBar(ProgressBarTitle, ProgressBarStepGatheringFiles, 0.4f); + var pathGuidPairs = GetPathGuidPairs(_exportSettings.ExportPaths); + + // Caching asset previews takes time, so we'll start doing it as we + // iterate through assets and only retrieve them after generating the rest + // of the package structure + AssetPreview.SetPreviewTextureCacheSize(pathGuidPairs.Count + 100); + var pathObjectPairs = new Dictionary(); + + foreach (var pair in pathGuidPairs) + { + var originalAssetPath = pair.Key; + var outputAssetPath = $"{tempOutputPath}/{pair.Value}"; + + if (Directory.Exists(outputAssetPath)) + { + var path1 = File.ReadAllText($"{outputAssetPath}/pathname"); + var path2 = originalAssetPath; + throw new InvalidOperationException($"Multiple assets with guid {pair.Value} have been detected " + + $"when exporting the package. Please resolve the guid conflicts and try again:\n{path1}\n{path2}"); + } + + Directory.CreateDirectory(outputAssetPath); + + // Every exported asset has a pathname file + using (StreamWriter writer = new StreamWriter($"{outputAssetPath}/pathname")) + writer.Write(originalAssetPath); + + // Only files (not folders) have an asset file + if (File.Exists(originalAssetPath)) + File.Copy(originalAssetPath, $"{outputAssetPath}/asset"); + + // Most files and folders have an asset.meta file (but ProjectSettings folder assets do not) + if (File.Exists($"{originalAssetPath}.meta")) + File.Copy($"{originalAssetPath}.meta", $"{outputAssetPath}/asset.meta"); + + // To-do: handle previews in hidden folders as they are not part of the AssetDatabase + var previewObject = AssetDatabase.LoadAssetAtPath(originalAssetPath); + if (previewObject == null) + continue; + // Start caching the asset preview + AssetPreview.GetAssetPreview(previewObject); + pathObjectPairs.Add(outputAssetPath, previewObject); + } + + WritePreviewTextures(pathObjectPairs); + + if (_exportSettings.Dependencies == null || _exportSettings.Dependencies.Length == 0) + return; + + var exportDependenciesDict = JsonValue.NewDict(); + var allRegistryPackages = PackageUtility.GetAllRegistryPackages(); + + foreach(var exportDependency in _exportSettings.Dependencies) + { + var registryPackage = allRegistryPackages.FirstOrDefault(x => x.name == exportDependency); + if (registryPackage == null) + { + // Package is either not from a registry source or does not exist in the project + UnityEngine.Debug.LogWarning($"Found an unsupported Package Manager dependency: {exportDependency}.\n" + + "This dependency is not supported in the project's manifest.json and will be skipped."); + continue; + } + + exportDependenciesDict[registryPackage.name] = registryPackage.version; + } + + var exportManifestJson = JsonValue.NewDict(); + exportManifestJson["dependencies"] = exportDependenciesDict; + + var tempManifestDirectoryPath = $"{tempOutputPath}/packagemanagermanifest"; + Directory.CreateDirectory(tempManifestDirectoryPath); + var tempManifestFilePath = $"{tempManifestDirectoryPath}/asset"; + + File.WriteAllText(tempManifestFilePath, exportManifestJson.ToString()); + } + + private Dictionary GetPathGuidPairs(string[] exportPaths) + { + var pathGuidPairs = new Dictionary(); + + foreach (var exportPath in exportPaths) + { + var assetPaths = GetAssetPaths(exportPath); + + foreach (var assetPath in assetPaths) + { + var guid = GetAssetGuid(assetPath, true, true); + if (string.IsNullOrEmpty(guid)) + continue; + + pathGuidPairs.Add(assetPath, guid); + } + } + + return pathGuidPairs; + } + + private void WritePreviewTextures(Dictionary pathObjectPairs) + { + foreach (var kvp in pathObjectPairs) + { + var obj = kvp.Value; + var queuePreview = false; + + switch (obj) + { + case Material _: + case TerrainLayer _: + case AudioClip _: + case Mesh _: + case Texture _: + case UnityEngine.Tilemaps.Tile _: + case GameObject _: + queuePreview = true; + break; + } + + if (!queuePreview) + continue; + + AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long _); + var preview = GetAssetPreviewFromGuid(guid); + + if (!preview) + continue; + + var thumbnailWidth = Mathf.Min(preview.width, 128); + var thumbnailHeight = Mathf.Min(preview.height, 128); + var rt = RenderTexture.GetTemporary(thumbnailWidth, thumbnailHeight, 0, RenderTextureFormat.Default, RenderTextureReadWrite.sRGB); + + var copy = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false); + + RenderTexture.active = rt; + GL.Clear(true, true, new Color(0, 0, 0, 0)); + Graphics.Blit(preview, rt); + copy.ReadPixels(new Rect(0, 0, copy.width, copy.height), 0, 0, false); + copy.Apply(); + RenderTexture.active = null; + + var bytes = copy.EncodeToPNG(); + if (bytes != null && bytes.Length > 0) + { + File.WriteAllBytes(kvp.Key + "/preview.png", bytes); + } + + RenderTexture.ReleaseTemporary(rt); + } + } + + private Texture2D GetAssetPreviewFromGuid(string guid) + { + var method = typeof(AssetPreview).GetMethod("GetAssetPreviewFromGUID", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(string) }, null); + var args = new object[] { guid }; + + return method?.Invoke(null, args) as Texture2D; + } + + private void CreateUnityPackage(string pathToArchive, string outputPath) + { + if (Directory.GetDirectories(pathToArchive).Length == 0) + throw new InvalidOperationException("Unable to export package. The specified path is empty"); + + EditorUtility.DisplayProgressBar(ProgressBarTitle, ProgressBarStepCompressingPackage, 0.5f); + + // Archiving process working path will be set to the + // temporary package path so adjust the output path accordingly + if (!Path.IsPathRooted(outputPath)) + outputPath = $"{Application.dataPath.Substring(0, Application.dataPath.Length - "/Assets".Length)}/{outputPath}"; + +#if UNITY_EDITOR_WIN + CreateUnityPackageUniversal(pathToArchive, outputPath); +#elif UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX + CreateUnityPackageOsxLinux(pathToArchive, outputPath); +#endif + } + + private void CreateUnityPackageUniversal(string pathToArchive, string outputPath) + { + var _7zPath = EditorApplication.applicationContentsPath; +#if UNITY_EDITOR_WIN + _7zPath = Path.Combine(_7zPath, "Tools", "7z.exe"); +#elif UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX + _7zPath = Path.Combine(_7zPath, "Tools", "7za"); +#endif + if (!File.Exists(_7zPath)) + throw new FileNotFoundException("Archiving utility was not found in your Unity installation directory"); + + var argumentsTar = $"a -r -ttar -y -bd archtemp.tar ."; + var result = StartProcess(_7zPath, argumentsTar, pathToArchive); + if (result != 0) + throw new Exception("Failed to compress the package"); + + // Create a GZIP archive + var argumentsGzip = $"a -tgzip -bd -y \"{outputPath}\" archtemp.tar"; + result = StartProcess(_7zPath, argumentsGzip, pathToArchive); + if (result != 0) + throw new Exception("Failed to compress the package"); + } + + private void CreateUnityPackageOsxLinux(string pathToArchive, string outputPath) + { + var tarPath = "/usr/bin/tar"; + + if (!File.Exists(tarPath)) + { + // Fallback to the universal export method + ASDebug.LogWarning("'/usr/bin/tar' executable not found. Falling back to 7za"); + CreateUnityPackageUniversal(pathToArchive, outputPath); + return; + } + + // Create a TAR archive + var arguments = $"-czpf \"{outputPath}\" ."; + var result = StartProcess(tarPath, arguments, pathToArchive); + if (result != 0) + throw new Exception("Failed to compress the package"); + } + + private int StartProcess(string processPath, string arguments, string workingDirectory) + { + var info = new ProcessStartInfo() + { + FileName = processPath, + Arguments = arguments, + WorkingDirectory = workingDirectory, + CreateNoWindow = true, + UseShellExecute = false + }; + + using (Process process = Process.Start(info)) + { + process.WaitForExit(); + return process.ExitCode; + } + } + + protected override void PostExportCleanup() + { + base.PostExportCleanup(); + + var tempExportPath = GetTemporaryExportPath(); + if (Directory.Exists(tempExportPath)) + Directory.Delete(tempExportPath, true); + } + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs.meta new file mode 100644 index 000000000..fa1bd636a --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 32f50122a1b2bc2428cf8fba321e2414 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs new file mode 100644 index 000000000..3a665d79d --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs @@ -0,0 +1,102 @@ +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Exporter +{ + internal class PackageExporterLegacy : PackageExporter + { + private const string ExportMethodWithoutDependencies = "UnityEditor.PackageUtility.ExportPackage"; + private const string ExportMethodWithDependencies = "UnityEditor.PackageUtility.ExportPackageAndPackageManagerManifest"; + + private LegacyExporterSettings _exportSettings; + + private PackageExporterLegacy(LegacyExporterSettings exportSettings) + { + _exportSettings = exportSettings; + } + + public static async Task ExportPackage(LegacyExporterSettings exportSettings) + { + var exporter = new PackageExporterLegacy(exportSettings); + return await exporter.ExportPackage(); + } + + private async Task ExportPackage() + { + ASDebug.Log("Using native package exporter"); + + try + { + var guids = GetGuids(_exportSettings.ExportPaths, out bool onlyFolders); + + if (guids.Length == 0 || onlyFolders) + throw new ArgumentException("Package Exporting failed: provided export paths are empty or only contain empty folders"); + + string exportMethod = ExportMethodWithoutDependencies; + if (_exportSettings.IncludeDependencies) + exportMethod = ExportMethodWithDependencies; + + var split = exportMethod.Split('.'); + var assembly = Assembly.Load(split[0]); // UnityEditor + var typeName = $"{split[0]}.{split[1]}"; // UnityEditor.PackageUtility + var methodName = split[2]; // ExportPackage or ExportPackageAndPackageManagerManifest + + var type = assembly.GetType(typeName); + var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, + null, new Type[] { typeof(string[]), typeof(string) }, null); + + ASDebug.Log("Invoking native export method"); + + method?.Invoke(null, new object[] { guids, _exportSettings.OutputFilename }); + + // The internal exporter methods are asynchronous, therefore + // we need to wait for exporting to finish before returning + await Task.Run(() => + { + while (!File.Exists(_exportSettings.OutputFilename)) + Thread.Sleep(100); + }); + + ASDebug.Log($"Package file has been created at {_exportSettings.OutputFilename}"); + return new ExportResult() { Success = true, ExportedPath = _exportSettings.OutputFilename }; + } + catch (Exception e) + { + return new ExportResult() { Success = false, Error = ASError.GetGenericError(e) }; + } + finally + { + PostExportCleanup(); + } + } + + private string[] GetGuids(string[] exportPaths, out bool onlyFolders) + { + var guids = new List(); + onlyFolders = true; + + foreach (var exportPath in exportPaths) + { + var assetPaths = GetAssetPaths(exportPath); + + foreach (var assetPath in assetPaths) + { + var guid = GetAssetGuid(assetPath, false, false); + if (string.IsNullOrEmpty(guid)) + continue; + + guids.Add(guid); + if (onlyFolders == true && (File.Exists(assetPath))) + onlyFolders = false; + } + } + + return guids.ToArray(); + } + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs.meta new file mode 100644 index 000000000..853e472fe --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3200380dff2de104aa79620e4b41dc70 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef new file mode 100644 index 000000000..a222ddb71 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef @@ -0,0 +1,15 @@ +{ + "name": "asset-store-tools-editor", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef.meta new file mode 100644 index 000000000..2f67bb9e0 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c183be512f4485d40a3437fabd6c81cf +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader.meta new file mode 100644 index 000000000..b4b866142 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9722d52df16aab742b26fe301782c74c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/AssetStoreUploader.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/AssetStoreUploader.cs new file mode 100644 index 000000000..4ff2bd1f5 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/AssetStoreUploader.cs @@ -0,0 +1,227 @@ +using AssetStoreTools.Utility.Json; +using System; +using System.Collections.Generic; +using AssetStoreTools.Utility; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.UIElements; +using AssetStoreTools.Uploader.Utility; +using AssetStoreTools.Uploader.UIElements; + +namespace AssetStoreTools.Uploader +{ + internal class AssetStoreUploader : AssetStoreToolsWindow + { + public const string MinRequiredPackageVersion = "2021.3"; + public const long MaxPackageSizeBytes = 6442450944; // 6 GB + + private const string MainWindowVisualTree = "Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/Base/BaseWindow_Main"; + private const string DebugPhrase = "debug"; + + // UI Windows + private LoginWindow _loginWindow; + private UploadWindow _uploadWindow; + + private readonly List _debugBuffer = new List(); + + public static bool ShowPackageVersionDialog + { + get => string.Compare(Application.unityVersion, MinRequiredPackageVersion, StringComparison.Ordinal) == -1 && ASToolsPreferences.Instance.UploadVersionCheck; + set { ASToolsPreferences.Instance.UploadVersionCheck = value; ASToolsPreferences.Instance.Save(); } + } + + protected override string WindowTitle => "Asset Store Uploader"; + + protected override void Init() + { + if (_loginWindow != null && _uploadWindow != null) + return; + + minSize = new Vector2(400, 430); + this.SetAntiAliasing(4); + + base.Init(); + + VisualElement root = rootVisualElement; + root.AddToClassList("root"); + + // Getting a reference to the UXML Document and adding to the root + var visualTree = AssetDatabase.LoadAssetAtPath($"{MainWindowVisualTree}.uxml"); + VisualElement uxmlRoot = visualTree.CloneTree(); + uxmlRoot.style.flexGrow = 1; + root.Add(uxmlRoot); + + root.styleSheets.Add(StyleSelector.UploaderWindow.BaseWindowStyle); + root.styleSheets.Add(StyleSelector.UploaderWindow.BaseWindowTheme); + + + // Find necessary windows / views and sets up appropriate functionality + SetupCoreElements(); + + if (!AssetStoreAPI.IsUploading) + { + // Should only authenticate if the session is available. Other authentications are only available + // in the login window. See "SetupLoginElements". + HideElement(_uploadWindow); + Authenticate(); + } + else + { + ShowUploadWindow(); + } + } + + private void OnGUI() + { + CheckForDebugMode(); + } + + private void OnDestroy() + { + if (AssetStoreAPI.IsUploading) + EditorUtility.DisplayDialog("Notice", "Assets are still being uploaded to the Asset Store. " + + "If you wish to check on the progress, please re-open the Asset Store Uploader window", "OK"); + } + + private void SetupCoreElements() + { + _loginWindow = rootVisualElement.Q("LoginWindow"); + _uploadWindow = rootVisualElement.Q("UploadWindow"); + + _loginWindow.SetupLoginElements(OnLoginSuccess, OnLoginFail); + _uploadWindow.SetupWindows(OnLogout, OnPackageDownloadFail); + } + + #region Login Interface + + private async void Authenticate() + { + ShowLoginWindow(); + + // 1 - Check if there's an active session + // 2 - Check if there's a saved session + // 3 - Attempt to login via Cloud session token + // 4 - Prompt manual login + EnableLoginWindow(false); + var result = await AssetStoreAPI.LoginWithSessionAsync(); + if (result.Success) + OnLoginSuccess(result.Response); + else if (result.SilentFail) + OnLoginFailSession(); + else + OnLoginFail(result.Error); + } + + private void OnLoginFail(ASError error) + { + Debug.LogError(error.Message); + + _loginWindow.EnableErrorBox(true, error.Message); + EnableLoginWindow(true); + } + + private void OnLoginFailSession() + { + // All previous login methods are unavailable + EnableLoginWindow(true); + } + + private void OnLoginSuccess(JsonValue json) + { + ASDebug.Log($"Login json\n{json}"); + + if (!AssetStoreAPI.IsPublisherValid(json, out var error)) + { + EnableLoginWindow(true); + _loginWindow.EnableErrorBox(true, error.Message); + ASDebug.Log($"Publisher {json["name"]} is invalid."); + return; + } + + ASDebug.Log($"Publisher {json["name"]} is valid."); + AssetStoreAPI.SavedSessionId = json["xunitysession"].AsString(); + AssetStoreAPI.LastLoggedInUser = json["username"].AsString(); + + ShowUploadWindow(); + } + + private void OnPackageDownloadFail(ASError error) + { + _loginWindow.EnableErrorBox(true, error.Message); + EnableLoginWindow(true); + ShowLoginWindow(); + } + + private void OnLogout() + { + AssetStoreAPI.SavedSessionId = String.Empty; + AssetStoreCache.ClearTempCache(); + + _loginWindow.ClearLoginBoxes(); + ShowLoginWindow(); + EnableLoginWindow(true); + } + + #endregion + + #region UI Window Utils + private void ShowLoginWindow() + { + HideElement(_uploadWindow); + ShowElement(_loginWindow); + } + + private void ShowUploadWindow() + { + HideElement(_loginWindow); + ShowElement(_uploadWindow); + + _uploadWindow.ShowAllPackagesView(); + _uploadWindow.ShowPublisherEmail(AssetStoreAPI.LastLoggedInUser); + _uploadWindow.LoadPackages(true, OnPackageDownloadFail); + } + + private void ShowElement(params VisualElement[] elements) + { + foreach(var e in elements) + e.style.display = DisplayStyle.Flex; + } + + private void HideElement(params VisualElement[] elements) + { + foreach(var e in elements) + e.style.display = DisplayStyle.None; + } + + private void EnableLoginWindow(bool enable) + { + _loginWindow.SetEnabled(enable); + } + + #endregion + + #region Debug Utility + + private void CheckForDebugMode() + { + Event e = Event.current; + + if (e.type != EventType.KeyDown || e.keyCode == KeyCode.None) + return; + + _debugBuffer.Add(e.keyCode.ToString().ToLower()[0]); + if (_debugBuffer.Count > DebugPhrase.Length) + _debugBuffer.RemoveAt(0); + + if (string.Join(string.Empty, _debugBuffer.ToArray()) != DebugPhrase) + return; + + ASDebug.DebugModeEnabled = !ASDebug.DebugModeEnabled; + ASDebug.Log($"DEBUG MODE ENABLED: {ASDebug.DebugModeEnabled}"); + _debugBuffer.Clear(); + } + + #endregion + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/AssetStoreUploader.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/AssetStoreUploader.cs.meta new file mode 100644 index 000000000..ef78266e2 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/AssetStoreUploader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b5319699cc84194a9a768ad33b86c21 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons.meta new file mode 100644 index 000000000..7026063d3 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ab9d0e254817f4f4589a6a378d77babc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png new file mode 100644 index 000000000..245875b4d Binary files /dev/null and b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png differ diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png.meta new file mode 100644 index 000000000..26ccaa5a2 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png.meta @@ -0,0 +1,147 @@ +fileFormatVersion: 2 +guid: e7df43612bbf44d4692de879c751902a +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png new file mode 100644 index 000000000..621e906e3 Binary files /dev/null and b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png differ diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png.meta new file mode 100644 index 000000000..305aa317d --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png.meta @@ -0,0 +1,128 @@ +fileFormatVersion: 2 +guid: 8e0749dce5b14cc46b73b0303375c162 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 0 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png new file mode 100644 index 000000000..70f470378 Binary files /dev/null and b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png differ diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png.meta new file mode 100644 index 000000000..a0f13697c --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png.meta @@ -0,0 +1,128 @@ +fileFormatVersion: 2 +guid: 003e2710f9b29d94c87632022a3c7c48 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 18 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 2 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 2 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 2 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 2 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts.meta new file mode 100644 index 000000000..63c6efc3d --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 15b24ad8f9d236249910fb8eef1e30ea +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs new file mode 100644 index 000000000..f6f6996d2 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs @@ -0,0 +1,796 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Uploader.Utility; +using AssetStoreTools.Utility; +using AssetStoreTools.Utility.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Uploader +{ + /// + /// A class for retrieving data from the Asset Store backend + /// Note: most data retrieval methods require to be set + /// + internal static class AssetStoreAPI + { + public const string ToolVersion = "V11.4.3"; + + private const string UnauthSessionId = "26c4202eb475d02864b40827dfff11a14657aa41"; + private const string KharmaSessionId = "kharma.sessionid"; + private const int UploadResponseTimeoutMs = 10000; + + public static string AssetStoreProdUrl = "https://kharma.unity3d.com"; + private static string s_sessionId = EditorPrefs.GetString(KharmaSessionId); + private static HttpClient httpClient = new HttpClient(); + private static CancellationTokenSource s_downloadCancellationSource; + + public static string SavedSessionId + { + get => s_sessionId; + set + { + s_sessionId = value; + EditorPrefs.SetString(KharmaSessionId, value); + httpClient.DefaultRequestHeaders.Clear(); + if (!string.IsNullOrEmpty(value)) + httpClient.DefaultRequestHeaders.Add("X-Unity-Session", SavedSessionId); + } + } + + public static bool IsCloudUserAvailable => CloudProjectSettings.userName != "anonymous"; + public static string LastLoggedInUser = ""; + public static ConcurrentDictionary ActiveUploads = new ConcurrentDictionary(); + public static bool IsUploading => (ActiveUploads.Count > 0); + + static AssetStoreAPI() + { + ServicePointManager.DefaultConnectionLimit = 500; + httpClient.DefaultRequestHeaders.ConnectionClose = false; + httpClient.Timeout = TimeSpan.FromMinutes(1320); + } + + /// + /// A structure used to return the success outcome and the result of Asset Store API calls + /// + internal class APIResult + { + public JsonValue Response; + public bool Success; + public bool SilentFail; + public ASError Error; + + public static implicit operator bool(APIResult value) + { + return value != null && value.Success != false; + } + } + + #region Login API + + /// + /// A login API call that uses the email and password credentials + /// + /// + /// Note: this method only returns a response from the server and does not set the itself + /// + public static async Task LoginWithCredentialsAsync(string email, string password) + { + FormUrlEncodedContent data = GetLoginContent(new Dictionary { { "user", email }, { "pass", password } }); + return await LoginAsync(data); + } + + /// + /// A login API call that uses the + /// + /// + /// Note: this method only returns a response from the server and does not set the itself + /// + public static async Task LoginWithSessionAsync() + { + if (string.IsNullOrEmpty(SavedSessionId)) + return new APIResult() { Success = false, SilentFail = true, Error = ASError.GetGenericError(new Exception("No active session available")) }; + + FormUrlEncodedContent data = GetLoginContent(new Dictionary { { "reuse_session", SavedSessionId }, { "xunitysession", UnauthSessionId } }); + return await LoginAsync(data); + } + + /// + /// A login API call that uses the + /// + /// + /// Note: this method only returns a response from the server and does not set the itself + /// + /// Cloud access token. Can be retrieved by calling + public static async Task LoginWithTokenAsync(string token) + { + FormUrlEncodedContent data = GetLoginContent(new Dictionary { { "user_access_token", token } }); + return await LoginAsync(data); + } + + private static async Task LoginAsync(FormUrlEncodedContent data) + { + OverrideAssetStoreUrl(); + Uri uri = new Uri($"{AssetStoreProdUrl}/login"); + + httpClient.DefaultRequestHeaders.Clear(); + httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); + + try + { + var response = await httpClient.PostAsync(uri, data); + return UploadValuesCompletedLogin(response); + } + catch (Exception e) + { + return new APIResult() { Success = false, Error = ASError.GetGenericError(e) }; + } + } + + private static APIResult UploadValuesCompletedLogin(HttpResponseMessage response) + { + ASDebug.Log($"Upload Values Complete {response.ReasonPhrase}"); + ASDebug.Log($"Login success? {response.IsSuccessStatusCode}"); + try + { + response.EnsureSuccessStatusCode(); + var responseResult = response.Content.ReadAsStringAsync().Result; + var success = JSONParser.AssetStoreResponseParse(responseResult, out ASError error, out JsonValue jsonResult); + if (success) + return new APIResult() { Success = true, Response = jsonResult }; + else + return new APIResult() { Success = false, Error = error }; + } + catch (HttpRequestException ex) + { + return new APIResult() { Success = false, Error = ASError.GetLoginError(response, ex) }; + } + } + + #endregion + + #region Package Metadata API + + private static async Task GetPackageDataMain() + { + return await GetAssetStoreData(APIUri("asset-store-tools", "metadata/0", SavedSessionId)); + } + + private static async Task GetPackageDataExtra() + { + return await GetAssetStoreData(APIUri("management", "packages", SavedSessionId)); + } + + private static async Task GetCategories(bool useCached) + { + if (useCached) + { + if (AssetStoreCache.GetCachedCategories(out JsonValue cachedCategoryJson)) + return cachedCategoryJson; + + ASDebug.LogWarning("Failed to retrieve cached category data. Proceeding to download"); + } + var categoryJson = await GetAssetStoreData(APIUri("management", "categories", SavedSessionId)); + AssetStoreCache.CacheCategories(categoryJson); + + return categoryJson; + } + + /// + /// Retrieve data for all packages associated with the currently logged in account (identified by ) + /// + /// + /// + public static async Task GetFullPackageDataAsync(bool useCached) + { + if (useCached) + { + if (AssetStoreCache.GetCachedPackageMetadata(out JsonValue cachedData)) + return new APIResult() { Success = true, Response = cachedData }; + + ASDebug.LogWarning("Failed to retrieve cached package metadata. Proceeding to download"); + } + + try + { + var jsonMainData = await GetPackageDataMain(); + var jsonExtraData = await GetPackageDataExtra(); + var jsonCategoryData = await GetCategories(useCached); + + var joinedData = MergePackageData(jsonMainData, jsonExtraData, jsonCategoryData); + AssetStoreCache.CachePackageMetadata(joinedData); + + return new APIResult() { Success = true, Response = joinedData }; + } + catch (OperationCanceledException e) + { + ASDebug.Log("Package metadata download operation cancelled"); + DisposeDownloadCancellation(); + return new APIResult() { Success = false, SilentFail = true, Error = ASError.GetGenericError(e) }; + } + catch (Exception e) + { + return new APIResult() { Success = false, Error = ASError.GetGenericError(e) }; + } + } + + /// + /// Retrieve the thumbnail textures for all packages within the provided json structure and perform a given action after each retrieval + /// + /// A json file retrieved from + /// Return cached thumbnails if they are found + /// + /// Action to perform upon a successful thumbnail retrieval + /// - Package Id
+ /// - Associated Thumbnail + /// + /// + /// Action to perform upon a failed thumbnail retrieval + /// - Package Id
+ /// - Associated error + /// + public static async void GetPackageThumbnails(JsonValue packageJson, bool useCached, Action onSuccess, Action onFail) + { + SetupDownloadCancellation(); + var packageDict = packageJson["packages"].AsDict(); + var packageEnum = packageDict.GetEnumerator(); + + for (int i = 0; i < packageDict.Count; i++) + { + packageEnum.MoveNext(); + var package = packageEnum.Current; + + try + { + s_downloadCancellationSource.Token.ThrowIfCancellationRequested(); + + if (package.Value["icon_url"] + .IsNull()) // If no URL is found in the package metadata, use the default image + { + Texture2D fallbackTexture = null; + ASDebug.Log($"Package {package.Key} has no thumbnail. Returning default image"); + onSuccess?.Invoke(package.Key, fallbackTexture); + continue; + } + + if (useCached && + AssetStoreCache.GetCachedTexture(package.Key, + out Texture2D texture)) // Try returning cached thumbnails first + { + ASDebug.Log($"Returning cached thumbnail for package {package.Key}"); + onSuccess?.Invoke(package.Key, texture); + continue; + } + + var textureBytes = + await DownloadPackageThumbnail(package.Value["icon_url"].AsString()); + Texture2D tex = new Texture2D(1, 1, TextureFormat.RGBA32, false); + tex.LoadImage(textureBytes); + AssetStoreCache.CacheTexture(package.Key, tex); + ASDebug.Log($"Returning downloaded thumbnail for package {package.Key}"); + onSuccess?.Invoke(package.Key, tex); + } + catch (OperationCanceledException) + { + DisposeDownloadCancellation(); + ASDebug.Log("Package thumbnail download operation cancelled"); + return; + } + catch (Exception e) + { + onFail?.Invoke(package.Key, ASError.GetGenericError(e)); + } + finally + { + packageEnum.Dispose(); + } + } + } + + private static async Task DownloadPackageThumbnail(string url) + { + // icon_url is presented without http/https + Uri uri = new Uri($"https:{url}"); + + var textureBytes = await httpClient.GetAsync(uri, s_downloadCancellationSource.Token). + ContinueWith((response) => response.Result.Content.ReadAsByteArrayAsync().Result, s_downloadCancellationSource.Token); + s_downloadCancellationSource.Token.ThrowIfCancellationRequested(); + return textureBytes; + } + + /// + /// Retrieve, update the cache and return the updated data for a previously cached package + /// + public static async Task GetRefreshedPackageData(string packageId) + { + try + { + var refreshedDataJson = await GetPackageDataExtra(); + var refreshedPackage = default(JsonValue); + + // Find the updated package data in the latest data json + foreach (var p in refreshedDataJson["packages"].AsList()) + { + if (p["id"] == packageId) + { + refreshedPackage = p["versions"].AsList()[p["versions"].AsList().Count - 1]; + break; + } + } + + if (refreshedPackage.Equals(default(JsonValue))) + return new APIResult() { Success = false, Error = ASError.GetGenericError(new MissingMemberException($"Unable to find downloaded package data for package id {packageId}")) }; + + // Check if the supplied package id data has been cached and if it contains the corresponding package + if (!AssetStoreCache.GetCachedPackageMetadata(out JsonValue cachedData) || + !cachedData["packages"].AsDict().ContainsKey(packageId)) + return new APIResult() { Success = false, Error = ASError.GetGenericError(new MissingMemberException($"Unable to find cached package id {packageId}")) }; + + var cachedPackage = cachedData["packages"].AsDict()[packageId]; + + // Retrieve the category map + var categoryJson = await GetCategories(true); + var categories = CreateCategoryDictionary(categoryJson); + + // Update the package data + cachedPackage["name"] = refreshedPackage["name"].AsString(); + cachedPackage["status"] = refreshedPackage["status"].AsString(); + cachedPackage["extra_info"].AsDict()["category_info"].AsDict()["id"] = refreshedPackage["category_id"].AsString(); + cachedPackage["extra_info"].AsDict()["category_info"].AsDict()["name"] = + categories.ContainsKey(refreshedPackage["category_id"]) ? categories[refreshedPackage["category_id"].AsString()] : "Unknown"; + cachedPackage["extra_info"].AsDict()["modified"] = refreshedPackage["modified"].AsString(); + cachedPackage["extra_info"].AsDict()["size"] = refreshedPackage["size"].AsString(); + + AssetStoreCache.CachePackageMetadata(cachedData); + return new APIResult() { Success = true, Response = cachedPackage }; + } + catch (OperationCanceledException) + { + ASDebug.Log("Package metadata download operation cancelled"); + DisposeDownloadCancellation(); + return new APIResult() { Success = false, SilentFail = true }; + } + catch (Exception e) + { + return new APIResult() { Success = false, Error = ASError.GetGenericError(e) }; + } + } + + /// + /// Retrieve all Unity versions that the given package has already had uploaded content with + /// + /// + /// + /// + public static List GetPackageUploadedVersions(string packageId, string versionId) + { + var versions = new List(); + try + { + // Retrieve the data for already uploaded versions (should prevent interaction with Uploader) + var versionsTask = Task.Run(() => GetAssetStoreData(APIUri("content", $"preview/{packageId}/{versionId}", SavedSessionId))); + if (!versionsTask.Wait(5000)) + throw new TimeoutException("Could not retrieve uploaded versions within a reasonable time interval"); + + var versionsJson = versionsTask.Result; + foreach (var version in versionsJson["content"].AsDict()["unity_versions"].AsList()) + versions.Add(version.AsString()); + } + catch (OperationCanceledException) + { + ASDebug.Log("Package version download operation cancelled"); + DisposeDownloadCancellation(); + } + catch (Exception e) + { + ASDebug.LogError(e); + } + + return versions; + } + + #endregion + + #region Package Upload API + + /// + /// Upload a content file (.unitypackage) to a provided package version + /// + /// + /// Name of the package. Only used for identifying the package in class + /// Path to the .unitypackage file + /// The value of the main content folder for the provided package + /// The local path (relative to the root project folder) of the main content folder for the provided package + /// The path to the project that this package was built from + /// + public static async Task UploadPackageAsync(string versionId, string packageName, string filePath, + string localPackageGuid, string localPackagePath, string localProjectPath) + { + try + { + ASDebug.Log("Upload task starting"); + EditorApplication.LockReloadAssemblies(); + + if (!IsUploading) // Only subscribe before the first upload + EditorApplication.playModeStateChanged += EditorPlayModeStateChangeHandler; + + var progressData = new OngoingUpload(versionId, packageName); + ActiveUploads.TryAdd(versionId, progressData); + + var result = await Task.Run(() => UploadPackageTask(progressData, filePath, localPackageGuid, localPackagePath, localProjectPath)); + + ActiveUploads.TryRemove(versionId, out OngoingUpload _); + + ASDebug.Log("Upload task finished"); + return result; + } + catch (Exception e) + { + ASDebug.LogError("Upload task failed with an exception: " + e); + ActiveUploads.TryRemove(versionId, out OngoingUpload _); + return PackageUploadResult.PackageUploadFail(ASError.GetGenericError(e)); + } + finally + { + if (!IsUploading) // Only unsubscribe after the last upload + EditorApplication.playModeStateChanged -= EditorPlayModeStateChangeHandler; + + EditorApplication.UnlockReloadAssemblies(); + } + } + + private static PackageUploadResult UploadPackageTask(OngoingUpload currentUpload, string filePath, + string localPackageGuid, string localPackagePath, string localProjectPath) + { + ASDebug.Log("Preparing to upload package within API"); + string api = "asset-store-tools"; + string uri = $"package/{currentUpload.VersionId}/unitypackage"; + + Dictionary packageParams = new Dictionary + { + // Note: project_path is currently used to store UI selections + {"root_guid", localPackageGuid}, + {"root_path", localPackagePath}, + {"project_path", localProjectPath} + }; + + ASDebug.Log($"Creating upload request for {currentUpload.VersionId} {currentUpload.PackageName}"); + + FileStream requestFileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + + bool responseTimedOut = false; + long chunkSize = 32768; + try + { + ASDebug.Log("Starting upload process..."); + + var content = new StreamContent(requestFileStream, (int)chunkSize); + var response = httpClient.PutAsync(APIUri(api, uri, SavedSessionId, packageParams), content, currentUpload.CancellationToken); + + // Progress tracking + int updateIntervalMs = 100; + bool allBytesSent = false; + DateTime timeOfCompletion = default(DateTime); + + while (!response.IsCompleted) + { + float uploadProgress = (float)requestFileStream.Position / requestFileStream.Length * 100; + currentUpload.UpdateProgress(uploadProgress); + Thread.Sleep(updateIntervalMs); + + // A timeout for rare cases, when package uploading reaches 100%, but PutAsync task IsComplete value remains 'False' + if (requestFileStream.Position == requestFileStream.Length) + { + if (!allBytesSent) + { + allBytesSent = true; + timeOfCompletion = DateTime.UtcNow; + } + else if (DateTime.UtcNow.Subtract(timeOfCompletion).TotalMilliseconds > UploadResponseTimeoutMs) + { + responseTimedOut = true; + currentUpload.Cancel(); + break; + } + } + } + + // 2020.3 - although cancellation token shows a requested cancellation, the HttpClient + // tends to return a false 'IsCanceled' value, thus yielding an exception when attempting to read the response. + // For now we'll just check the token as well, but this needs to be investigated later on. + if (response.IsCanceled || currentUpload.CancellationToken.IsCancellationRequested) + currentUpload.CancellationToken.ThrowIfCancellationRequested(); + + var responseString = response.Result.Content.ReadAsStringAsync().Result; + + var success = JSONParser.AssetStoreResponseParse(responseString, out ASError error, out JsonValue json); + ASDebug.Log("Upload response JSON: " + json.ToString()); + if (success) + return PackageUploadResult.PackageUploadSuccess(); + else + return PackageUploadResult.PackageUploadFail(error); + } + catch (OperationCanceledException) + { + // Uploading is canceled + if (!responseTimedOut) + { + ASDebug.Log("Upload operation cancelled"); + return PackageUploadResult.PackageUploadCancelled(); + } + else + { + ASDebug.LogWarning("All data has been uploaded, but waiting for the response timed out"); + return PackageUploadResult.PackageUploadResponseTimeout(); + } + } + catch (Exception e) + { + ASDebug.LogError("Upload operation encountered an undefined exception: " + e); + var fullError = e.InnerException != null ? ASError.GetGenericError(e.InnerException) : ASError.GetGenericError(e); + return PackageUploadResult.PackageUploadFail(fullError); + } + finally + { + requestFileStream.Dispose(); + currentUpload.Dispose(); + } + } + + /// + /// Cancel the uploading task for a package with the provided package id + /// + public static void AbortPackageUpload(string packageId) + { + ActiveUploads[packageId]?.Cancel(); + } + + #endregion + + #region Utility Methods + + public static async Task GetLatestAssetStoreToolsVersion() + { + try + { + var url = "https://api.assetstore.unity3d.com/package/latest-version/115"; + var result = await httpClient.GetAsync(url); + + result.EnsureSuccessStatusCode(); + + var resultStr = await result.Content.ReadAsStringAsync(); + + var json = JSONParser.SimpleParse(resultStr); + + return new APIResult() { Success = true, Response = json }; + } + catch (Exception e) + { + return new APIResult() { Success = false, Error = ASError.GetGenericError(e) }; + } + } + + private static string GetLicenseHash() + { + return UnityEditorInternal.InternalEditorUtility.GetAuthToken().Substring(0, 40); + } + + private static string GetHardwareHash() + { + return UnityEditorInternal.InternalEditorUtility.GetAuthToken().Substring(40, 40); + } + + private static FormUrlEncodedContent GetLoginContent(Dictionary loginData) + { + loginData.Add("unityversion", Application.unityVersion); + loginData.Add("toolversion", ToolVersion); + loginData.Add("license_hash", GetLicenseHash()); + loginData.Add("hardware_hash", GetHardwareHash()); + + return new FormUrlEncodedContent(loginData); + } + + private static async Task GetAssetStoreData(Uri uri) + { + SetupDownloadCancellation(); + + var response = await httpClient.GetAsync(uri, s_downloadCancellationSource.Token) + .ContinueWith((x) => x.Result.Content.ReadAsStringAsync().Result, s_downloadCancellationSource.Token); + s_downloadCancellationSource.Token.ThrowIfCancellationRequested(); + + if (!JSONParser.AssetStoreResponseParse(response, out var error, out var jsonMainData)) + throw error.Exception; + + return jsonMainData; + } + + private static Uri APIUri(string apiPath, string endPointPath, string sessionId) + { + return APIUri(apiPath, endPointPath, sessionId, null); + } + + // Method borrowed from A$ tools, could maybe be simplified to only retain what is necessary? + private static Uri APIUri(string apiPath, string endPointPath, string sessionId, IDictionary extraQuery) + { + Dictionary extraQueryMerged; + + if (extraQuery == null) + extraQueryMerged = new Dictionary(); + else + extraQueryMerged = new Dictionary(extraQuery); + + extraQueryMerged.Add("unityversion", Application.unityVersion); + extraQueryMerged.Add("toolversion", ToolVersion); + extraQueryMerged.Add("xunitysession", sessionId); + + string uriPath = $"{AssetStoreProdUrl}/api/{apiPath}/{endPointPath}.json"; + UriBuilder uriBuilder = new UriBuilder(uriPath); + + StringBuilder queryToAppend = new StringBuilder(); + foreach (KeyValuePair queryPair in extraQueryMerged) + { + string queryName = queryPair.Key; + string queryValue = Uri.EscapeDataString(queryPair.Value); + + queryToAppend.AppendFormat("&{0}={1}", queryName, queryValue); + } + if (!string.IsNullOrEmpty(uriBuilder.Query)) + uriBuilder.Query = uriBuilder.Query.Substring(1) + queryToAppend; + else + uriBuilder.Query = queryToAppend.Remove(0, 1).ToString(); + + return uriBuilder.Uri; + } + + private static JsonValue MergePackageData(JsonValue mainPackageData, JsonValue extraPackageData, JsonValue categoryData) + { + ASDebug.Log($"Main package data\n{mainPackageData}"); + var mainDataDict = mainPackageData["packages"].AsDict(); + + // Most likely both of them will be true at the same time, but better to be safe + if (mainDataDict.Count == 0 || !extraPackageData.ContainsKey("packages")) + return new JsonValue(); + + ASDebug.Log($"Extra package data\n{extraPackageData}"); + var extraDataDict = extraPackageData["packages"].AsList(); + + var categories = CreateCategoryDictionary(categoryData); + + foreach (var md in mainDataDict) + { + foreach (var ed in extraDataDict) + { + if (ed["id"].AsString() != md.Key) + continue; + + // Create a field for extra data + var extraData = JsonValue.NewDict(); + + // Add category field + var categoryEntry = JsonValue.NewDict(); + + var categoryId = ed["category_id"].AsString(); + var categoryName = categories.ContainsKey(categoryId) ? categories[categoryId] : "Unknown"; + + categoryEntry["id"] = categoryId; + categoryEntry["name"] = categoryName; + + extraData["category_info"] = categoryEntry; + + // Add modified time and size + var versions = ed["versions"].AsList(); + extraData["modified"] = versions[versions.Count - 1]["modified"]; + extraData["size"] = versions[versions.Count - 1]["size"]; + + md.Value.AsDict()["extra_info"] = extraData; + } + } + + mainPackageData.AsDict()["packages"] = new JsonValue(mainDataDict); + return mainPackageData; + } + + private static Dictionary CreateCategoryDictionary(JsonValue json) + { + var categories = new Dictionary(); + + var list = json.AsList(); + + for (int i = 0; i < list.Count; i++) + { + var category = list[i].AsDict(); + if (category["status"].AsString() == "deprecated") + continue; + categories.Add(category["id"].AsString(), category["assetstore_name"].AsString()); + } + + return categories; + } + + /// + /// Check if the account data is for a valid publisher account + /// + /// Json structure retrieved from one of the API login methods + public static bool IsPublisherValid(JsonValue json, out ASError error) + { + error = ASError.GetPublisherNullError(json["name"]); + + if (!json.ContainsKey("publisher")) + return false; + + // If publisher account is not created - let them know + return !json["publisher"].IsNull(); + } + + /// + /// Cancel all data retrieval tasks + /// + public static void AbortDownloadTasks() + { + s_downloadCancellationSource?.Cancel(); + } + + /// + /// Cancel all data uploading tasks + /// + public static void AbortUploadTasks() + { + foreach (var upload in ActiveUploads) + { + AbortPackageUpload(upload.Key); + } + } + + private static void SetupDownloadCancellation() + { + if (s_downloadCancellationSource != null && s_downloadCancellationSource.IsCancellationRequested) + DisposeDownloadCancellation(); + + if (s_downloadCancellationSource == null) + s_downloadCancellationSource = new CancellationTokenSource(); + } + + private static void DisposeDownloadCancellation() + { + s_downloadCancellationSource?.Dispose(); + s_downloadCancellationSource = null; + } + + private static void EditorPlayModeStateChangeHandler(PlayModeStateChange state) + { + if (state != PlayModeStateChange.ExitingEditMode) + return; + + EditorApplication.ExitPlaymode(); + EditorUtility.DisplayDialog("Notice", "Entering Play Mode is not allowed while there's a package upload in progress.\n\n" + + "Please wait until the upload is finished or cancel the upload from the Asset Store Uploader window", "OK"); + } + + private static void OverrideAssetStoreUrl() + { + var args = Environment.GetCommandLineArgs(); + for (var i = 0; i < args.Length; i++) + { + if (!args[i].Equals("-assetStoreUrl")) + continue; + + if (i + 1 >= args.Length) + return; + + ASDebug.Log($"Overriding A$ URL to: {args[i + 1]}"); + AssetStoreProdUrl = args[i + 1]; + return; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs.meta new file mode 100644 index 000000000..d248bfef2 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 684fca3fffd79d944a32d9b3adbfc007 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data.meta new file mode 100644 index 000000000..be3554a6d --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9e3cae7082463da41b807724242fd617 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs new file mode 100644 index 000000000..3d6772df2 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs @@ -0,0 +1,80 @@ +using System; +using UnityEditor; +using UnityEngine.Analytics; + +namespace AssetStoreTools.Uploader.Data +{ + internal static class ASAnalytics + { + private const int VersionId = 3; + private const int MaxEventsPerHour = 20; + private const int MaxNumberOfElements = 1000; + + private const string VendorKey = "unity.assetStoreTools"; + private const string EventName = "assetStoreTools"; + + static bool EnableAnalytics() + { +#if UNITY_2023_2_OR_NEWER + return true; +#else + var result = EditorAnalytics.RegisterEventWithLimit(EventName, MaxEventsPerHour, MaxNumberOfElements, VendorKey, VersionId); + return result == AnalyticsResult.Ok; +#endif + } + + [System.Serializable] + public struct AnalyticsData +#if UNITY_2023_2_OR_NEWER + : IAnalytic.IData +#endif + { + public string ToolVersion; + public string PackageId; + public string Category; + public bool UsedValidator; + public string ValidatorResults; + public string UploadFinishedReason; + public double TimeTaken; + public long PackageSize; + public string Workflow; + public string EndpointUrl; + } + +#if UNITY_2023_2_OR_NEWER + [AnalyticInfo(eventName: EventName, vendorKey: VendorKey, version: VersionId, maxEventsPerHour: MaxEventsPerHour, maxNumberOfElements: MaxNumberOfElements)] + private class AssetStoreToolsAnalytic : IAnalytic + { + private AnalyticsData _data; + + public AssetStoreToolsAnalytic(AnalyticsData data) + { + _data = data; + } + + public bool TryGatherData(out IAnalytic.IData data, out Exception error) + { + error = null; + data = _data; + return data != null; + } + } +#endif + + public static void SendUploadingEvent(AnalyticsData data) + { + if (!EditorAnalytics.enabled) + return; + + if (!EnableAnalytics()) + return; + +#if UNITY_2023_2_OR_NEWER + var analytic = new AssetStoreToolsAnalytic(data); + EditorAnalytics.SendAnalytic(analytic); +#else + EditorAnalytics.SendEventWithLimit(EventName, data, VersionId); +#endif + } + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs.meta new file mode 100644 index 000000000..22a47b189 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1095145789a64767a6add837eea19786 +timeCreated: 1658832954 \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs new file mode 100644 index 000000000..ecc76aeaa --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading; + +namespace AssetStoreTools.Uploader.Data +{ + internal class OngoingUpload : IDisposable + { + private CancellationTokenSource _cancellationTokenSource; + + public string VersionId { get; } + public string PackageName { get; } + public float Progress { get; private set; } + public CancellationToken CancellationToken => _cancellationTokenSource.Token; + + public OngoingUpload(string versionId, string packageName) + { + VersionId = versionId; + PackageName = packageName; + Progress = 0f; + _cancellationTokenSource = new CancellationTokenSource(); + } + + public void Cancel() + { + _cancellationTokenSource?.Cancel(); + } + + public void Dispose() + { + _cancellationTokenSource?.Dispose(); + _cancellationTokenSource = null; + } + + public void UpdateProgress(float newProgress) + { + Progress = newProgress; + } + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs.meta new file mode 100644 index 000000000..82a24a3dd --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 601fdada4edc5b94eb83a21d1a01ed26 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs new file mode 100644 index 000000000..42d3205b9 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs @@ -0,0 +1,36 @@ +namespace AssetStoreTools.Uploader.Data +{ + internal class PackageData + { + public string Id { get; } + public string Name { get; } + public string VersionId { get; } + public string Status { get; } + public string Category { get; } + public bool IsCompleteProject { get; } + public string LastUploadedPath { get; } + public string LastUploadedGuid { get; } + + public string LastDate { get; } + public string LastSize { get; } + + public PackageData(string id, string name, string versionId, string status, string category, bool isCompleteProject, string lastUploadedPath, string lastUploadedGuid, string lastDate, string lastSize) + { + Id = id; + Name = name; + VersionId = versionId; + Status = status; + Category = category; + IsCompleteProject = isCompleteProject; + LastUploadedPath = lastUploadedPath; + LastUploadedGuid = lastUploadedGuid; + LastDate = lastDate; + LastSize = lastSize; + } + + public override string ToString() + { + return $"{Id} {Name} {VersionId} {Status} {Category} {LastUploadedPath} {LastUploadedGuid} {IsCompleteProject} {LastDate} {LastSize}"; + } + } +} \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs.meta new file mode 100644 index 000000000..c9c1b2113 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8157930875be4972a48c870a3d1e8ff1 +timeCreated: 1658919930 \ No newline at end of file diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageUploadResult.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageUploadResult.cs new file mode 100644 index 000000000..9931b4f62 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageUploadResult.cs @@ -0,0 +1,46 @@ +using AssetStoreTools.Utility; +using UnityEngine; + +namespace AssetStoreTools.Uploader.Data +{ + internal class PackageUploadResult + { + public enum UploadStatus + { + Default = 0, + Success = 1, + Fail = 2, + Cancelled = 3, + ResponseTimeout = 4 + } + + public UploadStatus Status; + public ASError Error; + + private PackageUploadResult() { } + + public static PackageUploadResult PackageUploadSuccess() => new PackageUploadResult() { Status = UploadStatus.Success }; + + public static PackageUploadResult PackageUploadFail(ASError e) => new PackageUploadResult() { Status = UploadStatus.Fail, Error = e }; + + public static PackageUploadResult PackageUploadCancelled() => new PackageUploadResult() { Status = UploadStatus.Cancelled }; + + public static PackageUploadResult PackageUploadResponseTimeout() => new PackageUploadResult() { Status = UploadStatus.ResponseTimeout }; + + public static Color GetColorByStatus(UploadStatus status) + { + switch (status) + { + default: + case UploadStatus.Default: + return new Color(0.13f, 0.59f, 0.95f); + case UploadStatus.Success: + return new Color(0f, 0.50f, 0.14f); + case UploadStatus.Cancelled: + return new Color(0.78f, 0.59f, 0f); + case UploadStatus.Fail: + return new Color(0.69f, 0.04f, 0.04f); + } + } + } +} diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageUploadResult.cs.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageUploadResult.cs.meta new file mode 100644 index 000000000..c69572269 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageUploadResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 188361b01a1450145a6fc2a7aa0a3a3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements.meta new file mode 100644 index 000000000..87a66a2d5 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3eb6991a3db8cc34dad63504bc6c3c0e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login.meta b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login.meta new file mode 100644 index 000000000..0a7ccff81 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d6e2d6bcfe000764e9330d78017e32bc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login/LoginWindow.cs b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login/LoginWindow.cs new file mode 100644 index 000000000..625d083d0 --- /dev/null +++ b/OneSignalExample/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login/LoginWindow.cs @@ -0,0 +1,233 @@ +using AssetStoreTools.Utility; +using AssetStoreTools.Utility.Json; +using System; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UIElements +{ + internal class LoginWindow : VisualElement + { + private readonly string REGISTER_URL = "https://publisher.unity.com/access"; + private readonly string FORGOT_PASSWORD_URL = "https://id.unity.com/password/new"; + + private Button _cloudLoginButton; + private Button _credentialsLoginButton; + + private Label _cloudLoginLabel; + + private TextField _emailField; + private TextField _passwordField; + + private Box _errorBox; + private Label _errorLabel; + + private double _cloudLoginRefreshTime = 1d; + private double _lastRefreshTime; + + public new class UxmlFactory : UxmlFactory { } + + public LoginWindow() + { + styleSheets.Add(StyleSelector.UploaderWindow.LoginWindowStyle); + styleSheets.Add(StyleSelector.UploaderWindow.LoginWindowTheme); + ConstructLoginWindow(); + EditorApplication.update += UpdateCloudLoginButton; + } + + public void SetupLoginElements(Action onSuccess, Action onFail) + { + this.SetEnabled(true); + + _cloudLoginLabel = _cloudLoginButton.Q