diff --git a/.github/workflows/2019.4.40f1_build.yml b/.github/workflows/2019.4.40f1_build.yml deleted file mode 100644 index 79c0100..0000000 --- a/.github/workflows/2019.4.40f1_build.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2019.4.40f1-Build - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2019.4.40f1' - testMode: 'standalone' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2019.4.40f1_editor.yml b/.github/workflows/2019.4.40f1_editor.yml deleted file mode 100644 index 45e1f11..0000000 --- a/.github/workflows/2019.4.40f1_editor.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2019.4.40f1-Editor - -on: - pull_request: - branches: - - main - push: - branches: - - main - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2019.4.40f1' - testMode: 'editmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2019.4.40f1_player.yml b/.github/workflows/2019.4.40f1_player.yml deleted file mode 100644 index d06f183..0000000 --- a/.github/workflows/2019.4.40f1_player.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2019.4.40f1-Player - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2019.4.40f1' - testMode: 'playmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2020.3.40f1_build.yml b/.github/workflows/2020.3.40f1_build.yml deleted file mode 100644 index 91ff3b7..0000000 --- a/.github/workflows/2020.3.40f1_build.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2020.3.40f1-Build - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2020.3.40f1' - testMode: 'standalone' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2020.3.40f1_editor.yml b/.github/workflows/2020.3.40f1_editor.yml deleted file mode 100644 index b839aa1..0000000 --- a/.github/workflows/2020.3.40f1_editor.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2020.3.40f1-Editor - -on: - pull_request: - branches: - - main - push: - branches: - - main - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2020.3.40f1' - testMode: 'editmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2020.3.40f1_player.yml b/.github/workflows/2020.3.40f1_player.yml deleted file mode 100644 index 632f6a1..0000000 --- a/.github/workflows/2020.3.40f1_player.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2020.3.40f1-Player - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2020.3.40f1' - testMode: 'playmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2021.3.45f1_build.yml b/.github/workflows/2021.3.45f1_build.yml deleted file mode 100644 index 0e9cb07..0000000 --- a/.github/workflows/2021.3.45f1_build.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2021.3.45f1-Build - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2021.3.45f1' - testMode: 'standalone' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2021.3.45f1_editor.yml b/.github/workflows/2021.3.45f1_editor.yml deleted file mode 100644 index 411c5f8..0000000 --- a/.github/workflows/2021.3.45f1_editor.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2021.3.45f1-Editor - -on: - pull_request: - branches: - - main - push: - branches: - - main - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2021.3.45f1' - testMode: 'editmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2021.3.45f1_player.yml b/.github/workflows/2021.3.45f1_player.yml deleted file mode 100644 index a90b84d..0000000 --- a/.github/workflows/2021.3.45f1_player.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2021.3.45f1-Player - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2021.3.45f1' - testMode: 'playmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2022.3.57f1_build.yml b/.github/workflows/2022.3.57f1_build.yml deleted file mode 100644 index 26abe34..0000000 --- a/.github/workflows/2022.3.57f1_build.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2022.3.57f1-Build - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2022.3.57f1' - testMode: 'standalone' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2022.3.57f1_editor.yml b/.github/workflows/2022.3.57f1_editor.yml deleted file mode 100644 index eaa76bc..0000000 --- a/.github/workflows/2022.3.57f1_editor.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2022.3.57f1-Editor - -on: - pull_request: - branches: - - main - push: - branches: - - main - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2022.3.57f1' - testMode: 'editmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2022.3.57f1_player.yml b/.github/workflows/2022.3.57f1_player.yml deleted file mode 100644 index ae44fc3..0000000 --- a/.github/workflows/2022.3.57f1_player.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2022.3.57f1-Player - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2022.3.57f1' - testMode: 'playmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2023.1.20f1_build.yml b/.github/workflows/2023.1.20f1_build.yml deleted file mode 100644 index 4c85706..0000000 --- a/.github/workflows/2023.1.20f1_build.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2023.1.20f1-Build - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2023.1.20f1' - testMode: 'standalone' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2023.1.20f1_editor.yml b/.github/workflows/2023.1.20f1_editor.yml deleted file mode 100644 index cb70fdc..0000000 --- a/.github/workflows/2023.1.20f1_editor.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2023.1.20f1-Editor - -on: - pull_request: - branches: - - main - push: - branches: - - main - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2023.1.20f1' - testMode: 'editmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2023.1.20f1_player.yml b/.github/workflows/2023.1.20f1_player.yml deleted file mode 100644 index e076ad5..0000000 --- a/.github/workflows/2023.1.20f1_player.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2023.1.20f1-Player - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2023.1.20f1' - testMode: 'playmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2023.2.20f1_build.yml b/.github/workflows/2023.2.20f1_build.yml deleted file mode 100644 index 8226e6f..0000000 --- a/.github/workflows/2023.2.20f1_build.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2023.2.20f1-Build - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2023.2.20f1' - testMode: 'standalone' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2023.2.20f1_editor.yml b/.github/workflows/2023.2.20f1_editor.yml deleted file mode 100644 index 729c4ed..0000000 --- a/.github/workflows/2023.2.20f1_editor.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2023.2.20f1-Editor - -on: - pull_request: - branches: - - main - push: - branches: - - main - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2023.2.20f1' - testMode: 'editmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/2023.2.20f1_player.yml b/.github/workflows/2023.2.20f1_player.yml deleted file mode 100644 index 60dad76..0000000 --- a/.github/workflows/2023.2.20f1_player.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 2023.2.20f1-Player - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '2023.2.20f1' - testMode: 'playmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/6000.0.37f1_build.yml b/.github/workflows/6000.0.37f1_build.yml deleted file mode 100644 index 59f9d12..0000000 --- a/.github/workflows/6000.0.37f1_build.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 6000.0.37f1-Build - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '6000.0.37f1' - testMode: 'standalone' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/6000.0.37f1_editor.yml b/.github/workflows/6000.0.37f1_editor.yml deleted file mode 100644 index 11994ab..0000000 --- a/.github/workflows/6000.0.37f1_editor.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 6000.0.37f1-Editor - -on: - pull_request: - branches: - - main - push: - branches: - - main - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '6000.0.37f1' - testMode: 'editmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/6000.0.37f1_player.yml b/.github/workflows/6000.0.37f1_player.yml deleted file mode 100644 index 3e20ef7..0000000 --- a/.github/workflows/6000.0.37f1_player.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 6000.0.37f1-Player - -on: - pull_request: - branches: - - main_disabled - push: - branches: - - main_disabled - -jobs: - editor-tests: - uses: ./.github/workflows/main.yml - with: - projectPath: './' - unityVersion: '6000.0.37f1' - testMode: 'playmode' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 08e113f..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Editor Tests - -on: - workflow_call: - inputs: - projectPath: - required: true - type: string - unityVersion: - required: true - type: string - testMode: - required: true - type: string - secrets: - UNITY_LICENSE: - required: true - UNITY_EMAIL: - required: true - UNITY_PASSWORD: - required: true - -jobs: - test: - name: ${{ inputs.unityVersion }} ${{ inputs.testMode }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - lfs: false - - uses: actions/cache@v4 - with: - path: ${{ inputs.projectPath }}/Library - key: Library-${{ inputs.unityVersion }} - restore-keys: | - Library- - - uses: game-ci/unity-test-runner@v4 - id: tests - env: - UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} - UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} - UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} - with: - projectPath: ${{ inputs.projectPath }} - unityVersion: ${{ inputs.unityVersion }} - testMode: ${{ inputs.testMode }} - artifactsPath: ${{ inputs.testMode }}-artifacts - githubToken: ${{ secrets.GITHUB_TOKEN }} - checkName: ${{ inputs.unityVersion }} ${{ inputs.testMode }} Test Results \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..711d691 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,439 @@ +name: release + +on: + push: + branches: + - main + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + UnityProjectPath: ${{ steps.vars.outputs.UnityProjectPath }} + UnityPackageRoot: ${{ steps.vars.outputs.UnityPackageRoot }} + InstallerProjectPath: ${{ steps.vars.outputs.InstallerProjectPath }} + InstallerFileName: ${{ steps.vars.outputs.InstallerFileName }} + InstallerExportMethod: ${{ steps.vars.outputs.InstallerExportMethod }} + InstallerUnityVersion: ${{ steps.vars.outputs.InstallerUnityVersion }} + steps: + - id: vars + run: | + echo "UnityProjectPath=./Unity-Package" >> $GITHUB_OUTPUT + echo "UnityPackageRoot=./Assets/root" >> $GITHUB_OUTPUT + echo "InstallerProjectPath=./Installer" >> $GITHUB_OUTPUT + echo "InstallerFileName=ImageLoader-Installer" >> $GITHUB_OUTPUT + echo "InstallerExportMethod=com.IvanMurzak.Unity.ImageLoader.Installer.PackageExporter.ExportPackage" >> $GITHUB_OUTPUT + echo "InstallerUnityVersion=2019.4.40f1" >> $GITHUB_OUTPUT + + check-version-tag: + needs: setup + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get_version.outputs.current-version }} + prev_tag: ${{ steps.prev_tag.outputs.tag }} + tag_exists: ${{ steps.tag_exists.outputs.exists }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Get version from package.json + id: get_version + uses: martinbeentjes/npm-get-version-action@v1.3.1 + with: + path: ${{ needs.setup.outputs.UnityProjectPath }}/${{ needs.setup.outputs.UnityPackageRoot }} + + - name: Find previous version tag + id: prev_tag + uses: WyriHaximus/github-action-get-previous-tag@v1 + + - name: Check if tag exists + id: tag_exists + uses: mukunku/tag-exists-action@v1.6.0 + with: + tag: ${{ steps.get_version.outputs.current-version }} + + build-unity-installer: + runs-on: [ubuntu-latest] + needs: [setup, check-version-tag] + if: needs.check-version-tag.outputs.tag_exists == 'false' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Cache Unity Library + uses: actions/cache@v4 + with: + path: ${{ needs.setup.outputs.InstallerProjectPath }}/Library + key: Library-Unity-Installer + + - name: Test Unity Installer (EditMode) + uses: game-ci/unity-test-runner@v4 + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} + with: + projectPath: ${{ needs.setup.outputs.InstallerProjectPath }} + unityVersion: ${{ needs.setup.outputs.InstallerUnityVersion }} + customImage: 'unityci/editor:ubuntu-${{ needs.setup.outputs.InstallerUnityVersion }}-base-3' + testMode: editmode + githubToken: ${{ secrets.GITHUB_TOKEN }} + checkName: Unity Installer EditMode Test Results + artifactsPath: artifacts-installer-editmode + customParameters: -CI true -GITHUB_ACTIONS true + + - name: Clean Unity artifacts and reset git state + run: | + # Force remove Unity generated files with restricted permissions + sudo rm -rf ${{ needs.setup.outputs.InstallerProjectPath }}/Logs/ || true + sudo rm -rf ${{ needs.setup.outputs.InstallerProjectPath }}/Temp/ || true + sudo rm -rf ./artifacts-installer-editmode/ || true + + # Reset only tracked files to their committed state + git reset --hard HEAD + echo "Cleaned Unity artifacts and reset tracked files" + + - name: Export Unity Package + uses: game-ci/unity-builder@v4 + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} + with: + projectPath: ${{ needs.setup.outputs.InstallerProjectPath }} + unityVersion: ${{ needs.setup.outputs.InstallerUnityVersion }} + customImage: 'unityci/editor:ubuntu-${{ needs.setup.outputs.InstallerUnityVersion }}-base-3' + buildName: ${{ needs.setup.outputs.InstallerFileName }} + buildsPath: build + buildMethod: ${{ needs.setup.outputs.InstallerExportMethod }} + customParameters: -CI true -GITHUB_ACTIONS true + + - name: Upload Unity Package as artifact + uses: actions/upload-artifact@v4 + with: + name: unity-installer-package + path: ${{ needs.setup.outputs.InstallerProjectPath }}/build/${{ needs.setup.outputs.InstallerFileName }}.unitypackage + + # --- UNITY TESTS --- + # ------------------- + + # --- EDIT MODE --- + + test-unity-2019-4-40f1-editmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2019.4.40f1' + testMode: 'editmode' + secrets: inherit + + test-unity-2020-3-40f1-editmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2020.3.40f1' + testMode: 'editmode' + secrets: inherit + + test-unity-2021-3-45f1-editmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2021.3.45f1' + testMode: 'editmode' + secrets: inherit + + test-unity-2022-3-57f1-editmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2022.3.57f1' + testMode: 'editmode' + secrets: inherit + + test-unity-2023-1-20f1-editmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.1.20f1' + testMode: 'editmode' + secrets: inherit + + test-unity-2023-2-20f1-editmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.2.20f1' + testMode: 'editmode' + secrets: inherit + + test-unity-6000-0-37f1-editmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '6000.0.37f1' + testMode: 'editmode' + secrets: inherit + + # --- PLAY MODE --- + + test-unity-2019-4-40f1-playmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2019.4.40f1' + testMode: 'playmode' + secrets: inherit + + test-unity-2020-3-40f1-playmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2020.3.40f1' + testMode: 'playmode' + secrets: inherit + + test-unity-2021-3-45f1-playmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2021.3.45f1' + testMode: 'playmode' + secrets: inherit + + test-unity-2022-3-57f1-playmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2022.3.57f1' + testMode: 'playmode' + secrets: inherit + + test-unity-2023-1-20f1-playmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.1.20f1' + testMode: 'playmode' + secrets: inherit + + test-unity-2023-2-20f1-playmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.2.20f1' + testMode: 'playmode' + secrets: inherit + + test-unity-6000-0-37f1-playmode: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '6000.0.37f1' + testMode: 'playmode' + secrets: inherit + + # --- STANDALONE --- + + test-unity-2019-4-40f1-standalone: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2019.4.40f1' + testMode: 'standalone' + secrets: inherit + + test-unity-2020-3-40f1-standalone: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2020.3.40f1' + testMode: 'standalone' + secrets: inherit + + test-unity-2021-3-45f1-standalone: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2021.3.45f1' + testMode: 'standalone' + secrets: inherit + + test-unity-2022-3-57f1-standalone: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2022.3.57f1' + testMode: 'standalone' + secrets: inherit + + test-unity-2023-1-20f1-standalone: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.1.20f1' + testMode: 'standalone' + secrets: inherit + + test-unity-2023-2-20f1-standalone: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.2.20f1' + testMode: 'standalone' + secrets: inherit + + test-unity-6000-0-37f1-standalone: + needs: [setup, build-unity-installer] + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '6000.0.37f1' + testMode: 'standalone' + secrets: inherit + + # ------------------- + + release-unity-plugin: + runs-on: ubuntu-latest + needs: [ + check-version-tag, + build-unity-installer, + test-unity-2019-4-40f1-editmode, + test-unity-2019-4-40f1-playmode, + test-unity-2019-4-40f1-standalone, + test-unity-2020-3-40f1-editmode, + test-unity-2020-3-40f1-playmode, + test-unity-2020-3-40f1-standalone, + test-unity-2021-3-45f1-editmode, + test-unity-2021-3-45f1-playmode, + test-unity-2021-3-45f1-standalone, + test-unity-2022-3-57f1-editmode, + test-unity-2022-3-57f1-playmode, + test-unity-2022-3-57f1-standalone, + test-unity-2023-1-20f1-editmode, + test-unity-2023-1-20f1-playmode, + test-unity-2023-1-20f1-standalone, + test-unity-2023-2-20f1-editmode, + test-unity-2023-2-20f1-playmode, + test-unity-2023-2-20f1-standalone, + test-unity-6000-0-37f1-editmode, + test-unity-6000-0-37f1-playmode, + test-unity-6000-0-37f1-standalone + ] + if: needs.check-version-tag.outputs.tag_exists == 'false' + outputs: + version: ${{ needs.check-version-tag.outputs.version }} + success: ${{ steps.rel_desc.outputs.success }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Generate release description + id: rel_desc + env: + GH_TOKEN: ${{ github.token }} + run: | + set -e + version=${{ needs.check-version-tag.outputs.version }} + prev_tag=${{ needs.check-version-tag.outputs.prev_tag }} + repo_url="https://github.com/${GITHUB_REPOSITORY}" + today=$(date +'%B %e, %Y') + + echo "repo_url: $repo_url" + echo "today: $today" + + echo "# Unity Theme $version" > release.md + echo "**Released:** *$today*" >> release.md + + echo "" >> release.md + echo "---" >> release.md + echo "" >> release.md + + if [ -n "$prev_tag" ]; then + echo "## Comparison" >> release.md + echo "See every change: [Compare $prev_tag...$version]($repo_url/compare/$prev_tag...$version)" >> release.md + + echo "" >> release.md + echo "---" >> release.md + echo "" >> release.md + + echo "## Commit Summary (Newest → Oldest)" >> release.md + for sha in $(git log --pretty=format:'%H' $prev_tag..HEAD); do + username=$(gh api repos/${GITHUB_REPOSITORY}/commits/$sha --jq '.author.login // .commit.author.name') + message=$(git log -1 --pretty=format:'%s' $sha) + short_sha=$(git log -1 --pretty=format:'%h' $sha) + echo "- [\`$short_sha\`]($repo_url/commit/$sha) — $message by @$username" >> release.md + done + fi + + printf "release_body<> $GITHUB_OUTPUT + echo "success=true" >> $GITHUB_OUTPUT + + - name: Create Tag and Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ needs.check-version-tag.outputs.version }} + name: ${{ needs.check-version-tag.outputs.version }} + body: ${{ steps.rel_desc.outputs.release_body }} + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-unity-installer: + runs-on: ubuntu-latest + needs: [setup, release-unity-plugin] + steps: + - name: Download Unity Package artifact + uses: actions/download-artifact@v4 + with: + name: unity-installer-package + path: ./ + + - name: Upload Unity Package to Release + uses: softprops/action-gh-release@v2 + with: + files: ./${{ needs.setup.outputs.InstallerFileName }}.unitypackage + tag_name: ${{ needs.release-unity-plugin.outputs.version }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Cleanup job to remove build artifacts after publishing + cleanup-artifacts: + runs-on: ubuntu-latest + needs: [publish-unity-installer] + if: always() + steps: + - name: Delete Unity Package artifacts + uses: geekyeggo/delete-artifact@v5 + with: + name: unity-installer-package + failOnError: false + continue-on-error: true \ No newline at end of file diff --git a/.github/workflows/test_pull_request.yml b/.github/workflows/test_pull_request.yml new file mode 100644 index 0000000..7e45d4c --- /dev/null +++ b/.github/workflows/test_pull_request.yml @@ -0,0 +1,223 @@ +name: test-pull-request + +on: + pull_request: + branches: [main, dev] + types: [opened, synchronize, reopened] + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + UnityProjectPath: ${{ steps.vars.outputs.UnityProjectPath }} + UnityPackageRoot: ${{ steps.vars.outputs.UnityPackageRoot }} + InstallerProjectPath: ${{ steps.vars.outputs.InstallerProjectPath }} + InstallerFileName: ${{ steps.vars.outputs.InstallerFileName }} + InstallerExportMethod: ${{ steps.vars.outputs.InstallerExportMethod }} + InstallerUnityVersion: ${{ steps.vars.outputs.InstallerUnityVersion }} + steps: + - id: vars + run: | + echo "UnityProjectPath=./Unity-Package" >> $GITHUB_OUTPUT + echo "UnityPackageRoot=./Assets/root" >> $GITHUB_OUTPUT + echo "InstallerProjectPath=./Installer" >> $GITHUB_OUTPUT + echo "InstallerFileName=Unity-ImageLoader-Installer" >> $GITHUB_OUTPUT + echo "InstallerExportMethod=com.IvanMurzak.Unity.ImageLoader.Installer.PackageExporter.ExportPackage" >> $GITHUB_OUTPUT + echo "InstallerUnityVersion=2019.4.40f1" >> $GITHUB_OUTPUT + + # --- EDIT MODE --- + + test-unity-2019-4-40f1-editmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2019.4.40f1' + testMode: 'editmode' + secrets: inherit + + test-unity-2020-3-40f1-editmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2020.3.40f1' + testMode: 'editmode' + secrets: inherit + + test-unity-2021-3-45f1-editmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2021.3.45f1' + testMode: 'editmode' + secrets: inherit + + test-unity-2022-3-57f1-editmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2022.3.57f1' + testMode: 'editmode' + secrets: inherit + + test-unity-2023-1-20f1-editmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.1.20f1' + testMode: 'editmode' + secrets: inherit + + test-unity-2023-2-20f1-editmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.2.20f1' + testMode: 'editmode' + secrets: inherit + + test-unity-6000-0-37f1-editmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '6000.0.37f1' + testMode: 'editmode' + secrets: inherit + + # --- PLAY MODE --- + + test-unity-2019-4-40f1-playmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2019.4.40f1' + testMode: 'playmode' + secrets: inherit + + test-unity-2020-3-40f1-playmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2020.3.40f1' + testMode: 'playmode' + secrets: inherit + + test-unity-2021-3-45f1-playmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2021.3.45f1' + testMode: 'playmode' + secrets: inherit + + test-unity-2022-3-57f1-playmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2022.3.57f1' + testMode: 'playmode' + secrets: inherit + + test-unity-2023-1-20f1-playmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.1.20f1' + testMode: 'playmode' + secrets: inherit + + test-unity-2023-2-20f1-playmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.2.20f1' + testMode: 'playmode' + secrets: inherit + + test-unity-6000-0-37f1-playmode: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '6000.0.37f1' + testMode: 'playmode' + secrets: inherit + + # --- STANDALONE --- + + test-unity-2019-4-40f1-standalone: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2019.4.40f1' + testMode: 'standalone' + secrets: inherit + + test-unity-2020-3-40f1-standalone: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2020.3.40f1' + testMode: 'standalone' + secrets: inherit + + test-unity-2021-3-45f1-standalone: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2021.3.45f1' + testMode: 'standalone' + secrets: inherit + + test-unity-2022-3-57f1-standalone: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2022.3.57f1' + testMode: 'standalone' + secrets: inherit + + test-unity-2023-1-20f1-standalone: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.1.20f1' + testMode: 'standalone' + secrets: inherit + + test-unity-2023-2-20f1-standalone: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '2023.2.20f1' + testMode: 'standalone' + secrets: inherit + + test-unity-6000-0-37f1-standalone: + needs: setup + uses: ./.github/workflows/test_unity_plugin.yml + with: + projectPath: ${{ needs.setup.outputs.UnityProjectPath }} + unityVersion: '6000.0.37f1' + testMode: 'standalone' + secrets: inherit + + # ------------------- \ No newline at end of file diff --git a/.github/workflows/test_unity_plugin.yml b/.github/workflows/test_unity_plugin.yml new file mode 100644 index 0000000..7fb1d0c --- /dev/null +++ b/.github/workflows/test_unity_plugin.yml @@ -0,0 +1,112 @@ +name: test-unity-plugin + +############################################################################## +# 1. Triggers +############################################################################## +on: + workflow_call: + inputs: + projectPath: { required: true, type: string } + unityVersion: { required: true, type: string } + testMode: { required: true, type: string } + secrets: + UNITY_LICENSE: { required: true } + UNITY_EMAIL: { required: true } + UNITY_PASSWORD: { required: true } + +############################################################################## +# 2. Jobs – runs only after a maintainer applies the `ci-ok` label +############################################################################## +jobs: + test: + if: | + github.event_name != 'pull_request_target' || + contains(github.event.pull_request.labels.*.name,'ci-ok') + strategy: + fail-fast: false + matrix: + include: + - os: windows + runs-on: [windows-latest] + - os: ubuntu + runs-on: [ubuntu-latest] + + name: ${{ inputs.unityVersion }} ${{ inputs.testMode }} on ${{ matrix.os }} + runs-on: ${{ matrix.runs-on }} + + # permissions: # minimize the default token + # contents: write + # pull-requests: write + + steps: + # --------------------------------------------------------------------- # + # 2-a. Skip Unity 2019 on Windows early + # --------------------------------------------------------------------- # + - name: Skip Unity 2019 on Windows + if: ${{ startsWith(inputs.unityVersion, '2019.') && matrix.os == 'windows' }} + run: | + echo "::notice::Unity 2019 on Windows doesn't support Docker test running - skipping tests" + exit 0 + shell: bash + + # --------------------------------------------------------------------- # + # 2-b. (PR only) abort if the contributor also changed workflow files + # --------------------------------------------------------------------- # + - name: Abort if workflow files modified + if: ${{ github.event_name == 'pull_request_target' && !(startsWith(inputs.unityVersion, '2019.') && matrix.os == 'windows') }} + run: | + git fetch --depth=1 origin "${{ github.base_ref }}" + if git diff --name-only HEAD origin/${{ github.base_ref }} | grep -q '^\.github/workflows/'; then + echo "::error::This PR edits workflow files – refusing to run with secrets"; exit 1; + fi + + # --------------------------------------------------------------------- # + # 2-c. Checkout the contributor's commit safely + # --------------------------------------------------------------------- # + - uses: actions/checkout@v4 + if: ${{ !(startsWith(inputs.unityVersion, '2019.') && matrix.os == 'windows') }} + with: + lfs: false + + # --------------------------------------------------------------------- # + # 2-c. Cache & run the Unity test-runner + # --------------------------------------------------------------------- # + - uses: actions/cache@v4 + if: ${{ !(startsWith(inputs.unityVersion, '2019.') && matrix.os == 'windows') }} + with: + path: | + ${{ inputs.projectPath }}/Library + ~/.cache/unity3d + key: Library-${{ inputs.unityVersion }}-${{ matrix.os }} + + # --------------------------------------------------------------------- # + - name: Generate custom image name + if: ${{ !(startsWith(inputs.unityVersion, '2019.') && matrix.os == 'windows') }} + id: custom_image + run: | + echo "image=unityci/editor:${{ matrix.os }}-${{ inputs.unityVersion }}-base-3" >> $GITHUB_OUTPUT + shell: bash + + - uses: game-ci/unity-test-runner@v4 + if: ${{ !(startsWith(inputs.unityVersion, '2019.') && matrix.os == 'windows') }} + id: tests + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} + with: + projectPath: ${{ inputs.projectPath }} + unityVersion: ${{ inputs.unityVersion }} + testMode: ${{ inputs.testMode }} + customImage: ${{ steps.custom_image.outputs.image }} + githubToken: ${{ secrets.GITHUB_TOKEN }} + checkName: ${{ inputs.unityVersion }} ${{ inputs.testMode }} ${{ matrix.os }} Test Results + artifactsPath: artifacts-${{ inputs.unityVersion }}-${{ inputs.testMode }}-${{ matrix.os }} + customParameters: -CI true -GITHUB_ACTIONS true + + # --------------------------------------------------------------------- # + - uses: actions/upload-artifact@v4 + if: ${{ always() && !(startsWith(inputs.unityVersion, '2019.') && matrix.os == 'windows') }} + with: + name: Test results for ${{ inputs.unityVersion }} ${{ inputs.testMode }} on ${{ matrix.os }} + path: ${{ steps.tests.outputs.artifactsPath }} \ No newline at end of file diff --git a/.vsconfig b/.vsconfig deleted file mode 100644 index d70cd98..0000000 --- a/.vsconfig +++ /dev/null @@ -1,6 +0,0 @@ -{ - "version": "1.0", - "components": [ - "Microsoft.VisualStudio.Workload.ManagedGame" - ] -} diff --git a/Assets/_PackageRoot/README.md b/Assets/_PackageRoot/README.md deleted file mode 100644 index f6cb8e4..0000000 --- a/Assets/_PackageRoot/README.md +++ /dev/null @@ -1,592 +0,0 @@ -# Unity Image Loader - -![npm](https://img.shields.io/npm/v/extensions.unity.imageloader) [![openupm](https://img.shields.io/npm/v/extensions.unity.imageloader?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/extensions.unity.imageloader/) ![License](https://img.shields.io/github/license/IvanMurzak/Unity-ImageLoader) [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) - -![2019.4.40f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2019.4.40f1_editor.yml?label=2019.4.40f1-Editor) ![2020.3.40f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2020.3.40f1_editor.yml?label=2020.3.40f1-Editor) ![2021.3.45f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2021.3.45f1_editor.yml?label=2021.3.45f1-Editor) ![2022.3.57f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2022.3.57f1_editor.yml?label=2022.3.57f1-Editor) ![2023.1.20f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2023.1.20f1_editor.yml?label=2023.1.20f1-Editor) ![2023.2.20f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2023.2.20f1_editor.yml?label=2023.2.20f1-Editor) ![6000.0.37f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/6000.0.37f1_editor.yml?label=6000.0.37f1-Editor) - -Async image loader with two caching layers for Unity. It supports loading images from web or local paths and provides memory and disk caching to optimize performance. The package includes features for automatic image setting, cancellation handling, error handling, and lifecycle management. - -Wait for image get loaded then set: - -```csharp -image.sprite = await ImageLoader.LoadSprite(imageURL); -``` - -Don't wait, use callback to set loaded image later: - -```csharp -ImageLoader.LoadSprite(imageURL).Consume(image).Forget(); -``` - -Use callback to set image and still wait for the completion: - -```csharp -await ImageLoader.LoadSprite(imageURL).Consume(image); -``` - -## Features - -- ✔️ Async loading from **Web** or **Local** `ImageLoader.LoadSprite(imageURL);` -- ✔️ **Memory** and **Disk** caching - tries to load from memory first, then from disk -- ✔️ Dedicated thread for disk operations -- ✔️ Supports loading of `Texture2D` and `Sprite` -- ✔️ Avoids loading same image multiple times simultaneously, a new load task waits for completion of existed task -- ✔️ Uses `UnityWebRequest` to load data which works smooth across all platforms including `WebGL` -- ✔️ Cache supported on at `WebGL`. Memory cache works, Disk cache isn't allowed by the platform -- ✔️ Set into Image `ImageLoader.LoadSprite(imageURL).Consume(image);` -- ✔️ Set into RawImage `ImageLoader.LoadSprite(imageURL).Consume(rawImage);` -- ✔️ Set into Material `ImageLoader.LoadSprite(imageURL).Consume("_MainTex", material);` -- ✔️ Set into SpriteRenderer `ImageLoader.LoadSprite(imageURL).Consume(spriteRenderer);` -- ✔️ [Set into anything](#cancellation) -- ✔️ Cancellation `ImageLoader.LoadSprite(imageURL).Cancel();` -- ✔️ Cancellation callback `ImageLoader.LoadSprite(imageURL).Cancelled(() => ...);` -- ✔️ Error callback `ImageLoader.LoadSprite(imageURL).Failed(exception => ...);` -- ✔️ Debug level for logging `ImageLoader.settings.debugLevel = DebugLevel.Error;` -- ✔️ Debug level per each task `ImageLoader.LoadSprite(imageURL).SetLogLevel(DebugLevel.Trace);` - -## Content - -- [Unity Image Loader](#unity-image-loader) - - [Features](#features) - - [Content](#content) - - [Installation](#installation) -- [Usage](#usage) - - [Events lifecycle](#events-lifecycle) - - [Load `Sprite` then set into `Image`](#load-sprite-then-set-into-image) - - [Load `Texture2D` then set into `Material`](#load-texture2d-then-set-into-material) - - [Load `Sprite` then set into multiple `Image`](#load-sprite-then-set-into-multiple-image) - - [Error handling](#error-handling) - - [Async `await` and `Forget`](#async-await-and-forget) - - [Placeholder](#placeholder) - - [Cancellation](#cancellation) - - [Cancel by MonoBehaviour events](#cancel-by-monobehaviour-events) - - [Explicit cancellation](#explicit-cancellation) - - [Cancellation Token](#cancellation-token) - - [Cancellation by `using`](#cancellation-by-using) - - [Timeout](#timeout) - - [Cache](#cache) - - [Setup Cache](#setup-cache) - - [Change Disk cache folder](#change-disk-cache-folder) - - [Override for a specific loading task](#override-for-a-specific-loading-task) - - [Manually read / write into cache](#manually-read--write-into-cache) - - [Check cache existence](#check-cache-existence) - - [Clear cache](#clear-cache) - - [Texture Memory Management](#texture-memory-management) - - [Manual Memory cache cleaning](#manual-memory-cache-cleaning) - - [Automatic Memory cache cleaning](#automatic-memory-cache-cleaning) - - [Load Reference](#load-reference) - - [Dispose `Reference` on `Component` destroy event](#dispose-referencet-on-component-destroy-event) - - [Get references count](#get-references-count) -- [Other](#other) - - [Understanding `IFuture`](#understanding-ifuturet) - - [Key Properties of `IFuture`](#key-properties-of-ifuturet) - - [Key Methods of `IFuture`](#key-methods-of-ifuturet) - - [Example Usage of `IFuture`](#example-usage-of-ifuturet) - - [Understanding `Reference`](#understanding-referencet) - - [Key Properties of `Reference`](#key-properties-of-referencet) - - [Key Methods of `Reference`](#key-methods-of-referencet) - - [Example Usage of `Reference`](#example-usage-of-referencet) - - [Understanding `Future>`](#understanding-futurereferencet) - - [Why It Is Needed](#why-it-is-needed) - ---- - -## Installation - -- [Install OpenUPM-CLI](https://github.com/openupm/openupm-cli#installation) -- Open command line in Unity project folder -- Run the command - -``` CLI -openupm add extensions.unity.imageloader -``` - -# Usage - -In the main thread somewhere at the start of the project need to call `ImageLoader.Init();` once to initialize static properties in the right thread. It is required to make in the main thread. Then you can use `ImageLoader` from any thread and at any time. - -```csharp -ImageLoader.Init(); // just once from the main thread -``` - -## Events lifecycle - -> [Full sample source code](https://github.com/IvanMurzak/Unity-ImageLoader/blob/master/Assets/_PackageRoot/Samples/SampleLifecycle.cs) - -`ImageLoader.LoadSprite` returns `IFuture`. This instance provides all range of callbacks and API to modify it. [Understanding `IFuture`](#understanding-ifuturet). - -```csharp -ImageLoader.LoadSprite(imageURL) // loading process started - // ┌──────────────────────────┬────────────────────────────────────────────────────────────────────────┐ - // │ Loading lifecycle events │ │ - // └──────────────────────────┘ │ - .LoadedFromMemoryCache(sprite => Debug.Log("Loaded from memory cache")) // on loaded from memory cache │ - .LoadingFromDiskCache (() => Debug.Log("Loading from disk cache")) // on loading from disk cache │ - .LoadedFromDiskCache (sprite => Debug.Log("Loaded from disk cache")) // on loaded from disk cache │ - .LoadingFromSource (() => Debug.Log("Loading from source")) // on loading from source │ - .LoadedFromSource (sprite => Debug.Log("Loaded from source")) // on loaded from source │ - // ────────────────────────────────────────────────────────────────────────────────────────────────────┘ - - // ┌──────────────────────────┬───────────────────────────────────────────┐ - // │ Success lifecycle events │ │ - // └──────────────────────────┘ │ - .Loaded(sprite => Debug.Log("Loaded")) // on successfully loaded │ - // ┌────────────────────────────────────────────────────────┤ - // │ Set/Consume sprite [placeholder, successfully loaded] │ - // └────────────────────────────────────────────────────────┤ - .Consume(sprite => Debug.Log("Consumed")) // │ - .Consume(image) // │ - // ───────────────────────────────────────────────────────────────────────┘ - - // ┌───────────────────────────┬──────────────────────────────────────────┐ - // │ Negative lifecycle events │ │ - // └───────────────────────────┘ │ - .Canceled(() => Debug.Log("Canceled")) // on canceled │ - .Failed(exception => Debug.LogException(exception)) // on failed to load │ - // ───────────────────────────────────────────────────────────────────────┘ - - // ┌──────────────────────┬──────────────────────────────────────────────────────────────────────────┐ - // │ The end of lifecycle │ │ - // └──────────────────────┘ │ - .Completed(isLoaded => Debug.Log($"Completed, isLoaded={isLoaded}")) // on completed │ - // // [loaded, failed or canceled] │ - // ──────────────────────────────────────────────────────────────────────────────────────────────────┘ - - .Forget(); // removes the compilation warning, does nothing else -``` - -## Load `Sprite` then set into `Image` - -> [Full sample source code](https://github.com/IvanMurzak/Unity-ImageLoader/blob/master/Assets/_PackageRoot/Samples/SampleLoadSpriteConsumeImage.cs) - -```csharp -// Load a sprite from the web and cache it for faster loading next time -image.sprite = await ImageLoader.LoadSprite(imageURL); - -// Load a sprite from the web and set it directly to the Image component -await ImageLoader.LoadSprite(imageURL).Consume(image); -``` - -## Load `Texture2D` then set into `Material` - -> [Full sample source code](https://github.com/IvanMurzak/Unity-ImageLoader/blob/master/Assets/_PackageRoot/Samples/SampleLoadTextureConsumeMaterial.cs) - -```csharp -// Load a Texture2D from the web and cache it for faster loading next time -material.mainTexture = await ImageLoader.LoadTexture(imageURL); - -// Load a Texture2D from the web and set it directly to the Material -await ImageLoader.LoadTexture(imageURL).Consume(material); -``` - -## Load `Sprite` then set into multiple `Image` - -> [Full sample source code](https://github.com/IvanMurzak/Unity-ImageLoader/blob/master/Assets/_PackageRoot/Samples/SampleLoadSpriteConsumeIntoMultipleImages.cs) - -```csharp -ImageLoader.LoadSprite(imageURL).Consume(image1, image2).Forget(); -``` - -## Error handling - -> [Full sample source code](https://github.com/IvanMurzak/Unity-ImageLoader/blob/master/Assets/_PackageRoot/Samples/SampleErrorHandle.cs) - -```csharp -ImageLoader.LoadSprite(imageURL) // Attempt to load a sprite - .Consume(image) // If successful, set the sprite to the Image component - .Failed(exception => Debug.LogException(exception)) // If an error occurs, log the exception - .Forget(); // Forget the task to avoid compilation warning - -ImageLoader.LoadSprite(imageURL) // Attempt to load a sprite - .Consume(image) // If successful, set the sprite to the Image component - .Then(sprite => image.gameObject.SetActive(true)) // If successful, activate the GameObject - .Failed(exception => image.gameObject.SetActive(false)) // If an error occurs, deactivate the GameObject - .Forget(); // Forget the task to avoid compilation warning -``` - -## Async `await` and `Forget` - -> [Full sample source code](https://github.com/IvanMurzak/Unity-ImageLoader/blob/master/Assets/_PackageRoot/Samples/SampleAwaitAndForget.cs) - -```csharp -// Load image and wait -await ImageLoader.LoadSprite(imageURL); - -// Load image, set image and wait -await ImageLoader.LoadSprite(imageURL).Consume(image); - -// Skip waiting for completion. -// To do that we can simply remove 'await' from the start. -// To avoid compilation warning need to add '.Forget()'. -ImageLoader.LoadSprite(imageURL).Consume(image).Forget(); -``` - -## Placeholder - -While the target image is loading it would be a good idea to set placeholder image. Also, it works well for setting image if loading fails. - -> [Full sample source code](https://github.com/IvanMurzak/Unity-ImageLoader/blob/master/Assets/_PackageRoot/Samples/SamplePlaceholder.cs) - -```csharp -ImageLoader.LoadSprite(imageURL) - // set placeholder in all conditions - .SetPlaceholder(placeholderAny) - - // set placeholder in a specific conditions - .SetPlaceholder(placeholderLoadingFromSource, PlaceholderTrigger.LoadingFromSource) - .SetPlaceholder(placeholderFailedToLoad, PlaceholderTrigger.FailedToLoad) - - // set consumer - .Consume(image) - .Forget(); -``` - -## Cancellation - -Cancellation is helpful if target image consumer doesn't exist anymore. For example the `Image` was destroyed because another level had been loaded. The is not much sense to continue to load the image. It would safe some network traffic, CPU resources, and RAM. `IFuture` provides wide range of options to cancel the ongoing loading process. - -> [Full sample source code](https://github.com/IvanMurzak/Unity-ImageLoader/blob/master/Assets/_PackageRoot/Samples/SampleCancellation.cs) - -### Cancel by MonoBehaviour events - -```csharp -ImageLoader.LoadSprite(imageURL) - .Consume(image) - .CancelOnEnable(this) // cancel on OnEnable event of current MonoBehaviour - .CancelOnDisable(this) // cancel on OnDisable event of current MonoBehaviour - .CancelOnDestroy(this); // cancel on OnDestroy event of current MonoBehaviour -``` - -### Explicit cancellation - -```csharp -var future = ImageLoader.LoadSprite(imageURL).Consume(image); -future.Cancel(); -``` - -### Cancellation Token - -```csharp -var cancellationTokenSource = new CancellationTokenSource(); - -// loading with attached cancellation token -ImageLoader.LoadSprite(imageURL, cancellationToken: cancellationTokenSource.Token) - .Consume(image) - .Forget(); - -cancellationTokenSource.Cancel(); // canceling -``` - -```csharp -var cancellationTokenSource = new CancellationTokenSource(); - -ImageLoader.LoadSprite(imageURL) - .Consume(image) - .Register(cancellationTokenSource.Token) // registering cancellation token - .Forget(); - -cancellationTokenSource.Cancel(); // canceling -``` - -### Cancellation by `using` - -```csharp -using (var future = ImageLoader.LoadSprite(imageURL).Consume(image)) -{ - // future would be canceled and disposed outside of the brackets -} -``` - -```csharp -ImageLoader.LoadSprite(imageURL) // load sprite - .Consume(image) // if success set sprite into image - .CancelOnDestroy(this) // cancel OnDestroy event of current gameObject - .Forget(); - -ImageLoader.LoadSprite(imageURL) // load sprite - .Consume(image) // if success set sprite into image - .Failed(exception => Debug.LogException(exception)) // if fail print exception - .CancelOnDestroy(this) // cancel OnDestroy event of current gameObject - .Forget(); - -ImageLoader.LoadSprite(imageURL) // load sprite - .Consume(image) // if success set sprite into image - .Then(sprite => image.gameObject.SetActive(true)) // if success activate gameObject - .Failed(exception => image.gameObject.SetActive(false)) // if fail deactivate gameObject - .Canceled(() => Debug.Log("ImageLoading canceled")) // if cancelled - .CancelOnDisable(this) // cancel OnDisable event of current gameObject - .Forget(); -``` - -### Timeout - -Timeout triggers `IFuture` cancellation. - -> [Full sample source code](https://github.com/IvanMurzak/Unity-ImageLoader/blob/master/Assets/_PackageRoot/Samples/SampleTimeout.cs) - -Set global timeout in the settings: - -```csharp -ImageLoader.settings.timeout = TimeSpan.FromSeconds(30); -``` - -Set timeout for a specific loading request (`IFuture`): - -```csharp -ImageLoader.LoadSprite(imageURL) // load sprite - .Consume(image) // if success set sprite into image - .Timeout(TimeSpan.FromSeconds(10)) // set timeout duration 10 seconds - .Forget(); -``` - -## Cache - -Cache system based on the two layers. The first layer is **Memory cache**, second is **Disk cache**. Each layer could be enabled or disabled. Could be used without caching at all. By default both layers are enabled. `WebGL` doesn't support Disk cache, because it doesn't have access to disk. - -### Setup Cache - -- `ImageLoader.settings.useMemoryCache = true;` default value is `true` -- `ImageLoader.settings.useDiskCache = true;` default value is `true` - -#### Change Disk cache folder - -By default it uses `Application.persistentDataPath + "/ImageLoader"` - -```csharp -ImageLoader.settings.diskSaveLocation = Application.persistentDataPath + "/myCustomFolder"; -``` - -#### Override for a specific loading task - -It overrides global `ImageLoader.settings` - -```csharp -ImageLoader.LoadSprite(url) - .SetUseDiskCache(false) - .SetUseMemoryCache(true); -``` - -> [Full sample source code](https://github.com/IvanMurzak/Unity-ImageLoader/blob/master/Assets/_PackageRoot/Samples/SampleCache.cs) - -### Manually read / write into cache - -```csharp -// Override Memory cache for specific image -ImageLoader.SaveToMemoryCache(url, sprite); - -// Take from Memory cache for specific image if exists -ImageLoader.LoadSpriteFromMemoryCache(url); -``` - -### Check cache existence - -```csharp -// Check if any cache contains specific image -ImageLoader.CacheContains(url); - -// Check if Memory cache contains specific image -ImageLoader.MemoryCacheContains(url); - -// Check if Disk cache contains specific image -ImageLoader.DiskCacheContains(url); -``` - -### Clear cache - -```csharp -// Clear memory Memory and Disk cache for all images -ImageLoader.ClearCacheAll(); - -// Clear only Memory and Disk cache for specific image -ImageLoader.ClearCache(url); - -// Clear only Memory cache for all images -ImageLoader.ClearMemoryCacheAll(); - -// Clear only Memory cache for specific image -ImageLoader.ClearMemoryCache(url); - -// Clear only Disk cache for all images -ImageLoader.ClearDiskCacheAll(); - -// Clear only Disk cache for specific image -ImageLoader.ClearDiskCache(url); -``` - -Memory cache could be cleared automatically if to use `Reference` and the heavy `Texture2D` memory would be released as well. Read more at [Texture Memory Management](#texture-memory-management). - -## Texture Memory Management - -Texture2D objects consume a lot of memory. Ignoring it may impact performance or even trigger `OutOfMemory` crash by operation system. To avoid it, let's dig deeper into tools the package provides. We worry less about Disk cache, because it doesn't impact game performance directly. Let's focus on the Memory cache. - -### Manual Memory cache cleaning - -It is simple, just executing this line of code would release memory of a single Texture2D in the case if no other `Reference` pointing on it exists. Before doing that please make sure that no Unity component is using the texture. - -```csharp -ImageLoader.ClearMemoryCache(url); -``` - -Under the hood it calls `UnityEngine.Object.DestroyImmediate(texture)`. - -> :warning: Releasing `Texture2D` from memory while any Unity's component uses it may trigger native app crash or even Unity Editor crash - -### Automatic Memory cache cleaning - -ImageLoader can manager memory releasing of loaded textures. To use it need to call `ImageLoader.LoadSpriteRef` instead of `ImageLoader.LoadSprite`. It returns `Reference` object which contains `Sprite` and `Url`. When `Reference` object is not needed anymore, call `reference.Dispose()` method to release memory, or just don't save the reference on it. It is `IDisposable` and it will be disposed by Garbage Collector. Each new instance of `Reference` increments reference counter of the texture. When the last reference is disposed, the texture memory releases. Also, if any reference is alive, calling `ImageLoader.ClearMemoryCache` or `ImageLoader.ClearCache` would have zero effect for only referenced textures. It prints warning messages about it. - -```csharp -// Load sprite image and get reference to it -var reference = await ImageLoader.LoadSpriteRef(imageURL); - -// Take from Memory cache reference for specific image if exists -var reference = ImageLoader.LoadSpriteRefFromMemoryCache(url); - -// Dispose `reference` when you don't need the texture anymore -reference.Dispose(); - -// You may also nullify the reference to let Garbage Collector at some point to Dispose it for you -reference = null; -``` - -> :warning: Releasing `Texture2D` from memory while any Unity's component uses it may trigger native app crash or even Unity Editor crash. Please pay enough attention to manage `Reference` instances in a proper way. Or do not use them. - -#### Load Reference - -> [Full sample source code](https://github.com/IvanMurzak/Unity-ImageLoader/blob/master/Assets/_PackageRoot/Samples/SampleReferences.cs) - -`Future>.Consume` has a unique feature to attach the reference to the target consumer if consumer is `UnityEngine.Component`. The reference would be disposed as only the consumer gets destroyed. - -```csharp -ImageLoader.LoadSpriteRef(imageURL) // load sprite using Reference - .Consume(image) // if success set sprite into image, also creates binding to `image` - .Forget(); -``` - -#### Dispose `Reference` on `Component` destroy event - -It automatically dispose the reference as only `this.gameObject` gets `OnDestroy` callback. - -```csharp -ImageLoader.LoadSpriteRef(imageURL) // load sprite using Reference - .Then(reference => reference.DisposeOnDestroy(this)) - .Then(reference => - { - var sprite = reference.Value; - // use sprite - }) - .Forget(); -``` - -#### Get references count - -```csharp -var count = ImageLoader.GetReferenceCount(imageURL); // get count of references -``` - ---- - -# Other - -## Understanding `IFuture` - -The `IFuture` interface represents an asynchronous operation that will eventually produce a result of type `T`. It provides a range of methods and properties to handle the lifecycle of the asynchronous operation, including loading, success, failure, and cancellation events. - -### Key Properties of `IFuture` - -- **`Id`**: Unique identifier for the future. -- **`Url`**: URL associated with the future. -- **`IsCancelled`**: Indicates if the operation has been cancelled. -- **`IsLoaded`**: Indicates if the operation has successfully loaded the result. -- **`IsCompleted`**: Indicates if the operation has completed (either successfully or with an error). -- **`IsInProgress`**: Indicates if the operation is currently in progress. -- **`Status`**: Current status of the future. -- **`CancellationToken`**: Token used to cancel the operation. -- **`Value`**: The result of the operation. -- **`LogLevel`**: The logging level for the operation. - -### Key Methods of `IFuture` - -- **``Then(Action onCompleted)``**: Registers a callback to be executed when the operation successfully completes and produces a result. -- **`Failed(Action action)`**: Registers a callback to be executed if the operation fails with an exception. -- **`Completed(Action action)`**: Registers a callback to be executed when the operation completes, regardless of success or failure. The boolean parameter indicates whether the operation was successful. -- **`Canceled(Action action)`**: Registers a callback to be executed if the operation is canceled. -- **`SetUseDiskCache(bool value = true)`**: Configures whether the operation should use disk caching. -- **`SetUseMemoryCache(bool value = true)`**: Configures whether the operation should use memory caching. -- **`SetLogLevel(DebugLevel value)`**: Sets the logging level for the operation. -- **`Cancel()`**: Cancels the operation if it is still in progress. -- **`Forget()`**: Ignores the result of the operation, useful for avoiding compilation warnings about unawaited tasks. -- **`AsUniTask()`**: Converts the `IFuture` instance to a `UniTask`. -- **`AsTask()`**: Converts the `IFuture` instance to a `Task`. -- **`AsReference(DebugLevel logLevel = DebugLevel.Trace)`**: Converts the `IFuture` instance to a `Future>` instance. -- **`GetAwaiter()`**: Returns an awaiter for the `IFuture` instance, allowing it to be awaited using the `await` keyword. -- **`PassEvents(IFutureInternal to, bool passCancelled = true)`**: Passes events to another future. -- **`PassEvents(IFutureInternal to, Func convert, bool passCancelled = true)`**: Passes events to another future with conversion. -- **`Register(CancellationToken cancellationToken)`**: Registers a new cancellation token to cancel the future with it. -- **`Timeout(TimeSpan duration)`**: Sets a timeout duration for the future. If the duration is reached, it fails the future with a related exception. - -### Example Usage of `IFuture` - -```csharp -ImageLoader.LoadSprite(imageURL) // Start loading the sprite - .Then(sprite => Debug.Log("Loaded")) // On successful load - .Failed(exception => Debug.LogException(exception)) // On failure - .Completed(isLoaded => Debug.Log($"Completed, isLoaded={isLoaded}")) // On completion - .Canceled(() => Debug.Log("Canceled")) // On cancellation - .Forget(); // Avoid compilation warnings -``` - -## Understanding `Reference` - -The `Reference` class is used to manage the lifecycle of loaded resources, such as `Texture2D` or `Sprite`, in a memory-efficient manner. It helps to automatically release memory when the resource is no longer needed, preventing memory leaks and optimizing performance. - -### Key Properties of `Reference` - -- **`Value`**: The actual resource (e.g., `Sprite` or `Texture2D`) that is being referenced. -- **`Url`**: The URL associated with the resource. -- **`IsDisposed`**: Indicates whether the reference has been disposed. - -### Key Methods of `Reference` - -- **`Dispose()`**: Disposes the reference, releasing the associated resource from memory. This should be called when the resource is no longer needed. -- **`DisposeOnDestroy(Component component)`**: Automatically disposes the reference when the specified `Component` is destroyed. This is useful for ensuring that resources are released when the associated GameObject is destroyed. -- **`DisposeOnDisable(Component component)`**: Automatically disposes the reference when the specified `Component` is disabled. -- **`DisposeOnEnable(Component component)`**: Automatically disposes the reference when the specified `Component` is enabled. - -### Example Usage of `Reference` - -```csharp -// Load a sprite using Reference -var reference = await ImageLoader.LoadSpriteRef(imageURL); - -// Use the sprite -var sprite = reference.Value; - -// Dispose the reference when the sprite is no longer needed -reference.Dispose(); -``` - -```csharp -// Alternatively, automatically dispose the reference when the GameObject is destroyed -ImageLoader.LoadSpriteRef(imageURL) - .Then(reference => reference.DisposeOnDestroy(this)) - .Then(reference => - { - var sprite = reference.Value; - // use sprite - }) - .Forget(); -``` - -## Understanding `Future>` - -The `Future>` class combines the functionality of `IFuture` and `Reference`, providing a powerful tool for managing the lifecycle of asynchronous operations that produce resources such as `Texture2D` or `Sprite`. This combination allows you to handle the loading process and the resource management in a memory-efficient manner. - -### Why It Is Needed - -The `Future>` class is needed to ensure that resources are loaded asynchronously and managed efficiently. By using this class, you can: - -1. **Load Resources Asynchronously**: Start loading resources such as `Texture2D` or `Sprite` without blocking the main thread. -2. **Manage Resource Lifecycle**: Automatically release resources when they are no longer needed, preventing memory leaks and optimizing performance. -3. **Handle Loading Events**: Register callbacks for various events such as success, failure, and cancellation during the loading process. -4. **Bind Resources to Components**: Automatically dispose of resources when the associated `UnityEngine.Component` is destroyed, ensuring that resources are released when the GameObject is destroyed. diff --git a/Assets/_PackageRoot/Tests/Runtime/.gitignore b/Assets/_PackageRoot/Tests/Runtime/.gitignore deleted file mode 100644 index 6427e2a..0000000 --- a/Assets/_PackageRoot/Tests/Runtime/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Except this file -!.gitignore \ No newline at end of file diff --git a/Assets/_PackageRoot/package.json b/Assets/_PackageRoot/package.json deleted file mode 100644 index 8c1b3ed..0000000 --- a/Assets/_PackageRoot/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "extensions.unity.imageloader", - "displayName": "Image Loader", - "author": { - "name": "Ivan Murzak", - "email": "ivan.d.murzak@gmail.com", - "url": "https://github.com/IvanMurzak" - }, - "license": "MIT", - "version": "7.0.1", - "unity": "2019.4", - "description": "Asynchronous image loading from remote or local destination. It has two layers of configurable Memory and Disk cache systems.", - "keywords": [ - "image", - "image loader", - "image loading", - "image cache", - "image placeholder", - "async image loading", - "memory", - "disk", - "future" - ], - "homepage": "https://github.com/IvanMurzak/Unity-ImageLoader", - "bugs": { - "url": "https://github.com/IvanMurzak/Unity-ImageLoader/issues" - }, - "repository": { - "type": "git", - "url": "git@github.com:IvanMurzak/Unity-ImageLoader.git" - }, - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.cysharp.unitask": "2.5.10" - }, - "scopedRegistries": [ - { - "name": "package.openupm.com", - "url": "https://package.openupm.com", - "scopes": [ - "com.cysharp.unitask", - "com.openupm" - ] - } - ], - "samples": [ - { - "displayName": "Full Usage", - "description": "Full usage example of ImageLoader", - "path": "Samples" - } - ] -} \ No newline at end of file diff --git a/.gitignore b/Installer/.gitignore similarity index 100% rename from .gitignore rename to Installer/.gitignore diff --git a/Installer/.vscode/settings.json b/Installer/.vscode/settings.json new file mode 100644 index 0000000..e232cd6 --- /dev/null +++ b/Installer/.vscode/settings.json @@ -0,0 +1,55 @@ +{ + "files.exclude": + { + "**/.DS_Store":true, + "**/.git":true, + "**/.gitmodules":true, + "**/*.booproj":true, + "**/*.pidb":true, + "**/*.suo":true, + "**/*.user":true, + "**/*.userprefs":true, + "**/*.unityproj":true, + "**/*.dll":true, + "**/*.exe":true, + "**/*.pdf":true, + "**/*.mid":true, + "**/*.midi":true, + "**/*.wav":true, + "**/*.gif":true, + "**/*.ico":true, + "**/*.jpg":true, + "**/*.jpeg":true, + "**/*.png":true, + "**/*.psd":true, + "**/*.tga":true, + "**/*.tif":true, + "**/*.tiff":true, + "**/*.3ds":true, + "**/*.3DS":true, + "**/*.fbx":true, + "**/*.FBX":true, + "**/*.lxo":true, + "**/*.LXO":true, + "**/*.ma":true, + "**/*.MA":true, + "**/*.obj":true, + "**/*.OBJ":true, + "**/*.asset":true, + "**/*.cubemap":true, + "**/*.flare":true, + "**/*.mat":true, + "**/*.meta":true, + "**/*.prefab":true, + "**/*.unity":true, + "build/":true, + "Build/":true, + "Library/":true, + "library/":true, + "obj/":true, + "Obj/":true, + "ProjectSettings/":true, + "temp/":true, + "Temp/":true + } +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak.meta b/Installer/Assets/com.IvanMurzak.meta new file mode 100644 index 0000000..4e97834 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f485f6d327a9e774ba5b1c1303c6d51d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer.meta new file mode 100644 index 0000000..43b681d --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1a2eff886f7524341acf2326f95a2a56 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.Manifest.cs b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.Manifest.cs new file mode 100644 index 0000000..6bb89e2 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.Manifest.cs @@ -0,0 +1,128 @@ +/* +┌────────────────────────────────────────────────────────────────────────┐ +│ Author: Ivan Murzak (https://github.com/IvanMurzak) │ +│ Repository: GitHub (https://github.com/IvanMurzak/Unity-ImageLoader) │ +│ Copyright (c) 2025 Ivan Murzak │ +│ Licensed under the Apache License, Version 2.0. │ +│ See the LICENSE file in the project root for more information. │ +└────────────────────────────────────────────────────────────────────────┘ +*/ + +using System.IO; +using System.Linq; +using com.IvanMurzak.Unity.ImageLoader.Installer.SimpleJSON; +using UnityEngine; + +namespace com.IvanMurzak.Unity.ImageLoader.Installer +{ + public static partial class Installer + { + static string ManifestPath => Path.Combine(Application.dataPath, "../Packages/manifest.json"); + + // Property names + public const string Dependencies = "dependencies"; + public const string ScopedRegistries = "scopedRegistries"; + public const string Name = "name"; + public const string Url = "url"; + public const string Scopes = "scopes"; + + // Property values + public const string RegistryName = "package.openupm.com"; + public const string RegistryUrl = "https://package.openupm.com"; + public static readonly string[] PackageIds = new string[] { + "com.ivanmurzak", // Ivan Murzak's OpenUPM packages + "extensions.unity", // Ivan Murzak's OpenUPM packages (older) + "org.nuget.com.ivanmurzak", // Ivan Murzak's NuGet packages + "org.nuget.microsoft", // Microsoft NuGet packages + "org.nuget.system", // Microsoft NuGet packages + "org.nuget.r3", // R3 package NuGet package + "com.cysharp.unitask", // Cysharp UniTask package + }; + + public static void AddScopedRegistryIfNeeded(string manifestPath, int indent = 2) + { + if (!File.Exists(manifestPath)) + { + Debug.LogError($"{manifestPath} not found!"); + return; + } + var jsonText = File.ReadAllText(manifestPath) + .Replace("{ }", "{\n}") + .Replace("{}", "{\n}") + .Replace("[ ]", "[\n]") + .Replace("[]", "[\n]"); + + var manifestJson = JSONObject.Parse(jsonText); + if (manifestJson == null) + { + Debug.LogError($"Failed to parse {manifestPath} as JSON."); + return; + } + + var modified = false; + + // --- Add scoped registries if needed + var scopedRegistries = manifestJson[ScopedRegistries]; + if (scopedRegistries == null) + { + manifestJson[ScopedRegistries] = new JSONArray(); + modified = true; + } + + // --- Add OpenUPM registry if needed + var openUpmRegistry = scopedRegistries.Linq + .Select(kvp => kvp.Value) + .Where(r => r.Linq + .Any(p => p.Key == Name && p.Value == RegistryName)) + .FirstOrDefault(); + + if (openUpmRegistry == null) + { + scopedRegistries.Add(openUpmRegistry = new JSONObject + { + [Name] = RegistryName, + [Url] = RegistryUrl, + [Scopes] = new JSONArray() + }); + modified = true; + } + + // --- Add missing scopes + var scopes = openUpmRegistry[Scopes]; + if (scopes == null) + { + openUpmRegistry[Scopes] = scopes = new JSONArray(); + modified = true; + } + foreach (var packageId in PackageIds) + { + var existingScope = scopes.Linq + .Select(kvp => kvp.Value) + .Where(value => value == packageId) + .FirstOrDefault(); + if (existingScope == null) + { + scopes.Add(packageId); + modified = true; + } + } + + // --- Package Dependency + var dependencies = manifestJson[Dependencies]; + if (dependencies == null) + { + manifestJson[Dependencies] = dependencies = new JSONObject(); + modified = true; + } + if (dependencies[PackageId] != Version) + { + dependencies[PackageId] = Version; + modified = true; + } + + // --- Write changes back to manifest + if (modified) + File.WriteAllText(manifestPath, manifestJson.ToString(indent).Replace("\" : ", "\": ")); + } + } +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.Manifest.cs.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.Manifest.cs.meta new file mode 100644 index 0000000..ed56ac8 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.Manifest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 481009727e4bbd94ebc0b6903fecb43b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.cs b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.cs new file mode 100644 index 0000000..37c865e --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.cs @@ -0,0 +1,27 @@ +/* +┌────────────────────────────────────────────────────────────────────────┐ +│ Author: Ivan Murzak (https://github.com/IvanMurzak) │ +│ Repository: GitHub (https://github.com/IvanMurzak/Unity-ImageLoader) │ +│ Copyright (c) 2025 Ivan Murzak │ +│ Licensed under the Apache License, Version 2.0. │ +│ See the LICENSE file in the project root for more information. │ +└────────────────────────────────────────────────────────────────────────┘ +*/ +using UnityEditor; + +namespace com.IvanMurzak.Unity.ImageLoader.Installer +{ + [InitializeOnLoad] + public static partial class Installer + { + public const string PackageId = "extensions.unity.imageloader"; + public const string Version = "7.0.2"; + + static Installer() + { +#if !IVAN_MURZAK_INSTALLER_PROJECT + AddScopedRegistryIfNeeded(ManifestPath); +#endif + } + } +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.cs.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.cs.meta new file mode 100644 index 0000000..f91c0b4 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d2a87a1f57606d145905f49a78a1b895 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/PackageExporter.cs b/Installer/Assets/com.IvanMurzak/Image Loader Installer/PackageExporter.cs new file mode 100644 index 0000000..61d87a0 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/PackageExporter.cs @@ -0,0 +1,36 @@ +/* +┌────────────────────────────────────────────────────────────────────────┐ +│ Author: Ivan Murzak (https://github.com/IvanMurzak) │ +│ Repository: GitHub (https://github.com/IvanMurzak/Unity-ImageLoader) │ +│ Copyright (c) 2025 Ivan Murzak │ +│ Licensed under the Apache License, Version 2.0. │ +│ See the LICENSE file in the project root for more information. │ +└────────────────────────────────────────────────────────────────────────┘ +*/ +using UnityEngine; +using UnityEditor; +using System.IO; + +namespace com.IvanMurzak.Unity.ImageLoader.Installer +{ + public static class PackageExporter + { + public static void ExportPackage() + { + var packagePath = "Assets/com.IvanMurzak/Image Loader Installer"; + var outputPath = "build/ImageLoader-Installer.unitypackage"; + + // Ensure build directory exists + var buildDir = Path.GetDirectoryName(outputPath); + if (!Directory.Exists(buildDir)) + { + Directory.CreateDirectory(buildDir); + } + + // Export the package + AssetDatabase.ExportPackage(packagePath, outputPath, ExportPackageOptions.Recurse); + + Debug.Log($"Package exported to: {outputPath}"); + } + } +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/PackageExporter.cs.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/PackageExporter.cs.meta new file mode 100644 index 0000000..8d71f9f --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/PackageExporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e5844f4459f0def459a6a1da59a7f130 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/README.pdf b/Installer/Assets/com.IvanMurzak/Image Loader Installer/README.pdf new file mode 100644 index 0000000..603d8c5 Binary files /dev/null and b/Installer/Assets/com.IvanMurzak/Image Loader Installer/README.pdf differ diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/README.pdf.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/README.pdf.meta new file mode 100644 index 0000000..972c165 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/README.pdf.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 91a9eb418a8c9e0498c0441bc580c4ab +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/SimpleJSON.cs b/Installer/Assets/com.IvanMurzak/Image Loader Installer/SimpleJSON.cs new file mode 100644 index 0000000..936bb88 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/SimpleJSON.cs @@ -0,0 +1,1434 @@ +/* * * * * + * A simple JSON Parser / builder + * ------------------------------ + * + * It mainly has been written as a simple JSON parser. It can build a JSON string + * from the node-tree, or generate a node tree from any valid JSON string. + * + * Written by Bunny83 + * 2012-06-09 + * + * Changelog now external. See Changelog.txt + * + * The MIT License (MIT) + * + * Copyright (c) 2012-2022 Markus Göbel (Bunny83) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * * * * */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace com.IvanMurzak.Unity.ImageLoader.Installer.SimpleJSON +{ + public enum JSONNodeType + { + Array = 1, + Object = 2, + String = 3, + Number = 4, + NullValue = 5, + Boolean = 6, + None = 7, + Custom = 0xFF, + } + public enum JSONTextMode + { + Compact, + Indent + } + + public abstract partial class JSONNode + { + #region Enumerators + public struct Enumerator + { + private enum Type { None, Array, Object } + private Type type; + private Dictionary.Enumerator m_Object; + private List.Enumerator m_Array; + public bool IsValid { get { return type != Type.None; } } + public Enumerator(List.Enumerator aArrayEnum) + { + type = Type.Array; + m_Object = default(Dictionary.Enumerator); + m_Array = aArrayEnum; + } + public Enumerator(Dictionary.Enumerator aDictEnum) + { + type = Type.Object; + m_Object = aDictEnum; + m_Array = default(List.Enumerator); + } + public KeyValuePair Current + { + get + { + if (type == Type.Array) + return new KeyValuePair(string.Empty, m_Array.Current); + else if (type == Type.Object) + return m_Object.Current; + return new KeyValuePair(string.Empty, null); + } + } + public bool MoveNext() + { + if (type == Type.Array) + return m_Array.MoveNext(); + else if (type == Type.Object) + return m_Object.MoveNext(); + return false; + } + } + public struct ValueEnumerator + { + private Enumerator m_Enumerator; + public ValueEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } + public ValueEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } + public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } + public JSONNode Current { get { return m_Enumerator.Current.Value; } } + public bool MoveNext() { return m_Enumerator.MoveNext(); } + public ValueEnumerator GetEnumerator() { return this; } + } + public struct KeyEnumerator + { + private Enumerator m_Enumerator; + public KeyEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } + public KeyEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } + public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } + public string Current { get { return m_Enumerator.Current.Key; } } + public bool MoveNext() { return m_Enumerator.MoveNext(); } + public KeyEnumerator GetEnumerator() { return this; } + } + + public class LinqEnumerator : IEnumerator>, IEnumerable> + { + private JSONNode m_Node; + private Enumerator m_Enumerator; + internal LinqEnumerator(JSONNode aNode) + { + m_Node = aNode; + if (m_Node != null) + m_Enumerator = m_Node.GetEnumerator(); + } + public KeyValuePair Current { get { return m_Enumerator.Current; } } + object IEnumerator.Current { get { return m_Enumerator.Current; } } + public bool MoveNext() { return m_Enumerator.MoveNext(); } + + public void Dispose() + { + m_Node = null; + m_Enumerator = new Enumerator(); + } + + public IEnumerator> GetEnumerator() + { + return new LinqEnumerator(m_Node); + } + + public void Reset() + { + if (m_Node != null) + m_Enumerator = m_Node.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new LinqEnumerator(m_Node); + } + } + + #endregion Enumerators + + #region common interface + + public static bool forceASCII = false; // Use Unicode by default + public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber + public static bool allowLineComments = true; // allow "//"-style comments at the end of a line + + public abstract JSONNodeType Tag { get; } + + public virtual JSONNode this[int aIndex] { get { return null; } set { } } + + public virtual JSONNode this[string aKey] { get { return null; } set { } } + + public virtual string Value { get { return ""; } set { } } + + public virtual int Count { get { return 0; } } + + public virtual bool IsNumber { get { return false; } } + public virtual bool IsString { get { return false; } } + public virtual bool IsBoolean { get { return false; } } + public virtual bool IsNull { get { return false; } } + public virtual bool IsArray { get { return false; } } + public virtual bool IsObject { get { return false; } } + + public virtual bool Inline { get { return false; } set { } } + + public virtual void Add(string aKey, JSONNode aItem) + { + } + public virtual void Add(JSONNode aItem) + { + Add("", aItem); + } + + public virtual JSONNode Remove(string aKey) + { + return null; + } + + public virtual JSONNode Remove(int aIndex) + { + return null; + } + + public virtual JSONNode Remove(JSONNode aNode) + { + return aNode; + } + public virtual void Clear() { } + + public virtual JSONNode Clone() + { + return null; + } + + public virtual IEnumerable Children + { + get + { + yield break; + } + } + + public IEnumerable DeepChildren + { + get + { + foreach (var C in Children) + foreach (var D in C.DeepChildren) + yield return D; + } + } + + public virtual bool HasKey(string aKey) + { + return false; + } + + public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) + { + return aDefault; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + WriteToStringBuilder(sb, 0, 0, JSONTextMode.Compact); + return sb.ToString(); + } + + public virtual string ToString(int aIndent) + { + StringBuilder sb = new StringBuilder(); + WriteToStringBuilder(sb, 0, aIndent, JSONTextMode.Indent); + return sb.ToString(); + } + internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); + + public abstract Enumerator GetEnumerator(); + public IEnumerable> Linq { get { return new LinqEnumerator(this); } } + public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } } + public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } } + + #endregion common interface + + #region typecasting properties + + + public virtual double AsDouble + { + get + { + double v = 0.0; + if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) + return v; + return 0.0; + } + set + { + Value = value.ToString(CultureInfo.InvariantCulture); + } + } + + public virtual int AsInt + { + get { return (int)AsDouble; } + set { AsDouble = value; } + } + + public virtual float AsFloat + { + get { return (float)AsDouble; } + set { AsDouble = value; } + } + + public virtual bool AsBool + { + get + { + bool v = false; + if (bool.TryParse(Value, out v)) + return v; + return !string.IsNullOrEmpty(Value); + } + set + { + Value = (value) ? "true" : "false"; + } + } + + public virtual long AsLong + { + get + { + long val = 0; + if (long.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out val)) + return val; + return 0L; + } + set + { + Value = value.ToString(CultureInfo.InvariantCulture); + } + } + + public virtual ulong AsULong + { + get + { + ulong val = 0; + if (ulong.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out val)) + return val; + return 0; + } + set + { + Value = value.ToString(CultureInfo.InvariantCulture); + } + } + + public virtual JSONArray AsArray + { + get + { + return this as JSONArray; + } + } + + public virtual JSONObject AsObject + { + get + { + return this as JSONObject; + } + } + + + #endregion typecasting properties + + #region operators + + public static implicit operator JSONNode(string s) + { + return (s == null) ? (JSONNode)JSONNull.CreateOrGet() : new JSONString(s); + } + public static implicit operator string(JSONNode d) + { + return (d == null) ? null : d.Value; + } + + public static implicit operator JSONNode(double n) + { + return new JSONNumber(n); + } + public static implicit operator double(JSONNode d) + { + return (d == null) ? 0 : d.AsDouble; + } + + public static implicit operator JSONNode(float n) + { + return new JSONNumber(n); + } + public static implicit operator float(JSONNode d) + { + return (d == null) ? 0 : d.AsFloat; + } + + public static implicit operator JSONNode(int n) + { + return new JSONNumber(n); + } + public static implicit operator int(JSONNode d) + { + return (d == null) ? 0 : d.AsInt; + } + + public static implicit operator JSONNode(long n) + { + if (longAsString) + return new JSONString(n.ToString(CultureInfo.InvariantCulture)); + return new JSONNumber(n); + } + public static implicit operator long(JSONNode d) + { + return (d == null) ? 0L : d.AsLong; + } + + public static implicit operator JSONNode(ulong n) + { + if (longAsString) + return new JSONString(n.ToString(CultureInfo.InvariantCulture)); + return new JSONNumber(n); + } + public static implicit operator ulong(JSONNode d) + { + return (d == null) ? 0 : d.AsULong; + } + + public static implicit operator JSONNode(bool b) + { + return new JSONBool(b); + } + public static implicit operator bool(JSONNode d) + { + return (d == null) ? false : d.AsBool; + } + + public static implicit operator JSONNode(KeyValuePair aKeyValue) + { + return aKeyValue.Value; + } + + public static bool operator ==(JSONNode a, object b) + { + if (ReferenceEquals(a, b)) + return true; + bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator; + bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator; + if (aIsNull && bIsNull) + return true; + return !aIsNull && a.Equals(b); + } + + public static bool operator !=(JSONNode a, object b) + { + return !(a == b); + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + #endregion operators + + [ThreadStatic] + private static StringBuilder m_EscapeBuilder; + internal static StringBuilder EscapeBuilder + { + get + { + if (m_EscapeBuilder == null) + m_EscapeBuilder = new StringBuilder(); + return m_EscapeBuilder; + } + } + internal static string Escape(string aText) + { + var sb = EscapeBuilder; + sb.Length = 0; + if (sb.Capacity < aText.Length + aText.Length / 10) + sb.Capacity = aText.Length + aText.Length / 10; + foreach (char c in aText) + { + switch (c) + { + case '\\': + sb.Append("\\\\"); + break; + case '\"': + sb.Append("\\\""); + break; + case '\n': + sb.Append("\\n"); + break; + case '\r': + sb.Append("\\r"); + break; + case '\t': + sb.Append("\\t"); + break; + case '\b': + sb.Append("\\b"); + break; + case '\f': + sb.Append("\\f"); + break; + default: + if (c < ' ' || (forceASCII && c > 127)) + { + ushort val = c; + sb.Append("\\u").Append(val.ToString("X4")); + } + else + sb.Append(c); + break; + } + } + string result = sb.ToString(); + sb.Length = 0; + return result; + } + + private static JSONNode ParseElement(string token, bool quoted) + { + if (quoted) + return token; + if (token.Length <= 5) + { + string tmp = token.ToLower(); + if (tmp == "false" || tmp == "true") + return tmp == "true"; + if (tmp == "null") + return JSONNull.CreateOrGet(); + } + double val; + if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out val)) + return val; + else + return token; + } + + public static JSONNode Parse(string aJSON) + { + Stack stack = new Stack(); + JSONNode ctx = null; + int i = 0; + StringBuilder Token = new StringBuilder(); + string TokenName = ""; + bool QuoteMode = false; + bool TokenIsQuoted = false; + bool HasNewlineChar = false; + while (i < aJSON.Length) + { + switch (aJSON[i]) + { + case '{': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + stack.Push(new JSONObject()); + if (ctx != null) + { + ctx.Add(TokenName, stack.Peek()); + } + TokenName = ""; + Token.Length = 0; + ctx = stack.Peek(); + HasNewlineChar = false; + break; + + case '[': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + + stack.Push(new JSONArray()); + if (ctx != null) + { + ctx.Add(TokenName, stack.Peek()); + } + TokenName = ""; + Token.Length = 0; + ctx = stack.Peek(); + HasNewlineChar = false; + break; + + case '}': + case ']': + if (QuoteMode) + { + + Token.Append(aJSON[i]); + break; + } + if (stack.Count == 0) + throw new Exception("JSON Parse: Too many closing brackets"); + + stack.Pop(); + if (Token.Length > 0 || TokenIsQuoted) + ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); + if (ctx != null) + ctx.Inline = !HasNewlineChar; + TokenIsQuoted = false; + TokenName = ""; + Token.Length = 0; + if (stack.Count > 0) + ctx = stack.Peek(); + break; + + case ':': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + TokenName = Token.ToString(); + Token.Length = 0; + TokenIsQuoted = false; + break; + + case '"': + QuoteMode ^= true; + TokenIsQuoted |= QuoteMode; + break; + + case ',': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + if (Token.Length > 0 || TokenIsQuoted) + ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); + TokenIsQuoted = false; + TokenName = ""; + Token.Length = 0; + TokenIsQuoted = false; + break; + + case '\r': + case '\n': + HasNewlineChar = true; + break; + + case ' ': + case '\t': + if (QuoteMode) + Token.Append(aJSON[i]); + break; + + case '\\': + ++i; + if (QuoteMode) + { + char C = aJSON[i]; + switch (C) + { + case 't': + Token.Append('\t'); + break; + case 'r': + Token.Append('\r'); + break; + case 'n': + Token.Append('\n'); + break; + case 'b': + Token.Append('\b'); + break; + case 'f': + Token.Append('\f'); + break; + case 'u': + { + string s = aJSON.Substring(i + 1, 4); + Token.Append((char)int.Parse( + s, + System.Globalization.NumberStyles.AllowHexSpecifier)); + i += 4; + break; + } + default: + Token.Append(C); + break; + } + } + break; + case '/': + if (allowLineComments && !QuoteMode && i + 1 < aJSON.Length && aJSON[i + 1] == '/') + { + while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') ; + break; + } + Token.Append(aJSON[i]); + break; + case '\uFEFF': // remove / ignore BOM (Byte Order Mark) + break; + + default: + Token.Append(aJSON[i]); + break; + } + ++i; + } + if (QuoteMode) + { + throw new Exception("JSON Parse: Quotation marks seems to be messed up."); + } + if (ctx == null) + return ParseElement(Token.ToString(), TokenIsQuoted); + return ctx; + } + + } + // End of JSONNode + + public partial class JSONArray : JSONNode + { + private List m_List = new List(); + private bool inline = false; + public override bool Inline + { + get { return inline; } + set { inline = value; } + } + + public override JSONNodeType Tag { get { return JSONNodeType.Array; } } + public override bool IsArray { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } + + public override JSONNode this[int aIndex] + { + get + { + if (aIndex < 0 || aIndex >= m_List.Count) + return new JSONLazyCreator(this); + return m_List[aIndex]; + } + set + { + if (value == null) + value = JSONNull.CreateOrGet(); + if (aIndex < 0 || aIndex >= m_List.Count) + m_List.Add(value); + else + m_List[aIndex] = value; + } + } + + public override JSONNode this[string aKey] + { + get { return new JSONLazyCreator(this); } + set + { + if (value == null) + value = JSONNull.CreateOrGet(); + m_List.Add(value); + } + } + + public override int Count + { + get { return m_List.Count; } + } + + public override void Add(string aKey, JSONNode aItem) + { + if (aItem == null) + aItem = JSONNull.CreateOrGet(); + m_List.Add(aItem); + } + + public override JSONNode Remove(int aIndex) + { + if (aIndex < 0 || aIndex >= m_List.Count) + return null; + JSONNode tmp = m_List[aIndex]; + m_List.RemoveAt(aIndex); + return tmp; + } + + public override JSONNode Remove(JSONNode aNode) + { + m_List.Remove(aNode); + return aNode; + } + + public override void Clear() + { + m_List.Clear(); + } + + public override JSONNode Clone() + { + var node = new JSONArray(); + node.m_List.Capacity = m_List.Capacity; + foreach (var n in m_List) + { + if (n != null) + node.Add(n.Clone()); + else + node.Add(null); + } + return node; + } + + public override IEnumerable Children + { + get + { + foreach (JSONNode N in m_List) + yield return N; + } + } + + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append('['); + int count = m_List.Count; + if (inline) + aMode = JSONTextMode.Compact; + for (int i = 0; i < count; i++) + { + if (i > 0) + aSB.Append(','); + if (aMode == JSONTextMode.Indent) + aSB.AppendLine(); + + if (aMode == JSONTextMode.Indent) + aSB.Append(' ', aIndent + aIndentInc); + m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); + } + if (aMode == JSONTextMode.Indent) + aSB.AppendLine().Append(' ', aIndent); + aSB.Append(']'); + } + } + // End of JSONArray + + public partial class JSONObject : JSONNode + { + private Dictionary m_Dict = new Dictionary(); + + private bool inline = false; + public override bool Inline + { + get { return inline; } + set { inline = value; } + } + + public override JSONNodeType Tag { get { return JSONNodeType.Object; } } + public override bool IsObject { get { return true; } } + + public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); } + + + public override JSONNode this[string aKey] + { + get + { + if (m_Dict.TryGetValue(aKey, out JSONNode outJsonNode)) + return outJsonNode; + else + return new JSONLazyCreator(this, aKey); + } + set + { + if (value == null) + value = JSONNull.CreateOrGet(); + if (m_Dict.ContainsKey(aKey)) + m_Dict[aKey] = value; + else + m_Dict.Add(aKey, value); + } + } + + public override JSONNode this[int aIndex] + { + get + { + if (aIndex < 0 || aIndex >= m_Dict.Count) + return null; + return m_Dict.ElementAt(aIndex).Value; + } + set + { + if (value == null) + value = JSONNull.CreateOrGet(); + if (aIndex < 0 || aIndex >= m_Dict.Count) + return; + string key = m_Dict.ElementAt(aIndex).Key; + m_Dict[key] = value; + } + } + + public override int Count + { + get { return m_Dict.Count; } + } + + public override void Add(string aKey, JSONNode aItem) + { + if (aItem == null) + aItem = JSONNull.CreateOrGet(); + + if (aKey != null) + { + if (m_Dict.ContainsKey(aKey)) + m_Dict[aKey] = aItem; + else + m_Dict.Add(aKey, aItem); + } + else + m_Dict.Add(Guid.NewGuid().ToString(), aItem); + } + + public override JSONNode Remove(string aKey) + { + if (!m_Dict.ContainsKey(aKey)) + return null; + JSONNode tmp = m_Dict[aKey]; + m_Dict.Remove(aKey); + return tmp; + } + + public override JSONNode Remove(int aIndex) + { + if (aIndex < 0 || aIndex >= m_Dict.Count) + return null; + var item = m_Dict.ElementAt(aIndex); + m_Dict.Remove(item.Key); + return item.Value; + } + + public override JSONNode Remove(JSONNode aNode) + { + try + { + var item = m_Dict.Where(k => k.Value == aNode).First(); + m_Dict.Remove(item.Key); + return aNode; + } + catch + { + return null; + } + } + + public override void Clear() + { + m_Dict.Clear(); + } + + public override JSONNode Clone() + { + var node = new JSONObject(); + foreach (var n in m_Dict) + { + node.Add(n.Key, n.Value.Clone()); + } + return node; + } + + public override bool HasKey(string aKey) + { + return m_Dict.ContainsKey(aKey); + } + + public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) + { + JSONNode res; + if (m_Dict.TryGetValue(aKey, out res)) + return res; + return aDefault; + } + + public override IEnumerable Children + { + get + { + foreach (KeyValuePair N in m_Dict) + yield return N.Value; + } + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append('{'); + bool first = true; + if (inline) + aMode = JSONTextMode.Compact; + foreach (var k in m_Dict) + { + if (!first) + aSB.Append(','); + first = false; + if (aMode == JSONTextMode.Indent) + aSB.AppendLine(); + if (aMode == JSONTextMode.Indent) + aSB.Append(' ', aIndent + aIndentInc); + aSB.Append('\"').Append(Escape(k.Key)).Append('\"'); + if (aMode == JSONTextMode.Compact) + aSB.Append(':'); + else + aSB.Append(" : "); + k.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); + } + if (aMode == JSONTextMode.Indent) + aSB.AppendLine().Append(' ', aIndent); + aSB.Append('}'); + } + + } + // End of JSONObject + + public partial class JSONString : JSONNode + { + private string m_Data; + + public override JSONNodeType Tag { get { return JSONNodeType.String; } } + public override bool IsString { get { return true; } } + + public override Enumerator GetEnumerator() { return new Enumerator(); } + + + public override string Value + { + get { return m_Data; } + set + { + m_Data = value; + } + } + + public JSONString(string aData) + { + m_Data = aData; + } + public override JSONNode Clone() + { + return new JSONString(m_Data); + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append('\"').Append(Escape(m_Data)).Append('\"'); + } + public override bool Equals(object obj) + { + if (base.Equals(obj)) + return true; + string s = obj as string; + if (s != null) + return m_Data == s; + JSONString s2 = obj as JSONString; + if (s2 != null) + return m_Data == s2.m_Data; + return false; + } + public override int GetHashCode() + { + return m_Data.GetHashCode(); + } + public override void Clear() + { + m_Data = ""; + } + } + // End of JSONString + + public partial class JSONNumber : JSONNode + { + private double m_Data; + + public override JSONNodeType Tag { get { return JSONNodeType.Number; } } + public override bool IsNumber { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public override string Value + { + get { return m_Data.ToString(CultureInfo.InvariantCulture); } + set + { + double v; + if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) + m_Data = v; + } + } + + public override double AsDouble + { + get { return m_Data; } + set { m_Data = value; } + } + public override long AsLong + { + get { return (long)m_Data; } + set { m_Data = value; } + } + public override ulong AsULong + { + get { return (ulong)m_Data; } + set { m_Data = value; } + } + + public JSONNumber(double aData) + { + m_Data = aData; + } + + public JSONNumber(string aData) + { + Value = aData; + } + + public override JSONNode Clone() + { + return new JSONNumber(m_Data); + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append(Value.ToString(CultureInfo.InvariantCulture)); + } + private static bool IsNumeric(object value) + { + return value is int || value is uint + || value is float || value is double + || value is decimal + || value is long || value is ulong + || value is short || value is ushort + || value is sbyte || value is byte; + } + public override bool Equals(object obj) + { + if (obj == null) + return false; + if (base.Equals(obj)) + return true; + JSONNumber s2 = obj as JSONNumber; + if (s2 != null) + return m_Data == s2.m_Data; + if (IsNumeric(obj)) + return Convert.ToDouble(obj) == m_Data; + return false; + } + public override int GetHashCode() + { + return m_Data.GetHashCode(); + } + public override void Clear() + { + m_Data = 0; + } + } + // End of JSONNumber + + public partial class JSONBool : JSONNode + { + private bool m_Data; + + public override JSONNodeType Tag { get { return JSONNodeType.Boolean; } } + public override bool IsBoolean { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public override string Value + { + get { return m_Data.ToString(); } + set + { + bool v; + if (bool.TryParse(value, out v)) + m_Data = v; + } + } + public override bool AsBool + { + get { return m_Data; } + set { m_Data = value; } + } + + public JSONBool(bool aData) + { + m_Data = aData; + } + + public JSONBool(string aData) + { + Value = aData; + } + + public override JSONNode Clone() + { + return new JSONBool(m_Data); + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append((m_Data) ? "true" : "false"); + } + public override bool Equals(object obj) + { + if (obj == null) + return false; + if (obj is bool) + return m_Data == (bool)obj; + return false; + } + public override int GetHashCode() + { + return m_Data.GetHashCode(); + } + public override void Clear() + { + m_Data = false; + } + } + // End of JSONBool + + public partial class JSONNull : JSONNode + { + static JSONNull m_StaticInstance = new JSONNull(); + public static bool reuseSameInstance = true; + public static JSONNull CreateOrGet() + { + if (reuseSameInstance) + return m_StaticInstance; + return new JSONNull(); + } + private JSONNull() { } + + public override JSONNodeType Tag { get { return JSONNodeType.NullValue; } } + public override bool IsNull { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public override string Value + { + get { return "null"; } + set { } + } + public override bool AsBool + { + get { return false; } + set { } + } + + public override JSONNode Clone() + { + return CreateOrGet(); + } + + public override bool Equals(object obj) + { + if (object.ReferenceEquals(this, obj)) + return true; + return (obj is JSONNull); + } + public override int GetHashCode() + { + return 0; + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append("null"); + } + } + // End of JSONNull + + internal partial class JSONLazyCreator : JSONNode + { + private JSONNode m_Node = null; + private string m_Key = null; + public override JSONNodeType Tag { get { return JSONNodeType.None; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public JSONLazyCreator(JSONNode aNode) + { + m_Node = aNode; + m_Key = null; + } + + public JSONLazyCreator(JSONNode aNode, string aKey) + { + m_Node = aNode; + m_Key = aKey; + } + + private T Set(T aVal) where T : JSONNode + { + if (m_Key == null) + m_Node.Add(aVal); + else + m_Node.Add(m_Key, aVal); + m_Node = null; // Be GC friendly. + return aVal; + } + + public override JSONNode this[int aIndex] + { + get { return new JSONLazyCreator(this); } + set { Set(new JSONArray()).Add(value); } + } + + public override JSONNode this[string aKey] + { + get { return new JSONLazyCreator(this, aKey); } + set { Set(new JSONObject()).Add(aKey, value); } + } + + public override void Add(JSONNode aItem) + { + Set(new JSONArray()).Add(aItem); + } + + public override void Add(string aKey, JSONNode aItem) + { + Set(new JSONObject()).Add(aKey, aItem); + } + + public static bool operator ==(JSONLazyCreator a, object b) + { + if (b == null) + return true; + return System.Object.ReferenceEquals(a, b); + } + + public static bool operator !=(JSONLazyCreator a, object b) + { + return !(a == b); + } + + public override bool Equals(object obj) + { + if (obj == null) + return true; + return System.Object.ReferenceEquals(this, obj); + } + + public override int GetHashCode() + { + return 0; + } + + public override int AsInt + { + get { Set(new JSONNumber(0)); return 0; } + set { Set(new JSONNumber(value)); } + } + + public override float AsFloat + { + get { Set(new JSONNumber(0.0f)); return 0.0f; } + set { Set(new JSONNumber(value)); } + } + + public override double AsDouble + { + get { Set(new JSONNumber(0.0)); return 0.0; } + set { Set(new JSONNumber(value)); } + } + + public override long AsLong + { + get + { + if (longAsString) + Set(new JSONString("0")); + else + Set(new JSONNumber(0.0)); + return 0L; + } + set + { + if (longAsString) + Set(new JSONString(value.ToString(CultureInfo.InvariantCulture))); + else + Set(new JSONNumber(value)); + } + } + + public override ulong AsULong + { + get + { + if (longAsString) + Set(new JSONString("0")); + else + Set(new JSONNumber(0.0)); + return 0L; + } + set + { + if (longAsString) + Set(new JSONString(value.ToString(CultureInfo.InvariantCulture))); + else + Set(new JSONNumber(value)); + } + } + + public override bool AsBool + { + get { Set(new JSONBool(false)); return false; } + set { Set(new JSONBool(value)); } + } + + public override JSONArray AsArray + { + get { return Set(new JSONArray()); } + } + + public override JSONObject AsObject + { + get { return Set(new JSONObject()); } + } + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append("null"); + } + } + // End of JSONLazyCreator + + public static class JSON + { + public static JSONNode Parse(string aJSON) + { + return JSONNode.Parse(aJSON); + } + } +} diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/SimpleJSON.cs.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/SimpleJSON.cs.meta new file mode 100644 index 0000000..f2bb12e --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/SimpleJSON.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1142caffb0ad0244adbf35e0c87c71c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests.meta new file mode 100644 index 0000000..ec880e1 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8fc55354727f7b743b22943a4b7435ea +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files.meta new file mode 100644 index 0000000..cc56033 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d31292e7f1ce4a643933c12676c15c7f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/Correct.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/Correct.meta new file mode 100644 index 0000000..b4841c2 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/Correct.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f13da4ae705efb74590909e95b4ffabb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/Correct/correct_manifest.json b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/Correct/correct_manifest.json new file mode 100644 index 0000000..2334066 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/Correct/correct_manifest.json @@ -0,0 +1,33 @@ +{ + "dependencies": { + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.test-framework": "1.1.33", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "org.nuget.microsoft.aspnetcore.signalr.client": "9.0.7", + "org.nuget.microsoft.aspnetcore.signalr.protocols.json": "9.0.7", + "org.nuget.microsoft.bcl.memory": "9.0.7", + "org.nuget.microsoft.codeanalysis.csharp": "4.13.0", + "org.nuget.microsoft.extensions.caching.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.dependencyinjection.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.hosting": "9.0.7", + "org.nuget.microsoft.extensions.hosting.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.logging.abstractions": "9.0.7", + "org.nuget.r3": "1.3.0", + "org.nuget.system.text.json": "9.0.7", + "PACKAGE_ID": "PACKAGE_VERSION" + }, + "scopedRegistries": [ + { + "name": "package.openupm.com", + "url": "https://package.openupm.com", + "scopes": [ + "com.ivanmurzak", + "extensions.unity", + "org.nuget.com.ivanmurzak", + "org.nuget.microsoft", + "org.nuget.system", + "org.nuget.r3" + ] + } + ] +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/Correct/correct_manifest.json.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/Correct/correct_manifest.json.meta new file mode 100644 index 0000000..7e5a0d7 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/Correct/correct_manifest.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: cbdb0933e6c74cb499b9639657cca035 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_1.json b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_1.json new file mode 100644 index 0000000..f94ad81 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_1.json @@ -0,0 +1,20 @@ +{ + "dependencies": { + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.test-framework": "1.1.33", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "org.nuget.microsoft.aspnetcore.signalr.client": "9.0.7", + "org.nuget.microsoft.aspnetcore.signalr.protocols.json": "9.0.7", + "org.nuget.microsoft.bcl.memory": "9.0.7", + "org.nuget.microsoft.codeanalysis.csharp": "4.13.0", + "org.nuget.microsoft.extensions.caching.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.dependencyinjection.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.hosting": "9.0.7", + "org.nuget.microsoft.extensions.hosting.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.logging.abstractions": "9.0.7", + "org.nuget.r3": "1.3.0", + "org.nuget.system.text.json": "9.0.7", + "PACKAGE_ID": "PACKAGE_VERSION" + }, + "scopedRegistries": [] +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_1.json.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_1.json.meta new file mode 100644 index 0000000..9ea8ccc --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_1.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e2ad12d2396fcbf4f9adeef63d494497 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_2.json b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_2.json new file mode 100644 index 0000000..f94ad81 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_2.json @@ -0,0 +1,20 @@ +{ + "dependencies": { + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.test-framework": "1.1.33", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "org.nuget.microsoft.aspnetcore.signalr.client": "9.0.7", + "org.nuget.microsoft.aspnetcore.signalr.protocols.json": "9.0.7", + "org.nuget.microsoft.bcl.memory": "9.0.7", + "org.nuget.microsoft.codeanalysis.csharp": "4.13.0", + "org.nuget.microsoft.extensions.caching.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.dependencyinjection.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.hosting": "9.0.7", + "org.nuget.microsoft.extensions.hosting.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.logging.abstractions": "9.0.7", + "org.nuget.r3": "1.3.0", + "org.nuget.system.text.json": "9.0.7", + "PACKAGE_ID": "PACKAGE_VERSION" + }, + "scopedRegistries": [] +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_2.json.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_2.json.meta new file mode 100644 index 0000000..812dc5d --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_empty_2.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 37546d3d6a7ab5f4d9625b7bf96a0514 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_gone.json b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_gone.json new file mode 100644 index 0000000..bcdb7a6 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_gone.json @@ -0,0 +1,19 @@ +{ + "dependencies": { + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.test-framework": "1.1.33", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "org.nuget.microsoft.aspnetcore.signalr.client": "9.0.7", + "org.nuget.microsoft.aspnetcore.signalr.protocols.json": "9.0.7", + "org.nuget.microsoft.bcl.memory": "9.0.7", + "org.nuget.microsoft.codeanalysis.csharp": "4.13.0", + "org.nuget.microsoft.extensions.caching.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.dependencyinjection.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.hosting": "9.0.7", + "org.nuget.microsoft.extensions.hosting.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.logging.abstractions": "9.0.7", + "org.nuget.r3": "1.3.0", + "org.nuget.system.text.json": "9.0.7", + "PACKAGE_ID": "PACKAGE_VERSION" + } +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_gone.json.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_gone.json.meta new file mode 100644 index 0000000..488d87e --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopedregistries_gone.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 32071bdc37d5dcd48b6fd92bfaabd14b +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_empty.json b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_empty.json new file mode 100644 index 0000000..c556ab4 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_empty.json @@ -0,0 +1,26 @@ +{ + "dependencies": { + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.test-framework": "1.1.33", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "org.nuget.microsoft.aspnetcore.signalr.client": "9.0.7", + "org.nuget.microsoft.aspnetcore.signalr.protocols.json": "9.0.7", + "org.nuget.microsoft.bcl.memory": "9.0.7", + "org.nuget.microsoft.codeanalysis.csharp": "4.13.0", + "org.nuget.microsoft.extensions.caching.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.dependencyinjection.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.hosting": "9.0.7", + "org.nuget.microsoft.extensions.hosting.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.logging.abstractions": "9.0.7", + "org.nuget.r3": "1.3.0", + "org.nuget.system.text.json": "9.0.7", + "PACKAGE_ID": "PACKAGE_VERSION" + }, + "scopedRegistries": [ + { + "name": "package.openupm.com", + "url": "https://package.openupm.com", + "scopes": [] + } + ] +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_empty.json.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_empty.json.meta new file mode 100644 index 0000000..3f270de --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_empty.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b68c119671aeb2e4ab69848b91754e1c +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_gone.json b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_gone.json new file mode 100644 index 0000000..5b0829d --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_gone.json @@ -0,0 +1,25 @@ +{ + "dependencies": { + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.test-framework": "1.1.33", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "org.nuget.microsoft.aspnetcore.signalr.client": "9.0.7", + "org.nuget.microsoft.aspnetcore.signalr.protocols.json": "9.0.7", + "org.nuget.microsoft.bcl.memory": "9.0.7", + "org.nuget.microsoft.codeanalysis.csharp": "4.13.0", + "org.nuget.microsoft.extensions.caching.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.dependencyinjection.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.hosting": "9.0.7", + "org.nuget.microsoft.extensions.hosting.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.logging.abstractions": "9.0.7", + "org.nuget.r3": "1.3.0", + "org.nuget.system.text.json": "9.0.7", + "PACKAGE_ID": "PACKAGE_VERSION" + }, + "scopedRegistries": [ + { + "name": "package.openupm.com", + "url": "https://package.openupm.com" + } + ] +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_gone.json.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_gone.json.meta new file mode 100644 index 0000000..2286a38 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_gone.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1131be0de99e30941a03c24b4d03bd1c +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_1.json b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_1.json new file mode 100644 index 0000000..ecd2092 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_1.json @@ -0,0 +1,32 @@ +{ + "dependencies": { + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.test-framework": "1.1.33", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "org.nuget.microsoft.aspnetcore.signalr.client": "9.0.7", + "org.nuget.microsoft.aspnetcore.signalr.protocols.json": "9.0.7", + "org.nuget.microsoft.bcl.memory": "9.0.7", + "org.nuget.microsoft.codeanalysis.csharp": "4.13.0", + "org.nuget.microsoft.extensions.caching.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.dependencyinjection.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.hosting": "9.0.7", + "org.nuget.microsoft.extensions.hosting.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.logging.abstractions": "9.0.7", + "org.nuget.r3": "1.3.0", + "org.nuget.system.text.json": "9.0.7", + "PACKAGE_ID": "PACKAGE_VERSION" + }, + "scopedRegistries": [ + { + "name": "package.openupm.com", + "url": "https://package.openupm.com", + "scopes": [ + "com.ivanmurzak", + "extensions.unity", + "org.nuget.com.ivanmurzak", + "org.nuget.microsoft", + "org.nuget.system" + ] + } + ] +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_1.json.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_1.json.meta new file mode 100644 index 0000000..30c61c3 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_1.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e80aa235740481e4b98d4b5f178b453d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_2.json b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_2.json new file mode 100644 index 0000000..dca084f --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_2.json @@ -0,0 +1,31 @@ +{ + "dependencies": { + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.test-framework": "1.1.33", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "org.nuget.microsoft.aspnetcore.signalr.client": "9.0.7", + "org.nuget.microsoft.aspnetcore.signalr.protocols.json": "9.0.7", + "org.nuget.microsoft.bcl.memory": "9.0.7", + "org.nuget.microsoft.codeanalysis.csharp": "4.13.0", + "org.nuget.microsoft.extensions.caching.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.dependencyinjection.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.hosting": "9.0.7", + "org.nuget.microsoft.extensions.hosting.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.logging.abstractions": "9.0.7", + "org.nuget.r3": "1.3.0", + "org.nuget.system.text.json": "9.0.7", + "PACKAGE_ID": "PACKAGE_VERSION" + }, + "scopedRegistries": [ + { + "name": "package.openupm.com", + "url": "https://package.openupm.com", + "scopes": [ + "com.ivanmurzak", + "extensions.unity", + "org.nuget.com.ivanmurzak", + "org.nuget.microsoft" + ] + } + ] +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_2.json.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_2.json.meta new file mode 100644 index 0000000..1ca70c7 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_2.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a28ae6e13530f0f41b6fa79c4bc8f414 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_3.json b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_3.json new file mode 100644 index 0000000..5a2d1a0 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_3.json @@ -0,0 +1,30 @@ +{ + "dependencies": { + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.test-framework": "1.1.33", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "org.nuget.microsoft.aspnetcore.signalr.client": "9.0.7", + "org.nuget.microsoft.aspnetcore.signalr.protocols.json": "9.0.7", + "org.nuget.microsoft.bcl.memory": "9.0.7", + "org.nuget.microsoft.codeanalysis.csharp": "4.13.0", + "org.nuget.microsoft.extensions.caching.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.dependencyinjection.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.hosting": "9.0.7", + "org.nuget.microsoft.extensions.hosting.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.logging.abstractions": "9.0.7", + "org.nuget.r3": "1.3.0", + "org.nuget.system.text.json": "9.0.7", + "PACKAGE_ID": "PACKAGE_VERSION" + }, + "scopedRegistries": [ + { + "name": "package.openupm.com", + "url": "https://package.openupm.com", + "scopes": [ + "com.ivanmurzak", + "extensions.unity", + "org.nuget.com.ivanmurzak" + ] + } + ] +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_3.json.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_3.json.meta new file mode 100644 index 0000000..2900fb2 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_3.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: cc111cf04b55b1a42a4418da1755c6c4 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_4.json b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_4.json new file mode 100644 index 0000000..1ce5ba4 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_4.json @@ -0,0 +1,29 @@ +{ + "dependencies": { + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.test-framework": "1.1.33", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "org.nuget.microsoft.aspnetcore.signalr.client": "9.0.7", + "org.nuget.microsoft.aspnetcore.signalr.protocols.json": "9.0.7", + "org.nuget.microsoft.bcl.memory": "9.0.7", + "org.nuget.microsoft.codeanalysis.csharp": "4.13.0", + "org.nuget.microsoft.extensions.caching.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.dependencyinjection.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.hosting": "9.0.7", + "org.nuget.microsoft.extensions.hosting.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.logging.abstractions": "9.0.7", + "org.nuget.r3": "1.3.0", + "org.nuget.system.text.json": "9.0.7", + "PACKAGE_ID": "PACKAGE_VERSION" + }, + "scopedRegistries": [ + { + "name": "package.openupm.com", + "url": "https://package.openupm.com", + "scopes": [ + "com.ivanmurzak", + "extensions.unity" + ] + } + ] +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_4.json.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_4.json.meta new file mode 100644 index 0000000..e4a09ca --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_4.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 68a401803efe2bc43b6f229f7c57e9db +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_5.json b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_5.json new file mode 100644 index 0000000..18b1858 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_5.json @@ -0,0 +1,28 @@ +{ + "dependencies": { + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.test-framework": "1.1.33", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "org.nuget.microsoft.aspnetcore.signalr.client": "9.0.7", + "org.nuget.microsoft.aspnetcore.signalr.protocols.json": "9.0.7", + "org.nuget.microsoft.bcl.memory": "9.0.7", + "org.nuget.microsoft.codeanalysis.csharp": "4.13.0", + "org.nuget.microsoft.extensions.caching.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.dependencyinjection.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.hosting": "9.0.7", + "org.nuget.microsoft.extensions.hosting.abstractions": "9.0.7", + "org.nuget.microsoft.extensions.logging.abstractions": "9.0.7", + "org.nuget.r3": "1.3.0", + "org.nuget.system.text.json": "9.0.7", + "PACKAGE_ID": "PACKAGE_VERSION" + }, + "scopedRegistries": [ + { + "name": "package.openupm.com", + "url": "https://package.openupm.com", + "scopes": [ + "com.ivanmurzak" + ] + } + ] +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_5.json.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_5.json.meta new file mode 100644 index 0000000..8a3dd4b --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/Files/scopes_partial_5.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0fc316ae5a9cfac4cb34c84ec91e0774 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/ManifestInstallerTests.cs b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/ManifestInstallerTests.cs new file mode 100644 index 0000000..878fb70 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/ManifestInstallerTests.cs @@ -0,0 +1,71 @@ +/* +┌────────────────────────────────────────────────────────────────────────┐ +│ Author: Ivan Murzak (https://github.com/IvanMurzak) │ +│ Repository: GitHub (https://github.com/IvanMurzak/Unity-ImageLoader) │ +│ Copyright (c) 2025 Ivan Murzak │ +│ Licensed under the Apache License, Version 2.0. │ +│ See the LICENSE file in the project root for more information. │ +└────────────────────────────────────────────────────────────────────────┘ +*/ +using System.IO; +using NUnit.Framework; +using UnityEngine; + +namespace com.IvanMurzak.Unity.ImageLoader.Installer.Tests +{ + public class ManifestInstallerTests + { + const string PackageIdTag = "PACKAGE_ID"; + const string PackageVersionTag = "PACKAGE_VERSION"; + const string FilesRoot = "Assets/com.IvanMurzak/Image Loader Installer/Tests/Files"; + const string FilesCopyRoot = "Temp/com.IvanMurzak/Image Loader Installer/Tests/Files"; + static string CorrectManifestPath => $"{FilesRoot}/Correct/correct_manifest.json"; + + [SetUp] + public void SetUp() + { + Debug.Log($"[{nameof(ManifestInstallerTests)}] SetUp"); + Directory.CreateDirectory(FilesCopyRoot); + } + + [TearDown] + public void TearDown() + { + Debug.Log($"[{nameof(ManifestInstallerTests)}] TearDown"); + + // var files = Directory.GetFiles(FilesCopyRoot, "*.json", SearchOption.TopDirectoryOnly); + // foreach (var file in files) + // File.Delete(file); + } + + [Test] + public void All() + { + var files = Directory.GetFiles(FilesRoot, "*.json", SearchOption.TopDirectoryOnly); + var correctManifest = File.ReadAllText(CorrectManifestPath) + .Replace(PackageVersionTag, Installer.Version) + .Replace(PackageIdTag, Installer.PackageId); + + foreach (var file in files) + { + Debug.Log($"Found JSON file: {file}"); + + // Copy the file + var fileCopy = Path.Combine(FilesCopyRoot, Path.GetFileName(file)); + File.Copy(file, fileCopy, overwrite: true); + + // Arrange + File.WriteAllText(fileCopy, File.ReadAllText(fileCopy) + .Replace(PackageVersionTag, Installer.Version) + .Replace(PackageIdTag, Installer.PackageId)); + + // Act + Installer.AddScopedRegistryIfNeeded(fileCopy); + + // Assert + var modifiedManifest = File.ReadAllText(fileCopy); + Assert.AreEqual(correctManifest, modifiedManifest, $"Modified manifest from {file} does not match the correct manifest."); + } + } + } +} diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/ManifestInstallerTests.cs.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/ManifestInstallerTests.cs.meta new file mode 100644 index 0000000..c6cc9fa --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/ManifestInstallerTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f2b892b3f51554a499a215bf02a07d75 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/com.IvanMurzak.Unity.ImageLoader.Installer.Tests.asmdef b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/com.IvanMurzak.Unity.ImageLoader.Installer.Tests.asmdef new file mode 100644 index 0000000..9cf1ade --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/com.IvanMurzak.Unity.ImageLoader.Installer.Tests.asmdef @@ -0,0 +1,21 @@ +{ + "name": "com.IvanMurzak.Unity.ImageLoader.Installer.Tests", + "references": [ + "com.IvanMurzak.Unity.ImageLoader.Installer" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/com.IvanMurzak.Unity.ImageLoader.Installer.Tests.asmdef.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/com.IvanMurzak.Unity.ImageLoader.Installer.Tests.asmdef.meta new file mode 100644 index 0000000..ccc2876 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/Tests/com.IvanMurzak.Unity.ImageLoader.Installer.Tests.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 452d4fe10325a0e4fa0c5b27dcfb4ea3 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/com.IvanMurzak.Unity.ImageLoader.Installer.asmdef b/Installer/Assets/com.IvanMurzak/Image Loader Installer/com.IvanMurzak.Unity.ImageLoader.Installer.asmdef new file mode 100644 index 0000000..cea4a4b --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/com.IvanMurzak.Unity.ImageLoader.Installer.asmdef @@ -0,0 +1,15 @@ +{ + "name": "com.IvanMurzak.Unity.ImageLoader.Installer", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": false, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Installer/Assets/com.IvanMurzak/Image Loader Installer/com.IvanMurzak.Unity.ImageLoader.Installer.asmdef.meta b/Installer/Assets/com.IvanMurzak/Image Loader Installer/com.IvanMurzak.Unity.ImageLoader.Installer.asmdef.meta new file mode 100644 index 0000000..940dc63 --- /dev/null +++ b/Installer/Assets/com.IvanMurzak/Image Loader Installer/com.IvanMurzak.Unity.ImageLoader.Installer.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: af29ddb021ab2a44bbf9b8e8ba364bdc +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/CHANGELOG.md b/Installer/Packages/com.unity.asset-store-tools/CHANGELOG.md new file mode 100644 index 0000000..d51cbe8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/CHANGELOG.md @@ -0,0 +1,324 @@ +# Changelog +All notable changes to this package will be documented in this file. + +## [12.0.1] - 2025-01-16 + +### Preview Generator changes +- Updated generated preview collection UI to display the asset extension +- Fixed an issue with some prefab and model asset types not generating previews +- Fixed an error that could occur when changing scenes after deleting a preview source asset + +## [12.0.0] - 2025-01-13 + +### General changes +- The code comprising the Asset Store Publishing Tools has been refactored. +- Added dependency on Newtonsoft Json + +### Uploader changes +- Updated window to retain its state if closed unless a domain reload occurs +- Added option to generate higher resolution asset previews when exporting +- Fixed a rare issue where authentication would fail +- Minor UI tweaks + +### Validator changes +- Added validation tests for: + - Package naming + - Project Template assets +- Updated the Type Namespace validation test to check for Unity top level namespaces + +### Exporter changes +- Updated how asset previews are generated/included for the package that is being exported + +### Preview Generator +- Added a Preview Generator window that can be used to pre-generate and inspect package previews before exporting + +## [11.4.4] - 2024-11-29 + +### Validator Changes +- The validator UI window description section can now be expanded or shrunk to take up less screen space +- Updated severity of the Model Importer Logs validation test + +### Exporter Changes +- Updated exporter to always exclude hidden files and folders beginning with the dot symbol (e.g.: .hiddenFolder/ or .hiddenfile.txt) +- Updated exporter to explicitly exclude extended attribute files when exporting packages on macOS systems + +### Other +- Moved the Asset Store Tools toolbar items into the Tools section +- Fixed several window-related compilation warnings when using Unity 6 versions of the Editor + +## [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/Installer/Packages/com.unity.asset-store-tools/CHANGELOG.md.meta b/Installer/Packages/com.unity.asset-store-tools/CHANGELOG.md.meta new file mode 100644 index 0000000..0db1ee3 --- /dev/null +++ b/Installer/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/Installer/Packages/com.unity.asset-store-tools/Editor.meta b/Installer/Packages/com.unity.asset-store-tools/Editor.meta new file mode 100644 index 0000000..8521ad6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 166da5c6fc70e814a8262463903b2714 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api.meta new file mode 100644 index 0000000..0cf8e3f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d48a2325d352e7a4cae56d3f8eeaab2d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions.meta new file mode 100644 index 0000000..80ccfb1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 25799fb31cd475347af7f5442c231797 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs new file mode 100644 index 0000000..84d1fa8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs @@ -0,0 +1,48 @@ +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; + +namespace AssetStoreTools.Api +{ + internal abstract class AuthenticationBase : IAuthenticationType + { + protected Uri LoginUrl = ApiUtility.CreateUri(Constants.Api.AuthenticateUrl, true); + protected FormUrlEncodedContent AuthenticationContent; + + protected FormUrlEncodedContent GetAuthenticationContent(params KeyValuePair[] content) + { + var baseContent = Constants.Api.DefaultAssetStoreQuery(); + + try { baseContent.Add("license_hash", ApiUtility.GetLicenseHash()); } catch { ASDebug.LogWarning("Could not retrieve license hash"); } + try { baseContent.Add("hardware_hash", ApiUtility.GetHardwareHash()); } catch { ASDebug.LogWarning("Could not retrieve hardware hash"); } + + foreach (var extraContent in content) + { + baseContent.Add(extraContent.Key, extraContent.Value); + } + + return new FormUrlEncodedContent(baseContent); + } + + protected AuthenticationResponse ParseResponse(HttpResponseMessage response) + { + try + { + response.EnsureSuccessStatusCode(); + var responseString = response.Content.ReadAsStringAsync().Result; + return new AuthenticationResponse(responseString); + } + catch (HttpRequestException e) + { + return new AuthenticationResponse(response.StatusCode, e) { Success = false }; + } + } + + public abstract Task Authenticate(IAssetStoreClient client, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs.meta new file mode 100644 index 0000000..6b0cd8f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f677e03f1be1048439a1fa5e7a0a37b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs new file mode 100644 index 0000000..86db5cd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs @@ -0,0 +1,21 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Api.Responses; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal interface IAssetStoreApi + { + Task GetLatestAssetStoreToolsVersion(CancellationToken cancellationToken = default); + Task Authenticate(IAuthenticationType authenticationType, CancellationToken cancellationToken = default); + void Deauthenticate(); + Task GetPackages(CancellationToken cancellationToken = default); + Task GetCategories(CancellationToken cancellationToken = default); + Task GetPackageThumbnail(Package package, CancellationToken cancellationToken = default); + Task RefreshPackageMetadata(Package package, CancellationToken cancellationToken = default); + Task GetPackageUploadedVersions(Package package, CancellationToken cancellationToken = default); + Task UploadPackage(IPackageUploader uploader, IProgress progress = null, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs.meta new file mode 100644 index 0000000..91ac851 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e616488c25d278741bb0d08168219309 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs new file mode 100644 index 0000000..f6a6d3e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs @@ -0,0 +1,18 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal interface IAssetStoreClient + { + void SetSessionId(string sessionId); + void ClearSessionId(); + + Task Get(Uri uri, CancellationToken cancellationToken = default); + Task Post(Uri uri, HttpContent content, CancellationToken cancellationToken = default); + Task Put(Uri uri, HttpContent content, CancellationToken cancellationToken = default); + Task Send(HttpRequestMessage request, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs.meta new file mode 100644 index 0000000..6671aaf --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b2bbadec62178cc4189e605367b219e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs new file mode 100644 index 0000000..8fa5c78 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs @@ -0,0 +1,11 @@ +using AssetStoreTools.Api.Responses; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal interface IAuthenticationType + { + Task Authenticate(IAssetStoreClient client, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs.meta new file mode 100644 index 0000000..916782f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0000dcd6975bc8e4abc546a19f194040 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs new file mode 100644 index 0000000..ef7c89f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs @@ -0,0 +1,12 @@ +using AssetStoreTools.Api.Responses; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal interface IPackageUploader + { + Task Upload(IAssetStoreClient client, IProgress progress, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs.meta new file mode 100644 index 0000000..b4eb3db --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0fc6c47b1c0a65540a40efbf1491193b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs new file mode 100644 index 0000000..e26bfc1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs @@ -0,0 +1,59 @@ +using AssetStoreTools.Api.Responses; +using System; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal abstract class PackageUploaderBase : IPackageUploader + { + protected const int UploadChunkSizeBytes = 32768; + protected const int UploadResponseTimeoutMs = 10000; + + protected abstract void ValidateSettings(); + public abstract Task Upload(IAssetStoreClient client, IProgress progress = null, CancellationToken cancellationToken = default); + + protected void EnsureSuccessResponse(HttpResponseMessage response) + { + try + { + response.EnsureSuccessStatusCode(); + } + catch + { + throw new Exception(response.Content.ReadAsStringAsync().Result); + } + } + + protected void WaitForUploadCompletion(Task response, FileStream requestFileStream, IProgress progress, CancellationToken cancellationToken) + { + // Progress tracking + int updateIntervalMs = 100; + bool allBytesSent = false; + DateTime timeOfCompletion = default; + + while (!response.IsCompleted) + { + float uploadProgress = (float)requestFileStream.Position / requestFileStream.Length * 100; + progress?.Report(uploadProgress); + Thread.Sleep(updateIntervalMs); + + // A timeout for rare cases, when package uploading reaches 100%, but Put 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) + { + throw new TimeoutException(); + } + } + } + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs.meta new file mode 100644 index 0000000..4764456 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2718ddd16e425ba4a82ab973724bcff7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs new file mode 100644 index 0000000..545d6d4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs @@ -0,0 +1,76 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditorInternal; + +namespace AssetStoreTools.Api +{ + internal class ApiUtility + { + public static Uri CreateUri(string url, bool includeDefaultAssetStoreQuery) => CreateUri(url, null, includeDefaultAssetStoreQuery); + public static Uri CreateUri(string url, IDictionary queryParameters, bool includeDefaultAssetStoreQuery) + { + IDictionary fullQueryParameters = includeDefaultAssetStoreQuery ? + Constants.Api.DefaultAssetStoreQuery() : new Dictionary(); + + if (queryParameters != null && queryParameters.Count > 0) + { + foreach (var kvp in queryParameters) + fullQueryParameters.Add(kvp); + } + + var builder = new UriBuilder(url); + if (fullQueryParameters.Count == 0) + return builder.Uri; + + var fullQueryParameterString = string.Empty; + foreach (var queryParam in fullQueryParameters) + { + var escapedValue = queryParam.Value != null ? Uri.EscapeDataString(queryParam.Value) : string.Empty; + fullQueryParameterString += $"{queryParam.Key}={escapedValue}&"; + } + fullQueryParameterString = fullQueryParameterString.Remove(fullQueryParameterString.Length - 1); + + builder.Query = fullQueryParameterString; + return builder.Uri; + } + + public static List CombinePackageData(List mainPackageData, List extraPackageData, List categoryData) + { + foreach (var package in mainPackageData) + { + var extraData = extraPackageData.FirstOrDefault(x => package.PackageId == x.PackageId); + + if (extraData == null) + { + ASDebug.LogWarning($"Could not find extra data for Package {package.PackageId}"); + continue; + } + + var categoryId = extraData.CategoryId; + var category = categoryData.FirstOrDefault(x => x.Id.ToString() == categoryId); + if (category != null) + package.Category = category.Name; + else + package.Category = "Unknown"; + + package.Modified = extraData.Modified; + package.Size = extraData.Size; + } + + return mainPackageData; + } + + public static string GetLicenseHash() + { + return InternalEditorUtility.GetAuthToken().Substring(0, 40); + } + + public static string GetHardwareHash() + { + return InternalEditorUtility.GetAuthToken().Substring(40, 40); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs.meta new file mode 100644 index 0000000..5291958 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5becec0b3c0ba274fb0b01544e63b6c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs new file mode 100644 index 0000000..a8530dc --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs @@ -0,0 +1,268 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Api.Responses; +using Newtonsoft.Json.Linq; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class AssetStoreApi : IAssetStoreApi + { + private IAssetStoreClient _client; + + public AssetStoreApi(IAssetStoreClient client) + { + _client = client; + } + + public async Task GetLatestAssetStoreToolsVersion(CancellationToken cancellationToken = default) + { + try + { + var uri = ApiUtility.CreateUri(Constants.Api.AssetStoreToolsLatestVersionUrl, false); + var response = await _client.Get(uri, cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + var responseStr = response.Content.ReadAsStringAsync().Result; + return new AssetStoreToolsVersionResponse(responseStr); + } + catch (OperationCanceledException e) + { + return new AssetStoreToolsVersionResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new AssetStoreToolsVersionResponse() { Success = false, Exception = e }; + } + } + + public async Task Authenticate(IAuthenticationType authenticationType, CancellationToken cancellationToken = default) + { + try + { + var loginResponse = await authenticationType.Authenticate(_client, cancellationToken); + if (loginResponse.Success) + { + _client.SetSessionId(loginResponse.User.SessionId); + } + + return loginResponse; + } + catch (OperationCanceledException e) + { + return new AuthenticationResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new AuthenticationResponse() { Success = false, Exception = e }; + } + } + + public void Deauthenticate() + { + _client.ClearSessionId(); + } + + public async Task GetPackages(CancellationToken cancellationToken = default) + { + try + { + var mainDataResponse = await GetPackageDataMain(cancellationToken); + if (!mainDataResponse.Success) + throw mainDataResponse.Exception; + var additionalDataResponse = await GetPackageDataExtra(cancellationToken); + if (!additionalDataResponse.Success) + throw additionalDataResponse.Exception; + var categoryDataResponse = await GetCategories(cancellationToken); + if (!categoryDataResponse.Success) + throw categoryDataResponse.Exception; + + var joinedData = ApiUtility.CombinePackageData(mainDataResponse.Packages, additionalDataResponse.Packages, categoryDataResponse.Categories); + return new PackagesDataResponse() { Success = true, Packages = joinedData }; + } + catch (OperationCanceledException e) + { + return new PackagesDataResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackagesDataResponse() { Success = false, Exception = e }; + } + } + + private async Task GetPackageDataMain(CancellationToken cancellationToken) + { + try + { + var uri = ApiUtility.CreateUri(Constants.Api.GetPackagesUrl, true); + var response = await _client.Get(uri, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + + var responseStr = response.Content.ReadAsStringAsync().Result; + return new PackagesDataResponse(responseStr); + } + catch (OperationCanceledException e) + { + return new PackagesDataResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackagesDataResponse() { Success = false, Exception = e }; + } + } + + private async Task GetPackageDataExtra(CancellationToken cancellationToken) + { + try + { + var uri = ApiUtility.CreateUri(Constants.Api.GetPackagesAdditionalDataUrl, true); + var response = await _client.Get(uri, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + + var responseStr = response.Content.ReadAsStringAsync().Result; + return new PackagesAdditionalDataResponse(responseStr); + } + catch (OperationCanceledException e) + { + return new PackagesAdditionalDataResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackagesAdditionalDataResponse() { Success = false, Exception = e }; + } + } + + public async Task GetCategories(CancellationToken cancellationToken) + { + try + { + var uri = ApiUtility.CreateUri(Constants.Api.GetCategoriesUrl, true); + var response = await _client.Get(uri, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + + var responseStr = response.Content.ReadAsStringAsync().Result; + return new CategoryDataResponse(responseStr); + } + catch (OperationCanceledException e) + { + return new CategoryDataResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new CategoryDataResponse() { Success = false, Exception = e }; + } + } + + public async Task GetPackageThumbnail(Package package, CancellationToken cancellationToken = default) + { + try + { + if (string.IsNullOrEmpty(package.IconUrl)) + throw new Exception($"Could not retrieve thumbnail for package {package.PackageId} - icon url is null"); + + var response = await _client.Get(new Uri(package.IconUrl), cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + + var responseBytes = response.Content.ReadAsByteArrayAsync().Result; + return new PackageThumbnailResponse(responseBytes); + } + catch (OperationCanceledException e) + { + return new PackageThumbnailResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackageThumbnailResponse() { Success = false, Exception = e }; + } + } + + public async Task RefreshPackageMetadata(Package package, CancellationToken cancellationToken = default) + { + try + { + var refreshedPackage = JObject.FromObject(package).DeepClone().ToObject(); + + var packagesResponse = await GetPackageDataExtra(cancellationToken); + if (!packagesResponse.Success) + throw packagesResponse.Exception; + + // Find the updated package data in the latest data json + var packageRefreshSource = packagesResponse.Packages.FirstOrDefault(x => x.PackageId == refreshedPackage.PackageId); + if (packageRefreshSource == null) + return new RefreshedPackageDataResponse() { Success = false, Exception = new MissingMemberException($"Unable to find downloaded package data for package id {package.PackageId}") }; + + // Retrieve the category map + var categoryData = await GetCategories(cancellationToken); + if (!categoryData.Success) + return new RefreshedPackageDataResponse() { Success = false, Exception = packagesResponse.Exception }; + + // Update the package data + refreshedPackage.Name = packageRefreshSource.Name; + refreshedPackage.Status = packageRefreshSource.Status; + var newCategory = categoryData.Categories.FirstOrDefault(x => x.Id.ToString() == packageRefreshSource.CategoryId); + refreshedPackage.Category = newCategory != null ? newCategory.Name : "Unknown"; + refreshedPackage.Modified = packageRefreshSource.Modified; + refreshedPackage.Size = packageRefreshSource.Size; + + return new RefreshedPackageDataResponse() { Success = true, Package = refreshedPackage }; + } + catch (OperationCanceledException) + { + return new RefreshedPackageDataResponse() { Success = false, Cancelled = true }; + } + catch (Exception e) + { + return new RefreshedPackageDataResponse() { Success = false, Exception = e }; + } + } + + public async Task GetPackageUploadedVersions(Package package, CancellationToken cancellationToken = default) + { + try + { + var uri = ApiUtility.CreateUri(Constants.Api.GetPackageUploadedVersionsUrl(package.PackageId, package.VersionId), true); + var response = await _client.Get(uri, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + + var responseStr = response.Content.ReadAsStringAsync().Result; + return new PackageUploadedUnityVersionDataResponse(responseStr); + } + catch (OperationCanceledException e) + { + return new PackageUploadedUnityVersionDataResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackageUploadedUnityVersionDataResponse() { Success = false, Exception = e }; + } + } + + public async Task UploadPackage(IPackageUploader uploader, IProgress progress = null, CancellationToken cancellationToken = default) + { + try + { + return await uploader.Upload(_client, progress, cancellationToken); + } + catch (OperationCanceledException e) + { + return new PackageUploadResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackageUploadResponse() { Success = false, Exception = e }; + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs.meta new file mode 100644 index 0000000..d248bfe --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/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/Installer/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs new file mode 100644 index 0000000..f71bff8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs @@ -0,0 +1,55 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class AssetStoreClient : IAssetStoreClient + { + private HttpClient _httpClient; + + public AssetStoreClient() + { + ServicePointManager.DefaultConnectionLimit = 500; + _httpClient = new HttpClient(); + _httpClient.DefaultRequestHeaders.ConnectionClose = false; + _httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); + _httpClient.Timeout = TimeSpan.FromMinutes(1320); + } + + public void SetSessionId(string sessionId) + { + ClearSessionId(); + + if (!string.IsNullOrEmpty(sessionId)) + _httpClient.DefaultRequestHeaders.Add("X-Unity-Session", sessionId); + } + + public void ClearSessionId() + { + _httpClient.DefaultRequestHeaders.Remove("X-Unity-Session"); + } + + public Task Get(Uri uri, CancellationToken cancellationToken = default) + { + return _httpClient.GetAsync(uri, cancellationToken); + } + + public Task Post(Uri uri, HttpContent content, CancellationToken cancellationToken = default) + { + return _httpClient.PostAsync(uri, content, cancellationToken); + } + + public Task Put(Uri uri, HttpContent content, CancellationToken cancellationToken = default) + { + return _httpClient.PutAsync(uri, content, cancellationToken); + } + + public Task Send(HttpRequestMessage request, CancellationToken cancellationToken = default) + { + return _httpClient.SendAsync(request, cancellationToken); + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs.meta new file mode 100644 index 0000000..097e6a3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 80b4527c908161a4b9f06dc393b502f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs new file mode 100644 index 0000000..5a1bd5e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs @@ -0,0 +1,25 @@ +using AssetStoreTools.Api.Responses; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class CloudTokenAuthentication : AuthenticationBase + { + public CloudTokenAuthentication(string cloudToken) + { + AuthenticationContent = GetAuthenticationContent( + new KeyValuePair("user_access_token", cloudToken) + ); + } + + public override async Task Authenticate(IAssetStoreClient client, CancellationToken cancellationToken) + { + var result = await client.Post(LoginUrl, AuthenticationContent, cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + + return ParseResponse(result); + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs.meta new file mode 100644 index 0000000..4d1495a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 99f1baec74f26a34bb972b19c92d523f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs new file mode 100644 index 0000000..2c51505 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs @@ -0,0 +1,26 @@ +using AssetStoreTools.Api.Responses; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class CredentialsAuthentication : AuthenticationBase + { + public CredentialsAuthentication(string email, string password) + { + AuthenticationContent = GetAuthenticationContent( + new KeyValuePair("user", email), + new KeyValuePair("pass", password) + ); + } + + public override async Task Authenticate(IAssetStoreClient client, CancellationToken cancellationToken) + { + var result = await client.Post(LoginUrl, AuthenticationContent, cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + + return ParseResponse(result); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs.meta new file mode 100644 index 0000000..35404d1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 353e556b63fd441428f387bc85aa612c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models.meta new file mode 100644 index 0000000..bfcab0b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1f83e4b5507886f4b873c22c146b8f6a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs new file mode 100644 index 0000000..2cb1298 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs @@ -0,0 +1,58 @@ +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Models +{ + internal class Category + { + public int Id { get; set; } + public string Name { get; set; } + public string Status { get; set; } + + public class AssetStoreCategoryResolver : DefaultContractResolver + { + private Dictionary _propertyConversions; + + public AssetStoreCategoryResolver() + { + _propertyConversions = new Dictionary() + { + { nameof(Category.Name), "assetstore_name" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversions.ContainsKey(propertyName)) + return _propertyConversions[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + + public class CachedCategoryResolver : DefaultContractResolver + { + private static CachedCategoryResolver _instance; + public static CachedCategoryResolver Instance => _instance ?? (_instance = new CachedCategoryResolver()); + + private Dictionary _propertyConversion; + + private CachedCategoryResolver() + { + this.NamingStrategy = new SnakeCaseNamingStrategy(); + _propertyConversion = new Dictionary() + { + { nameof(Category.Name), "name" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversion.ContainsKey(propertyName)) + return _propertyConversion[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs.meta new file mode 100644 index 0000000..cbdb96e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5897866bc65f5834dab1f17371daada7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs new file mode 100644 index 0000000..442c919 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs @@ -0,0 +1,80 @@ +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Models +{ + internal class Package + { + public string PackageId { get; set; } + public string VersionId { get; set; } + public string Name { get; set; } + public string Status { get; set; } + public string Category { get; set; } + public bool IsCompleteProject { get; set; } + public string RootGuid { get; set; } + public string RootPath { get; set; } + public string ProjectPath { get; set; } + public string Modified { get; set; } + public string Size { get; set; } + public string IconUrl { get; set; } + + public class AssetStorePackageResolver : DefaultContractResolver + { + private static AssetStorePackageResolver _instance; + public static AssetStorePackageResolver Instance => _instance ?? (_instance = new AssetStorePackageResolver()); + + private Dictionary _propertyConversions; + + private AssetStorePackageResolver() + { + _propertyConversions = new Dictionary() + { + { nameof(Package.VersionId), "id" }, + { nameof(Package.IsCompleteProject), "is_complete_project" }, + { nameof(Package.RootGuid), "root_guid" }, + { nameof(Package.RootPath), "root_path" }, + { nameof(Package.ProjectPath), "project_path" }, + { nameof(Package.IconUrl), "icon_url" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversions.ContainsKey(propertyName)) + return _propertyConversions[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + + public class CachedPackageResolver : DefaultContractResolver + { + private static CachedPackageResolver _instance; + public static CachedPackageResolver Instance => _instance ?? (_instance = new CachedPackageResolver()); + + private Dictionary _propertyConversion; + + private CachedPackageResolver() + { + this.NamingStrategy = new SnakeCaseNamingStrategy(); + _propertyConversion = new Dictionary() + { + { nameof(Package.PackageId), "package_id" }, + { nameof(Package.VersionId), "version_id" }, + { nameof(Package.IsCompleteProject), "is_complete_project" }, + { nameof(Package.RootGuid), "root_guid" }, + { nameof(Package.RootPath), "root_path" }, + { nameof(Package.IconUrl), "icon_url" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversion.ContainsKey(propertyName)) + return _propertyConversion[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs.meta new file mode 100644 index 0000000..6e345c3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7e9f0b99820061b49abf6e8cf544a727 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs new file mode 100644 index 0000000..8686ccc --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Models +{ + internal class PackageAdditionalData + { + public string Name { get; set; } + public string Status { get; set; } + public string PackageId { get; set; } + public string VersionId { get; set; } + public string CategoryId { get; set; } + public string Modified { get; set; } + public string Size { get; set; } + + public class AssetStorePackageResolver : DefaultContractResolver + { + private static AssetStorePackageResolver _instance; + public static AssetStorePackageResolver Instance => _instance ?? (_instance = new AssetStorePackageResolver()); + + private Dictionary _propertyConversions; + + private AssetStorePackageResolver() + { + _propertyConversions = new Dictionary() + { + { nameof(PackageAdditionalData.PackageId), "id" }, + { nameof(PackageAdditionalData.CategoryId), "category_id" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversions.ContainsKey(propertyName)) + return _propertyConversions[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs.meta new file mode 100644 index 0000000..75113a2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0663f29f3fcd0e34ab77338d1bdbb528 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs new file mode 100644 index 0000000..c80a506 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs @@ -0,0 +1,51 @@ +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Models +{ + internal class User + { + public string Id { get; set; } + public string SessionId { get; set; } + public string Name { get; set; } + public string Username { get; set; } + public string PublisherId { get; set; } + public bool IsPublisher => !string.IsNullOrEmpty(PublisherId); + + public override string ToString() + { + return + $"{nameof(Id)}: {Id}\n" + + $"{nameof(Name)}: {Name}\n" + + $"{nameof(Username)}: {Username}\n" + + $"{nameof(PublisherId)}: {PublisherId}\n" + + $"{nameof(IsPublisher)}: {IsPublisher}\n" + + $"{nameof(SessionId)}: [HIDDEN]"; + } + + public class AssetStoreUserResolver : DefaultContractResolver + { + private static AssetStoreUserResolver _instance; + public static AssetStoreUserResolver Instance => _instance ?? (_instance = new AssetStoreUserResolver()); + + private Dictionary _propertyConversions; + + private AssetStoreUserResolver() + { + _propertyConversions = new Dictionary() + { + { nameof(User.SessionId), "xunitysession" }, + { nameof(User.PublisherId), "publisher" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversions.ContainsKey(propertyName)) + return _propertyConversions[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs.meta new file mode 100644 index 0000000..bf60282 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: caf38df5cd685a345a1ebec8f7651c93 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses.meta new file mode 100644 index 0000000..534c529 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 49581213e7b6ca645955cce8ce23ac4b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs new file mode 100644 index 0000000..2e7a6b2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; + +namespace AssetStoreTools.Api.Responses +{ + /// + /// A structure used to return the success outcome and the result of Asset Store API calls + /// + internal class AssetStoreResponse + { + public bool Success { get; set; } = false; + public bool Cancelled { get; set; } = false; + public Exception Exception { get; set; } + + public AssetStoreResponse() { } + + public AssetStoreResponse(Exception e) : this() + { + Exception = e; + } + + protected void ValidateAssetStoreResponse(string json) + { + var dict = JsonConvert.DeserializeObject(json); + if (dict == null) + throw new Exception("Response is empty"); + + // Some json responses return an error field on error + if (dict.ContainsKey("error")) + { + // Server side error message + // Do not write to console since this is an error that + // is "expected" ie. can be handled by the gui. + throw new Exception(dict.GetValue("error").ToString()); + } + // Some json responses return status+message fields instead of an error field. Go figure. + else if (dict.ContainsKey("status") && dict.GetValue("status").ToString() != "ok" + && dict.ContainsKey("message")) + { + throw new Exception(dict.GetValue("message").ToString()); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs.meta new file mode 100644 index 0000000..3169e74 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee338db031a0cfb459f7cac7f41a5d75 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs new file mode 100644 index 0000000..ec3bb87 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; + +namespace AssetStoreTools.Api.Responses +{ + internal class AssetStoreToolsVersionResponse : AssetStoreResponse + { + public string Version { get; set; } + + public AssetStoreToolsVersionResponse() : base() { } + public AssetStoreToolsVersionResponse(Exception e) : base(e) { } + + public AssetStoreToolsVersionResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + ParseVersion(json); + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + + private void ParseVersion(string json) + { + var dict = JsonConvert.DeserializeObject(json); + if (!dict.ContainsKey("version")) + throw new Exception("Version was not found"); + + Version = dict.GetValue("version").ToString(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs.meta new file mode 100644 index 0000000..cdcdfd5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 40558675926f913478a654350149209e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs new file mode 100644 index 0000000..09c786c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs @@ -0,0 +1,74 @@ +using AssetStoreTools.Api.Models; +using Newtonsoft.Json; +using System; +using System.Net; +using System.Net.Http; + +namespace AssetStoreTools.Api.Responses +{ + internal class AuthenticationResponse : AssetStoreResponse + { + public User User { get; set; } + + public AuthenticationResponse() : base() { } + + public AuthenticationResponse(Exception e) : base(e) { } + + public AuthenticationResponse(HttpStatusCode statusCode, HttpRequestException fallbackException) + { + string message; + switch (statusCode) + { + case HttpStatusCode.Unauthorized: + message = "Incorrect email and/or password. Please try again."; + break; + case HttpStatusCode.InternalServerError: + message = "Authentication request failed\nIf you were logging in with your Unity Cloud account, please make sure you are still logged in.\n" + + "This might also be caused by too many invalid login attempts - if that is the case, please try again later."; + break; + default: + Exception = fallbackException; + return; + } + + Exception = new Exception(message); + } + + public AuthenticationResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = User.AssetStoreUserResolver.Instance + }; + User = JsonConvert.DeserializeObject(json, serializerSettings); + ValidateLoginData(); + ValidatePublisher(); + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + + private void ValidateLoginData() + { + if (string.IsNullOrEmpty(User.Id) + || string.IsNullOrEmpty(User.SessionId) + || string.IsNullOrEmpty(User.Name) + || string.IsNullOrEmpty(User.Username)) + throw new Exception("Could not parse the necessary publisher information from the response."); + } + + private void ValidatePublisher() + { + if (!User.IsPublisher) + throw new Exception($"Your Unity ID {User.Name} is not currently connected to a publisher account. " + + $"Please create a publisher profile."); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs.meta new file mode 100644 index 0000000..8a0544c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ec3a5cb59a7e78646b07f800d317874d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs new file mode 100644 index 0000000..4ff740b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs @@ -0,0 +1,43 @@ +using AssetStoreTools.Api.Models; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Responses +{ + internal class CategoryDataResponse : AssetStoreResponse + { + public List Categories { get; set; } + + public CategoryDataResponse() : base() { } + public CategoryDataResponse(Exception e) : base(e) { } + + public CategoryDataResponse(string json) + { + try + { + var categoryArray = JsonConvert.DeserializeObject(json); + + Categories = new List(); + var serializer = new JsonSerializer() + { + ContractResolver = new Category.AssetStoreCategoryResolver() + }; + + foreach (var categoryData in categoryArray) + { + var category = categoryData.ToObject(serializer); + Categories.Add(category); + } + + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs.meta new file mode 100644 index 0000000..9c79529 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e3789323453f1604286b436f77bdca97 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs new file mode 100644 index 0000000..f4e8ac9 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs @@ -0,0 +1,31 @@ +using System; +using UnityEngine; + +namespace AssetStoreTools.Api.Responses +{ + internal class PackageThumbnailResponse : AssetStoreResponse + { + public Texture2D Thumbnail { get; set; } + public PackageThumbnailResponse() : base() { } + public PackageThumbnailResponse(Exception e) : base(e) { } + + public PackageThumbnailResponse(byte[] textureBytes) + { + try + { + var tex = new Texture2D(1, 1, TextureFormat.RGBA32, false); + var success = tex.LoadImage(textureBytes); + if (!success) + throw new Exception("Could not retrieve image from the provided texture bytes"); + + Thumbnail = tex; + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs.meta new file mode 100644 index 0000000..4187474 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dacfba636b3757e408514b850d715e18 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs new file mode 100644 index 0000000..4f57d64 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Responses +{ + internal class PackageUploadedUnityVersionDataResponse : AssetStoreResponse + { + public List UnityVersions { get; set; } + + public PackageUploadedUnityVersionDataResponse() : base() { } + public PackageUploadedUnityVersionDataResponse(Exception e) : base(e) { } + + public PackageUploadedUnityVersionDataResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + ParseVersionData(json); + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + + private void ParseVersionData(string json) + { + var data = JsonConvert.DeserializeObject(json); + try + { + var content = data.GetValue("content").ToObject(); + UnityVersions = content.GetValue("unity_versions").ToObject>(); + } + catch + { + throw new Exception("Could not parse the unity versions array"); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs.meta new file mode 100644 index 0000000..d34cef1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2552f659a600e124aa952f3ba760ddf3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs new file mode 100644 index 0000000..df01e70 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs @@ -0,0 +1,59 @@ +using AssetStoreTools.Api.Models; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Responses +{ + internal class PackagesAdditionalDataResponse : AssetStoreResponse + { + public List Packages { get; set; } + + public PackagesAdditionalDataResponse() : base() { } + public PackagesAdditionalDataResponse(Exception e) : base(e) { } + + public PackagesAdditionalDataResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + ParseExtraData(json); + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + + private void ParseExtraData(string json) + { + var packageDict = JsonConvert.DeserializeObject(json); + if (!packageDict.ContainsKey("packages")) + throw new Exception("Response did not not contain the list of packages"); + + Packages = new List(); + var serializer = new JsonSerializer() + { + ContractResolver = PackageAdditionalData.AssetStorePackageResolver.Instance + }; + + var packageArray = packageDict.GetValue("packages").ToObject(); + foreach (var packageData in packageArray) + { + var package = packageData.ToObject(serializer); + + // Some fields are based on the latest version in the json + var latestVersion = packageData["versions"].ToObject().Last; + + package.VersionId = latestVersion["id"].ToString(); + package.Modified = latestVersion["modified"].ToString(); + package.Size = latestVersion["size"].ToString(); + + Packages.Add(package); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs.meta new file mode 100644 index 0000000..c5e3704 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88d58ad5e0eea6345b5c83f30ee8ebd5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs new file mode 100644 index 0000000..d9fb47c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs @@ -0,0 +1,59 @@ +using AssetStoreTools.Api.Models; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Responses +{ + internal class PackagesDataResponse : AssetStoreResponse + { + public List Packages { get; set; } + + public PackagesDataResponse() : base() { } + public PackagesDataResponse(Exception e) : base(e) { } + + public PackagesDataResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + ParseMainData(json); + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + + private void ParseMainData(string json) + { + var packageDict = JsonConvert.DeserializeObject(json); + if (!packageDict.ContainsKey("packages")) + throw new Exception("Response did not not contain the list of packages"); + + Packages = new List(); + var serializer = new JsonSerializer() + { + ContractResolver = Package.AssetStorePackageResolver.Instance + }; + + foreach (var packageToken in packageDict["packages"]) + { + var property = (JProperty)packageToken; + var packageData = property.Value.ToObject(serializer); + + // Package Id is the key of the package object + packageData.PackageId = property.Name; + + // Package Icon Url is returned without the https: prefix + if (!string.IsNullOrEmpty(packageData.IconUrl)) + packageData.IconUrl = $"https:{packageData.IconUrl}"; + + Packages.Add(packageData); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs.meta new file mode 100644 index 0000000..c47ff0b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 705ec748e689148479f54666993cd79d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs new file mode 100644 index 0000000..a0479a0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs @@ -0,0 +1,12 @@ +using AssetStoreTools.Api.Models; +using System; + +namespace AssetStoreTools.Api.Responses +{ + internal class RefreshedPackageDataResponse : AssetStoreResponse + { + public Package Package { get; set; } + public RefreshedPackageDataResponse() { } + public RefreshedPackageDataResponse(Exception e) : base(e) { } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs.meta new file mode 100644 index 0000000..2b7a974 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 20f710024d5ed514db02672f12ac361c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs new file mode 100644 index 0000000..3fa2160 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs @@ -0,0 +1,28 @@ +using System; + +namespace AssetStoreTools.Api.Responses +{ + internal class PackageUploadResponse : AssetStoreResponse + { + public UploadStatus Status { get; set; } + + public PackageUploadResponse() : base() { } + public PackageUploadResponse(Exception e) : base(e) { } + + public PackageUploadResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + Status = UploadStatus.Success; + Success = true; + } + catch (Exception e) + { + Success = false; + Status = UploadStatus.Fail; + Exception = e; + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs.meta new file mode 100644 index 0000000..6ac626b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8f1758cfa8119cf49a61b010a04352e4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs new file mode 100644 index 0000000..eb5e54c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs @@ -0,0 +1,25 @@ +using AssetStoreTools.Api.Responses; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class SessionAuthentication : AuthenticationBase + { + public SessionAuthentication(string sessionId) + { + AuthenticationContent = GetAuthenticationContent( + new KeyValuePair("reuse_session", sessionId) + ); + } + + public override async Task Authenticate(IAssetStoreClient client, CancellationToken cancellationToken) + { + var result = await client.Post(LoginUrl, AuthenticationContent, cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + + return ParseResponse(result); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs.meta new file mode 100644 index 0000000..f11fc25 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02e970f660088cc4b89003608d59cd06 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs new file mode 100644 index 0000000..17c765f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs @@ -0,0 +1,99 @@ +using AssetStoreTools.Api.Responses; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class UnityPackageUploadSettings + { + public string VersionId { get; set; } + public string UnityPackagePath { get; set; } + public string RootGuid { get; set; } + public string RootPath { get; set; } + public string ProjectPath { get; set; } + } + + internal class UnityPackageUploader : PackageUploaderBase + { + private UnityPackageUploadSettings _settings; + private Uri _uploadUri; + + public UnityPackageUploader(UnityPackageUploadSettings settings) + { + _settings = settings; + } + + protected override void ValidateSettings() + { + if (string.IsNullOrEmpty(_settings.VersionId)) + throw new Exception("Version Id is unset"); + + if (string.IsNullOrEmpty(_settings.UnityPackagePath) + || !File.Exists(_settings.UnityPackagePath)) + throw new Exception("Package file could not be found"); + + if (!_settings.UnityPackagePath.EndsWith(".unitypackage")) + throw new Exception("Provided package file is not .unitypackage"); + } + + public override async Task Upload(IAssetStoreClient client, IProgress progress = null, CancellationToken cancellationToken = default) + { + try + { + ValidateSettings(); + + var endpoint = Constants.Api.UploadUnityPackageUrl(_settings.VersionId); + var query = new Dictionary() + { + { "root_guid", _settings.RootGuid }, + { "root_path", _settings.RootPath }, + { "project_path", _settings.ProjectPath } + }; + + _uploadUri = ApiUtility.CreateUri(endpoint, query, true); + } + catch (Exception e) + { + return new PackageUploadResponse() { Success = false, Status = UploadStatus.Fail, Exception = e }; + } + + return await Task.Run(() => UploadTask(client, progress, cancellationToken)); + } + + private PackageUploadResponse UploadTask(IAssetStoreClient client, IProgress progress, CancellationToken cancellationToken) + { + try + { + using (FileStream requestFileStream = new FileStream(_settings.UnityPackagePath, FileMode.Open, FileAccess.Read)) + { + var content = new StreamContent(requestFileStream, UploadChunkSizeBytes); + + var response = client.Put(_uploadUri, content, cancellationToken); + WaitForUploadCompletion(response, requestFileStream, progress, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + EnsureSuccessResponse(response.Result); + + var responseStr = response.Result.Content.ReadAsStringAsync().Result; + return new PackageUploadResponse(responseStr); + } + } + catch (OperationCanceledException e) + { + return new PackageUploadResponse() { Success = false, Cancelled = true, Status = UploadStatus.Cancelled, Exception = e }; + } + catch (TimeoutException e) + { + return new PackageUploadResponse() { Success = true, Status = UploadStatus.ResponseTimeout, Exception = e }; + } + catch (Exception e) + { + return new PackageUploadResponse() { Success = false, Exception = e, Status = UploadStatus.Fail }; + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs.meta new file mode 100644 index 0000000..cbff3f3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 95bd0284375397f41a2065e07d4ba526 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs new file mode 100644 index 0000000..cc3adf4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs @@ -0,0 +1,11 @@ +namespace AssetStoreTools.Api +{ + internal enum UploadStatus + { + Default = 0, + Success = 1, + Fail = 2, + Cancelled = 3, + ResponseTimeout = 4 + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs.meta new file mode 100644 index 0000000..3c25205 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0a121831a941cb64a8a9d21ca6fda9c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs new file mode 100644 index 0000000..81fbd08 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("AssetStoreTools.Tests")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] +[assembly: InternalsVisibleTo("ab-builder")] +[assembly: InternalsVisibleTo("Inspector-Editor")] diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs.meta new file mode 100644 index 0000000..972ae52 --- /dev/null +++ b/Installer/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/Installer/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs new file mode 100644 index 0000000..373ac32 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs @@ -0,0 +1,82 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.UI; +using AssetStoreTools.Uploader; +using AssetStoreTools.Utility; +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.UI; +using System; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools +{ + internal static class AssetStoreTools + { + [MenuItem("Tools/Asset Store/Uploader", false, 0)] + public static void ShowAssetStoreToolsUploader() + { + Type inspectorType = Type.GetType("UnityEditor.InspectorWindow,UnityEditor.dll"); + var wnd = EditorWindow.GetWindow(inspectorType); + wnd.Show(); + } + + [MenuItem("Tools/Asset Store/Validator", false, 1)] + public static void ShowAssetStoreToolsValidator() + { + Type inspectorType = Type.GetType("UnityEditor.InspectorWindow,UnityEditor.dll"); + var wnd = EditorWindow.GetWindow(typeof(UploaderWindow), inspectorType); + wnd.Show(); + } + + public static void ShowAssetStoreToolsValidator(ValidationSettings settings, ValidationResult result) + { + ShowAssetStoreToolsValidator(); + EditorWindow.GetWindow().Load(settings, result); + } + + [MenuItem("Tools/Asset Store/Preview Generator", false, 2)] + public static void ShowAssetStoreToolsPreviewGenerator() + { + Type inspectorType = Type.GetType("UnityEditor.InspectorWindow,UnityEditor.dll"); + var wnd = EditorWindow.GetWindow(inspectorType); + wnd.Show(); + } + + public static void ShowAssetStoreToolsPreviewGenerator(PreviewGenerationSettings settings) + { + ShowAssetStoreToolsPreviewGenerator(); + EditorWindow.GetWindow().Load(settings); + } + + [MenuItem("Tools/Asset Store/Publisher Portal", false, 20)] + public static void OpenPublisherPortal() + { + Application.OpenURL("https://publisher.unity.com/"); + } + + [MenuItem("Tools/Asset Store/Submission Guidelines", false, 21)] + public static void OpenSubmissionGuidelines() + { + Application.OpenURL("https://assetstore.unity.com/publishing/submission-guidelines/"); + } + + [MenuItem("Tools/Asset Store/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("Tools/Asset Store/Check for Updates", false, 45)] + public static void OpenUpdateChecker() + { + var wnd = EditorWindow.GetWindowWithRect(new Rect(Screen.width / 2, Screen.height / 2, 400, 150), true); + wnd.Show(); + } + + [MenuItem("Tools/Asset Store/Settings", false, 50)] + public static void OpenSettings() + { + ASToolsPreferencesProvider.OpenSettings(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs.meta new file mode 100644 index 0000000..9452bb0 --- /dev/null +++ b/Installer/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/Installer/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs new file mode 100644 index 0000000..43f3c11 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs @@ -0,0 +1,23 @@ +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools +{ + internal abstract class AssetStoreToolsWindow : EditorWindow + { + protected abstract string WindowTitle { get; } + + private void DefaultInit() + { + titleContent = new GUIContent(WindowTitle); + Init(); + } + + protected abstract void Init(); + + private void OnEnable() + { + DefaultInit(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs.meta new file mode 100644 index 0000000..2fe87e5 --- /dev/null +++ b/Installer/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/Installer/Packages/com.unity.asset-store-tools/Editor/Constants.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Constants.cs new file mode 100644 index 0000000..d2c777a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Constants.cs @@ -0,0 +1,178 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using PackageInfo = UnityEditor.PackageManager.PackageInfo; + +namespace AssetStoreTools +{ + internal class Constants + { +#if UNITY_EDITOR_OSX + public static readonly string UnityPath = System.IO.Path.Combine(EditorApplication.applicationPath, "Contents", "MacOS", "Unity"); +#else + public static readonly string UnityPath = EditorApplication.applicationPath; +#endif + public static readonly string RootProjectPath = Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length); + + private static bool GetArgument(string argumentName, out string argumentValue) + { + argumentValue = string.Empty; + var args = Environment.GetCommandLineArgs(); + + for (int i = 0; i < args.Length; i++) + { + if (!args[i].Equals(argumentName, StringComparison.OrdinalIgnoreCase)) + continue; + + if (i + 1 >= args.Length) + return false; + + argumentValue = args[i + 1]; + break; + } + + return !string.IsNullOrEmpty(argumentValue); + } + + public class Api + { + public static readonly string ApiVersion = $"V{PackageInfo.FindForAssetPath("Packages/com.unity.asset-store-tools").version}"; + public const string AssetStoreToolsLatestVersionUrl = "https://api.assetstore.unity3d.com/package/latest-version/115"; + + private const string AssetStoreBaseUrlDefault = "https://kharma.unity3d.com"; + private const string AssetStoreBaseUrlOverrideArgument = "-assetStoreUrl"; + public static readonly string AssetStoreBaseUrl = !GetArgument(AssetStoreBaseUrlOverrideArgument, out var overriddenUrl) + ? AssetStoreBaseUrlDefault + : overriddenUrl; + + public static readonly string AuthenticateUrl = $"{AssetStoreBaseUrl}/login"; + public static readonly string GetPackagesUrl = $"{AssetStoreBaseUrl}/api/asset-store-tools/metadata/0.json"; + public static readonly string GetPackagesAdditionalDataUrl = $"{AssetStoreBaseUrl}/api/management/packages.json"; + public static readonly string GetCategoriesUrl = $"{AssetStoreBaseUrl}/api/management/categories.json"; + + public static string GetPackageUploadedVersionsUrl(string packageId, string versionId) => + $"{AssetStoreBaseUrl}/api/content/preview/{packageId}/{versionId}.json"; + public static string UploadUnityPackageUrl(string versionId) => + $"{AssetStoreBaseUrl}/api/asset-store-tools/package/{versionId}/unitypackage.json"; + + public static IDictionary DefaultAssetStoreQuery() + { + var dict = new Dictionary() + { + { "unityversion", Application.unityVersion }, + { "toolversion", ApiVersion } + }; + return dict; + } + } + + public class Updater + { + public const string AssetStoreToolsUrl = "https://assetstore.unity.com/packages/tools/utilities/asset-store-publishing-tools-115"; + } + + public class Cache + { + public const string SessionTokenKey = "kharma.sessionid"; + public const string TempCachePath = "Temp/AssetStoreToolsCache"; + public const string PersistentCachePath = "Library/AssetStoreToolsCache"; + + public const string PackageDataFileName = "PackageMetadata.json"; + public const string CategoryDataFile = "Categories.json"; + public const string ValidationResultFile = "ValidationStateData.asset"; + + public static string PackageThumbnailFileName(string packageId) => $"{packageId}.png"; + public static string WorkflowStateDataFileName(string packageId) => $"{packageId}-workflowStateData.asset"; + } + + public class Uploader + { + public const string MinRequiredUnitySupportVersion = "2021.3"; + public const long MaxPackageSizeBytes = 6576668672; // 6 GB + 128MB of headroom + public const string AccountRegistrationUrl = "https://publisher.unity.com/access"; + public const string AccountForgottenPasswordUrl = "https://id.unity.com/password/new"; + + public class Analytics + { + public const string VendorKey = "unity.assetStoreTools"; + public const int MaxEventsPerHour = 20; + public const int MaxNumberOfElements = 1000; + + public class AuthenticationAnalytics + { + public const string EventName = "assetStoreToolsLogin"; + public const int EventVersion = 1; + } + + public class PackageUploadAnalytics + { + public const string EventName = "assetStoreTools"; + public const int EventVersion = 3; + } + } + } + + public class Validator + { + public const string SubmissionGuidelinesUrl = "https://assetstore.unity.com/publishing/submission-guidelines#Overview"; + public const string SupportTicketUrl = "https://support.unity.com/hc/en-us/requests/new?ticket_form_id=65905"; + + public class Tests + { + public const string TestDefinitionsPath = "Packages/com.unity.asset-store-tools/Editor/Validator/Tests"; + public const string TestMethodsPath = "Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods"; + + public static readonly string GenericTestMethodsPath = $"{TestMethodsPath}/Generic"; + public static readonly string UnityPackageTestMethodsPath = $"{TestMethodsPath}/UnityPackage"; + } + } + + public class Previews + { + public const string PreviewDatabaseFile = "PreviewDatabase.json"; + + public static readonly string DefaultOutputPath = $"{Cache.TempCachePath}/AssetPreviews"; + public const FileNameFormat DefaultFileNameFormat = FileNameFormat.Guid; + + public class Native + { + public static readonly string DefaultOutputPath = $"{Previews.DefaultOutputPath}/Native"; + public const PreviewFormat DefaultFormat = PreviewFormat.PNG; + public const bool DefaultWaitForPreviews = true; + public const bool DefaultChunkedPreviewLoading = true; + public const int DefaultChunkSize = 100; + } + + public class Custom + { + public static readonly string DefaultOutputPath = $"{Previews.DefaultOutputPath}/Custom"; + public const PreviewFormat DefaultFormat = PreviewFormat.JPG; + public const int DefaultWidth = 300; + public const int DefaultHeight = 300; + public const int DefaultDepth = 32; + + public const int DefaultNativeWidth = 900; + public const int DefaultNativeHeight = 900; + + public static readonly Color DefaultAudioSampleColor = new Color(1f, 0.55f, 0); + public static readonly Color DefaultAudioBackgroundColor = new Color(0.32f, 0.32f, 0.32f); + } + } + + public class WindowStyles + { + public const string UploaderStylesPath = "Packages/com.unity.asset-store-tools/Editor/Uploader/Styles"; + public const string ValidatorStylesPath = "Packages/com.unity.asset-store-tools/Editor/Validator/Styles"; + public const string ValidatorIconsPath = "Packages/com.unity.asset-store-tools/Editor/Validator/Icons"; + public const string PreviewGeneratorStylesPath = "Packages/com.unity.asset-store-tools/Editor/Previews/Styles"; + public const string UpdaterStylesPath = "Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater"; + } + + public class Debug + { + public const string DebugModeKey = "ASTDebugMode"; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Constants.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Constants.cs.meta new file mode 100644 index 0000000..682cecb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Constants.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a4ee1f78505bda748ae24b3dfd2606ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter.meta new file mode 100644 index 0000000..4e1143b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5f5ca981958937a43997a9f365759edf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions.meta new file mode 100644 index 0000000..ac18e6c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2072d354c04b19c48b22593536b3ebcf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs new file mode 100644 index 0000000..0201a8e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace AssetStoreTools.Exporter +{ + internal interface IPackageExporter + { + PackageExporterSettings Settings { get; } + + Task Export(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs.meta new file mode 100644 index 0000000..91a3d96 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 41bc3a111ed1fd64c8b9acef211d9e51 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs new file mode 100644 index 0000000..ba503d8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs @@ -0,0 +1,7 @@ +namespace AssetStoreTools.Exporter +{ + internal interface IPreviewInjector + { + void Inject(string temporaryPackagePath); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs.meta new file mode 100644 index 0000000..534a5dd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dcff58dc716351f43b2709cfacdfebba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs new file mode 100644 index 0000000..885a188 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs @@ -0,0 +1,134 @@ +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 PackageExporterBase : IPackageExporter + { + public PackageExporterSettings Settings { get; private set; } + + public const string ProgressBarTitle = "Exporting Package"; + public const string ProgressBarStepSavingAssets = "Saving Assets..."; + public const string ProgressBarStepGatheringFiles = "Gathering files..."; + public const string ProgressBarStepGeneratingPreviews = "Generating previews..."; + public const string ProgressBarStepCompressingPackage = "Compressing package..."; + + private static readonly string[] PluginFolderExtensions = { "androidlib", "bundle", "plugin", "framework", "xcframework" }; + + public PackageExporterBase(PackageExporterSettings settings) + { + Settings = settings; + } + + public async Task Export() + { + try + { + ValidateSettings(); + } + catch (Exception e) + { + return new PackageExporterResult() { Success = false, Exception = e }; + } + + return await ExportImpl(); + } + + protected virtual void ValidateSettings() + { + if (Settings == null) + throw new ArgumentException("Settings cannot be null"); + + if (string.IsNullOrEmpty(Settings.OutputFilename)) + throw new ArgumentException("Output path cannot be null"); + + if (Settings.OutputFilename.EndsWith("/") || Settings.OutputFilename.EndsWith("\\")) + throw new ArgumentException("Output path must be a valid filename and not end with a directory separator character"); + } + + protected abstract Task ExportImpl(); + + 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 generateIfPlugin, bool scrapeFromMeta) + { + if (!FileUtility.ShouldHaveMeta(assetPath)) + 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 (generateIfPlugin && 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 non-native exporter since the native exporter + // will not be able to retrieve the asset path from a hidden folder + if (scrapeFromMeta) + { + 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/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs.meta new file mode 100644 index 0000000..1c88d87 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aab20a0b596e60b40b1f7f7e0f54858e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs new file mode 100644 index 0000000..6197ba5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs @@ -0,0 +1,7 @@ +namespace AssetStoreTools.Exporter +{ + internal abstract class PackageExporterSettings + { + public string OutputFilename; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs.meta new file mode 100644 index 0000000..25ff427 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 82c350daaabdc784e95e09cdc8511e23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs new file mode 100644 index 0000000..82a10ac --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs @@ -0,0 +1,11 @@ +using AssetStoreTools.Previews.Generators; + +namespace AssetStoreTools.Exporter +{ + internal class DefaultExporterSettings : PackageExporterSettings + { + public string[] ExportPaths; + public string[] Dependencies; + public IPreviewGenerator PreviewGenerator; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs.meta new file mode 100644 index 0000000..31644b1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 92cbd0e60b4bb9049bcf1e9fd92ccae6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs new file mode 100644 index 0000000..c8d6c42 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs @@ -0,0 +1,304 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Utility; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; +using CacheConstants = AssetStoreTools.Constants.Cache; + +namespace AssetStoreTools.Exporter +{ + internal class DefaultPackageExporter : PackageExporterBase + { + private const string TemporaryExportPathName = "CustomExport"; + + private DefaultExporterSettings _defaultExportSettings; + + public DefaultPackageExporter(DefaultExporterSettings settings) : base(settings) + { + _defaultExportSettings = settings; + } + + protected override void ValidateSettings() + { + base.ValidateSettings(); + + if (_defaultExportSettings.ExportPaths == null || _defaultExportSettings.ExportPaths.Length == 0) + throw new ArgumentException("Export paths array cannot be empty"); + } + + protected override async Task ExportImpl() + { + return await this.Export(); + } + + private new async Task Export() + { + ASDebug.Log("Using custom package exporter"); + + // Save assets before exporting + EditorUtility.DisplayProgressBar(ProgressBarTitle, ProgressBarStepSavingAssets, 0.1f); + AssetDatabase.SaveAssets(); + + try + { + // Create a temporary export path + PostExportCleanup(); + var temporaryExportPath = GetTemporaryExportPath(); + if (!Directory.Exists(temporaryExportPath)) + Directory.CreateDirectory(temporaryExportPath); + + // Construct an unzipped package structure + CreateTempPackageStructure(temporaryExportPath); + + var previewGenerationResult = await GeneratePreviews(); + InjectPreviews(previewGenerationResult, temporaryExportPath); + + // Build a .unitypackage file from the temporary folder + CreateUnityPackage(temporaryExportPath, _defaultExportSettings.OutputFilename); + + EditorUtility.RevealInFinder(_defaultExportSettings.OutputFilename); + + ASDebug.Log($"Package file has been created at {_defaultExportSettings.OutputFilename}"); + return new PackageExporterResult() { Success = true, ExportedPath = _defaultExportSettings.OutputFilename, PreviewGenerationResult = previewGenerationResult }; + } + catch (Exception e) + { + return new PackageExporterResult() { Success = false, Exception = e }; + } + finally + { + PostExportCleanup(); + } + } + + private string GetTemporaryExportPath() + { + return $"{CacheConstants.TempCachePath}/{TemporaryExportPathName}"; + } + + private void CreateTempPackageStructure(string tempOutputPath) + { + EditorUtility.DisplayProgressBar(ProgressBarTitle, ProgressBarStepGatheringFiles, 0.4f); + var pathGuidPairs = GetPathGuidPairs(_defaultExportSettings.ExportPaths); + + 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; + } + + if (_defaultExportSettings.Dependencies == null || _defaultExportSettings.Dependencies.Length == 0) + return; + + var exportDependenciesDict = new JObject(); + var allRegistryPackages = PackageUtility.GetAllRegistryPackages(); + + foreach (var exportDependency in _defaultExportSettings.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; + } + + if (exportDependenciesDict.Count == 0) + return; + + var exportManifestJson = new JObject(); + 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 async Task GeneratePreviews() + { + if (_defaultExportSettings.PreviewGenerator == null) + return null; + + void ReportProgress(float progress) + { + EditorUtility.DisplayProgressBar(ProgressBarTitle, ProgressBarStepGeneratingPreviews, progress); + } + + _defaultExportSettings.PreviewGenerator.OnProgressChanged += ReportProgress; + var result = await _defaultExportSettings.PreviewGenerator.Generate(); + _defaultExportSettings.PreviewGenerator.OnProgressChanged -= ReportProgress; + EditorUtility.ClearProgressBar(); + + if (!result.Success) + { + UnityEngine.Debug.LogWarning($"An error was encountered while generating previews. Exported package may be missing previews.\n{result.Exception}"); + } + + return result; + } + + private void InjectPreviews(PreviewGenerationResult result, string temporaryExportPath) + { + if (result == null || !result.Success) + return; + + var injector = new PreviewInjector(result); + injector.Inject(temporaryExportPath); + } + + 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.8f); + + // 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"); + } + +#if UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX + 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"); + } +#endif + + private int StartProcess(string processPath, string arguments, string workingDirectory) + { + var info = new ProcessStartInfo() + { + FileName = processPath, + Arguments = arguments, + WorkingDirectory = workingDirectory, + CreateNoWindow = true, + UseShellExecute = false + }; + +#if UNITY_EDITOR_OSX + // Prevent OSX-specific archive pollution + info.EnvironmentVariables.Add("COPYFILE_DISABLE", "1"); +#endif + + 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/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs.meta new file mode 100644 index 0000000..fa1bd63 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.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/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs new file mode 100644 index 0000000..52a7cad --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs @@ -0,0 +1,8 @@ +namespace AssetStoreTools.Exporter +{ + internal class LegacyExporterSettings : PackageExporterSettings + { + public string[] ExportPaths; + public bool IncludeDependencies; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs.meta new file mode 100644 index 0000000..dbade08 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c7dea1cfe45989e4eab6fc5fd18c1e10 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs new file mode 100644 index 0000000..1720043 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs @@ -0,0 +1,109 @@ +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 LegacyPackageExporter : PackageExporterBase + { + private const string ExportMethodWithoutDependencies = "UnityEditor.PackageUtility.ExportPackage"; + private const string ExportMethodWithDependencies = "UnityEditor.PackageUtility.ExportPackageAndPackageManagerManifest"; + + private LegacyExporterSettings _legacyExportSettings; + + public LegacyPackageExporter(LegacyExporterSettings settings) : base(settings) + { + _legacyExportSettings = settings; + } + + protected override void ValidateSettings() + { + base.ValidateSettings(); + + if (_legacyExportSettings.ExportPaths == null || _legacyExportSettings.ExportPaths.Length == 0) + throw new ArgumentException("Export paths array cannot be empty"); + } + + protected override async Task ExportImpl() + { + return await this.Export(); + } + + private async new Task Export() + { + ASDebug.Log("Using native package exporter"); + + try + { + var guids = GetGuids(_legacyExportSettings.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 (_legacyExportSettings.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, _legacyExportSettings.OutputFilename }); + + // The internal exporter methods are asynchronous, therefore + // we need to wait for exporting to finish before returning + await Task.Run(() => + { + while (!File.Exists(_legacyExportSettings.OutputFilename)) + Thread.Sleep(100); + }); + + ASDebug.Log($"Package file has been created at {_legacyExportSettings.OutputFilename}"); + return new PackageExporterResult() { Success = true, ExportedPath = _legacyExportSettings.OutputFilename }; + } + catch (Exception e) + { + return new PackageExporterResult() { Success = false, Exception = 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/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs.meta new file mode 100644 index 0000000..853e472 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.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/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs new file mode 100644 index 0000000..a0ae7a5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs @@ -0,0 +1,13 @@ +using AssetStoreTools.Previews.Data; +using System; + +namespace AssetStoreTools.Exporter +{ + internal class PackageExporterResult + { + public bool Success; + public string ExportedPath; + public PreviewGenerationResult PreviewGenerationResult; + public Exception Exception; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs.meta new file mode 100644 index 0000000..840bcf6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e685b1c322eab4540919d4fc970e812d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs new file mode 100644 index 0000000..69e06da --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs @@ -0,0 +1,41 @@ +using AssetStoreTools.Previews.Data; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace AssetStoreTools.Exporter +{ + internal class PreviewInjector : IPreviewInjector + { + private PreviewGenerationResult _result; + + public PreviewInjector(PreviewGenerationResult result) + { + _result = result; + } + + public void Inject(string temporaryPackagePath) + { + if (_result == null || !_result.Success) + return; + + var previews = _result.Previews.Where(x => x.Type == _result.GenerationType && x.Exists()); + InjectFilesIntoGuidFolders(previews, temporaryPackagePath); + } + + private void InjectFilesIntoGuidFolders(IEnumerable previews, string temporaryPackagePath) + { + foreach (var assetFolder in Directory.EnumerateDirectories(temporaryPackagePath)) + { + var guid = assetFolder.Replace("\\", "/").Split('/').Last(); + var generatedPreview = previews.FirstOrDefault(x => x.Guid.Equals(guid)); + + if (generatedPreview == null) + continue; + + // Note: Unity Importer and Asset Store only operate with .png extensions + File.Copy(generatedPreview.Path, $"{assetFolder}/preview.png", true); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs.meta new file mode 100644 index 0000000..98e155a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 772db784128e32d4792bb680258c71df +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews.meta new file mode 100644 index 0000000..747d5eb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 13e8cd63112e52d43a7e65949f0143a4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts.meta new file mode 100644 index 0000000..f95e49f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cbe1aebea6551424997b361fab69f266 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data.meta new file mode 100644 index 0000000..89f1356 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ae99e2e3b5a83d1469110306c96f4c58 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs new file mode 100644 index 0000000..2705875 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs @@ -0,0 +1,19 @@ +using UnityEngine; + +namespace AssetStoreTools.Previews.Data +{ + internal class CustomPreviewGenerationSettings : PreviewGenerationSettings + { + public override GenerationType GenerationType => GenerationType.Custom; + + public int Width; + public int Height; + public int Depth; + + public int NativeWidth; + public int NativeHeight; + + public Color AudioSampleColor; + public Color AudioBackgroundColor; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs.meta new file mode 100644 index 0000000..38f9eba --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2ccb1292c1c4ba94cb6f4022ecfdfa50 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs new file mode 100644 index 0000000..cb841a8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs @@ -0,0 +1,9 @@ +namespace AssetStoreTools.Previews.Data +{ + internal enum FileNameFormat + { + Guid = 0, + FullAssetPath = 1, + AssetName = 2, + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs.meta new file mode 100644 index 0000000..9a6639f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 38a1babecfeaf524f98e8d67882acf48 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs new file mode 100644 index 0000000..fef979f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs @@ -0,0 +1,9 @@ +namespace AssetStoreTools.Previews.Data +{ + internal enum GenerationType + { + Unknown = 0, + Native = 1, + Custom = 2 + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs.meta new file mode 100644 index 0000000..f179123 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66697a5d16404d948ba3191ddfc60bd9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs new file mode 100644 index 0000000..fff204e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs @@ -0,0 +1,10 @@ +namespace AssetStoreTools.Previews.Data +{ + internal class NativePreviewGenerationSettings : PreviewGenerationSettings + { + public override GenerationType GenerationType => GenerationType.Native; + public bool WaitForPreviews; + public bool ChunkedPreviewLoading; + public int ChunkSize; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs.meta new file mode 100644 index 0000000..e3e7940 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5b2ef019acae6fe43b5565858e15433a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs new file mode 100644 index 0000000..cabc56c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.Data +{ + internal class PreviewDatabase + { + public List Previews; + + public PreviewDatabase() + { + Previews = new List(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs.meta new file mode 100644 index 0000000..fd55b05 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cf8cef28a68324742a7e4b47efc87563 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs new file mode 100644 index 0000000..e68f29a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs @@ -0,0 +1,8 @@ +namespace AssetStoreTools.Previews.Data +{ + internal enum PreviewFormat + { + JPG = 0, + PNG = 1 + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs.meta new file mode 100644 index 0000000..0d8c214 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0500e4459ebfe8448a13194af49f89fa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs new file mode 100644 index 0000000..6fb7791 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.Data +{ + internal class PreviewGenerationResult + { + public GenerationType GenerationType; + public bool Success; + public IEnumerable GeneratedPreviews; + public IEnumerable Previews; + public Exception Exception; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs.meta new file mode 100644 index 0000000..7062382 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e040f2cdf0177824dacb158b23a63374 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs new file mode 100644 index 0000000..920ac13 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs @@ -0,0 +1,12 @@ +namespace AssetStoreTools.Previews.Data +{ + internal abstract class PreviewGenerationSettings + { + public abstract GenerationType GenerationType { get; } + public string[] InputPaths; + public string OutputPath; + public PreviewFormat Format; + public FileNameFormat PreviewFileNamingFormat; + public bool OverwriteExisting; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs.meta new file mode 100644 index 0000000..38563dc --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7e578ae6616505a4795da8f632d63229 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs new file mode 100644 index 0000000..a7ba227 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs @@ -0,0 +1,17 @@ +using System.IO; + +namespace AssetStoreTools.Previews.Data +{ + internal class PreviewMetadata + { + public GenerationType Type; + public string Guid; + public string Name; + public string Path; + + public bool Exists() + { + return File.Exists(Path); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs.meta new file mode 100644 index 0000000..2aed929 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4ff6be4e277d8314e921baff52ea25bc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators.meta new file mode 100644 index 0000000..7019d5c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ed651159a2004574789e97726da5090c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom.meta new file mode 100644 index 0000000..cac3d99 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f675855c3d971694785806c0c7a463be +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs new file mode 100644 index 0000000..f46e406 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs @@ -0,0 +1,97 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom +{ + internal class AudioChannel + { + private int _yMin; + private int _yMax; + + private int _yBaseline; + private int _yAmplitude; + + private List _samples; + + public AudioChannel(int minY, int maxY, List samples) + { + _yMin = minY; + _yMax = maxY; + + _yBaseline = (_yMin + _yMax) / 2; + _yAmplitude = _yMax - _yBaseline; + + _samples = samples; + } + + public IEnumerable GetCoordinateData(int desiredWidth) + { + var coordinates = new List(); + var step = Mathf.RoundToInt((float)_samples.Count / desiredWidth); + + for (int i = 0; i < desiredWidth; i++) + { + var startIndex = i * step; + var endIndex = (i + 1) * step; + var sampleChunk = CreateChunk(startIndex, endIndex); + + if (sampleChunk.Count() == 0) + break; + + DownsampleMax(sampleChunk, out var aboveBaseline, out var belowBaseline); + + var yAboveBaseline = SampleToCoordinate(aboveBaseline); + var yBelowBaseline = SampleToCoordinate(belowBaseline); + + coordinates.Add(new AudioChannelCoordinate(i, _yBaseline, yAboveBaseline, yBelowBaseline)); + } + + // If there weren't enough samples to complete the desired width - fill out the rest with zeroes + for (int i = coordinates.Count; i < desiredWidth; i++) + coordinates.Add(new AudioChannelCoordinate(i, _yBaseline, 0, 0)); + + return coordinates; + } + + private IEnumerable CreateChunk(int startIndex, int endIndex) + { + var chunk = new List(); + for (int i = startIndex; i < endIndex; i++) + { + if (i >= _samples.Count) + break; + + chunk.Add(_samples[i]); + } + + return chunk; + } + + private void DownsampleMax(IEnumerable samples, out float valueAboveBaseline, out float valueBelowBaseline) + { + valueAboveBaseline = 0; + valueBelowBaseline = 0; + + foreach (var sample in samples) + { + if (sample > 0 && sample > valueAboveBaseline) + { + valueAboveBaseline = sample; + continue; + } + + if (sample < 0 && sample < valueBelowBaseline) + { + valueBelowBaseline = sample; + continue; + } + } + } + + private int SampleToCoordinate(float sample) + { + return _yBaseline + (int)(sample * _yAmplitude); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs.meta new file mode 100644 index 0000000..4a7d3fb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 82fab55b08a1be94cb2e18f3feae91ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs new file mode 100644 index 0000000..1030eed --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs @@ -0,0 +1,18 @@ +namespace AssetStoreTools.Previews.Generators.Custom +{ + internal struct AudioChannelCoordinate + { + public int X { get; private set; } + public int YBaseline { get; private set; } + public int YAboveBaseline { get; private set; } + public int YBelowBaseline { get; private set; } + + public AudioChannelCoordinate(int x, int yBaseline, int yAboveBaseline, int yBelowBaseline) + { + X = x; + YBaseline = yBaseline; + YAboveBaseline = yAboveBaseline; + YBelowBaseline = yBelowBaseline; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs.meta new file mode 100644 index 0000000..9142554 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b54462f6af82a2644944d6e4bde23c9e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters.meta new file mode 100644 index 0000000..91cfc9f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c07070deed666d54cb72a89a5fcd7ef7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs new file mode 100644 index 0000000..ec7cd57 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters +{ + internal interface ISceneScreenshotter + { + SceneScreenshotterSettings Settings { get; } + + string Screenshot(string outputPath); + string Screenshot(GameObject target, string outputPath); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs.meta new file mode 100644 index 0000000..1c18549 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 045ac265a792af243918af0849ee2ac8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs new file mode 100644 index 0000000..a446208 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs @@ -0,0 +1,32 @@ +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters +{ + internal class MaterialScreenshotter : SceneScreenshotterBase + { + public MaterialScreenshotter(SceneScreenshotterSettings settings) : base(settings) { } + + public override void PositionCamera(GameObject target) + { + var renderers = target.GetComponentsInChildren(); + if (renderers == null || renderers.Length == 0) + return; + + var bounds = GetGlobalBounds(renderers); + + var materialSphereRadius = bounds.extents.y * 1.1f; + + var angle = Camera.fieldOfView / 2; + var sinAngle = Mathf.Sin(angle * Mathf.Deg2Rad); + var distance = materialSphereRadius / sinAngle; + + Camera.transform.position = new Vector3(bounds.center.x, bounds.center.y + distance, bounds.center.z); + Camera.transform.LookAt(bounds.center); + Camera.transform.RotateAround(bounds.center, Vector3.left, 60); + Camera.transform.RotateAround(bounds.center, Vector3.up, -45); + + Camera.nearClipPlane = 0.01f; + Camera.farClipPlane = 10000; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs.meta new file mode 100644 index 0000000..9c07505 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c2bd7b01b0cebeb43a6fbc53377f0ea6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs new file mode 100644 index 0000000..6ce5905 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs @@ -0,0 +1,33 @@ +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters +{ + internal class MeshScreenshotter : SceneScreenshotterBase + { + public MeshScreenshotter(SceneScreenshotterSettings settings) : base(settings) { } + + public override void PositionCamera(GameObject target) + { + var renderers = target.GetComponentsInChildren(); + if (renderers == null || renderers.Length == 0) + return; + + var bounds = GetGlobalBounds(renderers); + + var encapsulatingSphereDiameter = (bounds.max - bounds.min).magnitude; + var encapsulatingSphereRadius = encapsulatingSphereDiameter / 2; + + var angle = Camera.fieldOfView / 2; + var sinAngle = Mathf.Sin(angle * Mathf.Deg2Rad); + var distance = encapsulatingSphereRadius / sinAngle; + + Camera.transform.position = new Vector3(bounds.center.x, bounds.center.y + distance, bounds.center.z); + Camera.transform.LookAt(bounds.center); + Camera.transform.RotateAround(bounds.center, Vector3.left, 65); + Camera.transform.RotateAround(bounds.center, Vector3.up, 235); + + Camera.nearClipPlane = 0.01f; + Camera.farClipPlane = 10000; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs.meta new file mode 100644 index 0000000..660fb06 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0339d22d91b7c94ebc18b1de6f1e287 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs new file mode 100644 index 0000000..fb289f4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs @@ -0,0 +1,124 @@ +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters +{ + internal abstract class SceneScreenshotterBase : ISceneScreenshotter + { + public SceneScreenshotterSettings Settings { get; } + + protected Camera Camera => GetCamera(); + private Camera _camera; + + public SceneScreenshotterBase(SceneScreenshotterSettings settings) + { + Settings = settings; + } + + private Camera GetCamera() + { + if (_camera == null) + { +#if UNITY_2022_3_OR_NEWER + _camera = GameObject.FindFirstObjectByType(FindObjectsInactive.Include); +#else + _camera = GameObject.FindObjectOfType(); +#endif + } + + return _camera; + } + + public virtual void ValidateSettings() + { + if (Settings.Width <= 0) + throw new ArgumentException("Width should be larger than 0"); + + if (Settings.Height <= 0) + throw new ArgumentException("Height should be larger than 0"); + + if (Settings.Depth <= 0) + throw new ArgumentException("Depth should be larger than 0"); + + if (Settings.NativeWidth <= 0) + throw new ArgumentException("Native width should be larger than 0"); + + if (Settings.NativeHeight <= 0) + throw new ArgumentException("Native height should be larger than 0"); + } + + public abstract void PositionCamera(GameObject target); + + public string Screenshot(string outputPath) + { + ValidateSettings(); + + var texture = GraphicsUtility.GetTextureFromCamera(Camera, Settings.NativeWidth, Settings.NativeHeight, Settings.Depth); + + if (Settings.Width < Settings.NativeWidth || Settings.Height < Settings.NativeHeight) + texture = GraphicsUtility.ResizeTexture(texture, Settings.Width, Settings.Height); + + var extension = PreviewConvertUtility.ConvertExtension(Settings.Format); + var writtenPath = $"{outputPath}.{extension}"; + var bytes = PreviewConvertUtility.ConvertTexture(texture, Settings.Format); + File.WriteAllBytes(writtenPath, bytes); + + return writtenPath; + } + + public string Screenshot(GameObject target, string outputPath) + { + PositionCamera(target); + PositionLighting(target); + return Screenshot(outputPath); + } + + private void PositionLighting(GameObject target) + { +#if UNITY_2022_3_OR_NEWER + var light = GameObject.FindFirstObjectByType(FindObjectsInactive.Include); +#else + var light = GameObject.FindObjectOfType(); +#endif + light.transform.position = Camera.transform.position; + light.transform.LookAt(target.transform); + light.transform.RotateAround(target.transform.position, Vector3.forward, 60f); + } + + protected Bounds GetGlobalBounds(IEnumerable renderers) + { + var center = Vector3.zero; + + foreach (var renderer in renderers) + { + center += renderer.bounds.center; + } + center /= renderers.Count(); + + var globalBounds = new Bounds(center, Vector3.zero); + + foreach (var renderer in renderers) + { + globalBounds.Encapsulate(renderer.bounds); + } + + return globalBounds; + } + + protected Bounds GetNormalizedBounds(Bounds bounds) + { + var largestExtent = Mathf.Max(bounds.extents.x, bounds.extents.y, bounds.extents.z); + var normalizedBounds = new Bounds() + { + center = bounds.center, + extents = new Vector3(largestExtent, largestExtent, largestExtent) + }; + + return normalizedBounds; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs.meta new file mode 100644 index 0000000..adab09f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ce77aefdce8a37f498d17d73da53d0a4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs new file mode 100644 index 0000000..82e0ec1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs @@ -0,0 +1,16 @@ +using AssetStoreTools.Previews.Data; + +namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters +{ + internal class SceneScreenshotterSettings + { + public int Width; + public int Height; + public int Depth; + + public int NativeWidth; + public int NativeHeight; + + public PreviewFormat Format; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs.meta new file mode 100644 index 0000000..c152cef --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aa34806e243bad949892d06dd47295e2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators.meta new file mode 100644 index 0000000..e13d726 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b4bb2dd0960418d4a8d4efd34b92a418 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs new file mode 100644 index 0000000..36a6b21 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs @@ -0,0 +1,15 @@ +using AssetStoreTools.Previews.Data; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class AudioTypeGeneratorSettings : TypeGeneratorSettings + { + public int Width; + public int Height; + + public Color SampleColor; + public Color BackgroundColor; + public PreviewFormat Format; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs.meta new file mode 100644 index 0000000..22aa253 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5b7ab1b072d95be4daf221ee23af1c80 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs new file mode 100644 index 0000000..a0f1545 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs @@ -0,0 +1,207 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class AudioTypePreviewGenerator : TypePreviewGeneratorBase + { + private AudioTypeGeneratorSettings _settings; + private Texture2D _texture; + + public override event Action OnAssetProcessed; + + public AudioTypePreviewGenerator(AudioTypeGeneratorSettings settings) : base(settings) + { + _settings = settings; + } + + public override void ValidateSettings() + { + base.ValidateSettings(); + + if (_settings.Width <= 0) + throw new ArgumentException("Width must be larger than 0"); + + if (_settings.Height <= 0) + throw new ArgumentException("Height must be larger than 0"); + } + + protected override IEnumerable CollectAssets() + { + var assets = new List(); + var materialGuids = AssetDatabase.FindAssets("t:audioclip", Settings.InputPaths); + foreach (var guid in materialGuids) + { + var audioClip = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); + + // Skip nested audio clips + if (!AssetDatabase.IsMainAsset(audioClip)) + continue; + + // Skip materials with an error shader + if (!IsLoadTypeSupported(audioClip)) + { + Debug.LogWarning($"Audio clip '{audioClip}' is using a load type which cannot retrieve sample data. Preview will not be generated."); + continue; + } + + assets.Add(audioClip); + } + + return assets; + } + + private bool IsLoadTypeSupported(AudioClip audioClip) + { + if (audioClip.loadType == AudioClipLoadType.DecompressOnLoad) + return true; + + return false; + } + + protected override async Task> GenerateImpl(IEnumerable assets) + { + var generatedPreviews = new List(); + var audioClips = assets.ToList(); + for (int i = 0; i < audioClips.Count; i++) + { + var audioClip = audioClips[i] as AudioClip; + if (audioClip != null) + { + var texture = GenerateAudioClipTexture(audioClip); + + var outputPath = GenerateOutputPathWithExtension(audioClip, _settings.PreviewFileNamingFormat, _settings.Format); + var bytes = PreviewConvertUtility.ConvertTexture(texture, _settings.Format); + File.WriteAllBytes(outputPath, bytes); + generatedPreviews.Add(ObjectToMetadata(audioClip, outputPath)); + } + + OnAssetProcessed?.Invoke(i, audioClips.Count); + await Task.Yield(); + } + + return generatedPreviews; + } + + private Texture2D GenerateAudioClipTexture(AudioClip audioClip) + { + if (!audioClip.LoadAudioData()) + throw new Exception("Could not load audio data"); + + try + { + if (_texture == null) + _texture = new Texture2D(_settings.Width, _settings.Height); + else +#if UNITY_2021_3_OR_NEWER || UNITY_2022_1_OR_NEWER || UNITY_2021_2_OR_NEWER + _texture.Reinitialize(_settings.Width, _settings.Height); +#else + _texture.Resize(_settings.Width, _settings.Height); +#endif + + FillTextureBackground(); + FillTextureForeground(audioClip); + + _texture.Apply(); + return _texture; + } + finally + { + audioClip.UnloadAudioData(); + } + } + + private void FillTextureBackground() + { + for (int i = 0; i < _texture.width; i++) + { + for (int j = 0; j < _texture.height; j++) + { + _texture.SetPixel(i, j, _settings.BackgroundColor); + } + } + } + + private void FillTextureForeground(AudioClip audioClip) + { + var channels = CreateChannels(audioClip); + + for (int i = 0; i < channels.Count; i++) + { + DrawChannel(channels[i]); + } + } + + private List CreateChannels(AudioClip audioClip) + { + var channelSamples = GetChannelSamples(audioClip); + var sectionSize = _texture.height / audioClip.channels; + + var channels = new List(); + + for (int i = 0; i < audioClip.channels; i++) + { + var channelMaxY = (_texture.height - 1) - i * sectionSize; + var channelMinY = _texture.height - (i + 1) * sectionSize; + var channel = new AudioChannel(channelMinY, channelMaxY, channelSamples[i]); + channels.Add(channel); + } + + return channels; + } + + private List> GetChannelSamples(AudioClip audioClip) + { + var channelSamples = new List>(); + var allSamples = new float[audioClip.samples * audioClip.channels]; + + if (!audioClip.GetData(allSamples, 0)) + throw new Exception("Could not retrieve audio samples"); + + for (int i = 0; i < audioClip.channels; i++) + { + var samples = new List(); + var sampleIndex = i; + while (sampleIndex < allSamples.Length) + { + samples.Add(allSamples[sampleIndex]); + sampleIndex += audioClip.channels; + } + + channelSamples.Add(samples); + } + + return channelSamples; + } + + private void DrawChannel(AudioChannel channel) + { + var sectionData = channel.GetCoordinateData(_texture.width); + + foreach (var data in sectionData) + { + DrawVerticalColumn(data.X, data.YBaseline, data.YAboveBaseline, data.YBelowBaseline, _settings.SampleColor); + } + } + + private void DrawVerticalColumn(int x, int yBaseline, int y1, int y2, Color color) + { + _texture.SetPixel(x, yBaseline, color); + + var startIndex = y1 < y2 ? y1 : y2; + var endIndex = y1 < y2 ? y2 : y1; + + for (int i = startIndex; i < endIndex; i++) + { + _texture.SetPixel(x, i, color); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs.meta new file mode 100644 index 0000000..0511663 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9ddf69bb2dca51a42aff247b3a471bb3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs new file mode 100644 index 0000000..bdd5056 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs @@ -0,0 +1,16 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal interface ITypePreviewGenerator + { + TypeGeneratorSettings Settings { get; } + + event Action OnAssetProcessed; + + Task> Generate(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs.meta new file mode 100644 index 0000000..a42ecd1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d9cd368dc73a23478390ee1332cb0be +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs new file mode 100644 index 0000000..13b44ec --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs @@ -0,0 +1,85 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class MaterialTypePreviewGenerator : TypePreviewGeneratorFromScene + { + public override event Action OnAssetProcessed; + + public MaterialTypePreviewGenerator(TypePreviewGeneratorFromSceneSettings settings) : base(settings) { } + + protected override IEnumerable CollectAssets() + { + var assets = new List(); + var materialGuids = AssetDatabase.FindAssets("t:material", Settings.InputPaths); + foreach (var guid in materialGuids) + { + var mat = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); + + // Skip nested materials + if (!AssetDatabase.IsMainAsset(mat)) + continue; + + // Skip materials with an error shader + if (IsShaderInvalid(mat.shader)) + { + Debug.LogWarning($"Material '{mat}' is using an erroring shader. Preview will not be generated."); + continue; + } + + assets.Add(mat); + } + + return assets; + } + + protected override async Task> GeneratePreviewsInScene(IEnumerable assets) + { + var generatedPreviews = new List(); + var materials = assets.ToList(); + var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere); + + var hasMeshRenderer = sphere.TryGetComponent(out var meshRenderer); + if (!hasMeshRenderer) + throw new Exception($"Could not find a MeshRenderer for {sphere}"); + + for (int i = 0; i < materials.Count; i++) + { + ThrowIfSceneChanged(); + + var material = materials[i] as Material; + + if (material != null) + { + meshRenderer.sharedMaterial = material; + var previewPath = Settings.Screenshotter.Screenshot(sphere, GenerateOutputPathWithoutExtension(material, Settings.PreviewFileNamingFormat)); + if (!string.IsNullOrEmpty(previewPath)) + generatedPreviews.Add(ObjectToMetadata(material, previewPath)); + } + + OnAssetProcessed?.Invoke(i, materials.Count); + await Task.Yield(); + } + + UnityEngine.Object.DestroyImmediate(sphere); + return generatedPreviews; + } + + private bool IsShaderInvalid(Shader shader) + { + if (ShaderUtil.ShaderHasError(shader)) + return true; + + if (!shader.isSupported) + return true; + + return false; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs.meta new file mode 100644 index 0000000..c692df7 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a4e121bae63a199458e53a523dd18c8c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs new file mode 100644 index 0000000..7d97607 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs @@ -0,0 +1,86 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class ModelTypePreviewGenerator : TypePreviewGeneratorFromScene + { + public override event Action OnAssetProcessed; + + public ModelTypePreviewGenerator(TypePreviewGeneratorFromSceneSettings settings) : base(settings) { } + + protected override IEnumerable CollectAssets() + { + var models = new List(); + var modelGuids = AssetDatabase.FindAssets("t:model", Settings.InputPaths); + + foreach (var guid in modelGuids) + { + var model = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); + + // Skip nested models + if (!AssetDatabase.IsMainAsset(model)) + continue; + + // Skip models without renderers + if (model.GetComponentsInChildren().Length == 0) + continue; + + models.Add(model); + } + + return models; + } + + protected override async Task> GeneratePreviewsInScene(IEnumerable assets) + { + var generatedPreviews = new List(); + var models = assets.ToList(); + var referenceShader = GetDefaultObjectShader(); + + for (int i = 0; i < models.Count; i++) + { + ThrowIfSceneChanged(); + + var model = models[i] as GameObject; + + if (model != null) + { + var go = UnityEngine.Object.Instantiate(model, Vector3.zero, Quaternion.Euler(0, 0, 0)); + ReplaceShaders(go, referenceShader); + + var previewPath = Settings.Screenshotter.Screenshot(go, GenerateOutputPathWithoutExtension(model, Settings.PreviewFileNamingFormat)); + if (!string.IsNullOrEmpty(previewPath)) + generatedPreviews.Add(ObjectToMetadata(model, previewPath)); + + UnityEngine.Object.DestroyImmediate(go); + } + + OnAssetProcessed?.Invoke(i, models.Count); + await Task.Yield(); + } + + return generatedPreviews; + } + + private void ReplaceShaders(GameObject go, Shader shader) + { + var meshRenderers = go.GetComponentsInChildren(); + foreach (var mr in meshRenderers) + { + var materialArray = mr.sharedMaterials; + for (int i = 0; i < materialArray.Length; i++) + { + materialArray[i] = new Material(shader) { color = new Color(0.7f, 0.7f, 0.7f) }; + } + + mr.sharedMaterials = materialArray; + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs.meta new file mode 100644 index 0000000..72c41d0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fca8a1fa8a211874cb84d3d811a0158c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs new file mode 100644 index 0000000..f0eeb6b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs @@ -0,0 +1,113 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class PrefabTypePreviewGenerator : TypePreviewGeneratorFromScene + { + public override event Action OnAssetProcessed; + + public PrefabTypePreviewGenerator(TypePreviewGeneratorFromSceneSettings settings) : base(settings) { } + + protected override IEnumerable CollectAssets() + { + var prefabs = new List(); + var prefabGuids = AssetDatabase.FindAssets("t:prefab", Settings.InputPaths); + + foreach (var guid in prefabGuids) + { + var prefab = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); + + // Skip nested prefabs + if (!AssetDatabase.IsMainAsset(prefab)) + continue; + + // Skip prefabs without renderers + if (prefab.GetComponentsInChildren().Length == 0) + continue; + + prefabs.Add(prefab); + } + + return prefabs; + } + + protected override async Task> GeneratePreviewsInScene(IEnumerable assets) + { + var generatedPreviews = new List(); + var prefabs = assets.ToList(); + var objectReferenceShader = GetDefaultObjectShader(); + var particleReferenceShader = GetDefaultParticleShader(); + + for (int i = 0; i < prefabs.Count; i++) + { + ThrowIfSceneChanged(); + + var prefab = prefabs[i] as GameObject; + if (prefab != null) + { + var go = UnityEngine.Object.Instantiate(prefab, Vector3.zero, Quaternion.Euler(0, 0, 0)); + + ReplaceMissingShaders(go, objectReferenceShader, particleReferenceShader); + + HandleParticleSystems(go); + + var previewPath = Settings.Screenshotter.Screenshot(go, GenerateOutputPathWithoutExtension(prefab, Settings.PreviewFileNamingFormat)); + if (!string.IsNullOrEmpty(previewPath)) + generatedPreviews.Add(ObjectToMetadata(prefab, previewPath)); + + UnityEngine.Object.DestroyImmediate(go); + } + + OnAssetProcessed?.Invoke(i, prefabs.Count); + await Task.Yield(); + } + + return generatedPreviews; + } + + private void ReplaceMissingShaders(GameObject go, Shader objectShader, Shader particleShader) + { + var meshRenderers = go.GetComponentsInChildren(); + foreach (var mr in meshRenderers) + { + var shaderToUse = mr is ParticleSystemRenderer ? particleShader : objectShader; + + var materialArray = mr.sharedMaterials; + for (int i = 0; i < materialArray.Length; i++) + { + if (materialArray[i] == null) + { + materialArray[i] = new Material(shaderToUse); + } + else if (!materialArray[i].shader.isSupported) + { + materialArray[i].shader = shaderToUse; + } + } + + mr.sharedMaterials = materialArray; + } + } + + private void HandleParticleSystems(GameObject go) + { + var particleSystems = go.GetComponentsInChildren(); + if (particleSystems.Length == 0) + return; + + foreach (var ps in particleSystems) + { + ps.Stop(); + ps.Clear(); + ps.randomSeed = 1; + ps.Simulate(10, false, true, false); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs.meta new file mode 100644 index 0000000..61b31c1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 24b15b10bc361c84581f46cb6dd644cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs new file mode 100644 index 0000000..666b32d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs @@ -0,0 +1,11 @@ +using AssetStoreTools.Previews.Data; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class TextureTypeGeneratorSettings : TypeGeneratorSettings + { + public int MaxWidth; + public int MaxHeight; + public PreviewFormat Format; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs.meta new file mode 100644 index 0000000..29385d1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 058d746982619b04eb5e200363003899 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs new file mode 100644 index 0000000..24e1102 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs @@ -0,0 +1,116 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class TextureTypePreviewGenerator : TypePreviewGeneratorBase + { + private TextureTypeGeneratorSettings _settings; + + public override event Action OnAssetProcessed; + + public TextureTypePreviewGenerator(TextureTypeGeneratorSettings settings) : base(settings) + { + _settings = settings; + } + + public override void ValidateSettings() + { + base.ValidateSettings(); + + if (_settings.MaxWidth <= 0) + throw new ArgumentException("Max width should be larger than 0"); + + if (_settings.MaxHeight <= 0) + throw new ArgumentException("Max height should be larger than 0"); + } + + protected override IEnumerable CollectAssets() + { + var textures = new List(); + var textureGuids = AssetDatabase.FindAssets("t:texture", Settings.InputPaths); + + foreach (var guid in textureGuids) + { + var texture = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); + + // Skip nested textures + if (!AssetDatabase.IsMainAsset(texture)) + continue; + + textures.Add(texture); + } + + return textures; + } + + protected override async Task> GenerateImpl(IEnumerable assets) + { + var generatedPreviews = new List(); + var textures = assets.ToList(); + + for (int i = 0; i < textures.Count; i++) + { + var texture = textures[i] as Texture2D; + + if (texture != null) + { + Texture2D resizedTexture; + CalculateTextureSize(texture, out var resizeWidth, out var resizeHeight); + + var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture)) as TextureImporter; + if (importer != null && importer.textureType == TextureImporterType.NormalMap) + resizedTexture = GraphicsUtility.ResizeTextureNormalMap(texture, resizeWidth, resizeHeight); + else + resizedTexture = GraphicsUtility.ResizeTexture(texture, resizeWidth, resizeHeight); + + var previewPath = GenerateOutputPathWithExtension(texture, _settings.PreviewFileNamingFormat, _settings.Format); + + // Some textures may be transparent and need to be encoded as PNG to look correctly + var targetFormat = texture.alphaIsTransparency ? PreviewFormat.PNG : _settings.Format; + var bytes = PreviewConvertUtility.ConvertTexture(resizedTexture, targetFormat); + + File.WriteAllBytes(previewPath, bytes); + generatedPreviews.Add(ObjectToMetadata(texture, previewPath)); + } + + OnAssetProcessed?.Invoke(i, textures.Count); + await Task.Yield(); + } + + return generatedPreviews; + } + + private void CalculateTextureSize(Texture2D texture, out int width, out int height) + { + if (texture.width <= _settings.MaxWidth && texture.height <= _settings.MaxHeight) + { + width = texture.width; + height = texture.height; + return; + } + + var widthLongerThanHeight = texture.width > texture.height; + + if (widthLongerThanHeight) + { + var ratio = (float)texture.width / texture.height; + width = _settings.MaxWidth; + height = Mathf.RoundToInt(width / ratio); + } + else + { + var ratio = (float)texture.height / texture.width; + height = _settings.MaxHeight; + width = Mathf.RoundToInt(height / ratio); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs.meta new file mode 100644 index 0000000..cbe7491 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b04f55867ee575c489803356220feb31 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs new file mode 100644 index 0000000..1aaaa42 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs @@ -0,0 +1,12 @@ +using AssetStoreTools.Previews.Data; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal abstract class TypeGeneratorSettings + { + public string[] InputPaths; + public string[] IgnoredGuids; + public string OutputPath; + public FileNameFormat PreviewFileNamingFormat; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs.meta new file mode 100644 index 0000000..4c24cf5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a224a4b41a8c7cf4cb53dd77d6f2518b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs new file mode 100644 index 0000000..a49d968 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs @@ -0,0 +1,126 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal abstract class TypePreviewGeneratorBase : ITypePreviewGenerator + { + public TypeGeneratorSettings Settings { get; } + + public abstract event Action OnAssetProcessed; + + public TypePreviewGeneratorBase(TypeGeneratorSettings settings) + { + Settings = settings; + } + + public virtual void ValidateSettings() + { + if (Settings.InputPaths == null || Settings.InputPaths.Length == 0) + throw new ArgumentException("Input path cannot be null"); + + foreach (var path in Settings.InputPaths) + { + var inputPath = path.EndsWith("/") ? path.Remove(path.Length - 1) : path; + if (!AssetDatabase.IsValidFolder(inputPath)) + throw new ArgumentException($"Input path '{inputPath}' is not a valid ADB folder"); + } + + if (string.IsNullOrEmpty(Settings.OutputPath)) + throw new ArgumentException("Output path cannot be null"); + } + + public async Task> Generate() + { + var generatedPreviews = new List(); + ValidateSettings(); + + var assets = CollectAssets(); + assets = FilterIgnoredAssets(assets); + + if (assets.Count() == 0) + return generatedPreviews; + + return await GenerateImpl(assets); + } + + protected abstract IEnumerable CollectAssets(); + + private IEnumerable FilterIgnoredAssets(IEnumerable assets) + { + if (Settings.IgnoredGuids == null || Settings.IgnoredGuids.Length == 0) + return assets; + + var filteredAssets = new List(); + foreach (var asset in assets) + { + if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out var guid, out long _)) + continue; + + if (Settings.IgnoredGuids.Any(x => x == guid)) + continue; + + filteredAssets.Add(asset); + } + + return filteredAssets; + } + + protected abstract Task> GenerateImpl(IEnumerable assets); + + protected PreviewMetadata ObjectToMetadata(UnityEngine.Object obj, string previewPath) + { + if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long _)) + throw new Exception($"Could not retrieve guid for object {obj}"); + + return new PreviewMetadata() + { + Type = GenerationType.Custom, + Guid = guid, + Name = obj.name, + Path = previewPath + }; + } + + protected string GenerateOutputPathWithoutExtension(UnityEngine.Object asset, FileNameFormat fileNameFormat) + { + PrepareOutputFolder(Settings.OutputPath, false); + var directoryPath = Settings.OutputPath; + var fileName = PreviewConvertUtility.ConvertFilename(asset, fileNameFormat); + var fullPath = $"{directoryPath}/{fileName}"; + + return fullPath; + } + + protected string GenerateOutputPathWithExtension(UnityEngine.Object asset, FileNameFormat fileNameFormat, PreviewFormat previewFormat) + { + var partialOutputPath = GenerateOutputPathWithoutExtension(asset, fileNameFormat); + var extension = PreviewConvertUtility.ConvertExtension(previewFormat); + + return $"{partialOutputPath}.{extension}"; + } + + private void PrepareOutputFolder(string outputPath, bool cleanup) + { + var dir = new DirectoryInfo(outputPath); + + if (!dir.Exists) + { + dir.Create(); + return; + } + + if (!cleanup) + return; + + dir.Delete(true); + dir.Create(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs.meta new file mode 100644 index 0000000..7e751c2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c6fb2d639232bce4698338a252f47f3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs new file mode 100644 index 0000000..f9fe453 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs @@ -0,0 +1,111 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal abstract class TypePreviewGeneratorFromScene : TypePreviewGeneratorBase + { + protected new TypePreviewGeneratorFromSceneSettings Settings; + + private CancellationTokenSource _cancellationTokenSource; + + public TypePreviewGeneratorFromScene(TypePreviewGeneratorFromSceneSettings settings) : base(settings) + { + Settings = settings; + } + + public override void ValidateSettings() + { + base.ValidateSettings(); + + if (Settings.Screenshotter == null) + throw new ArgumentException("Screenshotter cannot be null"); + } + + protected sealed override async Task> GenerateImpl(IEnumerable assets) + { + var originalScenePath = EditorSceneManager.GetActiveScene().path; + await PreviewSceneUtility.OpenPreviewSceneForCurrentPipeline(); + + try + { + _cancellationTokenSource = new CancellationTokenSource(); + EditorSceneManager.sceneOpened += SceneOpenedDuringGeneration; + return await GeneratePreviewsInScene(assets); + } + finally + { + EditorSceneManager.sceneOpened -= SceneOpenedDuringGeneration; + _cancellationTokenSource.Dispose(); + if (!string.IsNullOrEmpty(originalScenePath)) + EditorSceneManager.OpenScene(originalScenePath); + } + } + + protected abstract Task> GeneratePreviewsInScene(IEnumerable assets); + + private void SceneOpenedDuringGeneration(Scene _, OpenSceneMode __) + { + if (!_cancellationTokenSource.IsCancellationRequested) + _cancellationTokenSource.Cancel(); + } + + protected void ThrowIfSceneChanged() + { + if (_cancellationTokenSource.Token.IsCancellationRequested) + throw new Exception("Preview generation was aborted due to a change of the scene"); + } + + protected Shader GetDefaultObjectShader() + { + switch (RenderPipelineUtility.GetCurrentPipeline()) + { + case RenderPipeline.BiRP: + return Shader.Find("Standard"); + case RenderPipeline.URP: + return Shader.Find("Universal Render Pipeline/Lit"); + case RenderPipeline.HDRP: + return Shader.Find("HDRP/Lit"); + default: + throw new NotImplementedException("Undefined Render Pipeline"); + } + } + + protected Shader GetDefaultParticleShader() + { + switch (RenderPipelineUtility.GetCurrentPipeline()) + { + case RenderPipeline.BiRP: + return Shader.Find("Particles/Standard Unlit"); + case RenderPipeline.URP: + return Shader.Find("Universal Render Pipeline/Particles/Unlit"); + case RenderPipeline.HDRP: + return Shader.Find("HDRP/Unlit"); + default: + throw new NotImplementedException("Undefined Render Pipeline"); + } + } + + protected Shader GetDefaultTextureShader() + { + switch (RenderPipelineUtility.GetCurrentPipeline()) + { + case RenderPipeline.BiRP: + return Shader.Find("Unlit/Texture"); + case RenderPipeline.URP: + return Shader.Find("Universal Render Pipeline/Unlit"); + case RenderPipeline.HDRP: + return Shader.Find("HDRP/Unlit"); + default: + throw new NotImplementedException("Undefined Render Pipeline"); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs.meta new file mode 100644 index 0000000..fcc5e57 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1bf0eb8d7ef65f340be785dae96e4b73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs new file mode 100644 index 0000000..36a2e6e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs @@ -0,0 +1,9 @@ +using AssetStoreTools.Previews.Generators.Custom.Screenshotters; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class TypePreviewGeneratorFromSceneSettings : TypeGeneratorSettings + { + public ISceneScreenshotter Screenshotter; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs.meta new file mode 100644 index 0000000..ead93bd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a6b871798f99ad44d9fca46789239ec1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs new file mode 100644 index 0000000..7a1ffeb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs @@ -0,0 +1,213 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Generators; +using AssetStoreTools.Previews.Generators.Custom.Screenshotters; +using AssetStoreTools.Previews.Generators.Custom.TypeGenerators; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; + +namespace AssetStoreTools.Previews +{ + internal class CustomPreviewGenerator : PreviewGeneratorBase + { + private CustomPreviewGenerationSettings _customSettings; + + public override event Action OnProgressChanged; + + public CustomPreviewGenerator(CustomPreviewGenerationSettings settings) + : base(settings) + { + _customSettings = settings; + } + + protected override void Validate() + { + base.Validate(); + + if (_customSettings.Width <= 0) + throw new ArgumentException("Width should be larger than 0"); + + if (_customSettings.Height <= 0) + throw new ArgumentException("Height should be larger than 0"); + + if (_customSettings.Depth <= 0) + throw new ArgumentException("Depth should be larger than 0"); + + if (_customSettings.NativeWidth <= 0) + throw new ArgumentException("Native width should be larger than 0"); + + if (_customSettings.NativeHeight <= 0) + throw new ArgumentException("Native height should be larger than 0"); + } + + protected override async Task GenerateImpl() + { + var result = new PreviewGenerationResult() + { + GenerationType = _customSettings.GenerationType + }; + + OnProgressChanged?.Invoke(0f); + + var generatedPreviews = new List(); + var existingPreviews = GetExistingPreviews(); + var generators = CreateGenerators(existingPreviews); + + var currentGenerator = 0; + Action generatorProgressCallback = null; + generatorProgressCallback = (currentAsset, totalAssets) => ReportProgress(currentGenerator, generators.Count(), currentAsset, totalAssets); + + try + { + foreach (var generator in generators) + { + generator.OnAssetProcessed += generatorProgressCallback; + var typeGeneratorPreviews = await generator.Generate(); + generatedPreviews.AddRange(typeGeneratorPreviews); + currentGenerator++; + } + + AssetDatabase.Refresh(); + + var allPreviews = new List(); + allPreviews.AddRange(generatedPreviews); + allPreviews.AddRange(existingPreviews); + + result.Success = true; + result.GeneratedPreviews = generatedPreviews; + result.Previews = allPreviews; + } + catch (Exception e) + { + result.Success = false; + result.Exception = e; + } + finally + { + foreach (var generator in generators) + generator.OnAssetProcessed -= generatorProgressCallback; + } + + return result; + } + + private IEnumerable GetExistingPreviews() + { + var existingPreviews = new List(); + + if (Settings.OverwriteExisting || !CachingService.GetCachedMetadata(out var database)) + return existingPreviews; + + var inputGuids = AssetDatabase.FindAssets("", _customSettings.InputPaths); + existingPreviews = database.Previews.Where(x => x.Type == GenerationType.Custom && x.Exists() && inputGuids.Any(y => y.Equals(x.Guid))).ToList(); + return existingPreviews; + } + + private IEnumerable CreateGenerators(IEnumerable existingPreviews) + { + var ignoredGuids = existingPreviews.Select(x => x.Guid).ToArray(); + + var generators = new ITypePreviewGenerator[] + { + CreateAudioPreviewGenerator(ignoredGuids), + CreateMaterialPreviewGenerator(ignoredGuids), + CreateModelPreviewGenerator(ignoredGuids), + CreatePrefabPreviewGenerator(ignoredGuids), + CreateTexturePreviewGenerator(ignoredGuids) + }; + + return generators; + } + + private ITypePreviewGenerator CreateAudioPreviewGenerator(string[] ignoredGuids) + { + var settings = new AudioTypeGeneratorSettings() + { + Width = _customSettings.Width, + Height = _customSettings.Height, + InputPaths = _customSettings.InputPaths, + OutputPath = _customSettings.OutputPath, + PreviewFileNamingFormat = _customSettings.PreviewFileNamingFormat, + Format = _customSettings.Format, + SampleColor = _customSettings.AudioSampleColor, + BackgroundColor = _customSettings.AudioBackgroundColor, + IgnoredGuids = ignoredGuids + }; + + return new AudioTypePreviewGenerator(settings); + } + + private ITypePreviewGenerator CreateMaterialPreviewGenerator(string[] ignoredGuids) + { + var settings = CreateSceneGeneratorSettings(new MaterialScreenshotter(CreateScreenshotterSettings()), ignoredGuids); + return new MaterialTypePreviewGenerator(settings); + } + + private ITypePreviewGenerator CreateModelPreviewGenerator(string[] ignoredGuids) + { + var settings = CreateSceneGeneratorSettings(new MeshScreenshotter(CreateScreenshotterSettings()), ignoredGuids); + return new ModelTypePreviewGenerator(settings); + } + + private ITypePreviewGenerator CreatePrefabPreviewGenerator(string[] ignoredGuids) + { + var settings = CreateSceneGeneratorSettings(new MeshScreenshotter(CreateScreenshotterSettings()), ignoredGuids); + return new PrefabTypePreviewGenerator(settings); + } + + private ITypePreviewGenerator CreateTexturePreviewGenerator(string[] ignoredGuids) + { + var settings = new TextureTypeGeneratorSettings() + { + MaxWidth = _customSettings.Width, + MaxHeight = _customSettings.Height, + InputPaths = _customSettings.InputPaths, + OutputPath = _customSettings.OutputPath, + Format = _customSettings.Format, + PreviewFileNamingFormat = _customSettings.PreviewFileNamingFormat, + IgnoredGuids = ignoredGuids + }; + + return new TextureTypePreviewGenerator(settings); + } + + private TypePreviewGeneratorFromSceneSettings CreateSceneGeneratorSettings(ISceneScreenshotter screenshotter, string[] ignoredGuids) + { + var settings = new TypePreviewGeneratorFromSceneSettings() + { + InputPaths = _customSettings.InputPaths, + OutputPath = _customSettings.OutputPath, + PreviewFileNamingFormat = _customSettings.PreviewFileNamingFormat, + Screenshotter = screenshotter, + IgnoredGuids = ignoredGuids + }; + + return settings; + } + + private SceneScreenshotterSettings CreateScreenshotterSettings() + { + var settings = new SceneScreenshotterSettings() + { + Width = _customSettings.Width, + Height = _customSettings.Height, + Depth = _customSettings.Depth, + Format = _customSettings.Format, + NativeWidth = _customSettings.NativeWidth, + NativeHeight = _customSettings.NativeHeight, + }; + + return settings; + } + + private void ReportProgress(int currentGenerator, int totalGenerators, int currentGeneratorAsset, int totalCurrentGeneratorAssets) + { + var completedGeneratorProgress = (float)currentGenerator / totalGenerators; + var currentGeneratorProgress = ((float)currentGeneratorAsset / totalCurrentGeneratorAssets) / totalGenerators; + var progressToReport = completedGeneratorProgress + currentGeneratorProgress; + OnProgressChanged?.Invoke(progressToReport); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs.meta new file mode 100644 index 0000000..e10c56c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e5906f8cb3e4eec489a2f7a82844bb3f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs new file mode 100644 index 0000000..b548baf --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs @@ -0,0 +1,15 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Threading.Tasks; + +namespace AssetStoreTools.Previews.Generators +{ + internal interface IPreviewGenerator + { + PreviewGenerationSettings Settings { get; } + + event Action OnProgressChanged; + + Task Generate(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs.meta new file mode 100644 index 0000000..e379870 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ebddaccb94ca6e34aa36b535d0a47249 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs new file mode 100644 index 0000000..760e2d1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs @@ -0,0 +1,362 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; +using UnityEngine.Tilemaps; + +namespace AssetStoreTools.Previews.Generators +{ + internal class NativePreviewGenerator : PreviewGeneratorBase + { + private const double InitialPreviewLoadingTimeoutSeconds = 10; + + private NativePreviewGenerationSettings _nativeSettings; + + private RenderTexture _renderTexture; + + private int _generatedPreviewsCount; + private int _totalPreviewsCount; + + public override event Action OnProgressChanged; + + public NativePreviewGenerator(NativePreviewGenerationSettings settings) + : base(settings) + { + _nativeSettings = settings; + } + + protected override void Validate() + { + base.Validate(); + + if (_nativeSettings.ChunkSize <= 0) + throw new ArgumentException("Chunk size must be larger than 0"); + } + + protected override async Task GenerateImpl() + { + var result = new PreviewGenerationResult() + { + GenerationType = _nativeSettings.GenerationType + }; + + OnProgressChanged?.Invoke(0f); + + try + { + var objects = GetObjectsRequiringPreviews(_nativeSettings.InputPaths); + var filteredObjects = new List(); + var reusedPreviews = new List(); + FilterObjects(objects, filteredObjects, reusedPreviews); + + _generatedPreviewsCount = 0; + _totalPreviewsCount = objects.Count; + + Directory.CreateDirectory(_nativeSettings.OutputPath); + + var generatedPreviews = new List(); + if (!_nativeSettings.WaitForPreviews) + { + WritePreviewsWithoutWaiting(filteredObjects, out generatedPreviews); + } + else + { + if (_nativeSettings.ChunkedPreviewLoading) + { + await WaitAndWritePreviewsChunked(filteredObjects, generatedPreviews); + } + else + { + await WaitAndWritePreviews(filteredObjects, generatedPreviews); + } + } + + var allPreviews = new List(); + allPreviews.AddRange(generatedPreviews); + allPreviews.AddRange(reusedPreviews); + + result.Success = true; + result.GeneratedPreviews = generatedPreviews; + result.Previews = allPreviews; + } + catch (Exception e) + { + result.Success = false; + result.Exception = e; + } + + return result; + } + + private List GetObjectsRequiringPreviews(string[] inputPaths) + { + var objects = new List(); + var guids = AssetDatabase.FindAssets("", inputPaths); + + foreach (var guid in guids) + { + if (objects.Any(x => x.Guid == guid)) + continue; + + var assetPath = AssetDatabase.GUIDToAssetPath(guid); + var obj = AssetDatabase.LoadAssetAtPath(assetPath); + if (!ShouldHavePreview(obj)) + continue; + + objects.Add(new PreviewMetadata() { Type = GenerationType.Native, Guid = guid }); + } + + return objects; + } + + private void FilterObjects(List objects, List filteredObjects, List reusedPreviews) + { + if (Settings.OverwriteExisting || !CachingService.GetCachedMetadata(out var database)) + { + filteredObjects.AddRange(objects); + return; + } + + foreach (var obj in objects) + { + var matchingEntry = database.Previews.FirstOrDefault(x => + x.Guid == obj.Guid + && x.Type == GenerationType.Native + && x.Exists()); + + if (matchingEntry == null) + { + filteredObjects.Add(obj); + } + else + { + reusedPreviews.Add(matchingEntry); + } + } + } + + private bool ShouldHavePreview(UnityEngine.Object asset) + { + if (asset == null) + return false; + + if (!AssetDatabase.IsMainAsset(asset)) + return false; + + switch (asset) + { + case AudioClip _: + case Material _: + case Mesh _: + case TerrainLayer _: + case Texture _: + case Tile _: + return true; + case GameObject go: + var renderers = go.GetComponentsInChildren(); + return renderers != null && renderers.Length > 0; + default: + return false; + } + } + + private PreviewMetadata WritePreviewToDisk(PreviewMetadata metadata, Texture2D texture) + { + var asset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(metadata.Guid)); + var width = Mathf.Min(texture.width, 128); + var height = Mathf.Min(texture.height, 128); + var readableTexture = GraphicsUtility.ResizeTexture(texture, width, height); + var fileName = PreviewConvertUtility.ConvertFilenameWithExtension(asset, _nativeSettings.PreviewFileNamingFormat, _nativeSettings.Format); + var filePath = $"{_nativeSettings.OutputPath}/{fileName}"; + var bytes = PreviewConvertUtility.ConvertTexture(readableTexture, _nativeSettings.Format); + + File.WriteAllBytes(filePath, bytes); + + metadata.Type = GenerationType.Native; + metadata.Name = asset.name; + metadata.Path = filePath; + + return metadata; + } + + private void WritePreviewsWithoutWaiting(List objects, out List generatedPreviews) + { + generatedPreviews = new List(); + + foreach (var obj in objects) + { + var texture = GetAssetPreviewFromGuid(obj.Guid); + if (texture == null) + continue; + + var generatedPreview = WritePreviewToDisk(obj, texture); + generatedPreviews.Add(generatedPreview); + } + } + + 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 async Task WaitAndWritePreviewsChunked(List objects, List generatedPreviews) + { + var chunks = objects.Count / _nativeSettings.ChunkSize; + var remainder = objects.Count % _nativeSettings.ChunkSize; + if (remainder != 0) + chunks += 1; + + for (int i = 0; i < chunks; i++) + { + var chunkObjects = new List(); + + for (int j = 0; j < _nativeSettings.ChunkSize; j++) + { + var index = i * _nativeSettings.ChunkSize + j; + if (index == objects.Count) + break; + + chunkObjects.Add(objects[index]); + } + + var generatedPreviewsChunk = new List(); + await WaitAndWritePreviews(chunkObjects, generatedPreviewsChunk); + generatedPreviews.AddRange(generatedPreviewsChunk); + } + } + + private async Task WaitAndWritePreviews(List objects, List generatedPreviews) + { + var initialObjectCount = objects.Count(); + if (initialObjectCount == 0) + return; + + await WaitAndWritePreviewIteration(objects, generatedPreviews); + var remainingObjectCount = objects.Count; + + // First iteration may take longer to start loading objects + var firstIterationStartTime = EditorApplication.timeSinceStartup; + while (true) + { + if (remainingObjectCount < initialObjectCount) + break; + + if (EditorApplication.timeSinceStartup - firstIterationStartTime > InitialPreviewLoadingTimeoutSeconds) + throw new Exception("Preview loading timed out."); + + await WaitAndWritePreviewIteration(objects, generatedPreviews); + remainingObjectCount = objects.Count; + } + + if (remainingObjectCount == 0) + return; + + while (true) + { + await WaitForEndOfFrame(1); + await WaitAndWritePreviewIteration(objects, generatedPreviews); + + // If no more previews are being loaded, try one more time before quitting + if (objects.Count == remainingObjectCount) + { + await WaitForEndOfFrame(1); + await WaitAndWritePreviewIteration(objects, generatedPreviews); + + if (objects.Count == remainingObjectCount) + { + var missingObjects = string.Join("\n", objects.Select(x => AssetDatabase.GUIDToAssetPath(x.Guid))); + Debug.LogWarning($"Unity Editor failed to fetch previews for {objects.Count} objects:\n{missingObjects}"); + break; + } + } + + remainingObjectCount = objects.Count; + + // Exit when all previews are loaded + if (remainingObjectCount == 0) + break; + } + } + + private async Task WaitAndWritePreviewIteration(List objects, List generatedPreviews) + { + var cacheSize = Mathf.Max(_nativeSettings.ChunkSize * 2, objects.Count() + _nativeSettings.ChunkSize); + AssetPreview.SetPreviewTextureCacheSize(cacheSize); + + // Initial queueing + foreach (var obj in objects) + { + var asset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(obj.Guid)); + AssetPreview.GetAssetPreview(asset); + } + + await WaitForEndOfFrame(); + + // Waiting (NOTE: works inconsistently across Unity streams) + foreach (var obj in objects) + { + var asset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(obj.Guid)); + if (AssetPreview.IsLoadingAssetPreview(asset.GetInstanceID())) + { + await WaitForEndOfFrame(); + } + } + + // Writing + for (int i = 0; i < objects.Count; i++) + { + var obj = objects[i]; + + var asset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(obj.Guid)); + var texture = AssetPreview.GetAssetPreview(asset); + if (texture == null) + continue; + + WritePreviewToDisk(obj, texture); + generatedPreviews.Add(obj); + _generatedPreviewsCount++; + OnProgressChanged?.Invoke((float)_generatedPreviewsCount / _totalPreviewsCount); + } + + // Removing written objects from the list + for (int i = objects.Count - 1; i >= 0; i--) + { + if (objects[i].Exists()) + objects.RemoveAt(i); + } + } + + private async Task WaitForEndOfFrame(double atLeastSeconds) + { + var startTime = EditorApplication.timeSinceStartup; + while (EditorApplication.timeSinceStartup - startTime <= atLeastSeconds) + { + await WaitForEndOfFrame(); + } + } + + private async Task WaitForEndOfFrame() + { + var isNextFrame = false; + EditorApplication.CallbackFunction callback = null; + callback = () => + { + EditorApplication.update -= callback; + isNextFrame = true; + }; + + EditorApplication.update += callback; + while (!isNextFrame) + await Task.Yield(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs.meta new file mode 100644 index 0000000..5a59e84 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd6eeb2f97a2ed34db51ab5ac0b3ffa1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs new file mode 100644 index 0000000..02d779d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs @@ -0,0 +1,45 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Services; +using System; +using System.Threading.Tasks; + +namespace AssetStoreTools.Previews.Generators +{ + internal abstract class PreviewGeneratorBase : IPreviewGenerator + { + public PreviewGenerationSettings Settings { get; } + protected ICachingService CachingService; + + public abstract event Action OnProgressChanged; + + public PreviewGeneratorBase(PreviewGenerationSettings settings) + { + Settings = settings; + CachingService = PreviewServiceProvider.Instance.GetService(); + } + + public async Task Generate() + { + Validate(); + + var result = await GenerateImpl(); + if (result.Success) + { + CachingService.CacheMetadata(result.GeneratedPreviews); + } + + return result; + } + + protected virtual void Validate() + { + if (Settings.InputPaths == null || Settings.InputPaths.Length == 0) + throw new ArgumentException("Input paths cannot be null"); + + if (string.IsNullOrEmpty(Settings.OutputPath)) + throw new ArgumentException("Output path cannot be null"); + } + + protected abstract Task GenerateImpl(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs.meta new file mode 100644 index 0000000..0369b2b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cedf01448e0edcc4fb55f19f2e92b740 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services.meta new file mode 100644 index 0000000..55ebe0c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aa18c820f185bfc4d8cd59e3418e2c4e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching.meta new file mode 100644 index 0000000..449835a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eb61a60f2ff91a448a7808ef2a25f871 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs new file mode 100644 index 0000000..10bb448 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs @@ -0,0 +1,87 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Utility; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace AssetStoreTools.Previews.Services +{ + internal class CachingService : ICachingService + { + public void CacheMetadata(IEnumerable previews) + { + var updatedDatabase = UpdatePreviewDatabase(previews); + + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = PreviewDatabaseContractResolver.Instance, + Converters = new List() { new StringEnumConverter() }, + Formatting = Formatting.Indented + }; + + CacheUtil.CreateFileInTempCache(Constants.Previews.PreviewDatabaseFile, JsonConvert.SerializeObject(updatedDatabase, serializerSettings), true); + } + + public bool GetCachedMetadata(out PreviewDatabase previewDatabase) + { + previewDatabase = null; + if (!CacheUtil.GetFileFromTempCache(Constants.Previews.PreviewDatabaseFile, out string filePath)) + return false; + + try + { + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = PreviewDatabaseContractResolver.Instance, + Converters = new List() { new StringEnumConverter() } + }; + + previewDatabase = JsonConvert.DeserializeObject(File.ReadAllText(filePath, Encoding.UTF8), serializerSettings); + return true; + } + catch + { + return false; + } + } + + private PreviewDatabase UpdatePreviewDatabase(IEnumerable previews) + { + PreviewDatabase database; + if (!GetCachedMetadata(out database)) + database = new PreviewDatabase(); + + // Delete missing previews + for (int i = database.Previews.Count - 1; i >= 0; i--) + { + if (database.Previews[i].Exists()) + continue; + + database.Previews.RemoveAt(i); + } + + // Append new previews & Replace existing previews + foreach (var preview in previews) + { + var matchingPreviews = database.Previews.Where(x => x.Guid == preview.Guid).ToList(); + foreach (var matchingPreview in matchingPreviews) + { + // Delete previously generated preview of the same type + if (matchingPreview.Type == preview.Type) + database.Previews.Remove(matchingPreview); + // Delete previously generated preview of a different type if the path matches + else if (matchingPreview.Path.Equals(preview.Path)) + database.Previews.Remove(matchingPreview); + } + + database.Previews.Add(preview); + } + + database.Previews = database.Previews.OrderBy(x => x.Guid).ThenBy(x => x.Type).ToList(); + return database; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs.meta new file mode 100644 index 0000000..02a153e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e0b6cf909c8798b4590744959571a21f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs new file mode 100644 index 0000000..6713000 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs @@ -0,0 +1,11 @@ +using AssetStoreTools.Previews.Data; +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.Services +{ + internal interface ICachingService : IPreviewService + { + void CacheMetadata(IEnumerable previews); + bool GetCachedMetadata(out PreviewDatabase previewDatabase); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs.meta new file mode 100644 index 0000000..7dd1195 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eeaeae010299dcd489adb00dbf51b274 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs new file mode 100644 index 0000000..0bc56ae --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs @@ -0,0 +1,4 @@ +namespace AssetStoreTools.Previews.Services +{ + public interface IPreviewService { } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs.meta new file mode 100644 index 0000000..4780114 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8c2761fe05638644d8e3a265865beef8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs new file mode 100644 index 0000000..7b2754b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs @@ -0,0 +1,17 @@ +using AssetStoreTools.Utility; + +namespace AssetStoreTools.Previews.Services +{ + internal class PreviewServiceProvider : ServiceProvider + { + public static PreviewServiceProvider Instance => _instance ?? (_instance = new PreviewServiceProvider()); + private static PreviewServiceProvider _instance; + + private PreviewServiceProvider() { } + + protected override void RegisterServices() + { + Register(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs.meta new file mode 100644 index 0000000..3dd4631 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a19bf5a4e3e441047bbc1b894e2a1149 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI.meta new file mode 100644 index 0000000..67d04e3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4738f3648c8368244a968bc840c1152b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data.meta new file mode 100644 index 0000000..b302df5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 23a2f4eadd444194a91ff4ce509e4798 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs new file mode 100644 index 0000000..1b2fee9 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs @@ -0,0 +1,56 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.IO; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal class AssetPreview : IAssetPreview + { + private PreviewMetadata _metadata; + + private UnityEngine.Object _cachedAsset; + private string _cachedAssetPath; + private Texture2D _cachedTexture; + + public UnityEngine.Object Asset => _cachedAsset ?? (_cachedAsset = AssetDatabase.LoadAssetAtPath(AssetPath)); + public string AssetPath => _cachedAssetPath ?? (_cachedAssetPath = AssetDatabase.GUIDToAssetPath(_metadata.Guid)); + + public AssetPreview(PreviewMetadata metadata) + { + _metadata = metadata; + } + + public string GetAssetPath() + { + var assetPath = AssetDatabase.GUIDToAssetPath(_metadata.Guid); + return assetPath; + } + + public async Task LoadImage(Action onSuccess) + { + if (_cachedTexture == null) + { + if (!_metadata.Exists()) + return; + + await Task.Yield(); + + try + { + _cachedTexture = new Texture2D(1, 1); + _cachedTexture.LoadImage(File.ReadAllBytes(_metadata.Path)); + } + catch (Exception e) + { + Debug.LogException(e); + return; + } + } + + onSuccess?.Invoke(_cachedTexture); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs.meta new file mode 100644 index 0000000..580a1b1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 739cf05c689204f4089fd0a6bddb8c3b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs new file mode 100644 index 0000000..40db2b6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs @@ -0,0 +1,46 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal class AssetPreviewCollection : IAssetPreviewCollection + { + private GenerationType _generationType; + private List _images; + + public event Action OnCollectionChanged; + + public AssetPreviewCollection() + { + _images = new List(); + } + + public GenerationType GetGenerationType() + { + return _generationType; + } + + public IEnumerable GetPreviews() + { + return _images; + } + + public void Refresh(GenerationType generationType, IEnumerable previews) + { + _images.Clear(); + + _generationType = generationType; + + foreach (var entry in previews) + { + if (!entry.Exists()) + continue; + + _images.Add(new AssetPreview(entry)); + } + + OnCollectionChanged?.Invoke(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs.meta new file mode 100644 index 0000000..6977abd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9b1a0db8710933048b49dcca463fb8fd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs new file mode 100644 index 0000000..f7fc750 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading.Tasks; +using UnityEngine; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal interface IAssetPreview + { + UnityEngine.Object Asset { get; } + string GetAssetPath(); + Task LoadImage(Action onSuccess); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs.meta new file mode 100644 index 0000000..280bf1c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0f9373dfc16d0fa4794dac29b75204ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs new file mode 100644 index 0000000..6e614c6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs @@ -0,0 +1,15 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal interface IAssetPreviewCollection + { + event Action OnCollectionChanged; + + GenerationType GetGenerationType(); + IEnumerable GetPreviews(); + void Refresh(GenerationType generationType, IEnumerable previews); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs.meta new file mode 100644 index 0000000..d7c7c04 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc9d9abd80c070f44ac49d5ce23d2fc0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs new file mode 100644 index 0000000..51f626c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs @@ -0,0 +1,27 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Generators; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal interface IPreviewGeneratorSettings + { + event Action OnGenerationTypeChanged; + event Action OnGenerationPathsChanged; + + void LoadSettings(PreviewGenerationSettings settings); + + GenerationType GetGenerationType(); + void SetGenerationType(GenerationType type); + List GetAvailableGenerationTypes(); + + List GetGenerationPaths(); + void AddGenerationPath(string path); + void RemoveGenerationPath(string path); + void ClearGenerationPaths(); + bool IsGenerationPathValid(string path, out string error); + + IPreviewGenerator CreateGenerator(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs.meta new file mode 100644 index 0000000..4ba406e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 55c9fcde15f06754588fd02fb8b99a60 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs new file mode 100644 index 0000000..86962e4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs @@ -0,0 +1,212 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Generators; +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal class PreviewGeneratorSettings : IPreviewGeneratorSettings + { + private readonly GenerationType[] _availableGenerationTypes = new GenerationType[] + { + GenerationType.Native, + GenerationType.Custom + }; + + private List _inputPaths; + private GenerationType _generationType; + + public event Action OnGenerationTypeChanged; + public event Action OnGenerationPathsChanged; + + public PreviewGeneratorSettings() + { + _inputPaths = new List(); + _generationType = GenerationType.Native; + } + + public void LoadSettings(PreviewGenerationSettings settings) + { + if (settings == null) + return; + + _inputPaths = settings.InputPaths.ToList(); + OnGenerationPathsChanged?.Invoke(); + + switch (settings) + { + case NativePreviewGenerationSettings _: + _generationType = GenerationType.Native; + break; + case CustomPreviewGenerationSettings _: + _generationType = GenerationType.Custom; + break; + default: + return; + } + + OnGenerationTypeChanged?.Invoke(); + } + + public GenerationType GetGenerationType() + { + return _generationType; + } + + public void SetGenerationType(GenerationType type) + { + _generationType = type; + OnGenerationTypeChanged?.Invoke(); + } + + public List GetAvailableGenerationTypes() + { + return _availableGenerationTypes.ToList(); + } + + public List GetGenerationPaths() + { + return _inputPaths; + } + + public void AddGenerationPath(string path) + { + if (string.IsNullOrEmpty(path)) + return; + + if (_inputPaths.Contains(path)) + return; + + // Prevent redundancy for new paths + var existingPath = _inputPaths.FirstOrDefault(x => path.StartsWith(x + "/")); + if (existingPath != null) + { + Debug.LogWarning($"Path '{path}' is already included with existing path: '{existingPath}'"); + return; + } + + // Prevent redundancy for already added paths + var redundantPaths = _inputPaths.Where(x => x.StartsWith(path + "/")).ToArray(); + foreach (var redundantPath in redundantPaths) + { + Debug.LogWarning($"Existing validation path '{redundantPath}' has been made redundant by the inclusion of new validation path: '{path}'"); + _inputPaths.Remove(redundantPath); + } + + _inputPaths.Add(path); + + OnGenerationPathsChanged?.Invoke(); + } + + public void RemoveGenerationPath(string path) + { + if (!_inputPaths.Contains(path)) + return; + + _inputPaths.Remove(path); + + OnGenerationPathsChanged?.Invoke(); + } + + public void ClearGenerationPaths() + { + if (_inputPaths.Count == 0) + return; + + _inputPaths.Clear(); + + OnGenerationPathsChanged?.Invoke(); + } + + public bool IsGenerationPathValid(string path, out string error) + { + error = string.Empty; + + if (string.IsNullOrEmpty(path)) + { + error = "Path cannot be empty"; + return false; + } + + var isAssetsPath = path.StartsWith("Assets/") + || path.Equals("Assets"); + var isPackagePath = PackageUtility.GetPackageByManifestPath($"{path}/package.json", out _); + + if (!isAssetsPath && !isPackagePath) + { + error = "Selected path must be within the Assets folder or point to a root path of a package"; + return false; + } + + if (!Directory.Exists(path)) + { + error = "Path does not exist"; + return false; + } + + if (path.Split('/').Any(x => x.StartsWith(".") || x.EndsWith("~"))) + { + error = $"Path '{path}' cannot be selected as it is a hidden folder and not part of the Asset Database"; + return false; + } + + return true; + } + + public IPreviewGenerator CreateGenerator() + { + switch (_generationType) + { + case GenerationType.Native: + return CreateNativeGenerator(); + case GenerationType.Custom: + return CreateCustomGenerator(); + default: + throw new NotImplementedException("Undefined generator type"); + } + } + + private IPreviewGenerator CreateNativeGenerator() + { + var settings = new NativePreviewGenerationSettings() + { + InputPaths = _inputPaths.ToArray(), + OutputPath = Constants.Previews.Native.DefaultOutputPath, + PreviewFileNamingFormat = Constants.Previews.DefaultFileNameFormat, + Format = Constants.Previews.Native.DefaultFormat, + WaitForPreviews = Constants.Previews.Native.DefaultWaitForPreviews, + ChunkedPreviewLoading = Constants.Previews.Native.DefaultChunkedPreviewLoading, + ChunkSize = Constants.Previews.Native.DefaultChunkSize, + OverwriteExisting = true + }; + + return new NativePreviewGenerator(settings); + } + + private IPreviewGenerator CreateCustomGenerator() + { + var settings = new CustomPreviewGenerationSettings() + { + InputPaths = _inputPaths.ToArray(), + OutputPath = Constants.Previews.Custom.DefaultOutputPath, + Width = Constants.Previews.Custom.DefaultWidth, + Height = Constants.Previews.Custom.DefaultHeight, + Depth = Constants.Previews.Custom.DefaultDepth, + NativeWidth = Constants.Previews.Custom.DefaultNativeWidth, + NativeHeight = Constants.Previews.Custom.DefaultNativeHeight, + PreviewFileNamingFormat = Constants.Previews.DefaultFileNameFormat, + Format = Constants.Previews.Custom.DefaultFormat, + AudioSampleColor = Constants.Previews.Custom.DefaultAudioSampleColor, + AudioBackgroundColor = Constants.Previews.Custom.DefaultAudioBackgroundColor, + OverwriteExisting = true + }; + + var generator = new CustomPreviewGenerator(settings); + return generator; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs.meta new file mode 100644 index 0000000..1de481b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e6f754b1179d8d4cb40f62692619a63 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements.meta new file mode 100644 index 0000000..d81acd5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 700ec0107b011824892281e880281bb1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs new file mode 100644 index 0000000..aee6fa8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs @@ -0,0 +1,83 @@ +using AssetStoreTools.Previews.UI.Data; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class AssetPreviewElement : VisualElement + { + // Data + private IAssetPreview _assetPreview; + + // UI + private Image _image; + private Label _label; + + public AssetPreviewElement() + { + AddToClassList("preview-list-image"); + + Create(); + + RegisterCallback(OnImageClicked); + } + + private void Create() + { + CreateFiller(); + CreateImage(); + CreateLabel(); + } + + private void CreateImage() + { + _image = new Image(); + Add(_image); + } + + private void CreateFiller() + { + var filler = new VisualElement() { name = "Filler" }; + Add(filler); + } + + private void CreateLabel() + { + _label = new Label(); + Add(_label); + } + + private void SetImage(Texture2D texture) + { + _image.style.width = texture.width < 128 ? texture.width : 128; + _image.style.height = texture.height < 128 ? texture.height : 128; + _image.style.backgroundImage = texture; + } + + private void OnImageClicked(MouseDownEvent _) + { + EditorGUIUtility.PingObject(_assetPreview.Asset); + } + + public void SetSource(IAssetPreview assetPreview) + { + _assetPreview = assetPreview; + _assetPreview.LoadImage(SetImage); + + var assetPath = _assetPreview.GetAssetPath(); + + if (string.IsNullOrEmpty(assetPath)) + { + _label.text = "[Missing]"; + tooltip = "This asset has been deleted"; + return; + } + + var assetNameWithExtension = assetPath.Split('/').Last(); + _label.text = assetNameWithExtension; + tooltip = assetPath; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs.meta new file mode 100644 index 0000000..56990b4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 28891b8cff841a44eb508494d62c190c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs new file mode 100644 index 0000000..a1bf53a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class GridListElement : VisualElement + { + public int ElementWidth; + public int ElementHeight; + private int _visibilityHeadroom => ElementHeight; + + public IList ItemSource; + public Func MakeItem; + public Action BindItem; + + private ScrollView _scrollView; + + public GridListElement() + { + style.flexGrow = 1; + + Create(); + + _scrollView.contentViewport.RegisterCallback(OnGeometryChanged); + _scrollView.verticalScroller.valueChanged += OnVerticalScroll; +#if UNITY_2021_1_OR_NEWER + _scrollView.horizontalScrollerVisibility = ScrollerVisibility.Hidden; +#else + _scrollView.showHorizontal = false; +#endif + } + + private void Create() + { + _scrollView = new ScrollView(); + Add(_scrollView); + } + + private void OnGeometryChanged(GeometryChangedEvent evt) + { + Redraw(); + } + + private void OnVerticalScroll(float value) + { + Redraw(); + } + + public void Redraw() + { + if (ElementWidth == 0 + || ElementHeight == 0 + || ItemSource == null + || MakeItem == null + || BindItem == null) + return; + + _scrollView.Clear(); + + var rowCapacity = Mathf.FloorToInt(_scrollView.contentContainer.worldBound.width / ElementWidth); + if (rowCapacity == 0) + rowCapacity = 1; + + var totalRequiredRows = ItemSource.Count / rowCapacity; + if (ItemSource.Count % rowCapacity != 0) + totalRequiredRows++; + + _scrollView.contentContainer.style.height = totalRequiredRows * ElementHeight; + + var visibleRows = new List(); + for (int i = 0; i < totalRequiredRows; i++) + { + var visible = IsRowVisible(i); + if (!visible) + continue; + + var rowElement = CreateRow(i); + + for (int j = 0; j < rowCapacity; j++) + { + var elementIndex = i * rowCapacity + j; + if (elementIndex >= ItemSource.Count) + { + rowElement.Add(CreateFillerElement()); + continue; + } + + var element = MakeItem?.Invoke(); + BindItem?.Invoke(element, elementIndex); + + rowElement.Add(element); + } + + _scrollView.Add(rowElement); + } + } + + private bool IsRowVisible(int rowIndex) + { + var contentStartY = _scrollView.contentContainer.worldBound.yMin; + var visibleContentMinY = _scrollView.contentViewport.worldBound.yMin - _visibilityHeadroom; + var visibleContentMaxY = _scrollView.contentViewport.worldBound.yMax + _visibilityHeadroom; + if (_scrollView.contentViewport.worldBound.height == 0) + visibleContentMaxY = this.worldBound.yMax; + + var rowMinY = (rowIndex * ElementHeight) + contentStartY; + var rowMaxY = (rowIndex * ElementHeight) + ElementHeight + contentStartY; + + var fullyVisible = rowMinY >= visibleContentMinY && rowMaxY <= visibleContentMaxY; + var partiallyAbove = rowMinY < visibleContentMinY && rowMaxY > visibleContentMinY; + var partiallyBelow = rowMaxY > visibleContentMaxY && rowMinY < visibleContentMaxY; + + return fullyVisible || partiallyAbove || partiallyBelow; + } + + private VisualElement CreateRow(int rowIndex) + { + var rowElement = new VisualElement() { name = $"Row {rowIndex}" }; + rowElement.style.flexDirection = FlexDirection.Row; + rowElement.style.position = Position.Absolute; + rowElement.style.top = ElementHeight * rowIndex; + rowElement.style.width = _scrollView.contentViewport.worldBound.width; + rowElement.style.justifyContent = Justify.SpaceAround; + + return rowElement; + } + + private VisualElement CreateFillerElement() + { + var element = new VisualElement(); + element.style.width = ElementWidth; + element.style.height = ElementHeight; + + return element; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs.meta new file mode 100644 index 0000000..bae1ff9 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 81d9f779e8c2a464cbdc1e39a4864803 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs new file mode 100644 index 0000000..28a35b1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs @@ -0,0 +1,116 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.UI.Data; +using System.Linq; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.SceneManagement; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class PreviewCollectionElement : VisualElement + { + // Data + private IAssetPreviewCollection _collection; + + // UI + private Label _previewCountLabel; + private GridListElement _gridListElement; + + public PreviewCollectionElement(IAssetPreviewCollection collection) + { + AddToClassList("preview-list"); + + _collection = collection; + _collection.OnCollectionChanged += RefreshList; + + Create(); + RefreshList(); + + SubscribeToSceneChanges(); + } + + private void Create() + { + CreateLabel(); + CreateGridListElement(); + } + + private void CreateLabel() + { + _previewCountLabel = new Label(); + _previewCountLabel.style.display = DisplayStyle.None; + Add(_previewCountLabel); + } + + private void CreateGridListElement() + { + _gridListElement = new GridListElement(); + _gridListElement.MakeItem = CreatePreview; + _gridListElement.BindItem = BindPreview; + _gridListElement.ElementWidth = 140 + 10; // Accounting for margin style + _gridListElement.ElementHeight = 160 + 10; // Accounting for margin style + Add(_gridListElement); + } + + private VisualElement CreatePreview() + { + var preview = new AssetPreviewElement(); + return preview; + } + + private void BindPreview(VisualElement element, int index) + { + var previewElement = (AssetPreviewElement)element; + var preview = _collection.GetPreviews().ToList()[index]; + previewElement.SetSource(preview); + } + + private void RefreshList() + { + var type = _collection.GetGenerationType(); + var items = _collection.GetPreviews().ToList(); + _previewCountLabel.text = $"Displaying {items.Count} {ConvertGenerationTypeName(type)} previews"; + _previewCountLabel.style.display = DisplayStyle.Flex; + _previewCountLabel.style.alignSelf = Align.Center; + _previewCountLabel.style.marginBottom = 10; + _previewCountLabel.style.unityFontStyleAndWeight = FontStyle.Bold; + + _gridListElement.ItemSource = items; + _gridListElement.Redraw(); + } + + private string ConvertGenerationTypeName(GenerationType type) + { + switch (type) + { + case GenerationType.Custom: + return "high resolution"; + default: + return type.ToString().ToLower(); + } + } + + private void SubscribeToSceneChanges() + { + var windowToSubscribeTo = Resources.FindObjectsOfTypeAll().FirstOrDefault(); + UnityAction sceneChanged = null; + sceneChanged = new UnityAction((_, __) => RefreshObjects(windowToSubscribeTo)); + EditorSceneManager.activeSceneChangedInEditMode += sceneChanged; + + void RefreshObjects(PreviewGeneratorWindow subscribedWindow) + { + // Remove callback if preview generator window instance changed + var activeWindow = Resources.FindObjectsOfTypeAll().FirstOrDefault(); + if (subscribedWindow == null || subscribedWindow != activeWindow) + { + EditorSceneManager.activeSceneChangedInEditMode -= sceneChanged; + return; + } + + RefreshList(); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs.meta new file mode 100644 index 0000000..3068d08 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 842a11e046ca5284d9de9f4a05b1fa26 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs new file mode 100644 index 0000000..3d2f9a8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs @@ -0,0 +1,50 @@ +using AssetStoreTools.Previews.UI.Data; +using System; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class PreviewGenerateButtonElement : VisualElement + { + // Data + private IPreviewGeneratorSettings _settings; + + // UI + private Button _generateButton; + + public event Action OnGenerate; + + public PreviewGenerateButtonElement(IPreviewGeneratorSettings settings) + { + _settings = settings; + _settings.OnGenerationPathsChanged += GenerationPathsChanged; + + Create(); + Deserialize(); + } + + private void Create() + { + _generateButton = new Button(Validate) { text = "Generate" }; + _generateButton.AddToClassList("preview-generate-button"); + + Add(_generateButton); + } + + private void Validate() + { + OnGenerate?.Invoke(); + } + + private void GenerationPathsChanged() + { + var inputPathsPresent = _settings.GetGenerationPaths().Count > 0; + _generateButton.SetEnabled(inputPathsPresent); + } + + private void Deserialize() + { + GenerationPathsChanged(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs.meta new file mode 100644 index 0000000..1f3a7b4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c8fbb0b13ba7d3479c0867c440821e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs new file mode 100644 index 0000000..cf72440 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs @@ -0,0 +1,122 @@ +using AssetStoreTools.Previews.UI.Data; +using AssetStoreTools.Utility; +using UnityEditor; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Validator.UI.Elements +{ + internal class PreviewGeneratorPathsElement : VisualElement + { + // Data + private IPreviewGeneratorSettings _settings; + + // UI + private ScrollView _pathBoxScrollView; + + public PreviewGeneratorPathsElement(IPreviewGeneratorSettings settings) + { + AddToClassList("preview-paths"); + + _settings = settings; + _settings.OnGenerationPathsChanged += InputPathsChanged; + + Create(); + Deserialize(); + } + + private void Create() + { + var pathSelectionRow = new VisualElement(); + pathSelectionRow.AddToClassList("preview-settings-selection-row"); + + VisualElement labelHelpRow = new VisualElement(); + labelHelpRow.AddToClassList("preview-settings-selection-label-help-row"); + labelHelpRow.style.alignSelf = Align.FlexStart; + + Label pathLabel = new Label { text = "Input paths" }; + Image pathLabelTooltip = new Image + { + tooltip = "Select the folder (or multiple folders) to generate asset previews for." + }; + + labelHelpRow.Add(pathLabel); + labelHelpRow.Add(pathLabelTooltip); + + var fullPathBox = new VisualElement() { name = "PreviewPaths" }; + fullPathBox.AddToClassList("preview-paths-box"); + + _pathBoxScrollView = new ScrollView { name = "PreviewPathsScrollView" }; + _pathBoxScrollView.AddToClassList("preview-paths-scroll-view"); + + VisualElement scrollViewBottomRow = new VisualElement(); + scrollViewBottomRow.AddToClassList("preview-paths-scroll-view-bottom-row"); + + var addExtraPathsButton = new Button(BrowsePath) { text = "Add a path" }; + addExtraPathsButton.AddToClassList("preview-paths-add-button"); + scrollViewBottomRow.Add(addExtraPathsButton); + + fullPathBox.Add(_pathBoxScrollView); + fullPathBox.Add(scrollViewBottomRow); + + pathSelectionRow.Add(labelHelpRow); + pathSelectionRow.Add(fullPathBox); + + Add(pathSelectionRow); + } + + private VisualElement CreateSinglePathElement(string path) + { + var validationPath = new VisualElement(); + validationPath.AddToClassList("preview-paths-path-row"); + + var folderPathLabel = new Label(path); + folderPathLabel.AddToClassList("preview-paths-path-row-input-field"); + + var removeButton = new Button(() => + { + _settings.RemoveGenerationPath(path); + }); + removeButton.text = "X"; + removeButton.AddToClassList("preview-paths-path-row-remove-button"); + + validationPath.Add(folderPathLabel); + validationPath.Add(removeButton); + + return validationPath; + } + + private void BrowsePath() + { + string absolutePath = EditorUtility.OpenFolderPanel("Select a directory", "Assets", ""); + + if (string.IsNullOrEmpty(absolutePath)) + return; + + var relativePath = FileUtility.AbsolutePathToRelativePath(absolutePath, ASToolsPreferences.Instance.EnableSymlinkSupport); + + if (!_settings.IsGenerationPathValid(relativePath, out var error)) + { + EditorUtility.DisplayDialog("Invalid path", error, "OK"); + return; + } + + _settings.AddGenerationPath(relativePath); + } + + private void InputPathsChanged() + { + var inputPaths = _settings.GetGenerationPaths(); + + _pathBoxScrollView.Clear(); + foreach (var path in inputPaths) + { + _pathBoxScrollView.Add(CreateSinglePathElement(path)); + } + } + + private void Deserialize() + { + InputPathsChanged(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs.meta new file mode 100644 index 0000000..afdcc93 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cd3e3f7fbfc5f1e46835438be2756746 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs new file mode 100644 index 0000000..d5268f5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs @@ -0,0 +1,99 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.UI.Data; +using AssetStoreTools.Validator.UI.Elements; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class PreviewGeneratorSettingsElement : VisualElement + { + // Data + private IPreviewGeneratorSettings _settings; + + // UI + private PreviewGeneratorPathsElement _previewPathsElement; + private ToolbarMenu _generationTypeMenu; + + public PreviewGeneratorSettingsElement(IPreviewGeneratorSettings settings) + { + AddToClassList("preview-settings"); + + _settings = settings; + _settings.OnGenerationTypeChanged += GenerationTypeChanged; + + Create(); + Deserialize(); + } + + private void Create() + { + CreateGenerationType(); + CreateInputPathsElement(); + } + + private void CreateInputPathsElement() + { + _previewPathsElement = new PreviewGeneratorPathsElement(_settings); + Add(_previewPathsElement); + } + + private void CreateGenerationType() + { + var typeSelectionBox = new VisualElement(); + typeSelectionBox.AddToClassList("preview-settings-selection-row"); + + VisualElement labelHelpRow = new VisualElement(); + labelHelpRow.AddToClassList("preview-settings-selection-label-help-row"); + + Label generationTypeLabel = new Label { text = "Generation type" }; + Image categoryLabelTooltip = new Image + { + tooltip = "Choose the generation type for your previews.\n\n" + + "- Native: retrieve previews from the Asset Database which are generated by Unity Editor internally\n" + + "- High Resolution (experimental): generate previews using a custom implementation. Resulting previews are of higher resolution " + + "than those generated by Unity Editor. Note that they may look slightly different from native previews" + }; + + labelHelpRow.Add(generationTypeLabel); + labelHelpRow.Add(categoryLabelTooltip); + + _generationTypeMenu = new ToolbarMenu { name = "GenerationTypeMenu" }; + _generationTypeMenu.AddToClassList("preview-settings-selection-dropdown"); + + typeSelectionBox.Add(labelHelpRow); + typeSelectionBox.Add(_generationTypeMenu); + + // Append available categories + var types = _settings.GetAvailableGenerationTypes(); + foreach (var t in types) + { + _generationTypeMenu.menu.AppendAction(ConvertGenerationTypeName(t), _ => _settings.SetGenerationType(t)); + } + + Add(typeSelectionBox); + } + + private string ConvertGenerationTypeName(GenerationType type) + { + switch (type) + { + case GenerationType.Custom: + return "High Resolution (experimental)"; + default: + return type.ToString(); + } + } + + private void GenerationTypeChanged() + { + var t = _settings.GetGenerationType(); + _generationTypeMenu.text = ConvertGenerationTypeName(t); + } + + private void Deserialize() + { + GenerationTypeChanged(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs.meta new file mode 100644 index 0000000..6e1abfb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6f38de8a438b8c94a81fe5f2cc45c110 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs new file mode 100644 index 0000000..1306f05 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs @@ -0,0 +1,87 @@ +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class PreviewWindowDescriptionElement : VisualElement + { + private const string DescriptionFoldoutText = "Generate and inspect asset preview images to be displayed in your package listing page on the Asset Store."; + private const string DescriptionFoldoutContentText = "Images generated in this window will be reused when exporting a package. Any missing images generated during the package export process will also appear here.\n\n" + + "Preview images are displayed on the Asset Store under the 'Package Content' section of the package listing. " + + "They are also displayed in the package importer window that appears during the package import process. " + + "Note that these images will not replace the images used for the assets in the Project window after the package gets imported."; + + private VisualElement _descriptionSimpleContainer; + private Button _showMoreButton; + + private VisualElement _descriptionFullContainer; + private Button _showLessButton; + + public PreviewWindowDescriptionElement() + { + AddToClassList("asset-preview-description"); + Create(); + } + + private void Create() + { + CreateSimpleDescription(); + CreateFullDescription(); + } + + private void CreateSimpleDescription() + { + _descriptionSimpleContainer = new VisualElement(); + _descriptionSimpleContainer.AddToClassList("asset-preview-description-simple-container"); + + var simpleDescription = new Label(DescriptionFoldoutText); + simpleDescription.AddToClassList("asset-preview-description-simple-label"); + + _showMoreButton = new Button(ToggleFullDescription) { text = "Show more..." }; + _showMoreButton.AddToClassList("asset-preview-description-hyperlink-button"); + _showMoreButton.AddToClassList("asset-preview-description-show-button"); + + _descriptionSimpleContainer.Add(simpleDescription); + _descriptionSimpleContainer.Add(_showMoreButton); + + Add(_descriptionSimpleContainer); + } + + private void CreateFullDescription() + { + _descriptionFullContainer = new VisualElement(); + _descriptionFullContainer.AddToClassList("asset-preview-description-full-container"); + + var validatorDescription = new Label() + { + text = DescriptionFoldoutContentText + }; + validatorDescription.AddToClassList("asset-preview-description-full-label"); + + _showLessButton = new Button(ToggleFullDescription) { text = "Show less..." }; + _showLessButton.AddToClassList("asset-preview-description-hide-button"); + _showLessButton.AddToClassList("asset-preview-description-hyperlink-button"); + + _descriptionFullContainer.Add(validatorDescription); + _descriptionFullContainer.Add(_showLessButton); + + _descriptionFullContainer.style.display = DisplayStyle.None; + Add(_descriptionFullContainer); + } + + private void ToggleFullDescription() + { + var displayFullDescription = _descriptionFullContainer.style.display == DisplayStyle.None; + + if (displayFullDescription) + { + _showMoreButton.style.display = DisplayStyle.None; + _descriptionFullContainer.style.display = DisplayStyle.Flex; + } + else + { + _showMoreButton.style.display = DisplayStyle.Flex; + _descriptionFullContainer.style.display = DisplayStyle.None; + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs.meta new file mode 100644 index 0000000..20f5147 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2cab289a87b0ba74f89cb458ff6d44f8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs new file mode 100644 index 0000000..3a2d36f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs @@ -0,0 +1,51 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Services; +using AssetStoreTools.Previews.UI.Views; +using AssetStoreTools.Utility; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI +{ + internal class PreviewGeneratorWindow : AssetStoreToolsWindow + { + protected override string WindowTitle => "Preview Generator"; + + private ICachingService _cachingService; + + private PreviewListView _previewListView; + + protected override void Init() + { + minSize = new Vector2(350, 350); + + this.SetAntiAliasing(4); + + VisualElement root = rootVisualElement; + + // Getting a reference to the USS Document and adding stylesheet to the root + root.styleSheets.Add(StyleSelector.PreviewGeneratorWindow.PreviewGeneratorWindowStyle); + root.styleSheets.Add(StyleSelector.PreviewGeneratorWindow.PreviewGeneratorWindowTheme); + + GetServices(); + ConstructWindow(); + } + + private void GetServices() + { + _cachingService = PreviewServiceProvider.Instance.GetService(); + } + + private void ConstructWindow() + { + _previewListView = new PreviewListView(_cachingService); + rootVisualElement.Add(_previewListView); + } + + public void Load(PreviewGenerationSettings settings) + { + _previewListView.LoadSettings(settings); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs.meta new file mode 100644 index 0000000..c6f537e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4cad15de2de8cdc46b48a4b05eac5d78 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views.meta new file mode 100644 index 0000000..598279e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5e154861b0e2af64b93f6c831e6c0dc2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs new file mode 100644 index 0000000..36f5264 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs @@ -0,0 +1,142 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Services; +using AssetStoreTools.Previews.UI.Data; +using AssetStoreTools.Previews.UI.Elements; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Views +{ + internal class PreviewListView : VisualElement + { + //Data + private PreviewDatabase _previewDatabase; + private IPreviewGeneratorSettings _previewGeneratorSettings; + private IAssetPreviewCollection _previewCollection; + + private ICachingService _cachingService; + + // UI + private PreviewWindowDescriptionElement _descriptionElement; + private PreviewGeneratorSettingsElement _settingsElement; + private PreviewGenerateButtonElement _generateButtonElement; + private PreviewCollectionElement _previewCollectionElement; + + public PreviewListView(ICachingService cachingService) + { + _cachingService = cachingService; + + _previewGeneratorSettings = new PreviewGeneratorSettings(); + _previewCollection = new AssetPreviewCollection(); + + _previewGeneratorSettings.OnGenerationTypeChanged += RefreshPreviewList; + _previewGeneratorSettings.OnGenerationPathsChanged += RefreshPreviewList; + + Create(); + RefreshPreviewList(); + } + + private void Create() + { + CreateDescription(); + CreateSettings(); + CreateGenerateButton(); + CreatePreviewList(); + } + + private void CreateDescription() + { + _descriptionElement = new PreviewWindowDescriptionElement(); + Add(_descriptionElement); + } + + private void CreateSettings() + { + _settingsElement = new PreviewGeneratorSettingsElement(_previewGeneratorSettings); + Add(_settingsElement); + } + + private void CreateGenerateButton() + { + _generateButtonElement = new PreviewGenerateButtonElement(_previewGeneratorSettings); + _generateButtonElement.OnGenerate += GeneratePreviews; + Add(_generateButtonElement); + } + + private void CreatePreviewList() + { + _previewCollectionElement = new PreviewCollectionElement(_previewCollection); + Add(_previewCollectionElement); + } + + private async void GeneratePreviews() + { + try + { + _settingsElement.SetEnabled(false); + _generateButtonElement.SetEnabled(false); + _previewCollectionElement.SetEnabled(false); + + var generator = _previewGeneratorSettings.CreateGenerator(); + generator.OnProgressChanged += DisplayProgress; + var result = await generator.Generate(); + generator.OnProgressChanged -= DisplayProgress; + + if (!result.Success) + { + EditorUtility.DisplayDialog("Error", result.Exception.Message, "OK"); + Debug.LogException(result.Exception); + return; + } + + RefreshPreviewList(); + } + finally + { + _settingsElement.SetEnabled(true); + _generateButtonElement.SetEnabled(true); + _previewCollectionElement.SetEnabled(true); + EditorUtility.ClearProgressBar(); + } + } + + private void DisplayProgress(float progress) + { + EditorUtility.DisplayProgressBar("Generating", "Generating previews...", progress); + } + + public void LoadSettings(PreviewGenerationSettings settings) + { + _previewGeneratorSettings.LoadSettings(settings); + } + + private void RefreshPreviewList() + { + if (!_cachingService.GetCachedMetadata(out _previewDatabase)) + _previewDatabase = new PreviewDatabase(); + + var paths = _previewGeneratorSettings.GetGenerationPaths(); + var guids = AssetDatabase.FindAssets("", paths.ToArray()); + var displayedPreviews = new List(); + + foreach (var entry in _previewDatabase.Previews) + { + if (!entry.Exists()) + continue; + + if (entry.Type != _previewGeneratorSettings.GetGenerationType()) + continue; + + if (!guids.Any(x => x == entry.Guid)) + continue; + + displayedPreviews.Add(entry); + } + + _previewCollection.Refresh(_previewGeneratorSettings.GetGenerationType(), displayedPreviews); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs.meta new file mode 100644 index 0000000..7ddbc60 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 94d417240bb510d469acb8a11f15b277 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility.meta new file mode 100644 index 0000000..92fdd4f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 99cf24252c136f246bfa4b02a69fe992 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs new file mode 100644 index 0000000..1a304f0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs @@ -0,0 +1,96 @@ +using UnityEngine; + +namespace AssetStoreTools.Previews.Utility +{ + internal static class GraphicsUtility + { + public static Texture2D GetTextureFromCamera(Camera camera, int desiredWidth, int desiredHeight, int desiredDepth) + { + var texture = new Texture2D(desiredWidth, desiredHeight); + var originalRenderTexture = RenderTexture.active; + var renderTexture = RenderTexture.GetTemporary(desiredWidth, desiredHeight, desiredDepth); + var cameraInitiallyEnabled = camera.enabled; + + try + { + if (cameraInitiallyEnabled) + camera.enabled = false; + + camera.targetTexture = renderTexture; + camera.Render(); + + RenderTexture.active = renderTexture; + texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); + texture.Apply(); + } + finally + { + camera.targetTexture = null; + RenderTexture.active = originalRenderTexture; + RenderTexture.ReleaseTemporary(renderTexture); + camera.enabled = cameraInitiallyEnabled; + } + + return texture; + } + + public static Texture2D ResizeTexture(Texture2D source, int desiredWidth, int desiredHeight) + { + var texture = new Texture2D(desiredWidth, desiredHeight); + var originalRenderTexture = RenderTexture.active; + var renderTexture = RenderTexture.GetTemporary(desiredWidth, desiredHeight, 32); + + try + { + RenderTexture.active = renderTexture; + Graphics.Blit(source, renderTexture); + + texture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); + texture.Apply(); + } + finally + { + RenderTexture.active = originalRenderTexture; + RenderTexture.ReleaseTemporary(renderTexture); + } + + return texture; + } + + public static Texture2D ResizeTextureNormalMap(Texture2D source, int desiredWidth, int desiredHeight) + { + var texture = new Texture2D(desiredWidth, desiredHeight); + var originalRenderTexture = RenderTexture.active; + var renderTexture = RenderTexture.GetTemporary(desiredWidth, desiredHeight, 32, RenderTextureFormat.Default, RenderTextureReadWrite.Linear); + + try + { + RenderTexture.active = renderTexture; + Graphics.Blit(source, renderTexture); + + texture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); + + for (int i = 0; i < texture.width; i++) + { + for (int j = 0; j < texture.height; j++) + { + var color = texture.GetPixel(i, j); + color.b = color.r; + color.r = color.a; + color.a = 1; + texture.SetPixel(i, j, color); + } + } + + texture.Apply(); + } + finally + { + RenderTexture.active = originalRenderTexture; + RenderTexture.ReleaseTemporary(renderTexture); + } + + return texture; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs.meta new file mode 100644 index 0000000..04602e6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0a4fc8f266b4dd41a59693dd581e232 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs new file mode 100644 index 0000000..ab93f97 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs @@ -0,0 +1,72 @@ +using AssetStoreTools.Previews.Data; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Utility +{ + internal static class PreviewConvertUtility + { + public static string ConvertFilename(Object asset, FileNameFormat format) + { + string fileName = string.Empty; + + switch (format) + { + case FileNameFormat.Guid: + AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out var guid, out long _); + fileName = guid; + break; + case FileNameFormat.FullAssetPath: + var assetPath = AssetDatabase.GetAssetPath(asset); + + if (assetPath.StartsWith("Assets/")) + fileName = assetPath.Substring("Assets/".Length); + else if (assetPath.StartsWith("Packages/")) + fileName = assetPath.Substring("Packages/".Length); + + fileName = fileName.Replace("/", "_"); + break; + case FileNameFormat.AssetName: + fileName = asset.name; + break; + default: + throw new System.Exception("Undefined format"); + } + + return fileName; + } + + public static string ConvertExtension(PreviewFormat format) + { + switch (format) + { + case PreviewFormat.JPG: + return "jpg"; + case PreviewFormat.PNG: + return "png"; + default: + throw new System.Exception("Undefined format"); + } + } + + public static string ConvertFilenameWithExtension(Object asset, FileNameFormat nameFormat, PreviewFormat imageFormat) + { + var filename = ConvertFilename(asset, nameFormat); + var extension = ConvertExtension(imageFormat); + return $"{filename}.{extension}"; + } + + public static byte[] ConvertTexture(Texture2D texture, PreviewFormat format) + { + switch (format) + { + case PreviewFormat.JPG: + return texture.EncodeToJPG(); + case PreviewFormat.PNG: + return texture.EncodeToPNG(); + default: + throw new System.Exception("Undefined format"); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs.meta new file mode 100644 index 0000000..241997d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 700eaf82299628d44853599774664bea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs new file mode 100644 index 0000000..8ca2b3a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs @@ -0,0 +1,196 @@ +using System; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +#if AST_URP_AVAILABLE +using UnityEngine.Rendering.Universal; +#endif +#if AST_HDRP_AVAILABLE +using UnityEngine.Rendering; +using UnityEngine.Rendering.HighDefinition; +#endif + +namespace AssetStoreTools.Previews.Utility +{ + internal static class PreviewSceneUtility + { + private const string PreviewSceneName = "Preview Generation In Progress"; + private static readonly Color BackgroundColor = new Color(82f / 255, 82f / 255, 82f / 255); + private static readonly Color BackgroundColorHDRP = new Color(38f / 255, 38f / 255, 38f / 255); + + public static async Task OpenPreviewSceneForCurrentPipeline() + { + // Wait for an Editor frame to avoid recursive player loop internal errors + await WaitForEditorUpdate(); + + switch (RenderPipelineUtility.GetCurrentPipeline()) + { + case RenderPipeline.BiRP: + await OpenPreviewSceneBiRP(); + break; +#if AST_URP_AVAILABLE + case RenderPipeline.URP: + await OpenPreviewSceneURP(); + break; +#endif +#if AST_HDRP_AVAILABLE + case RenderPipeline.HDRP: + await OpenPreviewSceneHDRP(); + break; +#endif + default: + throw new NotImplementedException("Undefined Render Pipeline"); + } + } + + private static async Task WaitForEditorUpdate() + { + var updateCalled = false; + var delayCalled = false; + + void Update() + { + EditorApplication.update -= Update; + updateCalled = true; + } + + EditorApplication.update += Update; + while (!updateCalled) + await Task.Delay(10); + + void DelayCall() + { + EditorApplication.delayCall -= DelayCall; + delayCalled = true; + } + + EditorApplication.delayCall += DelayCall; + while (!delayCalled) + await Task.Delay(10); + } + + public static async Task OpenPreviewSceneBiRP() + { + OpenNewScene(); + + CreateSceneCamera(); + CreateSceneLighting(); + + await WaitForLighting(); + } + + private static void OpenNewScene() + { + EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo(); + var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); + scene.name = PreviewSceneName; + } + + private static Camera CreateSceneCamera() + { + var cameraGO = new GameObject() { name = "Camera" }; + var camera = cameraGO.AddComponent(); + camera.enabled = false; + camera.tag = "MainCamera"; + + camera.nearClipPlane = 0.01f; + camera.farClipPlane = 100000; + camera.clearFlags = CameraClearFlags.SolidColor; + camera.backgroundColor = BackgroundColor; + + return camera; + } + + private static Light CreateSceneLighting() + { + var lightGO = new GameObject() { name = "Lights" }; + lightGO.transform.rotation = Quaternion.Euler(45, 225, 0); + var light = lightGO.AddComponent(); + light.intensity = 0.75f; + light.type = LightType.Directional; + light.shadows = LightShadows.None; + + return light; + } + + private static async Task WaitForLighting() + { + while (!DynamicGI.isConverged) + await Task.Delay(100); + + await Task.Yield(); + } + +#if AST_URP_AVAILABLE + public static async Task OpenPreviewSceneURP() + { + OpenNewScene(); + + var camera = CreateSceneCamera(); + camera.gameObject.AddComponent(); + + var lighting = CreateSceneLighting(); + lighting.intensity = 0.5f; + lighting.gameObject.AddComponent(); + + await WaitForLighting(); + } +#endif + +#if AST_HDRP_AVAILABLE + public static async Task OpenPreviewSceneHDRP() + { + OpenNewScene(); + + var camera = CreateSceneCamera(); + var cameraData = camera.gameObject.AddComponent(); + cameraData.clearColorMode = HDAdditionalCameraData.ClearColorMode.Color; + cameraData.backgroundColorHDR = BackgroundColorHDRP; + + var light = CreateSceneLighting(); + var lightData = light.gameObject.AddComponent(); + lightData.SetIntensity(5000, LightUnit.Lux); + + CreateHDRPVolumeProfile(); + + await WaitForLighting(); + } + + private static Volume CreateHDRPVolumeProfile() + { + var volumeGO = new GameObject() { name = "Volume" }; + var volume = volumeGO.gameObject.AddComponent(); + + var profile = VolumeProfile.CreateInstance(); + volume.profile = profile; + volume.isGlobal = true; + + var exposure = profile.Add(); + exposure.active = true; + + exposure.mode.overrideState = true; + exposure.mode.value = ExposureMode.Fixed; + + exposure.fixedExposure.overrideState = true; + exposure.fixedExposure.value = 11; + + var fog = profile.Add(); + fog.active = true; + + fog.enabled.overrideState = true; + fog.enabled.value = false; + +#if AST_HDRP_AVAILABLE_V12 + var volumetricClouds = profile.Add(); + volumetricClouds.active = true; + + volumetricClouds.enable.overrideState = true; + volumetricClouds.enable.value = false; +#endif + + return volume; + } +#endif + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs.meta new file mode 100644 index 0000000..9f0056a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 63fa5650920e7914dae6fe76badac249 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs new file mode 100644 index 0000000..028f96d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs @@ -0,0 +1,10 @@ +namespace AssetStoreTools.Previews.Utility +{ + internal enum RenderPipeline + { + Unknown = 0, + BiRP = 1, + URP = 2, + HDRP = 3 + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs.meta new file mode 100644 index 0000000..1d40470 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c43c7ce2b9090ab49bb8944bc6bdb3c7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs new file mode 100644 index 0000000..c8e79d5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs @@ -0,0 +1,32 @@ +using UnityEngine.Rendering; +#if AST_URP_AVAILABLE +using UnityEngine.Rendering.Universal; +#endif +#if AST_HDRP_AVAILABLE +using UnityEngine.Rendering.HighDefinition; +#endif + +namespace AssetStoreTools.Previews.Utility +{ + internal static class RenderPipelineUtility + { + public static RenderPipeline GetCurrentPipeline() + { + var currentPipelineAsset = GraphicsSettings.currentRenderPipeline; + if (currentPipelineAsset == null) + return RenderPipeline.BiRP; + +#if AST_URP_AVAILABLE + if (currentPipelineAsset is UniversalRenderPipelineAsset) + return RenderPipeline.URP; +#endif + +#if AST_HDRP_AVAILABLE + if (currentPipelineAsset is HDRenderPipelineAsset) + return RenderPipeline.HDRP; +#endif + + return RenderPipeline.Unknown; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs.meta new file mode 100644 index 0000000..4148f8a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e42bdf53cd8b51429b10a6742ec5272 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles.meta new file mode 100644 index 0000000..50a0018 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 70d30555bce30014a9143c3d003105bf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss new file mode 100644 index 0000000..c363a3d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss @@ -0,0 +1,210 @@ +/* Asset Preview Description */ + +.asset-preview-description { + flex-direction: column; + flex-shrink: 0; + + margin: 10px 5px 2px 5px; + padding: 2px 4px; +} + +.asset-preview-description-simple-container { + flex-direction: column; + flex-wrap: wrap; +} + +.asset-preview-description-simple-label { + white-space: normal; +} + +.asset-preview-description-hyperlink-button { + margin: 0; + padding: 0; + + align-self: flex-start; + cursor: link; +} + +.asset-preview-description-show-button { + margin-top: 12px; +} + +.asset-preview-description-hide-button { + margin-top: 12px; +} + +.asset-preview-description-full-container { + margin-top: 12px; +} + +.asset-preview-description-full-label { + white-space: normal; +} + +/* Asset Preview Settings */ + +.preview-settings { + flex-direction: column; + flex-shrink: 0; + + margin: 0px 5px 2px 5px; + padding: 2px 4px; +} + +.preview-settings-selection-row { + flex-direction: row; + flex-grow: 1; + + margin-top: 10px; + padding: 0 3px 0 2px; +} + +.preview-settings-selection-label-help-row { + flex-direction: row; + flex-shrink: 1; + flex-grow: 0; + + align-self: center; + align-items: center; + justify-content: flex-start; + + width: 120px; +} + +.preview-settings-selection-label-help-row > Label { + -unity-font-style: bold; +} + +.preview-settings-selection-label-help-row > Image { + height: 16px; + width: 16px; +} + +.preview-settings-selection-dropdown { + flex-grow: 1; + flex-shrink: 1; + + align-self: stretch; + + margin-right: 0; + margin-left: 3px; + padding: 1px 4px; +} + +/* Preview Paths */ + +.preview-paths { + flex-direction: column; + flex-grow: 1; + flex-shrink: 0; + + margin-bottom: 10px; + padding: 0; +} + +.preview-paths-box { + flex-grow: 1; + flex-direction: column; +} + +.preview-paths-scroll-view { + flex-grow: 1; + height: 100px; + margin-left: 3px; +} + +.preview-paths-scroll-view > .unity-scroll-view__content-viewport +{ + margin-left: 1px; +} + +.preview-paths-scroll-view > * > .unity-scroll-view__content-container +{ + padding: 0 0 0 0; +} + +.preview-paths-scroll-view > * > .unity-scroll-view__vertical-scroller +{ + margin: -1px 0; +} + +.preview-paths-scroll-view-bottom-row { + flex-direction: row-reverse; + margin: -1px 0 0 3px; +} + +.preview-paths-add-button { + margin: 3px 0 0 0; + align-self: center; +} + +.preview-paths-path-row { + flex-direction: row; + flex-grow: 1; + + margin-top: 2px; + padding: 0 5px 0 2px; +} + +.preview-paths-path-row-input-field { + flex-grow: 1; + flex-shrink: 1; + + padding-left: 5px; + + white-space: normal; + -unity-text-align: middle-left; +} + +.preview-paths-path-row-remove-button { + width: 20px; + height: 20px; + margin-left: 2px; + margin-right: 1px; + padding: 1px 0 0 0; +} + +/* Generate Button */ + +.preview-generate-button { + align-self: stretch; + + height: 25px; + margin-left: 2px; +} + +/* Asset Preview List Element */ + +.preview-list { + margin-top: 10px; + flex-grow: 1; +} + +.preview-list-image { + width: 140px; + height: 160px; + margin: 5px; + padding: 5px; + justify-content: space-between; +} + +.preview-list-image:hover{ + background-color: #444444; +} + +.preview-list-image > Image { + flex-shrink: 0; + max-width: 100%; + max-height: 100%; + -unity-background-scale-mode: scale-to-fit; + align-self: center; +} + +.preview-list-image > Label { + align-self: center; + overflow: hidden; + text-overflow: ellipsis; + margin-top: 2px; + padding: 0; + -unity-text-align: middle-center; +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss.meta new file mode 100644 index 0000000..4b97f84 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 095b74bd60b187c418dcc4cd47aa696d +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss new file mode 100644 index 0000000..6dff0dd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss @@ -0,0 +1,67 @@ +.primary-colors { + /* Light - lighter */ + background-color: rgb(220, 220, 220); + /* Light - middle */ + background-color: rgb(200, 200, 200); + /* Light - darker */ + background-color: rgb(180, 180, 180); + + /* Dark - lighter */ + background-color: rgb(78, 78, 78); + /* Dark - middle */ + background-color: rgb(68, 68, 68); + /* Dark - darker */ + background-color: rgb(58, 58, 58); + + /* Border color - light */ + border-color: rgb(200, 200, 200); + /* Border color - dark */ + border-color: rgb(33, 33, 33); +} + +/* Asset Preview Description */ + +.asset-preview-description-hyperlink-button { + color: rgb(68, 113, 229); + border-width: 0; + background-color: rgba(0, 0, 0, 0); +} + +.asset-preview-description-hyperlink-button:hover { + color: rgb(68, 133, 229); +} + +.asset-preview-description-hyperlink-button:active { + color: rgb(68, 93, 229); +} + +/* Asset Preview Settings */ + +.preview-settings-selection-label-help-row > Image { + --unity-image: resource("d__Help@2x"); +} + +.preview-settings-selection-dropdown { + color: rgb(238, 238, 238); + background-color: rgb(88, 88, 88); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(36, 36, 36); +} + +/* Preview Paths */ + +.preview-paths-scroll-view { + border-width: 1px; + border-color: rgb(33, 33, 33); + background-color: rgb(58, 58, 58); +} + +.preview-paths-scroll-view > * > .unity-scroll-view__vertical-scroller { + border-right-width: 0; +} + +.preview-paths-path-row-input-field:hover { + background-color: rgb(78, 78, 78); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss.meta new file mode 100644 index 0000000..cbc87a7 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c04ee69303d45644bb3971a4e8ce952 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss new file mode 100644 index 0000000..b98e1cd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss @@ -0,0 +1,67 @@ +.primary-colors { + /* Light - lighter */ + background-color: rgb(220, 220, 220); + /* Light - middle */ + background-color: rgb(200, 200, 200); + /* Light - darker */ + background-color: rgb(180, 180, 180); + + /* Dark - lighter */ + background-color: rgb(50, 50, 50); + /* Dark - middle */ + background-color: rgb(28, 28, 28); + /* Dark - darker */ + background-color: rgb(0, 0, 0); + + /* Border color - light */ + border-color: rgb(200, 200, 200); + /* Border color - dark */ + border-color: rgb(33, 33, 33); +} + +/* Asset Preview Description */ + +.asset-preview-description-hyperlink-button { + color: rgb(68, 113, 229); + border-width: 0; + background-color: rgba(0, 0, 0, 0); +} + +.asset-preview-description-hyperlink-button:hover { + color: rgb(68, 133, 229); +} + +.asset-preview-description-hyperlink-button:active { + color: rgb(68, 93, 229); +} + +/* Asset Preview Settings */ + +.preview-settings-selection-label-help-row > Image { + --unity-image: resource("_Help@2x"); +} + +.preview-settings-selection-dropdown { + color: rgb(9, 9, 9); + background-color: rgb(228, 228, 228); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(178, 178, 178); +} + +/* Preview Paths */ + +.preview-paths-scroll-view { + border-width: 1px; + border-color: rgb(33, 33, 33); + background-color: rgb(180, 180, 180); +} + +.preview-paths-scroll-view > * > .unity-scroll-view__vertical-scroller { + border-right-width: 0; +} + +.preview-paths-path-row-input-field:hover { + background-color: rgb(200, 200, 200); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss.meta new file mode 100644 index 0000000..cbafb32 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 38ae9e6ef965cae43902ba22967938ee +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef b/Installer/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef new file mode 100644 index 0000000..b0ba405 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef @@ -0,0 +1,36 @@ +{ + "name": "asset-store-tools-editor", + "rootNamespace": "", + "references": [ + "Unity.RenderPipelines.Universal.Runtime", + "Unity.RenderPipelines.Core.Runtime", + "Unity.RenderPipelines.HighDefinition.Runtime" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.render-pipelines.universal", + "expression": "1.0.0", + "define": "AST_URP_AVAILABLE" + }, + { + "name": "com.unity.render-pipelines.high-definition", + "expression": "1.0.0", + "define": "AST_HDRP_AVAILABLE" + }, + { + "name": "com.unity.render-pipelines.high-definition", + "expression": "12.0.0", + "define": "AST_HDRP_AVAILABLE_V12" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef.meta new file mode 100644 index 0000000..2f67bb9 --- /dev/null +++ b/Installer/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/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader.meta new file mode 100644 index 0000000..b4b8661 --- /dev/null +++ b/Installer/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/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons.meta new file mode 100644 index 0000000..7026063 --- /dev/null +++ b/Installer/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/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png new file mode 100644 index 0000000..f54f1a2 Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png.meta new file mode 100644 index 0000000..6a7e9d8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png.meta @@ -0,0 +1,123 @@ +fileFormatVersion: 2 +guid: 92f8a779a7c786a4f87ed8e1b36a66b3 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + 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 + 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: 1 + 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 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + 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: 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: Android + 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 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png new file mode 100644 index 0000000..d6684e5 Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png.meta new file mode 100644 index 0000000..0ff13ea --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png.meta @@ -0,0 +1,123 @@ +fileFormatVersion: 2 +guid: 7c0661b9a6385a3488c075711f368cf4 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + 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 + 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: 1 + 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 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + 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: 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: Android + 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 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png new file mode 100644 index 0000000..245875b Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png.meta new file mode 100644 index 0000000..26ccaa5 --- /dev/null +++ b/Installer/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/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.png new file mode 100644 index 0000000..70f4703 Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.png.meta new file mode 100644 index 0000000..a0f1369 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.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/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png new file mode 100644 index 0000000..621e906 Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png.meta new file mode 100644 index 0000000..2f19af2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png.meta @@ -0,0 +1,135 @@ +fileFormatVersion: 2 +guid: 8e0749dce5b14cc46b73b0303375c162 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + 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 + 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: 1 + 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 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + cookieLightType: 1 + 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: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts.meta new file mode 100644 index 0000000..63c6efc --- /dev/null +++ b/Installer/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/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data.meta new file mode 100644 index 0000000..1382bf8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 930cfc857321b1048bf9607d4c8f89d3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions.meta new file mode 100644 index 0000000..73728d0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 771776e4d51c47945b3449d4de948c00 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs new file mode 100644 index 0000000..84033b5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs @@ -0,0 +1,34 @@ +using System; +using UnityEngine; +using PackageModel = AssetStoreTools.Api.Models.Package; + +namespace AssetStoreTools.Uploader.Data +{ + internal interface IPackage + { + string PackageId { get; } + string VersionId { get; } + string Name { get; } + string Status { get; } + string Category { get; } + bool IsCompleteProject { get; } + string RootGuid { get; } + string RootPath { get; } + string ProjectPath { get; } + string Modified { get; } + string Size { get; } + bool IsDraft { get; } + Texture2D Icon { get; } + + event Action OnUpdate; + event Action OnIconUpdate; + + string FormattedSize(); + string FormattedModified(); + + void UpdateData(PackageModel source); + void UpdateIcon(Texture2D texture); + + PackageModel ToModel(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs.meta new file mode 100644 index 0000000..f0a0de3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b92f2ed98d0b31a479aa2bfd95528fbd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs new file mode 100644 index 0000000..d82520f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Uploader.Data +{ + internal interface IPackageContent + { + event Action OnActiveWorkflowChanged; + + IWorkflow GetActiveWorkflow(); + List GetAvailableWorkflows(); + void SetActiveWorkflow(IWorkflow workflow); + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs.meta new file mode 100644 index 0000000..3d538b8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 45ce41158c3174149b7056a30ac901db +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs new file mode 100644 index 0000000..c35936b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Uploader.Data +{ + internal interface IPackageGroup + { + string Name { get; } + List Packages { get; } + + event Action> OnPackagesSorted; + event Action> OnPackagesFiltered; + + void Sort(PackageSorting sortingType); + void Filter(string filter); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs.meta new file mode 100644 index 0000000..f247540 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f683845071b8891498156d95a1a5c2dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs new file mode 100644 index 0000000..c6e9b81 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs @@ -0,0 +1,36 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Exporter; +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Data +{ + internal interface IWorkflow + { + string Name { get; } + string DisplayName { get; } + string PackageName { get; } + string PackageExtension { get; } + bool IsPathSet { get; } + + event Action OnChanged; + event Action OnUploadStateChanged; + + bool GenerateHighQualityPreviews { get; set; } + ValidationSettings LastValidationSettings { get; } + ValidationResult LastValidationResult { get; } + + IEnumerable GetAllPaths(); + ValidationResult Validate(); + Task ExportPackage(string outputPath); + Task ValidatePackageUploadedVersions(); + + Task UploadPackage(string exportedPackagePath); + void AbortUpload(); + void ResetUploadStatus(); + Task RefreshPackage(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs.meta new file mode 100644 index 0000000..6efa281 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a2f796eadafa774bae89cf3939611dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs new file mode 100644 index 0000000..6987b91 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs @@ -0,0 +1,18 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Uploader.Services.Analytics.Data; +using System; +using System.Threading.Tasks; +using UnityEngine.Analytics; + +namespace AssetStoreTools.Uploader.Data +{ + internal interface IWorkflowServices + { + Task GetPackageUploadedVersions(IPackage package, int timeoutMs); + Task UploadPackage(IPackageUploader uploader, IProgress progress); + void StopUploading(IPackageUploader uploader); + AnalyticsResult SendAnalytic(IAssetStoreAnalytic data); + Task UpdatePackageData(IPackage package); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs.meta new file mode 100644 index 0000000..70b77be --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ae017363fa41ff4d9926dc4a5852246 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs new file mode 100644 index 0000000..cd3462a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs @@ -0,0 +1,253 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Exporter; +using AssetStoreTools.Previews; +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Generators; +using AssetStoreTools.Uploader.Services.Analytics.Data; +using AssetStoreTools.Utility; +using AssetStoreTools.Validator; +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UnityEngine; + +namespace AssetStoreTools.Uploader.Data +{ + internal abstract class WorkflowBase : IWorkflow + { + protected IPackage Package; + + public abstract string Name { get; } + public abstract string DisplayName { get; } + public string PackageName => Package.Name; + public abstract string PackageExtension { get; } + public abstract bool IsPathSet { get; } + + protected string LocalPackageGuid; + protected string LocalPackagePath; + protected string LocalProjectPath; + + public bool GenerateHighQualityPreviews { get; set; } + public ValidationSettings LastValidationSettings { get; private set; } + public ValidationResult LastValidationResult { get; private set; } + + private IWorkflowServices _services; + private IPackageUploader _activeUploader; + + public abstract event Action OnChanged; + public event Action OnUploadStateChanged; + + public WorkflowBase(IPackage package, IWorkflowServices services) + { + Package = package; + _services = services; + } + + public abstract IEnumerable GetAllPaths(); + + public abstract IValidator CreateValidator(); + + public ValidationResult Validate() + { + var validator = CreateValidator(); + var result = CreateValidator().Validate(); + + LastValidationSettings = validator.Settings; + LastValidationResult = result; + + return result; + } + + protected IPreviewGenerator CreatePreviewGenerator(List inputPaths) + { + PreviewGenerationSettings settings; + IPreviewGenerator generator; + + // Filter out ProjectSettings + inputPaths = inputPaths.Where(x => x == "Assets" || x.StartsWith("Assets/") || x.StartsWith("Packages/")).ToList(); + + if (!GenerateHighQualityPreviews) + { + settings = new NativePreviewGenerationSettings() + { + InputPaths = inputPaths.ToArray(), + OverwriteExisting = false, + OutputPath = Constants.Previews.Native.DefaultOutputPath, + Format = Constants.Previews.Native.DefaultFormat, + PreviewFileNamingFormat = Constants.Previews.DefaultFileNameFormat, + WaitForPreviews = Constants.Previews.Native.DefaultWaitForPreviews, + ChunkedPreviewLoading = Constants.Previews.Native.DefaultChunkedPreviewLoading, + ChunkSize = Constants.Previews.Native.DefaultChunkSize + }; + + generator = new NativePreviewGenerator((NativePreviewGenerationSettings)settings); + } + else + { + settings = new CustomPreviewGenerationSettings() + { + InputPaths = inputPaths.ToArray(), + OverwriteExisting = false, + Width = Constants.Previews.Custom.DefaultWidth, + Height = Constants.Previews.Custom.DefaultHeight, + Depth = Constants.Previews.Custom.DefaultDepth, + NativeWidth = Constants.Previews.Custom.DefaultNativeWidth, + NativeHeight = Constants.Previews.Custom.DefaultNativeHeight, + OutputPath = Constants.Previews.Custom.DefaultOutputPath, + Format = Constants.Previews.Custom.DefaultFormat, + PreviewFileNamingFormat = Constants.Previews.DefaultFileNameFormat, + AudioSampleColor = Constants.Previews.Custom.DefaultAudioSampleColor, + AudioBackgroundColor = Constants.Previews.Custom.DefaultAudioBackgroundColor, + }; + + generator = new CustomPreviewGenerator((CustomPreviewGenerationSettings)settings); + } + + return generator; + } + + public abstract IPackageExporter CreateExporter(string outputPath); + + public virtual async Task ExportPackage(string outputPath) + { + var exporter = CreateExporter(outputPath); + var result = await exporter.Export(); + return result; + } + + public async Task ValidatePackageUploadedVersions() + { + var unityVersionSupported = string.Compare(Application.unityVersion, Constants.Uploader.MinRequiredUnitySupportVersion, StringComparison.Ordinal) >= 0; + if (unityVersionSupported) + return true; + + var response = await _services.GetPackageUploadedVersions(Package, 5000); + if (response.Cancelled || response.Success == false) + return true; + + return response.UnityVersions.Any(x => string.Compare(x, Constants.Uploader.MinRequiredUnitySupportVersion, StringComparison.Ordinal) >= 0); + } + + private bool ValidatePackageBeforeUpload(string packagePath, out string error) + { + error = string.Empty; + + if (!File.Exists(packagePath)) + { + error = $"File '{packagePath}' was not found."; + return false; + } + + if (!ValidatePackageSize(packagePath, out error)) + { + return false; + } + + return true; + } + + private bool ValidatePackageSize(string packagePath, out string error) + { + error = string.Empty; + + long packageSize = new FileInfo(packagePath).Length; + long packageSizeLimit = Constants.Uploader.MaxPackageSizeBytes; + + if (packageSize <= packageSizeLimit) + return true; + + float packageSizeInGB = packageSize / (float)1073741824; // (1024 * 1024 * 1024) + float maxPackageSizeInGB = packageSizeLimit / (float)1073741824; + error = $"The size of your package ({packageSizeInGB:0.0} GB) exceeds the maximum allowed package size of {maxPackageSizeInGB:0} GB. " + + $"Please reduce the size of your package."; + + return false; + } + + public async Task UploadPackage(string packagePath) + { + if (!ValidatePackageBeforeUpload(packagePath, out var error)) + { + return new PackageUploadResponse() { Success = false, Status = UploadStatus.Fail, Exception = new Exception(error) }; + } + + _activeUploader = CreatePackageUploader(packagePath); + var progress = new Progress(); + + var time = System.Diagnostics.Stopwatch.StartNew(); + + progress.ProgressChanged += ReportUploadProgress; + var response = await _services.UploadPackage(_activeUploader, progress); + progress.ProgressChanged -= ReportUploadProgress; + + // Send analytics + time.Stop(); + if (!response.Cancelled) + SendAnalytics(packagePath, response.Status, time.Elapsed.TotalSeconds); + + OnUploadStateChanged?.Invoke(response.Status, null); + _activeUploader = null; + return response; + } + + protected abstract IPackageUploader CreatePackageUploader(string exportedPackagePath); + + private void ReportUploadProgress(object _, float value) + { + OnUploadStateChanged?.Invoke(null, value); + } + + private void SendAnalytics(string packagePath, UploadStatus uploadStatus, double timeTakenSeconds) + { + try + { + var analytic = new PackageUploadAnalytic( + packageId: Package.PackageId, + category: Package.Category, + usedValidator: LastValidationResult != null, + validationSettings: LastValidationSettings, + validationResult: LastValidationResult, + uploadFinishedReason: uploadStatus, + timeTaken: timeTakenSeconds, + packageSize: new FileInfo(packagePath).Length, + workflow: Name + ); + + var result = _services.SendAnalytic(analytic); + } + catch (Exception e) { ASDebug.LogError($"Could not send analytics: {e}"); } + } + + public void AbortUpload() + { + if (_activeUploader != null) + _services.StopUploading(_activeUploader); + + _activeUploader = null; + } + + public void ResetUploadStatus() + { + OnUploadStateChanged?.Invoke(UploadStatus.Default, 0f); + } + + public async Task RefreshPackage() + { + var response = await _services.UpdatePackageData(Package); + if (!response.Success) + return; + + Package.UpdateData(response.Package); + } + + public abstract bool IsPathValid(string path, out string reason); + + protected abstract void Serialize(); + + protected abstract void Deserialize(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs.meta new file mode 100644 index 0000000..65c40e6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d0e87ee17aa944c42b1c335abe19daaf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs new file mode 100644 index 0000000..967bd9b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs @@ -0,0 +1,329 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Exporter; +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Utility; +using AssetStoreTools.Validator; +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using PackageInfo = UnityEditor.PackageManager.PackageInfo; + +namespace AssetStoreTools.Uploader.Data +{ + internal class AssetsWorkflow : WorkflowBase + { + public override string Name => "AssetsWorkflow"; + public override string DisplayName => "From Assets Folder"; + public override string PackageExtension => ".unitypackage"; + public override bool IsPathSet => !string.IsNullOrEmpty(_mainExportPath); + public bool IsCompleteProject => Package.IsCompleteProject; + + private AssetsWorkflowState _stateData; + + private string _mainExportPath; + private bool _includeDependencies; + private List _dependencies; + private List _specialFolders; + + public override event Action OnChanged; + + // Special folders that would not work if not placed directly in the 'Assets' folder + private readonly string[] _extraAssetFolderNames = + { + "Editor Default Resources", "Gizmos", "Plugins", + "StreamingAssets", "Standard Assets", "WebGLTemplates", + "ExternalDependencyManager", "XR" + }; + + public AssetsWorkflow(IPackage package, AssetsWorkflowState stateData, IWorkflowServices services) + : base(package, services) + { + _stateData = stateData; + Deserialize(); + } + + public string GetMainExportPath() + { + return _mainExportPath; + } + + public void SetMainExportPath(string path, bool serialize) + { + _mainExportPath = path; + SetMetadata(); + if (serialize) + Serialize(); + } + + private void SetMetadata() + { + LocalPackageGuid = AssetDatabase.AssetPathToGUID(_mainExportPath); + LocalPackagePath = _mainExportPath; + LocalProjectPath = _mainExportPath; + } + + public bool GetIncludeDependencies() + { + return _includeDependencies; + } + + public void SetIncludeDependencies(bool value, bool serialize) + { + _includeDependencies = value; + // Note: make sure that exporting does not fail when + // a serialized dependency that has been removed from a project is sent to exporter + if (serialize) + Serialize(); + } + + public List GetDependencies() + { + return _dependencies; + } + + public void SetDependencies(IEnumerable dependencies, bool serialize) + { + _dependencies.Clear(); + foreach (var dependency in dependencies) + { + if (!PackageUtility.GetPackageByPackageName(dependency, out var package)) + continue; + _dependencies.Add(package); + } + + if (serialize) + Serialize(); + } + + public List GetSpecialFolders() + { + return _specialFolders; + } + + public void SetSpecialFolders(IEnumerable specialFolders, bool serialize) + { + _specialFolders.Clear(); + foreach (var folder in specialFolders) + { + _specialFolders.Add(folder); + } + + if (serialize) + Serialize(); + } + + public override bool IsPathValid(string path, out string error) + { + error = string.Empty; + + var pathIsFolder = Directory.Exists(path); + if (!pathIsFolder) + { + error = "Path must point to a valid folder"; + return false; + } + + var pathWithinAssetsFolder = path.StartsWith("Assets/") && path != "Assets/"; + if (pathWithinAssetsFolder) + return true; + + var pathIsAssetsFolder = path == "Assets" || path == "Assets/"; + if (pathIsAssetsFolder) + { + var assetsFolderSelectionAllowed = Package.IsCompleteProject; + if (assetsFolderSelectionAllowed) + return true; + + error = "'Assets' folder is only available for packages tagged as a 'Complete Project'."; + return false; + } + + error = "Selected folder path must be within the project's Assets."; + return false; + } + + public List GetAvailableDependencies() + { + var registryPackages = PackageUtility.GetAllRegistryPackages(); + return registryPackages.Select(x => x.name).ToList(); + } + + public List GetAvailableSpecialFolders() + { + var specialFolders = new List(); + + foreach (var extraAssetFolderName in _extraAssetFolderNames) + { + var fullExtraPath = "Assets/" + extraAssetFolderName; + + if (!Directory.Exists(fullExtraPath)) + continue; + + if (_mainExportPath.ToLower().StartsWith(fullExtraPath.ToLower())) + continue; + + // Don't include nested paths + if (!fullExtraPath.ToLower().StartsWith(_mainExportPath.ToLower())) + specialFolders.Add(fullExtraPath); + } + + return specialFolders; + } + + public override IEnumerable GetAllPaths() + { + var paths = new List() + { + _mainExportPath + }; + paths.AddRange(GetSpecialFolders()); + + return paths; + } + + public override IValidator CreateValidator() + { + var validationPaths = GetAllPaths(); + + var validationSettings = new CurrentProjectValidationSettings() + { + Category = Package.Category, + ValidationPaths = validationPaths.ToList(), + ValidationType = ValidationType.UnityPackage + }; + + var validator = new CurrentProjectValidator(validationSettings); + return validator; + } + + public override IPackageExporter CreateExporter(string outputPath) + { + var exportPaths = GetAllPaths().ToList(); + + if (IsCompleteProject && !exportPaths.Contains("ProjectSettings")) + { + exportPaths.Add("ProjectSettings"); + } + + var dependenciesToInclude = new List(); + if (_includeDependencies) + { + dependenciesToInclude.AddRange(_dependencies.Select(x => x.name)); + } + + if (ASToolsPreferences.Instance.UseLegacyExporting) + { + var exportSettings = new LegacyExporterSettings() + { + ExportPaths = exportPaths.ToArray(), + OutputFilename = outputPath, + IncludeDependencies = _includeDependencies, + }; + + return new LegacyPackageExporter(exportSettings); + } + else + { + var exportSettings = new DefaultExporterSettings() + { + ExportPaths = exportPaths.ToArray(), + OutputFilename = outputPath, + Dependencies = dependenciesToInclude.ToArray(), + PreviewGenerator = CreatePreviewGenerator(exportPaths), + }; + + return new DefaultPackageExporter(exportSettings); + } + } + + protected override IPackageUploader CreatePackageUploader(string exportedPackagePath) + { + var uploaderSettings = new UnityPackageUploadSettings() + { + UnityPackagePath = exportedPackagePath, + VersionId = Package.VersionId, + RootGuid = LocalPackageGuid, + RootPath = LocalPackagePath, + ProjectPath = LocalProjectPath + }; + + var uploader = new UnityPackageUploader(uploaderSettings); + return uploader; + } + + protected override void Serialize() + { + _stateData.SetMainPath(_mainExportPath); + _stateData.SetIncludeDependencies(_includeDependencies); + _stateData.SetDependencies(_dependencies.Select(x => x.name)); + _stateData.SetSpecialFolders(_specialFolders); + OnChanged?.Invoke(); + } + + protected override void Deserialize() + { + _mainExportPath = _stateData.GetMainPath(); + + _specialFolders = new List(); + foreach (var path in _stateData.GetSpecialFolders()) + { + _specialFolders.Add(path); + } + + _includeDependencies = _stateData.GetIncludeDependencies(); + + _dependencies = new List(); + foreach (var dependency in _stateData.GetDependencies()) + { + if (!PackageUtility.GetPackageByPackageName(dependency, out var package)) + continue; + + _dependencies.Add(package); + } + + DeserializeFromUploadedData(); + } + + private void DeserializeFromUploadedData() + { + DeserializeFromUploadedDataByGuid(); + DeserializeFromUploadedDataByPath(); + } + + private void DeserializeFromUploadedDataByGuid() + { + if (!string.IsNullOrEmpty(_mainExportPath)) + return; + + var lastUploadedGuid = Package.RootGuid; + if (string.IsNullOrEmpty(lastUploadedGuid)) + return; + + var potentialPackagePath = AssetDatabase.GUIDToAssetPath(lastUploadedGuid); + DeserializeFromUploadedDataByPath(potentialPackagePath); + } + + private void DeserializeFromUploadedDataByPath() + { + if (!string.IsNullOrEmpty(_mainExportPath)) + return; + + var lastUploadedPath = Package.ProjectPath; + if (string.IsNullOrEmpty(lastUploadedPath)) + return; + + DeserializeFromUploadedDataByPath(lastUploadedPath); + } + + private void DeserializeFromUploadedDataByPath(string path) + { + if (string.IsNullOrEmpty(path) || !IsPathValid(path, out var _)) + return; + + _mainExportPath = path; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs.meta new file mode 100644 index 0000000..331e6ab --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4657d35aaf9d70948a0840dc615f64ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs new file mode 100644 index 0000000..3759313 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs @@ -0,0 +1,251 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Exporter; +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Utility; +using AssetStoreTools.Validator; +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEditor.PackageManager; +using PackageInfo = UnityEditor.PackageManager.PackageInfo; +using PackageManager = UnityEditor.PackageManager; + +namespace AssetStoreTools.Uploader.Data +{ + internal class HybridPackageWorkflow : WorkflowBase + { + public override string Name => "HybridPackageWorkflow"; + public override string DisplayName => "Local UPM Package"; + public override string PackageExtension => ".unitypackage"; + public override bool IsPathSet => _packageInfo != null; + + private HybridPackageWorkflowState _stateData; + + private PackageInfo _packageInfo; + private List _dependencies; + + public override event Action OnChanged; + + public HybridPackageWorkflow(IPackage package, HybridPackageWorkflowState stateData, IWorkflowServices services) + : base(package, services) + { + _stateData = stateData; + Deserialize(); + } + + public PackageInfo GetPackage() + { + return _packageInfo; + } + + public void SetPackage(PackageInfo packageInfo, bool serialize) + { + if (packageInfo == null) + throw new ArgumentException("Package is null"); + + _packageInfo = packageInfo; + SetMetadata(); + if (serialize) + Serialize(); + } + + public void SetPackage(string packageManifestPath, bool serialize) + { + if (!PackageUtility.GetPackageByManifestPath(packageManifestPath, out var package)) + throw new ArgumentException("Path does not correspond to a valid package"); + + SetPackage(package, serialize); + } + + private void SetMetadata() + { + LocalPackageGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(_packageInfo.GetManifestAsset())); + LocalPackagePath = _packageInfo.assetPath; + LocalProjectPath = _packageInfo.name; + } + + public List GetDependencies() + { + return _dependencies; + } + + public void SetDependencies(IEnumerable dependencies, bool serialize) + { + _dependencies.Clear(); + foreach (var dependency in dependencies) + { + if (!PackageUtility.GetPackageByPackageName(dependency, out var package)) + continue; + _dependencies.Add(package); + } + + if (serialize) + Serialize(); + } + + public List GetAvailableDependencies() + { + var availableDependencies = new List(); + if (_packageInfo == null) + return availableDependencies; + + var packageDependencies = _packageInfo.dependencies.Select(x => x.name); + foreach (var dependency in packageDependencies) + { + if (!PackageUtility.GetPackageByPackageName(dependency, out var package)) + continue; + + if (package.source != PackageManager.PackageSource.Local + && package.source != PackageManager.PackageSource.Embedded) + continue; + + availableDependencies.Add(package); + } + + return availableDependencies; + } + + public override IEnumerable GetAllPaths() + { + var paths = new List(); + + if (_packageInfo == null) + return paths; + + paths.Add(_packageInfo.assetPath); + paths.AddRange(_dependencies.Select(x => x.assetPath)); + + return paths; + } + + public override bool IsPathValid(string path, out string reason) + { + reason = string.Empty; + + if (!PackageUtility.GetPackageByManifestPath(path, out var package)) + { + reason = "Selected path must point to a package manifest for a package that is imported into the current project"; + return false; + } + + var packageSourceValid = package.source == PackageSource.Embedded || package.source == PackageSource.Local; + if (!packageSourceValid) + { + reason = "Selected package must be a local or an embedded package"; + return false; + } + + return true; + } + + public override IValidator CreateValidator() + { + var validationPaths = GetAllPaths(); + + var validationSettings = new CurrentProjectValidationSettings() + { + Category = Package?.Category, + ValidationPaths = validationPaths.ToList(), + ValidationType = ValidationType.UnityPackage + }; + + var validator = new CurrentProjectValidator(validationSettings); + return validator; + } + + public override IPackageExporter CreateExporter(string outputPath) + { + var exportPaths = GetAllPaths(); + + var exportSettings = new DefaultExporterSettings() + { + ExportPaths = exportPaths.ToArray(), + OutputFilename = outputPath, + PreviewGenerator = CreatePreviewGenerator(exportPaths.ToList()) + }; + + return new DefaultPackageExporter(exportSettings); + } + + protected override IPackageUploader CreatePackageUploader(string exportedPackagePath) + { + var uploaderSettings = new UnityPackageUploadSettings() + { + UnityPackagePath = exportedPackagePath, + VersionId = Package.VersionId, + RootGuid = LocalPackageGuid, + RootPath = LocalPackagePath, + ProjectPath = LocalProjectPath + }; + + var uploader = new UnityPackageUploader(uploaderSettings); + return uploader; + } + + protected override void Serialize() + { + if (_packageInfo == null) + return; + + _stateData.SetPackageName(_packageInfo.name); + _stateData.SetPackageDependencies(_dependencies.Select(x => x.name).OrderBy(x => x)); + OnChanged?.Invoke(); + } + + protected override void Deserialize() + { + var packageName = _stateData.GetPackageName(); + if (PackageUtility.GetPackageByPackageName(packageName, out var package)) + _packageInfo = package; + + _dependencies = new List(); + var dependencies = _stateData.GetPackageDependencies(); + foreach (var dependency in dependencies) + { + if (PackageUtility.GetPackageByPackageName(dependency, out var packageDependency)) + _dependencies.Add(packageDependency); + } + + DeserializeFromUploadedData(); + } + + private void DeserializeFromUploadedData() + { + DeserializeFromUploadedDataByPackageName(); + DeserializeFromUploadedDataByPackageGuid(); + } + + private void DeserializeFromUploadedDataByPackageName() + { + if (_packageInfo != null) + return; + + var lastUploadedPackageName = Package.ProjectPath; + if (!PackageUtility.GetPackageByPackageName(lastUploadedPackageName, out var package)) + return; + + _packageInfo = package; + } + + private void DeserializeFromUploadedDataByPackageGuid() + { + if (_packageInfo != null) + return; + + var lastUploadedGuid = Package.RootGuid; + if (string.IsNullOrEmpty(lastUploadedGuid)) + return; + + var potentialPackageManifestPath = AssetDatabase.GUIDToAssetPath(lastUploadedGuid); + if (string.IsNullOrEmpty(potentialPackageManifestPath) || !IsPathValid(potentialPackageManifestPath, out var _)) + return; + + if (!PackageUtility.GetPackageByManifestPath(potentialPackageManifestPath, out var package)) + return; + + _packageInfo = package; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs.meta new file mode 100644 index 0000000..f815b9d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3061839aba3894246a20195639eeda1f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs new file mode 100644 index 0000000..1a984e5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs @@ -0,0 +1,91 @@ +using System; +using UnityEngine; +using PackageModel = AssetStoreTools.Api.Models.Package; + +namespace AssetStoreTools.Uploader.Data +{ + internal class Package : IPackage + { + private PackageModel _source; + + public string PackageId => _source.PackageId; + public string VersionId => _source.VersionId; + public string Name => _source.Name; + public string Status => _source.Status; + public string Category => _source.Category; + public bool IsCompleteProject => _source.IsCompleteProject; + public string RootGuid => _source.RootGuid; + public string RootPath => _source.RootPath; + public string ProjectPath => _source.ProjectPath; + public string Modified => _source.Modified; + public string Size => _source.Size; + public string IconUrl => _source.IconUrl; + public bool IsDraft => Status.Equals("draft", StringComparison.OrdinalIgnoreCase); + public Texture2D Icon { get; private set; } + + public event Action OnUpdate; + public event Action OnIconUpdate; + + public Package(PackageModel packageSource) + { + _source = packageSource; + } + + public void UpdateIcon(Texture2D texture) + { + if (texture == null) + return; + + Icon = texture; + OnIconUpdate?.Invoke(); + } + + public string FormattedSize() + { + var defaultSize = "0.00 MB"; + if (float.TryParse(Size, out var sizeBytes)) + return $"{sizeBytes / (1024f * 1024f):0.00} MB"; + + return defaultSize; + } + + public string FormattedModified() + { + var defaultDate = "Unknown"; + if (DateTime.TryParse(Modified, out var dt)) + return dt.Date.ToString("yyyy-MM-dd"); + + return defaultDate; + } + + public void UpdateData(PackageModel source) + { + if (source == null) + throw new ArgumentException("Provided package is null"); + + _source = source; + OnUpdate?.Invoke(); + } + + public PackageModel ToModel() + { + var model = new PackageModel() + { + PackageId = _source.PackageId, + VersionId = _source.VersionId, + Name = _source.Name, + Status = _source.Status, + Category = _source.Category, + IsCompleteProject = _source.IsCompleteProject, + RootGuid = _source.RootGuid, + RootPath = _source.RootPath, + ProjectPath = _source.ProjectPath, + Modified = _source.Modified, + Size = _source.Size, + IconUrl = _source.IconUrl + }; + + return model; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs.meta new file mode 100644 index 0000000..588a5fd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc2198164bbd6394b87c51a74fe2915e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs new file mode 100644 index 0000000..bac6c08 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs @@ -0,0 +1,68 @@ +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Uploader.Services; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AssetStoreTools.Uploader.Data +{ + internal class PackageContent : IPackageContent + { + private IWorkflow _activeWorkflow; + private List _workflows; + private WorkflowStateData _workflowStateData; + + private ICachingService _cachingService; + + public event Action OnActiveWorkflowChanged; + + public PackageContent(List workflows, WorkflowStateData workflowStateData, ICachingService cachingService) + { + _workflows = workflows; + _workflowStateData = workflowStateData; + _cachingService = cachingService; + + foreach (var workflow in _workflows) + { + workflow.OnChanged += Serialize; + } + + Deserialize(); + } + + public IWorkflow GetActiveWorkflow() + { + return _activeWorkflow; + } + + public void SetActiveWorkflow(IWorkflow workflow) + { + _activeWorkflow = workflow; + + OnActiveWorkflowChanged?.Invoke(_activeWorkflow); + + Serialize(); + } + + public List GetAvailableWorkflows() + { + return _workflows; + } + + private void Serialize() + { + _workflowStateData.SetActiveWorkflow(_activeWorkflow.Name); + _cachingService.CacheWorkflowStateData(_workflowStateData); + } + + private void Deserialize() + { + var serializedWorkflow = _workflowStateData.GetActiveWorkflow(); + var workflow = _workflows.FirstOrDefault(x => x.Name == serializedWorkflow); + if (workflow != null) + _activeWorkflow = workflow; + else + _activeWorkflow = _workflows[0]; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs.meta new file mode 100644 index 0000000..afce82d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f36086f9380a49949ab45463abc6fee8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs new file mode 100644 index 0000000..78e9aa1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AssetStoreTools.Uploader.Data +{ + internal class PackageGroup : IPackageGroup + { + private class FilteredPackage + { + public IPackage Package; + public bool IsInFilter; + } + + public string Name { get; private set; } + public List Packages { get; private set; } + + private List _filteredPackages; + + public event Action> OnPackagesSorted; + public event Action> OnPackagesFiltered; + + public PackageGroup(string name, List packages) + { + Name = name; + Packages = packages; + + _filteredPackages = new List(); + foreach (var package in Packages) + _filteredPackages.Add(new FilteredPackage() { Package = package, IsInFilter = true }); + } + + public void Sort(PackageSorting sortingType) + { + switch (sortingType) + { + case PackageSorting.Name: + _filteredPackages = _filteredPackages.OrderBy(x => x.Package.Name).ToList(); + break; + case PackageSorting.Date: + _filteredPackages = _filteredPackages.OrderByDescending(x => x.Package.Modified).ToList(); + break; + case PackageSorting.Category: + _filteredPackages = _filteredPackages.OrderBy(x => x.Package.Category).ThenBy(x => x.Package.Name).ToList(); + break; + default: + throw new NotImplementedException("Undefined sorting type"); + } + + OnPackagesSorted?.Invoke(_filteredPackages.Where(x => x.IsInFilter).Select(x => x.Package).ToList()); + } + + public void Filter(string filter) + { + foreach (var package in _filteredPackages) + { + bool inFilter = package.Package.Name.ToLower().Contains(filter.ToLower()); + package.IsInFilter = inFilter; + } + + OnPackagesFiltered?.Invoke(_filteredPackages.Where(x => x.IsInFilter).Select(x => x.Package).ToList()); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs.meta new file mode 100644 index 0000000..2806af6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c9cc17f6b95bb2c42913a1451b9af29e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs new file mode 100644 index 0000000..7ef00e4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs @@ -0,0 +1,9 @@ +namespace AssetStoreTools.Uploader.Data +{ + internal enum PackageSorting + { + Name, + Category, + Date + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs.meta new file mode 100644 index 0000000..a9261e6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b1d61d0de90e022469b5ed312d4b7beb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization.meta new file mode 100644 index 0000000..2ba3d4c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0b05e199f21f636439844a8cc7e2c225 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs new file mode 100644 index 0000000..4970e73 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs @@ -0,0 +1,59 @@ +using AssetStoreTools.Utility; +using Newtonsoft.Json; +using System.IO; +using UnityEditor; + +namespace AssetStoreTools.Uploader.Data.Serialization +{ + internal class AssetPath + { + [JsonProperty("path")] + private string _path = string.Empty; + [JsonProperty("guid")] + private string _guid = string.Empty; + + [JsonIgnore] + public string Path { get => _path; set { SetAssetPath(value); } } + [JsonIgnore] + public string Guid { get => _guid; set { _guid = value; } } + + public AssetPath() { } + + public AssetPath(string path) + { + SetAssetPath(path); + } + + private void SetAssetPath(string path) + { + _path = path.Replace("\\", "/"); + if (TryGetGuid(_path, out var guid)) + _guid = guid; + } + + private bool TryGetGuid(string path, out string guid) + { + guid = string.Empty; + + var relativePath = FileUtility.AbsolutePathToRelativePath(path, ASToolsPreferences.Instance.EnableSymlinkSupport); + + if (!relativePath.StartsWith("Assets/") && !relativePath.StartsWith("Packages/")) + return false; + + guid = AssetDatabase.AssetPathToGUID(relativePath); + return !string.IsNullOrEmpty(guid); + } + + public override string ToString() + { + var pathFromGuid = AssetDatabase.GUIDToAssetPath(_guid); + if (!string.IsNullOrEmpty(pathFromGuid) && (File.Exists(pathFromGuid) || Directory.Exists(pathFromGuid))) + return pathFromGuid; + + if (File.Exists(_path) || Directory.Exists(_path)) + return _path; + + return string.Empty; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs.meta new file mode 100644 index 0000000..d6ced93 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 920ff8e4ffe77ec44bede985593cc187 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs new file mode 100644 index 0000000..8d955c3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs @@ -0,0 +1,77 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace AssetStoreTools.Uploader.Data.Serialization +{ + internal class AssetsWorkflowState + { + [JsonProperty("main_path")] + private AssetPath _mainPath; + [JsonProperty("special_folders")] + private List _specialFolders; + [JsonProperty("include_dependencies")] + private bool _includeDependencies; + [JsonProperty("dependencies")] + private List _dependencies; + + public AssetsWorkflowState() + { + _mainPath = new AssetPath(); + _includeDependencies = false; + _dependencies = new List(); + _specialFolders = new List(); + } + + public string GetMainPath() + { + return _mainPath?.ToString(); + } + + public void SetMainPath(string path) + { + _mainPath = new AssetPath(path); + } + + public bool GetIncludeDependencies() + { + return _includeDependencies; + } + + public void SetIncludeDependencies(bool value) + { + _includeDependencies = value; + } + + public List GetDependencies() + { + return _dependencies; + } + + public void SetDependencies(IEnumerable dependencies) + { + _dependencies = new List(); + foreach (var dependency in dependencies) + _dependencies.Add(dependency); + } + + public List GetSpecialFolders() + { + var specialFolders = new List(); + foreach (var folder in _specialFolders) + { + var path = folder.ToString(); + if (!string.IsNullOrEmpty(path)) + specialFolders.Add(path); + } + + return specialFolders; + } + + public void SetSpecialFolders(List specialFolders) + { + _specialFolders = new List(); + foreach (var path in specialFolders) + _specialFolders.Add(new AssetPath(path)); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs.meta new file mode 100644 index 0000000..58c77a5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 505f0a5aa753b4445a467539e150190a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs new file mode 100644 index 0000000..58e428e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace AssetStoreTools.Uploader.Data.Serialization +{ + internal class HybridPackageWorkflowState + { + [JsonProperty("package_name")] + private string _packageName; + [JsonProperty("dependencies")] + private List _dependencies; + + public HybridPackageWorkflowState() + { + _packageName = string.Empty; + _dependencies = new List(); + } + + public string GetPackageName() + { + return _packageName; + } + + public void SetPackageName(string packageName) + { + _packageName = packageName; + } + + public List GetPackageDependencies() + { + return _dependencies; + } + + public void SetPackageDependencies(IEnumerable dependencies) + { + _dependencies.Clear(); + foreach (var dependency in dependencies) + _dependencies.Add(dependency); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs.meta new file mode 100644 index 0000000..6638cf8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2848375fcb0a4174495573190bfc3900 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs new file mode 100644 index 0000000..ca14b88 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace AssetStoreTools.Uploader.Data.Serialization +{ + internal class UnityPackageWorkflowState + { + [JsonProperty("package_path")] + private AssetPath _packagePath; + + public UnityPackageWorkflowState() + { + _packagePath = new AssetPath(); + } + + public string GetPackagePath() + { + return _packagePath?.ToString(); + } + + public void SetPackagePath(string path) + { + _packagePath = new AssetPath(path); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs.meta new file mode 100644 index 0000000..a9d8b2c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 101a66adc88639b43b07cc28214474cf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs new file mode 100644 index 0000000..25e72c6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs @@ -0,0 +1,68 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace AssetStoreTools.Uploader.Data.Serialization +{ + internal class WorkflowStateData + { + [JsonProperty("package_id")] + private string _packageId; + [JsonProperty("active_workflow")] + private string _activeWorkflow; + [JsonProperty("assets_workflow")] + private AssetsWorkflowState _assetsWorkflow; + [JsonProperty("unitypackage_workflow")] + private UnityPackageWorkflowState _unityPackageWorkflow; + [JsonProperty("hybrid_workflow")] + private HybridPackageWorkflowState _hybridPackageWorkflow; + + public WorkflowStateData() + { + _activeWorkflow = string.Empty; + + _assetsWorkflow = new AssetsWorkflowState(); + _unityPackageWorkflow = new UnityPackageWorkflowState(); + _hybridPackageWorkflow = new HybridPackageWorkflowState(); + } + + public WorkflowStateData(string packageId) : this() + { + SetPackageId(packageId); + } + + public string GetPackageId() + { + return _packageId; + } + + public void SetPackageId(string packageId) + { + _packageId = packageId; + } + + public string GetActiveWorkflow() + { + return _activeWorkflow; + } + + public void SetActiveWorkflow(string activeWorkflow) + { + _activeWorkflow = activeWorkflow; + } + + public AssetsWorkflowState GetAssetsWorkflowState() + { + return _assetsWorkflow; + } + + public UnityPackageWorkflowState GetUnityPackageWorkflowState() + { + return _unityPackageWorkflow; + } + + public HybridPackageWorkflowState GetHybridPackageWorkflowState() + { + return _hybridPackageWorkflow; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs.meta new file mode 100644 index 0000000..4a58328 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eecebbc83661a4f41a14e293c9fc3331 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs new file mode 100644 index 0000000..6668fd6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs @@ -0,0 +1,135 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Exporter; +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Validator; +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Data +{ + internal class UnityPackageWorkflow : WorkflowBase + { + public override string Name => "UnityPackageWorkflow"; + public override string DisplayName => "Pre-exported .unitypackage"; + public override string PackageExtension => ".unitypackage"; + public override bool IsPathSet => !string.IsNullOrEmpty(_packagePath); + + private UnityPackageWorkflowState _workflowState; + private string _packagePath; + + public override event Action OnChanged; + + public UnityPackageWorkflow(IPackage package, UnityPackageWorkflowState workflowState, IWorkflowServices services) + : base(package, services) + { + _workflowState = workflowState; + Deserialize(); + } + + public void SetPackagePath(string path, bool serialize) + { + _packagePath = path; + SetMetadata(); + if (serialize) + Serialize(); + } + + private void SetMetadata() + { + LocalPackageGuid = string.Empty; + LocalPackagePath = string.Empty; + LocalProjectPath = _packagePath; + } + + public string GetPackagePath() + { + return _packagePath; + } + + public override IEnumerable GetAllPaths() + { + return new List() { _packagePath }; + } + + public override bool IsPathValid(string path, out string error) + { + error = null; + + var pathIsUnityPackage = path.EndsWith(PackageExtension); + var pathExists = File.Exists(path); + + if (!pathIsUnityPackage || !pathExists) + { + error = "Path must point to a .unitypackage file"; + return false; + } + + return true; + } + + public override IValidator CreateValidator() + { + var validationSettings = new ExternalProjectValidationSettings() + { + Category = Package.Category, + PackagePath = GetPackagePath() + }; + + var validator = new ExternalProjectValidator(validationSettings); + return validator; + } + + public override IPackageExporter CreateExporter(string _) + { + // This workflow already takes exported packages as input + throw new InvalidOperationException($"{nameof(UnityPackageWorkflow)} already takes exported packages as input"); + } + + public override Task ExportPackage(string _) + { + return Task.FromResult(new PackageExporterResult() { Success = true, ExportedPath = GetPackagePath() }); + } + + protected override IPackageUploader CreatePackageUploader(string exportedPackagePath) + { + var uploaderSettings = new UnityPackageUploadSettings() + { + VersionId = Package.VersionId, + UnityPackagePath = exportedPackagePath, + RootGuid = LocalPackageGuid, + RootPath = LocalPackagePath, + ProjectPath = LocalProjectPath + }; + + var uploader = new UnityPackageUploader(uploaderSettings); + return uploader; + } + + protected override void Serialize() + { + _workflowState.SetPackagePath(_packagePath); + OnChanged?.Invoke(); + } + + protected override void Deserialize() + { + _packagePath = _workflowState.GetPackagePath(); + DeserializeFromUploadedData(); + } + + private void DeserializeFromUploadedData() + { + if (!string.IsNullOrEmpty(_packagePath)) + return; + + var potentialPackagePath = Package.ProjectPath; + if (string.IsNullOrEmpty(potentialPackagePath) || !IsPathValid(potentialPackagePath, out var _)) + return; + + _packagePath = potentialPackagePath; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs.meta new file mode 100644 index 0000000..031a510 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47ee1db30792bf84aa1af8be7ce0dee6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs new file mode 100644 index 0000000..525374d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs @@ -0,0 +1,53 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Uploader.Services.Analytics; +using AssetStoreTools.Uploader.Services.Analytics.Data; +using AssetStoreTools.Uploader.Services.Api; +using System; +using System.Threading.Tasks; +using UnityEngine.Analytics; + +namespace AssetStoreTools.Uploader.Data +{ + internal class WorkflowServices : IWorkflowServices + { + private IPackageDownloadingService _downloadingService; + private IPackageUploadingService _uploadingService; + private IAnalyticsService _analyticsService; + + public WorkflowServices( + IPackageDownloadingService downloadingService, + IPackageUploadingService uploadingService, + IAnalyticsService analyticsService) + { + _downloadingService = downloadingService; + _uploadingService = uploadingService; + _analyticsService = analyticsService; + } + + public Task GetPackageUploadedVersions(IPackage package, int timeoutMs) + { + return _downloadingService.GetPackageUploadedVersions(package, timeoutMs); + } + + public Task UploadPackage(IPackageUploader uploader, IProgress progress) + { + return _uploadingService.UploadPackage(uploader, progress); + } + + public void StopUploading(IPackageUploader uploader) + { + _uploadingService.StopUploading(uploader); + } + + public Task UpdatePackageData(IPackage package) + { + return _downloadingService.UpdatePackageData(package); + } + + public AnalyticsResult SendAnalytic(IAssetStoreAnalytic data) + { + return _analyticsService.Send(data); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs.meta new file mode 100644 index 0000000..2431462 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a78b96ae30966e94ba9ffdddf19c1692 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services.meta new file mode 100644 index 0000000..6d1acef --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d9787842821f3d041904186d0e0cc61d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics.meta new file mode 100644 index 0000000..30b3cbf --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 17f95678acdb51548908d81be7146b5b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs new file mode 100644 index 0000000..a2f6e1e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs @@ -0,0 +1,45 @@ +using AssetStoreTools.Uploader.Services.Analytics.Data; +using UnityEditor; +using UnityEngine.Analytics; +#if !UNITY_2023_2_OR_NEWER +using AnalyticsConstants = AssetStoreTools.Constants.Uploader.Analytics; +#endif + +namespace AssetStoreTools.Uploader.Services.Analytics +{ + internal class AnalyticsService : IAnalyticsService + { + public AnalyticsResult Send(IAssetStoreAnalytic analytic) + { + if (!EditorAnalytics.enabled) + return AnalyticsResult.AnalyticsDisabled; + + if (!Register(analytic)) + return AnalyticsResult.AnalyticsDisabled; + +#if UNITY_2023_2_OR_NEWER + return EditorAnalytics.SendAnalytic(analytic); +#else + return EditorAnalytics.SendEventWithLimit(analytic.EventName, + analytic.Data, + analytic.EventVersion); +#endif + } + + private bool Register(IAssetStoreAnalytic analytic) + { +#if UNITY_2023_2_OR_NEWER + return true; +#else + var result = EditorAnalytics.RegisterEventWithLimit( + eventName: analytic.EventName, + maxEventPerHour: AnalyticsConstants.MaxEventsPerHour, + maxItems: AnalyticsConstants.MaxNumberOfElements, + vendorKey: AnalyticsConstants.VendorKey, + ver: analytic.EventVersion); + + return result == AnalyticsResult.Ok; +#endif + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs.meta new file mode 100644 index 0000000..a4acea1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 408b5b0136da9ca4f9598b8688f6210e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data.meta new file mode 100644 index 0000000..59ec0e4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: df1fca726619f2f4fae3bd93b0ef5a8b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs new file mode 100644 index 0000000..74edf0e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs @@ -0,0 +1,46 @@ +using AssetStoreTools.Api; +using System; +#if UNITY_2023_2_OR_NEWER +using UnityEngine.Analytics; +#endif +using AnalyticsConstants = AssetStoreTools.Constants.Uploader.Analytics; + +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ +#if UNITY_2023_2_OR_NEWER + [AnalyticInfo + (eventName: AnalyticsConstants.AuthenticationAnalytics.EventName, + vendorKey: AnalyticsConstants.VendorKey, + version: AnalyticsConstants.AuthenticationAnalytics.EventVersion, + maxEventsPerHour: AnalyticsConstants.MaxEventsPerHour, + maxNumberOfElements: AnalyticsConstants.MaxNumberOfElements)] +#endif + internal class AuthenticationAnalytic : BaseAnalytic + { + [Serializable] + public class AuthenticationAnalyticData : BaseAnalyticData + { + public string AuthenticationType; + public string PublisherId; + } + + public override string EventName => AnalyticsConstants.AuthenticationAnalytics.EventName; + public override int EventVersion => AnalyticsConstants.AuthenticationAnalytics.EventVersion; + + private AuthenticationAnalyticData _data; + + public AuthenticationAnalytic(IAuthenticationType authenticationType, string publisherId) + { + _data = new AuthenticationAnalyticData + { + AuthenticationType = authenticationType.GetType().Name, + PublisherId = publisherId + }; + } + + protected override BaseAnalyticData GetData() + { + return _data; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs.meta new file mode 100644 index 0000000..83763d7 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b9389e3ee578484493d36775c75baa1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs new file mode 100644 index 0000000..60c3396 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs @@ -0,0 +1,35 @@ +using System; +#if UNITY_2023_2_OR_NEWER +using UnityEngine.Analytics; +#endif + +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ + internal abstract class BaseAnalytic : IAssetStoreAnalytic + { + [Serializable] + public class BaseAnalyticData : IAssetStoreAnalyticData + { + public string ToolVersion = Constants.Api.ApiVersion; + } + + public abstract string EventName { get; } + public abstract int EventVersion { get; } + + public IAssetStoreAnalyticData Data => GetData(); + protected abstract BaseAnalyticData GetData(); + +#if UNITY_2023_2_OR_NEWER + public bool TryGatherData(out IAnalytic.IData data, [System.Diagnostics.CodeAnalysis.NotNullWhen(false)] out Exception error) + { + error = null; + data = Data; + + if (data == null) + error = new Exception("Analytic data is null"); + + return error == null; + } +#endif + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs.meta new file mode 100644 index 0000000..b16de7f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 51ec1e4b6505b694ab01f7c523744fbc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs new file mode 100644 index 0000000..5207ca0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs @@ -0,0 +1,16 @@ +#if UNITY_2023_2_OR_NEWER +using UnityEngine.Analytics; +#endif + +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ + internal interface IAssetStoreAnalytic +#if UNITY_2023_2_OR_NEWER + : IAnalytic +#endif + { + string EventName { get; } + int EventVersion { get; } + IAssetStoreAnalyticData Data { get; } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs.meta new file mode 100644 index 0000000..29bfff8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e9b53aa176bbed48bafa538c26df304 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs new file mode 100644 index 0000000..e08b8f8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs @@ -0,0 +1,8 @@ +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ + interface IAssetStoreAnalyticData +#if UNITY_2023_2_OR_NEWER + : UnityEngine.Analytics.IAnalytic.IData +#endif + { } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs.meta new file mode 100644 index 0000000..0c3280d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b639e25d9b9abd34d8eb67b0e17dde86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs new file mode 100644 index 0000000..d8a3479 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs @@ -0,0 +1,72 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Validator.Data; +using System; +#if UNITY_2023_2_OR_NEWER +using UnityEngine.Analytics; +#endif +using AnalyticsConstants = AssetStoreTools.Constants.Uploader.Analytics; + +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ +#if UNITY_2023_2_OR_NEWER + [AnalyticInfo + (eventName: AnalyticsConstants.PackageUploadAnalytics.EventName, + vendorKey: AnalyticsConstants.VendorKey, + version: AnalyticsConstants.PackageUploadAnalytics.EventVersion, + maxEventsPerHour: AnalyticsConstants.MaxEventsPerHour, + maxNumberOfElements: AnalyticsConstants.MaxNumberOfElements)] +#endif + internal class PackageUploadAnalytic : BaseAnalytic + { + [Serializable] + public class PackageUploadAnalyticData : BaseAnalyticData + { + 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; + } + + public override string EventName => AnalyticsConstants.PackageUploadAnalytics.EventName; + public override int EventVersion => AnalyticsConstants.PackageUploadAnalytics.EventVersion; + + private PackageUploadAnalyticData _data; + + public PackageUploadAnalytic( + string packageId, + string category, + bool usedValidator, + ValidationSettings validationSettings, + ValidationResult validationResult, + UploadStatus uploadFinishedReason, + double timeTaken, + long packageSize, + string workflow + ) + { + _data = new PackageUploadAnalyticData() + { + PackageId = packageId, + Category = category, + UsedValidator = usedValidator, + ValidatorResults = usedValidator ? + ValidationResultsSerializer.ConstructValidationResultsJson(validationSettings, validationResult) : null, + UploadFinishedReason = uploadFinishedReason.ToString(), + TimeTaken = timeTaken, + PackageSize = packageSize, + Workflow = workflow, + EndpointUrl = Constants.Api.AssetStoreBaseUrl + }; + } + + protected override BaseAnalyticData GetData() + { + return _data; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs.meta new file mode 100644 index 0000000..bb91393 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6cc34de12dce9964b9c900d5bb159966 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs new file mode 100644 index 0000000..1bb7b56 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs @@ -0,0 +1,91 @@ +using AssetStoreTools.Validator.Data; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; +using System.Reflection; + +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ + internal class ValidationResultsSerializer + { + private class ValidationResults + { + public bool HasCompilationErrors; + public string[] Paths; + public Dictionary Results; + } + + private class TestResultOutcome + { + public int IntegerValue; + public string StringValue; + + public TestResultOutcome(TestResultStatus status) + { + IntegerValue = (int)status; + StringValue = status.ToString(); + } + } + + private class ValidationResultsResolver : DefaultContractResolver + { + private static ValidationResultsResolver _instance; + public static ValidationResultsResolver Instance => _instance ?? (_instance = new ValidationResultsResolver()); + + private Dictionary _propertyConversion; + + private ValidationResultsResolver() + { + _propertyConversion = new Dictionary() + { + { nameof(ValidationResults.HasCompilationErrors), "has_compilation_errors" }, + { nameof(ValidationResults.Paths), "validation_paths" }, + { nameof(ValidationResults.Results), "validation_results" }, + { nameof(TestResultOutcome.IntegerValue), "int" }, + { nameof(TestResultOutcome.StringValue), "string" }, + }; + } + + protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + var property = base.CreateProperty(member, memberSerialization); + if (_propertyConversion.ContainsKey(property.PropertyName)) + property.PropertyName = _propertyConversion[property.PropertyName]; + + return property; + } + } + + public static string ConstructValidationResultsJson(ValidationSettings settings, ValidationResult result) + { + if (result == null) + return string.Empty; + + var resultObject = new ValidationResults(); + resultObject.HasCompilationErrors = result.HadCompilationErrors; + + switch (settings) + { + case CurrentProjectValidationSettings currentProjectValidationSettings: + resultObject.Paths = currentProjectValidationSettings.ValidationPaths.ToArray(); + break; + case ExternalProjectValidationSettings externalProjectValidationSettings: + resultObject.Paths = new string[] { externalProjectValidationSettings.PackagePath }; + break; + } + + resultObject.Results = new Dictionary(); + foreach (var test in result.Tests) + { + resultObject.Results.Add(test.Id.ToString(), new TestResultOutcome(test.Result.Status)); + } + + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = ValidationResultsResolver.Instance + }; + + return JsonConvert.SerializeObject(resultObject, serializerSettings); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs.meta new file mode 100644 index 0000000..f5415b8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa15fc27c7f3d044884885b3dad73efc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs new file mode 100644 index 0000000..a64dce1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs @@ -0,0 +1,10 @@ +using AssetStoreTools.Uploader.Services.Analytics.Data; +using UnityEngine.Analytics; + +namespace AssetStoreTools.Uploader.Services.Analytics +{ + internal interface IAnalyticsService : IUploaderService + { + AnalyticsResult Send(IAssetStoreAnalytic analytic); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs.meta new file mode 100644 index 0000000..2db5042 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: faa1f39fc83b86b438f6e0f34f01167b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api.meta new file mode 100644 index 0000000..7075aa4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4d983b64bd0866a428f937434252f537 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs new file mode 100644 index 0000000..e002818 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs @@ -0,0 +1,100 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Models; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Uploader.Services.Analytics; +using AssetStoreTools.Uploader.Services.Analytics.Data; +using AssetStoreTools.Utility; +using System; +using System.Threading.Tasks; +using UnityEditor; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal class AuthenticationService : IAuthenticationService + { + private IAssetStoreApi _api; + private ICachingService _cachingService; + private IAnalyticsService _analyticsService; + + public User User { get; private set; } + + public AuthenticationService(IAssetStoreApi api, ICachingService cachingService, IAnalyticsService analyticsService) + { + _api = api; + _cachingService = cachingService; + _analyticsService = analyticsService; + } + + public async Task AuthenticateWithCredentials(string email, string password) + { + var authenticationType = new CredentialsAuthentication(email, password); + return await Authenticate(authenticationType); + } + + public async Task AuthenticateWithSessionToken() + { + if (!_cachingService.GetCachedSessionToken(out var cachedSessionToken)) + { + return new AuthenticationResponse() { Success = false, Exception = new Exception("No cached session token found") }; + } + + var authenticationType = new SessionAuthentication(cachedSessionToken); + return await Authenticate(authenticationType); + } + + public async Task AuthenticateWithCloudToken() + { + var authenticationType = new CloudTokenAuthentication(CloudProjectSettings.accessToken); + return await Authenticate(authenticationType); + } + + private async Task Authenticate(IAuthenticationType authenticationType) + { + var response = await _api.Authenticate(authenticationType); + HandleLoginResponse(authenticationType, response); + return response; + } + + private void HandleLoginResponse(IAuthenticationType authenticationType, AuthenticationResponse response) + { + if (!response.Success) + { + Deauthenticate(); + return; + } + + User = response.User; + _cachingService.CacheSessionToken(User.SessionId); + SendAnalytics(authenticationType, User); + } + + public bool CloudAuthenticationAvailable(out string username, out string cloudToken) + { + username = CloudProjectSettings.userName; + cloudToken = CloudProjectSettings.accessToken; + return !username.Equals("anonymous"); + } + + public void Deauthenticate() + { + _api.Deauthenticate(); + + User = null; + _cachingService.ClearCachedSessionToken(); + } + + private void SendAnalytics(IAuthenticationType authenticationType, User user) + { + try + { + // Do not send session authentication events + if (authenticationType is SessionAuthentication) + return; + + var analytic = new AuthenticationAnalytic(authenticationType, user.PublisherId); + var result = _analyticsService.Send(analytic); + } + catch (Exception e) { ASDebug.LogError($"Could not send analytics: {e}"); } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs.meta new file mode 100644 index 0000000..b36dd7d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c1c3d6578d298d049a8dcf858fd3686e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs new file mode 100644 index 0000000..96c9ee3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs @@ -0,0 +1,16 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Api.Responses; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal interface IAuthenticationService : IUploaderService + { + User User { get; } + Task AuthenticateWithCredentials(string email, string password); + Task AuthenticateWithSessionToken(); + Task AuthenticateWithCloudToken(); + bool CloudAuthenticationAvailable(out string username, out string cloudToken); + void Deauthenticate(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs.meta new file mode 100644 index 0000000..25b27c4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ff0518dc0d95d3540857d138215bb900 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs new file mode 100644 index 0000000..4cef2c0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs @@ -0,0 +1,16 @@ +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Uploader.Data; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal interface IPackageDownloadingService : IUploaderService + { + Task GetPackageData(); + Task UpdatePackageData(IPackage package); + void ClearPackageData(); + Task GetPackageThumbnail(IPackage package); + Task GetPackageUploadedVersions(IPackage package, int timeoutMs); + void StopDownloading(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs.meta new file mode 100644 index 0000000..35eaf1e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 96acd12a628311d429cc285f418f8b90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs new file mode 100644 index 0000000..f1ff1d4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs @@ -0,0 +1,16 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using System; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal interface IPackageUploadingService : IUploaderService + { + bool IsUploading { get; } + + Task UploadPackage(IPackageUploader uploader, IProgress progress); + void StopUploading(IPackageUploader package); + void StopAllUploadinng(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs.meta new file mode 100644 index 0000000..153b53a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a3d78f3bc68d3d44b4300bc8ffe69c2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs new file mode 100644 index 0000000..5ab052f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs @@ -0,0 +1,109 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Uploader.Data; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal class PackageDownloadingService : IPackageDownloadingService + { + public const int MaxConcurrentTumbnailDownloads = 10; + + private IAssetStoreApi _api; + private ICachingService _cachingService; + + private int _currentDownloads; + private CancellationTokenSource _cancellationTokenSource; + + public PackageDownloadingService(IAssetStoreApi api, ICachingService cachingService) + { + _api = api; + _cachingService = cachingService; + _cancellationTokenSource = new CancellationTokenSource(); + } + + public void ClearPackageData() + { + _cachingService.DeletePackageMetadata(); + } + + public async Task GetPackageData() + { + if (!_cachingService.GetCachedPackageMetadata(out var models)) + { + var cancellationToken = _cancellationTokenSource.Token; + var packagesResponse = await _api.GetPackages(cancellationToken); + + if (packagesResponse.Cancelled || !packagesResponse.Success) + return packagesResponse; + + _cachingService.CachePackageMetadata(packagesResponse.Packages); + return packagesResponse; + } + + return new PackagesDataResponse() { Success = true, Packages = models }; + } + + public async Task UpdatePackageData(IPackage package) + { + var response = await _api.RefreshPackageMetadata(package.ToModel()); + + if (response.Success) + _cachingService.UpdatePackageMetadata(response.Package); + + return response; + } + + public async Task GetPackageThumbnail(IPackage package) + { + if (_cachingService.GetCachedPackageThumbnail(package.PackageId, out var cachedTexture)) + { + return new PackageThumbnailResponse() { Success = true, Thumbnail = cachedTexture }; + } + + var cancellationToken = _cancellationTokenSource.Token; + while (_currentDownloads >= MaxConcurrentTumbnailDownloads) + await Task.Delay(100); + + if (cancellationToken.IsCancellationRequested) + return new PackageThumbnailResponse() { Success = false, Cancelled = true }; + + _currentDownloads++; + var result = await _api.GetPackageThumbnail(package.ToModel(), cancellationToken); + _currentDownloads--; + + if (result.Success && result.Thumbnail != null) + _cachingService.CachePackageThumbnail(package.PackageId, result.Thumbnail); + + return result; + } + + public async Task GetPackageUploadedVersions(IPackage package, int timeoutMs) + { + var timeoutTokenSource = new CancellationTokenSource(); + try + { + var versionsTask = _api.GetPackageUploadedVersions(package.ToModel(), timeoutTokenSource.Token); + + // Wait for versions to be retrieved, or a timeout to occur, whichever is first + if (await Task.WhenAny(versionsTask, Task.Delay(timeoutMs)) != versionsTask) + { + timeoutTokenSource.Cancel(); + } + + return await versionsTask; + } + finally + { + timeoutTokenSource.Dispose(); + } + } + + public void StopDownloading() + { + _cancellationTokenSource.Cancel(); + _cancellationTokenSource = new CancellationTokenSource(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs.meta new file mode 100644 index 0000000..5e82ecb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: adc44e974cb91b54fac3819284b7ba82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs new file mode 100644 index 0000000..53da1bb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs @@ -0,0 +1,103 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using UnityEditor; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal class PackageUploadingService : IPackageUploadingService + { + private class UploadInProgress + { + public IPackageUploader Uploader; + public IProgress Progress = new Progress(); + public CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); + + public UploadInProgress(IPackageUploader uploader, IProgress progress) + { + Uploader = uploader; + Progress = progress; + CancellationTokenSource = new CancellationTokenSource(); + } + } + + private IAssetStoreApi _api; + private List _uploadsInProgress; + + public bool IsUploading => _uploadsInProgress.Count > 0; + + public PackageUploadingService(IAssetStoreApi api) + { + _api = api; + _uploadsInProgress = new List(); + } + + public async Task UploadPackage(IPackageUploader uploader, IProgress progress) + { + using (var cancellationTokenSource = new CancellationTokenSource()) + { + var uploadInProgress = StartTrackingUpload(uploader, progress); + var response = await _api.UploadPackage(uploadInProgress.Uploader, uploadInProgress.Progress, uploadInProgress.CancellationTokenSource.Token); + StopTrackingUpload(uploadInProgress); + + return response; + } + } + + private UploadInProgress StartTrackingUpload(IPackageUploader uploader, IProgress progress) + { + // If this is the first upload - lock reload assemblies and prevent entering play mode + if (_uploadsInProgress.Count == 0) + { + EditorApplication.LockReloadAssemblies(); + EditorApplication.playModeStateChanged += PreventEnteringPlayMode; + } + + var uploadInProgress = new UploadInProgress(uploader, progress); + _uploadsInProgress.Add(uploadInProgress); + + return uploadInProgress; + } + + private void StopTrackingUpload(UploadInProgress uploadInProgress) + { + _uploadsInProgress.Remove(uploadInProgress); + + // If this was the last upload - unlock reload assemblies and allow entering play mode + if (_uploadsInProgress.Count > 0) + return; + + EditorApplication.UnlockReloadAssemblies(); + EditorApplication.playModeStateChanged -= PreventEnteringPlayMode; + } + + private void PreventEnteringPlayMode(PlayModeStateChange change) + { + if (change != 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"); + } + + public void StopUploading(IPackageUploader uploader) + { + var uploadInProgress = _uploadsInProgress.FirstOrDefault(x => x.Uploader == uploader); + if (uploadInProgress == null) + return; + + uploadInProgress.CancellationTokenSource.Cancel(); + } + + public void StopAllUploadinng() + { + foreach (var uploadInProgress in _uploadsInProgress) + uploadInProgress.CancellationTokenSource.Cancel(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs.meta new file mode 100644 index 0000000..3ea1381 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 22e23997fe339a74bb5355d6a88ce731 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching.meta new file mode 100644 index 0000000..7267ddb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a834946d92154754493879c5fcc7dbc9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs new file mode 100644 index 0000000..c86ee80 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs @@ -0,0 +1,157 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Utility; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.Services +{ + internal class CachingService : ICachingService + { + private VisualElement _cachedUploaderWindow; + + public bool GetCachedUploaderWindow(out VisualElement uploaderWindow) + { + uploaderWindow = _cachedUploaderWindow; + return uploaderWindow != null; + } + + public void CacheUploaderWindow(VisualElement uploaderWindow) + { + _cachedUploaderWindow = uploaderWindow; + } + + public void CacheSessionToken(string sessionToken) + { + if (string.IsNullOrEmpty(sessionToken)) + throw new ArgumentException("Session token cannot be null"); + + EditorPrefs.SetString(Constants.Cache.SessionTokenKey, sessionToken); + } + + public bool GetCachedSessionToken(out string sessionToken) + { + sessionToken = EditorPrefs.GetString(Constants.Cache.SessionTokenKey, string.Empty); + return !string.IsNullOrEmpty(sessionToken); + } + + public void ClearCachedSessionToken() + { + EditorPrefs.DeleteKey(Constants.Cache.SessionTokenKey); + } + + public bool GetCachedPackageMetadata(out List data) + { + data = new List(); + if (!CacheUtil.GetFileFromTempCache(Constants.Cache.PackageDataFileName, out var filePath)) + return false; + + try + { + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = Package.CachedPackageResolver.Instance + }; + + data = JsonConvert.DeserializeObject>(File.ReadAllText(filePath, Encoding.UTF8), serializerSettings); + return true; + } + catch + { + return false; + } + } + + public void CachePackageMetadata(List data) + { + if (data == null) + throw new ArgumentException("Package data cannot be null"); + + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = Package.CachedPackageResolver.Instance, + Formatting = Formatting.Indented + }; + + CacheUtil.CreateFileInTempCache(Constants.Cache.PackageDataFileName, JsonConvert.SerializeObject(data, serializerSettings), true); + } + + public void DeletePackageMetadata() + { + CacheUtil.DeleteFileFromTempCache(Constants.Cache.PackageDataFileName); + } + + public void UpdatePackageMetadata(Package data) + { + if (!GetCachedPackageMetadata(out var cachedData)) + return; + + var index = cachedData.FindIndex(x => x.PackageId.Equals(data.PackageId)); + if (index == -1) + { + cachedData.Add(data); + } + else + { + cachedData.RemoveAt(index); + cachedData.Insert(index, data); + } + + CachePackageMetadata(cachedData); + } + + public bool GetCachedPackageThumbnail(string packageId, out Texture2D texture) + { + texture = null; + if (!CacheUtil.GetFileFromTempCache(Constants.Cache.PackageThumbnailFileName(packageId), out var filePath)) + return false; + + texture = new Texture2D(1, 1); + texture.LoadImage(File.ReadAllBytes(filePath)); + return true; + } + + public void CachePackageThumbnail(string packageId, Texture2D texture) + { + CacheUtil.CreateFileInTempCache(Constants.Cache.PackageThumbnailFileName(packageId), texture.EncodeToPNG(), true); + } + + public bool GetCachedWorkflowStateData(string packageId, out WorkflowStateData data) + { + data = null; + + if (string.IsNullOrEmpty(packageId)) + return false; + + if (!CacheUtil.GetFileFromPersistentCache(Constants.Cache.WorkflowStateDataFileName(packageId), out var filePath)) + return false; + + try + { + data = JsonConvert.DeserializeObject(File.ReadAllText(filePath, Encoding.UTF8)); + if (string.IsNullOrEmpty(data.GetPackageId())) + return false; + } + catch + { + return false; + } + + return true; + } + + public void CacheWorkflowStateData(WorkflowStateData data) + { + if (data == null) + throw new ArgumentException("Workflow state data cannot be null"); + + CacheUtil.CreateFileInPersistentCache(Constants.Cache.WorkflowStateDataFileName(data.GetPackageId()), JsonConvert.SerializeObject(data, Formatting.Indented), true); + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs.meta new file mode 100644 index 0000000..b5893a5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fffaed09a3f76f945a7ececfb355f3e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs new file mode 100644 index 0000000..1077281 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs @@ -0,0 +1,25 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Uploader.Data.Serialization; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.Services +{ + internal interface ICachingService : IUploaderService + { + void CacheUploaderWindow(VisualElement uploaderWindow); + bool GetCachedUploaderWindow(out VisualElement uploaderWindow); + void CacheSessionToken(string sessionToken); + bool GetCachedSessionToken(out string sessionToken); + void ClearCachedSessionToken(); + bool GetCachedPackageMetadata(out List data); + void UpdatePackageMetadata(Package data); + void CachePackageMetadata(List data); + void DeletePackageMetadata(); + bool GetCachedPackageThumbnail(string packageId, out Texture2D texture); + void CachePackageThumbnail(string packageId, Texture2D texture); + bool GetCachedWorkflowStateData(string packageId, out WorkflowStateData data); + void CacheWorkflowStateData(WorkflowStateData data); + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs.meta new file mode 100644 index 0000000..559ee87 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a904477679e07bc4889bc15e894c0c48 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs new file mode 100644 index 0000000..424dd9c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs @@ -0,0 +1,4 @@ +namespace AssetStoreTools.Uploader.Services +{ + internal interface IUploaderService { } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs.meta new file mode 100644 index 0000000..7168b6f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 757d7a4dc29863740859c936be514fea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory.meta new file mode 100644 index 0000000..cb9e723 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 02e4a5ee9e2fb7941b876b207078e01d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs new file mode 100644 index 0000000..83c9c48 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs @@ -0,0 +1,18 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Uploader.Data.Serialization; +using System.Collections.Generic; +using PackageModel = AssetStoreTools.Api.Models.Package; + +namespace AssetStoreTools.Uploader.Services +{ + internal interface IPackageFactoryService : IUploaderService + { + IPackageGroup CreatePackageGroup(string groupName, List packages); + IPackage CreatePackage(PackageModel packageModel); + IPackageContent CreatePackageContent(IPackage package); + List CreateWorkflows(IPackage package, WorkflowStateData stateData); + AssetsWorkflow CreateAssetsWorkflow(IPackage package, AssetsWorkflowState stateData); + UnityPackageWorkflow CreateUnityPackageWorkflow(IPackage package, UnityPackageWorkflowState stateData); + HybridPackageWorkflow CreateHybridPackageWorkflow(IPackage package, HybridPackageWorkflowState stateData); + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs.meta new file mode 100644 index 0000000..1a49e7c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 14324b71768a1ea499baa06de33f05af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs new file mode 100644 index 0000000..a9fffa5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs @@ -0,0 +1,95 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Uploader.Services.Analytics; +using AssetStoreTools.Uploader.Services.Api; +using System.Collections.Generic; +using PackageModel = AssetStoreTools.Api.Models.Package; + +namespace AssetStoreTools.Uploader.Services +{ + internal class PackageFactoryService : IPackageFactoryService + { + private IWorkflowServices _workflowServices; + + // Service dependencies + private ICachingService _cachingService; + private IPackageDownloadingService _packageDownloadingService; + private IPackageUploadingService _packageUploadingService; + private IAnalyticsService _analyticsService; + + public PackageFactoryService( + ICachingService cachingService, + IPackageDownloadingService packageDownloadingService, + IPackageUploadingService packageUploadingService, + IAnalyticsService analyticsService + ) + { + _cachingService = cachingService; + _packageDownloadingService = packageDownloadingService; + _packageUploadingService = packageUploadingService; + _analyticsService = analyticsService; + + _workflowServices = new WorkflowServices(_packageDownloadingService, _packageUploadingService, _analyticsService); + } + + public IPackage CreatePackage(PackageModel packageModel) + { + var package = new Package(packageModel); + return package; + } + + public IPackageGroup CreatePackageGroup(string groupName, List packages) + { + return new PackageGroup(groupName, packages); + } + + public IPackageContent CreatePackageContent(IPackage package) + { + if (!package.IsDraft) + return null; + + WorkflowStateData stateData = GetOrCreateWorkflowStateData(package); + + var workflows = CreateWorkflows(package, stateData); + var packageContent = new PackageContent(workflows, stateData, _cachingService); + return packageContent; + } + + public List CreateWorkflows(IPackage package, WorkflowStateData stateData) + { + var workflows = new List + { + CreateAssetsWorkflow(package, stateData.GetAssetsWorkflowState()), + CreateUnityPackageWorkflow(package, stateData.GetUnityPackageWorkflowState()), +#if UNITY_ASTOOLS_EXPERIMENTAL + CreateHybridPackageWorkflow(package, stateData.GetHybridPackageWorkflowState()), +#endif + }; + + return workflows; + } + + public AssetsWorkflow CreateAssetsWorkflow(IPackage package, AssetsWorkflowState stateData) + { + return new AssetsWorkflow(package, stateData, _workflowServices); + } + + public UnityPackageWorkflow CreateUnityPackageWorkflow(IPackage package, UnityPackageWorkflowState stateData) + { + return new UnityPackageWorkflow(package, stateData, _workflowServices); + } + + public HybridPackageWorkflow CreateHybridPackageWorkflow(IPackage package, HybridPackageWorkflowState stateData) + { + return new HybridPackageWorkflow(package, stateData, _workflowServices); + } + + private WorkflowStateData GetOrCreateWorkflowStateData(IPackage package) + { + if (!_cachingService.GetCachedWorkflowStateData(package.PackageId, out var stateData)) + stateData = new WorkflowStateData(package.PackageId); + + return stateData; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs.meta new file mode 100644 index 0000000..b99429f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4074a5b21b6201d449974dcfb652a00b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs new file mode 100644 index 0000000..0aaa686 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs @@ -0,0 +1,26 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Uploader.Services.Analytics; +using AssetStoreTools.Uploader.Services.Api; +using AssetStoreTools.Utility; + +namespace AssetStoreTools.Uploader.Services +{ + internal class UploaderServiceProvider : ServiceProvider + { + public static UploaderServiceProvider Instance => _instance ?? (_instance = new UploaderServiceProvider()); + private static UploaderServiceProvider _instance; + + private UploaderServiceProvider() { } + + protected override void RegisterServices() + { + var api = new AssetStoreApi(new AssetStoreClient()); + Register(); + Register(); + Register(() => new AuthenticationService(api, GetService(), GetService())); + Register(() => new PackageDownloadingService(api, GetService())); + Register(() => new PackageUploadingService(api)); + Register(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs.meta new file mode 100644 index 0000000..ccf2e7b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e66f9c7f198baff41ba77f4d0ed7b60f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI.meta new file mode 100644 index 0000000..e39a22f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ed759a6e886dbfd4fbcecc2beb7248b8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements.meta new file mode 100644 index 0000000..497fc1d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d14df9cf4e7e9b54c8c94a8cc1aa70c0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions.meta new file mode 100644 index 0000000..9ec9955 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 144a518ff26df1e41845217c0f0002d7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/ValidationElementBase.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/ValidationElementBase.cs new file mode 100644 index 0000000..27957b2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/ValidationElementBase.cs @@ -0,0 +1,170 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Validator.Data; +using System.Linq; +using UnityEditor; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal abstract class ValidationElementBase : VisualElement + { + // Data + protected IWorkflow Workflow; + + // UI + protected VisualElement ResultsBox; + protected Image ResultsBoxImage; + protected Label ResultsBoxLabel; + + protected ValidationElementBase(IWorkflow workflow) + { + Workflow = workflow; + Create(); + } + + private void Create() + { + CreateInfoRow(); + CreateResultsBox(); + } + + private void CreateInfoRow() + { + VisualElement validatorButtonRow = new VisualElement(); + validatorButtonRow.AddToClassList("package-content-option-box"); + + VisualElement validatorLabelHelpRow = new VisualElement(); + validatorLabelHelpRow.AddToClassList("package-content-option-label-help-row"); + + Label validatorLabel = new Label { text = "Validation" }; + Image validatorLabelTooltip = new Image + { + tooltip = "You can use the Asset Store Validator to check your package for common publishing issues" + }; + + var validateButton = new Button(Validate) { name = "ValidateButton", text = "Validate" }; + + validatorLabelHelpRow.Add(validatorLabel); + validatorLabelHelpRow.Add(validatorLabelTooltip); + + validatorButtonRow.Add(validatorLabelHelpRow); + validatorButtonRow.Add(validateButton); + + Add(validatorButtonRow); + } + + private void CreateResultsBox() + { + ResultsBox = new Box { name = "InfoBox" }; + ResultsBox.style.display = DisplayStyle.None; + ResultsBox.AddToClassList("validation-result-box"); + + ResultsBoxImage = new Image(); + ResultsBoxLabel = new Label { name = "ValidationLabel" }; + + ResultsBox.Add(ResultsBoxImage); + ResultsBox.Add(ResultsBoxLabel); + + Add(ResultsBox); + } + + protected virtual bool ConfirmValidation() + { + // Child classes can implement pre-validation prompts + return true; + } + + private void Validate() + { + if (!ConfirmValidation()) + return; + + var validationResult = Workflow.Validate(); + + if (validationResult.Status == ValidationStatus.Cancelled) + return; + + if (validationResult.Status != ValidationStatus.RanToCompletion) + { + EditorUtility.DisplayDialog("Validation failed", $"Package validation failed: {validationResult.Exception.Message}", "OK"); + return; + } + + DisplayResult(validationResult); + } + + private void DisplayResult(ValidationResult result) + { + ResultsBox.style.display = DisplayStyle.Flex; + UpdateValidationResultImage(result); + UpdateValidationResultLabel(result); + } + + public void HideResult() + { + ResultsBox.style.display = DisplayStyle.None; + } + + protected void UpdateValidationResultImage(ValidationResult result) + { + switch (GetValidationSummaryStatus(result)) + { + case TestResultStatus.Pass: + ResultsBoxImage.image = EditorGUIUtility.IconContent("console.infoicon@2x").image; + break; + case TestResultStatus.Warning: + ResultsBoxImage.image = EditorGUIUtility.IconContent("console.warnicon@2x").image; + break; + case TestResultStatus.Fail: + ResultsBoxImage.image = EditorGUIUtility.IconContent("console.erroricon@2x").image; + break; + default: + ResultsBoxImage.image = EditorGUIUtility.IconContent("_Help@2x").image; + break; + } + } + + private void UpdateValidationResultLabel(ValidationResult result) + { + var errorCount = result.Tests.Where(x => x.Result.Status == TestResultStatus.Fail).Count(); + var warningCount = result.Tests.Where(x => x.Result.Status == TestResultStatus.Warning).Count(); + + string text = string.Empty; + if (result.HadCompilationErrors) + { + text += "- Package caused compilation errors\n"; + } + if (errorCount > 0) + { + text += $"- Validation reported {errorCount} error(s)\n"; + } + if (warningCount > 0) + { + text += $"- Validation reported {warningCount} warning(s)\n"; + } + + if (string.IsNullOrEmpty(text)) + { + text = "No issues were found!"; + } + else + { + text = text.Substring(0, text.Length - "\n".Length); + } + + ResultsBoxLabel.text = text; + } + + private TestResultStatus GetValidationSummaryStatus(ValidationResult result) + { + if (result.HadCompilationErrors || + result.Tests.Any(x => x.Result.Status == TestResultStatus.Fail)) + return TestResultStatus.Fail; + + if (result.Tests.Any(x => x.Result.Status == TestResultStatus.Warning)) + return TestResultStatus.Warning; + + return TestResultStatus.Pass; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/ValidationElementBase.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/ValidationElementBase.cs.meta new file mode 100644 index 0000000..f96190a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/ValidationElementBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb20404763eac7144b562c18ad1c37fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/WorkflowElementBase.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/WorkflowElementBase.cs new file mode 100644 index 0000000..f4b22b7 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/WorkflowElementBase.cs @@ -0,0 +1,151 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal abstract class WorkflowElementBase : VisualElement + { + // Data + protected IWorkflow Workflow; + public string Name => Workflow.Name; + public string DisplayName => Workflow.DisplayName; + + // UI Elements that all workflows have + protected PathSelectionElement PathSelectionElement; + protected PreviewGenerationElement PreviewGenerationElement; + protected ValidationElementBase ValidationElement; + protected PackageUploadElement UploadElement; + + public event Action OnInteractionAvailable; + public event Action OnInteractionUnavailable; + + public WorkflowElementBase(IWorkflow workflow) + { + Workflow = workflow; + } + + protected void CreatePathElement(string labelText, string labelTooltip) + { + PathSelectionElement = new PathSelectionElement(labelText, labelTooltip); + PathSelectionElement.OnBrowse += BrowsePath; + Add(PathSelectionElement); + } + + protected void CreatePreviewGenerationElement() + { + PreviewGenerationElement = new PreviewGenerationElement(Workflow); + PreviewGenerationElement.style.display = DisplayStyle.None; + var callback = new Action(() => + PreviewGenerationElement.style.display = ASToolsPreferences.Instance.UseLegacyExporting + ? DisplayStyle.None + : DisplayStyle.Flex); + RegisterCallback((_) => { ASToolsPreferences.OnSettingsChange += callback; }); + RegisterCallback((_) => { ASToolsPreferences.OnSettingsChange -= callback; }); + Add(PreviewGenerationElement); + } + + protected void CreateValidationElement(ValidationElementBase validationElement) + { + ValidationElement = validationElement; + ValidationElement.style.display = DisplayStyle.None; + Add(ValidationElement); + } + + protected void CreateUploadElement(IWorkflow workflow, bool exposeExportButton) + { + UploadElement = new PackageUploadElement(workflow, exposeExportButton); + UploadElement.OnInteractionAvailable += EnableInteraction; + UploadElement.OnInteractionUnavailable += DisableInteraction; + UploadElement.style.display = DisplayStyle.None; + Add(UploadElement); + } + + protected abstract void BrowsePath(); + + protected void SetPathSelectionTextField(string value) + { + if (string.IsNullOrEmpty(value)) + return; + + PathSelectionElement.SetPath(value); + ValidationElement.style.display = DisplayStyle.Flex; + UploadElement.style.display = DisplayStyle.Flex; + + if (PreviewGenerationElement != null && !ASToolsPreferences.Instance.UseLegacyExporting) + { + PreviewGenerationElement.style.display = DisplayStyle.Flex; + } + } + + protected void CheckForMissingMetas(IEnumerable paths) + { + bool displayDialog = ASToolsPreferences.Instance.DisplayHiddenMetaDialog && FileUtility.IsMissingMetaFiles(paths); + if (!displayDialog) + return; + + var selectedOption = EditorUtility.DisplayDialogComplex( + "Notice", + "Your package includes hidden folders which do not contain meta files. " + + "Hidden folders will not be exported unless they contain meta files.\n\nWould you like meta files to be generated?", + "Yes", "No", "No and do not display this again"); + + switch (selectedOption) + { + case 0: + try + { + FileUtility.GenerateMetaFiles(paths); + EditorUtility.DisplayDialog( + "Success", + "Meta files have been generated. Please note that further manual tweaking may be required to set up correct references", + "OK"); + } + catch (Exception e) + { + EditorUtility.DisplayDialog( + "Error", + $"Meta file generation failed: {e.Message}", + "OK" + ); + } + break; + case 1: + // Do nothing + return; + case 2: + ASToolsPreferences.Instance.DisplayHiddenMetaDialog = false; + ASToolsPreferences.Instance.Save(); + return; + } + } + + public bool Is(IWorkflow workflow) + { + return Workflow == workflow; + } + + protected virtual void EnableInteraction() + { + PathSelectionElement.SetEnabled(true); + ValidationElement.SetEnabled(true); + PreviewGenerationElement?.SetEnabled(true); + UploadElement.SetEnabled(true); + OnInteractionAvailable?.Invoke(); + } + + protected virtual void DisableInteraction() + { + PathSelectionElement.SetEnabled(false); + ValidationElement.SetEnabled(false); + PreviewGenerationElement?.SetEnabled(false); + UploadElement.SetEnabled(false); + OnInteractionUnavailable?.Invoke(); + } + + protected abstract void Deserialize(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/WorkflowElementBase.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/WorkflowElementBase.cs.meta new file mode 100644 index 0000000..bdd8416 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/Abstractions/WorkflowElementBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 45d1bf267c3ea9048bfdd75d0d19c8bd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AccountToolbar.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AccountToolbar.cs new file mode 100644 index 0000000..393c4aa --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AccountToolbar.cs @@ -0,0 +1,102 @@ +using AssetStoreTools.Api.Models; +using System; +using System.Threading.Tasks; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class AccountToolbar : VisualElement + { + private Image _accountImage; + private Label _accountEmailLabel; + private Button _refreshButton; + + public event Func OnRefresh; + public event Action OnLogout; + + public AccountToolbar() + { + Create(); + } + + private void Create() + { + AddToClassList("account-toolbar"); + + // Left side of the toolbar + VisualElement leftSideContainer = new VisualElement { name = "LeftSideContainer" }; + leftSideContainer.AddToClassList("account-toolbar-left-side-container"); + + _accountImage = new Image(); + _accountImage.AddToClassList("account-toolbar-user-image"); + + _accountEmailLabel = new Label() { name = "AccountEmail" }; + _accountEmailLabel.AddToClassList("account-toolbar-email-label"); + + leftSideContainer.Add(_accountImage); + leftSideContainer.Add(_accountEmailLabel); + + // Right side of the toolbar + VisualElement rightSideContainer = new VisualElement { name = "RightSideContainer" }; + rightSideContainer.AddToClassList("account-toolbar-right-side-container"); + + // Refresh button + _refreshButton = new Button(Refresh) { name = "RefreshButton", text = "Refresh" }; + _refreshButton.AddToClassList("account-toolbar-button-refresh"); + + // Logout button + var logoutButton = new Button(Logout) { name = "LogoutButton", text = "Log out" }; + logoutButton.AddToClassList("account-toolbar-button-logout"); + + rightSideContainer.Add(_refreshButton); + rightSideContainer.Add(logoutButton); + + // Constructing the final toolbar + Add(leftSideContainer); + Add(rightSideContainer); + } + + private async void Refresh() + { + _refreshButton.SetEnabled(false); + await OnRefresh?.Invoke(); + _refreshButton.SetEnabled(true); + } + + private void Logout() + { + OnLogout?.Invoke(); + } + + public void SetUser(User user) + { + if (user == null) + { + _accountEmailLabel.text = string.Empty; + _accountImage.tooltip = string.Empty; + return; + } + + var userEmail = !string.IsNullOrEmpty(user.Username) ? user.Username : "Unknown"; + var publisherName = !string.IsNullOrEmpty(user.Name) ? user.Name : "Unknown"; + var publisherId = !string.IsNullOrEmpty(user.PublisherId) ? user.PublisherId : "Unknown"; + var userInfo = + $"Username: {userEmail}\n" + + $"Publisher Name: {publisherName}\n" + + $"Publisher ID: {publisherId}"; + + _accountEmailLabel.text = userEmail; + _accountImage.tooltip = userInfo; + } + + public void EnableButtons() + { + _refreshButton.SetEnabled(true); + } + + public void DisableButtons() + { + _refreshButton.SetEnabled(false); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AccountToolbar.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AccountToolbar.cs.meta new file mode 100644 index 0000000..8ac16ee --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AccountToolbar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3c275be3817d1684ca1802c2738ac4d9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AssetsWorkflowElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AssetsWorkflowElement.cs new file mode 100644 index 0000000..283017d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AssetsWorkflowElement.cs @@ -0,0 +1,215 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class AssetsWorkflowElement : WorkflowElementBase + { + // Data + private AssetsWorkflow _workflow; + + // UI + private VisualElement _dependenciesToggleElement; + private Toggle _dependenciesToggle; + private MultiToggleSelectionElement _dependenciesElement; + private MultiToggleSelectionElement _specialFoldersElement; + + private const string PathSelectionTooltip = "Select the main folder of your package" + + "\n\nAll files and folders of your package should preferably be contained within a single root folder that is named after your package" + + "\n\nExample: 'Assets/[MyPackageName]'" + + "\n\nNote: If your content makes use of special folders that are required to be placed in the root Assets folder (e.g. 'StreamingAssets')," + + " you will be able to include them after selecting the main folder"; + + public AssetsWorkflowElement(AssetsWorkflow workflow) : base(workflow) + { + _workflow = workflow; + + Create(); + Deserialize(); + } + + private void Create() + { + CreatePathElement("Folder path", PathSelectionTooltip); + CreateDependenciesToggleElement(); + CreateDependenciesSelectionElement(); + CreateSpecialFoldersElement(); + CreatePreviewGenerationElement(); + CreateValidationElement(new CurrentProjectValidationElement(_workflow)); + CreateUploadElement(_workflow, true); + } + + private void CreateDependenciesToggleElement() + { + _dependenciesToggleElement = new VisualElement() { name = "Dependencies Toggle" }; + _dependenciesToggleElement.AddToClassList("package-content-option-box"); + + VisualElement dependenciesLabelHelpRow = new VisualElement(); + dependenciesLabelHelpRow.AddToClassList("package-content-option-label-help-row"); + + Label dependenciesLabel = new Label { text = "Dependencies" }; + Image dependenciesLabelTooltip = new Image + { + tooltip = "Tick this checkbox if your package content has dependencies on Unity packages from the Package Manager" + }; + + _dependenciesToggle = new Toggle { name = "DependenciesToggle", text = "Include Package Manifest" }; + _dependenciesToggle.AddToClassList("package-content-option-toggle"); + + var callback = new Action(() => DependencyToggleValueChange(true)); + _dependenciesToggle.RegisterValueChangedCallback((_) => DependencyToggleValueChange(true)); + RegisterCallback((_) => { ASToolsPreferences.OnSettingsChange += callback; }); + RegisterCallback((_) => { ASToolsPreferences.OnSettingsChange -= callback; }); + + dependenciesLabelHelpRow.Add(dependenciesLabel); + dependenciesLabelHelpRow.Add(dependenciesLabelTooltip); + + _dependenciesToggleElement.Add(dependenciesLabelHelpRow); + _dependenciesToggleElement.Add(_dependenciesToggle); + + _dependenciesToggleElement.style.display = DisplayStyle.None; + Add(_dependenciesToggleElement); + } + + private void CreateDependenciesSelectionElement() + { + _dependenciesElement = new MultiToggleSelectionElement() + { + DisplayElementLabel = false, + ElementLabel = "Dependencies", + NoSelectionLabel = "No packages match this criteria" + }; + + var setDependencies = new Action>((dict) => _workflow.SetDependencies(dict.Where(x => x.Value).Select(x => x.Key), true)); + _dependenciesElement.OnValuesChanged += setDependencies; + _dependenciesElement.style.display = DisplayStyle.None; + Add(_dependenciesElement); + } + + private void CreateSpecialFoldersElement() + { + _specialFoldersElement = new MultiToggleSelectionElement() + { + ElementLabel = "Special Folders", + ElementTooltip = "If your package content relies on Special Folders (e.g. StreamingAssets), please select which of these folders should be included in the package.", + NoSelectionLabel = "No folders match this criteria." + }; + + var setSpecialFolders = new Action>((dict) => _workflow.SetSpecialFolders(dict.Where(x => x.Value).Select(x => x.Key), true)); + _specialFoldersElement.OnValuesChanged += setSpecialFolders; + _specialFoldersElement.style.display = DisplayStyle.None; + Add(_specialFoldersElement); + } + + protected override void BrowsePath() + { + string absoluteExportPath = string.Empty; + bool includeAllAssets = false; + + if (_workflow.IsCompleteProject) + { + includeAllAssets = EditorUtility.DisplayDialog("Notice", + "Your package draft is set to a category that is treated" + + " as a complete project. Project settings will be included automatically. Would you like everything in the " + + "'Assets' folder to be included?\n\nYou will still be able to change the selected assets before uploading", + "Yes, include all folders and assets", + "No, I'll select what to include manually"); + + if (includeAllAssets) + absoluteExportPath = Application.dataPath; + } + + if (!includeAllAssets) + { + absoluteExportPath = EditorUtility.OpenFolderPanel( + "Select folder to compress into a package", "Assets/", ""); + + if (string.IsNullOrEmpty(absoluteExportPath)) + return; + } + + var relativeExportPath = FileUtility.AbsolutePathToRelativePath(absoluteExportPath, ASToolsPreferences.Instance.EnableSymlinkSupport); + if (!_workflow.IsPathValid(relativeExportPath, out var error)) + { + EditorUtility.DisplayDialog("Invalid selection", error, "OK"); + return; + } + + HandlePathSelection(relativeExportPath, true); + CheckForMissingMetas(); + } + + private void HandlePathSelection(string relativeExportPath, bool serialize) + { + if (string.IsNullOrEmpty(relativeExportPath)) + return; + + _workflow.SetMainExportPath(relativeExportPath, serialize); + SetPathSelectionTextField(relativeExportPath + "/"); + + _dependenciesToggleElement.style.display = DisplayStyle.Flex; + UpdateSpecialFoldersElement(); + } + + private void CheckForMissingMetas() + { + var paths = new List() { _workflow.GetMainExportPath() }; + paths.AddRange(_workflow.GetSpecialFolders()); + CheckForMissingMetas(paths); + } + + private void DependencyToggleValueChange(bool serialize) + { + _workflow.SetIncludeDependencies(_dependenciesToggle.value, serialize); + + if (_dependenciesToggle.value && !ASToolsPreferences.Instance.UseLegacyExporting) + { + var allDependencies = _workflow.GetAvailableDependencies(); + var selectedDependencies = allDependencies.ToDictionary(x => x, y => _workflow.GetDependencies().Any(x => x.name == y)); + _dependenciesElement.Populate(selectedDependencies); + _dependenciesElement.style.display = DisplayStyle.Flex; + } + else + { + _dependenciesElement.style.display = DisplayStyle.None; + } + } + + private void UpdateSpecialFoldersElement() + { + var availableSpecialFolders = _workflow.GetAvailableSpecialFolders(); + var selectedSpecialFolders = availableSpecialFolders.ToDictionary(x => x, y => _workflow.GetSpecialFolders().Any(x => x == y)); + _specialFoldersElement.Populate(selectedSpecialFolders); + _specialFoldersElement.style.display = availableSpecialFolders.Count > 0 ? DisplayStyle.Flex : DisplayStyle.None; + } + + protected override void EnableInteraction() + { + base.EnableInteraction(); + _dependenciesToggleElement.SetEnabled(true); + _dependenciesElement.SetEnabled(true); + _specialFoldersElement.SetEnabled(true); + } + + protected override void DisableInteraction() + { + base.DisableInteraction(); + _dependenciesToggleElement.SetEnabled(false); + _dependenciesElement.SetEnabled(false); + _specialFoldersElement.SetEnabled(false); + } + + protected override void Deserialize() + { + HandlePathSelection(_workflow.GetMainExportPath(), false); + _dependenciesToggle.SetValueWithoutNotify(_workflow.GetIncludeDependencies()); + DependencyToggleValueChange(false); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AssetsWorkflowElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AssetsWorkflowElement.cs.meta new file mode 100644 index 0000000..90d9e5a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/AssetsWorkflowElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f62ea8ab5c102e4fa574a3dcac7f6fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/CurrentProjectValidationElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/CurrentProjectValidationElement.cs new file mode 100644 index 0000000..adfeab1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/CurrentProjectValidationElement.cs @@ -0,0 +1,31 @@ +using AssetStoreTools.Uploader.Data; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class CurrentProjectValidationElement : ValidationElementBase + { + public CurrentProjectValidationElement(IWorkflow workflow) : base(workflow) + { + Create(); + } + + private void Create() + { + CreateResultsBox(); + } + + private void CreateResultsBox() + { + var _viewReportButton = new Button(ViewReport) { text = "View report" }; + _viewReportButton.AddToClassList("validation-result-view-report-button"); + + ResultsBox.Add(_viewReportButton); + } + + private void ViewReport() + { + AssetStoreTools.ShowAssetStoreToolsValidator(Workflow.LastValidationSettings, Workflow.LastValidationResult); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/CurrentProjectValidationElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/CurrentProjectValidationElement.cs.meta new file mode 100644 index 0000000..4acf35f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/CurrentProjectValidationElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 21a1f13231b167b4c80079a2c1212101 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/ExternalProjectValidationElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/ExternalProjectValidationElement.cs new file mode 100644 index 0000000..12c596b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/ExternalProjectValidationElement.cs @@ -0,0 +1,92 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Utility; +using AssetStoreTools.Validator; +using System; +using System.IO; +using UnityEditor; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class ExternalProjectValidationElement : ValidationElementBase + { + private VisualElement _projectButtonContainer; + + public ExternalProjectValidationElement(IWorkflow workflow) : base(workflow) + { + Create(); + } + + private void Create() + { + CreateProjectButtonContainer(); + CreateProjectButtons(); + } + + private void CreateProjectButtonContainer() + { + _projectButtonContainer = new VisualElement(); + _projectButtonContainer.AddToClassList("validation-result-view-report-button-container"); + + ResultsBox.Add(_projectButtonContainer); + } + + private void CreateProjectButtons() + { + var openButton = new Button(OpenProject) { text = "Open Project" }; + openButton.AddToClassList("validation-result-view-report-button"); + + var saveButton = new Button(SaveProject) { text = "Save Project" }; + saveButton.AddToClassList("validation-result-view-report-button"); + + _projectButtonContainer.Add(openButton); + _projectButtonContainer.Add(saveButton); + } + + private void OpenProject() + { + try + { + EditorUtility.DisplayProgressBar("Waiting...", "Validation project is open. Waiting for it to exit...", 0.4f); + var projectPath = Workflow.LastValidationResult.ProjectPath; + ExternalProjectValidator.OpenExternalValidationProject(projectPath); + } + finally + { + EditorUtility.ClearProgressBar(); + } + } + + private void SaveProject() + { + try + { + var projectPath = Workflow.LastValidationResult.ProjectPath; + var savePath = EditorUtility.SaveFolderPanel("Select a folder", Environment.GetFolderPath(Environment.SpecialFolder.Desktop), string.Empty); + if (string.IsNullOrEmpty(savePath)) + return; + + var saveDir = new DirectoryInfo(savePath); + if (!saveDir.Exists || saveDir.GetFileSystemInfos().Length != 0) + { + EditorUtility.DisplayDialog("Saving project failed", "Selected directory must be an empty folder", "OK"); + return; + } + + EditorUtility.DisplayProgressBar("Saving...", "Saving project...", 0.4f); + FileUtility.CopyDirectory(projectPath, savePath, true); + } + finally + { + EditorUtility.ClearProgressBar(); + } + } + + protected override bool ConfirmValidation() + { + return EditorUtility.DisplayDialog("Notice", "Pre-exported package validation is performed in a separate temporary project. " + + "It may take some time for the temporary project to be created, which will halt any actions in the current project. " + + "The current project will resume work after the temporary project is exited.\n\nDo you wish to proceed?", "Yes", "No"); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/ExternalProjectValidationElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/ExternalProjectValidationElement.cs.meta new file mode 100644 index 0000000..63b8bea --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/ExternalProjectValidationElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 706f01d53c7eaf04bae07fb36684e31b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/HybridPackageWorkflowElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/HybridPackageWorkflowElement.cs new file mode 100644 index 0000000..7598142 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/HybridPackageWorkflowElement.cs @@ -0,0 +1,116 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class HybridPackageWorkflowElement : WorkflowElementBase + { + // Data + private HybridPackageWorkflow _workflow; + + // UI + private MultiToggleSelectionElement _dependenciesElement; + + public HybridPackageWorkflowElement(HybridPackageWorkflow workflow) : base(workflow) + { + _workflow = workflow; + + Create(); + Deserialize(); + } + + private void Create() + { + CreatePathElement("Package path", "Select a local Package you would like to export and upload to the Store."); + CreateDependenciesElement(); + CreatePreviewGenerationElement(); + CreateValidationElement(new CurrentProjectValidationElement(_workflow)); + CreateUploadElement(_workflow, true); + } + + private void CreateDependenciesElement() + { + _dependenciesElement = new MultiToggleSelectionElement() + { + ElementLabel = "Dependencies", + ElementTooltip = "Select which local package dependencies should be included when exporting." + + "\n\nNote that only local or embedded dependencies defined in the package.json can be selected.", + NoSelectionLabel = "No packages match this criteria" + }; + + var setDependencies = new Action>((dict) => _workflow.SetDependencies(dict.Where(x => x.Value).Select(x => x.Key), true)); + _dependenciesElement.OnValuesChanged += setDependencies; + Add(_dependenciesElement); + _dependenciesElement.style.display = DisplayStyle.None; + } + + protected override void BrowsePath() + { + var absoluteExportPath = EditorUtility.OpenFilePanel("Select a package.json file", "Packages/", "json"); + if (string.IsNullOrEmpty(absoluteExportPath)) + return; + + if (!_workflow.IsPathValid(absoluteExportPath, out var error)) + { + EditorUtility.DisplayDialog("Invalid selection", error, "OK"); + return; + } + + HandlePathSelection(absoluteExportPath, true); + CheckForMissingMetas(); + } + + private void HandlePathSelection(string packageManifestPath, bool serialize) + { + if (string.IsNullOrEmpty(packageManifestPath)) + return; + + _workflow.SetPackage(packageManifestPath, serialize); + var packageFolderPath = _workflow.GetPackage().assetPath; + SetPathSelectionTextField(packageFolderPath + "/"); + + UpdateDependenciesElement(); + } + + private void CheckForMissingMetas() + { + var paths = new List() { _workflow.GetPackage().assetPath }; + paths.AddRange(_workflow.GetDependencies().Select(x => x.assetPath)); + CheckForMissingMetas(paths); + } + + private void UpdateDependenciesElement() + { + var availableDependencies = _workflow.GetAvailableDependencies(); + var selectedDependencies = availableDependencies.ToDictionary(x => x.name, y => _workflow.GetDependencies().Any(x => x.name == y.name)); + _dependenciesElement.Populate(selectedDependencies); + _dependenciesElement.style.display = availableDependencies.Count > 0 ? DisplayStyle.Flex : DisplayStyle.None; + } + + protected override void EnableInteraction() + { + base.EnableInteraction(); + _dependenciesElement.SetEnabled(true); + } + + protected override void DisableInteraction() + { + base.DisableInteraction(); + _dependenciesElement.SetEnabled(false); + } + + protected override void Deserialize() + { + var package = _workflow.GetPackage(); + if (package == null) + return; + + HandlePathSelection(AssetDatabase.GetAssetPath(package.GetManifestAsset()), false); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/HybridPackageWorkflowElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/HybridPackageWorkflowElement.cs.meta new file mode 100644 index 0000000..53ad66c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/HybridPackageWorkflowElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34cd1e5cbe87bb546937a521bd2bc69c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/LoadingSpinner.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/LoadingSpinner.cs new file mode 100644 index 0000000..b2ad70b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/LoadingSpinner.cs @@ -0,0 +1,52 @@ +using UnityEditor; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class LoadingSpinner : VisualElement + { + // Data + private int _spinIndex; + private double _spinTimer; + private double _spinThreshold = 0.1; + + // UI + private Image _spinnerImage; + + public LoadingSpinner() + { + AddToClassList("loading-spinner-box"); + + _spinnerImage = new Image { name = "SpinnerImage" }; + _spinnerImage.AddToClassList("loading-spinner-image"); + + Add(_spinnerImage); + } + + public void Show() + { + EditorApplication.update += SpinnerLoop; + style.display = DisplayStyle.Flex; + } + + public void Hide() + { + EditorApplication.update -= SpinnerLoop; + style.display = DisplayStyle.None; + } + + private void SpinnerLoop() + { + if (_spinTimer + _spinThreshold > EditorApplication.timeSinceStartup) + return; + + _spinTimer = EditorApplication.timeSinceStartup; + _spinnerImage.image = EditorGUIUtility.IconContent($"WaitSpin{_spinIndex:00}").image; + + _spinIndex += 1; + + if (_spinIndex > 11) + _spinIndex = 0; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/LoadingSpinner.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/LoadingSpinner.cs.meta new file mode 100644 index 0000000..78031dc --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/LoadingSpinner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7c7cdef91eb9a894091869ca10d9d178 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/MultiToggleSelectionElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/MultiToggleSelectionElement.cs new file mode 100644 index 0000000..175a095 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/MultiToggleSelectionElement.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class MultiToggleSelectionElement : VisualElement + { + // Data + private Dictionary _selections; + private readonly List _selectionFilters = new List { "All", "Selected", "Not Selected" }; + private string _activeFilter; + + public bool DisplayElementLabel + { + get => _multiToggleSelectionHelpRow.style.visibility == Visibility.Visible; + set { _multiToggleSelectionHelpRow.style.visibility = value ? Visibility.Visible : Visibility.Hidden; } + } + + public string ElementLabel { get => _multiToggleSelectionLabel.text; set { _multiToggleSelectionLabel.text = value; } } + public string ElementTooltip { get => _multiToggleSelectionTooltip.tooltip; set { _multiToggleSelectionTooltip.tooltip = value; } } + public string NoSelectionLabel { get => _noSelectionsLabel.text; set { _noSelectionsLabel.text = value; } } + + // UI + private VisualElement _multiToggleSelectionHelpRow; + private Label _multiToggleSelectionLabel; + private Image _multiToggleSelectionTooltip; + + private ScrollView _selectionTogglesBox; + private Label _noSelectionsLabel; + private ToolbarMenu _filteringDropdown; + + public event Action> OnValuesChanged; + + public MultiToggleSelectionElement() + { + _activeFilter = _selectionFilters[0]; + AddToClassList("package-content-option-box"); + Create(); + } + + private void Create() + { + _multiToggleSelectionHelpRow = new VisualElement(); + _multiToggleSelectionHelpRow.AddToClassList("package-content-option-label-help-row"); + + _multiToggleSelectionLabel = new Label(); + _multiToggleSelectionTooltip = new Image(); + + VisualElement fullSelectionBox = new VisualElement(); + fullSelectionBox.AddToClassList("multi-toggle-box"); + + _selectionTogglesBox = new ScrollView { name = "DependencyToggles" }; + _selectionTogglesBox.AddToClassList("multi-toggle-box-scrollview"); + + _noSelectionsLabel = new Label(); + _noSelectionsLabel.AddToClassList("multi-toggle-box-empty-label"); + + var scrollContainer = _selectionTogglesBox.Q("unity-content-viewport"); + scrollContainer.Add(_noSelectionsLabel); + + VisualElement filteringBox = new VisualElement(); + filteringBox.AddToClassList("multi-toggle-box-toolbar"); + + // Select - deselect buttons + VisualElement selectingBox = new VisualElement(); + selectingBox.AddToClassList("multi-toggle-box-toolbar-selecting-box"); + + Button selectAllButton = new Button(SelectAllToggles) + { + text = "Select All" + }; + + Button deSelectAllButton = new Button(UnselectAllToggles) + { + text = "Deselect All" + }; + + selectingBox.Add(selectAllButton); + selectingBox.Add(deSelectAllButton); + + // Filtering dropdown + VisualElement filteringDropdownBox = new VisualElement(); + filteringDropdownBox.AddToClassList("multi-toggle-box-toolbar-filtering-box"); + + _filteringDropdown = new ToolbarMenu { text = _selectionFilters[0] }; + + foreach (var filter in _selectionFilters) + _filteringDropdown.menu.AppendAction(filter, (_) => { FilterDependencies(filter); }); + + filteringDropdownBox.Add(_filteringDropdown); + + // Final adding + filteringBox.Add(filteringDropdownBox); + filteringBox.Add(selectingBox); + + fullSelectionBox.Add(_selectionTogglesBox); + fullSelectionBox.Add(filteringBox); + + _multiToggleSelectionHelpRow.Add(_multiToggleSelectionLabel); + _multiToggleSelectionHelpRow.Add(_multiToggleSelectionTooltip); + + Add(_multiToggleSelectionHelpRow); + Add(fullSelectionBox); + } + + public void Populate(Dictionary selections) + { + _selectionTogglesBox.Clear(); + _selections = selections; + + EventCallback, string> callback = OnToggle; + + foreach (var kvp in selections) + { + var toggle = new Toggle() { text = kvp.Key, value = kvp.Value }; + toggle.AddToClassList("multi-toggle-box-toggle"); + toggle.RegisterCallback(callback, toggle.text); + _selectionTogglesBox.Add(toggle); + } + + FilterDependencies(_activeFilter); + } + + private void FilterDependencies(string filter) + { + _activeFilter = filter; + + var allToggles = _selectionTogglesBox.Children().Cast().ToArray(); + var selectedIndex = _selectionFilters.FindIndex(x => x == filter); + + switch (selectedIndex) + { + case 0: + foreach (var toggle in allToggles) + toggle.style.display = DisplayStyle.Flex; + break; + case 1: + foreach (var toggle in allToggles) + toggle.style.display = toggle.value ? DisplayStyle.Flex : DisplayStyle.None; + break; + case 2: + foreach (var toggle in allToggles) + toggle.style.display = toggle.value ? DisplayStyle.None : DisplayStyle.Flex; + break; + } + + // Check if any toggles are displayed + var count = allToggles.Count(toggle => toggle.style.display == DisplayStyle.Flex); + _noSelectionsLabel.style.display = count > 0 ? DisplayStyle.None : DisplayStyle.Flex; + + _filteringDropdown.text = filter; + } + + private void OnToggle(ChangeEvent evt, string text) + { + FilterDependencies(_activeFilter); + _selections[text] = evt.newValue; + OnValuesChanged?.Invoke(_selections); + } + + private void OnAllToggles(bool value) + { + var allToggles = _selectionTogglesBox.Children().Cast(); + foreach (var toggle in allToggles) + toggle.SetValueWithoutNotify(value); + + foreach (var key in _selections.Keys.ToArray()) + _selections[key] = value; + + FilterDependencies(_activeFilter); + OnValuesChanged?.Invoke(_selections); + } + + private void SelectAllToggles() + { + OnAllToggles(true); + } + + private void UnselectAllToggles() + { + OnAllToggles(false); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/MultiToggleSelectionElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/MultiToggleSelectionElement.cs.meta new file mode 100644 index 0000000..8e33995 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/MultiToggleSelectionElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 19e30766043794345beb432973e0eb3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageContentElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageContentElement.cs new file mode 100644 index 0000000..eac8455 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageContentElement.cs @@ -0,0 +1,137 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Utility; +using System.Collections.Generic; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class PackageContentElement : VisualElement + { + // Data + private IPackageContent _content; + private List _workflowElements; + + // UI + private VisualElement _workflowSelectionBox; + private ToolbarMenu _toolbarMenu; + + public PackageContentElement(IPackageContent content) + { + _content = content; + content.OnActiveWorkflowChanged += ActiveWorkflowChanged; + + _workflowElements = new List(); + + Create(); + } + + private void Create() + { + AddToClassList("package-content-element"); + + CreateWorkflowSelection(); + CreateWorkflows(); + Deserialize(); + } + + private void CreateWorkflowSelection() + { + _workflowSelectionBox = new VisualElement(); + _workflowSelectionBox.AddToClassList("package-content-option-box"); + + VisualElement labelHelpRow = new VisualElement(); + labelHelpRow.AddToClassList("package-content-option-label-help-row"); + + Label workflowLabel = new Label { text = "Upload type" }; + Image workflowLabelTooltip = new Image + { + tooltip = "Select what content you are uploading to the Asset Store" + + "\n\n- From Assets Folder - content located within the project's 'Assets' folder or one of its subfolders" + + "\n\n- Pre-exported .unitypackage - content that has already been compressed into a .unitypackage file" +#if UNITY_ASTOOLS_EXPERIMENTAL + + "\n\n- Local UPM Package - content that is located within the project's 'Packages' folder. Only embedded and local packages are supported" +#endif + }; + + labelHelpRow.Add(workflowLabel); + labelHelpRow.Add(workflowLabelTooltip); + + _toolbarMenu = new ToolbarMenu(); + _toolbarMenu.AddToClassList("package-content-option-dropdown"); + + foreach (var workflow in _content.GetAvailableWorkflows()) + { + AppendToolbarActionForWorkflow(workflow); + } + + _workflowSelectionBox.Add(labelHelpRow); + _workflowSelectionBox.Add(_toolbarMenu); + + Add(_workflowSelectionBox); + } + + private void AppendToolbarActionForWorkflow(IWorkflow workflow) + { + _toolbarMenu.menu.AppendAction(workflow.DisplayName, _ => + { + _content.SetActiveWorkflow(workflow); + }); + } + + private void CreateWorkflows() + { + foreach (var workflow in _content.GetAvailableWorkflows()) + { + WorkflowElementBase element; + switch (workflow) + { + case AssetsWorkflow assetsWorkflow: + element = new AssetsWorkflowElement(assetsWorkflow); + break; + case UnityPackageWorkflow unityPackageWorkflow: + element = new UnityPackageWorkflowElement(unityPackageWorkflow); + break; +#if UNITY_ASTOOLS_EXPERIMENTAL + case HybridPackageWorkflow hybridPackageWorkflow: + element = new HybridPackageWorkflowElement(hybridPackageWorkflow); + break; +#endif + default: + ASDebug.LogWarning("Package Content Element received an undefined workflow"); + continue; + } + + element.OnInteractionAvailable += EnableInteraction; + element.OnInteractionUnavailable += DisableInteraction; + _workflowElements.Add(element); + Add(element); + } + } + + private void ActiveWorkflowChanged(IWorkflow workflow) + { + _toolbarMenu.text = workflow.DisplayName; + foreach (var workflowElement in _workflowElements) + { + bool show = workflowElement.Is(workflow); + workflowElement.style.display = show ? DisplayStyle.Flex : DisplayStyle.None; + } + } + + private void EnableInteraction() + { + _workflowSelectionBox.SetEnabled(true); + } + + private void DisableInteraction() + { + _workflowSelectionBox.SetEnabled(false); + } + + private void Deserialize() + { + ActiveWorkflowChanged(_content.GetActiveWorkflow()); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageContentElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageContentElement.cs.meta new file mode 100644 index 0000000..6a3eca3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageContentElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 09927e9c8fd6e074fa451add92b7ab6f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageElement.cs new file mode 100644 index 0000000..f934979 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageElement.cs @@ -0,0 +1,215 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Uploader.Services; +using System; +#if !UNITY_2021_1_OR_NEWER +using UnityEditor.UIElements; +#endif +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class PackageElement : VisualElement + { + // Data + private IPackage _package; + private bool _isSelected; + + private IPackageFactoryService _packageFactory; + + // UI + private Button _foldoutBox; + private Label _expanderLabel; + private Label _assetLabel; + private Label _lastDateSizeLabel; + private Image _assetImage; + + private ProgressBar _uploadProgressBar; + private VisualElement _uploadProgressBarBackground; + + private PackageContentElement _contentElement; + + public event Action OnSelected; + + public PackageElement(IPackage package, IPackageFactoryService packageFactory) + { + _package = package; + _package.OnUpdate += Refresh; + _package.OnIconUpdate += SetPackageThumbnail; + + _packageFactory = packageFactory; + + _isSelected = false; + + Create(); + } + + private void Create() + { + AddToClassList("package-full-box"); + + _foldoutBox = new Button { name = "Package" }; + _foldoutBox.AddToClassList("package-foldout-box"); + if (_package.IsDraft) + _foldoutBox.AddToClassList("package-foldout-box-draft"); + _foldoutBox.clickable.clicked += Toggle; + + // Expander, Icon and Asset Label + VisualElement foldoutBoxInfo = new VisualElement { name = "foldoutBoxInfo" }; + foldoutBoxInfo.AddToClassList("package-foldout-box-info"); + + VisualElement labelExpanderRow = new VisualElement { name = "labelExpanderRow" }; + labelExpanderRow.AddToClassList("package-expander-label-row"); + + _expanderLabel = new Label { name = "ExpanderLabel", text = "►" }; + _expanderLabel.AddToClassList("package-expander"); + _expanderLabel.style.display = _package.IsDraft ? DisplayStyle.Flex : DisplayStyle.None; + + _assetImage = new Image { name = "AssetImage" }; + _assetImage.AddToClassList("package-image"); + + VisualElement assetLabelInfoBox = new VisualElement { name = "assetLabelInfoBox" }; + assetLabelInfoBox.AddToClassList("package-label-info-box"); + + _assetLabel = new Label { name = "AssetLabel", text = _package.Name }; + _assetLabel.AddToClassList("package-label"); + + _lastDateSizeLabel = new Label { name = "AssetInfoLabel", text = FormatDateSize() }; + _lastDateSizeLabel.AddToClassList("package-info"); + + assetLabelInfoBox.Add(_assetLabel); + assetLabelInfoBox.Add(_lastDateSizeLabel); + + labelExpanderRow.Add(_expanderLabel); + labelExpanderRow.Add(_assetImage); + labelExpanderRow.Add(assetLabelInfoBox); + + var openInBrowserButton = new Button(OpenPackageInBrowser) + { + name = "OpenInBrowserButton", + tooltip = "View your package in the Publishing Portal." + }; + openInBrowserButton.AddToClassList("package-open-in-browser-button"); + + // Header Progress bar + _uploadProgressBar = new ProgressBar { name = "HeaderProgressBar" }; + _uploadProgressBar.AddToClassList("package-header-progress-bar"); + _uploadProgressBar.style.display = DisplayStyle.None; + _uploadProgressBarBackground = _uploadProgressBar.Q(className: "unity-progress-bar__progress"); + + // Connect it all + foldoutBoxInfo.Add(labelExpanderRow); + foldoutBoxInfo.Add(openInBrowserButton); + + _foldoutBox.Add(foldoutBoxInfo); + _foldoutBox.Add(_uploadProgressBar); + + Add(_foldoutBox); + } + + private void CreateFoldoutContent() + { + var content = _packageFactory.CreatePackageContent(_package); + if (content == null) + return; + + _contentElement = new PackageContentElement(content); + _contentElement.style.display = DisplayStyle.None; + Add(_contentElement); + + SubscribeToContentWorkflowUpdates(content); + } + + private void SubscribeToContentWorkflowUpdates(IPackageContent content) + { + foreach (var workflow in content.GetAvailableWorkflows()) + { + workflow.OnUploadStateChanged += UpdateProgressBar; + } + } + + private void UpdateProgressBar(UploadStatus? status, float? progress) + { + if (status != null) + { + _uploadProgressBarBackground.style.backgroundColor = PackageUploadElement.GetColorByStatus(status.Value); + } + + if (progress != null) + { + _uploadProgressBar.value = progress.Value; + } + } + + private void Toggle() + { + if (!_package.IsDraft) + return; + + if (!Contains(_contentElement)) + CreateFoldoutContent(); + + var shouldExpand = !_isSelected; + _expanderLabel.text = shouldExpand ? "▼" : "►"; + + if (shouldExpand) + _foldoutBox.AddToClassList("package-foldout-box-expanded"); + else + _foldoutBox.RemoveFromClassList("package-foldout-box-expanded"); + _contentElement.style.display = shouldExpand ? DisplayStyle.Flex : DisplayStyle.None; + + _isSelected = !_isSelected; + ToggleProgressBar(); + + if (_isSelected) + OnSelected?.Invoke(); + } + + private void ToggleProgressBar() + { + if (!_isSelected && _uploadProgressBar.value != 0) + _uploadProgressBar.style.display = DisplayStyle.Flex; + else + _uploadProgressBar.style.display = DisplayStyle.None; + } + + public bool Is(IPackage package) + { + return package == _package; + } + + public void Select() + { + if (!_isSelected) + Toggle(); + } + + public void Unselect() + { + if (_isSelected) + Toggle(); + } + + private void SetPackageThumbnail() + { + _assetImage.image = _package.Icon; + } + + private void Refresh() + { + _assetLabel.text = _package.Name; + _lastDateSizeLabel.text = FormatDateSize(); + } + + private string FormatDateSize() + { + return $"{_package.Category} | {_package.FormattedSize()} | {_package.FormattedModified()}"; + } + + private void OpenPackageInBrowser() + { + Application.OpenURL($"https://publisher.unity.com/packages/{_package.VersionId}/edit/upload"); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageElement.cs.meta new file mode 100644 index 0000000..e807599 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cef5f23043d318945b844bcac7a7a984 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageGroupElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageGroupElement.cs new file mode 100644 index 0000000..6c549c0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageGroupElement.cs @@ -0,0 +1,149 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Uploader.Services; +using System.Collections.Generic; +using System.Linq; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class PackageGroupElement : VisualElement + { + // Data + public string Name => _packageGroup.Name; + private IPackageGroup _packageGroup; + private List _packageElements; + private bool _isExpanded; + + private IPackageFactoryService _packageFactory; + + // UI + private Button _groupExpanderBox; + private VisualElement _groupContent; + + private Label _expanderLabel; + private Label _groupLabel; + + public PackageGroupElement(IPackageGroup packageGroup, IPackageFactoryService packageFactory) + { + _packageGroup = packageGroup; + _packageElements = new List(); + _packageGroup.OnPackagesSorted += RefreshPackages; + _packageGroup.OnPackagesFiltered += RefreshPackages; + + _packageFactory = packageFactory; + + Create(); + } + + private void Create() + { + CreatePackageGroup(); + CreatePackageGroupContent(); + AddPackagesToGroupContent(); + } + + protected void CreatePackageGroup() + { + _groupExpanderBox = new Button(OnPackageGroupClicked); + _groupExpanderBox.AddToClassList("package-group-expander-box"); + + _expanderLabel = new Label { name = "ExpanderLabel", text = "►" }; + _expanderLabel.AddToClassList("package-group-expander"); + + _groupLabel = new Label { text = $"{_packageGroup.Name} ({_packageGroup.Packages.Count})" }; + _groupLabel.AddToClassList("package-group-label"); + FormatGroupLabel(_packageGroup.Packages.Count); + + _groupExpanderBox.Add(_expanderLabel); + _groupExpanderBox.Add(_groupLabel); + + Add(_groupExpanderBox); + } + + private void CreatePackageGroupContent() + { + _groupContent = new VisualElement { name = "GroupContentBox" }; + _groupContent.AddToClassList("package-group-content-box"); + Toggle(false); + + var groupSeparator = new VisualElement { name = "GroupSeparator" }; + groupSeparator.AddToClassList("package-group-separator"); + + if (_packageGroup.Name.ToLower() != "draft") + { + _groupLabel.SetEnabled(false); + _groupContent.AddToClassList("unity-disabled"); + groupSeparator.style.display = DisplayStyle.Flex; + } + + Add(_groupContent); + Add(groupSeparator); + } + + private void AddPackagesToGroupContent() + { + foreach (var package in _packageGroup.Packages) + { + var packageElement = new PackageElement(package, _packageFactory); + packageElement.OnSelected += () => OnPackageSelected(packageElement); + _packageElements.Add(packageElement); + } + } + + private void FormatGroupLabel(int displayedPackageCount) + { + if (_packageGroup.Packages.Count == displayedPackageCount) + _groupLabel.text = $"{Name} ({displayedPackageCount})"; + else + _groupLabel.text = $"{Name} ({displayedPackageCount}/{_packageGroup.Packages.Count})"; + } + + private void RefreshPackages(List packages) + { + _groupContent.Clear(); + + foreach (var package in packages) + { + var correspondingElement = _packageElements.FirstOrDefault(x => x.Is(package)); + if (correspondingElement == null) + continue; + + _groupContent.Add(correspondingElement); + } + + FormatGroupLabel(packages.Count()); + } + + private void OnPackageGroupClicked() + { + Toggle(!_isExpanded); + } + + public void Toggle(bool expand) + { + if (expand) + { + _expanderLabel.text = "▼"; + _groupContent.style.display = DisplayStyle.Flex; + } + else + { + _expanderLabel.text = "►"; + _groupContent.style.display = DisplayStyle.None; + } + + _isExpanded = expand; + } + + private void OnPackageSelected(PackageElement packageElement) + { + foreach (var element in _packageElements) + { + if (element == packageElement) + continue; + + element.Unselect(); + } + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageGroupElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageGroupElement.cs.meta new file mode 100644 index 0000000..64cde03 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageGroupElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 41e322a1418ab824182eade111145dff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageListToolbar.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageListToolbar.cs new file mode 100644 index 0000000..0dfc3c2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageListToolbar.cs @@ -0,0 +1,58 @@ +using AssetStoreTools.Uploader.Data; +using System.Collections.Generic; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class PackageListToolbar : VisualElement + { + private List _packageGroups; + + public PackageListToolbar() + { + Create(); + } + + private void Create() + { + AddToClassList("package-list-toolbar"); + + // Search + var searchField = new ToolbarSearchField { name = "SearchField" }; + searchField.AddToClassList("package-search-field"); + + // Sorting menu button + var sortMenu = new ToolbarMenu() { text = "Sort: Name ↓" }; + sortMenu.menu.AppendAction("Sort: Name ↓", (_) => { sortMenu.text = "Sort: Name ↓"; Sort(PackageSorting.Name); }); + sortMenu.menu.AppendAction("Sort: Updated ↓", (_) => { sortMenu.text = "Sort: Updated ↓"; Sort(PackageSorting.Date); }); + sortMenu.menu.AppendAction("Sort: Category ↓", (_) => { sortMenu.text = "Sort: Category ↓"; Sort(PackageSorting.Category); }); + sortMenu.AddToClassList("package-sort-menu"); + + // Finalize the bar + Add(searchField); + Add(sortMenu); + + // Add Callbacks and click events + searchField.RegisterCallback>(SearchFilter); + } + + public void SetPackageGroups(List packageGroups) + { + _packageGroups = packageGroups; + } + + private void SearchFilter(ChangeEvent evt) + { + var searchString = evt.newValue.ToLower(); + foreach (var packageGroup in _packageGroups) + packageGroup.Filter(searchString); + } + + public void Sort(PackageSorting sortingType) + { + foreach (var packageGroup in _packageGroups) + packageGroup.Sort(sortingType); + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageListToolbar.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageListToolbar.cs.meta new file mode 100644 index 0000000..d877de7 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageListToolbar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d2659328222e0e4cb36cd194e023f4b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageUploadElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageUploadElement.cs new file mode 100644 index 0000000..06fe8ea --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageUploadElement.cs @@ -0,0 +1,321 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Exporter; +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Utility; +using System; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using UnityEditor; +#if !UNITY_2021_1_OR_NEWER +using UnityEditor.UIElements; +#endif +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class PackageUploadElement : VisualElement + { + // Data + private IWorkflow _workflow; + private bool _enableExporting; + + // UI + private VisualElement _exportAndUploadContainer; + + private Button _cancelUploadButton; + private VisualElement _uploadProgressContainer; + private ProgressBar _uploadProgressBar; + private VisualElement _uploadProgressBarBackground; + + public event Action OnInteractionAvailable; + public event Action OnInteractionUnavailable; + + public PackageUploadElement(IWorkflow workflow, bool exposeExportButton) + { + _workflow = workflow; + _enableExporting = exposeExportButton; + + Create(); + } + + private void Create() + { + AddToClassList("uploading-box"); + + CreateButtonContainer(); + CreateProgressContainer(); + } + + private void CreateButtonContainer() + { + _exportAndUploadContainer = new VisualElement(); + _exportAndUploadContainer.AddToClassList("uploading-export-and-upload-container"); + + CreateExportButton(); + CreateUploadButton(); + Add(_exportAndUploadContainer); + } + + private void CreateExportButton() + { + if (!_enableExporting) + return; + + var _exportAndUploadButton = new Button(async () => await Export(true)) { name = "ExportButton", text = "Export" }; + _exportAndUploadButton.AddToClassList("uploading-export-button"); + + _exportAndUploadContainer.Add(_exportAndUploadButton); + } + + private void CreateUploadButton() + { + var _uploadButton = new Button(Upload) { name = "UploadButton" }; + _uploadButton.text = _enableExporting ? "Export and Upload" : "Upload"; + _uploadButton.AddToClassList("uploading-upload-button"); + + _exportAndUploadContainer.Add(_uploadButton); + } + + private void CreateProgressContainer() + { + _uploadProgressContainer = new VisualElement(); + _uploadProgressContainer.AddToClassList("uploading-progress-container"); + _uploadProgressContainer.style.display = DisplayStyle.None; + + _uploadProgressBar = new ProgressBar { name = "UploadProgressBar" }; + _uploadProgressBar.AddToClassList("uploading-progress-bar"); + _uploadProgressBarBackground = _uploadProgressBar.Q(className: "unity-progress-bar__progress"); + + _cancelUploadButton = new Button() { name = "CancelButton", text = "Cancel" }; + _cancelUploadButton.AddToClassList("uploading-cancel-button"); + + _uploadProgressContainer.Add(_uploadProgressBar); + _uploadProgressContainer.Add(_cancelUploadButton); + + Add(_uploadProgressContainer); + } + + private async Task Export(bool interactive) + { + try + { + DisableInteraction(); + + if (!_workflow.IsPathSet) + { + EditorUtility.DisplayDialog("Exporting failed", "No path was selected. Please " + + "select a path and try again.", "OK"); + return new PackageExporterResult() { Success = false, Exception = new Exception("No path was selected.") }; + } + + var rootProjectPath = Constants.RootProjectPath; + var packageNameStripped = Regex.Replace(_workflow.PackageName, "[^a-zA-Z0-9]", ""); + var outputName = $"{packageNameStripped}-{DateTime.Now:yyyy-dd-M--HH-mm-ss}"; + + string outputPath; + if (interactive) + { + outputPath = EditorUtility.SaveFilePanel("Export Package", rootProjectPath, + outputName, _workflow.PackageExtension.Remove(0, 1)); // Ignoring the '.' character since SaveFilePanel already appends it + + if (string.IsNullOrEmpty(outputPath)) + return new PackageExporterResult() { Success = false, Exception = null }; + } + else + { + outputPath = $"Temp/{outputName}{_workflow.PackageExtension}"; + } + + var exportResult = await _workflow.ExportPackage(outputPath); + if (!exportResult.Success) + { + Debug.LogError($"Package exporting failed: {exportResult.Exception}"); + EditorUtility.DisplayDialog("Exporting failed", exportResult.Exception.Message, "OK"); + } + else if (interactive) + Debug.Log($"Package exported to '{Path.GetFullPath(exportResult.ExportedPath).Replace("\\", "/")}'"); + + return exportResult; + } + finally + { + if (interactive) + EnableInteraction(); + } + } + + private async void Upload() + { + DisableInteraction(); + + if (await ValidateUnityVersionsBeforeUpload() == false) + { + EnableInteraction(); + return; + } + + var exportResult = await Export(false); + if (!exportResult.Success) + { + EnableInteraction(); + return; + } + + if (!_workflow.IsPathSet) + { + EditorUtility.DisplayDialog("Uploading failed", "No path was selected. Please " + + "select a path and try again.", "OK"); + EnableInteraction(); + return; + } + + _exportAndUploadContainer.style.display = DisplayStyle.None; + _uploadProgressContainer.style.display = DisplayStyle.Flex; + + _cancelUploadButton.clicked += Cancel; + _workflow.OnUploadStateChanged += UpdateProgressBar; + var response = await _workflow.UploadPackage(exportResult.ExportedPath); + _workflow.OnUploadStateChanged -= UpdateProgressBar; + + await OnUploadingStopped(response); + } + + private async Task ValidateUnityVersionsBeforeUpload() + { + var validationEnabled = ASToolsPreferences.Instance.UploadVersionCheck; + if (!validationEnabled) + return true; + + var requiredVersionUploaded = await _workflow.ValidatePackageUploadedVersions(); + if (requiredVersionUploaded) + return true; + + var result = EditorUtility.DisplayDialogComplex("Asset Store Tools", $"You may upload this package, but you will need to add a package using Unity version {Constants.Uploader.MinRequiredUnitySupportVersion} " + + "or higher to be able to submit a new asset", "Upload", "Cancel", "Upload and do not display this again"); + + switch (result) + { + case 1: + return false; + case 2: + ASToolsPreferences.Instance.UploadVersionCheck = false; + ASToolsPreferences.Instance.Save(); + break; + } + + return true; + } + + private void UpdateProgressBar(UploadStatus? status, float? progress) + { + if (status != null) + { + _uploadProgressBarBackground.style.backgroundColor = GetColorByStatus(status.Value); + } + + if (progress != null) + { + _uploadProgressBar.value = progress.Value; + _uploadProgressBar.title = $"{progress.Value:0.#}%"; + + if (progress == 100f && _cancelUploadButton.enabledInHierarchy) + _cancelUploadButton.SetEnabled(false); + } + } + + private void Cancel() + { + _cancelUploadButton.SetEnabled(false); + _workflow.AbortUpload(); + } + + private async Task OnUploadingStopped(PackageUploadResponse response) + { + if (!response.Success && !response.Cancelled) + { + Debug.LogException(response.Exception); + } + + if (response.Success) + { + await _workflow.RefreshPackage(); + } + + if (response.Status == UploadStatus.ResponseTimeout) + { + Debug.LogWarning($"All bytes for the package '{_workflow.PackageName}' have been uploaded, but a response " + + $"from the server was not received. This can happen because of Firewall restrictions. " + + $"Please make sure that a new version of your package has reached the Publishing Portal."); + } + + _uploadProgressBar.title = GetProgressBarTitleByStatus(response.Status); + + _cancelUploadButton.clickable = null; + _cancelUploadButton.clicked += Reset; + _cancelUploadButton.text = "Done"; + _cancelUploadButton.SetEnabled(true); + } + + private void Reset() + { + _cancelUploadButton.clickable = null; + _cancelUploadButton.text = "Cancel"; + + _workflow.ResetUploadStatus(); + UpdateProgressBar(UploadStatus.Default, 0f); + + _uploadProgressContainer.style.display = DisplayStyle.None; + _exportAndUploadContainer.style.display = DisplayStyle.Flex; + EnableInteraction(); + } + + public static Color GetColorByStatus(UploadStatus status) + { + switch (status) + { + default: + case UploadStatus.Default: + return new Color(0.13f, 0.59f, 0.95f); + case UploadStatus.Success: + case UploadStatus.ResponseTimeout: + 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); + } + } + + private string GetProgressBarTitleByStatus(UploadStatus status) + { + var progressBarTitle = "Upload: "; + switch (status) + { + case UploadStatus.ResponseTimeout: + progressBarTitle += UploadStatus.Success; + break; + default: + progressBarTitle += status; + break; + } + + return progressBarTitle; + } + + private void EnableInteraction() + { + _exportAndUploadContainer.SetEnabled(true); + OnInteractionAvailable?.Invoke(); + } + + private void DisableInteraction() + { + _exportAndUploadContainer.SetEnabled(false); + OnInteractionUnavailable?.Invoke(); + SetEnabled(true); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageUploadElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageUploadElement.cs.meta new file mode 100644 index 0000000..b3d5d3d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PackageUploadElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 242524e968bd4484eaeb154d8013f427 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PathSelectionElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PathSelectionElement.cs new file mode 100644 index 0000000..d8d79bf --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PathSelectionElement.cs @@ -0,0 +1,63 @@ +using System; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class PathSelectionElement : VisualElement + { + // Data + private string _labelText; + private string _labelTooltip; + + public event Action OnBrowse; + + // UI + private TextField _pathSelectionTextField; + + public PathSelectionElement(string labelText, string labelTooltip) + { + AddToClassList("package-content-option-box"); + + _labelText = labelText; + _labelTooltip = labelTooltip; + + Create(); + } + + private void Create() + { + VisualElement labelHelpRow = new VisualElement(); + labelHelpRow.AddToClassList("package-content-option-label-help-row"); + + Label folderPathLabel = new Label { text = _labelText }; + Image folderPathLabelTooltip = new Image + { + tooltip = _labelTooltip + }; + + labelHelpRow.Add(folderPathLabel); + labelHelpRow.Add(folderPathLabelTooltip); + + _pathSelectionTextField = new TextField(); + _pathSelectionTextField.AddToClassList("package-content-option-textfield"); + _pathSelectionTextField.isReadOnly = true; + + Button browsePathButton = new Button(Browse) { name = "BrowsePathButton", text = "Browse" }; + browsePathButton.AddToClassList("package-content-option-button"); + + Add(labelHelpRow); + Add(_pathSelectionTextField); + Add(browsePathButton); + } + + private void Browse() + { + OnBrowse?.Invoke(); + } + + public void SetPath(string path) + { + _pathSelectionTextField.value = path; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PathSelectionElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PathSelectionElement.cs.meta new file mode 100644 index 0000000..2ba9f4a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PathSelectionElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 753d4442293e5cc4b9efcab089da4d59 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PreviewGenerationElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PreviewGenerationElement.cs new file mode 100644 index 0000000..8533207 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PreviewGenerationElement.cs @@ -0,0 +1,109 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Uploader.Data; +using System.Linq; +using UnityEditor; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class PreviewGenerationElement : VisualElement + { + // Data + private IWorkflow _workflow; + + // UI + private VisualElement _toggleRow; + private Toggle _previewToggle; + + private VisualElement _buttonRow; + private VisualElement _viewButton; + + public PreviewGenerationElement(IWorkflow workflow) + { + _workflow = workflow; + + Create(); + } + + private void Create() + { + CreateInfoRow(); + CreateViewButton(); + } + + private void CreateInfoRow() + { + _toggleRow = new VisualElement(); + _toggleRow.AddToClassList("package-content-option-box"); + + VisualElement toggleLabelHelpRow = new VisualElement(); + toggleLabelHelpRow.AddToClassList("package-content-option-label-help-row"); + + Label toggleLabel = new Label { text = "Asset Previews" }; + Image toggleLabelTooltip = new Image + { + tooltip = "Select how the previews for your assets will be generated.\n\n" + + "Unity generates asset preview images natively up to a size of 128x128. " + + "You can try generating previews which are of higher resolution, up to 300x300.\n\n" + + "Note: these asset preview images will only be displayed in the 'Package Content' section of the " + + "Asset Store listing page once the package is published, and in the package importer window that appears during the package import process.\n" + + "They will not replace the images used for the assets in the Project window after the package gets imported." + }; + + _previewToggle = new Toggle { name = "PreviewToggle", text = "Generate Hi-Res (experimental)" }; + _previewToggle.AddToClassList("package-content-option-toggle"); + _previewToggle.RegisterValueChangedCallback((_) => DependencyToggleValueChange()); + + toggleLabelHelpRow.Add(toggleLabel); + toggleLabelHelpRow.Add(toggleLabelTooltip); + + _toggleRow.Add(toggleLabelHelpRow); + _toggleRow.Add(_previewToggle); + + Add(_toggleRow); + } + + private void CreateViewButton() + { + _buttonRow = new VisualElement(); + _buttonRow.AddToClassList("package-content-option-box"); + _buttonRow.style.display = DisplayStyle.None; + + var spaceFiller = new VisualElement(); + spaceFiller.AddToClassList("package-content-option-label-help-row"); + + _viewButton = new Button(ViewClicked) { text = "Inspect Previews" }; + + _buttonRow.Add(spaceFiller); + _buttonRow.Add(_viewButton); + + Add(_buttonRow); + } + + private void DependencyToggleValueChange() + { + _workflow.GenerateHighQualityPreviews = _previewToggle.value; + _buttonRow.style.display = _previewToggle.value ? DisplayStyle.Flex : DisplayStyle.None; + } + + private void ViewClicked() + { + PreviewGenerationSettings settings; + if (_workflow.GenerateHighQualityPreviews) + { + settings = new CustomPreviewGenerationSettings() { InputPaths = _workflow.GetAllPaths().ToArray() }; + } + else + { + settings = new NativePreviewGenerationSettings() { InputPaths = _workflow.GetAllPaths().ToArray() }; + } + + AssetStoreTools.ShowAssetStoreToolsPreviewGenerator(settings); + } + + private void DisplayProgress(float value) + { + EditorUtility.DisplayProgressBar("Generating", "Generating previews...", value); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PreviewGenerationElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PreviewGenerationElement.cs.meta new file mode 100644 index 0000000..251024b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/PreviewGenerationElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 54c7971e2ad639644936d3552b4f4f49 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/UnityPackageWorkflowElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/UnityPackageWorkflowElement.cs new file mode 100644 index 0000000..2d61e6d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/UnityPackageWorkflowElement.cs @@ -0,0 +1,58 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Utility; +using UnityEditor; + +namespace AssetStoreTools.Uploader.UI.Elements +{ + internal class UnityPackageWorkflowElement : WorkflowElementBase + { + // Data + private UnityPackageWorkflow _workflow; + + public UnityPackageWorkflowElement(UnityPackageWorkflow workflow) : base(workflow) + { + _workflow = workflow; + Create(); + } + + private void Create() + { + CreatePathElement("Package path", "Select the .unitypackage file you would like to upload."); + CreateValidationElement(new ExternalProjectValidationElement(_workflow)); + CreateUploadElement(_workflow, false); + Deserialize(); + } + + protected override void BrowsePath() + { + // Path retrieval + var absolutePackagePath = EditorUtility.OpenFilePanel("Select a .unitypackage file", Constants.RootProjectPath, "unitypackage"); + + if (string.IsNullOrEmpty(absolutePackagePath)) + return; + + var relativeExportPath = FileUtility.AbsolutePathToRelativePath(absolutePackagePath, ASToolsPreferences.Instance.EnableSymlinkSupport); + if (!_workflow.IsPathValid(relativeExportPath, out var error)) + { + EditorUtility.DisplayDialog("Invalid selection", error, "OK"); + return; + } + + HandleUnityPackageUploadPathSelection(relativeExportPath, true); + } + + private void HandleUnityPackageUploadPathSelection(string selectedPackagePath, bool serialize) + { + if (string.IsNullOrEmpty(selectedPackagePath)) + return; + + _workflow.SetPackagePath(selectedPackagePath, serialize); + SetPathSelectionTextField(selectedPackagePath); + } + + protected override void Deserialize() + { + HandleUnityPackageUploadPathSelection(_workflow.GetPackagePath(), false); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/UnityPackageWorkflowElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/UnityPackageWorkflowElement.cs.meta new file mode 100644 index 0000000..cc30abb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Elements/UnityPackageWorkflowElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a0c8a79b7dba9e458ddc54eec30ea66 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views.meta new file mode 100644 index 0000000..e5882f0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7511d6ab930f33c469562bc3c1c2aab2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/LoginView.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/LoginView.cs new file mode 100644 index 0000000..3039572 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/LoginView.cs @@ -0,0 +1,284 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Uploader.Services.Api; +using AssetStoreTools.Utility; +using System; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.UI.Views +{ + internal class LoginView : VisualElement + { + // Data + private IAuthenticationService _authenticationService; + private double _cloudLoginRefreshTime = 1d; + private double _lastRefreshTime; + + // UI + private Button _cloudLoginButton; + private Label _cloudLoginLabel; + + private Box _errorBox; + private Label _errorLabel; + + private TextField _emailField; + private TextField _passwordField; + private Button _credentialsLoginButton; + + public event Action OnAuthenticated; + + public LoginView(IAuthenticationService authenticationService) + { + _authenticationService = authenticationService; + Create(); + } + + public void Create() + { + styleSheets.Add(StyleSelector.UploaderWindow.LoginViewStyle); + styleSheets.Add(StyleSelector.UploaderWindow.LoginViewTheme); + + CreateAssetStoreLogo(); + CreateCloudLogin(); + CreateErrorBox(); + CreateCredentialsLogin(); + } + + private void CreateAssetStoreLogo() + { + // Asset Store logo + Image assetStoreLogo = new Image { name = "AssetStoreLogo" }; + assetStoreLogo.AddToClassList("asset-store-logo"); + + Add(assetStoreLogo); + } + + private void CreateCloudLogin() + { + VisualElement cloudLogin = new VisualElement { name = "CloudLogin" }; + + _cloudLoginButton = new Button(LoginWithCloudToken) { name = "LoginButtonCloud" }; + _cloudLoginButton.AddToClassList("cloud-button-login"); + _cloudLoginButton.SetEnabled(false); + + _cloudLoginLabel = new Label { text = "Cloud login unavailable" }; + _cloudLoginLabel.AddToClassList("cloud-button-login-label"); + + Label orLabel = new Label { text = "or" }; + orLabel.AddToClassList("cloud-label-or"); + + _cloudLoginButton.Add(_cloudLoginLabel); + + cloudLogin.Add(_cloudLoginButton); + cloudLogin.Add(orLabel); + + UpdateCloudLoginButton(); + EditorApplication.update += UpdateCloudLoginButton; + Add(cloudLogin); + } + + private void CreateErrorBox() + { + _errorBox = new Box() { name = "LoginErrorBox" }; + _errorBox.AddToClassList("error-container"); + _errorBox.style.display = DisplayStyle.None; + + var errorImage = new Image(); + _errorBox.Add(errorImage); + + _errorLabel = new Label(); + _errorBox.Add(_errorLabel); + + Add(_errorBox); + } + + public void DisplayError(string message) + { + if (string.IsNullOrEmpty(message)) + return; + + _errorLabel.text = message; + Debug.LogError(message); + + _errorBox.style.display = DisplayStyle.Flex; + } + + private void ClearError() + { + _errorLabel.text = string.Empty; + _errorBox.style.display = DisplayStyle.None; + } + + private void CreateCredentialsLogin() + { + // Manual login + VisualElement manualLoginBox = new VisualElement { name = "ManualLoginBox" }; + manualLoginBox.AddToClassList("credentials-container"); + + // Email input box + VisualElement inputBoxEmail = new VisualElement(); + inputBoxEmail.AddToClassList("credentials-input-container"); + + Label emailTitle = new Label { text = "Email" }; + _emailField = new TextField(); + + inputBoxEmail.Add(emailTitle); + inputBoxEmail.Add(_emailField); + + manualLoginBox.Add(inputBoxEmail); + + // Password input box + VisualElement inputBoxPassword = new VisualElement(); + inputBoxPassword.AddToClassList("credentials-input-container"); + + Label passwordTitle = new Label { text = "Password" }; + _passwordField = new TextField { isPasswordField = true }; + + inputBoxPassword.Add(passwordTitle); + inputBoxPassword.Add(_passwordField); + + manualLoginBox.Add(inputBoxPassword); + + // Login button + _credentialsLoginButton = new Button(LoginWithCredentials) { name = "LoginButtonCredentials" }; + _credentialsLoginButton.AddToClassList("credentials-button-login"); + + Label loginDescriptionCredentials = new Label { text = "Login" }; + loginDescriptionCredentials.AddToClassList("credentials-button-login-label"); + + _credentialsLoginButton.Add(loginDescriptionCredentials); + + manualLoginBox.Add(_credentialsLoginButton); + + Add(manualLoginBox); + + // Credentials login helpers + VisualElement helperBox = new VisualElement { name = "HelperBox" }; + helperBox.AddToClassList("help-section-container"); + + Button createAccountButton = new Button { name = "CreateAccountButton", text = "Create Publisher ID" }; + Button forgotPasswordButton = new Button { name = "ForgotPasswordButton", text = "Reset Password" }; + + createAccountButton.AddToClassList("help-section-hyperlink-button"); + forgotPasswordButton.AddToClassList("help-section-hyperlink-button"); + + createAccountButton.clicked += () => Application.OpenURL(Constants.Uploader.AccountRegistrationUrl); + forgotPasswordButton.clicked += () => Application.OpenURL(Constants.Uploader.AccountForgottenPasswordUrl); + + helperBox.Add(createAccountButton); + helperBox.Add(forgotPasswordButton); + + Add(helperBox); + } + + public async void LoginWithSessionToken() + { + ASDebug.Log("Authenticating with session token..."); + ClearError(); + SetEnabled(false); + + var result = await _authenticationService.AuthenticateWithSessionToken(); + if (!result.Success) + { + // Session authentication fail does not display errors in the UI + ASDebug.Log("No existing session was found"); + SetEnabled(true); + return; + } + + OnLoginSuccess(result.User); + } + + private async void LoginWithCloudToken() + { + ASDebug.Log("Authenticating with cloud token..."); + ClearError(); + SetEnabled(false); + + var result = await _authenticationService.AuthenticateWithCloudToken(); + if (!result.Success) + { + OnLoginFail(result.Exception.Message); + return; + } + + OnLoginSuccess(result.User); + } + + private async void LoginWithCredentials() + { + ASDebug.Log("Authenticating with credentials..."); + ClearError(); + var isValid = IsLoginDataValid(_emailField.text, _passwordField.value); + SetEnabled(!isValid); + + if (!isValid) + return; + + var result = await _authenticationService.AuthenticateWithCredentials(_emailField.text, _passwordField.text); + if (result.Success) + OnLoginSuccess(result.User); + else + OnLoginFail(result.Exception.Message); + } + + private bool IsLoginDataValid(string email, string password) + { + if (string.IsNullOrEmpty(email)) + { + DisplayError("Email field cannot be empty."); + return false; + } + + if (string.IsNullOrEmpty(password)) + { + DisplayError("Password field cannot be empty."); + return false; + } + + return true; + } + + private void UpdateCloudLoginButton() + { + if (_cloudLoginLabel == null) + return; + + if (_lastRefreshTime + _cloudLoginRefreshTime > EditorApplication.timeSinceStartup) + return; + + _lastRefreshTime = EditorApplication.timeSinceStartup; + + // Cloud login + if (_authenticationService.CloudAuthenticationAvailable(out var username, out var _)) + { + _cloudLoginLabel.text = $"Login as {username}"; + _cloudLoginButton.SetEnabled(true); + } + else + { + _cloudLoginLabel.text = "Cloud login unavailable"; + _cloudLoginButton.SetEnabled(false); + } + } + + private void OnLoginSuccess(User user) + { + ASDebug.Log($"Successfully authenticated as {user.Username}\n{user}"); + + _emailField.value = string.Empty; + _passwordField.value = string.Empty; + + OnAuthenticated?.Invoke(user); + SetEnabled(true); + } + + private void OnLoginFail(string message) + { + ASDebug.LogError($"Authentication failed: {message}"); + DisplayError(message); + SetEnabled(true); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/LoginView.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/LoginView.cs.meta new file mode 100644 index 0000000..1d2ed9e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/LoginView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e20b8602a9bd8ca48a5689b3f32cdd90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/PackageListView.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/PackageListView.cs new file mode 100644 index 0000000..a980d66 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/PackageListView.cs @@ -0,0 +1,249 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Uploader.Services; +using AssetStoreTools.Uploader.Services.Api; +using AssetStoreTools.Uploader.UI.Elements; +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine.SceneManagement; +using UnityEngine.UIElements; +using PackageModel = AssetStoreTools.Api.Models.Package; + +namespace AssetStoreTools.Uploader.UI.Views +{ + internal class PackageListView : VisualElement + { + // Data + private List _packages; + private readonly string[] _priorityGroupNames = { "draft", "published" }; + + private IPackageDownloadingService _packageDownloadingService; + private IPackageFactoryService _packageFactory; + + // UI + private LoadingSpinner _loadingSpinner; + private ScrollView _packageScrollView; + private PackageListToolbar _packageListToolbar; + + public event Action OnInitializeError; + + public PackageListView(IPackageDownloadingService packageDownloadingService, IPackageFactoryService elementFactory) + { + _packages = new List(); + _packageDownloadingService = packageDownloadingService; + _packageFactory = elementFactory; + + Create(); + EditorSceneManager.activeSceneChangedInEditMode += OnSceneChange; + } + + private void Create() + { + styleSheets.Add(StyleSelector.UploaderWindow.PackageListViewStyle); + styleSheets.Add(StyleSelector.UploaderWindow.PackageListViewTheme); + + AddToClassList("package-list-view"); + + CreateFilteringTools(); + CreateLoadingSpinner(); + CreateScrollView(); + + ShowPackagesView(); + } + + private void CreateScrollView() + { + _packageScrollView = new ScrollView(); + Add(_packageScrollView); + } + + private void CreateFilteringTools() + { + _packageListToolbar = new PackageListToolbar(); + Add(_packageListToolbar); + } + + private void CreateLoadingSpinner() + { + _loadingSpinner = new LoadingSpinner(); + Add(_loadingSpinner); + } + + private void InsertReadOnlyInfoBox(string infoText) + { + var groupHeader = new Box { name = "GroupReadOnlyInfoBox" }; + groupHeader.AddToClassList("package-group-info-box"); + + var infoImage = new Image(); + groupHeader.Add(infoImage); + + var infoLabel = new Label { text = infoText }; + groupHeader.Add(infoLabel); + + _packageScrollView.Add(groupHeader); + } + + public async Task LoadPackages(bool useCachedData) + { + _packages.Clear(); + _packageScrollView.Clear(); + _packageListToolbar.SetEnabled(false); + + if (!useCachedData) + { + _packageDownloadingService.ClearPackageData(); + } + + _loadingSpinner.Show(); + await Task.Delay(100); + + try + { + var response = await _packageDownloadingService.GetPackageData(); + + if (response.Cancelled) + { + ASDebug.Log("Package retrieval was cancelled"); + return; + } + + if (!response.Success) + { + ASDebug.LogError(response.Exception); + OnInitializeError?.Invoke(response.Exception); + return; + } + + var packageModels = response.Packages; + ASDebug.Log($"Found {packageModels.Count} packages"); + + if (packageModels.Count == 0) + { + InsertReadOnlyInfoBox("You do not have any packages yet. Please visit the Publishing Portal if you " + + "would like to create one."); + return; + } + + // Create package groups + _packages = CreatePackages(packageModels); + var packageGroups = CreatePackageGroups(_packages); + var packageGroupElements = CreatePackageGroupElements(packageGroups); + PopulatePackageList(packageGroupElements); + + // Setup filtering and thumbnails + SetupFilteringToolbar(packageGroups); + DownloadAndSetThumbnails(); + } + finally + { + _loadingSpinner.Hide(); + } + } + + private List CreatePackages(List packageModels) + { + return _packages = packageModels.Select(x => _packageFactory.CreatePackage(x)).ToList(); + } + + private List CreatePackageGroups(List packages) + { + var packageGroups = new List(); + var packagesByStatus = packages.GroupBy(x => x.Status).ToDictionary(x => x.Key, x => x.ToList()); + + foreach (var kvp in packagesByStatus) + { + var groupName = char.ToUpper(kvp.Key[0]) + kvp.Key.Substring(1); + var groupPackages = kvp.Value; + var packageGroup = _packageFactory.CreatePackageGroup(groupName, groupPackages); + packageGroups.Add(packageGroup); + } + + return packageGroups; + } + + private List CreatePackageGroupElements(List packageGroups) + { + var elements = new List(); + foreach (var packageGroup in packageGroups) + elements.Add(new PackageGroupElement(packageGroup, _packageFactory)); + + return elements; + } + + private void PopulatePackageList(List packageGroups) + { + // Draft group + var draftGroup = packageGroups.FirstOrDefault(x => x.Name.Equals("draft", StringComparison.OrdinalIgnoreCase)); + if (draftGroup != null) + { + draftGroup.Toggle(true); + _packageScrollView.Add(draftGroup); + } + + // Infobox will only be shown if: + // 1) There is more than 1 group OR + // 2) There is only 1 group, but it is not draft + var showInfoBox = packageGroups.Count > 1 + || packageGroups.Count == 1 && !packageGroups[0].Name.Equals("draft", StringComparison.OrdinalIgnoreCase); + + if (showInfoBox) + InsertReadOnlyInfoBox("Only packages with a 'Draft' status can be selected for uploading Assets"); + + // Priority groups + foreach (var priorityName in _priorityGroupNames) + { + var priorityGroup = packageGroups.FirstOrDefault(x => x.Name.Equals(priorityName, StringComparison.OrdinalIgnoreCase)); + if (priorityGroup == null || _packageScrollView.Contains(priorityGroup)) + continue; + + _packageScrollView.Add(priorityGroup); + } + + // The rest + foreach (var group in packageGroups) + { + if (!_packageScrollView.Contains(group)) + _packageScrollView.Add(group); + } + } + + private void SetupFilteringToolbar(List packageGroups) + { + _packageListToolbar.SetPackageGroups(packageGroups); + _packageListToolbar.Sort(PackageSorting.Name); + _packageListToolbar.SetEnabled(true); + } + + private void DownloadAndSetThumbnails() + { + foreach (var package in _packages) + { + DownloadAndSetThumbnail(package); + } + } + + private async void DownloadAndSetThumbnail(IPackage package) + { + var response = await _packageDownloadingService.GetPackageThumbnail(package); + if (!response.Success) + return; + + package.UpdateIcon(response.Thumbnail); + } + + private void ShowPackagesView() + { + _packageScrollView.style.display = DisplayStyle.Flex; + _packageListToolbar.style.display = DisplayStyle.Flex; + } + + private void OnSceneChange(Scene _, Scene __) + { + DownloadAndSetThumbnails(); + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/PackageListView.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/PackageListView.cs.meta new file mode 100644 index 0000000..ccae9d2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/PackageListView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3c499c2d0a5e8fd4b9984184c59893e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles.meta new file mode 100644 index 0000000..6040ca4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f9398c14296d30f479b9de5f3ec3b827 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView.meta new file mode 100644 index 0000000..03dac6e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7564d91cabdce1344ba4a0fca25e13d5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/Style.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/Style.uss new file mode 100644 index 0000000..d97cb7a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/Style.uss @@ -0,0 +1,134 @@ +/* Logo */ + +.asset-store-logo { + width: 90%; + height: 32px; + + align-self: center; + + margin: 40px 0; + + -unity-background-scale-mode: scale-to-fit; +} + +/* Cloud Login */ + +.cloud-button-login { + align-self: center; + + width: 75%; +} + +.cloud-button-login-label { + -unity-text-align: middle-center; + white-space: normal; + + min-height: 24px; + padding-right: 4px; +} + +.cloud-label-or { + align-self: center; + + -unity-text-align: middle-center; + + margin: 10px 0; +} + +/* Error Section */ + +.error-container { + flex-direction: row; + flex-shrink: 0; + + align-self: center; + + min-width: 300px; + max-width: 300px; + + margin: 5px 0 5px 1px; +} + +.error-container > Image { + flex-direction: row; + + width: 32px; + height: 32px; + + margin: 5px 10px; +} + +.error-container > Label { + flex-grow: 1; + flex-shrink: 1; + + -unity-text-align: middle-left; + white-space: normal; + + margin-right: 5px; + padding: 2px 0; +} + +/* Credentials Login */ + +.credentials-container { + align-self: center; + + width: 75%; + + padding: 15px; +} + +.credentials-input-container { + align-self: center; + + width: 100%; + + margin: 5px 0; +} + +.credentials-input-container > Label { + -unity-text-align: upper-left; + + margin: 2px 0; +} + +.credentials-input-container > TextField { + height: 20px; + margin: 0 1px; +} + +.credentials-button-login { + align-self: center; + + width: 100%; + margin: 10px 0 15px 0; +} + +.credentials-button-login-label { + -unity-text-align: middle-center; + white-space: normal; + + min-height: 24px; + padding-right: 4px; +} + +/* Help Section */ + +.help-section-container { + flex-direction: row; + + justify-content: space-between; + align-self: center; + + width: 75%; + margin: 5px 0; +} + +.help-section-hyperlink-button { + margin: 0 10px; + padding: 0; + + flex-shrink: 1; + white-space: normal; +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/Style.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/Style.uss.meta new file mode 100644 index 0000000..e15c190 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/Style.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e05fbdf7dd89a14985a87aa62a03a0e +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeDark.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeDark.uss new file mode 100644 index 0000000..39e3dfc --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeDark.uss @@ -0,0 +1,44 @@ +/* Logo */ + +.asset-store-logo { + background-image: url("../../Icons/publisher-portal-dark.png"); +} + +/* Cloud Login */ + +.cloud-label-or { + color: rgb(200, 200, 200); +} + +/* Error Section */ + +.error-container { + background-color: rgb(63, 63, 63); +} + +.error-container > Image { + --unity-image: resource("console.erroricon@2x"); +} + +/* Credentials Login */ + +.credentials-container { + background-color: rgb(63, 63, 63); +} + +/* Help Section */ + +.help-section-hyperlink-button { + color: rgb(68, 113, 229); + border-color: rgba(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0); +} + +.help-section-hyperlink-button:hover { + color: rgb(68, 133, 229); + cursor: link; +} + +.help-section-hyperlink-button:active { + color: rgb(68, 93, 229); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeDark.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeDark.uss.meta new file mode 100644 index 0000000..525eb0e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeDark.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bea9503736c358b4e99eac03c0db32b7 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeLight.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeLight.uss new file mode 100644 index 0000000..993c2b8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeLight.uss @@ -0,0 +1,44 @@ +/* Logo */ + +.asset-store-logo { + background-image: url("../../Icons/publisher-portal-light.png"); +} + +/* Cloud Login */ + +.cloud-label-or { + color: rgb(28, 28, 28); +} + +/* Error Section */ + +.error-container { + background-color: rgb(212, 212, 212); +} + +.error-container > Image { + --unity-image: resource("console.erroricon@2x"); +} + +/* Credentials Login */ + +.credentials-container { + background-color: rgb(212, 212, 212); +} + +/* Help Section */ + +.help-section-hyperlink-button { + color: rgb(68, 113, 229); + border-color: rgba(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0); +} + +.help-section-hyperlink-button:hover { + color: rgb(68, 133, 229); + cursor: link; +} + +.help-section-hyperlink-button:active { + color: rgb(68, 93, 229); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeLight.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeLight.uss.meta new file mode 100644 index 0000000..498f536 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/LoginView/ThemeLight.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 895ecc4cb6c82b144b8423996c89f7ef +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView.meta new file mode 100644 index 0000000..f5446fd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 46d44b66f60dc484897f0afeee574dbe +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/Style.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/Style.uss new file mode 100644 index 0000000..d36598c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/Style.uss @@ -0,0 +1,485 @@ +.package-list-view { + flex-grow: 1; +} + +/* Package List Toolbar */ + +.package-list-toolbar { + flex-direction: row; + flex-shrink: 0; + + justify-content: space-between; + + height: 24px; +} + +.package-search-field { + flex-grow: 1; + flex-shrink: 1; + align-items: center; + + margin: 1px; +} + +.package-search-field > #unity-search { + margin-top: 0; +} + +.package-search-field > * > .unity-base-field__input { + font-size: 12px; + + padding-left: 5px; +} + +.package-sort-menu { + flex-shrink: 0.001; + align-self: center; + + height: 21px; + + margin: 1px; + padding: 1px 6px; +} + +/* Loading Spinner */ + +.loading-spinner-box +{ + flex-grow: 1; + flex-shrink: 1; + + justify-content: center; +} + +.loading-spinner-image { + align-self: center; + + width: 16px; + height: 16px; +} + +/* Package Group */ + +.package-group-expander-box { + flex-direction: row; + flex-grow: 0; + flex-shrink: 0; + + align-items: center; + + min-height: 30px; + + margin: 10px 0 2px 0; + padding: 1px 5px; +} + +.package-group-expander +{ + align-self: center; + + width: 30px; + height: 30px; +} + +.package-group-label { + font-size: 14px; + -unity-font-style: bold; +} + +.package-group-content-box { + margin: 0; +} + +.package-group-separator { + height: 2px; + + margin: 5px 15px; + + display: none; +} + +.package-group-info-box { + flex-direction: row; + align-items: center; + font-size: 12px; + margin: 5px; + margin-top: 5px; +} + +.package-group-info-box > Image +{ + flex-direction: row; + flex-shrink: 0; + + width: 32px; + height: 32px; + + margin: 5px 10px; +} + +.package-group-info-box > Label +{ + flex-grow: 1; + flex-shrink: 1; + + -unity-text-align: middle-left; + white-space: normal; + + margin-right: 5px; +} + +/* Package Element */ + +.package-full-box { + flex-direction: column; + flex-shrink: 0; + flex-grow: 0; + + min-width: 280px; +} + +.package-foldout-box { + flex-direction: column; + margin: 0; +} + +.package-foldout-box-expanded { + padding-top: 3px; + top: -2px; +} + +.package-foldout-box-info { + flex-direction: row; + flex-grow: 0; + flex-shrink: 0; + + align-items: center; + justify-content: space-between; + + min-width: 200px; + min-height: 50px; + + margin: 2px; +} + +.package-expander-label-row { + flex-basis: 200px; + flex-direction: row; + flex-grow: 1; +} + +.package-expander { + align-self: center; + + width: 30px; + height: 30px; +} + +.package-image { + flex-shrink: 0; + + width: 48px; + height: 48px; + + margin: 0 5px; +} + +.package-label-info-box { + flex-direction: column; + flex-grow: 1; + flex-shrink: 1; + + align-self: center; +} + +.package-label { + flex-shrink: 1; + + align-self: stretch; + + -unity-text-align: middle-left; + -unity-font-style: bold; + white-space: normal; +} + +.package-info { + font-size: 11px; + -unity-text-align: middle-left; + white-space: normal; +} + +.package-open-in-browser-button { + flex-shrink: 0; + + width: 16px; + height: 16px; + + -unity-background-scale-mode: scale-to-fit; +} + +.package-header-progress-bar { + margin: 0 -6px -1px -6px; + padding: 0 0 2px 0; +} + +.package-header-progress-bar > * > .unity-progress-bar__background { + height: 5px; +} + +/* Package Content Element & its nested elements */ + +.package-content-element { + flex-grow: 1; + flex-shrink: 0; + + overflow: hidden; + + margin: -5px 0px 2px 0px; + padding: 5px 10px 5px 40px; +} + +.package-content-option-box { + flex-direction: row; + + margin-top: 10px; +} + +.package-content-option-label-help-row { + flex-direction: row; + flex-shrink: 0; + + align-self: flex-start; + align-items: center; + justify-content: flex-start; + + width: 115px; +} + +.package-content-option-label-help-row > Label { + -unity-text-align: middle-left; +} + +.package-content-option-label-help-row > Image { + height: 16px; + width: 16px; +} + +.package-content-option-dropdown { + flex-grow: 1; + flex-shrink: 1; + + align-self: stretch; + + margin-right: 0; + margin-left: 3px; +} + +.package-content-option-textfield { + flex-grow: 1; + flex-shrink: 1; + + align-self: stretch; +} + +.package-content-option-button { + margin-right: 0; +} + +.package-content-option-toggle { + flex-shrink: 1; +} + +.package-content-option-toggle > * { + flex-shrink: 1; +} + +.package-content-option-toggle * > .unity-label { + flex-shrink: 1; + overflow: hidden; + text-overflow: ellipsis; + margin-left: 5px; +} + +/* MutliToggleSelection Element */ + +.multi-toggle-box { + flex-grow: 1; + flex-direction: column; +} + +.multi-toggle-box-scrollview { + flex-grow: 1; + + height: 100px; + margin-left: 3px; +} + +.multi-toggle-box-scrollview > .unity-scroll-view__content-viewport +{ + margin-left: 1px; +} + +.multi-toggle-box-scrollview > * > .unity-scroll-view__content-container +{ + padding: 3px 0 5px 0; +} + +.multi-toggle-box-scrollview > * > .unity-scroll-view__vertical-scroller +{ + margin: -1px 0; +} + +.multi-toggle-box-empty-label { + flex-grow: 1; + + font-size: 11px; + -unity-text-align: middle-center; + white-space: normal; + + display: none; +} + +.multi-toggle-box-toolbar { + flex-direction: row; + margin: -1px 0 0 3px; +} + +.multi-toggle-box-toolbar-selecting-box { + flex-grow: 1; + flex-shrink: 1; + flex-direction: row; + + align-items: center; + justify-content: flex-end; + + margin-top: 1px; +} + +.multi-toggle-box-toolbar-selecting-box > Button { + flex-shrink: 1; + width: 75px; + margin-right: 0; +} + +.multi-toggle-box-toolbar-filtering-box { + flex-grow: 1; + flex-direction: row; + + align-items: center; + justify-content: flex-start; + + margin-top: 1px; + margin-left: 1px; +} + +.multi-toggle-box-toolbar-filtering-box > ToolbarMenu { + width: 100px; + margin: 0; +} + +.multi-toggle-box-toggle { + padding: 2px 0 0 5px; +} + +.multi-toggle-box-toggle > * > .unity-label { + margin-left: 5px; +} + +/* Validation Element */ + +.validation-result-box { + flex-direction: row; + + align-items: center; + + font-size: 12px; + + margin-top: 5px; +} + +.validation-result-box > Image { + flex-direction: row; + flex-shrink: 0; + + width: 32px; + height: 32px; + + margin: 5px 10px; +} + +.validation-result-box > Label { + flex-grow: 1; + flex-shrink: 1; + + -unity-text-align: middle-left; + white-space: normal; + + margin-right: 5px; +} + +.validation-result-box > Button { + margin-right: 10px; +} + +.validation-result-view-report-button-container { + flex-shrink: 0; + padding: 0 10px 0 10px; +} + +.validation-result-view-report-button { + margin: 0; + padding: 4px 0 4px 0; +} + +.validation-result-view-report-button:hover { + cursor: link; +} + +/* Uploading Element */ + +.uploading-box { + margin-top: 10px; +} + +.uploading-export-and-upload-container { + flex-direction: row; + flex-wrap: wrap; +} + +.uploading-export-button { + flex-basis: 110px; + flex-grow: 1; + height: 24px; + margin: 1px 2px 0 2px; +} + +.uploading-upload-button { + flex-basis: 110px; + flex-grow: 1; + height: 24px; + margin: 1px 2px 0 2px; +} + +.uploading-progress-container { + flex-direction: row; +} + +.uploading-progress-bar { + flex-grow: 1; + flex-shrink: 1; + + justify-content: center; + + padding: 0; + margin: 0 10px 0 0; +} + +.uploading-progress-bar > * > .unity-progress-bar__background { + height: 22px; +} + +.uploading-cancel-button { + width: 95px; + height: 24px; + margin: 0; +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/Style.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/Style.uss.meta new file mode 100644 index 0000000..9ac57e4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/Style.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b3b52dc166e3e24bb1ce4acd2592f3a +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeDark.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeDark.uss new file mode 100644 index 0000000..a0ef9ed --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeDark.uss @@ -0,0 +1,238 @@ +/* Package List Toolbar */ + +.package-list-toolbar { + border-bottom-width: 1px; + border-color: rgb(33, 33, 33); + + background-color: rgb(68, 68, 68); +} + +.package-sort-menu { + color: rgb(238, 238, 238); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(36, 36, 36); + + background-color: rgb(88, 88, 88); +} + +.package-sort-menu:hover { + background-color: rgb(103, 103, 103); +} + +.package-sort-menu:active { + background-color: rgb(73, 73, 73); +} + +/* Loading Spinner */ + +.loading-spinner-image { + --unity-image: resource("WaitSpin00"); +} + +/* Package Group */ + +.package-group-expander-box { + border-width: 0; + border-color: rgba(33, 33, 33, 0); + + background-color: rgba(68, 68, 68, 0); +} + +.package-group-expander { + color: rgb(104, 104, 104); +} + +.package-group-label { + color: rgb(255, 255, 255); +} + +.package-group-separator { + background-color: rgb(48, 48, 48); +} + +.package-group-info-box { + border-width: 1px; + border-color: rgb(33, 33, 33); +} + +.package-group-info-box > Image { + --unity-image: resource("console.infoicon@2x"); +} + +/* Package Element */ + +.package-foldout-box { + border-width: 0; + border-radius: 0; + background-color: rgb(56, 56, 56); +} + +.package-foldout-box:hover { + background-color: rgb(68, 68, 68); +} + +.package-foldout-box-draft:active { + background-color: rgb(48, 48, 48); +} + +.package-foldout-box-expanded { + background-color: rgb(68, 68, 68); +} + +.package-expander { + color: rgb(104, 104, 104); +} + +.package-image { + border-radius: 5px; + background-image: resource("Sprite Icon"); +} + +.package-label { + color: rgb(255, 255, 255); +} + +.package-info { + color: rgb(200, 200, 200); +} + +.package-open-in-browser-button { + border-width: 0; + border-radius: 0; + + background-color: rgba(0, 0, 0, 0); + background-image: url("../../Icons/open-in-browser.png"); + + -unity-background-image-tint-color: rgb(200, 200, 200); +} + +.package-open-in-browser-button:hover { + -unity-background-image-tint-color: rgb(255, 255, 255); +} + +.package-open-in-browser-button:active { + -unity-background-image-tint-color: rgb(155, 155, 155); +} + +.package-header-progress-bar > .unity-progress-bar__container > .unity-progress-bar__background { + border-width: 0; + background-color: rgba(0, 0, 0, 0); +} + +.package-header-progress-bar > .unity-progress-bar__container > .unity-progress-bar__background > .unity-progress-bar__progress { + background-image: none; + background-color: rgb(33, 150, 243); +} + +/* Package Content Element */ + +.package-content-element { + background-color: rgb(68, 68, 68); +} + +.package-content-option-label-help-row > Image { + --unity-image: resource("d__Help@2x"); +} + +.package-content-option-dropdown { + color: rgb(238, 238, 238); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(36, 36, 36); + + background-color: rgb(88, 88, 88); +} + +.package-content-option-dropdown:hover { + background-color: rgb(103, 103, 103); +} + +/* MultiToggleSelection Element */ + +.multi-toggle-box-scrollview { + border-width: 1px; + border-color: rgb(33, 33, 33); + background-color: rgb(58, 58, 58); +} + +.multi-toggle-box-scrollview > * > .unity-scroll-view__vertical-scroller { + border-right-width: 0; +} + +.multi-toggle-box-empty-label { + color: rgb(200, 200, 200); +} + +.multi-toggle-box-toolbar { + border-width: 1px; + border-color: rgb(33, 33, 33); + background-color: rgb(58, 58, 58); +} + +.multi-toggle-box-toolbar-filtering-box > ToolbarMenu { + color: rgb(188, 188, 188); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(36, 36, 36); + + background-color: rgb(88, 88, 88); +} + +/* Validation Element */ + +.validation-result-box { + border-width: 1px; + border-color: rgb(33, 33, 33); +} + +.validation-result-box > Image { + --unity-image: resource("console.infoicon@2x"); +} + +.validation-result-view-report-button { + color: rgb(68, 113, 229); + border-color: rgba(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0); +} + +.validation-result-view-report-button:hover { + color: rgb(68, 133, 229); +} + +.validation-result-view-report-button:active { + color: rgb(68, 93, 229); +} + +/* Uploading Element */ + +.uploading-export-button { + color: rgb(220, 220, 220); + border-width: 1px; + border-color: rgb(33, 33, 33); +} + +.uploading-upload-button { + color: rgb(220, 220, 220); + border-width: 1px; + border-color: rgb(33, 33, 33); +} + +.uploading-progress-bar > .unity-progress-bar__container > .unity-progress-bar__background { + border-width: 0; + border-radius: 2px; +} + +.uploading-progress-bar > .unity-progress-bar__container > .unity-progress-bar__background > .unity-progress-bar__progress { + background-image: none; + background-color: rgb(33, 150, 243); +} + +.uploading-cancel-button { + color: rgb(220, 220, 220); + border-width: 1px; + border-color: rgb(33, 33, 33); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeDark.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeDark.uss.meta new file mode 100644 index 0000000..6e8e7be --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeDark.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2096f0ee5b05ec643b409ded0d725eb3 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeLight.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeLight.uss new file mode 100644 index 0000000..d375fca --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeLight.uss @@ -0,0 +1,238 @@ +/* Package List Toolbar */ + +.package-list-toolbar { + border-bottom-width: 1px; + border-color: rgb(127, 127, 127); + + background-color: rgb(203, 203, 203); +} + +.package-sort-menu { + color: rgb(9, 9, 9); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(178, 178, 178); + + background-color: rgb(228, 228, 228); +} + +.package-sort-menu:hover { + background-color: rgb(236, 236, 236); +} + +.package-sort-menu:active { + background-color: rgb(220, 220, 220); +} + +/* Loading Spinner */ + +.loading-spinner-image { + --unity-image: resource("WaitSpin00"); +} + +/* Package Group */ + +.package-group-expander-box { + border-width: 0; + border-color: rgba(0, 0, 0, 0); + + background-color: rgba(0, 0, 0, 0); +} + +.package-group-expander { + color: rgb(77, 77, 77); +} + +.package-group-label { + color: rgb(0, 0, 0); +} + +.package-group-separator +{ + background-color: rgb(48, 48, 48); +} + +.package-group-info-box +{ + border-width: 1px; + border-color: rgb(33, 33, 33); +} + +.package-group-info-box > Image +{ + --unity-image: resource("console.infoicon@2x"); +} + +/* Package Element */ + +.package-foldout-box { + border-width: 0; + border-radius: 0; + background-color: rgb(198, 198, 198); +} + +.package-foldout-box:hover { + background-color: rgb(212, 212, 212); +} + +.package-foldout-box-draft:active { + background-color: rgb(180, 180, 180); +} + +.package-foldout-box-expanded { + background-color: rgb(212, 212, 212); +} + +.package-expander { + color: rgb(77, 77, 77); +} + +.package-image { + border-radius: 5px; + background-image: resource("Sprite Icon"); +} + +.package-label { + color: rgb(0, 0, 0); +} + +.package-info { + color: rgb(48, 48, 48); +} + +.package-open-in-browser-button { + border-width: 0; + border-radius: 0; + + background-color: rgba(0, 0, 0, 0); + background-image: url("../../Icons/open-in-browser.png"); + + -unity-background-image-tint-color: rgb(150, 150, 150); +} + +.package-open-in-browser-button:hover { + -unity-background-image-tint-color: rgb(100, 100, 100); +} + +.package-open-in-browser-button:active { + -unity-background-image-tint-color: rgb(50, 50, 50); +} + +.package-header-progress-bar > .unity-progress-bar__container > .unity-progress-bar__background { + border-width: 0; + background-color: rgba(0, 0, 0, 0); +} + +.package-header-progress-bar > .unity-progress-bar__container > .unity-progress-bar__background > .unity-progress-bar__progress { + background-image: none; + background-color: rgb(108, 157, 243); +} + +/* Package Content Element */ + +.package-content-element { + background-color: rgb(212, 212, 212); +} + +.package-content-option-label-help-row > Image { + --unity-image: resource("_Help@2x"); +} + +.package-content-option-dropdown { + color: rgb(9, 9, 9); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(178, 178, 178); + + background-color: rgb(228, 228, 228); +} + +.package-content-option-dropdown:hover { + background-color: rgb(236, 236, 236); +} + +/* MultiToggleSelection Element */ + +.multi-toggle-box-scrollview { + border-width: 1px; + border-color: rgb(33, 33, 33); + background-color: rgb(200, 200, 200); +} + +.multi-toggle-box-scrollview > * > .unity-scroll-view__vertical-scroller { + border-right-width: 0; +} + +.multi-toggle-box-empty-label { + color: rgb(0, 0, 0); +} + +.multi-toggle-box-toolbar { + border-width: 1px; + border-color: rgb(33, 33, 33); + background-color: rgb(200, 200, 200); +} + +.multi-toggle-box-toolbar-filtering-box > ToolbarMenu { + color: rgb(9, 9, 9); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(178, 178, 178); + + background-color: rgb(228, 228, 228); +} + +/* Validation Element */ + +.validation-result-box { + border-width: 1px; + border-color: rgb(33, 33, 33); +} + +.validation-result-box > Image { + --unity-image: resource("console.infoicon@2x"); +} + +.validation-result-view-report-button { + color: rgb(68, 113, 229); + border-color: rgba(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0); +} + +.validation-result-view-report-button:hover { + color: rgb(68, 133, 229); +} + +.validation-result-view-report-button:active { + color: rgb(68, 93, 229); +} + +/* Uploading Element */ + +.uploading-export-button { + border-width: 1px; + border-color: rgb(33, 33, 33); +} + +.uploading-upload-button { + border-width: 1px; + border-color: rgb(33, 33, 33); +} + +.uploading-progress-bar > .unity-progress-bar__container > .unity-progress-bar__background { + border-width: 0; + border-radius: 2px; +} + +.uploading-progress-bar > .unity-progress-bar__container > .unity-progress-bar__background > .unity-progress-bar__progress { + background-image: none; + background-color: rgb(108, 157, 243); +} + +.uploading-cancel-button { + border-width: 1px; + border-color: rgb(33, 33, 33); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeLight.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeLight.uss.meta new file mode 100644 index 0000000..1cc032a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/PackageListView/ThemeLight.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9b79808cdce73b949abc32c5fada1bda +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/Style.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/Style.uss new file mode 100644 index 0000000..6a69261 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/Style.uss @@ -0,0 +1,74 @@ +.uploader-window-root { + flex-grow: 1; +} + +.uploader-window-root-scrollview { + flex-grow: 1; +} + +.uploader-window-root-scrollview > * > .unity-scroll-view__content-container { + flex-grow: 1; +} + +.uploader-window-root-scrollview > * > .unity-scroll-view__content-viewport { + flex-shrink: 1; +} + +.uploader-window-root-scrollview > * > * > .unity-scroll-view__content-container { + flex-grow: 1; + flex-shrink: 1; +} + +.account-toolbar { + flex-direction: row; + flex-shrink: 0; + + justify-content: space-between; + margin: 0; +} + +.account-toolbar-left-side-container { + flex-grow: 1; + flex-shrink: 0; + flex-basis: 0px; + overflow: hidden; + min-width: 16px; + align-self: center; + flex-direction: row; + margin: 3px 0px 3px 1px; +} + +.account-toolbar-right-side-container { + flex-shrink: 1; + flex-grow: 0; + flex-wrap: wrap; + align-self: center; + flex-direction: row; + margin: 0; +} + +.account-toolbar-user-image { + width: 16px; + height: 16px; + flex-shrink: 0; + margin-right: 1px; +} + +.account-toolbar-email-label { + flex-shrink: 1; + overflow: hidden; + text-overflow: ellipsis; + -unity-font-style: bold; +} + +.account-toolbar-button-refresh { + flex-grow: 1; + min-width: 50px; + margin-right: 1px; +} + +.account-toolbar-button-logout { + flex-grow: 1; + min-width: 50px; + margin-right: 1px; +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/Style.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/Style.uss.meta new file mode 100644 index 0000000..1a2d462 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/Style.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6f0343bfa66bbb344bee68a23d24cca8 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeDark.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeDark.uss new file mode 100644 index 0000000..7f836d4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeDark.uss @@ -0,0 +1,9 @@ +.account-toolbar { + background-color: rgb(68, 68, 68); + border-color: rgb(33, 33, 33); + border-top-width: 1px; +} + +.account-toolbar-user-image { + background-image: url("../Icons/account-dark.png"); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeDark.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeDark.uss.meta new file mode 100644 index 0000000..2ebe886 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeDark.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26d328c2ffac2ff44af266e9c632f2ab +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeLight.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeLight.uss new file mode 100644 index 0000000..16e3de4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeLight.uss @@ -0,0 +1,9 @@ +.account-toolbar { + background-color: rgb(203, 203, 203); + border-color: rgb(127, 127, 127); + border-top-width: 1px; +} + +.account-toolbar-user-image { + background-image: url("../Icons/account-light.png"); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeLight.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeLight.uss.meta new file mode 100644 index 0000000..03e9d7c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/ThemeLight.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a994594cc493b7346b4d4a71d39908dc +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/UploaderWindow.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/UploaderWindow.cs new file mode 100644 index 0000000..f9fffa9 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/UploaderWindow.cs @@ -0,0 +1,232 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Uploader.Services; +using AssetStoreTools.Uploader.Services.Analytics; +using AssetStoreTools.Uploader.Services.Api; +using AssetStoreTools.Uploader.UI.Elements; +using AssetStoreTools.Uploader.UI.Views; +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader +{ + internal class UploaderWindow : AssetStoreToolsWindow + { + private const string DebugPhrase = "debug"; + + // Services + private ICachingService _cachingService; + private IAuthenticationService _authenticationService; + private IPackageDownloadingService _packageDownloadingService; + private IPackageUploadingService _packageUploadingService; + private IAnalyticsService _analyticsService; + private IPackageFactoryService _packageFactoryService; + + // Data + private bool _isQuitting = false; + + // UI + private VisualElement _uploaderWindowRoot; + private ScrollView _rootScrollView; + + private LoginView _loginView; + private PackageListView _packageListView; + private AccountToolbar _accountToolbar; + + protected override string WindowTitle => "Asset Store Uploader"; + + protected override void Init() + { + RegisterServices(); + SetupWindow(); + } + + private void RegisterServices() + { + var uploaderServiceProvider = UploaderServiceProvider.Instance; + _analyticsService = uploaderServiceProvider.GetService(); + _cachingService = uploaderServiceProvider.GetService(); + _authenticationService = uploaderServiceProvider.GetService(); + _packageDownloadingService = uploaderServiceProvider.GetService(); + _packageUploadingService = uploaderServiceProvider.GetService(); + _packageFactoryService = uploaderServiceProvider.GetService(); + } + + private void SetupWindow() + { + minSize = new Vector2(400, 430); + this.SetAntiAliasing(4); + rootVisualElement.styleSheets.Add(StyleSelector.UploaderWindow.UploaderWindowStyle); + rootVisualElement.styleSheets.Add(StyleSelector.UploaderWindow.UploaderWindowTheme); + + if (_cachingService.GetCachedUploaderWindow(out _uploaderWindowRoot)) + { + rootVisualElement.Add(_uploaderWindowRoot); + return; + } + + _uploaderWindowRoot = new VisualElement(); + _uploaderWindowRoot.AddToClassList("uploader-window-root"); + rootVisualElement.Add(_uploaderWindowRoot); + + _rootScrollView = new ScrollView(); + _rootScrollView.AddToClassList("uploader-window-root-scrollview"); + _uploaderWindowRoot.Add(_rootScrollView); + + CreateLoginView(); + CreatePackageListView(); + CreateAccountToolbar(); + EditorApplication.wantsToQuit += OnWantsToQuit; + + _cachingService.CacheUploaderWindow(_uploaderWindowRoot); + + PerformAuthentication(); + } + + private void CreateLoginView() + { + _loginView = new LoginView(_authenticationService); + _loginView.OnAuthenticated += OnAuthenticationSuccess; + _rootScrollView.Add(_loginView); + } + + private void CreatePackageListView() + { + _packageListView = new PackageListView(_packageDownloadingService, _packageFactoryService); + _packageListView.OnInitializeError += PackageViewInitializationError; + _rootScrollView.Add(_packageListView); + } + + private void CreateAccountToolbar() + { + _accountToolbar = new AccountToolbar(); + _accountToolbar.OnRefresh += RefreshPackages; + _accountToolbar.OnLogout += LogOut; + _uploaderWindowRoot.Add(_accountToolbar); + } + + private void PerformAuthentication() + { + ShowAuthenticationView(); + _loginView.LoginWithSessionToken(); + } + + private async void OnAuthenticationSuccess(User user) + { + _accountToolbar.SetUser(user); + _accountToolbar.DisableButtons(); + + ShowAccountPackageView(); + await _packageListView.LoadPackages(true); + + _accountToolbar.EnableButtons(); + } + + private async Task RefreshPackages() + { + _packageUploadingService.StopAllUploadinng(); + _packageDownloadingService.StopDownloading(); + + await _packageListView.LoadPackages(false); + } + + private void LogOut() + { + _packageUploadingService.StopAllUploadinng(); + _packageDownloadingService.StopDownloading(); + + _authenticationService.Deauthenticate(); + _packageDownloadingService.ClearPackageData(); + + _accountToolbar.SetUser(null); + ShowAuthenticationView(); + } + + private void PackageViewInitializationError(Exception e) + { + _loginView.DisplayError(e.Message); + LogOut(); + } + + private void ShowAuthenticationView() + { + HideElement(_accountToolbar); + HideElement(_packageListView); + ShowElement(_loginView); + } + + private void ShowAccountPackageView() + { + HideElement(_loginView); + ShowElement(_accountToolbar); + ShowElement(_packageListView); + } + + 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 OnDestroy() + { + if (!_isQuitting && _packageUploadingService.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 bool OnWantsToQuit() + { + if (!_packageUploadingService.IsUploading) + return true; + + _isQuitting = EditorUtility.DisplayDialog("Notice", "Assets are still being uploaded to the Asset Store. " + + "Would you still like to close Unity Editor?", "Yes", "No"); + + return _isQuitting; + } + + #region Debug Utility + + private readonly List _debugBuffer = new List(); + + private void OnGUI() + { + CheckForDebugMode(); + } + + 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/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/UploaderWindow.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/UploaderWindow.cs.meta new file mode 100644 index 0000000..ef78266 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Uploader/UploaderWindow.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/Installer/Packages/com.unity.asset-store-tools/Editor/Utility.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility.meta new file mode 100644 index 0000000..9a2bdff --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3dfd03e520c145b45a32c7e2915fe3cb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASDebug.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASDebug.cs new file mode 100644 index 0000000..16fc4c8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASDebug.cs @@ -0,0 +1,65 @@ +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Utility +{ + internal static class ASDebug + { + private enum LogType + { + Info, + Warning, + Error + } + + private static string FormatInfo(object message) => $"[AST Info] {message}"; + private static string FormatWarning(object message) => $"[AST Warning] {message}"; + private static string FormatError(object message) => $"[AST Error] {message}"; + + + private static bool s_debugModeEnabled = EditorPrefs.GetBool(Constants.Debug.DebugModeKey); + + public static bool DebugModeEnabled + { + get => s_debugModeEnabled; + set { s_debugModeEnabled = value; EditorPrefs.SetBool(Constants.Debug.DebugModeKey, value); } + } + + public static void Log(object message) + { + LogMessage(message, LogType.Info); + } + + public static void LogWarning(object message) + { + LogMessage(message, LogType.Warning); + } + + public static void LogError(object message) + { + LogMessage(message, LogType.Error); + } + + private static void LogMessage(object message, LogType type) + { + if (!DebugModeEnabled) + return; + + switch (type) + { + case LogType.Info: + Debug.Log(FormatInfo(message)); + break; + case LogType.Warning: + Debug.LogWarning(FormatWarning(message)); + break; + case LogType.Error: + Debug.LogError(FormatError(message)); + break; + default: + Debug.Log(message); + break; + } + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASDebug.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASDebug.cs.meta new file mode 100644 index 0000000..2f4aab7 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASDebug.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 478caa497d99100429a0509fa487bfe4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsPreferences.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsPreferences.cs new file mode 100644 index 0000000..73ad2bd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsPreferences.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Utility +{ + internal class ASToolsPreferences + { + private static ASToolsPreferences s_instance; + public static ASToolsPreferences Instance => s_instance ?? (s_instance = new ASToolsPreferences()); + + public static event Action OnSettingsChange; + + private ASToolsPreferences() + { + Load(); + } + + private void Load() + { + CheckForUpdates = PlayerPrefs.GetInt("AST_CheckForUpdates", 1) == 1; + LegacyVersionCheck = PlayerPrefs.GetInt("AST_LegacyVersionCheck", 1) == 1; + UploadVersionCheck = PlayerPrefs.GetInt("AST_UploadVersionCheck", 1) == 1; + DisplayHiddenMetaDialog = PlayerPrefs.GetInt("AST_HiddenFolderMetaCheck", 1) == 1; + EnableSymlinkSupport = PlayerPrefs.GetInt("AST_EnableSymlinkSupport", 0) == 1; + UseLegacyExporting = PlayerPrefs.GetInt("AST_UseLegacyExporting", 0) == 1; + } + + public void Save(bool triggerSettingsChange = false) + { + PlayerPrefs.SetInt("AST_CheckForUpdates", CheckForUpdates ? 1 : 0); + PlayerPrefs.SetInt("AST_LegacyVersionCheck", LegacyVersionCheck ? 1 : 0); + PlayerPrefs.SetInt("AST_UploadVersionCheck", UploadVersionCheck ? 1 : 0); + PlayerPrefs.SetInt("AST_HiddenFolderMetaCheck", DisplayHiddenMetaDialog ? 1 : 0); + PlayerPrefs.SetInt("AST_EnableSymlinkSupport", EnableSymlinkSupport ? 1 : 0); + PlayerPrefs.SetInt("AST_UseLegacyExporting", UseLegacyExporting ? 1 : 0); + PlayerPrefs.Save(); + + if (triggerSettingsChange) + OnSettingsChange?.Invoke(); + } + + /// + /// Periodically check if an update for the Asset Store Publishing Tools is available + /// + public bool CheckForUpdates; + + /// + /// Check if legacy Asset Store Tools are in the Project + /// + public bool LegacyVersionCheck; + + /// + /// Enables a DisplayDialog when hidden folders are found to be missing meta files + /// + public bool DisplayHiddenMetaDialog; + + /// + /// Check if the package has been uploaded from a correct Unity version at least once + /// + public bool UploadVersionCheck; + + /// + /// Enables Junction symlink support + /// + public bool EnableSymlinkSupport; + + /// + /// Enables legacy exporting for Folder Upload workflow + /// + public bool UseLegacyExporting; + } + + internal class ASToolsPreferencesProvider : SettingsProvider + { + private const string SettingsPath = "Project/Asset Store Tools"; + + private class Styles + { + public static readonly GUIContent CheckForUpdatesLabel = EditorGUIUtility.TrTextContent("Check for Updates", "Periodically check if an update for the Asset Store Publishing Tools is available."); + public static readonly GUIContent LegacyVersionCheckLabel = EditorGUIUtility.TrTextContent("Legacy ASTools Check", "Enable Legacy Asset Store Tools version checking."); + public static readonly GUIContent UploadVersionCheckLabel = EditorGUIUtility.TrTextContent("Upload Version Check", "Check if the package has been uploader from a correct Unity version at least once."); + public static readonly GUIContent DisplayHiddenMetaDialogLabel = EditorGUIUtility.TrTextContent("Display Hidden Folder Meta Dialog", "Show a DisplayDialog when hidden folders are found to be missing meta files.\nNote: this only affects hidden folders ending with a '~' character"); + public static readonly GUIContent EnableSymlinkSupportLabel = EditorGUIUtility.TrTextContent("Enable Symlink Support", "Enable Junction Symlink support. Note: folder selection validation will take longer."); + public static readonly GUIContent UseLegacyExportingLabel = EditorGUIUtility.TrTextContent("Use Legacy Exporting", "Enabling this option uses native Unity methods when exporting packages for the Folder Upload workflow.\nNote: individual package dependency selection when choosing to 'Include Package Manifest' is unavailable when this option is enabled."); + public static readonly GUIContent UseCustomPreviewsLabel = EditorGUIUtility.TrTextContent("Enable High Quality Previews (experimental)", "Override native asset preview retrieval with higher-quality preview generation"); + } + + public static void OpenSettings() + { + SettingsService.OpenProjectSettings(SettingsPath); + } + + private ASToolsPreferencesProvider(string path, SettingsScope scopes, IEnumerable keywords = null) + : base(path, scopes, keywords) { } + + public override void OnGUI(string searchContext) + { + var preferences = ASToolsPreferences.Instance; + + EditorGUI.BeginChangeCheck(); + using (CreateSettingsWindowGUIScope()) + { + preferences.CheckForUpdates = EditorGUILayout.Toggle(Styles.CheckForUpdatesLabel, preferences.CheckForUpdates); + preferences.LegacyVersionCheck = EditorGUILayout.Toggle(Styles.LegacyVersionCheckLabel, preferences.LegacyVersionCheck); + preferences.UploadVersionCheck = EditorGUILayout.Toggle(Styles.UploadVersionCheckLabel, preferences.UploadVersionCheck); + preferences.DisplayHiddenMetaDialog = EditorGUILayout.Toggle(Styles.DisplayHiddenMetaDialogLabel, preferences.DisplayHiddenMetaDialog); + preferences.EnableSymlinkSupport = EditorGUILayout.Toggle(Styles.EnableSymlinkSupportLabel, preferences.EnableSymlinkSupport); + preferences.UseLegacyExporting = EditorGUILayout.Toggle(Styles.UseLegacyExportingLabel, preferences.UseLegacyExporting); + } + + if (EditorGUI.EndChangeCheck()) + { + ASToolsPreferences.Instance.Save(true); + } + } + + [SettingsProvider] + public static SettingsProvider CreateAssetStoreToolsSettingProvider() + { + var provider = new ASToolsPreferencesProvider(SettingsPath, SettingsScope.Project, GetSearchKeywordsFromGUIContentProperties()); + return provider; + } + + private IDisposable CreateSettingsWindowGUIScope() + { + var unityEditorAssembly = Assembly.GetAssembly(typeof(EditorWindow)); + var type = unityEditorAssembly.GetType("UnityEditor.SettingsWindow+GUIScope"); + return Activator.CreateInstance(type) as IDisposable; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsPreferences.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsPreferences.cs.meta new file mode 100644 index 0000000..56807b0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsPreferences.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b75179c8d22a35b42a543d6fa7857ce0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsUpdater.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsUpdater.cs new file mode 100644 index 0000000..698b974 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsUpdater.cs @@ -0,0 +1,250 @@ +using AssetStoreTools.Api; +using System; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Utility +{ + [InitializeOnLoad] + internal class ASToolsUpdater : AssetStoreToolsWindow + { + protected override string WindowTitle => "Asset Store Tools Update Check"; + + private static IAssetStoreApi _api; + + private VisualElement _loadingContainer; + private VisualElement _versionInfoContainer; + + private Image _loadingImage; + private double _lastTimeSinceStartup; + private double _timeSinceLoadingImageChange; + private int _loadingImageIndex; + + private static bool _updateCheckPerformed + { + get + { + return SessionState.GetBool("AST_UpdateChecked", false); + } + set + { + SessionState.SetBool("AST_UpdateChecked", value); + } + } + + static ASToolsUpdater() + { + _api = new AssetStoreApi(new AssetStoreClient()); + // Retrieving cached SessionState/PlayerPrefs values is not allowed from an instance field initializer + EditorApplication.update += CheckForUpdatesAfterEditorUpdate; + } + + private static async void CheckForUpdatesAfterEditorUpdate() + { + EditorApplication.update -= CheckForUpdatesAfterEditorUpdate; + + if (!ShouldCheckForUpdates()) + return; + + await CheckForUpdates((success, currentVersion, latestVersion) => + { + if (success && currentVersion < latestVersion) + { + AssetStoreTools.OpenUpdateChecker(); + } + }); + } + + private static bool ShouldCheckForUpdates() + { + if (!ASToolsPreferences.Instance.CheckForUpdates) + return false; + + return _updateCheckPerformed == false; + } + + private static async Task CheckForUpdates(Action OnUpdatesChecked) + { + _updateCheckPerformed = true; + var latestVersionResult = await _api.GetLatestAssetStoreToolsVersion(); + if (!latestVersionResult.Success) + { + OnUpdatesChecked?.Invoke(false, null, null); + return; + } + + Version currentVersion = null; + Version latestVersion = null; + + try + { + var latestVersionStr = latestVersionResult.Version; + var currentVersionStr = PackageUtility.GetAllPackages().FirstOrDefault(x => x.name == "com.unity.asset-store-tools").version; + + currentVersion = new Version(currentVersionStr); + latestVersion = new Version(latestVersionStr); + } + catch + { + OnUpdatesChecked?.Invoke(false, null, null); + } + + OnUpdatesChecked?.Invoke(true, currentVersion, latestVersion); + } + + protected override void Init() + { + rootVisualElement.styleSheets.Add(StyleSelector.UpdaterWindow.UpdaterWindowStyle); + rootVisualElement.styleSheets.Add(StyleSelector.UpdaterWindow.UpdaterWindowTheme); + + SetupLoadingSpinner(); + _ = CheckForUpdates(OnVersionsRetrieved); + } + + private void OnVersionsRetrieved(bool success, Version currentVersion, Version latestVersion) + { + if (_loadingContainer != null) + _loadingContainer.style.display = DisplayStyle.None; + + if (success) + { + SetupVersionInfo(currentVersion, latestVersion); + } + else + { + SetupFailInfo(); + } + } + + private void SetupLoadingSpinner() + { + _loadingContainer = new VisualElement(); + _loadingContainer.AddToClassList("updater-loading-container"); + _loadingImage = new Image(); + EditorApplication.update += LoadingSpinLoop; + + _loadingContainer.Add(_loadingImage); + rootVisualElement.Add(_loadingContainer); + } + + private void SetupVersionInfo(Version currentVersion, Version latestVersion) + { + _versionInfoContainer = new VisualElement(); + _versionInfoContainer.AddToClassList("updater-info-container"); + + AddDescriptionLabels(currentVersion, latestVersion); + AddUpdateButtons(currentVersion, latestVersion); + AddCheckForUpdatesToggle(); + + rootVisualElement.Add(_versionInfoContainer); + } + + private void AddDescriptionLabels(Version currentVersion, Version latestVersion) + { + var descriptionText = currentVersion < latestVersion ? + "An update to the Asset Store Publishing Tools is available. Updating to the latest version is highly recommended." : + "Asset Store Publishing Tools are up to date!"; + + var labelContainer = new VisualElement(); + labelContainer.AddToClassList("updater-info-container-labels"); + var descriptionLabel = new Label(descriptionText); + descriptionLabel.AddToClassList("updater-info-container-labels-description"); + + var currentVersionRow = new VisualElement(); + currentVersionRow.AddToClassList("updater-info-container-labels-row"); + var latestVersionRow = new VisualElement(); + latestVersionRow.AddToClassList("updater-info-container-labels-row"); + + var currentVersionLabel = new Label("Current version:"); + currentVersionLabel.AddToClassList("updater-info-container-labels-row-identifier"); + var latestVersionLabel = new Label("Latest version:"); + latestVersionLabel.AddToClassList("updater-info-container-labels-row-identifier"); + + var currentVersionLabelValue = new Label(currentVersion.ToString()); + var latestVersionLabelValue = new Label(latestVersion.ToString()); + + currentVersionRow.Add(currentVersionLabel); + currentVersionRow.Add(currentVersionLabelValue); + latestVersionRow.Add(latestVersionLabel); + latestVersionRow.Add(latestVersionLabelValue); + + labelContainer.Add(descriptionLabel); + labelContainer.Add(currentVersionRow); + labelContainer.Add(latestVersionRow); + + _versionInfoContainer.Add(labelContainer); + } + + private void AddUpdateButtons(Version currentVersion, Version latestVersion) + { + if (currentVersion >= latestVersion) + return; + + var buttonContainer = new VisualElement(); + buttonContainer.AddToClassList("updater-info-container-buttons"); + var latestVersionButton = new Button(() => Application.OpenURL(Constants.Updater.AssetStoreToolsUrl)) { text = "Get the latest version" }; + var skipVersionButton = new Button(Close) { text = "Skip for now" }; + + buttonContainer.Add(latestVersionButton); + buttonContainer.Add(skipVersionButton); + + _versionInfoContainer.Add(buttonContainer); + } + + private void AddCheckForUpdatesToggle() + { + var toggleContainer = new VisualElement(); + toggleContainer.AddToClassList("updater-info-container-toggle"); + var checkForUpdatesToggle = new Toggle() { text = "Check for Updates", value = ASToolsPreferences.Instance.CheckForUpdates }; + checkForUpdatesToggle.RegisterValueChangedCallback(OnCheckForUpdatesToggleChanged); + + toggleContainer.Add(checkForUpdatesToggle); + _versionInfoContainer.Add(toggleContainer); + } + + private void OnCheckForUpdatesToggleChanged(ChangeEvent evt) + { + ASToolsPreferences.Instance.CheckForUpdates = evt.newValue; + ASToolsPreferences.Instance.Save(); + } + + private void SetupFailInfo() + { + var failContainer = new VisualElement(); + failContainer.AddToClassList("updater-fail-container"); + + var failImage = new Image(); + var failDescription = new Label("Asset Store Publishing Tools could not retrieve information about the latest version."); + + failContainer.Add(failImage); + failContainer.Add(failDescription); + + rootVisualElement.Add(failContainer); + } + + private void LoadingSpinLoop() + { + var currentTimeSinceStartup = EditorApplication.timeSinceStartup; + var deltaTime = EditorApplication.timeSinceStartup - _lastTimeSinceStartup; + _lastTimeSinceStartup = currentTimeSinceStartup; + + _timeSinceLoadingImageChange += deltaTime; + if (_timeSinceLoadingImageChange < 0.075) + return; + + _timeSinceLoadingImageChange = 0; + + _loadingImage.image = EditorGUIUtility.IconContent($"WaitSpin{_loadingImageIndex++:00}").image; + if (_loadingImageIndex > 11) + _loadingImageIndex = 0; + } + + private void OnDestroy() + { + EditorApplication.update -= LoadingSpinLoop; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsUpdater.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsUpdater.cs.meta new file mode 100644 index 0000000..d0e4183 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ASToolsUpdater.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2ac370b9d2279ed4c9faec7134ba2759 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/CacheUtil.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/CacheUtil.cs new file mode 100644 index 0000000..a596352 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/CacheUtil.cs @@ -0,0 +1,266 @@ +using System.IO; +using CacheConstants = AssetStoreTools.Constants.Cache; + +namespace AssetStoreTools.Utility +{ + internal static class CacheUtil + { + public static bool GetFileFromTempCache(string fileName, out string filePath) + { + return GetCacheFile(CacheConstants.TempCachePath, fileName, out filePath); + } + + public static bool GetFileFromPersistentCache(string fileName, out string filePath) + { + return GetCacheFile(CacheConstants.PersistentCachePath, fileName, out filePath); + } + + public static bool GetFileFromProjectPersistentCache(string projectPath, string fileName, out string filePath) + { + return GetCacheFile(Path.Combine(projectPath, CacheConstants.PersistentCachePath), fileName, out filePath); + } + + private static bool GetCacheFile(string rootPath, string fileName, out string filePath) + { + filePath = Path.Combine(rootPath, fileName); + return File.Exists(filePath); + } + + public static void CreateFileInTempCache(string fileName, object content, bool overwrite) + { + CreateCacheFile(CacheConstants.TempCachePath, fileName, content, overwrite); + } + + public static void CreateFileInPersistentCache(string fileName, object content, bool overwrite) + { + CreateCacheFile(CacheConstants.PersistentCachePath, fileName, content, overwrite); + } + + private static void CreateCacheFile(string rootPath, string fileName, object content, bool overwrite) + { + if (!Directory.Exists(rootPath)) + Directory.CreateDirectory(rootPath); + + var fullPath = Path.Combine(rootPath, fileName); + + bool willUpdate = false; + if (File.Exists(fullPath)) + { + if (overwrite) + { + File.Delete(fullPath); + willUpdate = true; + } + else + return; + } + + switch (content) + { + case byte[] bytes: + File.WriteAllBytes(fullPath, bytes); + break; + default: + File.WriteAllText(fullPath, content.ToString()); + break; + } + + var keyword = willUpdate ? "Updating" : "Creating"; + ASDebug.Log($"{keyword} cache file: '{fullPath}'"); + } + + public static void DeleteFileFromTempCache(string fileName) + { + DeleteFileFromCache(CacheConstants.TempCachePath, fileName); + } + + public static void DeleteFileFromPersistentCache(string fileName) + { + DeleteFileFromCache(CacheConstants.PersistentCachePath, fileName); + } + + private static void DeleteFileFromCache(string rootPath, string fileName) + { + var path = Path.Combine(rootPath, fileName); + if (File.Exists(path)) + File.Delete(path); + } + + //private static void CreateFileInPersistentCache(string fileName, object content, bool overwrite) + //{ + // CreateCacheFile(CacheConstants.PersistentCachePath, fileName, content, overwrite); + //} + + //private static void CreateCacheFile(string rootPath, string fileName, object content, bool overwrite) + //{ + // if (!Directory.Exists(rootPath)) + // Directory.CreateDirectory(rootPath); + + // var fullPath = Path.Combine(rootPath, fileName); + + // if (File.Exists(fullPath)) + // { + // if (overwrite) + // File.Delete(fullPath); + // else + // return; + // } + + // switch (content) + // { + // case byte[] bytes: + // File.WriteAllBytes(fullPath, bytes); + // break; + // default: + // File.WriteAllText(fullPath, content.ToString()); + // break; + // } + // ASDebug.Log($"Creating cached file: '{fullPath}'"); + //} + + //public static void ClearTempCache() + //{ + // if (!File.Exists(Path.Combine(CacheConstants.TempCachePath, CacheConstants.PackageDataFile))) + // return; + + + // // Cache consists of package data and package texture thumbnails. We don't clear + // // texture thumbnails here since they are less likely to change. They are still + // // deleted and redownloaded every project restart (because of being stored in the 'Temp' folder) + // var fullPath = Path.Combine(CacheConstants.TempCachePath, CacheConstants.PackageDataFile); + // ASDebug.Log($"Deleting cached file '{fullPath}'"); + // File.Delete(fullPath); + //} + + //public static void CachePackageMetadata(List data) + //{ + // var serializerSettings = new JsonSerializerSettings() + // { + // ContractResolver = Package.CachedPackageResolver.Instance, + // Formatting = Formatting.Indented + // }; + + // CreateFileInTempCache(CacheConstants.PackageDataFile, JsonConvert.SerializeObject(data, serializerSettings), true); + //} + + //public static void UpdatePackageMetadata(Package data) + //{ + // if (!GetCachedPackageMetadata(out var cachedData)) + // return; + + // var index = cachedData.FindIndex(x => x.PackageId.Equals(data.PackageId)); + // if (index == -1) + // { + // cachedData.Add(data); + // } + // else + // { + // cachedData.RemoveAt(index); + // cachedData.Insert(index, data); + // } + + // CachePackageMetadata(cachedData); + //} + + //public static bool GetCachedPackageMetadata(out List data) + //{ + // data = new List(); + // var path = Path.Combine(CacheConstants.TempCachePath, CacheConstants.PackageDataFile); + // if (!File.Exists(path)) + // return false; + + // try + // { + // var serializerSettings = new JsonSerializerSettings() + // { + // ContractResolver = Package.CachedPackageResolver.Instance + // }; + + // data = JsonConvert.DeserializeObject>(File.ReadAllText(path, Encoding.UTF8), serializerSettings); + // return true; + // } + // catch + // { + // return false; + // } + //} + + //public static void CacheTexture(string packageId, Texture2D texture) + //{ + // CreateFileInTempCache($"{packageId}.png", texture.EncodeToPNG(), true); + //} + + //public static bool GetCachedTexture(string packageId, out Texture2D texture) + //{ + // texture = new Texture2D(1, 1); + // var path = Path.Combine(CacheConstants.TempCachePath, $"{packageId}.png"); + // if (!File.Exists(path)) + // return false; + + // texture.LoadImage(File.ReadAllBytes(path)); + // return true; + //} + + //public static void CacheWorkflowStateData(string packageId, WorkflowStateData data) + //{ + // var fileName = $"{packageId}-workflowStateData.asset"; + // CreateFileInPersistentCache(fileName, JsonConvert.SerializeObject(data, Formatting.Indented), true); + //} + + //public static bool GetCachedWorkflowStateData(string packageId, out WorkflowStateData data) + //{ + // data = null; + // var path = Path.Combine(CacheConstants.PersistentCachePath, $"{packageId}-workflowStateData.asset"); + // if (!File.Exists(path)) + // return false; + + // data = JsonConvert.DeserializeObject(File.ReadAllText(path, Encoding.UTF8)); + // return true; + //} + + //public static void CacheValidationStateData(ValidationStateData data) + //{ + // var serializerSettings = new JsonSerializerSettings() + // { + // ContractResolver = ValidationStateDataContractResolver.Instance, + // Formatting = Formatting.Indented, + // TypeNameHandling = TypeNameHandling.Auto, + // Converters = new List() { new StringEnumConverter() } + // }; + + // CreateFileInPersistentCache(CacheConstants.ValidationResultFile, JsonConvert.SerializeObject(data, serializerSettings), true); + //} + + //public static bool GetCachedValidationStateData(out ValidationStateData data) + //{ + // return GetCachedValidationStateData(Constants.RootProjectPath, out data); + //} + + //public static bool GetCachedValidationStateData(string projectPath, out ValidationStateData data) + //{ + // data = null; + // var path = Path.Combine(projectPath, CacheConstants.PersistentCachePath, CacheConstants.ValidationResultFile); + // if (!File.Exists(path)) + // return false; + + // try + // { + // var serializerSettings = new JsonSerializerSettings() + // { + // ContractResolver = ValidationStateDataContractResolver.Instance, + // Formatting = Formatting.Indented, + // TypeNameHandling = TypeNameHandling.Auto, + // Converters = new List() { new StringEnumConverter() } + // }; + + // data = JsonConvert.DeserializeObject(File.ReadAllText(path, Encoding.UTF8), serializerSettings); + // return true; + // } + // catch (System.Exception e) + // { + // UnityEngine.Debug.LogException(e); + // return false; + // } + //} + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/CacheUtil.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/CacheUtil.cs.meta new file mode 100644 index 0000000..fca787f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/CacheUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e5fee0cad7655f458d9b600f4ae6d02 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/FileUtility.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/FileUtility.cs new file mode 100644 index 0000000..5e545f8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/FileUtility.cs @@ -0,0 +1,207 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; + +namespace AssetStoreTools.Utility +{ + internal static class FileUtility + { + private class RenameInfo + { + public string OriginalName; + public string CurrentName; + } + + public static string AbsolutePathToRelativePath(string path, bool allowSymlinks) + { + if (!File.Exists(path) && !Directory.Exists(path)) + return path; + + string convertedPath = path.Replace("\\", "/"); + + var allPackages = PackageUtility.GetAllPackages(); + foreach (var package in allPackages) + { + if (Path.GetFullPath(package.resolvedPath) != Path.GetFullPath(convertedPath) + && !Path.GetFullPath(convertedPath).StartsWith(Path.GetFullPath(package.resolvedPath) + Path.DirectorySeparatorChar)) + continue; + + convertedPath = Path.GetFullPath(convertedPath) + .Replace(Path.GetFullPath(package.resolvedPath), package.assetPath) + .Replace("\\", "/"); + + return convertedPath; + } + + if (convertedPath.StartsWith(Constants.RootProjectPath)) + { + convertedPath = convertedPath.Substring(Constants.RootProjectPath.Length); + } + else + { + if (allowSymlinks && SymlinkUtil.FindSymlinkFolderRelative(convertedPath, out var symlinkPath)) + convertedPath = symlinkPath; + } + + return convertedPath; + } + + public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive) + { + // Get information about the source directory + var dir = new DirectoryInfo(sourceDir); + + // Check if the source directory exists + if (!dir.Exists) + throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}"); + + // Cache directories before we start copying + DirectoryInfo[] dirs = dir.GetDirectories(); + + // Create the destination directory + Directory.CreateDirectory(destinationDir); + + // Get the files in the source directory and copy to the destination directory + foreach (FileInfo file in dir.GetFiles()) + { + string targetFilePath = Path.Combine(destinationDir, file.Name); + file.CopyTo(targetFilePath); + } + + // If recursive and copying subdirectories, recursively call this method + if (recursive) + { + foreach (DirectoryInfo subDir in dirs) + { + string newDestinationDir = Path.Combine(destinationDir, subDir.Name); + CopyDirectory(subDir.FullName, newDestinationDir, true); + } + } + } + + public static bool ShouldHaveMeta(string assetPath) + { + if (string.IsNullOrEmpty(assetPath)) + return false; + + // Meta files never have other metas + if (assetPath.EndsWith(".meta", System.StringComparison.OrdinalIgnoreCase)) + return false; + + // File System entries ending with '~' are hidden in the context of ADB + if (assetPath.EndsWith("~")) + return false; + + // File System entries whose names start with '.' are hidden in the context of ADB + var assetName = assetPath.Replace("\\", "/").Split('/').Last(); + if (assetName.StartsWith(".")) + return false; + + return true; + } + + public static bool IsMissingMetaFiles(IEnumerable sourcePaths) + { + foreach (var sourcePath in sourcePaths) + { + if (!Directory.Exists(sourcePath)) + continue; + + var allDirectories = Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories); + foreach (var dir in allDirectories) + { + var dirInfo = new DirectoryInfo(dir); + if (!dirInfo.Name.EndsWith("~")) + continue; + + var nestedContent = dirInfo.GetFileSystemInfos("*", SearchOption.AllDirectories); + foreach (var nested in nestedContent) + { + if (!ShouldHaveMeta(nested.FullName)) + continue; + + if (!File.Exists(nested.FullName + ".meta")) + return true; + } + } + } + + return false; + } + + public static void GenerateMetaFiles(IEnumerable sourcePaths) + { + var renameInfos = new List(); + + foreach (var sourcePath in sourcePaths) + { + if (!Directory.Exists(sourcePath)) + continue; + + var hiddenDirectoriesInPath = Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories).Where(x => x.EndsWith("~")); + foreach (var hiddenDir in hiddenDirectoriesInPath) + { + var hiddenDirRelative = AbsolutePathToRelativePath(hiddenDir, ASToolsPreferences.Instance.EnableSymlinkSupport); + if (!hiddenDirRelative.StartsWith("Assets/") && !hiddenDirRelative.StartsWith("Packages/")) + { + ASDebug.LogWarning($"Path {sourcePath} is not part of the Asset Database and will be skipped"); + continue; + } + + renameInfos.Add(new RenameInfo() { CurrentName = hiddenDirRelative, OriginalName = hiddenDirRelative }); + } + } + + if (renameInfos.Count == 0) + return; + + try + { + EditorApplication.LockReloadAssemblies(); + + // Order paths from longest to shortest to avoid having to rename them multiple times + renameInfos = renameInfos.OrderByDescending(x => x.OriginalName.Length).ToList(); + + try + { + AssetDatabase.StartAssetEditing(); + foreach (var renameInfo in renameInfos) + { + renameInfo.CurrentName = renameInfo.OriginalName.TrimEnd('~'); + Directory.Move(renameInfo.OriginalName, renameInfo.CurrentName); + } + } + finally + { + AssetDatabase.StopAssetEditing(); + AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); + AssetDatabase.ReleaseCachedFileHandles(); + } + + // Restore the original path names in reverse order + renameInfos = renameInfos.OrderBy(x => x.OriginalName.Length).ToList(); + + try + { + AssetDatabase.StartAssetEditing(); + foreach (var renameInfo in renameInfos) + { + Directory.Move(renameInfo.CurrentName, renameInfo.OriginalName); + if (File.Exists($"{renameInfo.CurrentName}.meta")) + File.Delete($"{renameInfo.CurrentName}.meta"); + } + } + finally + { + AssetDatabase.StopAssetEditing(); + AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); + } + } + finally + { + EditorApplication.UnlockReloadAssemblies(); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/FileUtility.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/FileUtility.cs.meta new file mode 100644 index 0000000..db285bb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/FileUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 80819cf6868374d478a8a38ebaba8e27 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/LegacyToolsRemover.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/LegacyToolsRemover.cs new file mode 100644 index 0000000..c9df7c2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/LegacyToolsRemover.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Utility +{ + [InitializeOnLoad] + internal class LegacyToolsRemover + { + private const string MessagePart1 = "A legacy version of Asset Store Tools " + + "was detected at the following path:\n"; + private const string MessagePart2 = "\n\nHaving both the legacy and the latest version installed at the same time is not supported " + + "and might prevent the latest version from functioning properly.\n\nWould you like the legacy version to be removed automatically?"; + + static LegacyToolsRemover() + { + try + { + if (Application.isBatchMode) + return; + + CheckAndRemoveLegacyTools(); + } + catch { } + } + + private static void CheckAndRemoveLegacyTools() + { + if (!ASToolsPreferences.Instance.LegacyVersionCheck || !ProjectContainsLegacyTools(out string path)) + return; + + var relativePath = path.Substring(Application.dataPath.Length - "Assets".Length).Replace("\\", "/"); + var result = EditorUtility.DisplayDialog("Asset Store Tools", MessagePart1 + relativePath + MessagePart2, "Yes", "No"); + + // If "No" - do nothing + if (!result) + return; + + // If "Yes" - remove legacy tools + File.Delete(path); + File.Delete(path + ".meta"); + RemoveEmptyFolders(Path.GetDirectoryName(path)?.Replace("\\", "/")); + AssetDatabase.Refresh(); + + // We could also optionally prevent future execution here + // but the ProjectContainsLegacyTools() function runs in less + // than a milisecond on an empty project + } + + private static bool ProjectContainsLegacyTools(out string path) + { + path = null; + + foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + if (assembly.ManifestModule.Name == "AssetStoreTools.dll") + { + path = assembly.Location; + break; + } + } + + if (string.IsNullOrEmpty(path)) + return false; + return true; + } + + private static void RemoveEmptyFolders(string directory) + { + if (directory.EndsWith(Application.dataPath)) + return; + + if (Directory.GetFiles(directory).Length == 0 && Directory.GetDirectories(directory).Length == 0) + { + var parentPath = Path.GetDirectoryName(directory).Replace("\\", "/"); + + Directory.Delete(directory); + File.Delete(directory + ".meta"); + + RemoveEmptyFolders(parentPath); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/LegacyToolsRemover.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/LegacyToolsRemover.cs.meta new file mode 100644 index 0000000..c16ca0a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/LegacyToolsRemover.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 46ead42026b1f0b4e94153e1a7e10d12 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/PackageUtility.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/PackageUtility.cs new file mode 100644 index 0000000..a384f7a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/PackageUtility.cs @@ -0,0 +1,170 @@ +#if !UNITY_2021_1_OR_NEWER +using System; +using System.Reflection; +#endif +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEditor.PackageManager; +using UnityEngine; +using PackageInfo = UnityEditor.PackageManager.PackageInfo; + +namespace AssetStoreTools.Utility +{ + internal static class PackageUtility + { + public class PackageInfoSampleMetadata + { + public string DisplayName; + public string Description; + public string Path; + } + + public class PackageInfoUnityVersionMetadata + { + /// + /// Major bit of the Unity version, e.g. 2021.3 + /// + public string Version; + /// + /// Minor bit of the Unity version, e.g. 0f1 + /// + public string Release; + + public override string ToString() + { + if (string.IsNullOrEmpty(Version)) + return Release; + + if (string.IsNullOrEmpty(Release)) + return Release; + + return $"{Version}.{Release}"; + } + } + + /// + /// Returns a package identifier, consisting of package name and package version + /// + /// + /// + public static string GetPackageIdentifier(this PackageInfo package) + { + return $"{package.name}-{package.version}"; + } + + public static PackageInfo[] GetAllPackages() + { +#if !UNITY_2021_1_OR_NEWER + var method = typeof(PackageInfo).GetMethod("GetAll", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, new Type[0], null); + var packages = method?.Invoke(null, null) as PackageInfo[]; +#else + var packages = PackageInfo.GetAllRegisteredPackages(); +#endif + return packages; + } + + public static PackageInfo[] GetAllLocalPackages() + { + var packages = GetAllPackages(); + var localPackages = packages.Where(x => x.source == PackageSource.Embedded || x.source == PackageSource.Local) + .Where(x => x.isDirectDependency).ToArray(); + return localPackages; + } + + public static PackageInfo[] GetAllRegistryPackages() + { + var packages = GetAllPackages(); + var registryPackages = packages.Where(x => x.source == PackageSource.Registry || x.source == PackageSource.BuiltIn) + .OrderBy(x => string.Compare(x.type, "module", System.StringComparison.OrdinalIgnoreCase) == 0) + .ThenBy(x => x.name).ToArray(); + + return registryPackages; + } + + public static bool GetPackageByManifestPath(string packageManifestPath, out PackageInfo package) + { + package = null; + + if (string.IsNullOrEmpty(packageManifestPath)) + return false; + + var fileInfo = new FileInfo(packageManifestPath); + if (!fileInfo.Exists) + return false; + + var allPackages = GetAllPackages(); + + package = allPackages.FirstOrDefault(x => Path.GetFullPath(x.resolvedPath).Equals(fileInfo.Directory.FullName)); + return package != null; + } + + public static bool GetPackageByPackageName(string packageName, out PackageInfo package) + { + package = null; + + if (string.IsNullOrEmpty(packageName)) + return false; + + return GetPackageByManifestPath($"Packages/{packageName}/package.json", out package); + } + + public static TextAsset GetManifestAsset(this PackageInfo packageInfo) + { + return AssetDatabase.LoadAssetAtPath($"{packageInfo.assetPath}/package.json"); + } + + public static List GetSamples(this PackageInfo packageInfo) + { + var samples = new List(); + + var packageManifest = packageInfo.GetManifestAsset(); + var json = JObject.Parse(packageManifest.text); + + if (!json.ContainsKey("samples") || json["samples"].Type != JTokenType.Array) + return samples; + + var sampleList = json["samples"].ToList(); + foreach (JObject sample in sampleList) + { + var displayName = string.Empty; + var description = string.Empty; + var path = string.Empty; + + if (sample.ContainsKey("displayName")) + displayName = sample["displayName"].ToString(); + if (sample.ContainsKey("description")) + description = sample["description"].ToString(); + if (sample.ContainsKey("path")) + path = sample["path"].ToString(); + + if (!string.IsNullOrEmpty(displayName) || !string.IsNullOrEmpty(description) || !string.IsNullOrEmpty(path)) + samples.Add(new PackageInfoSampleMetadata() { DisplayName = displayName, Description = description, Path = path }); + } + + return samples; + } + + public static PackageInfoUnityVersionMetadata GetUnityVersion(this PackageInfo packageInfo) + { + var packageManifest = packageInfo.GetManifestAsset(); + var json = JObject.Parse(packageManifest.text); + + var unityVersion = string.Empty; + var unityRelease = string.Empty; + + if (json.ContainsKey("unity")) + unityVersion = json["unity"].ToString(); + if (json.ContainsKey("unityRelease")) + unityRelease = json["unityRelease"].ToString(); + + return new PackageInfoUnityVersionMetadata() + { + Version = unityVersion, + Release = unityRelease + }; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/PackageUtility.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/PackageUtility.cs.meta new file mode 100644 index 0000000..6ce8b3d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/PackageUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 605ea62f8b11d674a95a53f895df4c67 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ServiceProvider.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ServiceProvider.cs new file mode 100644 index 0000000..9751081 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ServiceProvider.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AssetStoreTools.Utility +{ + internal abstract class ServiceProvider + { + private Dictionary _services = new Dictionary(); + private Dictionary> _queuedServices = new Dictionary>(); + + protected class MissingServiceDependencyException : Exception + { + public Type ServiceType { get; private set; } + public Type MissingDependencyType { get; private set; } + + public MissingServiceDependencyException(Type serviceType, Type missingDependencyType) + { + ServiceType = serviceType; + MissingDependencyType = missingDependencyType; + } + } + + protected ServiceProvider() + { + RegisterServices(); + CreateRegisteredServices(); + } + + protected abstract void RegisterServices(); + + protected void Register() where TService : Service where TInstance : TService + { + Register(() => CreateServiceInstance(typeof(TInstance))); + } + + protected void Register(Func initializer) where TService : Service + { + _queuedServices.Add(typeof(TService), initializer); + } + + private void CreateRegisteredServices() + { + if (_queuedServices.Count == 0) + return; + + var createdAnyService = false; + var missingServices = new List(); + + foreach (var service in _queuedServices) + { + try + { + var instance = service.Value.Invoke(); + _services.Add(service.Key, instance); + createdAnyService = true; + } + catch (MissingServiceDependencyException e) + { + missingServices.Add(e); + } + } + + foreach (var createdService in _services) + { + _queuedServices.Remove(createdService.Key); + } + + if (!createdAnyService) + { + var message = string.Join(", ", missingServices.Select(x => $"{x.ServiceType} depends on {x.MissingDependencyType}")); + throw new Exception("Could not create the following services due to missing dependencies: " + message); + } + + // Recursively register remaining queued services that may have failed + // due to missing depenedencies that are now registered + CreateRegisteredServices(); + } + + private Service CreateServiceInstance(Type concreteType) + { + if (concreteType.IsAbstract) + throw new Exception($"Cannot create an instance of an abstract class {concreteType}"); + + var constructor = concreteType.GetConstructors().First(); + var expectedParameters = constructor.GetParameters(); + var parametersToUse = new List(); + + foreach (var parameter in expectedParameters) + { + if (!_services.ContainsKey(parameter.ParameterType)) + throw new MissingServiceDependencyException(concreteType, parameter.ParameterType); + + parametersToUse.Add(_services[parameter.ParameterType]); + } + + return (Service)constructor.Invoke(parametersToUse.ToArray()); + } + + public T GetService() where T : Service + { + return (T)GetService(typeof(T)); + } + + public object GetService(Type type) + { + if (!_services.ContainsKey(type)) + throw new Exception($"Service of type {type} is not registered"); + + return _services[type]; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ServiceProvider.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ServiceProvider.cs.meta new file mode 100644 index 0000000..847bd4b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/ServiceProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2fcadafa6431d1647a82d35e6e4a13c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/StyleSelector.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/StyleSelector.cs new file mode 100644 index 0000000..1076bca --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/StyleSelector.cs @@ -0,0 +1,55 @@ +using System; +using UnityEditor; +using UnityEngine.UIElements; +using WindowStyles = AssetStoreTools.Constants.WindowStyles; + +namespace AssetStoreTools.Utility +{ + internal static class StyleSelector + { + private static StyleSheet GetStylesheet(string rootPath, string filePath) + { + var path = $"{rootPath}/{filePath}.uss"; + var sheet = AssetDatabase.LoadAssetAtPath(path); + if (sheet == null) + throw new Exception($"Stylesheet '{path}' was not found"); + return sheet; + } + + private static StyleSheet GetStylesheetTheme(string rootPath, string filePath) + { + var suffix = !EditorGUIUtility.isProSkin ? "Light" : "Dark"; + return GetStylesheet(rootPath, filePath + suffix); + } + + public static class UploaderWindow + { + public static StyleSheet UploaderWindowStyle => GetStylesheet(WindowStyles.UploaderStylesPath, "Style"); + public static StyleSheet UploaderWindowTheme => GetStylesheetTheme(WindowStyles.UploaderStylesPath, "Theme"); + + public static StyleSheet LoginViewStyle => GetStylesheet(WindowStyles.UploaderStylesPath, "LoginView/Style"); + public static StyleSheet LoginViewTheme => GetStylesheetTheme(WindowStyles.UploaderStylesPath, "LoginView/Theme"); + + public static StyleSheet PackageListViewStyle => GetStylesheet(WindowStyles.UploaderStylesPath, "PackageListView/Style"); + public static StyleSheet PackageListViewTheme => GetStylesheetTheme(WindowStyles.UploaderStylesPath, "PackageListView/Theme"); + } + + public static class ValidatorWindow + { + public static StyleSheet ValidatorWindowStyle => GetStylesheet(WindowStyles.ValidatorStylesPath, "Style"); + public static StyleSheet ValidatorWindowTheme => GetStylesheetTheme(WindowStyles.ValidatorStylesPath, "Theme"); + } + + public static class PreviewGeneratorWindow + { + public static StyleSheet PreviewGeneratorWindowStyle => GetStylesheet(WindowStyles.PreviewGeneratorStylesPath, "Style"); + public static StyleSheet PreviewGeneratorWindowTheme => GetStylesheetTheme(WindowStyles.PreviewGeneratorStylesPath, "Theme"); + } + + public static class UpdaterWindow + { + public static StyleSheet UpdaterWindowStyle => GetStylesheet(WindowStyles.UpdaterStylesPath, "Style"); + public static StyleSheet UpdaterWindowTheme => GetStylesheetTheme(WindowStyles.UpdaterStylesPath, "Theme"); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/StyleSelector.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/StyleSelector.cs.meta new file mode 100644 index 0000000..d4ab01c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/StyleSelector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b066ce502a289a4ca311a86fbf83f45 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles.meta new file mode 100644 index 0000000..0175b81 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f730eb0b8c48c434d93cc60a0b8aff40 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater.meta new file mode 100644 index 0000000..315b99b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5367435d9abe935438f4d7b588a55488 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/Style.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/Style.uss new file mode 100644 index 0000000..1ee89cd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/Style.uss @@ -0,0 +1,76 @@ +.updater-loading-container { + flex-grow: 1; + align-items: center; + justify-content: center; +} + +.updater-loading-container > Image { + width: 16px; + height: 16px; +} + +.updater-info-container { + flex-grow: 1; + margin: 0 5px 5px 5px; +} + +.updater-info-container-labels { + flex-grow: 1; + margin-bottom: 10px; + margin-top: 5px; +} + +.updater-info-container-labels-description { + flex-grow: 0.5; + margin-bottom: 5px; + white-space: normal; + -unity-text-align: middle-left; +} + +.updater-info-container-labels-row { + flex-direction: row; +} + +.updater-info-container-labels-row-identifier { + -unity-font-style: bold; +} + +.updater-info-container-buttons { + flex-direction: row; + margin-bottom: 5px; +} + +.updater-info-container-buttons > Button { + flex-grow: 1; + flex-shrink: 1; + flex-basis: 100%; + height: 25px; +} + +.updater-info-container-toggle { + align-self: flex-end; +} + +.updater-info-container-toggle > Toggle > VisualElement > Label { + margin-left: 5px; +} + +.updater-fail-container { + flex-grow: 1; + flex-direction: row; + margin: 0 5px 5px 5px; + justify-content: center; + align-items: center; +} + +.updater-fail-container > Image { + flex-shrink: 0; + width: 36px; + height: 36px; + margin-right: 5px; +} + +.updater-fail-container > Label { + flex-shrink: 1; + white-space: normal; +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/Style.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/Style.uss.meta new file mode 100644 index 0000000..3ba3542 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/Style.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 23112eed1f211274c94028490f81007c +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeDark.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeDark.uss new file mode 100644 index 0000000..3510f8a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeDark.uss @@ -0,0 +1,3 @@ +.updater-fail-container > Image { + --unity-image: resource("console.erroricon@2x"); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeDark.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeDark.uss.meta new file mode 100644 index 0000000..9fad31b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeDark.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0cbf43b8dabcd1242b32ed3ed2167a54 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeLight.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeLight.uss new file mode 100644 index 0000000..3510f8a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeLight.uss @@ -0,0 +1,3 @@ +.updater-fail-container > Image { + --unity-image: resource("console.erroricon@2x"); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeLight.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeLight.uss.meta new file mode 100644 index 0000000..0cdf6fd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater/ThemeLight.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d453bb92cd1f35943b1c5f652837ada9 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/SymlinkUtil.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/SymlinkUtil.cs new file mode 100644 index 0000000..93da1ee --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/SymlinkUtil.cs @@ -0,0 +1,67 @@ +using System.IO; + +namespace AssetStoreTools.Utility +{ + internal static class SymlinkUtil + { + private const FileAttributes FolderSymlinkAttributes = FileAttributes.Directory | FileAttributes.ReparsePoint; + + public static bool FindSymlinkFolderRelative(string folderPathAbsolute, out string relativePath) + { + // Get directory info for path outside of the project + var absoluteInfo = new DirectoryInfo(folderPathAbsolute); + + // Get all directories within the project + var allFolderPaths = Directory.GetDirectories("Assets", "*", SearchOption.AllDirectories); + foreach (var path in allFolderPaths) + { + var fullPath = path.Replace("\\", "/"); + + // Get directory info for one of the paths within the project + var relativeInfo = new DirectoryInfo(fullPath); + + // Check if project's directory is a symlink + if (!relativeInfo.Attributes.HasFlag(FolderSymlinkAttributes)) + continue; + + // Compare metadata of outside directory with a directories within the project + if (!CompareDirectories(absoluteInfo, relativeInfo)) + continue; + + // Found symlink within the project, assign it + relativePath = fullPath; + return true; + } + + relativePath = string.Empty; + return false; + } + + private static bool CompareDirectories(DirectoryInfo directory, DirectoryInfo directory2) + { + var contents = directory.EnumerateFileSystemInfos("*", SearchOption.AllDirectories).GetEnumerator(); + var contents2 = directory2.EnumerateFileSystemInfos("*", SearchOption.AllDirectories).GetEnumerator(); + + while (true) + { + var firstNext = contents.MoveNext(); + var secondNext = contents2.MoveNext(); + + if (firstNext != secondNext) + return false; + + if (!firstNext && !secondNext) + break; + + var equals = contents.Current?.Name == contents2.Current?.Name + && contents.Current?.LastWriteTime == contents2.Current?.LastWriteTime; + + if (!equals) + return false; + } + + return true; + } + + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/SymlinkUtil.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/SymlinkUtil.cs.meta new file mode 100644 index 0000000..43c8aaf --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Utility/SymlinkUtil.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 92092535fd064bb1843017f98db213e1 +timeCreated: 1659013521 \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator.meta new file mode 100644 index 0000000..e27c1ef --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 980c7bb65c02d464684c2220c57fcd75 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons.meta new file mode 100644 index 0000000..7f4c321 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8490c57c02b441e4dab99565da835c99 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error.png new file mode 100644 index 0000000..8d294bc Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error.png.meta new file mode 100644 index 0000000..4217052 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error.png.meta @@ -0,0 +1,128 @@ +fileFormatVersion: 2 +guid: 0cc0ccdb7de3e964ab553ce3c299d83c +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: 2 + aniso: 0 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 0 + 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: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error_d.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error_d.png new file mode 100644 index 0000000..451f640 Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error_d.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error_d.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error_d.png.meta new file mode 100644 index 0000000..5ccdf26 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/error_d.png.meta @@ -0,0 +1,128 @@ +fileFormatVersion: 2 +guid: cdf8d51df19d58341886cc474e810c7b +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: 2 + aniso: 0 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 0 + 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: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success.png new file mode 100644 index 0000000..a5f7853 Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success.png.meta new file mode 100644 index 0000000..c08b383 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success.png.meta @@ -0,0 +1,128 @@ +fileFormatVersion: 2 +guid: 832e106a677623145b3d8dbe015e31a0 +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: 2 + aniso: 0 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 0 + 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: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success_d.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success_d.png new file mode 100644 index 0000000..094a810 Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success_d.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success_d.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success_d.png.meta new file mode 100644 index 0000000..6741130 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/success_d.png.meta @@ -0,0 +1,128 @@ +fileFormatVersion: 2 +guid: 3dc139a2b2a28a54a8f39e266fc0af9c +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: 2 + aniso: 0 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 0 + 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: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined.png new file mode 100644 index 0000000..a587baa Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined.png.meta new file mode 100644 index 0000000..47db8b0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined.png.meta @@ -0,0 +1,128 @@ +fileFormatVersion: 2 +guid: 4a4eef842709db34cbb71baf22384730 +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: 2 + aniso: 0 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 0 + 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: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined_d.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined_d.png new file mode 100644 index 0000000..b7e2681 Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined_d.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined_d.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined_d.png.meta new file mode 100644 index 0000000..029753f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/undefined_d.png.meta @@ -0,0 +1,128 @@ +fileFormatVersion: 2 +guid: 88a36c7e4d60b6b4385c95cfc2d00c22 +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: 2 + aniso: 0 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 0 + 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: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning.png new file mode 100644 index 0000000..aab9fee Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning.png.meta new file mode 100644 index 0000000..05dd39d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning.png.meta @@ -0,0 +1,128 @@ +fileFormatVersion: 2 +guid: 83d0e58aa5f608a4b8232fbacca5ca89 +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: 2 + aniso: 0 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 0 + 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: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning_d.png b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning_d.png new file mode 100644 index 0000000..553bb16 Binary files /dev/null and b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning_d.png differ diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning_d.png.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning_d.png.meta new file mode 100644 index 0000000..3e0843e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Icons/warning_d.png.meta @@ -0,0 +1,128 @@ +fileFormatVersion: 2 +guid: d27d359f48fa1a14e9e4f02196589805 +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: 2 + aniso: 0 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 0 + 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: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts.meta new file mode 100644 index 0000000..5a81535 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1b5ff7c95381e82438f6c9dc40069031 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories.meta new file mode 100644 index 0000000..0c29f2c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7a971a9a200a4438945853d71066f16a +timeCreated: 1657617558 \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/CategoryEvaluator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/CategoryEvaluator.cs new file mode 100644 index 0000000..ba9acc2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/CategoryEvaluator.cs @@ -0,0 +1,52 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.TestDefinitions; + +namespace AssetStoreTools.Validator.Categories +{ + internal class CategoryEvaluator + { + private string _category; + + public CategoryEvaluator(string category) + { + if (string.IsNullOrEmpty(category)) + _category = string.Empty; + else + _category = category; + } + + public void SetCategory(string category) + { + if (category == null) + _category = string.Empty; + else + _category = category; + } + + public string GetCategory() + { + return _category; + } + + public TestResultStatus Evaluate(ValidationTest validation, bool slugify = false) + { + var result = validation.Result.Status; + if (result != TestResultStatus.VariableSeverityIssue) + return result; + + var category = _category; + + if (slugify) + category = validation.Slugify(category); + + return validation.CategoryInfo.EvaluateByFilter(category); + } + +#if AB_BUILDER + public TestResultStatus EvaluateAndSlugify(ValidationTest validation) + { + return Evaluate(validation, true); + } +#endif + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/CategoryEvaluator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/CategoryEvaluator.cs.meta new file mode 100644 index 0000000..3bc124c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/CategoryEvaluator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: eb61fd62b94248e4b5a3a07665b1a2bf +timeCreated: 1661420659 \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/ValidatorCategory.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/ValidatorCategory.cs new file mode 100644 index 0000000..631ef47 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/ValidatorCategory.cs @@ -0,0 +1,38 @@ +using AssetStoreTools.Validator.Data; +using System; +using System.Linq; + +namespace AssetStoreTools.Validator.Categories +{ + [System.Serializable] + internal class ValidatorCategory + { + public bool IsFailFilter = false; + public bool IsInclusiveFilter = true; + public bool AppliesToSubCategories = true; + public string[] Filter = { "Tools", "Art" }; + + public TestResultStatus EvaluateByFilter(string category) + { + if (AppliesToSubCategories) + category = category.Split('/')[0]; + + var isCategoryInFilter = Filter.Any(x => String.Compare(x, category, StringComparison.OrdinalIgnoreCase) == 0); + + if (IsInclusiveFilter) + { + if (isCategoryInFilter) + return IsFailFilter ? TestResultStatus.Fail : TestResultStatus.Warning; + else + return IsFailFilter ? TestResultStatus.Warning : TestResultStatus.Fail; + } + else + { + if (isCategoryInFilter) + return IsFailFilter ? TestResultStatus.Warning : TestResultStatus.Fail; + else + return IsFailFilter ? TestResultStatus.Fail : TestResultStatus.Warning; + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/ValidatorCategory.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/ValidatorCategory.cs.meta new file mode 100644 index 0000000..2690769 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Categories/ValidatorCategory.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a5e60d3639f24063a4eabc21ea1a04a9 +timeCreated: 1657617578 \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/CurrentProjectValidator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/CurrentProjectValidator.cs new file mode 100644 index 0000000..79ef18c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/CurrentProjectValidator.cs @@ -0,0 +1,71 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.TestDefinitions; +using System; +using System.IO; + +namespace AssetStoreTools.Validator +{ + internal class CurrentProjectValidator : ValidatorBase + { + private CurrentProjectValidationSettings _settings; + + public CurrentProjectValidator(CurrentProjectValidationSettings settings) : base(settings) + { + _settings = settings; + } + + protected override void ValidateSettings() + { + if (_settings == null) + throw new Exception("Validation Settings is null"); + + if (_settings.ValidationPaths == null + || _settings.ValidationPaths.Count == 0) + throw new Exception("No validation paths were set"); + + switch (_settings.ValidationType) + { + case ValidationType.Generic: + case ValidationType.UnityPackage: + ValidateUnityPackageSettings(); + break; + default: + throw new NotImplementedException("Undefined validation type"); + } + } + + private void ValidateUnityPackageSettings() + { + var invalidPaths = string.Empty; + foreach (var path in _settings.ValidationPaths) + { + if (!Directory.Exists(path)) + invalidPaths += $"\n{path}"; + } + + if (!string.IsNullOrEmpty(invalidPaths)) + throw new Exception("The following directories do not exist:" + invalidPaths); + } + + protected override ValidationResult GenerateValidationResult() + { + ITestConfig config; + var applicableTests = GetApplicableTests(ValidationType.Generic); + switch (_settings.ValidationType) + { + case ValidationType.Generic: + config = new GenericTestConfig() { ValidationPaths = _settings.ValidationPaths.ToArray() }; + break; + case ValidationType.UnityPackage: + applicableTests.AddRange(GetApplicableTests(ValidationType.UnityPackage)); + config = new GenericTestConfig() { ValidationPaths = _settings.ValidationPaths.ToArray() }; + break; + default: + return new ValidationResult() { Status = ValidationStatus.Failed, Exception = new Exception("Undefined validation type") }; + } + + var validationResult = RunTests(applicableTests, config); + return validationResult; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/CurrentProjectValidator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/CurrentProjectValidator.cs.meta new file mode 100644 index 0000000..ca8fe3d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/CurrentProjectValidator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3a6371dcfa8545c478545b4a43433599 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data.meta new file mode 100644 index 0000000..3b40063 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1c2a38ded8e054c4088aff1db7224f66 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/CurrentProjectValidationSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/CurrentProjectValidationSettings.cs new file mode 100644 index 0000000..6b83057 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/CurrentProjectValidationSettings.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; + +namespace AssetStoreTools.Validator.Data +{ + internal class CurrentProjectValidationSettings : ValidationSettings + { + public List ValidationPaths; + public ValidationType ValidationType; + + public CurrentProjectValidationSettings() + { + Category = string.Empty; + ValidationPaths = new List(); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (obj == null || obj.GetType() != typeof(CurrentProjectValidationSettings)) + return false; + + var other = (CurrentProjectValidationSettings)obj; + return Category == other.Category + && ValidationType == other.ValidationType + && ValidationPaths.OrderBy(x => x).SequenceEqual(other.ValidationPaths.OrderBy(x => x)); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/CurrentProjectValidationSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/CurrentProjectValidationSettings.cs.meta new file mode 100644 index 0000000..cf2630a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/CurrentProjectValidationSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e4a4a4aa3f501847b1abb1e08505f9b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ExternalProjectValidationSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ExternalProjectValidationSettings.cs new file mode 100644 index 0000000..f770cd4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ExternalProjectValidationSettings.cs @@ -0,0 +1,7 @@ +namespace AssetStoreTools.Validator.Data +{ + internal class ExternalProjectValidationSettings : ValidationSettings + { + public string PackagePath; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ExternalProjectValidationSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ExternalProjectValidationSettings.cs.meta new file mode 100644 index 0000000..a7a30e0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ExternalProjectValidationSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f79c895f4bb099b4983dd20eef72a7bd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions.meta new file mode 100644 index 0000000..7f0bd9a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d51c5c866dcd449488caa10a40dd3301 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/HighlightObjectAction.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/HighlightObjectAction.cs new file mode 100644 index 0000000..e642904 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/HighlightObjectAction.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.Data.MessageActions +{ + internal class HighlightObjectAction : IMessageAction + { + public string Tooltip => "Click to highlight the associated object in Hierarchy/Project view"; + public Object Target => _target?.GetObject(); + + [JsonProperty] + private TestResultObject _target; + + public HighlightObjectAction() { } + + public HighlightObjectAction(Object target) + { + _target = new TestResultObject(target); + } + + public void Execute() + { + var targetObject = _target.GetObject(); + if (targetObject == null) + return; + + EditorGUIUtility.PingObject(targetObject); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/HighlightObjectAction.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/HighlightObjectAction.cs.meta new file mode 100644 index 0000000..f9d8e9c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/HighlightObjectAction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de24c0a7f8a22c142a224e6abd0ddc68 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/IMessageAction.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/IMessageAction.cs new file mode 100644 index 0000000..ec48b5b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/IMessageAction.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using UnityEngine; + +namespace AssetStoreTools.Validator.Data.MessageActions +{ + internal interface IMessageAction + { + [JsonIgnore] + string Tooltip { get; } + + [JsonIgnore] + Object Target { get; } + + void Execute(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/IMessageAction.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/IMessageAction.cs.meta new file mode 100644 index 0000000..db2e565 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/IMessageAction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f1636d7241abdf1498368f841aa818a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/OpenAssetAction.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/OpenAssetAction.cs new file mode 100644 index 0000000..fb5dfa5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/OpenAssetAction.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.Data.MessageActions +{ + internal class OpenAssetAction : IMessageAction + { + public string Tooltip => "Click to open the associated asset"; + public Object Target => _target?.GetObject(); + + [JsonProperty] + private TestResultObject _target; + [JsonProperty] + private int _lineNumber; + + public OpenAssetAction() { } + + public OpenAssetAction(Object target) + { + _target = new TestResultObject(target); + } + + public OpenAssetAction(Object target, int lineNumber) : this(target) + { + _lineNumber = lineNumber; + } + + public void Execute() + { + var targetObject = _target.GetObject(); + if (targetObject == null) + return; + + AssetDatabase.OpenAsset(targetObject, _lineNumber); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/OpenAssetAction.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/OpenAssetAction.cs.meta new file mode 100644 index 0000000..83519a6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/MessageActions/OpenAssetAction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9fb4fec293bf73f4a8f870c535750613 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResult.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResult.cs new file mode 100644 index 0000000..21d1dd0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResult.cs @@ -0,0 +1,52 @@ +using AssetStoreTools.Validator.Data.MessageActions; +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace AssetStoreTools.Validator.Data +{ + internal struct TestResult + { + public TestResultStatus Status; + + [JsonProperty] + private List _messages; + + [JsonIgnore] + public int MessageCount => _messages != null ? _messages.Count : 0; + + public TestResultMessage GetMessage(int index) + { + return _messages[index]; + } + + public void AddMessage(string msg) + { + AddMessage(msg, null, null); + } + + public void AddMessage(string msg, IMessageAction clickAction) + { + AddMessage(msg, clickAction, null); + } + + public void AddMessage(string msg, IMessageAction clickAction, params UnityEngine.Object[] messageObjects) + { + if (_messages == null) + _messages = new List(); + + var message = new TestResultMessage(msg, clickAction); + _messages.Add(message); + + if (messageObjects == null) + return; + + foreach (var obj in messageObjects) + { + if (obj == null) + continue; + + message.AddMessageObject(obj); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResult.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResult.cs.meta new file mode 100644 index 0000000..ec1467f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 05d7d92bbda6bf44f8ed5fbd0cde57e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultMessage.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultMessage.cs new file mode 100644 index 0000000..7ebe0f5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultMessage.cs @@ -0,0 +1,53 @@ +using AssetStoreTools.Validator.Data.MessageActions; +using Newtonsoft.Json; +using System.Collections.Generic; +using Object = UnityEngine.Object; + +namespace AssetStoreTools.Validator.Data +{ + internal class TestResultMessage + { + [JsonIgnore] + public int MessageObjectCount => _messageObjects.Count; + + [JsonProperty] + private string _text; + [JsonProperty] + private List _messageObjects; + [JsonProperty] + private IMessageAction _clickAction; + + public TestResultMessage() { } + + public TestResultMessage(string text) + { + _text = text; + _messageObjects = new List(); + } + + public TestResultMessage(string text, IMessageAction clickAction) : this(text) + { + _clickAction = clickAction; + } + + public string GetText() + { + return _text; + } + + public IMessageAction GetClickAction() + { + return _clickAction; + } + + public void AddMessageObject(Object obj) + { + _messageObjects.Add(new TestResultObject(obj)); + } + + public TestResultObject GetMessageObject(int index) + { + return _messageObjects[index]; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultMessage.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultMessage.cs.meta new file mode 100644 index 0000000..2f0262f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0761356c44140ca49917f93b42926471 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultObject.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultObject.cs new file mode 100644 index 0000000..8b601d5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultObject.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.Data +{ + internal class TestResultObject + { + [JsonIgnore] + private Object _object; + [JsonProperty] + private string _objectGlobalId; + + public TestResultObject(Object obj) + { + _object = obj; + _objectGlobalId = GlobalObjectId.GetGlobalObjectIdSlow(obj).ToString(); + } + + public Object GetObject() + { + if (_object != null) + return _object; + + if (string.IsNullOrEmpty(_objectGlobalId)) + return null; + + if (!GlobalObjectId.TryParse(_objectGlobalId, out var globalObject)) + return null; + + _object = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalObject); + return _object; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultObject.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultObject.cs.meta new file mode 100644 index 0000000..a688e88 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: acce8e477b7fe2c4aa430ebdd65ea7d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultStatus.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultStatus.cs new file mode 100644 index 0000000..ccda879 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultStatus.cs @@ -0,0 +1,11 @@ +namespace AssetStoreTools.Validator.Data +{ + internal enum TestResultStatus + { + Undefined = 0, + Pass = 1, + Fail = 2, + Warning = 3, + VariableSeverityIssue = 4 + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultStatus.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultStatus.cs.meta new file mode 100644 index 0000000..7e7401a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/TestResultStatus.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eef1ba0cf35f1304d8929e23b94e7c23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationResult.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationResult.cs new file mode 100644 index 0000000..89ae633 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationResult.cs @@ -0,0 +1,24 @@ +using AssetStoreTools.Validator.TestDefinitions; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Validator.Data +{ + internal class ValidationResult + { + public ValidationStatus Status; + public bool HadCompilationErrors; + public string ProjectPath; + public List Tests; + public Exception Exception; + + public ValidationResult() + { + Status = ValidationStatus.NotRun; + HadCompilationErrors = false; + ProjectPath = string.Empty; + Tests = new List(); + Exception = null; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationResult.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationResult.cs.meta new file mode 100644 index 0000000..da48108 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b15525b8dcf3e654ca2f895472ab7cb1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationSettings.cs new file mode 100644 index 0000000..5b51134 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationSettings.cs @@ -0,0 +1,7 @@ +namespace AssetStoreTools.Validator.Data +{ + internal abstract class ValidationSettings + { + public string Category; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationSettings.cs.meta new file mode 100644 index 0000000..7783188 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 33e99d6b6e1e7ef4abd6cd2c0137741a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationStatus.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationStatus.cs new file mode 100644 index 0000000..f0fb66c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationStatus.cs @@ -0,0 +1,10 @@ +namespace AssetStoreTools.Validator.Data +{ + internal enum ValidationStatus + { + NotRun, + RanToCompletion, + Failed, + Cancelled + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationStatus.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationStatus.cs.meta new file mode 100644 index 0000000..32b6657 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationStatus.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a1f1e1e94faa6284f8d71804ba2bbd24 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationType.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationType.cs new file mode 100644 index 0000000..dd3b51c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationType.cs @@ -0,0 +1,8 @@ +namespace AssetStoreTools.Validator.Data +{ + internal enum ValidationType + { + Generic = 0, + UnityPackage = 1 + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationType.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationType.cs.meta new file mode 100644 index 0000000..dca424c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Data/ValidationType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 079f8963464230145853d86eff935e04 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ExternalProjectValidator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ExternalProjectValidator.cs new file mode 100644 index 0000000..3556d1a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ExternalProjectValidator.cs @@ -0,0 +1,259 @@ +using AssetStoreTools.Utility; +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator +{ + internal class ExternalProjectValidator : ValidatorBase + { + private ExternalProjectValidationSettings _settings; + + public ExternalProjectValidator(ExternalProjectValidationSettings settings) : base(settings) + { + _settings = settings; + } + + protected override void ValidateSettings() + { + if (_settings == null) + throw new Exception("Validation Settings is null"); + + if (string.IsNullOrEmpty(_settings.PackagePath) + || !File.Exists(_settings.PackagePath)) + throw new Exception("Package was not found"); + } + + protected override ValidationResult GenerateValidationResult() + { + bool interactiveMode = false; + try + { + // Step 1 - prepare a temporary project + var result = PrepareTemporaryValidationProject(interactiveMode); + + // If preparation was cancelled or setting up project failed - return immediately + if (result.Status == ValidationStatus.Cancelled || result.Status == ValidationStatus.Failed) + return result; + + // Step 2 - load the temporary project and validate the package + result = ValidateTemporaryValidationProject(result, interactiveMode); + + // Step 3 - copy validation results + result = ParseValidationResult(result.ProjectPath); + + return result; + } + catch (Exception e) + { + return new ValidationResult() { Status = ValidationStatus.Failed, Exception = e }; + } + finally + { + EditorUtility.ClearProgressBar(); + } + } + + private ValidationResult PrepareTemporaryValidationProject(bool interactiveMode) + { + EditorUtility.DisplayProgressBar("Validating...", "Preparing the validation project. This may take a while.", 0.3f); + + var result = new ValidationResult(); + var tempProjectPath = Path.Combine(Constants.RootProjectPath, "Temp", GUID.Generate().ToString()).Replace("\\", "/"); + result.ProjectPath = tempProjectPath; + + if (!Directory.Exists(tempProjectPath)) + Directory.CreateDirectory(tempProjectPath); + + // Cannot edit a package.json file that does not yet exist - copy over AST instead + var tempPackagesPath = $"{tempProjectPath}/Packages"; + if (!Directory.Exists(tempPackagesPath)) + Directory.CreateDirectory(tempPackagesPath); + var assetStoreToolsPath = PackageUtility.GetAllPackages().FirstOrDefault(x => x.name == "com.unity.asset-store-tools").resolvedPath.Replace("\\", "/"); + FileUtility.CopyDirectory(assetStoreToolsPath, $"{tempPackagesPath}/com.unity.asset-store-tools", true); + + var logFilePath = $"{tempProjectPath}/preparation.log"; + + // Create the temporary project + var processInfo = new System.Diagnostics.ProcessStartInfo() + { + FileName = Constants.UnityPath, + Arguments = $"-createProject \"{tempProjectPath}\" -logFile \"{logFilePath}\" -importpackage \"{Path.GetFullPath(_settings.PackagePath)}\" -quit" + }; + + if (!interactiveMode) + processInfo.Arguments += " -batchmode"; + + var exitCode = 0; + + using (var process = System.Diagnostics.Process.Start(processInfo)) + { + while (!process.HasExited) + { + if (EditorUtility.DisplayCancelableProgressBar("Validating...", "Preparing the validation project. This may take a while.", 0.3f)) + process.Kill(); + + Thread.Sleep(10); + } + + exitCode = process.ExitCode; + + // Windows and MacOS exit codes + if (exitCode == -1 || exitCode == 137) + { + result.Status = ValidationStatus.Cancelled; + return result; + } + } + + if (exitCode != 0) + { + result.Status = ValidationStatus.Failed; + result.Exception = new Exception($"Setting up the temporary project failed (exit code {exitCode})\n\nMore information can be found in the log file: {logFilePath}"); + } + else + { + result.Status = ValidationStatus.RanToCompletion; + } + + return result; + } + + private ValidationResult ValidateTemporaryValidationProject(ValidationResult result, bool interactiveMode) + { + EditorUtility.DisplayProgressBar("Validating...", "Performing validation...", 0.6f); + + var logFilePath = $"{result.ProjectPath}/validation.log"; + var processInfo = new System.Diagnostics.ProcessStartInfo() + { + FileName = Constants.UnityPath, + Arguments = $"-projectPath \"{result.ProjectPath}\" -logFile \"{logFilePath}\" -executeMethod AssetStoreTools.Validator.ExternalProjectValidator.ValidateProject -category \"{_settings.Category}\"" + }; + + if (!interactiveMode) + processInfo.Arguments += " -batchmode -ignorecompilererrors"; + + var exitCode = 0; + + using (var process = System.Diagnostics.Process.Start(processInfo)) + { + process.WaitForExit(); + exitCode = process.ExitCode; + } + + if (exitCode != 0) + { + result.Status = ValidationStatus.Failed; + result.Exception = new Exception($"Validating the temporary project failed (exit code {exitCode})\n\nMore information can be found in the log file: {logFilePath}"); + } + else + { + result.Status = ValidationStatus.RanToCompletion; + } + + return result; + } + + private ValidationResult ParseValidationResult(string externalProjectPath) + { + if (!CachingService.GetCachedValidatorStateData(externalProjectPath, out var validationStateData)) + throw new Exception("Could not find external project's validation results"); + + var cachedResult = validationStateData.GetResults(); + var cachedTestResults = cachedResult.GetResults(); + var tests = GetApplicableTests(ValidationType.Generic, ValidationType.UnityPackage); + + foreach (var test in tests) + { + if (!cachedTestResults.Any(x => x.Key == test.Id)) + continue; + + var matchingTest = cachedTestResults.First(x => x.Key == test.Id); + test.Result = matchingTest.Value; + } + + var result = new ValidationResult() + { + Status = cachedResult.GetStatus(), + HadCompilationErrors = cachedResult.GetHadCompilationErrors(), + ProjectPath = cachedResult.GetProjectPath(), + Tests = tests + }; + + return result; + } + + public static void OpenExternalValidationProject(string projectPath) + { + var unityPath = Constants.UnityPath; + var logFilePath = $"{projectPath}/editor.log"; + + var processInfo = new System.Diagnostics.ProcessStartInfo() + { + FileName = unityPath, + Arguments = $"-projectPath \"{projectPath}\" -logFile \"{logFilePath}\" -executeMethod AssetStoreTools.AssetStoreTools.ShowAssetStoreToolsValidator" + }; + + using (var process = System.Diagnostics.Process.Start(processInfo)) + { + process.WaitForExit(); + } + } + + // Invoked via Command Line Arguments + private static void ValidateProject() + { + var exitCode = 0; + try + { + // Determine whether to validate Assets folder or Packages folders + var validationPaths = new List(); + var packageDirectories = Directory.GetDirectories("Packages", "*", SearchOption.TopDirectoryOnly) + .Select(x => x.Replace("\\", "/")) + .Where(x => x != "Packages/com.unity.asset-store-tools").ToArray(); + + if (packageDirectories.Length > 0) + validationPaths.AddRange(packageDirectories); + else + validationPaths.Add("Assets"); + + // Parse category + var category = string.Empty; + var args = Environment.GetCommandLineArgs().ToList(); + var categoryIndex = args.IndexOf("-category"); + if (categoryIndex != -1 && categoryIndex + 1 < args.Count) + category = args[categoryIndex + 1]; + + // Run validation + var validationSettings = new CurrentProjectValidationSettings() + { + Category = category, + ValidationPaths = validationPaths, + ValidationType = ValidationType.UnityPackage + }; + + var validator = new CurrentProjectValidator(validationSettings); + var result = validator.Validate(); + + // Display results + AssetStoreTools.ShowAssetStoreToolsValidator(validationSettings, result); + EditorUtility.DisplayDialog("Validation complete", "Package validation complete.\n\nTo resume work in the original project, close this Editor instance", "OK"); + } + catch + { + exitCode = 1; + throw; + } + finally + { + if (Application.isBatchMode) + EditorApplication.Exit(exitCode); + } + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ExternalProjectValidator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ExternalProjectValidator.cs.meta new file mode 100644 index 0000000..46e89fb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ExternalProjectValidator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2664bbca63a2444498f13beb7e4fa731 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/IValidator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/IValidator.cs new file mode 100644 index 0000000..bd16b58 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/IValidator.cs @@ -0,0 +1,11 @@ +using AssetStoreTools.Validator.Data; + +namespace AssetStoreTools.Validator +{ + internal interface IValidator + { + ValidationSettings Settings { get; } + + ValidationResult Validate(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/IValidator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/IValidator.cs.meta new file mode 100644 index 0000000..5a4e713 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/IValidator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d49e9393288e0ed418c546e57c4cb425 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services.meta new file mode 100644 index 0000000..03e4234 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9315c4052243ab2488208604c11c53c7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService.meta new file mode 100644 index 0000000..5d9a1cb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0a52c1c4a2b7caa458af5b9a212b80a5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/CachingService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/CachingService.cs new file mode 100644 index 0000000..cff8d95 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/CachingService.cs @@ -0,0 +1,55 @@ +using AssetStoreTools.Utility; +using AssetStoreTools.Validator.UI.Data.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace AssetStoreTools.Validator.Services +{ + internal class CachingService : ICachingService + { + public bool GetCachedValidatorStateData(out ValidatorStateData stateData) + { + return GetCachedValidatorStateData(Constants.RootProjectPath, out stateData); + } + + public bool GetCachedValidatorStateData(string projectPath, out ValidatorStateData stateData) + { + stateData = null; + if (!CacheUtil.GetFileFromProjectPersistentCache(projectPath, Constants.Cache.ValidationResultFile, out var filePath)) + return false; + + try + { + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = ValidatorStateDataContractResolver.Instance, + TypeNameHandling = TypeNameHandling.Auto, + Converters = new List() { new StringEnumConverter() } + }; + + stateData = JsonConvert.DeserializeObject(File.ReadAllText(filePath, Encoding.UTF8), serializerSettings); + return true; + } + catch + { + return false; + } + } + + public void CacheValidatorStateData(ValidatorStateData stateData) + { + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = ValidatorStateDataContractResolver.Instance, + Formatting = Formatting.Indented, + TypeNameHandling = TypeNameHandling.Auto, + Converters = new List() { new StringEnumConverter() } + }; + + CacheUtil.CreateFileInPersistentCache(Constants.Cache.ValidationResultFile, JsonConvert.SerializeObject(stateData, serializerSettings), true); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/CachingService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/CachingService.cs.meta new file mode 100644 index 0000000..00ba6e2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/CachingService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b2d545f659acb4343bf485ffb20ecf72 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/ICachingService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/ICachingService.cs new file mode 100644 index 0000000..bee25d5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/ICachingService.cs @@ -0,0 +1,11 @@ +using AssetStoreTools.Validator.UI.Data.Serialization; + +namespace AssetStoreTools.Validator.Services +{ + internal interface ICachingService : IValidatorService + { + void CacheValidatorStateData(ValidatorStateData stateData); + bool GetCachedValidatorStateData(out ValidatorStateData stateData); + bool GetCachedValidatorStateData(string projectPath, out ValidatorStateData stateData); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/ICachingService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/ICachingService.cs.meta new file mode 100644 index 0000000..4f5aef4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/ICachingService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a8a3e36c133848447b043a91e709c63e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/PreviewDatabaseContractResolver.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/PreviewDatabaseContractResolver.cs new file mode 100644 index 0000000..aa495ee --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/PreviewDatabaseContractResolver.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json.Serialization; + +namespace AssetStoreTools.Previews.Services +{ + internal class PreviewDatabaseContractResolver : DefaultContractResolver + { + private static PreviewDatabaseContractResolver _instance; + public static PreviewDatabaseContractResolver Instance => _instance ?? (_instance = new PreviewDatabaseContractResolver()); + + private NamingStrategy _namingStrategy; + + private PreviewDatabaseContractResolver() + { + _namingStrategy = new SnakeCaseNamingStrategy(); + } + + protected override string ResolvePropertyName(string propertyName) + { + var resolvedName = _namingStrategy.GetPropertyName(propertyName, false); + if (resolvedName.StartsWith("_")) + return resolvedName.Substring(1); + + return resolvedName; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/PreviewDatabaseContractResolver.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/PreviewDatabaseContractResolver.cs.meta new file mode 100644 index 0000000..799994b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/PreviewDatabaseContractResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aee615e9aaf50fb4f989cd4698e8947e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/IValidatorService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/IValidatorService.cs new file mode 100644 index 0000000..3ff09b8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/IValidatorService.cs @@ -0,0 +1,4 @@ +namespace AssetStoreTools.Validator.Services +{ + internal interface IValidatorService { } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/IValidatorService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/IValidatorService.cs.meta new file mode 100644 index 0000000..74923b3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/IValidatorService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 075953f4ab4a65d4fae6e891360df0d0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation.meta new file mode 100644 index 0000000..bf10d70 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 184dcfbfe1d21454fa8cf49f1c637871 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions.meta new file mode 100644 index 0000000..d6c2cf0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ed0af5acc22551645ae4cb7d75bd1c36 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IAssetUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IAssetUtilityService.cs new file mode 100644 index 0000000..a73e3ce --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IAssetUtilityService.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal interface IAssetUtilityService : IValidatorService + { + IEnumerable GetAssetPathsFromAssets(string[] searchPaths, AssetType type); + IEnumerable GetObjectsFromAssets(string[] searchPaths, AssetType type) where T : Object; + IEnumerable GetObjectsFromAssets(string[] searchPaths, AssetType type); + string ObjectToAssetPath(Object obj); + T AssetPathToObject(string assetPath) where T : Object; + Object AssetPathToObject(string assetPath); + AssetImporter GetAssetImporter(string assetPath); + AssetImporter GetAssetImporter(Object asset); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IAssetUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IAssetUtilityService.cs.meta new file mode 100644 index 0000000..cb8e4df --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IAssetUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d28c5ea40f4c9954bae02804e416b898 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IFileSignatureUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IFileSignatureUtilityService.cs new file mode 100644 index 0000000..7633ffd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IFileSignatureUtilityService.cs @@ -0,0 +1,7 @@ +namespace AssetStoreTools.Validator.Services.Validation +{ + internal interface IFileSignatureUtilityService : IValidatorService + { + ArchiveType GetArchiveType(string filePath); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IFileSignatureUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IFileSignatureUtilityService.cs.meta new file mode 100644 index 0000000..132acba --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IFileSignatureUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 609c423482ecf8844a71166b4ef49cb6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IMeshUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IMeshUtilityService.cs new file mode 100644 index 0000000..ba4959e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IMeshUtilityService.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal interface IMeshUtilityService : IValidatorService + { + IEnumerable GetCustomMeshesInObject(GameObject obj); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IMeshUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IMeshUtilityService.cs.meta new file mode 100644 index 0000000..952889e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IMeshUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: acde6f9b97c9cac4b88a84aa9001a0fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IModelUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IModelUtilityService.cs new file mode 100644 index 0000000..fa29424 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IModelUtilityService.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal interface IModelUtilityService : IValidatorService + { + Dictionary> GetImportLogs(params Object[] models); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IModelUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IModelUtilityService.cs.meta new file mode 100644 index 0000000..5e464c9 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IModelUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91f6bacccdfecb84fb5ab0ba384353b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/ISceneUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/ISceneUtilityService.cs new file mode 100644 index 0000000..d80f184 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/ISceneUtilityService.cs @@ -0,0 +1,13 @@ +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal interface ISceneUtilityService : IValidatorService + { + string CurrentScenePath { get; } + + Scene OpenScene(string scenePath); + GameObject[] GetRootGameObjects(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/ISceneUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/ISceneUtilityService.cs.meta new file mode 100644 index 0000000..db0e94f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/ISceneUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cf5ef331063e5aa4e95dfe3eadedf9af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IScriptUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IScriptUtilityService.cs new file mode 100644 index 0000000..04214c3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IScriptUtilityService.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using Object = UnityEngine.Object; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal interface IScriptUtilityService : IValidatorService + { + IReadOnlyDictionary> GetTypeNamespacesFromScriptAssets(IList monoScripts); + IReadOnlyDictionary> GetTypesFromAssemblies(IList assemblies); + IReadOnlyDictionary> GetTypesFromScriptAssets(IList monoScripts); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IScriptUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IScriptUtilityService.cs.meta new file mode 100644 index 0000000..e5458be --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IScriptUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e0a9f88d37222e4428853b6d3d00b1bd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/AssetUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/AssetUtilityService.cs new file mode 100644 index 0000000..fb51d8a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/AssetUtilityService.cs @@ -0,0 +1,216 @@ +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEditor.Compilation; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal class AssetUtilityService : IAssetUtilityService + { + public IEnumerable GetAssetPathsFromAssets(string[] searchPaths, AssetType type) + { + string filter = string.Empty; + string[] extensions = null; + + switch (type) + { + // General Types + case AssetType.All: + filter = ""; + break; + case AssetType.Prefab: + filter = "t:prefab"; + break; + case AssetType.Material: + filter = "t:material"; + break; + case AssetType.Model: + filter = "t:model"; + break; + case AssetType.Scene: + filter = "t:scene"; + break; + case AssetType.Texture: + filter = "t:texture"; + break; + case AssetType.Video: + filter = "t:VideoClip"; + break; + // Specific Types + case AssetType.LossyAudio: + filter = "t:AudioClip"; + extensions = new[] { ".mp3", ".ogg" }; + break; + case AssetType.NonLossyAudio: + filter = "t:AudioClip"; + extensions = new[] { ".wav", ".aif", ".aiff" }; + break; + case AssetType.JavaScript: + filter = "t:TextAsset"; + extensions = new[] { ".js" }; + break; + case AssetType.Mixamo: + filter = "t:model"; + extensions = new[] { ".fbx" }; + break; + case AssetType.JPG: + filter = "t:texture"; + extensions = new[] { ".jpg", "jpeg" }; + break; + case AssetType.Executable: + filter = string.Empty; + extensions = new[] { ".exe", ".bat", ".msi", ".apk" }; + break; + case AssetType.Documentation: + filter = string.Empty; + extensions = new[] { ".txt", ".pdf", ".html", ".rtf", ".md" }; + break; + case AssetType.SpeedTree: + filter = string.Empty; + extensions = new[] { ".spm", ".srt", ".stm", ".scs", ".sfc", ".sme", ".st" }; + break; + case AssetType.Shader: + filter = string.Empty; + extensions = new[] { ".shader", ".shadergraph", ".raytrace", ".compute" }; + break; + case AssetType.MonoScript: + filter = "t:script"; + extensions = new[] { ".cs" }; + break; + case AssetType.UnityPackage: + filter = string.Empty; + extensions = new[] { ".unitypackage" }; + break; + case AssetType.PrecompiledAssembly: + var assemblyPaths = GetPrecompiledAssemblies(searchPaths); + return assemblyPaths; + default: + return Array.Empty(); + } + + var guids = AssetDatabase.FindAssets(filter, searchPaths); + var paths = guids.Select(AssetDatabase.GUIDToAssetPath); + + if (extensions != null) + paths = paths.Where(x => extensions.Any(x.ToLower().EndsWith)); + + if (type == AssetType.Mixamo) + paths = paths.Where(IsMixamoFbx); + + paths = paths.Distinct(); + return paths; + } + + public IEnumerable GetObjectsFromAssets(string[] searchPaths, AssetType type) where T : Object + { + var paths = GetAssetPathsFromAssets(searchPaths, type); +#if !AB_BUILDER + var objects = paths.Select(AssetDatabase.LoadAssetAtPath).Where(x => x != null); +#else + var objects = new AssetEnumerator(paths); +#endif + return objects; + } + + public IEnumerable GetObjectsFromAssets(string[] searchPaths, AssetType type) + { + return GetObjectsFromAssets(searchPaths, type); + } + + private IEnumerable GetPrecompiledAssemblies(string[] searchPaths) + { + // Note - for packages, Compilation Pipeline returns full paths, as they appear on disk, not Asset Database + var allDllPaths = CompilationPipeline.GetPrecompiledAssemblyPaths(CompilationPipeline.PrecompiledAssemblySources.UserAssembly); + var rootProjectPath = Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length); + var packages = PackageUtility.GetAllLocalPackages(); + + var result = new List(); + foreach (var dllPath in allDllPaths) + { + var absoluteDllPath = Path.GetFullPath(dllPath).Replace("\\", "/"); + foreach (var validationPath in searchPaths) + { + var absoluteValidationPath = Path.GetFullPath(validationPath).Replace("\\", "/"); + if (absoluteDllPath.StartsWith(absoluteValidationPath)) + { + int pathSeparatorLength = 1; + if (absoluteDllPath.StartsWith(Application.dataPath)) + { + var adbPath = $"Assets/{absoluteDllPath.Remove(0, Application.dataPath.Length + pathSeparatorLength)}"; + result.Add(adbPath); + } + else + { + // For non-Asset folder paths (i.e. local and embedded packages), convert disk path to ADB path + var package = packages.FirstOrDefault(x => dllPath.StartsWith(x.resolvedPath.Replace('\\', '/'))); + + if (package == null) + continue; + + var dllPathInPackage = absoluteDllPath.Remove(0, Path.GetFullPath(package.resolvedPath).Length + pathSeparatorLength); + var adbPath = $"Packages/{package.name}/{dllPathInPackage}"; + + result.Add(adbPath); + } + } + } + } + + return result; + } + + private bool IsMixamoFbx(string fbxPath) + { + // Location of Mixamo Header, this is located in every mixamo fbx file exported + //const int mixamoHeader = 0x4c0 + 2; // < this is the original location from A$ Tools, unsure if Mixamo file headers were changed since then + const int mixamoHeader = 1622; + // Length of Mixamo header + const int length = 0xa; + + var fs = new FileStream(fbxPath, FileMode.Open); + // Check if length is further than + if (fs.Length < mixamoHeader) + return false; + + byte[] buffer = new byte[length]; + using (BinaryReader reader = new BinaryReader(fs)) + { + reader.BaseStream.Seek(mixamoHeader, SeekOrigin.Begin); + reader.Read(buffer, 0, length); + } + + string result = System.Text.Encoding.ASCII.GetString(buffer); + return result.Contains("Mixamo"); + } + + public string ObjectToAssetPath(Object obj) + { + return AssetDatabase.GetAssetPath(obj); + } + + public T AssetPathToObject(string assetPath) where T : Object + { + return AssetDatabase.LoadAssetAtPath(assetPath); + } + + public Object AssetPathToObject(string assetPath) + { + return AssetPathToObject(assetPath); + } + + public AssetImporter GetAssetImporter(string assetPath) + { + return AssetImporter.GetAtPath(assetPath); + } + + public AssetImporter GetAssetImporter(Object asset) + { + return GetAssetImporter(ObjectToAssetPath(asset)); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/AssetUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/AssetUtilityService.cs.meta new file mode 100644 index 0000000..c91088e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/AssetUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9634968648d355c47b7cb12aead7abab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data.meta new file mode 100644 index 0000000..3256935 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8dcc2f4da0b6cea4ab4733ebf32edab4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/ArchiveType.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/ArchiveType.cs new file mode 100644 index 0000000..d225180 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/ArchiveType.cs @@ -0,0 +1,19 @@ +namespace AssetStoreTools.Validator.Services.Validation +{ + internal enum ArchiveType + { + None, + TarGz, + Zip, + Rar, + Tar, + TarZip, + Bz2, + LZip, + SevenZip, + GZip, + QuickZip, + Xz, + Wim + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/ArchiveType.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/ArchiveType.cs.meta new file mode 100644 index 0000000..82f4e85 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/ArchiveType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4061cb7aed3883346a66494c23e2e77b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetEnumerator.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetEnumerator.cs new file mode 100644 index 0000000..0df889a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetEnumerator.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using Object = UnityEngine.Object; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal class AssetEnumerator : IEnumerator, IEnumerable where T : Object + { + public const int Capacity = 32; + + private Queue _pathQueue; + private Queue _loadedAssetQueue; + + private T _currentElement; + + public AssetEnumerator(IEnumerable paths) + { + _pathQueue = new Queue(paths); + _loadedAssetQueue = new Queue(); + } + + public bool MoveNext() + { + bool hasPathsButHasNoAssets = _pathQueue.Count != 0 && _loadedAssetQueue.Count == 0; + if (hasPathsButHasNoAssets) + { + LoadMore(); + } + + bool dequeued = false; + if (_loadedAssetQueue.Count != 0) + { + _currentElement = _loadedAssetQueue.Dequeue(); + dequeued = true; + } + + return dequeued; + } + + private void LoadMore() + { + int limit = Capacity; + while (limit > 0 && _pathQueue.Count != 0) + { + string path = _pathQueue.Dequeue(); + T asset = AssetDatabase.LoadAssetAtPath(path); + if (asset != null) + { + _loadedAssetQueue.Enqueue(asset); + limit--; + } + } + + // Unload other loose asset references + EditorUtility.UnloadUnusedAssetsImmediate(); + } + + public void Reset() + { + throw new NotSupportedException("Asset Enumerator cannot be reset."); + } + + public T Current => _currentElement; + + object IEnumerator.Current => Current; + + public void Dispose() + { + // No need to dispose + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this; + } + + public IEnumerator GetEnumerator() + { + return this; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetEnumerator.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetEnumerator.cs.meta new file mode 100644 index 0000000..d82fc2f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetEnumerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0859579889cc56f4aa26eb863a1487b9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetType.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetType.cs new file mode 100644 index 0000000..2fc188d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetType.cs @@ -0,0 +1,25 @@ +namespace AssetStoreTools.Validator.Services.Validation +{ + internal enum AssetType + { + All, + Documentation, + Executable, + JPG, + JavaScript, + LossyAudio, + Material, + Mixamo, + Model, + MonoScript, + NonLossyAudio, + PrecompiledAssembly, + Prefab, + Scene, + Shader, + SpeedTree, + Texture, + UnityPackage, + Video + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetType.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetType.cs.meta new file mode 100644 index 0000000..3acaf4b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b81d00d4ed0a7da4289d4d6248ef9d34 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/LogEntry.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/LogEntry.cs new file mode 100644 index 0000000..67663be --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/LogEntry.cs @@ -0,0 +1,10 @@ +using UnityEngine; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal class LogEntry + { + public string Message; + public LogType Severity; + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/LogEntry.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/LogEntry.cs.meta new file mode 100644 index 0000000..cad8e06 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/LogEntry.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a1e81104d6b0f4c449ee57503c3b6669 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/FileSignatureUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/FileSignatureUtilityService.cs new file mode 100644 index 0000000..2a140f4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/FileSignatureUtilityService.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal class FileSignatureUtilityService : IFileSignatureUtilityService + { + private class FileSignature + { + public byte[] SignatureBytes; + public int Offset; + + public FileSignature(byte[] signatureBytes, int offset) + { + SignatureBytes = signatureBytes; + Offset = offset; + } + } + + private static readonly Dictionary ArchiveSignatures = new Dictionary + { + { new FileSignature(new byte[] { 0x1f, 0x8b }, 0), ArchiveType.TarGz }, + { new FileSignature(new byte[] { 0x50, 0x4b, 0x03, 0x04 }, 0), ArchiveType.Zip }, + { new FileSignature(new byte[] { 0x50, 0x4b, 0x05, 0x06 }, 0), ArchiveType.Zip }, // Empty Zip Archive + { new FileSignature(new byte[] { 0x50, 0x4b, 0x07, 0x08 }, 0), ArchiveType.Zip }, // Spanned Zip Archive + + { new FileSignature(new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }, 0), ArchiveType.Rar }, // RaR v1.50+ + { new FileSignature(new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 }, 0), ArchiveType.Rar }, // RaR v5.00+ + { new FileSignature(new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 }, 257), ArchiveType.Tar }, + { new FileSignature(new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 }, 257), ArchiveType.Tar }, + { new FileSignature(new byte[] { 0x1f, 0x9d }, 0), ArchiveType.TarZip }, // TarZip LZW algorithm + { new FileSignature(new byte[] { 0x1f, 0xa0 }, 0), ArchiveType.TarZip }, // TarZip LZH algorithm + { new FileSignature(new byte[] { 0x42, 0x5a, 0x68 }, 0), ArchiveType.Bz2 }, + { new FileSignature(new byte[] { 0x4c, 0x5a, 0x49, 0x50 }, 0), ArchiveType.LZip }, + { new FileSignature(new byte[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c }, 0), ArchiveType.SevenZip }, + { new FileSignature(new byte[] { 0x1f, 0x8b }, 0), ArchiveType.GZip }, + { new FileSignature(new byte[] { 0x52, 0x53, 0x56, 0x4b, 0x44, 0x41, 0x54, 0x41 }, 0), ArchiveType.QuickZip }, + { new FileSignature(new byte[] { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00 }, 0), ArchiveType.Xz }, + { new FileSignature(new byte[] { 0x4D, 0x53, 0x57, 0x49, 0x4D, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00 }, 0), ArchiveType.Wim } + }; + + public ArchiveType GetArchiveType(string filePath) + { + if (!File.Exists(filePath)) + return ArchiveType.None; + + try + { + using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + foreach (var kvp in ArchiveSignatures) + { + var fileSignature = kvp.Key; + var archiveType = kvp.Value; + + if (stream.Length < fileSignature.SignatureBytes.Length) + continue; + + var bytes = new byte[fileSignature.SignatureBytes.Length]; + stream.Seek(fileSignature.Offset, SeekOrigin.Begin); + stream.Read(bytes, 0, bytes.Length); + + if (fileSignature.SignatureBytes.SequenceEqual(bytes.Take(fileSignature.SignatureBytes.Length))) + return archiveType; + } + } + } + catch (DirectoryNotFoundException) + { + Debug.LogWarning($"File '{filePath}' exists, but could not be opened for reading. Please make sure the project path lengths are not too long for the Operating System"); + } + + return ArchiveType.None; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/FileSignatureUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/FileSignatureUtilityService.cs.meta new file mode 100644 index 0000000..fc0dc33 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/FileSignatureUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 695ed79ad88c3b44b8ae41b650ebe16c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/MeshUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/MeshUtilityService.cs new file mode 100644 index 0000000..fba268c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/MeshUtilityService.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal class MeshUtilityService : IMeshUtilityService + { + public IEnumerable GetCustomMeshesInObject(GameObject obj) + { + var meshes = new List(); + + var meshFilters = obj.GetComponentsInChildren(true); + var skinnedMeshes = obj.GetComponentsInChildren(true); + + meshes.AddRange(meshFilters.Select(m => m.sharedMesh)); + meshes.AddRange(skinnedMeshes.Select(m => m.sharedMesh)); + + meshes = meshes.Where(m => AssetDatabase.GetAssetPath(m).StartsWith("Assets/") || + AssetDatabase.GetAssetPath(m).StartsWith("Packages/")).ToList(); + + return meshes; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/MeshUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/MeshUtilityService.cs.meta new file mode 100644 index 0000000..7c677a2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/MeshUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 307f5dd7be983e246adbda52ac50ecf3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ModelUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ModelUtilityService.cs new file mode 100644 index 0000000..b018991 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ModelUtilityService.cs @@ -0,0 +1,147 @@ +#if !UNITY_2022_2_OR_NEWER +using System; +using System.Reflection; +#endif +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +#if UNITY_2022_2_OR_NEWER +using UnityEditor.AssetImporters; +#endif +using UnityEngine; +using Object = UnityEngine.Object; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal class ModelUtilityService : IModelUtilityService + { + private IAssetUtilityService _assetUtility; + +#if !UNITY_2022_2_OR_NEWER + // Rig fields + private const string RigImportWarningsField = "m_RigImportWarnings"; + private const string RigImportErrorsField = "m_RigImportErrors"; + + // Animation fields + private const string AnimationImportWarningsField = "m_AnimationImportWarnings"; + private const string AnimationImportErrorsField = "m_AnimationImportErrors"; + + private static Editor _modelImporterEditor = null; +#endif + + public ModelUtilityService(IAssetUtilityService assetUtility) + { + _assetUtility = assetUtility; + } + + public Dictionary> GetImportLogs(params Object[] models) + { +#if UNITY_2022_2_OR_NEWER + return GetImportLogsDefault(models); +#else + return GetImportLogsLegacy(models); +#endif + } + +#if UNITY_2022_2_OR_NEWER + private Dictionary> GetImportLogsDefault(params Object[] models) + { + var modelsWithLogs = new Dictionary>(); + + foreach (var model in models) + { + var modelLogs = new List(); + + var importLog = AssetImporter.GetImportLog(_assetUtility.ObjectToAssetPath(model)); + + if (importLog == null) + continue; + + var entries = importLog.logEntries.Where(x => x.flags.HasFlag(ImportLogFlags.Warning) || x.flags.HasFlag(ImportLogFlags.Error)); + foreach (var entry in entries) + { + var severity = entry.flags.HasFlag(ImportLogFlags.Error) ? LogType.Error : LogType.Warning; + modelLogs.Add(new LogEntry() { Message = entry.message, Severity = severity }); + } + + if (modelLogs.Count > 0) + modelsWithLogs.Add(model, modelLogs); + } + + return modelsWithLogs; + } +#endif + +#if !UNITY_2022_2_OR_NEWER + private Dictionary> GetImportLogsLegacy(params Object[] models) + { + var modelsWithLogs = new Dictionary>(); + + foreach (var model in models) + { + var modelLogs = new List(); + + // Load the Model Importer + var modelImporter = _assetUtility.GetAssetImporter(model) as ModelImporter; + + var editorAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.GetName().Name.Equals("UnityEditor")); + + var modelImporterEditorType = editorAssembly.GetType("UnityEditor.ModelImporterEditor"); + + // Load its Model Importer Editor + Editor.CreateCachedEditorWithContext(new Object[] { modelImporter }, model, modelImporterEditorType, ref _modelImporterEditor); + + // Find the base type + var modelImporterEditorTypeBase = _modelImporterEditor.GetType().BaseType; + + // Get the tabs value + var tabsArrayType = modelImporterEditorTypeBase.GetRuntimeProperties().FirstOrDefault(x => x.Name == "tabs"); + var tabsArray = (Array)tabsArrayType.GetValue(_modelImporterEditor); + + // Get the tabs (Model | Rig | Animation | Materials) + var rigTab = tabsArray.GetValue(1); + var animationTab = tabsArray.GetValue(2); + + var rigErrorsCheckSuccess = CheckFieldForSerializedProperty(rigTab, RigImportErrorsField, out var rigErrors); + var rigWarningsCheckSuccess = CheckFieldForSerializedProperty(rigTab, RigImportWarningsField, out var rigWarnings); + var animationErrorsCheckSuccess = CheckFieldForSerializedProperty(animationTab, AnimationImportErrorsField, out var animationErrors); + var animationWarningsCheckSuccess = CheckFieldForSerializedProperty(animationTab, AnimationImportWarningsField, out var animationWarnings); + + if (!rigErrorsCheckSuccess || !rigWarningsCheckSuccess || !animationErrorsCheckSuccess || !animationWarningsCheckSuccess) + UnityEngine.Debug.LogWarning($"An error was encountered when checking import logs for model '{model.name}'"); + + if (!string.IsNullOrEmpty(rigWarnings)) + modelLogs.Add(new LogEntry() { Message = rigWarnings, Severity = LogType.Warning }); + if (!string.IsNullOrEmpty(rigErrors)) + modelLogs.Add(new LogEntry() { Message = rigErrors, Severity = LogType.Error }); + if (!string.IsNullOrEmpty(animationWarnings)) + modelLogs.Add(new LogEntry() { Message = animationWarnings, Severity = LogType.Warning }); + if (!string.IsNullOrEmpty(animationErrors)) + modelLogs.Add(new LogEntry() { Message = animationErrors, Severity = LogType.Error }); + + if (modelLogs.Count > 0) + modelsWithLogs.Add(model, modelLogs); + } + + return modelsWithLogs; + } + + private static bool CheckFieldForSerializedProperty(object source, string propertyName, out string message) + { + message = string.Empty; + + try + { + var propertyType = source.GetType().GetRuntimeFields().FirstOrDefault(x => x.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); + var propertyValue = propertyType.GetValue(source) as SerializedProperty; + message = propertyValue.stringValue; + return true; + } + catch + { + return false; + } + } +#endif + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ModelUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ModelUtilityService.cs.meta new file mode 100644 index 0000000..ec4a95a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ModelUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c50ca4c87e66f1b478279e5d1db4a08e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/SceneUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/SceneUtilityService.cs new file mode 100644 index 0000000..ec7a43b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/SceneUtilityService.cs @@ -0,0 +1,26 @@ +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal class SceneUtilityService : ISceneUtilityService + { + public string CurrentScenePath => SceneManager.GetActiveScene().path; + + public Scene OpenScene(string scenePath) + { + EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo(); + if (string.IsNullOrEmpty(scenePath) || AssetDatabase.LoadAssetAtPath(scenePath) == null) + return EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects); + else + return EditorSceneManager.OpenScene(scenePath); + } + + public GameObject[] GetRootGameObjects() + { + return SceneManager.GetSceneByPath(CurrentScenePath).GetRootGameObjects(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/SceneUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/SceneUtilityService.cs.meta new file mode 100644 index 0000000..9f20ad4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/SceneUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 53e8deb0ebfb7ea47956f3a859580cd4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ScriptUtilityService.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ScriptUtilityService.cs new file mode 100644 index 0000000..2e85c2a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ScriptUtilityService.cs @@ -0,0 +1,658 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using UnityEditor; +using Object = UnityEngine.Object; + +namespace AssetStoreTools.Validator.Services.Validation +{ + internal class ScriptUtilityService : IScriptUtilityService + { + private const int ScriptTimeoutMs = 10000; + private const string IgnoredAssemblyCharacters = "!@#$%^*&()-+=[]{}\\|;:'\",.<>/?"; + + /// + /// For a given list of script assets, retrieves a list of types and their namespaces + /// + /// + /// A dictionary mapping each script asset with a list of its types. + /// The type tuple contains a name (e.g. class MyClass) and its namespace (e.g. MyNamespace) + /// + public IReadOnlyDictionary> GetTypeNamespacesFromScriptAssets(IList monoScripts) + { + var typesAndNamespaces = new Dictionary>(); + var typeInfos = GetTypeInfosFromScriptAssets(monoScripts); + + foreach (var kvp in typeInfos) + { + var namespacesInScript = new List<(string Name, string Namespace)>(); + foreach (var typeInfo in kvp.Value) + { + bool isValidType = typeInfo.TypeName == ScriptParser.TypeName.Class || typeInfo.TypeName == ScriptParser.TypeName.Struct || + typeInfo.TypeName == ScriptParser.TypeName.Interface || typeInfo.TypeName == ScriptParser.TypeName.Enum; + + if (isValidType) + namespacesInScript.Add(($"{typeInfo.TypeName.ToString().ToLower()} {typeInfo.Name}", typeInfo.Namespace)); + } + + typesAndNamespaces.Add(kvp.Key, namespacesInScript); + } + + return typesAndNamespaces; + } + + /// + /// Scans the given precompiled assembly assets to retrieve a list of their contained types + /// + /// + /// A dictionary mapping each precompiled assembly asset with a list of System.Type objects. + public IReadOnlyDictionary> GetTypesFromAssemblies(IList assemblies) + { + var dllPaths = assemblies.ToDictionary(t => AssetDatabase.GetAssetPath(t), t => t); + var types = new ConcurrentDictionary>(); + var failedDllPaths = new ConcurrentBag(); + + var allAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + + Parallel.ForEach(dllPaths.Keys, + (assemblyPath) => + { + try + { + var assembly = allAssemblies.FirstOrDefault(x => Path.GetFullPath(x.Location).Equals(Path.GetFullPath(assemblyPath), StringComparison.OrdinalIgnoreCase)); + if (assembly == null) + return; + + var assemblyTypes = assembly.GetTypes().Where(x => !IgnoredAssemblyCharacters.Any(c => x.Name.Contains(c))).ToList(); + types.TryAdd(dllPaths[assemblyPath], assemblyTypes); + } + catch + { + failedDllPaths.Add(assemblyPath); + } + }); + + if (failedDllPaths.Count > 0) + { + var message = new StringBuilder("The following precompiled assemblies could not be checked:"); + foreach (var path in failedDllPaths) + message.Append($"\n{path}"); + UnityEngine.Debug.LogWarning(message); + } + + // Types are sorted randomly due to parallelism, therefore need to be sorted before returning + var sortedTypes = dllPaths.Where(x => types.ContainsKey(x.Value)) + .Select(x => new KeyValuePair>(x.Value, types[x.Value])) + .ToDictionary(t => t.Key, t => t.Value); + + return sortedTypes; + } + + /// + /// Scans the given script assets to retrieve a list of their contained types + /// + /// + /// A dictionary mapping each precompiled assembly asset with a list of System.Type objects. + public IReadOnlyDictionary> GetTypesFromScriptAssets(IList monoScripts) + { + var realTypes = new Dictionary>(); + var typeInfos = GetTypeInfosFromScriptAssets(monoScripts); + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + foreach (var kvp in typeInfos) + { + var realTypesInScript = new List(); + foreach (var typeInfo in kvp.Value) + { + bool isValidType = typeInfo.TypeName == ScriptParser.TypeName.Class || typeInfo.TypeName == ScriptParser.TypeName.Struct || + typeInfo.TypeName == ScriptParser.TypeName.Interface || typeInfo.TypeName == ScriptParser.TypeName.Enum; + + if (isValidType) + { + var realType = assemblies.Where(a => a.GetType(typeInfo.GetReflectionFriendlyFullName()) != null) + .Select(a => a.GetType(typeInfo.GetReflectionFriendlyFullName())).FirstOrDefault(); + if (realType != null) + realTypesInScript.Add(realType); + } + } + + realTypes.Add(kvp.Key, realTypesInScript); + } + + return realTypes; + } + + /// + /// Scans the given MonoScript assets to retrieve a list of their contained types + /// + /// + /// A dictionary mapping each script asset with a list of TypeInfo objects. + private IReadOnlyDictionary> GetTypeInfosFromScriptAssets(IList monoScripts) + { + var types = new ConcurrentDictionary>(); + var monoScriptContents = new Dictionary(); + var failedScripts = new ConcurrentBag(); + + // A separate dictionary is needed because MonoScript contents cannot be accessed outside of the main thread + foreach (var kvp in monoScripts) + monoScriptContents.Add(kvp, kvp.text); + + var tasks = new List>(); + + try + { + foreach (var kvp in monoScriptContents) + { + var cancellationTokenSource = new CancellationTokenSource(ScriptTimeoutMs); + + var task = Task.Run(() => + { + var parsingTask = new ScriptParser(cancellationTokenSource.Token); + var parsed = parsingTask.GetTypesInScript(kvp.Value, out IList parsedTypes); + if (parsed) + types.TryAdd(kvp.Key, parsedTypes); + else + failedScripts.Add(kvp.Key); + }); + + tasks.Add(new Tuple(task, cancellationTokenSource)); + } + + foreach (var t in tasks) + t.Item1.Wait(); + } + finally + { + foreach (var t in tasks) + t.Item2.Dispose(); + } + + if (failedScripts.Count > 0) + { + var message = new StringBuilder("The following scripts could not be checked:"); + foreach (var s in failedScripts) + message.Append($"\n{AssetDatabase.GetAssetPath(s)}"); + UnityEngine.Debug.LogWarning(message); + } + + // Types are sorted randomly due to parallelism, therefore need to be sorted before returning + var sortedTypes = monoScriptContents.Where(x => types.ContainsKey(x.Key)) + .Select(x => new KeyValuePair>(x.Key, types[x.Key])) + .ToDictionary(t => t.Key, t => t.Value); + + return sortedTypes; + } + + /// + /// A simple script parser class to detect types declared within a script + /// + private class ScriptParser + { + /// + /// Types that can be identified by the script parser + /// + public enum TypeName + { + Undefined, + Namespace, + Class, + Struct, + Interface, + Enum, + IdentationStart, + IdentationEnd + } + + /// + /// A class containing information about each block of a C# script + /// + /// A block in this context is defined as script text that is contained within curly brackets. + /// If it's a type, it may have a preceding name and a namespace + /// + public class BlockInfo + { + public TypeName TypeName = TypeName.Undefined; + public string Name = string.Empty; + public string FullName = string.Empty; + public string Namespace = string.Empty; + public int FoundIndex; + public int StartIndex; + + public BlockInfo ParentBlock; + + public string GetReflectionFriendlyFullName() + { + StringBuilder sb = new StringBuilder(FullName); + for (int i = sb.Length - 1; i >= Namespace.Length + 1; i--) + if (sb[i] == '.') + sb[i] = '+'; + + return sb.ToString(); + } + } + + private CancellationToken _token; + + public ScriptParser(CancellationToken token) + { + _token = token; + } + + public bool GetTypesInScript(string text, out IList types) + { + types = null; + + try + { + var sanitized = SanitizeScript(text); + types = ScanForTypes(sanitized); + return true; + } + catch + { + return false; + } + } + + private string SanitizeScript(string source) + { + var sb = new StringBuilder(source); + + // Remove comments and strings + sb = RemoveStringsAndComments(sb); + + // Replace newlines with spaces + sb.Replace("\r", " ").Replace("\n", " "); + + // Space out the brackets + sb.Replace("{", " { ").Replace("}", " } "); + + // Insert a space at the start for more convenient parsing + sb.Insert(0, " "); + + // Remove repeating spaces + var sanitized = Regex.Replace(sb.ToString(), @"\s{2,}", " "); + + return sanitized; + } + + private StringBuilder RemoveStringsAndComments(StringBuilder sb) + { + void CheckStringIdentifiers(int index, out bool isVerbatim, out bool isInterpolated) + { + isVerbatim = false; + isInterpolated = false; + + string precedingChars = string.Empty; + for (int i = index - 1; i >= 0; i--) + { + if (sb[i] == ' ') + break; + precedingChars += sb[i]; + } + + if (precedingChars.Contains("@")) + isVerbatim = true; + if (precedingChars.Contains("$")) + isInterpolated = true; + } + + bool IsRegion(int index) + { + if (sb.Length - index < "#region".Length) + return false; + if (sb[index] == '#' && sb[index + 1] == 'r' && sb[index + 2] == 'e' && sb[index + 3] == 'g' && sb[index + 4] == 'i' && + sb[index + 5] == 'o' && sb[index + 6] == 'n') + return true; + return false; + } + + var removeRanges = new List>(); + + for (int i = 0; i < sb.Length; i++) + { + _token.ThrowIfCancellationRequested(); + + // Comment code + if (sb[i] == '/') + { + if (sb[i + 1] == '/') + { + for (int j = i + 1; j < sb.Length; j++) + { + _token.ThrowIfCancellationRequested(); + if (sb[j] == '\n' || j == sb.Length - 1) + { + removeRanges.Add(new Tuple(i, j - i + 1)); + i = j; + break; + } + } + } + else if (sb[i + 1] == '*') + { + for (int j = i + 2; j < sb.Length; j++) + { + _token.ThrowIfCancellationRequested(); + if (sb[j] == '/' && sb[j - 1] == '*') + { + removeRanges.Add(new Tuple(i, j - i + 1)); + i = j + 1; + break; + } + } + } + } + // Char code + else if (sb[i] == '\'') + { + for (int j = i + 1; j < sb.Length; j++) + { + _token.ThrowIfCancellationRequested(); + if (sb[j] == '\'') + { + if (sb[j - 1] == '\\') + { + int slashCount = 0; + int k = j - 1; + while (sb[k--] == '\\') + slashCount++; + if (slashCount % 2 != 0) + continue; + } + removeRanges.Add(new Tuple(i, j - i + 1)); + i = j; + break; + } + } + } + // String code + else if (sb[i] == '"') + { + if (sb[i - 1] == '\'' && sb[i + 1] == '\'' || (sb[i - 2] == '\'' && sb[i - 1] == '\\' && sb[i + 1] == '\'')) + continue; + + CheckStringIdentifiers(i, out bool isVerbatim, out bool isInterpolated); + + var bracketCount = 0; + bool interpolationEnd = true; + for (int j = i + 1; j < sb.Length; j++) + { + _token.ThrowIfCancellationRequested(); + if (isInterpolated && (sb[j] == '{' || sb[j] == '}')) + { + if (sb[j] == '{') + { + if (sb[j + 1] != '{') + bracketCount++; + else + j += 1; + } + else if (sb[j] == '}') + { + if (sb[j + 1] != '}') + bracketCount--; + else + j += 1; + } + + if (bracketCount == 0) + interpolationEnd = true; + else + interpolationEnd = false; + + continue; + } + + if (sb[j] == '\"') + { + if (isVerbatim) + { + if (sb[j + 1] != '\"') + { + if (!isInterpolated || isInterpolated && interpolationEnd == true) + { + removeRanges.Add(new Tuple(i, j - i + 1)); + i = j + 1; + break; + } + } + else + j += 1; + } + else + { + bool endOfComment = false; + if (sb[j - 1] != '\\') + endOfComment = true; + else + { + int slashCount = 0; + int k = j - 1; + while (sb[k--] == '\\') + slashCount++; + if (slashCount % 2 == 0) + endOfComment = true; + } + + if (!isInterpolated && endOfComment || (isInterpolated && interpolationEnd && endOfComment)) + { + removeRanges.Add(new Tuple(i, j - i + 1)); + i = j + 1; + break; + } + } + } + } + } + // Region code + else if (IsRegion(i)) + { + i += "#region".Length; + for (int j = i; j < sb.Length; j++) + { + _token.ThrowIfCancellationRequested(); + if (sb[j] == '\n') + { + removeRanges.Add(new Tuple(i, j - i + 1)); + i = j; + break; + } + } + } + } + + for (int i = removeRanges.Count - 1; i >= 0; i--) + sb = sb.Remove(removeRanges[i].Item1, removeRanges[i].Item2); + + return sb; + } + + private IList ScanForTypes(string script) + { + var typeList = new SortedList(); + BlockInfo currentActiveBlock = new BlockInfo(); + + int i = 0; + + BlockInfo nextNamespace = null; + BlockInfo nextClass = null; + BlockInfo nextStruct = null; + BlockInfo nextInterface = null; + BlockInfo nextEnum = null; + + while (i < script.Length) + { + _token.ThrowIfCancellationRequested(); + if (nextNamespace == null) + nextNamespace = FindNextTypeBlock(script, i, TypeName.Namespace); + if (nextClass == null) + nextClass = FindNextTypeBlock(script, i, TypeName.Class); + if (nextStruct == null) + nextStruct = FindNextTypeBlock(script, i, TypeName.Struct); + if (nextInterface == null) + nextInterface = FindNextTypeBlock(script, i, TypeName.Interface); + if (nextEnum == null) + nextEnum = FindNextTypeBlock(script, i, TypeName.Enum); + + var nextIdentationIncrease = FindNextTypeBlock(script, i, TypeName.IdentationStart); + var nextIdentationDecrease = FindNextTypeBlock(script, i, TypeName.IdentationEnd); + + if (!TryFindClosestBlock(out var closestBlock, nextNamespace, nextClass, + nextStruct, nextInterface, nextEnum, nextIdentationIncrease, nextIdentationDecrease)) + break; + + switch (closestBlock) + { + case var _ when closestBlock == nextIdentationIncrease: + closestBlock.ParentBlock = currentActiveBlock; + currentActiveBlock = closestBlock; + break; + case var _ when closestBlock == nextIdentationDecrease: + if (currentActiveBlock.TypeName != TypeName.Undefined) + typeList.Add(currentActiveBlock.StartIndex, currentActiveBlock); + currentActiveBlock = currentActiveBlock.ParentBlock; + break; + case var _ when closestBlock == nextNamespace: + closestBlock.Namespace = currentActiveBlock.TypeName == TypeName.Namespace ? currentActiveBlock.FullName : currentActiveBlock.Namespace; + closestBlock.FullName = string.IsNullOrEmpty(currentActiveBlock.FullName) ? closestBlock.Name : $"{currentActiveBlock.FullName}.{closestBlock.Name}"; + closestBlock.ParentBlock = currentActiveBlock; + currentActiveBlock = closestBlock; + nextNamespace = null; + break; + case var _ when closestBlock == nextClass: + case var _ when closestBlock == nextStruct: + case var _ when closestBlock == nextInterface: + case var _ when closestBlock == nextEnum: + closestBlock.FullName = string.IsNullOrEmpty(currentActiveBlock.FullName) ? closestBlock.Name : $"{currentActiveBlock.FullName}.{closestBlock.Name}"; + closestBlock.Namespace = currentActiveBlock.TypeName == TypeName.Namespace ? currentActiveBlock.FullName : currentActiveBlock.Namespace; + closestBlock.ParentBlock = currentActiveBlock; + currentActiveBlock = closestBlock; + switch (closestBlock) + { + case var _ when closestBlock == nextClass: + nextClass = null; + break; + case var _ when closestBlock == nextStruct: + nextStruct = null; + break; + case var _ when closestBlock == nextInterface: + nextInterface = null; + break; + case var _ when closestBlock == nextEnum: + nextEnum = null; + break; + } + break; + } + + i = closestBlock.StartIndex; + } + + return typeList.Select(x => x.Value).ToList(); + } + + private bool TryFindClosestBlock(out BlockInfo closestBlock, params BlockInfo[] blocks) + { + closestBlock = null; + for (int i = 0; i < blocks.Length; i++) + { + if (blocks[i].FoundIndex == -1) + continue; + + if (closestBlock == null || closestBlock.FoundIndex > blocks[i].FoundIndex) + closestBlock = blocks[i]; + } + + return closestBlock != null; + } + + private BlockInfo FindNextTypeBlock(string text, int startIndex, TypeName blockType) + { + string typeKeyword; + switch (blockType) + { + case TypeName.Namespace: + typeKeyword = "namespace"; + break; + case TypeName.Class: + typeKeyword = "class"; + break; + case TypeName.Struct: + typeKeyword = "struct"; + break; + case TypeName.Interface: + typeKeyword = "interface"; + break; + case TypeName.Enum: + typeKeyword = "enum"; + break; + case TypeName.IdentationStart: + var identationStart = text.IndexOf("{", startIndex); + return new BlockInfo() { FoundIndex = identationStart, StartIndex = identationStart + 1, TypeName = TypeName.Undefined }; + case TypeName.IdentationEnd: + var identationEnd = text.IndexOf("}", startIndex); + return new BlockInfo() { FoundIndex = identationEnd, StartIndex = identationEnd + 1, TypeName = TypeName.Undefined }; + default: + throw new ArgumentException("Invalid block type provided"); + } + + int start = -1; + int blockStart = -1; + string name = string.Empty; + while (startIndex < text.Length) + { + _token.ThrowIfCancellationRequested(); + start = text.IndexOf($" {typeKeyword} ", startIndex); + if (start == -1) + return new BlockInfo { FoundIndex = -1 }; + + // Check if the caught type keyword matches the type definition + var openingBracket = text.IndexOf("{", start); + if (openingBracket == -1) + return new BlockInfo { FoundIndex = -1 }; + + var declaration = text.Substring(start, openingBracket - start); + var split = declaration.Split(' '); + + // Namespace detection + if (typeKeyword == "namespace") + { + // Expected result: [null] [namespace] [null] + if (split.Length == 4) + { + name = split[2]; + blockStart = openingBracket + 1; + break; + } + else + startIndex = openingBracket + 1; + } + // Class, Interface, Struct, Enum detection + else + { + // Expected result: [null] [keywordName] [typeName] ... [null] + // Skip any keywords that only contains [null] [keywordName] [null] + if (split.Length != 3) + { + name = split[2]; + blockStart = openingBracket + 1; + break; + } + else + startIndex = openingBracket + 1; + } + } + + var info = new BlockInfo() { FoundIndex = start, StartIndex = blockStart, Name = name, TypeName = blockType }; + return info; + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ScriptUtilityService.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ScriptUtilityService.cs.meta new file mode 100644 index 0000000..76d8018 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ScriptUtilityService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9db4298044e2add44bc3aa6ba898d7c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/ValidatorServiceProvider.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/ValidatorServiceProvider.cs new file mode 100644 index 0000000..0407f8f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/ValidatorServiceProvider.cs @@ -0,0 +1,24 @@ +using AssetStoreTools.Utility; +using AssetStoreTools.Validator.Services.Validation; + +namespace AssetStoreTools.Validator.Services +{ + internal class ValidatorServiceProvider : ServiceProvider + { + public static ValidatorServiceProvider Instance => _instance ?? (_instance = new ValidatorServiceProvider()); + private static ValidatorServiceProvider _instance; + + private ValidatorServiceProvider() { } + + protected override void RegisterServices() + { + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/ValidatorServiceProvider.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/ValidatorServiceProvider.cs.meta new file mode 100644 index 0000000..d2f5d3d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/ValidatorServiceProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47ac495c61171824abb2b72b1b7ef676 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions.meta new file mode 100644 index 0000000..7b3ebc2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 462cf5f916fad974a831f6a44aadcc82 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/AutomatedTest.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/AutomatedTest.cs new file mode 100644 index 0000000..fa49d8c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/AutomatedTest.cs @@ -0,0 +1,121 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestDefinitions +{ + internal class AutomatedTest : ValidationTest + { + public AutomatedTest(ValidationTestScriptableObject source) : base(source) { } + + public override void Run(ITestConfig config) + { + Type testClass = null; + MethodInfo testMethod = null; + + try + { + ValidateTestMethod(ref testClass, ref testMethod); + ValidateConfig(config); + } + catch (Exception e) + { + Debug.LogError(e.Message); + return; + } + + object testClassInstance; + try + { + testClassInstance = CreateInstance(testClass, config); + } + catch (Exception e) + { + Debug.LogError($"Could not create an instance of class {testClass}:\n{e}"); + return; + } + + try + { + Result = (TestResult)testMethod.Invoke(testClassInstance, new object[0]); + } + catch (Exception e) + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + result.AddMessage("An exception was caught when running this test case. See Console for more details"); + Debug.LogError($"An exception was caught when running validation for test case '{Title}'\n{e}"); + Result = result; + } + } + + private void ValidateTestMethod(ref Type testClass, ref MethodInfo testMethod) + { + if (TestScript == null || (testClass = TestScript.GetClass()) == null) + throw new Exception($"Cannot run test {Title} - Test Script class was not found"); + + var interfaces = testClass.GetInterfaces(); + if (!interfaces.Contains(typeof(ITestScript))) + throw new Exception($"Cannot run test {Title} - Test Script class is not derived from {nameof(ITestScript)}"); + + testMethod = testClass.GetMethod("Run"); + if (testMethod == null) + throw new Exception($"Cannot run test {Title} - Run() method was not found"); + } + + private void ValidateConfig(ITestConfig config) + { + switch (ValidationType) + { + case ValidationType.Generic: + case ValidationType.UnityPackage: + if (config is GenericTestConfig) + return; + break; + default: + throw new NotImplementedException("Undefined validation type"); + } + + throw new Exception("Config does not match the validation type"); + } + + private object CreateInstance(Type testClass, ITestConfig testConfig) + { + var constructors = testClass.GetConstructors(); + if (constructors.Length != 1) + throw new Exception($"Test class {testClass} should only contain a single constructor"); + + var constructor = constructors[0]; + var expectedParameters = constructor.GetParameters(); + var parametersToUse = new List(); + foreach (var expectedParam in expectedParameters) + { + var paramType = expectedParam.ParameterType; + + if (paramType == testConfig.GetType()) + { + parametersToUse.Add(testConfig); + continue; + } + + if (typeof(IValidatorService).IsAssignableFrom(paramType)) + { + var matchingService = ValidatorServiceProvider.Instance.GetService(paramType); + if (matchingService == null) + throw new Exception($"Service {paramType} is not registered and could not be retrieved"); + + parametersToUse.Add(matchingService); + continue; + } + + throw new Exception($"Invalid parameter type: {paramType}"); + } + + var instance = constructor.Invoke(parametersToUse.ToArray()); + return instance; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/AutomatedTest.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/AutomatedTest.cs.meta new file mode 100644 index 0000000..14f79c3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/AutomatedTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b284048af6fef0d49b8c3a37f7083d04 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/GenericTestConfig.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/GenericTestConfig.cs new file mode 100644 index 0000000..22dfedf --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/GenericTestConfig.cs @@ -0,0 +1,7 @@ +namespace AssetStoreTools.Validator.TestDefinitions +{ + internal class GenericTestConfig : ITestConfig + { + public string[] ValidationPaths { get; set; } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/GenericTestConfig.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/GenericTestConfig.cs.meta new file mode 100644 index 0000000..d982131 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/GenericTestConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ba1ae4e7b45a6c84ca8ad0eb391bf95d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestConfig.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestConfig.cs new file mode 100644 index 0000000..5db2e32 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestConfig.cs @@ -0,0 +1,4 @@ +namespace AssetStoreTools.Validator.TestDefinitions +{ + internal interface ITestConfig { } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestConfig.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestConfig.cs.meta new file mode 100644 index 0000000..ee42939 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c7e57766d04022c4dac58caf8ebe339a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestScript.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestScript.cs new file mode 100644 index 0000000..e705ab8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestScript.cs @@ -0,0 +1,9 @@ +using AssetStoreTools.Validator.Data; + +namespace AssetStoreTools.Validator.TestDefinitions +{ + internal interface ITestScript + { + TestResult Run(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestScript.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestScript.cs.meta new file mode 100644 index 0000000..f8c4743 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ITestScript.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 839ef1f3e773ab347b66932d3f810aec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects.meta new file mode 100644 index 0000000..1dfe2af --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d62652f91f698904ea662c6ab252ea59 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/AutomatedTestScriptableObject.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/AutomatedTestScriptableObject.cs new file mode 100644 index 0000000..31e6498 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/AutomatedTestScriptableObject.cs @@ -0,0 +1,11 @@ +#if UNITY_ASTOOLS_DEVELOPMENT +using UnityEngine; +#endif + +namespace AssetStoreTools.Validator.TestDefinitions +{ +#if UNITY_ASTOOLS_DEVELOPMENT + [CreateAssetMenu(fileName = "AutomatedTest", menuName = "Asset Store Validator/Automated Test")] +#endif + internal class AutomatedTestScriptableObject : ValidationTestScriptableObject { } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/AutomatedTestScriptableObject.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/AutomatedTestScriptableObject.cs.meta new file mode 100644 index 0000000..28f0608 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/AutomatedTestScriptableObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d813ff809ae82f643bf975031305d541 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/Editor.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/Editor.meta new file mode 100644 index 0000000..eebb867 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7cd52466a2239344d90c3043b7afc1e4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/Editor/ValidationTestScriptableObjectInspector.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/Editor/ValidationTestScriptableObjectInspector.cs new file mode 100644 index 0000000..5b28389 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/Editor/ValidationTestScriptableObjectInspector.cs @@ -0,0 +1,196 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Utility; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestDefinitions +{ + [CustomEditor(typeof(ValidationTestScriptableObject), true)] + internal class ValidationTestScriptableObjectInspector : UnityEditor.Editor + { + private enum FilterSeverity + { + Warning, + Fail + } + + private enum FilterType + { + UseFilter, + ExcludeFilter + } + + private ValidationTestScriptableObject _data; + private ValidationTestScriptableObject[] _allObjects; + + private SerializedProperty _script; + private SerializedProperty _validationType; + + private SerializedProperty _testScript; + private SerializedProperty _category; + private SerializedProperty _failFilterProperty; + private SerializedProperty _isInclusiveProperty; + private SerializedProperty _appliesToSubCategories; + private SerializedProperty _categoryFilter; + + private bool _hadChanges; + + private void OnEnable() + { + if (target == null) return; + + _data = target as ValidationTestScriptableObject; + + _script = serializedObject.FindProperty("m_Script"); + + _validationType = serializedObject.FindProperty(nameof(ValidationTestScriptableObject.ValidationType)); + + _testScript = serializedObject.FindProperty(nameof(ValidationTestScriptableObject.TestScript)); + _category = serializedObject.FindProperty(nameof(ValidationTestScriptableObject.CategoryInfo)); + _failFilterProperty = _category.FindPropertyRelative(nameof(ValidationTestScriptableObject.CategoryInfo.IsFailFilter)); + _isInclusiveProperty = _category.FindPropertyRelative(nameof(ValidationTestScriptableObject.CategoryInfo.IsInclusiveFilter)); + _appliesToSubCategories = _category.FindPropertyRelative(nameof(ValidationTestScriptableObject.CategoryInfo.AppliesToSubCategories)); + _categoryFilter = _category.FindPropertyRelative(nameof(ValidationTestScriptableObject.CategoryInfo.Filter)); + + _allObjects = ValidatorUtility.GetAutomatedTestCases(ValidatorUtility.SortType.Id); + _hadChanges = false; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.LabelField(GetInspectorTitle(), new GUIStyle(EditorStyles.centeredGreyMiniLabel) { fontSize = 24 }, GUILayout.MinHeight(50)); + + EditorGUI.BeginDisabledGroup(true); + EditorGUILayout.PropertyField(_script); + + EditorGUI.BeginChangeCheck(); + // ID field + EditorGUILayout.IntField("Test Id", _data.Id); + if (!ValidateID()) + EditorGUILayout.HelpBox("ID is already in use", MessageType.Warning); + EditorGUI.EndDisabledGroup(); + + EditorGUILayout.Space(8); + EditorGUILayout.LabelField("Test Data", new GUIStyle(EditorStyles.centeredGreyMiniLabel) { alignment = TextAnchor.MiddleLeft, fontSize = 14, padding = new RectOffset(0, 0, 0, 0) }); + + // Validation Type + var validationType = (ValidationType)EditorGUILayout.EnumPopup("Validation Type", (ValidationType)_validationType.enumValueIndex); + _validationType.enumValueIndex = (int)validationType; + + // Other fields + _data.Title = EditorGUILayout.TextField("Title", _data.Title); + if (string.IsNullOrEmpty(_data.Title)) + EditorGUILayout.HelpBox("Title cannot be empty", MessageType.Warning); + + EditorGUILayout.LabelField("Description"); + GUIStyle myTextAreaStyle = new GUIStyle(EditorStyles.textArea) { wordWrap = true }; + _data.Description = EditorGUILayout.TextArea(_data.Description, myTextAreaStyle); + + // Test script + EditorGUILayout.Space(8); + EditorGUILayout.LabelField("Test Script", new GUIStyle(EditorStyles.centeredGreyMiniLabel) { alignment = TextAnchor.MiddleLeft, fontSize = 14, padding = new RectOffset(0, 0, 0, 0) }); + + EditorGUILayout.PropertyField(_testScript); + if (_testScript.objectReferenceValue != null) + { + var generatedScriptType = (_testScript.objectReferenceValue as MonoScript).GetClass(); + if (generatedScriptType == null || !generatedScriptType.GetInterfaces().Contains(typeof(ITestScript))) + EditorGUILayout.HelpBox($"Test Script does not derive from {nameof(ITestScript)}. Test execution will fail", MessageType.Warning); + } + else if (!string.IsNullOrEmpty(_data.Title)) + { + var generatedScriptName = GenerateTestScriptName(); + EditorGUILayout.LabelField($"Proposed script name: {generatedScriptName}.cs", new GUIStyle("Label") { richText = true }); + EditorGUILayout.Space(); + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Generate Test Method Script", GUILayout.MaxWidth(200f))) + { + var generatedScript = ValidatorUtility.GenerateTestScript(generatedScriptName, (ValidationType)_validationType.enumValueIndex); + _testScript.objectReferenceValue = generatedScript; + } + EditorGUILayout.EndHorizontal(); + } + + // Variable Sevetity Options + EditorGUILayout.Space(8); + EditorGUILayout.LabelField("Variable Severity Status Filtering", new GUIStyle(EditorStyles.centeredGreyMiniLabel) { alignment = TextAnchor.MiddleLeft, fontSize = 14, padding = new RectOffset(0, 0, 0, 0) }); + + var filterSeverity = (FilterSeverity)EditorGUILayout.EnumPopup("Fail Type", _failFilterProperty.boolValue ? FilterSeverity.Fail : FilterSeverity.Warning); + _failFilterProperty.boolValue = filterSeverity == FilterSeverity.Fail ? true : false; + var filterType = (FilterType)EditorGUILayout.EnumPopup("Filtering rule", _isInclusiveProperty.boolValue ? FilterType.UseFilter : FilterType.ExcludeFilter); + _isInclusiveProperty.boolValue = filterType == FilterType.UseFilter ? true : false; + + EditorGUILayout.PropertyField(_appliesToSubCategories); + + EditorGUILayout.Space(10); + + EditorGUILayout.BeginHorizontal(GUI.skin.FindStyle("HelpBox")); + EditorGUILayout.LabelField(GetFilterDescription(_failFilterProperty.boolValue, _isInclusiveProperty.boolValue), new GUIStyle(GUI.skin.label) { wordWrap = true, richText = true }); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(10); + + EditorGUILayout.PropertyField(_categoryFilter); + + if (EditorGUI.EndChangeCheck()) + { + EditorUtility.SetDirty(target); + _hadChanges = true; + } + + _hadChanges = serializedObject.ApplyModifiedProperties() || _hadChanges; + } + + private string GetInspectorTitle() + { + switch (_data) + { + case AutomatedTestScriptableObject _: + return "Automated Test"; + default: + return "Miscellaneous Test"; + } + } + + private string GenerateTestScriptName() + { + var name = _data.Title.Replace(" ", ""); + return name; + } + + private string GetFilterDescription(bool isFailFilter, bool isInclusive) + { + string text = $"When a {TestResultStatus.VariableSeverityIssue} result type is returned from the test method:\n\n"; + if (isFailFilter) + { + if (isInclusive) + return text + "• Categories IN the filter will result in a FAIL.\n• Categories NOT in the filter will result in a WARNING"; + else + return text + "• Categories NOT in the filter will result in a FAIL.\n• Categories IN the filter will result in a WARNING"; + } + else + { + if (isInclusive) + return text + "• Categories IN the filter will result in a WARNING.\n• Categories NOT in the filter will result in a FAIL"; + else + return text + "• Categories NOT in the filter will result in a WARNING.\n• Categories IN the filter will result in a FAIL"; + } + } + + private bool ValidateID() + { + return !_allObjects.Any(x => x.Id == _data.Id && x != _data); + } + + private void OnDisable() + { + if (!_hadChanges) return; + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/Editor/ValidationTestScriptableObjectInspector.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/Editor/ValidationTestScriptableObjectInspector.cs.meta new file mode 100644 index 0000000..37b320c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/Editor/ValidationTestScriptableObjectInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 06d76b0e6df91eb43ac956f883c4a2da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/ValidationTestScriptableObject.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/ValidationTestScriptableObject.cs new file mode 100644 index 0000000..9c2a29f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/ValidationTestScriptableObject.cs @@ -0,0 +1,35 @@ +using AssetStoreTools.Validator.Categories; +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Utility; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestDefinitions +{ + internal abstract class ValidationTestScriptableObject : ScriptableObject + { + [SerializeField, HideInInspector] + private bool HasBeenInitialized; + + public int Id; + public string Title; + public string Description; + public ValidatorCategory CategoryInfo; + public ValidationType ValidationType; + public MonoScript TestScript; + + private void OnEnable() + { + // To do: maybe replace with Custom Inspector + if (HasBeenInitialized) + return; + + var existingTestCases = ValidatorUtility.GetAutomatedTestCases(ValidatorUtility.SortType.Id); + if (existingTestCases.Length > 0) + Id = existingTestCases[existingTestCases.Length - 1].Id + 1; + else + Id = 1; + HasBeenInitialized = true; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/ValidationTestScriptableObject.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/ValidationTestScriptableObject.cs.meta new file mode 100644 index 0000000..66bee1d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/Scriptable Objects/ValidationTestScriptableObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 11c2422f057b75a458e184d169a00eb6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ValidationTest.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ValidationTest.cs new file mode 100644 index 0000000..abb77f6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ValidationTest.cs @@ -0,0 +1,38 @@ +using AssetStoreTools.Validator.Categories; +using AssetStoreTools.Validator.Data; +using UnityEditor; + +namespace AssetStoreTools.Validator.TestDefinitions +{ + internal abstract class ValidationTest + { + public int Id; + public string Title; + public string Description; + public MonoScript TestScript; + + public ValidationType ValidationType; + public ValidatorCategory CategoryInfo; + + public TestResult Result; + + protected ValidationTest(ValidationTestScriptableObject source) + { + Id = source.Id; + Title = source.Title; + Description = source.Description; + TestScript = source.TestScript; + CategoryInfo = source.CategoryInfo; + ValidationType = source.ValidationType; + Result = new TestResult(); + } + + public abstract void Run(ITestConfig config); + + public string Slugify(string value) + { + string newValue = value.Replace(' ', '-').ToLower(); + return newValue; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ValidationTest.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ValidationTest.cs.meta new file mode 100644 index 0000000..5f7071b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Definitions/ValidationTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 095d629656748914bb6202598876fdf4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods.meta new file mode 100644 index 0000000..5a3363e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: daedaf78228b5184297e7ca334ea2a12 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic.meta new file mode 100644 index 0000000..a9b691e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ecfb23f95f16d2347a4063411aad8063 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAnimationClips.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAnimationClips.cs new file mode 100644 index 0000000..e7543b0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAnimationClips.cs @@ -0,0 +1,64 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityObject = UnityEngine.Object; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckAnimationClips : ITestScript + { + private static readonly string[] InvalidNames = new[] { "Take 001" }; + + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckAnimationClips(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + var badModels = new Dictionary>(); + var models = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Model); + + foreach (var model in models) + { + var badClips = new List(); + var clips = AssetDatabase.LoadAllAssetsAtPath(_assetUtility.ObjectToAssetPath(model)); + foreach (var clip in clips) + { + if (InvalidNames.Any(x => x.ToLower().Equals(clip.name.ToLower()))) + { + badClips.Add(clip); + } + } + + if (badClips.Count > 0) + badModels.Add(model, badClips); + } + + if (badModels.Count > 0) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following models have animation clips with invalid names. Animation clip names should be unique and reflective of the animation itself"); + foreach (var kvp in badModels) + { + result.AddMessage(_assetUtility.ObjectToAssetPath(kvp.Key), null, kvp.Value.ToArray()); + } + } + else + { + result.AddMessage("No animation clips with invalid names were found!"); + result.Status = TestResultStatus.Pass; + } + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAnimationClips.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAnimationClips.cs.meta new file mode 100644 index 0000000..4529bdf --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAnimationClips.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a28985886f182c4bacc89a44777c742 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAudioClipping.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAudioClipping.cs new file mode 100644 index 0000000..6f960e9 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAudioClipping.cs @@ -0,0 +1,128 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckAudioClipping : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckAudioClipping(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + // How many peaks above threshold are required for Audio Clips to be considered clipping + const int TOLERANCE = 2; + // Min. amount of consecutive samples above threshold required for peak detection + const int PEAK_STEPS = 1; + // Clipping threshold. More lenient here than Submission Guidelines (-0.3db) due to the problematic nature of + // correctly determining how sensitive the audio clipping flagging should be, as well as to account for any + // distortion introduced when AudioClips are compresssed after importing to Unity. + const float THRESHOLD = -0.05f; + // Samples for 16-bit audio files + const float S16b = 32767f; + float clippingThreshold = (S16b - (S16b / (2 * Mathf.Log10(1 / S16b)) * THRESHOLD)) / S16b; + TestResult result = new TestResult(); + var clippingAudioClips = new Dictionary(); + + var losslessAudioClips = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.NonLossyAudio).Select(x => x as AudioClip).ToList(); + foreach (var clip in losslessAudioClips) + { + var path = AssetDatabase.GetAssetPath(clip.GetInstanceID()); + + if (IsClipping(clip, TOLERANCE, PEAK_STEPS, clippingThreshold)) + clippingAudioClips.Add(clip, path); + } + + var lossyAudioClips = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.LossyAudio).Select(x => x as AudioClip).ToList(); + foreach (var clip in lossyAudioClips) + { + var path = AssetDatabase.GetAssetPath(clip.GetInstanceID()); + + if (IsClipping(clip, TOLERANCE, PEAK_STEPS, clippingThreshold)) + clippingAudioClips.Add(clip, path); + } + + if (clippingAudioClips.Count > 0) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following AudioClips are clipping or are very close to 0db ceiling. Please ensure your exported audio files have at least 0.3db of headroom (should peak at no more than -0.3db):", null, clippingAudioClips.Select(x => x.Key).ToArray()); + } + else + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No clipping audio files were detected."); + } + + return result; + } + + private bool IsClipping(AudioClip clip, int tolerance, int peakTolerance, float clippingThreshold) + { + if (DetectNumPeaksAboveThreshold(clip, peakTolerance, clippingThreshold) >= tolerance) + return true; + + return false; + } + + private int DetectNumPeaksAboveThreshold(AudioClip clip, int peakTolerance, float clippingThreshold) + { + float[] samples = new float[clip.samples * clip.channels]; + var data = clip.GetData(samples, 0); + + float[] samplesLeft = samples.Where((s, i) => i % 2 == 0).ToArray(); + float[] samplesRight = samples.Where((s, i) => i % 2 == 1).ToArray(); + + int peaks = 0; + + peaks = GetPeaksInChannel(samplesLeft, peakTolerance, clippingThreshold) + + GetPeaksInChannel(samplesRight, peakTolerance, clippingThreshold); + + return peaks; + } + + private int GetPeaksInChannel(float[] samples, int peakTolerance, float clippingThreshold) + { + int peaks = 0; + bool evalPeak = false; + int peakSteps = 0; + int step = 0; + + while (step < samples.Length) + { + if (Mathf.Abs(samples[step]) >= clippingThreshold && evalPeak) + { + peakSteps++; + } + + if (Mathf.Abs(samples[step]) >= clippingThreshold && !evalPeak) + { + evalPeak = true; + peakSteps++; + } + + if (Mathf.Abs(samples[step]) < clippingThreshold && evalPeak) + { + evalPeak = false; + if (peakSteps >= peakTolerance) + peaks++; + peakSteps = 0; + } + + step++; + } + + return peaks; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAudioClipping.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAudioClipping.cs.meta new file mode 100644 index 0000000..06f0892 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckAudioClipping.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f604db0353da0cb46bb048f5cd37186f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckColliders.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckColliders.cs new file mode 100644 index 0000000..5f4499b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckColliders.cs @@ -0,0 +1,55 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckColliders : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + private IMeshUtilityService _meshUtility; + + public CheckColliders(GenericTestConfig config, IAssetUtilityService assetUtility, IMeshUtilityService meshUtility) + { + _config = config; + _assetUtility = assetUtility; + _meshUtility = meshUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var prefabs = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Prefab); + var badPrefabs = new List(); + + foreach (var p in prefabs) + { + var meshes = _meshUtility.GetCustomMeshesInObject(p); + + if (!p.isStatic || !meshes.Any()) + continue; + + var colliders = p.GetComponentsInChildren(true); + if (!colliders.Any()) + badPrefabs.Add(p); + } + + if (badPrefabs.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All found prefabs have colliders!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following prefabs contain meshes, but colliders were not found", null, badPrefabs.ToArray()); + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckColliders.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckColliders.cs.meta new file mode 100644 index 0000000..c90549a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckColliders.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 308b3d7b7a883b949a14f47cfd5c7ebe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckCompressedFiles.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckCompressedFiles.cs new file mode 100644 index 0000000..330c017 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckCompressedFiles.cs @@ -0,0 +1,121 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityObject = UnityEngine.Object; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckCompressedFiles : ITestScript + { + private enum ArchiveResult + { + Allowed, + NotAllowed, + TarGzWithIssues, + ZipWithIssues + } + + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + private IFileSignatureUtilityService _fileSignatureUtility; + + public CheckCompressedFiles(GenericTestConfig config, IAssetUtilityService assetUtility, IFileSignatureUtilityService fileSignatureUtility) + { + _config = config; + _assetUtility = assetUtility; + _fileSignatureUtility = fileSignatureUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var checkedArchives = new Dictionary(); + + // Retrieving assets via GetObjectsFromAssets() is insufficient because + // archives might be renamed and not use the expected extension + var allAssetPaths = _assetUtility.GetAssetPathsFromAssets(_config.ValidationPaths, AssetType.All); + + foreach (var assetPath in allAssetPaths) + { + ArchiveType archiveType; + if ((archiveType = _fileSignatureUtility.GetArchiveType(assetPath)) == ArchiveType.None) + continue; + + var archiveObj = _assetUtility.AssetPathToObject(assetPath); + + switch (archiveType) + { + case ArchiveType.TarGz: + if (assetPath.ToLower().EndsWith(".unitypackage")) + checkedArchives.Add(archiveObj, ArchiveResult.Allowed); + else + checkedArchives.Add(archiveObj, ArchiveResult.TarGzWithIssues); + break; + case ArchiveType.Zip: + if (FileNameContainsKeyword(assetPath, "source") && assetPath.ToLower().EndsWith(".zip")) + checkedArchives.Add(archiveObj, ArchiveResult.Allowed); + else + checkedArchives.Add(archiveObj, ArchiveResult.ZipWithIssues); + break; + default: + checkedArchives.Add(archiveObj, ArchiveResult.NotAllowed); + break; + } + } + + if (checkedArchives.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No archives were found in the package content!"); + return result; + } + + if (checkedArchives.Any(x => x.Value == ArchiveResult.Allowed)) + { + result.Status = TestResultStatus.Warning; + result.AddMessage("The following archives of allowed format were found in the package content.\n" + + "Please make sure they adhere to the nested archive guidelines:", null, + checkedArchives.Where(x => x.Value == ArchiveResult.Allowed).Select(x => x.Key).ToArray()); + } + + if (checkedArchives.Any(x => x.Value == ArchiveResult.TarGzWithIssues)) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following .gz archives were found in the package content.\n" + + " Gz archives are only allowed in form of '.unitypackage' files", null, + checkedArchives.Where(x => x.Value == ArchiveResult.TarGzWithIssues).Select(x => x.Key).ToArray()); + } + + if (checkedArchives.Any(x => x.Value == ArchiveResult.ZipWithIssues)) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following .zip archives were found in the package content.\n" + + " Zip archives should contain the keyword 'source' in the file name", null, + checkedArchives.Where(x => x.Value == ArchiveResult.ZipWithIssues).Select(x => x.Key).ToArray()); + } + + if (checkedArchives.Any(x => x.Value == ArchiveResult.NotAllowed)) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following archives are using formats that are not allowed:", null, + checkedArchives.Where(x => x.Value == ArchiveResult.NotAllowed).Select(x => x.Key).ToArray()); + } + + return result; + } + + private bool FileNameContainsKeyword(string filePath, string keyword) + { + var fileInfo = new FileInfo(filePath); + + if (!fileInfo.Exists) + return false; + + return fileInfo.Name.Remove(fileInfo.Name.Length - fileInfo.Extension.Length).ToLower().Contains(keyword.ToLower()); + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckCompressedFiles.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckCompressedFiles.cs.meta new file mode 100644 index 0000000..8bd989c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckCompressedFiles.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 84b23febe0d923842aef73b95da5f25b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckEmptyPrefabs.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckEmptyPrefabs.cs new file mode 100644 index 0000000..8a87dad --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckEmptyPrefabs.cs @@ -0,0 +1,46 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckEmptyPrefabs : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckEmptyPrefabs(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var prefabs = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Prefab); + var badPrefabs = new List(); + + foreach (var p in prefabs) + { + if (p.GetComponents().Length == 1 && p.transform.childCount == 0) + badPrefabs.Add(p); + } + + if (badPrefabs.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No empty prefabs were found!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following prefabs are empty", null, badPrefabs.ToArray()); + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckEmptyPrefabs.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckEmptyPrefabs.cs.meta new file mode 100644 index 0000000..fafd181 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckEmptyPrefabs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8055bed9373283e4793463b90b42f08f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckFileMenuNames.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckFileMenuNames.cs new file mode 100644 index 0000000..3f51144 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckFileMenuNames.cs @@ -0,0 +1,153 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEditor; +using UnityObject = UnityEngine.Object; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckFileMenuNames : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + private IScriptUtilityService _scriptUtility; + + public CheckFileMenuNames(GenericTestConfig config, IAssetUtilityService assetUtility, IScriptUtilityService scriptUtility) + { + _config = config; + _assetUtility = assetUtility; + _scriptUtility = scriptUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; + + #region Scripts + + var scripts = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.MonoScript).ToArray(); + var scriptTypes = _scriptUtility.GetTypesFromScriptAssets(scripts); + var affectedScripts = new Dictionary>(); + + foreach (var kvp in scriptTypes) + { + var badMethods = new List(); + foreach (var type in kvp.Value) + { + foreach (var method in type.GetMethods(bindingFlags)) + { + var attributes = method.GetCustomAttributes().ToList(); + if (attributes.Count == 0) + continue; + + var badAttributes = attributes.Where(x => !IsValidMenuItem(x.menuItem)).ToList(); + if (badAttributes.Count > 0) + badMethods.Add($"{string.Join("\n", badAttributes.Select(x => $"\'{x.menuItem}\'"))}\n(for method '{method.Name}')\n"); + } + } + + if (badMethods.Count > 0) + affectedScripts.Add(kvp.Key, badMethods); + } + + #endregion + + #region Precompiled Assemblies + + var assemblies = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.PrecompiledAssembly).ToArray(); + var assemblyTypes = _scriptUtility.GetTypesFromAssemblies(assemblies); + var affectedAssemblies = new Dictionary>(); + + foreach (var kvp in assemblyTypes) + { + var badMethods = new List(); + foreach (var type in kvp.Value) + { + foreach (var method in type.GetMethods(bindingFlags)) + { + var attributes = method.GetCustomAttributes().ToList(); + if (attributes.Count == 0) + continue; + + var badAttributes = attributes.Where(x => !IsValidMenuItem(x.menuItem)).ToList(); + if (badAttributes.Count > 0) + badMethods.Add($"{string.Join("\n", badAttributes.Select(x => (x as MenuItem).menuItem))}\n(Method '{method.Name}')\n"); + } + } + + if (badMethods.Count > 0) + affectedAssemblies.Add(kvp.Key, badMethods); + } + + #endregion + + if (affectedScripts.Count > 0 || affectedAssemblies.Count > 0) + { + if (affectedScripts.Count > 0) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following scripts contain invalid MenuItem names:"); + foreach (var kvp in affectedScripts) + { + var message = string.Empty; + foreach (var type in kvp.Value) + message += type + "\n"; + + message = message.Remove(message.Length - "\n".Length); + result.AddMessage(message, null, kvp.Key); + } + } + + if (affectedAssemblies.Count > 0) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following assemblies contain invalid MenuItem names:"); + foreach (var kvp in affectedAssemblies) + { + var message = string.Empty; + foreach (var type in kvp.Value) + message += type + "\n"; + + message = message.Remove(message.Length - "\n".Length); + result.AddMessage(message, null, kvp.Key); + } + } + } + else + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No MenuItems with invalid names were found!"); + } + + return result; + } + + private bool IsValidMenuItem(string menuItemName) + { + var acceptableMenuItems = new string[] + { + "File", + "Edit", + "Assets", + "GameObject", + "Component", + "Window", + "Help", + "CONTEXT", + "Tools" + }; + + menuItemName = menuItemName.Replace("\\", "/"); + if (acceptableMenuItems.Any(x => menuItemName.ToLower().StartsWith($"{x.ToLower()}/"))) + return true; + + return false; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckFileMenuNames.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckFileMenuNames.cs.meta new file mode 100644 index 0000000..0e0872b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckFileMenuNames.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d8e3b12ecc1fcd74d9a9f8d2b549fc63 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLODs.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLODs.cs new file mode 100644 index 0000000..8b8975c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLODs.cs @@ -0,0 +1,79 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Data.MessageActions; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckLODs : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckLODs(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var prefabs = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Prefab); + var badPrefabs = new Dictionary>(); + + foreach (var p in prefabs) + { + var meshFilters = p.GetComponentsInChildren(true); + var badMeshFilters = new List(); + var lodGroups = p.GetComponentsInChildren(true); + + foreach (var mf in meshFilters) + { + if (mf.name.Contains("LOD") && !IsPartOfLodGroup(mf, lodGroups)) + badMeshFilters.Add(mf); + } + + if (badMeshFilters.Count > 0) + badPrefabs.Add(p, badMeshFilters); + } + + if (badPrefabs.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All found prefabs are meeting the LOD requirements!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following prefabs do not meet the LOD requirements"); + + foreach (var p in badPrefabs) + { + var resultList = new List(); + resultList.Add(p.Key); + resultList.AddRange(p.Value); + result.AddMessage($"{p.Key.name}.prefab", new OpenAssetAction(p.Key), resultList.ToArray()); + } + + return result; + } + + private bool IsPartOfLodGroup(MeshFilter mf, LODGroup[] lodGroups) + { + foreach (var lodGroup in lodGroups) + { + // If MeshFilter is a child/deep child of a LodGroup AND is referenced in this LOD group - it is valid + if (mf.transform.IsChildOf(lodGroup.transform) && + lodGroup.GetLODs().Any(lod => lod.renderers.Any(renderer => renderer != null && renderer.gameObject == mf.gameObject))) + return true; + } + + return false; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLODs.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLODs.cs.meta new file mode 100644 index 0000000..060c427 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLODs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 43b2158602f87704fa7b91561cfc8678 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLineEndings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLineEndings.cs new file mode 100644 index 0000000..adb381c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLineEndings.cs @@ -0,0 +1,77 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System; +using System.Collections.Concurrent; +using System.Threading.Tasks; +using UnityEditor; +using UnityObject = UnityEngine.Object; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckLineEndings : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckLineEndings(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var scripts = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.MonoScript); + + var affectedScripts = new ConcurrentBag(); + var scriptContents = new ConcurrentDictionary(); + + // A separate dictionary is needed because MonoScript contents cannot be accessed outside of the main thread + foreach (var s in scripts) + if (s != null) + scriptContents.TryAdd(s, s.text); + + Parallel.ForEach(scriptContents, (s) => + { + if (HasInconsistentLineEndings(s.Value)) + affectedScripts.Add(s.Key); + }); + + if (affectedScripts.Count > 0) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following scripts have inconsistent line endings:", null, affectedScripts.ToArray()); + } + else + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No scripts with inconsistent line endings were found!"); + } + + return result; + } + + private bool HasInconsistentLineEndings(string text) + { + int crlfEndings = 0; + int lfEndings = 0; + + var split = text.Split(new[] { "\n" }, StringSplitOptions.None); + for (int i = 0; i < split.Length; i++) + { + var line = split[i]; + if (line.EndsWith("\r")) + crlfEndings++; + else if (i != split.Length - 1) + lfEndings++; + } + + if (crlfEndings > 0 && lfEndings > 0) + return true; + return false; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLineEndings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLineEndings.cs.meta new file mode 100644 index 0000000..fb52149 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckLineEndings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 85885005d1c594f42826de3555e98365 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMeshPrefabs.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMeshPrefabs.cs new file mode 100644 index 0000000..2c37803 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMeshPrefabs.cs @@ -0,0 +1,106 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckMeshPrefabs : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + private IMeshUtilityService _meshUtility; + + public CheckMeshPrefabs(GenericTestConfig config, IAssetUtilityService assetUtility, IMeshUtilityService meshUtility) + { + _config = config; + _assetUtility = assetUtility; + _meshUtility = meshUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var usedModelPaths = new List(); + var prefabs = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Prefab); + var missingMeshReferencePrefabs = new List(); + + // Get all meshes in existing prefabs and check if prefab has missing mesh references + foreach (var p in prefabs) + { + var meshes = _meshUtility.GetCustomMeshesInObject(p); + foreach (var mesh in meshes) + { + string meshPath = _assetUtility.ObjectToAssetPath(mesh); + usedModelPaths.Add(meshPath); + } + + if (HasMissingMeshReferences(p)) + missingMeshReferencePrefabs.Add(p); + } + + // Get all meshes in existing models + var allModelPaths = GetAllModelMeshPaths(_config.ValidationPaths); + + // Get the list of meshes without prefabs + List unusedModels = allModelPaths.Except(usedModelPaths).ToList(); + + if (unusedModels.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All found prefabs have meshes!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + var models = unusedModels.Select(_assetUtility.AssetPathToObject).ToArray(); + result.AddMessage("The following models do not have associated prefabs", null, models); + + if (missingMeshReferencePrefabs.Count > 0) + result.AddMessage("The following prefabs have missing mesh references", null, missingMeshReferencePrefabs.ToArray()); + + return result; + } + + private IEnumerable GetAllModelMeshPaths(string[] validationPaths) + { + var models = _assetUtility.GetObjectsFromAssets(validationPaths, AssetType.Model); + var paths = new List(); + + foreach (var o in models) + { + var m = (GameObject)o; + var modelPath = _assetUtility.ObjectToAssetPath(m); + var assetImporter = _assetUtility.GetAssetImporter(modelPath); + if (assetImporter is UnityEditor.ModelImporter modelImporter) + { + var clips = modelImporter.clipAnimations.Count(); + var meshes = _meshUtility.GetCustomMeshesInObject(m); + + // Only add if the model has meshes and no clips + if (meshes.Any() && clips == 0) + paths.Add(modelPath); + } + } + + return paths; + } + + private bool HasMissingMeshReferences(GameObject go) + { + var meshes = go.GetComponentsInChildren(true); + var skinnedMeshes = go.GetComponentsInChildren(true); + + if (meshes.Length == 0 && skinnedMeshes.Length == 0) + return false; + + if (meshes.Any(x => x.sharedMesh == null) || skinnedMeshes.Any(x => x.sharedMesh == null)) + return true; + + return false; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMeshPrefabs.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMeshPrefabs.cs.meta new file mode 100644 index 0000000..3419dd1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMeshPrefabs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3c3d0d642ac6a6a48aa124a93dae3734 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinAssets.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinAssets.cs new file mode 100644 index 0000000..089d95e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinAssets.cs @@ -0,0 +1,66 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckMissingComponentsinAssets : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckMissingComponentsinAssets(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var assets = GetAllAssetsWithMissingComponents(_config.ValidationPaths); + + if (assets.Length == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No assets have missing components!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following assets contain missing components", null, assets); + + return result; + } + + private GameObject[] GetAllAssetsWithMissingComponents(string[] validationPaths) + { + var missingReferenceAssets = new List(); + var prefabObjects = _assetUtility.GetObjectsFromAssets(validationPaths, AssetType.Prefab); + + foreach (var p in prefabObjects) + { + if (p != null && IsMissingReference(p)) + missingReferenceAssets.Add(p); + } + + return missingReferenceAssets.ToArray(); + } + + private bool IsMissingReference(GameObject asset) + { + var components = asset.GetComponentsInChildren(); + + foreach (var c in components) + { + if (!c) + return true; + } + + return false; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinAssets.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinAssets.cs.meta new file mode 100644 index 0000000..d7e72f3 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinAssets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 22d8f814e2363e34ea220736a4042728 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinScenes.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinScenes.cs new file mode 100644 index 0000000..3c0ee06 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinScenes.cs @@ -0,0 +1,93 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Data.MessageActions; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using SceneAsset = UnityEditor.SceneAsset; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckMissingComponentsinScenes : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + private ISceneUtilityService _sceneUtility; + + public CheckMissingComponentsinScenes(GenericTestConfig config, IAssetUtilityService assetUtility, ISceneUtilityService sceneUtility) + { + _config = config; + _assetUtility = assetUtility; + _sceneUtility = sceneUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var originalScenePath = _sceneUtility.CurrentScenePath; + + var scenePaths = _assetUtility.GetAssetPathsFromAssets(_config.ValidationPaths, AssetType.Scene); + foreach (var scenePath in scenePaths) + { + var missingComponentGOs = GetMissingComponentGOsInScene(scenePath); + + if (missingComponentGOs.Count == 0) + continue; + + result.Status = TestResultStatus.VariableSeverityIssue; + var message = $"GameObjects with missing components or prefab references found in {scenePath}.\n\nClick this message to open the Scene and see the affected GameObjects:"; + result.AddMessage(message, new OpenAssetAction(_assetUtility.AssetPathToObject(scenePath)), missingComponentGOs.ToArray()); + } + + _sceneUtility.OpenScene(originalScenePath); + + if (result.Status == TestResultStatus.Undefined) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No missing components were found!"); + } + + return result; + } + + private List GetMissingComponentGOsInScene(string path) + { + var missingComponentGOs = new List(); + + var scene = _sceneUtility.OpenScene(path); + + if (!scene.IsValid()) + { + Debug.LogWarning("Unable to get Scene in " + path); + return new List(); + } + + var rootObjects = scene.GetRootGameObjects(); + + foreach (var obj in rootObjects) + { + missingComponentGOs.AddRange(GetMissingComponentGOs(obj)); + } + + return missingComponentGOs; + } + + private List GetMissingComponentGOs(GameObject root) + { + var missingComponentGOs = new List(); + var rootComponents = root.GetComponents(); + + if (UnityEditor.PrefabUtility.GetPrefabInstanceStatus(root) == UnityEditor.PrefabInstanceStatus.MissingAsset || rootComponents.Any(c => !c)) + { + missingComponentGOs.Add(root); + } + + foreach (Transform child in root.transform) + missingComponentGOs.AddRange(GetMissingComponentGOs(child.gameObject)); + + return missingComponentGOs; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinScenes.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinScenes.cs.meta new file mode 100644 index 0000000..3ff3408 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckMissingComponentsinScenes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 511e76d0ebcb23d40a7b49dda0e2980f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelImportLogs.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelImportLogs.cs new file mode 100644 index 0000000..b628aac --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelImportLogs.cs @@ -0,0 +1,64 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using UnityObject = UnityEngine.Object; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckModelImportLogs : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + private IModelUtilityService _modelUtility; + + public CheckModelImportLogs(GenericTestConfig config, IAssetUtilityService assetUtility, IModelUtilityService modelUtility) + { + _config = config; + _assetUtility = assetUtility; + _modelUtility = modelUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var models = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Model); + var importLogs = _modelUtility.GetImportLogs(models.ToArray()); + + var warningModels = new List(); + var errorModels = new List(); + + foreach (var kvp in importLogs) + { + if (kvp.Value.Any(x => x.Severity == UnityEngine.LogType.Error)) + errorModels.Add(kvp.Key); + if (kvp.Value.Any(x => x.Severity == UnityEngine.LogType.Warning)) + warningModels.Add(kvp.Key); + } + + if (warningModels.Count > 0 || errorModels.Count > 0) + { + if (warningModels.Count > 0) + { + result.Status = TestResultStatus.Warning; + result.AddMessage("The following models contain import warnings:", null, warningModels.ToArray()); + } + + if (errorModels.Count > 0) + { + result.Status = TestResultStatus.Warning; + result.AddMessage("The following models contain import errors:", null, errorModels.ToArray()); + } + } + else + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No issues were detected when importing your models!"); + } + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelImportLogs.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelImportLogs.cs.meta new file mode 100644 index 0000000..884b6cb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelImportLogs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 98f3ec209166855408eaf4abe5bff591 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelOrientation.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelOrientation.cs new file mode 100644 index 0000000..82e9dd0 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelOrientation.cs @@ -0,0 +1,71 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckModelOrientation : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + private IMeshUtilityService _meshUtility; + + public CheckModelOrientation(GenericTestConfig config, IAssetUtilityService assetUtility, IMeshUtilityService meshUtility) + { + _config = config; + _assetUtility = assetUtility; + _meshUtility = meshUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var models = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Model); + var badModels = new List(); + + foreach (var m in models) + { + var meshes = _meshUtility.GetCustomMeshesInObject(m); + var assetImporter = _assetUtility.GetAssetImporter(m); + + if (!(assetImporter is UnityEditor.ModelImporter modelImporter)) + continue; + + var clips = modelImporter.clipAnimations.Length; + + // Only check if the model has meshes and no clips + if (!meshes.Any() || clips != 0) + continue; + + Transform[] transforms = m.GetComponentsInChildren(true); + + foreach (var t in transforms) + { + var hasMeshComponent = t.TryGetComponent(out _) || t.TryGetComponent(out _); + + if (t.localRotation == Quaternion.identity || !hasMeshComponent) + continue; + + badModels.Add(m); + break; + } + } + + if (badModels.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All found models are facing the right way!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following models have incorrect rotation", null, badModels.ToArray()); + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelOrientation.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelOrientation.cs.meta new file mode 100644 index 0000000..7e8243e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelOrientation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56cdcdc41a80fbc46b5b2b83ec8d66d7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelTypes.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelTypes.cs new file mode 100644 index 0000000..a434a0c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelTypes.cs @@ -0,0 +1,58 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityObject = UnityEngine.Object; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckModelTypes : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckModelTypes(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var allowedExtensions = new string[] { ".fbx", ".dae", ".abc", ".obj" }; + // Should retrieve All assets and not models here since ADB will not recognize certain + // types if appropriate software is not installed (e.g. .blend without Blender) + var allAssetPaths = _assetUtility.GetAssetPathsFromAssets(_config.ValidationPaths, AssetType.All); + var badModels = new List(); + + foreach (var assetPath in allAssetPaths) + { + var importer = _assetUtility.GetAssetImporter(assetPath); + if (importer == null || !(importer is ModelImporter)) + continue; + + if (allowedExtensions.Any(x => importer.assetPath.ToLower().EndsWith(x))) + continue; + + badModels.Add(_assetUtility.AssetPathToObject(assetPath)); + } + + if (badModels.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All models are of allowed formats!"); + } + else + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following models are of formats that should not be used for Asset Store packages:", null, badModels.ToArray()); + } + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelTypes.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelTypes.cs.meta new file mode 100644 index 0000000..6f45fa7 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckModelTypes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 428b1fb838e6f5a469bbfd26ca3fbfd2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckNormalMapTextures.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckNormalMapTextures.cs new file mode 100644 index 0000000..d911351 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckNormalMapTextures.cs @@ -0,0 +1,94 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckNormalMapTextures : ITestScript + { + public const int TextureCacheLimit = 8; + + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckNormalMapTextures(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var materials = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Material); + var badTextures = new List(); + var badPaths = new List(); + + foreach (var mat in materials) + { + for (int i = 0; i < mat.shader.GetPropertyCount(); i++) + { + if ((mat.shader.GetPropertyFlags(i) & UnityEngine.Rendering.ShaderPropertyFlags.Normal) != 0) + { + var propertyName = mat.shader.GetPropertyName(i); + var assignedTexture = mat.GetTexture(propertyName); + + if (assignedTexture == null) + continue; + + var texturePath = _assetUtility.ObjectToAssetPath(assignedTexture); + var textureImporter = _assetUtility.GetAssetImporter(texturePath) as TextureImporter; + if (textureImporter == null) + continue; + + if (textureImporter.textureType != TextureImporterType.NormalMap && !badTextures.Contains(assignedTexture)) + { + if (badTextures.Count < TextureCacheLimit) + { + badTextures.Add(assignedTexture); + } + else + { + string path = AssetDatabase.GetAssetPath(assignedTexture); + badPaths.Add(path); + } + } + } + } + + EditorUtility.UnloadUnusedAssetsImmediate(); + } + + if (badTextures.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All normal map textures have the correct texture type!"); + } + else if (badPaths.Count != 0) + { + foreach (Texture texture in badTextures) + { + string path = AssetDatabase.GetAssetPath(texture); + badPaths.Add(path); + } + + string paths = string.Join("\n", badPaths); + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following textures are not set to type 'Normal Map'", null); + result.AddMessage(paths); + } + else + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following textures are not set to type 'Normal Map'", null, badTextures.ToArray()); + } + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckNormalMapTextures.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckNormalMapTextures.cs.meta new file mode 100644 index 0000000..f996702 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckNormalMapTextures.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d55cea510248f814eb2194c2b53f88d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPackageNaming.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPackageNaming.cs new file mode 100644 index 0000000..5b9bf46 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPackageNaming.cs @@ -0,0 +1,295 @@ +using AssetStoreTools.Utility; +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckPackageNaming : ITestScript + { + private const string ForbiddenCharacters = "~`!@#$%^&*()-_=+[{]}\\|;:'\",<>?/"; + private readonly string[] PathsToCheckForForbiddenCharacters = new string[] + { + "Assets/", + "Assets/Editor/", + "Assets/Plugins/", + "Assets/Resources/", + "Assets/StreamingAssets/", + "Assets/WebGLTemplates/" + }; + + private class PathCheckResult + { + public Object[] InvalidMainPaths; + public Object[] InvalidMainPathContentPaths; + public Object[] InvalidMainPathLeadingUpPaths; + public Object[] InvalidHybridPackages; + public Object[] PotentiallyInvalidContent; + + public bool HasIssues => InvalidMainPaths.Length > 0 + || InvalidMainPathContentPaths.Length > 0 + || InvalidMainPathLeadingUpPaths.Length > 0 + || InvalidHybridPackages.Length > 0 + || PotentiallyInvalidContent.Length > 0; + } + + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + // Constructor also accepts dependency injection of registered IValidatorService types + public CheckPackageNaming(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var checkResult = GetInvalidPathsInAssets(); + + if (checkResult.HasIssues) + { + result.Status = TestResultStatus.Warning; + + if (checkResult.InvalidMainPaths.Length > 0) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following assets appear to be artificially bumped up in the project hierarchy within commonly used folders", null, checkResult.InvalidMainPaths); + } + + if (checkResult.InvalidMainPathContentPaths.Length > 0) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following assets appear to be artificially bumped up in the project hierarchy within commonly used folders", null, checkResult.InvalidMainPathContentPaths); + } + + if (checkResult.InvalidMainPathLeadingUpPaths.Length > 0) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("Despite not being directly validated, this path would be automatically created by the Unity Importer when importing your package", null, checkResult.InvalidMainPathLeadingUpPaths); + } + + if (checkResult.InvalidHybridPackages.Length > 0) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following packages appear to be artificially bumped up in the Package hierarchy with their 'Display Name' configuration", null, checkResult.InvalidHybridPackages); + } + + if (checkResult.PotentiallyInvalidContent.Length > 0) + { + // Do not override previously set severities + result.AddMessage("It is recommended that nested package content refrains from starting with a special character", null, checkResult.PotentiallyInvalidContent); + } + } + else + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All package asset names are valid!"); + } + + return result; + } + + private PathCheckResult GetInvalidPathsInAssets() + { + var allInvalidMainPaths = new List(); + var allInvalidMainContentPaths = new List(); + var allInvalidMainLeadingUpPaths = new List(); + var allInvalidPackagePaths = new List(); + var allInvalidOtherContentPaths = new List(); + + foreach (var validationPath in _config.ValidationPaths) + { + // Is path itself not forbidden e.g.: when validating Assets/_Package, the folder _Package would be invalid + if (!IsDirectMainPathValid(validationPath)) + allInvalidMainPaths.Add(validationPath); + + // Are path contents not forbidden e.g.: when validating Assets/, the folder _Package would be invalid + if (!IsDirectMainPathContentValid(validationPath, out var invalidContentPaths)) + allInvalidMainContentPaths.AddRange(invalidContentPaths); + + // Is the path leading up to this path not forbidden e.g: when validating Assets/_WorkDir/Package, the folder _Workdir would be invalid + if (!IsPathLeadingUpToMainPathValid(validationPath, out var invalidLeadingUpPath)) + allInvalidMainLeadingUpPaths.Add(invalidLeadingUpPath); + + // Is the path pointing to a package valid, e.g.: when validating Packages/com.company.product its display name _Product would be invalid + if (!IsHybridPackageMainPathValid(validationPath, out string invalidPackagePath)) + allInvalidPackagePaths.Add(invalidPackagePath); + } + + var ignoredPaths = new List(); + ignoredPaths.AddRange(allInvalidMainPaths); + ignoredPaths.AddRange(allInvalidMainContentPaths); + ignoredPaths.AddRange(allInvalidMainLeadingUpPaths); + ignoredPaths.AddRange(allInvalidPackagePaths); + + // Mark any other paths that start with a forbidden character + if (!ArePackageContentsValid(ignoredPaths, out var invalidContents)) + allInvalidOtherContentPaths.AddRange(invalidContents); + + return new PathCheckResult() + { + InvalidMainPaths = PathsToObjects(allInvalidMainPaths), + InvalidMainPathContentPaths = PathsToObjects(allInvalidMainContentPaths), + InvalidMainPathLeadingUpPaths = PathsToObjects(allInvalidMainLeadingUpPaths), + InvalidHybridPackages = PathsToObjects(allInvalidPackagePaths), + PotentiallyInvalidContent = PathsToObjects(allInvalidOtherContentPaths) + }; + } + + private bool IsDirectMainPathValid(string validationPath) + { + foreach (var forbiddenPath in PathsToCheckForForbiddenCharacters) + { + var forbiddenPathWithSeparator = forbiddenPath.EndsWith("/") ? forbiddenPath : forbiddenPath + "/"; + if (!validationPath.StartsWith(forbiddenPathWithSeparator)) + continue; + + var truncatedPath = validationPath.Remove(0, forbiddenPathWithSeparator.Length); + var truncatedPathSplit = truncatedPath.Split('/'); + + // It is not a direct main path if it has deeper paths + if (truncatedPathSplit.Length != 1) + continue; + + if (ForbiddenCharacters.Any(x => truncatedPath.StartsWith(x.ToString()))) + return false; + } + + return true; + } + + private bool IsDirectMainPathContentValid(string validationPath, out List invalidContentPaths) + { + invalidContentPaths = new List(); + + var contents = Directory.EnumerateFileSystemEntries(validationPath, "*", SearchOption.AllDirectories) + .Where(x => !x.EndsWith(".meta")) + .Select(GetAdbPath); + + foreach (var contentPath in contents) + { + foreach (var forbiddenPath in PathsToCheckForForbiddenCharacters) + { + var forbiddenPathWithSeparator = forbiddenPath.EndsWith("/") ? forbiddenPath : forbiddenPath + "/"; + if (!contentPath.StartsWith(forbiddenPathWithSeparator)) + continue; + + var truncatedPath = contentPath.Remove(0, forbiddenPathWithSeparator.Length); + var truncatedPathSplit = truncatedPath.Split('/'); + + // Only check the first level of content relative to the forbidden path + if (truncatedPathSplit.Length > 1) + continue; + + if (ForbiddenCharacters.Any(x => truncatedPathSplit[0].StartsWith(x.ToString()))) + invalidContentPaths.Add(contentPath); + } + } + + return invalidContentPaths.Count == 0; + } + + private bool IsPathLeadingUpToMainPathValid(string validationPath, out string invalidLeadingUpPath) + { + invalidLeadingUpPath = string.Empty; + + foreach (var forbiddenPath in PathsToCheckForForbiddenCharacters) + { + var forbiddenPathWithSeparator = forbiddenPath.EndsWith("/") ? forbiddenPath : forbiddenPath + "/"; + if (!validationPath.StartsWith(forbiddenPathWithSeparator)) + continue; + + var truncatedPath = validationPath.Remove(0, forbiddenPathWithSeparator.Length); + var truncatedPathSplit = truncatedPath.Split('/'); + + // It is not a leading up path if it has no deeper path + if (truncatedPathSplit.Length == 1) + continue; + + if (ForbiddenCharacters.Any(x => truncatedPathSplit[0].StartsWith(x.ToString()))) + { + invalidLeadingUpPath = forbiddenPathWithSeparator + truncatedPathSplit[0]; + return false; + } + } + + return true; + } + + private bool IsHybridPackageMainPathValid(string validationPath, out string invalidPackagePath) + { + invalidPackagePath = string.Empty; + + if (!PackageUtility.GetPackageByManifestPath($"{validationPath}/package.json", out var package)) + return true; + + var packageName = package.displayName; + if (ForbiddenCharacters.Any(x => packageName.StartsWith(x.ToString()))) + { + invalidPackagePath = validationPath; + return false; + } + + return true; + } + + private bool ArePackageContentsValid(IEnumerable ignoredPaths, out List invalidContentPaths) + { + invalidContentPaths = new List(); + + foreach (var validationPath in _config.ValidationPaths) + { + var validationPathFolderName = validationPath.Split('/').Last(); + if (!ignoredPaths.Contains(validationPath) && ForbiddenCharacters.Any(x => validationPathFolderName.StartsWith(x.ToString()))) + invalidContentPaths.Add(validationPath); + + var contents = Directory.EnumerateFileSystemEntries(validationPath, "*", SearchOption.AllDirectories) + .Where(x => !x.EndsWith(".meta")) + .Select(GetAdbPath); + + foreach (var contentEntry in contents) + { + if (ignoredPaths.Contains(contentEntry)) + continue; + + var entryName = contentEntry.Split('/').Last(); + if (ForbiddenCharacters.Any(x => entryName.StartsWith(x.ToString()))) + invalidContentPaths.Add(contentEntry); + } + } + + return invalidContentPaths.Count == 0; + } + + private string GetAdbPath(string path) + { + path = path.Replace("\\", "/"); + if (path.StartsWith(Constants.RootProjectPath)) + path = path.Remove(Constants.RootProjectPath.Length); + + return path; + } + + private Object[] PathsToObjects(IEnumerable paths) + { + var objects = new List(); + + foreach (var path in paths) + { + var obj = _assetUtility.AssetPathToObject(path); + if (obj != null) + objects.Add(obj); + } + + return objects.ToArray(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPackageNaming.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPackageNaming.cs.meta new file mode 100644 index 0000000..bf1c234 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPackageNaming.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: afe9e04825c7d904981a54404b222290 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckParticleSystems.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckParticleSystems.cs new file mode 100644 index 0000000..8e69b10 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckParticleSystems.cs @@ -0,0 +1,76 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Data.MessageActions; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckParticleSystems : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + private ISceneUtilityService _sceneUtility; + + public CheckParticleSystems(GenericTestConfig config, IAssetUtilityService assetUtility, ISceneUtilityService sceneUtility) + { + _config = config; + _assetUtility = assetUtility; + _sceneUtility = sceneUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var originalScenePath = _sceneUtility.CurrentScenePath; + + var scenePaths = _assetUtility.GetAssetPathsFromAssets(_config.ValidationPaths, AssetType.Scene); + + foreach (var path in scenePaths) + { + var badParticleSystems = new List(); + + var scene = _sceneUtility.OpenScene(path); + + if (!scene.IsValid()) + { + Debug.LogWarning("Unable to get Scene in " + path); + continue; + } + +#if UNITY_2023_1_OR_NEWER + var particleSystems = GameObject.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); +#else + var particleSystems = GameObject.FindObjectsOfType(); +#endif + + foreach (var ps in particleSystems) + { + if (PrefabUtility.IsPartOfAnyPrefab(ps.gameObject)) + continue; + badParticleSystems.Add(ps); + } + + if (badParticleSystems.Count == 0) + continue; + + result.Status = TestResultStatus.VariableSeverityIssue; + var message = $"Particle Systems not belonging to any Prefab were found in {path}.\n\nClick this message to open the Scene and see the affected Particle Systems:"; + result.AddMessage(message, new OpenAssetAction(AssetDatabase.LoadAssetAtPath(path)), badParticleSystems.ToArray()); + } + + _sceneUtility.OpenScene(originalScenePath); + + if (result.Status == TestResultStatus.Undefined) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No Particle Systems without Prefabs were found!"); + } + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckParticleSystems.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckParticleSystems.cs.meta new file mode 100644 index 0000000..66a1881 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckParticleSystems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a623f7988c75884bb17b169ccd3e993 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPathLengths.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPathLengths.cs new file mode 100644 index 0000000..27750c5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPathLengths.cs @@ -0,0 +1,98 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using AssetStoreTools.Validator.Utility; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityObject = UnityEngine.Object; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckPathLengths : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckPathLengths(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + TestResult result = new TestResult(); + + int pathLengthLimit = 140; + // Get all project paths and sort by length so that folders always come before files + var allPaths = ValidatorUtility.GetProjectPaths(_config.ValidationPaths).OrderBy(x => x.Length); + + var filteredDirs = new Dictionary(); + var filteredFiles = new Dictionary(); + + foreach (var path in allPaths) + { + // Truncated path examples: + // Assets/[Scenes/SampleScene.unity] + // Packages/com.example.package/[Editor/EditorScript.cs] + var truncatedPath = path; + if (path.StartsWith("Assets/")) + truncatedPath = path.Remove(0, "Assets/".Length); + else if (path.StartsWith("Packages/")) + { + var splitPath = path.Split('/'); + truncatedPath = string.Join("/", splitPath.Skip(2)); + } + + // Skip paths under the character limit + if (truncatedPath.Length < pathLengthLimit) + continue; + + // Skip children of already added directories + if (filteredDirs.Keys.Any(x => truncatedPath.StartsWith(x))) + continue; + + if (AssetDatabase.IsValidFolder(path)) + { + filteredDirs.Add(truncatedPath, _assetUtility.AssetPathToObject(path)); + continue; + } + + if (!filteredFiles.ContainsKey(truncatedPath)) + filteredFiles.Add(truncatedPath, _assetUtility.AssetPathToObject(path)); + } + + if (filteredDirs.Count == 0 && filteredFiles.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All package content matches the path limit criteria!"); + return result; + } + + if (filteredDirs.Count > 0) + { + var maxDirLength = filteredDirs.Keys.Aggregate("", (max, cur) => max.Length > cur.Length ? max : cur); + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage($"The following folders exceed the path length limit:"); + foreach (var kvp in filteredDirs) + { + result.AddMessage($"Path length: {kvp.Key.Length} characters", null, kvp.Value); + } + } + + if (filteredFiles.Count > 0) + { + var maxFileLength = filteredFiles.Keys.Aggregate("", (max, cur) => max.Length > cur.Length ? max : cur); + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage($"The following files exceed the path length limit:"); + foreach (var kvp in filteredFiles) + { + result.AddMessage($"Path length: {kvp.Key.Length} characters", null, kvp.Value); + } + } + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPathLengths.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPathLengths.cs.meta new file mode 100644 index 0000000..ff8f8ed --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPathLengths.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ae379305e9165e84584373a8272c09e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPrefabTransforms.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPrefabTransforms.cs new file mode 100644 index 0000000..aa01881 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPrefabTransforms.cs @@ -0,0 +1,71 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckPrefabTransforms : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + private IMeshUtilityService _meshUtility; + + public CheckPrefabTransforms(GenericTestConfig config, IAssetUtilityService assetUtility, IMeshUtilityService meshUtility) + { + _config = config; + _assetUtility = assetUtility; + _meshUtility = meshUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var prefabs = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Prefab); + var badPrefabs = new List(); + var badPrefabsLowOffset = new List(); + + foreach (var p in prefabs) + { + var hasRectTransform = p.TryGetComponent(out RectTransform _); + if (hasRectTransform || !_meshUtility.GetCustomMeshesInObject(p).Any()) + continue; + + var positionString = p.transform.position.ToString("F12"); + var rotationString = p.transform.rotation.eulerAngles.ToString("F12"); + var localScaleString = p.transform.localScale.ToString("F12"); + + var vectorZeroString = Vector3.zero.ToString("F12"); + var vectorOneString = Vector3.one.ToString("F12"); + + if (positionString != vectorZeroString || rotationString != vectorZeroString || localScaleString != vectorOneString) + { + if (p.transform.position == Vector3.zero && p.transform.rotation.eulerAngles == Vector3.zero && p.transform.localScale == Vector3.one) + badPrefabsLowOffset.Add(p); + else + badPrefabs.Add(p); + } + } + + if (badPrefabs.Count == 0 && badPrefabsLowOffset.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All found prefabs were reset!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + if (badPrefabs.Count > 0) + result.AddMessage("The following prefabs' transforms do not fit the requirements", null, badPrefabs.ToArray()); + if (badPrefabsLowOffset.Count > 0) + result.AddMessage("The following prefabs have unusually low transform values, which might not be accurately displayed " + + "in the Inspector window. Please use the 'Debug' Inspector mode to review the Transform component of these prefabs " + + "or reset the Transform components using the right-click context menu", null, badPrefabsLowOffset.ToArray()); + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPrefabTransforms.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPrefabTransforms.cs.meta new file mode 100644 index 0000000..60db269 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckPrefabTransforms.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f712c17a60bf2d049a4e61c8f79e56c2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckScriptCompilation.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckScriptCompilation.cs new file mode 100644 index 0000000..034f457 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckScriptCompilation.cs @@ -0,0 +1,30 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.TestDefinitions; +using UnityEditor; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckScriptCompilation : ITestScript + { + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var hasCompilationErrors = EditorUtility.scriptCompilationFailed; + + if (hasCompilationErrors) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("One or more scripts in the project failed to compile.\n" + + "Please check the Console window to see the list of errors"); + } + else + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All scripts in the project compiled successfully!"); + } + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckScriptCompilation.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckScriptCompilation.cs.meta new file mode 100644 index 0000000..47d2849 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckScriptCompilation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 59db88f43969db8499299bce7f4fb967 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckShaderCompilation.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckShaderCompilation.cs new file mode 100644 index 0000000..a004feb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckShaderCompilation.cs @@ -0,0 +1,63 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Linq; +using UnityEditor; +using UnityEngine; +#if !UNITY_2023_1_OR_NEWER +using UnityEngine.Experimental.Rendering; +#endif +#if UNITY_2023_1_OR_NEWER +using UnityEngine.Rendering; +#endif + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckShaderCompilation : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckShaderCompilation(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var shaders = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Shader); + var badShaders = shaders.Where(ShaderHasError).ToArray(); + + if (badShaders.Length > 0) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following shader files have errors", null, badShaders); + } + else + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All found Shaders have no compilation errors!"); + } + + return result; + } + + private bool ShaderHasError(Object obj) + { + switch (obj) + { + case Shader shader: + return ShaderUtil.ShaderHasError(shader); + case ComputeShader shader: + return ShaderUtil.GetComputeShaderMessageCount(shader) > 0; + case RayTracingShader shader: + return ShaderUtil.GetRayTracingShaderMessageCount(shader) > 0; + default: + return false; + } + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckShaderCompilation.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckShaderCompilation.cs.meta new file mode 100644 index 0000000..43f501a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckShaderCompilation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7abb278a6082bde4391e0779394cb85b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTextureDimensions.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTextureDimensions.cs new file mode 100644 index 0000000..d05c1ae --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTextureDimensions.cs @@ -0,0 +1,57 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckTextureDimensions : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckTextureDimensions(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var textures = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Texture); + var badTextures = new List(); + + foreach (var texture in textures) + { + if (Mathf.IsPowerOfTwo(texture.width) && Mathf.IsPowerOfTwo(texture.height)) + continue; + + var importer = _assetUtility.GetAssetImporter(_assetUtility.ObjectToAssetPath(texture)); + + if (importer == null || !(importer is TextureImporter textureImporter) + || textureImporter.textureType == TextureImporterType.Sprite + || textureImporter.textureType == TextureImporterType.GUI) + continue; + + badTextures.Add(texture); + } + + if (badTextures.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All texture dimensions are a power of 2!"); + } + else + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following texture dimensions are not a power of 2:", null, badTextures.ToArray()); + } + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTextureDimensions.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTextureDimensions.cs.meta new file mode 100644 index 0000000..bc0d1cf --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTextureDimensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 073f1dacf3da34d4783140ae9d485d5f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTypeNamespaces.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTypeNamespaces.cs new file mode 100644 index 0000000..35c9d4d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTypeNamespaces.cs @@ -0,0 +1,233 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityObject = UnityEngine.Object; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckTypeNamespaces : ITestScript + { + private readonly string[] ForbiddenNamespaces = new string[] { "Unity" }; + + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + private IScriptUtilityService _scriptUtility; + + private enum NamespaceEligibility + { + NoNamespace, + Ok, + Forbidden + } + + private class AnalysisResult + { + public Dictionary> TypesWithoutNamespaces; + public Dictionary> ForbiddenNamespaces; + + public bool HasIssues => TypesWithoutNamespaces.Count > 0 + || ForbiddenNamespaces.Count > 0; + } + + public CheckTypeNamespaces(GenericTestConfig config, IAssetUtilityService assetUtility, IScriptUtilityService scriptUtility) + { + _config = config; + _assetUtility = assetUtility; + _scriptUtility = scriptUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var scriptResult = CheckScripts(); + var assemblyResult = CheckAssemblies(); + + if (scriptResult.HasIssues || assemblyResult.HasIssues) + { + result.Status = TestResultStatus.Warning; + + // Error conditions for forbidden namespaces + + if (scriptResult.ForbiddenNamespaces.Count > 0) + { + result.Status = TestResultStatus.Fail; + result.AddMessage("The following scripts contain namespaces starting with a 'Unity' keyword:"); + AddJoinedMessage(result, scriptResult.ForbiddenNamespaces); + } + + if (assemblyResult.ForbiddenNamespaces.Count > 0) + { + result.Status = TestResultStatus.Fail; + result.AddMessage("The following assemblies contain namespaces starting with a 'Unity' keyword:"); + AddJoinedMessage(result, assemblyResult.ForbiddenNamespaces); + } + + // Variable severity conditions for no-namespace types + + if (scriptResult.TypesWithoutNamespaces.Count > 0) + { + if (result.Status != TestResultStatus.Fail) + result.Status = TestResultStatus.VariableSeverityIssue; + + result.AddMessage("The following scripts contain types not nested under a namespace:"); + AddJoinedMessage(result, scriptResult.TypesWithoutNamespaces); + } + + if (assemblyResult.TypesWithoutNamespaces.Count > 0) + { + if (result.Status != TestResultStatus.Fail) + result.Status = TestResultStatus.VariableSeverityIssue; + + result.AddMessage("The following assemblies contain types not nested under a namespace:"); + AddJoinedMessage(result, assemblyResult.TypesWithoutNamespaces); + } + } + else + { + result.Status = TestResultStatus.Pass; + result.AddMessage("All scripts contain valid namespaces!"); + } + + return result; + } + + private AnalysisResult CheckScripts() + { + var scripts = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.MonoScript).ToArray(); + var scriptNamespaces = _scriptUtility.GetTypeNamespacesFromScriptAssets(scripts); + + var scriptsWithoutNamespaces = new Dictionary>(); + var scriptsWithForbiddenNamespaces = new Dictionary>(); + + foreach (var kvp in scriptNamespaces) + { + var scriptAsset = kvp.Key; + var typesInScriptAsset = kvp.Value; + + var typesWithoutNamespace = new List(); + var discouragedNamespaces = new List(); + var forbiddenNamespaces = new List(); + + foreach (var t in typesInScriptAsset) + { + var eligibility = CheckNamespaceEligibility(t.Namespace); + + switch (eligibility) + { + case NamespaceEligibility.NoNamespace: + typesWithoutNamespace.Add(t.Name); + break; + case NamespaceEligibility.Forbidden: + if (!forbiddenNamespaces.Contains(t.Namespace)) + forbiddenNamespaces.Add(t.Namespace); + break; + case NamespaceEligibility.Ok: + break; + } + } + + if (typesWithoutNamespace.Count > 0) + scriptsWithoutNamespaces.Add(scriptAsset, typesWithoutNamespace); + + if (forbiddenNamespaces.Count > 0) + scriptsWithForbiddenNamespaces.Add(scriptAsset, forbiddenNamespaces); + } + + return new AnalysisResult + { + TypesWithoutNamespaces = scriptsWithoutNamespaces, + ForbiddenNamespaces = scriptsWithForbiddenNamespaces + }; + } + + private AnalysisResult CheckAssemblies() + { + var assemblies = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.PrecompiledAssembly).ToList(); + var assemblyTypes = _scriptUtility.GetTypesFromAssemblies(assemblies); + + var assembliesWithoutNamespaces = new Dictionary>(); + var assembliesWithForbiddenNamespaces = new Dictionary>(); + + foreach (var kvp in assemblyTypes) + { + var assemblyAsset = kvp.Key; + var typesInAssembly = kvp.Value; + + var typesWithoutNamespace = new List(); + var discouragedNamespaces = new List(); + var forbiddenNamespaces = new List(); + + foreach (var t in typesInAssembly) + { + var eligibility = CheckNamespaceEligibility(t.Namespace); + + switch (eligibility) + { + case NamespaceEligibility.NoNamespace: + typesWithoutNamespace.Add($"{GetTypeName(t)} {t.Name}"); + break; + case NamespaceEligibility.Forbidden: + if (!forbiddenNamespaces.Contains(t.Namespace)) + forbiddenNamespaces.Add(t.Namespace); + break; + case NamespaceEligibility.Ok: + break; + } + } + + if (typesWithoutNamespace.Count > 0) + assembliesWithoutNamespaces.Add(assemblyAsset, typesWithoutNamespace); + + if (forbiddenNamespaces.Count > 0) + assembliesWithForbiddenNamespaces.Add(assemblyAsset, forbiddenNamespaces); + } + + return new AnalysisResult + { + TypesWithoutNamespaces = assembliesWithoutNamespaces, + ForbiddenNamespaces = assembliesWithForbiddenNamespaces + }; + } + + private NamespaceEligibility CheckNamespaceEligibility(string fullNamespace) + { + if (string.IsNullOrEmpty(fullNamespace)) + return NamespaceEligibility.NoNamespace; + + var split = fullNamespace.Split('.'); + var topLevelNamespace = split[0]; + if (ForbiddenNamespaces.Any(x => topLevelNamespace.StartsWith(x, StringComparison.OrdinalIgnoreCase))) + return NamespaceEligibility.Forbidden; + + return NamespaceEligibility.Ok; + } + + private string GetTypeName(Type type) + { + if (type.IsClass) + return "class"; + if (type.IsInterface) + return "interface"; + if (type.IsEnum) + return "enum"; + if (type.IsValueType) + return "struct"; + + throw new ArgumentException($"Received an unrecognizable type {type}. Type must be either a class, interface, struct or enum"); + } + + private void AddJoinedMessage(TestResult result, Dictionary> assetsWithMessages) + { + foreach (var kvp in assetsWithMessages) + { + var message = string.Join("\n", kvp.Value); + result.AddMessage(message, null, kvp.Key); + } + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTypeNamespaces.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTypeNamespaces.cs.meta new file mode 100644 index 0000000..e18155a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/CheckTypeNamespaces.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 279249fa7ef8c2446b3a9f013eeedbf0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveExecutableFiles.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveExecutableFiles.cs new file mode 100644 index 0000000..71f31bc --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveExecutableFiles.cs @@ -0,0 +1,38 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Linq; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class RemoveExecutableFiles : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public RemoveExecutableFiles(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var executables = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Executable).ToArray(); + + if (executables.Length == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No executable files were found!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following executable files were found", null, executables); + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveExecutableFiles.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveExecutableFiles.cs.meta new file mode 100644 index 0000000..04899bc --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveExecutableFiles.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8e4450592cc60e54286ad089b66db94d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJPGFiles.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJPGFiles.cs new file mode 100644 index 0000000..3a5a4c7 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJPGFiles.cs @@ -0,0 +1,38 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Linq; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class RemoveJPGFiles : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public RemoveJPGFiles(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var jpgs = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.JPG).ToArray(); + + if (jpgs.Length == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No JPG/JPEG textures were found!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following textures are compressed as JPG/JPEG", null, jpgs); + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJPGFiles.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJPGFiles.cs.meta new file mode 100644 index 0000000..bc21513 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJPGFiles.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5634a12b3a8544c4585bbc280ae59ce2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJavaScriptFiles.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJavaScriptFiles.cs new file mode 100644 index 0000000..69a7681 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJavaScriptFiles.cs @@ -0,0 +1,38 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Linq; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class RemoveJavaScriptFiles : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public RemoveJavaScriptFiles(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var javascriptObjects = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.JavaScript).ToArray(); + + if (javascriptObjects.Length == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No UnityScript / JS files were found!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following assets are UnityScript / JS files", null, javascriptObjects); + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJavaScriptFiles.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJavaScriptFiles.cs.meta new file mode 100644 index 0000000..791abf8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveJavaScriptFiles.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ab1676bde9afba442b35fd3319c18063 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveLossyAudioFiles.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveLossyAudioFiles.cs new file mode 100644 index 0000000..6cdc77a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveLossyAudioFiles.cs @@ -0,0 +1,83 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using UnityObject = UnityEngine.Object; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class RemoveLossyAudioFiles : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public RemoveLossyAudioFiles(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + string SanitizeForComparison(UnityObject o) + { + Regex alphanumericRegex = new Regex("[^a-zA-Z0-9]"); + string path = _assetUtility.ObjectToAssetPath(o); + path = path.ToLower(); + + int extensionIndex = path.LastIndexOf('.'); + string extension = path.Substring(extensionIndex + 1); + string sanitized = path.Substring(0, extensionIndex); + + int separatorIndex = sanitized.LastIndexOf('/'); + sanitized = sanitized.Substring(separatorIndex); + sanitized = alphanumericRegex.Replace(sanitized, String.Empty); + sanitized = sanitized.Replace(extension, String.Empty); + sanitized = sanitized.Trim(); + + return sanitized; + } + + var lossyAudioObjects = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.LossyAudio).ToArray(); + if (lossyAudioObjects.Length == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No lossy audio files were found!"); + return result; + } + + // Try to find and match variants + var nonLossyAudioObjects = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.NonLossyAudio); + HashSet nonLossyPathSet = new HashSet(); + foreach (var asset in nonLossyAudioObjects) + { + var path = SanitizeForComparison(asset); + nonLossyPathSet.Add(path); + } + + var unmatchedAssets = new List(); + foreach (var asset in lossyAudioObjects) + { + var path = SanitizeForComparison(asset); + if (!nonLossyPathSet.Contains(path)) + unmatchedAssets.Add(asset); + } + + if (unmatchedAssets.Count == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No lossy audio files were found!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following lossy audio files were found without identically named non-lossy variants:", null, unmatchedAssets.ToArray()); + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveLossyAudioFiles.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveLossyAudioFiles.cs.meta new file mode 100644 index 0000000..729af7f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveLossyAudioFiles.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b7205a85061273a4eb50586f13f35d35 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveMixamoFiles.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveMixamoFiles.cs new file mode 100644 index 0000000..17a1cb2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveMixamoFiles.cs @@ -0,0 +1,38 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Linq; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class RemoveMixamoFiles : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public RemoveMixamoFiles(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var mixamoFiles = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Mixamo).ToArray(); + + if (mixamoFiles.Length == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No Mixamo files were found!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following Mixamo files were found", null, mixamoFiles); + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveMixamoFiles.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveMixamoFiles.cs.meta new file mode 100644 index 0000000..7a1e4f8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveMixamoFiles.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9df432e52aa958b44bb5e20c13d16552 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveSpeedTreeFiles.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveSpeedTreeFiles.cs new file mode 100644 index 0000000..feef230 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveSpeedTreeFiles.cs @@ -0,0 +1,38 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Linq; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class RemoveSpeedTreeFiles : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public RemoveSpeedTreeFiles(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var speedtreeObjects = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.SpeedTree).ToArray(); + + if (speedtreeObjects.Length == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No SpeedTree assets have been found!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following SpeedTree assets have been found", null, speedtreeObjects); + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveSpeedTreeFiles.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveSpeedTreeFiles.cs.meta new file mode 100644 index 0000000..855dbe2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveSpeedTreeFiles.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e06bb7e0aa4f9944abc18281c002dff4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveVideoFiles.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveVideoFiles.cs new file mode 100644 index 0000000..ae0e5ff --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveVideoFiles.cs @@ -0,0 +1,38 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Linq; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class RemoveVideoFiles : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public RemoveVideoFiles(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var videos = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.Video).ToArray(); + + if (videos.Length == 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No video files were found, looking good!"); + return result; + } + + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("The following video files were found", null, videos); + + return result; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveVideoFiles.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveVideoFiles.cs.meta new file mode 100644 index 0000000..98aea88 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/Generic/RemoveVideoFiles.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f99724c71b0de66419b5d6e8e9bfcc2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage.meta new file mode 100644 index 0000000..b8fde81 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 016d62b2cd8346a49815615efd1d6e39 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDemoScenes.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDemoScenes.cs new file mode 100644 index 0000000..6c635c2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDemoScenes.cs @@ -0,0 +1,172 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityObject = UnityEngine.Object; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckDemoScenes : ITestScript + { + private class DemoSceneScanResult + { + public List ValidAdbScenes; + public List HybridScenePaths; + public List NestedUnityPackages; + } + + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + private ISceneUtilityService _sceneUtility; + + public CheckDemoScenes(GenericTestConfig config, IAssetUtilityService assetUtility, ISceneUtilityService sceneUtility) + { + _config = config; + _assetUtility = assetUtility; + _sceneUtility = sceneUtility; + } + + public TestResult Run() + { + var result = new TestResult(); + var demoSceneScanResult = CheckForDemoScenes(_config); + + // Valid demo scenes were found in ADB + if (demoSceneScanResult.ValidAdbScenes.Count > 0) + { + result.Status = TestResultStatus.Pass; + result.AddMessage("Demo scenes found", null, demoSceneScanResult.ValidAdbScenes.ToArray()); + return result; + } + + // Valid demo scenes found in UPM package.json + if (demoSceneScanResult.HybridScenePaths.Count > 0) + { + result.Status = TestResultStatus.Pass; + + var upmSampleSceneList = string.Join("\n-", demoSceneScanResult.HybridScenePaths); + upmSampleSceneList = upmSampleSceneList.Insert(0, "-"); + + result.AddMessage($"Demo scenes found:\n{upmSampleSceneList}"); + return result; + } + + // No valid scenes found, but package contains nested .unitypackages + if (demoSceneScanResult.NestedUnityPackages.Count > 0) + { + result.Status = TestResultStatus.Warning; + result.AddMessage("Could not find any valid Demo scenes in the selected validation paths."); + result.AddMessage("The following nested .unitypackage files were found. " + + "If they contain any demo scenes, you can ignore this warning.", null, demoSceneScanResult.NestedUnityPackages.ToArray()); + return result; + } + + // No valid scenes were found and there is nothing pointing to their inclusion in the package + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("Could not find any valid Demo Scenes in the selected validation paths."); + return result; + } + + private DemoSceneScanResult CheckForDemoScenes(GenericTestConfig config) + { + var scanResult = new DemoSceneScanResult(); + scanResult.ValidAdbScenes = CheckForDemoScenesInAssetDatabase(config); + scanResult.HybridScenePaths = CheckForDemoScenesInUpmSamples(config); + scanResult.NestedUnityPackages = CheckForNestedUnityPackages(config); + + return scanResult; + } + + private List CheckForDemoScenesInAssetDatabase(GenericTestConfig config) + { + var scenePaths = _assetUtility.GetAssetPathsFromAssets(config.ValidationPaths, AssetType.Scene).ToArray(); + if (scenePaths.Length == 0) + return new List(); + + var originalScenePath = _sceneUtility.CurrentScenePath; + var validScenePaths = scenePaths.Where(CanBeDemoScene).ToArray(); + _sceneUtility.OpenScene(originalScenePath); + + if (validScenePaths.Length == 0) + return new List(); + + return validScenePaths.Select(x => AssetDatabase.LoadAssetAtPath(x)).ToList(); + } + + private bool CanBeDemoScene(string scenePath) + { + // Check skybox + var sceneSkyboxPath = _assetUtility.ObjectToAssetPath(RenderSettings.skybox).Replace("\\", "").Replace("/", ""); + var defaultSkyboxPath = "Resources/unity_builtin_extra".Replace("\\", "").Replace("/", ""); + + if (!sceneSkyboxPath.Equals(defaultSkyboxPath, StringComparison.OrdinalIgnoreCase)) + return true; + + // Check GameObjects + _sceneUtility.OpenScene(scenePath); + var rootObjects = _sceneUtility.GetRootGameObjects(); + var count = rootObjects.Length; + + if (count == 0) + return false; + + if (count != 2) + return true; + + var cameraGOUnchanged = rootObjects.Any(o => o.TryGetComponent(out _) && o.GetComponents(typeof(Component)).Length == 3); + var lightGOUnchanged = rootObjects.Any(o => o.TryGetComponent(out _) && o.GetComponents(typeof(Component)).Length == 2); + + return !cameraGOUnchanged || !lightGOUnchanged; + } + + private List CheckForDemoScenesInUpmSamples(GenericTestConfig config) + { + var scenePaths = new List(); + + foreach (var path in config.ValidationPaths) + { + if (!File.Exists($"{path}/package.json")) + continue; + + var packageJsonText = File.ReadAllText($"{path}/package.json"); + var json = JObject.Parse(packageJsonText); + + if (!json.ContainsKey("samples") || json["samples"].Type != JTokenType.Array || json["samples"].ToList().Count == 0) + continue; + + foreach (var sample in json["samples"].ToList()) + { + var samplePath = sample["path"].ToString(); + samplePath = $"{path}/{samplePath}"; + if (!Directory.Exists(samplePath)) + continue; + + var sampleScenePaths = Directory.GetFiles(samplePath, "*.unity", SearchOption.AllDirectories); + foreach (var scenePath in sampleScenePaths) + { + // If meta file is not found, the sample will not be included with the exported .unitypackage + if (!File.Exists($"{scenePath}.meta")) + continue; + + if (!scenePaths.Contains(scenePath.Replace("\\", "/"))) + scenePaths.Add(scenePath.Replace("\\", "/")); + } + } + } + + return scenePaths; + } + + private List CheckForNestedUnityPackages(GenericTestConfig config) + { + var unityPackages = _assetUtility.GetObjectsFromAssets(config.ValidationPaths, AssetType.UnityPackage).ToArray(); + return unityPackages.ToList(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDemoScenes.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDemoScenes.cs.meta new file mode 100644 index 0000000..2707290 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDemoScenes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f844c2dfa4669ff4eacf5591b544edaf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDocumentation.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDocumentation.cs new file mode 100644 index 0000000..1799c1d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDocumentation.cs @@ -0,0 +1,73 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System; +using System.IO; +using System.Linq; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckDocumentation : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + public CheckDocumentation(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var textFilePaths = _assetUtility.GetAssetPathsFromAssets(_config.ValidationPaths, AssetType.Documentation).ToArray(); + var documentationFilePaths = textFilePaths.Where(CouldBeDocumentation).ToArray(); + + if (textFilePaths.Length == 0) + { + result.Status = TestResultStatus.VariableSeverityIssue; + result.AddMessage("No potential documentation files ('.txt', '.pdf', " + + "'.html', '.rtf', '.md') found within the given path."); + } + else if (documentationFilePaths.Length == 0) + { + result.Status = TestResultStatus.Warning; + var textFileObjects = textFilePaths.Select(_assetUtility.AssetPathToObject).ToArray(); + result.AddMessage("The following files have been found to match the documentation file format," + + " but may not be documentation in content", + null, textFileObjects); + } + else + { + result.Status = TestResultStatus.Pass; + var documentationFileObjects = documentationFilePaths.Select(_assetUtility.AssetPathToObject).ToArray(); + result.AddMessage("Found documentation files", null, documentationFileObjects); + } + + return result; + } + + private bool CouldBeDocumentation(string filePath) + { + if (filePath.EndsWith(".pdf")) + return true; + + using (var fs = File.Open(filePath, FileMode.Open)) + using (var bs = new BufferedStream(fs)) + using (var sr = new StreamReader(bs)) + { + string line; + while ((line = sr.ReadLine()) != null) + { + var mentionsDocumentation = line.IndexOf("documentation", StringComparison.OrdinalIgnoreCase) >= 0; + if (mentionsDocumentation) + return true; + } + } + + return false; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDocumentation.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDocumentation.cs.meta new file mode 100644 index 0000000..f8cea26 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckDocumentation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3c8425198983eda4c9b35aa0d59ea33c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckPackageSize.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckPackageSize.cs new file mode 100644 index 0000000..caa8ac6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckPackageSize.cs @@ -0,0 +1,69 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.TestDefinitions; +using System.IO; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckPackageSize : ITestScript + { + private GenericTestConfig _config; + + public CheckPackageSize(GenericTestConfig config) + { + _config = config; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var packageSize = CalculatePackageSize(_config.ValidationPaths); + float packageSizeInGB = packageSize / (1024f * 1024f * 1024f); + float maxPackageSizeInGB = Constants.Uploader.MaxPackageSizeBytes / (1024f * 1024f * 1024f); + + if (packageSizeInGB - maxPackageSizeInGB >= 0.1f) + { + result.Status = TestResultStatus.Warning; + + result.AddMessage($"The uncompressed size of your package ({packageSizeInGB:0.#} GB) exceeds the maximum allowed package size of {maxPackageSizeInGB:0.#} GB. " + + $"Please make sure that the compressed .unitypackage size does not exceed the size limit."); + } + else + { + result.Status = TestResultStatus.Pass; + result.AddMessage("Your package does not exceed the maximum allowed package size!"); + } + + return result; + } + + private long CalculatePackageSize(string[] assetPaths) + { + long totalSize = 0; + + foreach (var path in assetPaths) + { + totalSize += CalculatePathSize(path); + } + + return totalSize; + } + + private long CalculatePathSize(string path) + { + long size = 0; + + var dirInfo = new DirectoryInfo(path); + if (!dirInfo.Exists) + return size; + + foreach (var file in dirInfo.EnumerateFiles()) + size += file.Length; + + foreach (var nestedDir in dirInfo.EnumerateDirectories()) + size += CalculatePathSize(nestedDir.FullName); + + return size; + } + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckPackageSize.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckPackageSize.cs.meta new file mode 100644 index 0000000..1909c93 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckPackageSize.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a8601b99f4afa5049954f3a2dd5996d6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckProjectTemplateAssets.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckProjectTemplateAssets.cs new file mode 100644 index 0000000..eab7aee --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckProjectTemplateAssets.cs @@ -0,0 +1,217 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services.Validation; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator.TestMethods +{ + internal class CheckProjectTemplateAssets : ITestScript + { + private GenericTestConfig _config; + private IAssetUtilityService _assetUtility; + + // Constructor also accepts dependency injection of registered IValidatorService types + public CheckProjectTemplateAssets(GenericTestConfig config, IAssetUtilityService assetUtility) + { + _config = config; + _assetUtility = assetUtility; + } + + public TestResult Run() + { + var result = new TestResult() { Status = TestResultStatus.Undefined }; + + var assets = _assetUtility.GetObjectsFromAssets(_config.ValidationPaths, AssetType.All); + var invalidAssetsByGuid = CheckGuids(assets); + var invalidAssetsByPath = CheckPaths(assets); + + var hasIssues = invalidAssetsByGuid.Length > 0 + || invalidAssetsByPath.Length > 0; + + if (hasIssues) + { + result.Status = TestResultStatus.VariableSeverityIssue; + + if (invalidAssetsByPath.Length > 0) + { + result.AddMessage("The following assets were found to have an asset path which is common to project template asset paths. They should be renamed or moved:", null, invalidAssetsByPath); + } + + if (invalidAssetsByGuid.Length > 0) + { + result.AddMessage("The following assets were found to be using a GUID which is common to project template asset GUIDs. They should be assigned a new GUID:", null, invalidAssetsByGuid); + } + } + else + { + result.Status = TestResultStatus.Pass; + result.AddMessage("No common assets that might cause asset clashing were found!"); + } + + return result; + } + + private Object[] CheckGuids(IEnumerable assets) + { + var clashingAssets = new List(); + foreach (var asset in assets) + { + if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out var guid, out long _)) + continue; + + if (CommonTemplateAssets.Any(x => x.Key.Equals(guid, System.StringComparison.OrdinalIgnoreCase))) + clashingAssets.Add(asset); + } + + return clashingAssets.ToArray(); + } + + private Object[] CheckPaths(IEnumerable assets) + { + var clashingAssets = new List(); + foreach (var asset in assets) + { + var assetPath = AssetDatabase.GetAssetPath(asset); + if (CommonTemplateAssets.Any(x => x.Value.Equals(assetPath, System.StringComparison.OrdinalIgnoreCase))) + clashingAssets.Add(asset); + } + + return clashingAssets.ToArray(); + } + + private Dictionary CommonTemplateAssets = new Dictionary() + { + {"3f9215ea0144899419cfbc0957140d3f", "Assets/DefaultVolumeProfile.asset"}, + {"3d4c13846a3e9bd4c8ccfbd0657ed847", "Assets/DefaultVolumeProfile.asset"}, + {"4cee8bca36f2ab74b8feb832747fa6f4", "Assets/Editor/com.unity.mobile.notifications/NotificationSettings.asset"}, + {"45a04f37e0f48c744acc0874c4a8918a", "Assets/Editor/com.unity.mobile.notifications/NotificationSettings.asset"}, + {"54a3a0570aebe8949bec4966f1376581", "Assets/HDRPDefaultResources/DefaultHDRISky.exr"}, + {"e93c35b24eb03c74284e7dc0b755bfcc", "Assets/HDRPDefaultResources/DefaultHDRPAsset.asset"}, + {"254320a857a30444da2c99496a186368", "Assets/HDRPDefaultResources/DefaultLookDevProfile.asset"}, + {"2bfa7b9d63fa79e4abdc033f54a868d2", "Assets/HDRPDefaultResources/DefaultSceneRoot.prefab"}, + {"f9e3ff5a1b8f49c4fa8686e68d2dadae", "Assets/HDRPDefaultResources/DefaultSceneRoot.prefab"}, + {"d87f7d7815073e840834a16a518c1237", "Assets/HDRPDefaultResources/DefaultSettingsVolumeProfile.asset"}, + {"145290c901d58b343bdeb3b4362c9ff2", "Assets/HDRPDefaultResources/DefaultVFXResources.asset"}, + {"acc11144f57719542b5fa25f02e74afb", "Assets/HDRPDefaultResources/HDRenderPipelineGlobalSettings.asset"}, + {"582adbd84082fdb4faf7cd4beb1ccd14", "Assets/HDRPDefaultResources/HDRPDefaultSettings.asset"}, + {"2801c2ff7303a7543a8727f862f6c236", "Assets/HDRPDefaultResources/Sky and Fog Settings Profile.asset"}, + {"ea5c25297f0c0a04da0eabb1c26a7509", "Assets/HDRPDefaultResources/SkyFogSettingsProfile.asset"}, + {"3590b91b4603b465dbb4216d601bff33", "Assets/InputSystem_Actions.inputactions"}, + {"289c1b55c9541489481df5cc06664110", "Assets/InputSystem_Actions.inputactions"}, + {"dc70d2c4f369241dd99afd7c451b813e", "Assets/InputSystem_Actions.inputactions"}, + {"2bcd2660ca9b64942af0de543d8d7100", "Assets/InputSystem_Actions.inputactions"}, + {"052faaac586de48259a63d0c4782560b", "Assets/InputSystem_Actions.inputactions"}, + {"35845fe01580c41289b024647b1d1c53", "Assets/InputSystem_Actions.inputactions"}, + {"8124e5870f4fd4c779e7a5f994e84ad1", "Assets/OutdoorsScene.unity"}, + {"2dd802e4d37c65149922028d3e973832", "Assets/Presets/AudioCompressedInMemory.preset"}, + {"e18fd6ecd9cdb524ca99844f39b9d9ac", "Assets/Presets/AudioCompressedInMemory.preset"}, + {"86bcce7f5575b54408aa0f3a7d321039", "Assets/Presets/AudioStreaming.preset"}, + {"460e573eb8466884baaa0b8475505f83", "Assets/Presets/AudioStreaming.preset"}, + {"e8537455c6c08bd4e8bf0be3707da685", "Assets/Presets/Defaults/AlbedoTexture_Default.preset"}, + {"7a99f8aa944efe94cb9bd74562b7d5f9", "Assets/Presets/Defaults/AlbedoTexture_Default.preset"}, + {"0cd792cc87e492d43b4e95b205fc5cc6", "Assets/Presets/Defaults/AudioDecompressOnLoad_Default.preset"}, + {"e7689051185d12f4298e1ebb2693a29f", "Assets/Presets/Defaults/AudioDecompressOnLoad.preset"}, + {"463065d4f17d1d94d848aa127b94dd43", "Assets/Presets/Defaults/DirectionalLight_Default.preset"}, + {"c1cf8506f04ef2c4a88b64b6c4202eea", "Assets/Presets/Defaults/DirectionalLight_Default.preset"}, + {"8fa3055e2a1363246838debd20206d37", "Assets/Presets/Defaults/SSSSettings_Default.preset"}, + {"78830bb1431cab940b74be615e2a739f", "Assets/Presets/HDRTexture.preset"}, + {"14a57cf3b9fa1c74b884aa7e0dcf1faa", "Assets/Presets/NormalTexture.preset"}, + {"1d826a4c23450f946b19c20560595a1f", "Assets/Presets/NormalTexture.preset"}, + {"45f7b2e3c78185248b3adbb14429c2ab", "Assets/Presets/UtilityTexture.preset"}, + {"78fae3569c6c66c46afc3d9d4fb0b8d4", "Assets/Presets/UtilityTexture.preset"}, + {"9303d565bd8aa6948ba775e843320e4d", "Assets/Presets/UtilityTexture.preset"}, + {"34f54ff1ff9415249a847506b6f2fec5", "Assets/Scenes/PrefabEditingScene.unity"}, + {"cbfe36cfddfde964d9dfce63a355d5dd", "Assets/Scenes/samplescene.unity"}, + {"2cda990e2423bbf4892e6590ba056729", "Assets/Scenes/SampleScene.unity"}, + {"9fc0d4010bbf28b4594072e72b8655ab", "Assets/Scenes/SampleScene.unity"}, + {"3db1837cc97a95e4c98610966fac2b0b", "Assets/Scenes/SampleScene.unity"}, + {"3fc8acdd13e6c734bafef6554d6fdbcd", "Assets/Scenes/SampleScene.unity"}, + {"8c9cfa26abfee488c85f1582747f6a02", "Assets/Scenes/SampleScene.unity"}, + {"c850ee8c3b14cc8459e7e186857cf567", "Assets/Scenes/SampleScene.unity"}, + {"99c9720ab356a0642a771bea13969a05", "Assets/Scenes/SampleScene.unity"}, + {"d1c3109bdb54ad54c8a2b2838528e640", "Assets/Scenes/SampleScene.unity"}, + {"477cc4148fad3449482a3bc3178594e2", "Assets/Scenes/SampleSceneLightingSettings.lighting"}, + {"4eb578550bc4f824e97f0a72eac1f3a5", "Assets/Scripts/LookWithMouse.cs"}, + {"87f6dfceb3e39a947a312f7eeaa2a113", "Assets/Scripts/PlayerMovement.cs"}, + {"be76e5f14cfee674cb30b491fb72b09b", "Assets/Scripts/SimpleCameraController.cs"}, + {"6547d18b2bc62c94aa5ec1e87434da4e", "Assets/Scripts/SimpleCameraController.cs"}, + {"e8a636f62116c0a40bbfefdf876d4608", "Assets/Scripts/SimpleCameraController.cs"}, + {"14e519c409be4a1428028347410f5677", "Assets/Scripts/SimpleCameraController.cs"}, + {"a04c28107d77d5e42b7155783b8475b6", "Assets/Settings/Cockpit_Renderer.asset"}, + {"ab09877e2e707104187f6f83e2f62510", "Assets/Settings/DefaultVolumeProfile.asset"}, + {"238cd62f6b58cb04e9c94749c4a015a7", "Assets/Settings/DefaultVolumeProfile.asset"}, + {"5a9a2dc462c7bde4f86d0615a19c2c72", "Assets/Settings/DiffusionProfiles/BambooLeaves.asset"}, + {"78322c7f82657514ebe48203160e3f39", "Assets/Settings/Foliage.asset"}, + {"3e2e6bfc59709614ab90c0cd7d755e48", "Assets/Settings/HDRP Balanced.asset"}, + {"36dd385e759c96147b6463dcd1149c11", "Assets/Settings/HDRP High Fidelity.asset"}, + {"168a2336534e4e043b2a210b6f8d379a", "Assets/Settings/HDRP Performant.asset"}, + {"4594f4a3fb14247e192bcca6dc23c8ed", "Assets/Settings/HDRPDefaultResources/DefaultLookDevProfile.asset"}, + {"14b392ee213d25a48b1feddbd9f5a9be", "Assets/Settings/HDRPDefaultResources/DefaultSettingsVolumeProfile.asset"}, + {"879ffae44eefa4412bb327928f1a96dd", "Assets/Settings/HDRPDefaultResources/FoliageDiffusionProfile.asset"}, + {"b9f3086da92434da0bc1518f19f0ce86", "Assets/Settings/HDRPDefaultResources/HDRenderPipelineAsset.asset"}, + {"ac0316ca287ba459492b669ff1317a6f", "Assets/Settings/HDRPDefaultResources/HDRenderPipelineGlobalSettings.asset"}, + {"48e911a1e337b44e2b85dbc65b47a594", "Assets/Settings/HDRPDefaultResources/SkinDiffusionProfile.asset"}, + {"d03ed43fc9d8a4f2e9fa70c1c7916eb9", "Assets/Settings/Lit2DSceneTemplate.scenetemplate"}, + {"65bc7dbf4170f435aa868c779acfb082", "Assets/Settings/Mobile_Renderer.asset"}, + {"5e6cbd92db86f4b18aec3ed561671858", "Assets/Settings/Mobile_RPAsset.asset"}, + {"23cccccf13c3d4170a9b21e52a9bc86b", "Assets/Settings/Mobile/Mobile_High_ScreenRenderer.asset"}, + {"6e8f76111115f44e0a76c2bff3cec258", "Assets/Settings/Mobile/Mobile_Low_Renderer.asset"}, + {"aed30aee6a3ceae4090dadd1934d2ad0", "Assets/Settings/Mobile/Mobile_Low_ScreenRenderer.asset"}, + {"d7686b11d09df481bac3c76ecc5ea626", "Assets/Settings/Mobile/Mobile_Low.asset"}, + {"f288ae1f4751b564a96ac7587541f7a2", "Assets/Settings/PC_Renderer.asset"}, + {"4b83569d67af61e458304325a23e5dfd", "Assets/Settings/PC_RPAsset.asset"}, + {"42b230d443c6d6c4b89c47f97db59121", "Assets/Settings/PC/PC_High_ScreenRenderer.asset"}, + {"13ba41cd2fa191f43890b271bd110ed9", "Assets/Settings/PC/PC_Low_Renderer.asset"}, + {"a73f6fa069dd14a42b40cbb01bae63b4", "Assets/Settings/PC/PC_Low_ScreenRenderer.asset"}, + {"4eb9ff6b5314098428cfa0be7e36ccda", "Assets/Settings/PC/PC_Low.asset"}, + {"573ac53c334415945bf239de2c2f0511", "Assets/Settings/PlayerControllerFPS.prefab"}, + {"7ba2b06fb32e5274aad88925a5b8d3f5", "Assets/Settings/PostProcessVolumeProfile.asset"}, + {"424799608f7334c24bf367e4bbfa7f9a", "Assets/Settings/Renderer2D.asset"}, + {"183cbd347d25080429f42b520742bbd8", "Assets/Settings/SampleScenePostProcessingSettings.asset"}, + {"10fc4df2da32a41aaa32d77bc913491c", "Assets/Settings/SampleSceneProfile.asset"}, + {"a6560a915ef98420e9faacc1c7438823", "Assets/Settings/SampleSceneProfile.asset"}, + {"a123fc0ac58cb774e8592c925f167e7c", "Assets/Settings/SampleSceneSkyandFogSettings.asset"}, + {"26bdddf49760c61438938733f07fa2a2", "Assets/Settings/Skin.asset"}, + {"8ba92e2dd7f884a0f88b98fa2d235fe7", "Assets/Settings/SkyandFogSettingsProfile.asset"}, + {"4a8e21d5c33334b11b34a596161b9360", "Assets/Settings/UniversalRenderer.asset"}, + {"18dc0cd2c080841dea60987a38ce93fa", "Assets/Settings/UniversalRenderPipelineGlobalSettings.asset"}, + {"bdede76083021864d8ff8bf23b2f37f1", "Assets/Settings/UniversalRenderPipelineGlobalSettings.asset"}, + {"19ba41d7c0026c3459d37c2fe90c55a0", "Assets/Settings/UniversalRP-HighQuality.asset"}, + {"a31e9f9f9c9d4b9429ed0d1234e22103", "Assets/Settings/UniversalRP-LowQuality.asset"}, + {"d847b876476d3d6468f5dfcd34266f96", "Assets/Settings/UniversalRP-MediumQuality.asset"}, + {"681886c5eb7344803b6206f758bf0b1c", "Assets/Settings/UniversalRP.asset"}, + {"e634585d5c4544dd297acaee93dc2beb", "Assets/Settings/URP-Balanced-Renderer.asset"}, + {"e1260c1148f6143b28bae5ace5e9c5d1", "Assets/Settings/URP-Balanced.asset"}, + {"c40be3174f62c4acf8c1216858c64956", "Assets/Settings/URP-HighFidelity-Renderer.asset"}, + {"7b7fd9122c28c4d15b667c7040e3b3fd", "Assets/Settings/URP-HighFidelity.asset"}, + {"707360a9c581a4bd7aa53bfeb1429f71", "Assets/Settings/URP-Performant-Renderer.asset"}, + {"d0e2fc18fe036412f8223b3b3d9ad574", "Assets/Settings/URP-Performant.asset"}, + {"b62413aeefabaaa41a4b5a71dd7ae1ac", "Assets/Settings/VolumeProfiles/CinematicProfile.asset"}, + {"ac0c2cad5778d4544b6a690963e02fe3", "Assets/Settings/VolumeProfiles/DefaultVolumeProfile.asset"}, + {"f2d4d916a6612574cad220d125febbf2", "Assets/Settings/VolumeProfiles/LowQualityVolumeProfile.asset"}, + {"cef078630d63d0442a070f84d4f13735", "Assets/Settings/VolumeProfiles/MarketProfile.asset"}, + {"7ede9c9f109e5c442b7d29e54b4996fc", "Assets/Settings/VolumeProfiles/MediaOverrides.asset"}, + {"3532e98caae428047bcefe69a344f72c", "Assets/Settings/VolumeProfiles/OutlineEnabled.asset"}, + {"bfc08ba7e35de1a44bb84a32f1a693e1", "Assets/Settings/VolumeProfiles/ZenGardenProfile.asset"}, + {"59a34a3881431c246b3564a0f0ca5bb0", "Assets/Settings/Volumes/CinematicPhysicalCamera.asset"}, + {"03bc34b71695890468eb021c73b228db", "Assets/Settings/Volumes/ScreenshotsTimelineProfile.asset"}, + {"7f342610b85f4164f808a1f380dcc668", "Assets/Settings/Volumes/VolumeGlobal.asset"}, + {"bd6d234073408c44ca3828113aac655e", "Assets/Settings/Volumes/VolumeRoom1.asset"}, + {"d78a1b031ab26034eb6ec3cbc9fbcec3", "Assets/Settings/Volumes/VolumeRoom2.asset"}, + {"5727d3e07f75c3744b6cc8a1e55850a9", "Assets/Settings/Volumes/VolumeRoom2Skylight.asset"}, + {"06114ad16a0bc0a41957375ac3bf472e", "Assets/Settings/Volumes/VolumeRoom3.asset"}, + {"1584bf21cf81d5147aa00e8a2deaf2fb", "Assets/Settings/Volumes/VolumeRoom3Corridor.asset"}, + {"7bca3a07cdd522c4c8020832c20b3eae", "Assets/Settings/Volumes/VolumeRoom3Sitting.asset"}, + {"2872d90954412244a8b4a477b939c3ca", "Assets/Settings/XR/Loaders/Mock_HMD_Loader.asset"}, + {"f25758a0f79593d4a9b3ee30a17b4c2e", "Assets/Settings/XR/Loaders/Oculus_Loader.asset"}, + {"9e9f2958d1b4b4642ace1d0c7770650b", "Assets/Settings/XR/Settings/Mock_HMD_Build_Settings.asset"}, + {"290a6e6411d135049940bec2237b8938", "Assets/Settings/XR/Settings/Oculus_Settings.asset"}, + {"4c1640683c539c14080cfd43fbeffbda", "Assets/Settings/XR/XRGeneralSettings.asset"}, + {"93b439a37f63240aca3dd4e01d978a9f", "Assets/UniversalRenderPipelineGlobalSettings.asset"}, + {"38b35347542e5af4c9b140950c5b18db", "Assets/UniversalRenderPipelineGlobalSettings.asset"} + }; + } +} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckProjectTemplateAssets.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckProjectTemplateAssets.cs.meta new file mode 100644 index 0000000..804f84b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods/UnityPackage/CheckProjectTemplateAssets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f02d52a702c712e4e8089f7c2e65bae7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI.meta new file mode 100644 index 0000000..f401e9e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0eed33a351c3c544ba6bf3cd29d24c26 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data.meta new file mode 100644 index 0000000..c3a170c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 461bfd99d0923cd4a8dae2f440af1064 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions.meta new file mode 100644 index 0000000..93d4155 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d7d9c6cc805e072429392e7a378d2c9c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorResults.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorResults.cs new file mode 100644 index 0000000..676b70c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorResults.cs @@ -0,0 +1,15 @@ +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Validator.UI.Data +{ + internal interface IValidatorResults + { + event Action OnResultsChanged; + event Action OnRequireSerialize; + + void LoadResult(ValidationResult result); + IEnumerable GetSortedTestGroups(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorResults.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorResults.cs.meta new file mode 100644 index 0000000..c914281 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorResults.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91c62333b36d5ef47989289e8f90c056 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorSettings.cs new file mode 100644 index 0000000..d439b0d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorSettings.cs @@ -0,0 +1,31 @@ +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Validator.UI.Data +{ + internal interface IValidatorSettings + { + event Action OnCategoryChanged; + event Action OnValidationTypeChanged; + event Action OnValidationPathsChanged; + event Action OnRequireSerialize; + + void LoadSettings(ValidationSettings settings); + + string GetActiveCategory(); + void SetActiveCategory(string category); + List GetAvailableCategories(); + + ValidationType GetValidationType(); + void SetValidationType(ValidationType validationType); + + List GetValidationPaths(); + void AddValidationPath(string path); + void RemoveValidationPath(string path); + void ClearValidationPaths(); + bool IsValidationPathValid(string path, out string error); + + IValidator CreateValidator(); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorSettings.cs.meta new file mode 100644 index 0000000..d907440 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cc6516196465ac6469258ef8950da607 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTest.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTest.cs new file mode 100644 index 0000000..1dcafce --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTest.cs @@ -0,0 +1,15 @@ +using AssetStoreTools.Validator.Data; + +namespace AssetStoreTools.Validator.UI.Data +{ + internal interface IValidatorTest + { + int Id { get; } + string Name { get; } + string Description { get; } + ValidationType ValidationType { get; } + TestResult Result { get; } + + void SetResult(TestResult result); + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTest.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTest.cs.meta new file mode 100644 index 0000000..f1cbf25 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d1e4d9ba8de8cfc4aa42786fbbc5037a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTestGroup.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTestGroup.cs new file mode 100644 index 0000000..81b8d8f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTestGroup.cs @@ -0,0 +1,12 @@ +using AssetStoreTools.Validator.Data; +using System.Collections.Generic; + +namespace AssetStoreTools.Validator.UI.Data +{ + internal interface IValidatorTestGroup + { + string Name { get; } + TestResultStatus Status { get; } + IEnumerable Tests { get; } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTestGroup.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTestGroup.cs.meta new file mode 100644 index 0000000..86c6b56 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Abstractions/IValidatorTestGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa8735b7eb65d3147ab8bdbf922f36cf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization.meta new file mode 100644 index 0000000..4122afe --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ec536685238584f41bd268edaaf0ad7d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateData.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateData.cs new file mode 100644 index 0000000..4e75ed6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateData.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; + +namespace AssetStoreTools.Validator.UI.Data.Serialization +{ + internal class ValidatorStateData + { + [JsonProperty("validation_settings")] + private ValidatorStateSettings _settings; + [JsonProperty("validation_results")] + private ValidatorStateResults _results; + + public ValidatorStateData() + { + _settings = new ValidatorStateSettings(); + _results = new ValidatorStateResults(); + } + + public ValidatorStateSettings GetSettings() + { + return _settings; + } + + public ValidatorStateResults GetResults() + { + return _results; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateData.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateData.cs.meta new file mode 100644 index 0000000..eea46eb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da7a885e302cb6b43855b68f44f2c0fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateDataContractResolver.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateDataContractResolver.cs new file mode 100644 index 0000000..a72360a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateDataContractResolver.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json.Serialization; + +namespace AssetStoreTools.Validator.UI.Data.Serialization +{ + internal class ValidatorStateDataContractResolver : DefaultContractResolver + { + private static ValidatorStateDataContractResolver _instance; + public static ValidatorStateDataContractResolver Instance => _instance ?? (_instance = new ValidatorStateDataContractResolver()); + + private NamingStrategy _namingStrategy; + + private ValidatorStateDataContractResolver() + { + _namingStrategy = new SnakeCaseNamingStrategy(); + } + + protected override string ResolvePropertyName(string propertyName) + { + var resolvedName = _namingStrategy.GetPropertyName(propertyName, false); + if (resolvedName.StartsWith("_")) + return resolvedName.Substring(1); + + return resolvedName; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateDataContractResolver.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateDataContractResolver.cs.meta new file mode 100644 index 0000000..4187dca --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateDataContractResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 60f0e8d9b2ab86547a288c337fb2be0a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateResults.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateResults.cs new file mode 100644 index 0000000..3558822 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateResults.cs @@ -0,0 +1,83 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.TestDefinitions; +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace AssetStoreTools.Validator.UI.Data.Serialization +{ + internal class ValidatorStateResults + { + // Primary data + [JsonProperty("validation_status")] + private ValidationStatus _status; + [JsonProperty("test_results")] + private SortedDictionary _results; + + // Secondary data + [JsonProperty("project_path")] + private string _projectPath; + [JsonProperty("had_compilation_errors")] + private bool _hadCompilationErrors; + + public ValidatorStateResults() + { + _projectPath = string.Empty; + _status = ValidationStatus.NotRun; + _hadCompilationErrors = false; + _results = new SortedDictionary(); + } + + public ValidationStatus GetStatus() + { + return _status; + } + + public void SetStatus(ValidationStatus status) + { + if (_status == status) + return; + + _status = status; + } + + public SortedDictionary GetResults() + { + return _results; + } + + public void SetResults(IEnumerable tests) + { + _results.Clear(); + foreach (var test in tests) + { + _results.Add(test.Id, test.Result); + } + } + + public string GetProjectPath() + { + return _projectPath; + } + + public void SetProjectPath(string projectPath) + { + if (_projectPath == projectPath) + return; + + _projectPath = projectPath; + } + + public bool GetHadCompilationErrors() + { + return _hadCompilationErrors; + } + + public void SetHadCompilationErrors(bool hadCompilationErrors) + { + if (_hadCompilationErrors == hadCompilationErrors) + return; + + _hadCompilationErrors = hadCompilationErrors; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateResults.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateResults.cs.meta new file mode 100644 index 0000000..876eadb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateResults.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a90c3acfa50e8da4aa3da2b9c669502d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateSettings.cs new file mode 100644 index 0000000..27cee0e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateSettings.cs @@ -0,0 +1,63 @@ +using AssetStoreTools.Validator.Data; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Linq; + +namespace AssetStoreTools.Validator.UI.Data.Serialization +{ + internal class ValidatorStateSettings + { + [JsonProperty("category")] + private string _category; + [JsonProperty("validation_type")] + private ValidationType _validationType; + [JsonProperty("validation_paths")] + private List _validationPaths; + + public ValidatorStateSettings() + { + _category = string.Empty; + _validationType = ValidationType.UnityPackage; + _validationPaths = new List(); + } + + public string GetCategory() + { + return _category; + } + + public void SetCategory(string category) + { + if (_category == category) + return; + + _category = category; + } + + public ValidationType GetValidationType() + { + return _validationType; + } + + public void SetValidationType(ValidationType validationType) + { + if (validationType == _validationType) + return; + + _validationType = validationType; + } + + public List GetValidationPaths() + { + return _validationPaths; + } + + public void SetValidationPaths(List validationPaths) + { + if (_validationPaths.SequenceEqual(validationPaths)) + return; + + _validationPaths = validationPaths; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateSettings.cs.meta new file mode 100644 index 0000000..0780a53 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/Serialization/ValidatorStateSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d39d56313ade8a8409aafe95dc84f79a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorResults.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorResults.cs new file mode 100644 index 0000000..38244c8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorResults.cs @@ -0,0 +1,137 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.TestDefinitions; +using AssetStoreTools.Validator.UI.Data.Serialization; +using AssetStoreTools.Validator.Utility; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AssetStoreTools.Validator.UI.Data +{ + internal class ValidatorResults : IValidatorResults + { + private ValidatorStateResults _stateData; + + private IValidatorSettings _settings; + private IEnumerable _tests; + + private readonly TestResultStatus[] _priorityGroups = new TestResultStatus[] + { + TestResultStatus.Undefined, + TestResultStatus.Fail, + TestResultStatus.Warning + }; + + public event Action OnResultsChanged; + public event Action OnRequireSerialize; + + public ValidatorResults(IValidatorSettings settings, ValidatorStateResults stateData) + { + _settings = settings; + _stateData = stateData; + + _tests = GetAllTests(); + + Deserialize(); + } + + private IEnumerable GetAllTests() + { + var tests = new List(); + var testObjects = ValidatorUtility.GetAutomatedTestCases(ValidatorUtility.SortType.Alphabetical); + + foreach (var testObject in testObjects) + { + var testSource = new AutomatedTest(testObject); + var test = new ValidatorTest(testSource); + tests.Add(test); + } + + return tests; + } + + public void LoadResult(ValidationResult result) + { + if (result == null) + return; + + foreach (var test in _tests) + { + if (!result.Tests.Any(x => x.Id == test.Id)) + continue; + + var matchingResult = result.Tests.First(x => x.Id == test.Id); + test.SetResult(matchingResult.Result); + } + + OnResultsChanged?.Invoke(); + + Serialize(result); + } + + public IEnumerable GetSortedTestGroups() + { + var groups = new List(); + var testsByStatus = _tests + .Where(x => x.ValidationType == ValidationType.Generic || x.ValidationType == _settings.GetValidationType()) + .GroupBy(x => x.Result.Status).ToDictionary(x => x.Key, x => x.ToList()); + + foreach (var kvp in testsByStatus) + { + var group = new ValidatorTestGroup(kvp.Key, kvp.Value); + groups.Add(group); + } + + return SortGroups(groups); + } + + private IEnumerable SortGroups(IEnumerable unsortedGroups) + { + var sortedGroups = new List(); + var groups = unsortedGroups.OrderBy(x => x.Status).ToList(); + + // Select priority groups first + foreach (var priority in _priorityGroups) + { + var priorityGroup = groups.FirstOrDefault(x => x.Status == priority); + if (priorityGroup == null) + continue; + + sortedGroups.Add(priorityGroup); + groups.Remove(priorityGroup); + } + + // Add the rest + sortedGroups.AddRange(groups); + + return sortedGroups; + } + + private void Serialize(ValidationResult result) + { + _stateData.SetStatus(result.Status); + _stateData.SetResults(result.Tests); + _stateData.SetProjectPath(result.ProjectPath); + _stateData.SetHadCompilationErrors(result.HadCompilationErrors); + OnRequireSerialize?.Invoke(); + } + + private void Deserialize() + { + if (_stateData == null) + return; + + var serializedResults = _stateData.GetResults(); + foreach (var test in _tests) + { + if (!serializedResults.Any(x => x.Key == test.Id)) + continue; + + var matchingResult = serializedResults.First(x => x.Key == test.Id); + test.SetResult(matchingResult.Value); + } + + OnResultsChanged?.Invoke(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorResults.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorResults.cs.meta new file mode 100644 index 0000000..069816b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorResults.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf2839f8b2340294aae39c2965039d2a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorSettings.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorSettings.cs new file mode 100644 index 0000000..d65ab0c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorSettings.cs @@ -0,0 +1,236 @@ +using AssetStoreTools.Utility; +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.UI.Data.Serialization; +using AssetStoreTools.Validator.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Validator.UI.Data +{ + internal class ValidatorSettings : IValidatorSettings + { + private ValidatorStateSettings _stateData; + + private string _category; + private ValidationType _validationType; + private List _validationPaths; + + public event Action OnCategoryChanged; + public event Action OnValidationTypeChanged; + public event Action OnValidationPathsChanged; + public event Action OnRequireSerialize; + + public ValidatorSettings(ValidatorStateSettings stateData) + { + _stateData = stateData; + + _category = string.Empty; + _validationType = ValidationType.UnityPackage; + _validationPaths = new List(); + + Deserialize(); + } + + public void LoadSettings(ValidationSettings settings) + { + if (settings == null) + return; + + var currentProjectValidationSettings = settings as CurrentProjectValidationSettings; + if (currentProjectValidationSettings == null) + throw new ArgumentException($"Only {nameof(CurrentProjectValidationSettings)} can be loaded"); + + _category = currentProjectValidationSettings.Category; + OnCategoryChanged?.Invoke(); + + _validationType = currentProjectValidationSettings.ValidationType; + OnValidationTypeChanged?.Invoke(); + + _validationPaths = currentProjectValidationSettings.ValidationPaths.ToList(); + OnValidationPathsChanged?.Invoke(); + + Serialize(); + } + + public string GetActiveCategory() + { + return _category; + } + + public void SetActiveCategory(string category) + { + if (category == _category) + return; + + _category = category; + Serialize(); + OnCategoryChanged?.Invoke(); + } + + public List GetAvailableCategories() + { + var categories = new HashSet(); + + var testData = ValidatorUtility.GetAutomatedTestCases(); + foreach (var test in testData) + { + if (test.CategoryInfo == null) + continue; + + foreach (var filter in test.CategoryInfo.Filter) + categories.Add(ConvertSlashToUnicodeSlash(filter)); + } + + return categories.OrderBy(x => x).ToList(); + } + + private string ConvertSlashToUnicodeSlash(string text) + { + return text.Replace('/', '\u2215'); + } + + public ValidationType GetValidationType() + { + return _validationType; + } + + public void SetValidationType(ValidationType validationType) + { + if (validationType == _validationType) + return; + + _validationType = validationType; + + Serialize(); + OnValidationTypeChanged?.Invoke(); + } + + public List GetValidationPaths() + { + return _validationPaths; + } + + public void AddValidationPath(string path) + { + if (string.IsNullOrEmpty(path)) + return; + + if (_validationPaths.Contains(path)) + return; + + // Prevent redundancy for new paths + var existingPath = _validationPaths.FirstOrDefault(x => path.StartsWith(x + "/")); + if (existingPath != null) + { + Debug.LogWarning($"Path '{path}' is already included with existing path: '{existingPath}'"); + return; + } + + // Prevent redundancy for already added paths + var redundantPaths = _validationPaths.Where(x => x.StartsWith(path + "/")).ToArray(); + foreach (var redundantPath in redundantPaths) + { + Debug.LogWarning($"Existing validation path '{redundantPath}' has been made redundant by the inclusion of new validation path: '{path}'"); + _validationPaths.Remove(redundantPath); + } + + _validationPaths.Add(path); + + Serialize(); + OnValidationPathsChanged?.Invoke(); + } + + public void RemoveValidationPath(string path) + { + if (!_validationPaths.Contains(path)) + return; + + _validationPaths.Remove(path); + + Serialize(); + OnValidationPathsChanged?.Invoke(); + } + + public void ClearValidationPaths() + { + if (_validationPaths.Count == 0) + return; + + _validationPaths.Clear(); + + Serialize(); + OnValidationPathsChanged?.Invoke(); + } + + public bool IsValidationPathValid(string path, out string error) + { + error = string.Empty; + + if (string.IsNullOrEmpty(path)) + { + error = "Path cannot be empty"; + return false; + } + + var isAssetsPath = path.StartsWith("Assets/") + || path.Equals("Assets"); + var isPackagePath = PackageUtility.GetPackageByManifestPath($"{path}/package.json", out _); + + if (!isAssetsPath && !isPackagePath) + { + error = "Selected path must be within the Assets folder or point to a root path of a package"; + return false; + } + + if (!Directory.Exists(path)) + { + error = "Path does not exist"; + return false; + } + + if (path.Split('/').Any(x => x.StartsWith(".") || x.EndsWith("~"))) + { + error = $"Path '{path}' cannot be validated as it is a hidden folder and not part of the Asset Database"; + return false; + } + + return true; + } + + public IValidator CreateValidator() + { + var settings = new CurrentProjectValidationSettings() + { + Category = _category, + ValidationType = _validationType, + ValidationPaths = _validationPaths + }; + + var validator = new CurrentProjectValidator(settings); + return validator; + } + + private void Serialize() + { + _stateData.SetCategory(_category); + _stateData.SetValidationType(_validationType); + _stateData.SetValidationPaths(_validationPaths); + + OnRequireSerialize?.Invoke(); + } + + private void Deserialize() + { + if (_stateData == null) + return; + + _category = _stateData.GetCategory(); + _validationType = _stateData.GetValidationType(); + foreach (var path in _stateData.GetValidationPaths()) + _validationPaths.Add(path); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorSettings.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorSettings.cs.meta new file mode 100644 index 0000000..4905909 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 89504c2259614a743a164c5c162a197a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTest.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTest.cs new file mode 100644 index 0000000..126cb4f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTest.cs @@ -0,0 +1,28 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.TestDefinitions; + +namespace AssetStoreTools.Validator.UI.Data +{ + internal class ValidatorTest : IValidatorTest + { + public int Id { get; private set; } + public string Name { get; private set; } + public string Description { get; private set; } + public ValidationType ValidationType { get; private set; } + public TestResult Result { get; private set; } + + public ValidatorTest(AutomatedTest source) + { + Id = source.Id; + Name = source.Title; + Description = source.Description; + ValidationType = source.ValidationType; + Result = source.Result; + } + + public void SetResult(TestResult result) + { + Result = result; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTest.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTest.cs.meta new file mode 100644 index 0000000..eb0b837 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 838e8d45ce997d8489185bc194dfcf25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTestGroup.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTestGroup.cs new file mode 100644 index 0000000..b3620d8 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTestGroup.cs @@ -0,0 +1,18 @@ +using AssetStoreTools.Validator.Data; +using System.Collections.Generic; + +namespace AssetStoreTools.Validator.UI.Data +{ + internal class ValidatorTestGroup : IValidatorTestGroup + { + public string Name => Status.ToString(); + public TestResultStatus Status { get; private set; } + public IEnumerable Tests { get; private set; } + + public ValidatorTestGroup(TestResultStatus status, IEnumerable tests) + { + Status = status; + Tests = tests; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTestGroup.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTestGroup.cs.meta new file mode 100644 index 0000000..46de398 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Data/ValidatorTestGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8f5d1fc9ff785904fb2e663e9232a7a5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements.meta new file mode 100644 index 0000000..9a9f507 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7ee7e5be29b8b184ba2abcd3ed38454e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorButtonElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorButtonElement.cs new file mode 100644 index 0000000..93f8f89 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorButtonElement.cs @@ -0,0 +1,50 @@ +using AssetStoreTools.Validator.UI.Data; +using System; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Validator.UI.Elements +{ + internal class ValidatorButtonElement : VisualElement + { + // Data + private IValidatorSettings _settings; + + // UI + private Button _validateButton; + + public event Action OnValidate; + + public ValidatorButtonElement(IValidatorSettings settings) + { + _settings = settings; + _settings.OnValidationPathsChanged += ValidationPathsChanged; + + Create(); + Deserialize(); + } + + private void Create() + { + _validateButton = new Button(Validate) { text = "Validate" }; + _validateButton.AddToClassList("validator-validate-button"); + + Add(_validateButton); + } + + private void Validate() + { + OnValidate?.Invoke(); + } + + private void ValidationPathsChanged() + { + var validationPathsPresent = _settings.GetValidationPaths().Count > 0; + _validateButton.SetEnabled(validationPathsPresent); + } + + private void Deserialize() + { + ValidationPathsChanged(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorButtonElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorButtonElement.cs.meta new file mode 100644 index 0000000..f136053 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorButtonElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 44fac105314df6341bf6a70fb3200baf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorDescriptionElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorDescriptionElement.cs new file mode 100644 index 0000000..73d6e70 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorDescriptionElement.cs @@ -0,0 +1,114 @@ +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Validator.UI.Elements +{ + internal class ValidatorDescriptionElement : VisualElement + { + private const string DescriptionFoldoutText = "Validate your package to ensure your content follows the chosen submission guidelines."; + private const string DescriptionFoldoutContentText = "The validations below do not cover all of the content standards, and passing all validations does not " + + "guarantee that your package will be accepted to the Asset Store.\n\n" + + "The tests are not obligatory for submitting your assets, but they can help avoid instant rejection by the " + + "automated vetting system, or clarify reasons of rejection communicated by the vetting team.\n\n" + + "For more information about the validations, view the message by expanding the tests or contact our support team."; + + private VisualElement _descriptionSimpleContainer; + private Label _descriptionSimpleLabel; + private Button _showMoreButton; + + private VisualElement _descriptionFullContainer; + private Button _showLessButton; + + public ValidatorDescriptionElement() + { + AddToClassList("validator-description"); + Create(); + } + + private void Create() + { + CreateSimpleDescription(); + CreateFullDescription(); + } + + private void CreateSimpleDescription() + { + _descriptionSimpleContainer = new VisualElement(); + _descriptionSimpleContainer.AddToClassList("validator-description-simple-container"); + + _descriptionSimpleLabel = new Label(DescriptionFoldoutText); + _descriptionSimpleLabel.AddToClassList("validator-description-simple-label"); + + _showMoreButton = new Button(ToggleFullDescription) { text = "Show more..." }; + _showMoreButton.AddToClassList("validator-description-show-button"); + _showMoreButton.AddToClassList("validator-description-hyperlink-button"); + + _descriptionSimpleContainer.Add(_descriptionSimpleLabel); + _descriptionSimpleContainer.Add(_showMoreButton); + + Add(_descriptionSimpleContainer); + } + + private void CreateFullDescription() + { + _descriptionFullContainer = new VisualElement(); + _descriptionFullContainer.AddToClassList("validator-description-full-container"); + + var validatorDescription = new Label() + { + text = DescriptionFoldoutContentText + }; + validatorDescription.AddToClassList("validator-description-full-label"); + + var submissionGuidelinesButton = new Button(OpenSubmissionGuidelinesUrl) + { + text = "Submission Guidelines" + }; + submissionGuidelinesButton.AddToClassList("validator-description-hyperlink-button"); + + var supportTicketButton = new Button(OpenSupportTicketUrl) + { + text = "Contact our Support Team" + }; + supportTicketButton.AddToClassList("validator-description-hyperlink-button"); + + _showLessButton = new Button(ToggleFullDescription) { text = "Show less..." }; + _showLessButton.AddToClassList("validator-description-hide-button"); + _showLessButton.AddToClassList("validator-description-hyperlink-button"); + + _descriptionFullContainer.Add(validatorDescription); + _descriptionFullContainer.Add(submissionGuidelinesButton); + _descriptionFullContainer.Add(supportTicketButton); + _descriptionFullContainer.Add(_showLessButton); + + _descriptionFullContainer.style.display = DisplayStyle.None; + Add(_descriptionFullContainer); + } + + private void ToggleFullDescription() + { + var displayFullDescription = _descriptionFullContainer.style.display == DisplayStyle.None; + + if (displayFullDescription) + { + _showMoreButton.style.display = DisplayStyle.None; + _descriptionFullContainer.style.display = DisplayStyle.Flex; + } + else + { + _showMoreButton.style.display = DisplayStyle.Flex; + _descriptionFullContainer.style.display = DisplayStyle.None; + } + } + + private void OpenSubmissionGuidelinesUrl() + { + Application.OpenURL(Constants.Validator.SubmissionGuidelinesUrl); + } + + private void OpenSupportTicketUrl() + { + Application.OpenURL(Constants.Validator.SupportTicketUrl); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorDescriptionElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorDescriptionElement.cs.meta new file mode 100644 index 0000000..898b2ce --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorDescriptionElement.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9866d77420d947ba852055eed2bac895 +timeCreated: 1653383883 \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorPathsElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorPathsElement.cs new file mode 100644 index 0000000..6e4fe6d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorPathsElement.cs @@ -0,0 +1,128 @@ +using AssetStoreTools.Utility; +using AssetStoreTools.Validator.UI.Data; +using UnityEditor; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Validator.UI.Elements +{ + internal class ValidatorPathsElement : VisualElement + { + // Data + private IValidatorSettings _settings; + + // UI + private ScrollView _pathBoxScrollView; + + public ValidatorPathsElement(IValidatorSettings settings) + { + AddToClassList("validator-paths"); + + _settings = settings; + _settings.OnValidationPathsChanged += ValidationPathsChanged; + + Create(); + Deserialize(); + } + + private void Create() + { + var pathSelectionRow = new VisualElement(); + pathSelectionRow.AddToClassList("validator-settings-selection-row"); + + VisualElement labelHelpRow = new VisualElement(); + labelHelpRow.AddToClassList("validator-settings-selection-label-help-row"); + labelHelpRow.style.alignSelf = Align.FlexStart; + + Label pathLabel = new Label { text = "Validation paths" }; + Image pathLabelTooltip = new Image + { + tooltip = "Select the folder (or multiple folders) that your package consists of." + + "\n\nAll files and folders of your package should be contained within " + + "a single root folder that is named after your package " + + "(e.g. 'Assets/[MyPackageName]' or 'Packages/[MyPackageName]')" + + "\n\nIf your package includes special folders that cannot be nested within " + + "the root package folder (e.g. 'WebGLTemplates'), they should be added to this list " + + "together with the root package folder" + }; + + labelHelpRow.Add(pathLabel); + labelHelpRow.Add(pathLabelTooltip); + + var fullPathBox = new VisualElement() { name = "ValidationPaths" }; + fullPathBox.AddToClassList("validator-paths-box"); + + _pathBoxScrollView = new ScrollView { name = "ValidationPathsScrollView" }; + _pathBoxScrollView.AddToClassList("validator-paths-scroll-view"); + + VisualElement scrollViewBottomRow = new VisualElement(); + scrollViewBottomRow.AddToClassList("validator-paths-scroll-view-bottom-row"); + + var addExtraPathsButton = new Button(BrowsePath) { text = "Add a path" }; + addExtraPathsButton.AddToClassList("validator-paths-add-button"); + scrollViewBottomRow.Add(addExtraPathsButton); + + fullPathBox.Add(_pathBoxScrollView); + fullPathBox.Add(scrollViewBottomRow); + + pathSelectionRow.Add(labelHelpRow); + pathSelectionRow.Add(fullPathBox); + + Add(pathSelectionRow); + } + + private VisualElement CreateSinglePathElement(string path) + { + var validationPath = new VisualElement(); + validationPath.AddToClassList("validator-paths-path-row"); + + var folderPathLabel = new Label(path); + folderPathLabel.AddToClassList("validator-paths-path-row-input-field"); + + var removeButton = new Button(() => + { + _settings.RemoveValidationPath(path); + }); + removeButton.text = "X"; + removeButton.AddToClassList("validator-paths-path-row-remove-button"); + + validationPath.Add(folderPathLabel); + validationPath.Add(removeButton); + + return validationPath; + } + + private void BrowsePath() + { + string absolutePath = EditorUtility.OpenFolderPanel("Select a directory", "Assets", ""); + + if (string.IsNullOrEmpty(absolutePath)) + return; + + var relativePath = FileUtility.AbsolutePathToRelativePath(absolutePath, ASToolsPreferences.Instance.EnableSymlinkSupport); + + if (!_settings.IsValidationPathValid(relativePath, out var error)) + { + EditorUtility.DisplayDialog("Invalid path", error, "OK"); + return; + } + + _settings.AddValidationPath(relativePath); + } + + private void ValidationPathsChanged() + { + var validationPaths = _settings.GetValidationPaths(); + + _pathBoxScrollView.Clear(); + foreach (var path in validationPaths) + { + _pathBoxScrollView.Add(CreateSinglePathElement(path)); + } + } + + private void Deserialize() + { + ValidationPathsChanged(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorPathsElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorPathsElement.cs.meta new file mode 100644 index 0000000..93c9b6b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorPathsElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 370dcd3bc87ace647940b4b07147bf93 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorResultsElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorResultsElement.cs new file mode 100644 index 0000000..f8e5113 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorResultsElement.cs @@ -0,0 +1,47 @@ +using AssetStoreTools.Validator.UI.Data; +using System.Linq; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Validator.UI.Elements +{ + internal class ValidatorResultsElement : ScrollView + { + private IValidatorResults _results; + + public ValidatorResultsElement(IValidatorResults results) + { + AddToClassList("validator-test-list"); + + _results = results; + _results.OnResultsChanged += ResultsChanged; + + Create(); + } + + private void Create() + { + var groups = _results.GetSortedTestGroups().ToList(); + for (int i = 0; i < groups.Count; i++) + { + var groupElement = new ValidatorTestGroupElement(groups[i]); + Add(groupElement); + if (i != groups.Count - 1) + Add(CreateSeparator()); + } + } + + private void ResultsChanged() + { + Clear(); + Create(); + } + + private VisualElement CreateSeparator() + { + var groupSeparator = new VisualElement { name = "GroupSeparator" }; + groupSeparator.AddToClassList("validator-test-list-group-separator"); + + return groupSeparator; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorResultsElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorResultsElement.cs.meta new file mode 100644 index 0000000..d84970a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorResultsElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12f80f9088944a149a34b3f078ca859a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorSettingsElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorSettingsElement.cs new file mode 100644 index 0000000..b3f69d7 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorSettingsElement.cs @@ -0,0 +1,96 @@ +using AssetStoreTools.Validator.UI.Data; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Validator.UI.Elements +{ + internal class ValidatorSettingsElement : VisualElement + { + // Data + private IValidatorSettings _settings; + + // UI + private ToolbarMenu _categoryMenu; + private ValidatorPathsElement _validationPathsElement; + + public ValidatorSettingsElement(IValidatorSettings settings) + { + AddToClassList("validator-settings"); + + _settings = settings; + _settings.OnCategoryChanged += CategoryChanged; + + Create(); + Deserialize(); + } + + public void Create() + { + CreateCategorySelection(); + CreateValidationPathSelection(); + } + + private void CreateCategorySelection() + { + var categorySelectionBox = new VisualElement(); + categorySelectionBox.AddToClassList("validator-settings-selection-row"); + + VisualElement labelHelpRow = new VisualElement(); + labelHelpRow.AddToClassList("validator-settings-selection-label-help-row"); + + Label categoryLabel = new Label { text = "Category" }; + Image categoryLabelTooltip = new Image + { + tooltip = "Choose a base category of your package" + + "\n\nThis can be found in the Publishing Portal when creating the package listing or just " + + "selecting a planned one." + + "\n\nNote: Different categories could have different severities of several test cases." + }; + + labelHelpRow.Add(categoryLabel); + labelHelpRow.Add(categoryLabelTooltip); + + _categoryMenu = new ToolbarMenu { name = "CategoryMenu" }; + _categoryMenu.AddToClassList("validator-settings-selection-dropdown"); + + categorySelectionBox.Add(labelHelpRow); + categorySelectionBox.Add(_categoryMenu); + + // Append available categories + var categories = _settings.GetAvailableCategories(); + foreach (var category in categories) + { + _categoryMenu.menu.AppendAction(category, _ => _settings.SetActiveCategory(category)); + } + + // Append misc category + _categoryMenu.menu.AppendAction("Other", _ => _settings.SetActiveCategory(string.Empty)); + + Add(categorySelectionBox); + } + + private void CreateValidationPathSelection() + { + _validationPathsElement = new ValidatorPathsElement(_settings); + Add(_validationPathsElement); + } + + private void CategoryChanged() + { + var category = _settings.GetActiveCategory(); + if (!string.IsNullOrEmpty(category)) + _categoryMenu.text = category; + else + _categoryMenu.text = "Other"; + } + + private void Deserialize() + { + if (_settings == null) + return; + + // Set initial category + CategoryChanged(); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorSettingsElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorSettingsElement.cs.meta new file mode 100644 index 0000000..0522b1b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorSettingsElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 760d25556d755d544bece3a605adea09 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestElement.cs new file mode 100644 index 0000000..c477c72 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestElement.cs @@ -0,0 +1,239 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.UI.Data; +using AssetStoreTools.Validator.Utility; +using System.Linq; +using UnityEditor.SceneManagement; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.SceneManagement; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Validator.UI.Elements +{ + internal class ValidatorTestElement : VisualElement + { + // Data + private IValidatorTest _test; + private bool _isExpanded; + + // UI + private Button _testFoldoutButton; + private Label _testFoldoutExpandStateLabel; + private Label _testFoldoutLabel; + private Image _testStatusImage; + + private VisualElement _testContent; + private VisualElement _resultMessagesBox; + + public ValidatorTestElement(IValidatorTest test) + { + AddToClassList("validator-test"); + + _test = test; + + Create(); + Unexpand(); + + SubscribeToSceneChanges(); + } + + private void Create() + { + CreateFoldoutButton(); + CreateTestContent(); + CreateTestDescription(); + CreateTestMessages(); + } + + private void CreateFoldoutButton() + { + _testFoldoutButton = new Button(ToggleExpand) { name = _test.Name }; + _testFoldoutButton.AddToClassList("validator-test-foldout"); + + // Expander and Asset Label + VisualElement labelExpanderRow = new VisualElement { name = "labelExpanderRow" }; + labelExpanderRow.AddToClassList("validator-test-expander"); + + _testFoldoutExpandStateLabel = new Label { name = "ExpanderLabel", text = "►" }; + _testFoldoutExpandStateLabel.AddToClassList("validator-test-expander-arrow"); + + _testFoldoutLabel = new Label { name = "TestLabel", text = _test.Name }; + _testFoldoutLabel.AddToClassList("validator-text-expander-label"); + + labelExpanderRow.Add(_testFoldoutExpandStateLabel); + labelExpanderRow.Add(_testFoldoutLabel); + + _testStatusImage = new Image + { + name = "TestImage", + image = ValidatorUtility.GetStatusTexture(_test.Result.Status) + }; + + _testStatusImage.AddToClassList("validator-test-expander-image"); + + _testFoldoutButton.Add(labelExpanderRow); + _testFoldoutButton.Add(_testStatusImage); + + Add(_testFoldoutButton); + } + + private void CreateTestContent() + { + _testContent = new VisualElement(); + _testContent.AddToClassList("validator-test-content"); + Add(_testContent); + } + + private void CreateTestDescription() + { + var testCaseDescription = new TextField + { + name = "Description", + value = _test.Description, + isReadOnly = true, + multiline = true, + focusable = false, + doubleClickSelectsWord = false, + tripleClickSelectsLine = false + }; + testCaseDescription.AddToClassList("validator-test-content-textfield"); + +#if UNITY_2022_1_OR_NEWER + testCaseDescription.focusable = true; + testCaseDescription.selectAllOnFocus = false; + testCaseDescription.selectAllOnMouseUp = false; +#endif + + _testContent.Add(testCaseDescription); + } + + private void CreateTestMessages() + { + if (_test.Result.MessageCount == 0) + return; + + _resultMessagesBox = new VisualElement(); + _resultMessagesBox.AddToClassList("validator-test-content-result-messages"); + + switch (_test.Result.Status) + { + case TestResultStatus.Pass: + _resultMessagesBox.AddToClassList("validator-test-content-result-messages-pass"); + break; + case TestResultStatus.Warning: + _resultMessagesBox.AddToClassList("validator-test-content-result-messages-warning"); + break; + case TestResultStatus.Fail: + _resultMessagesBox.AddToClassList("validator-test-content-result-messages-fail"); + break; + } + + for (int i = 0; i < _test.Result.MessageCount; i++) + { + _resultMessagesBox.Add(CreateMessage(_test.Result.GetMessage(i))); + + if (i == _test.Result.MessageCount - 1) + continue; + + var separator = new VisualElement() { name = "Separator" }; + separator.AddToClassList("message-separator"); + _resultMessagesBox.Add(separator); + } + + _testContent.Add(_resultMessagesBox); + } + + private VisualElement CreateMessage(TestResultMessage message) + { + var resultText = message.GetText(); + var clickAction = message.GetClickAction(); + + var resultMessage = new VisualElement { name = "ResultMessageElement" }; + resultMessage.AddToClassList("validator-test-content-result-messages-content"); + + var informationButton = new Button(); + informationButton.AddToClassList("validator-test-content-result-messages-content-button"); + + if (clickAction != null) + { + informationButton.tooltip = clickAction.Tooltip; + informationButton.clicked += clickAction.Execute; + informationButton.SetEnabled(true); + } + + var informationDescription = new Label { name = "InfoDesc", text = resultText }; + informationDescription.AddToClassList("validator-test-content-result-messages-content-label"); + + informationButton.Add(informationDescription); + resultMessage.Add(informationButton); + + for (int i = 0; i < message.MessageObjectCount; i++) + { + var obj = message.GetMessageObject(i); + if (obj == null) + continue; + + if (obj.GetObject() == null) + continue; + + var objectField = new ObjectField() { objectType = obj.GetType(), value = obj.GetObject() }; + objectField.RegisterCallback>((evt) => + objectField.SetValueWithoutNotify(evt.previousValue)); + resultMessage.Add(objectField); + } + + return resultMessage; + } + + private void ToggleExpand() + { + if (!_isExpanded) + Expand(); + else + Unexpand(); + } + + private void Expand() + { + _testFoldoutExpandStateLabel.text = "▼"; + _testFoldoutButton.AddToClassList("validator-test-foldout-expanded"); + _testContent.style.display = DisplayStyle.Flex; + _isExpanded = true; + } + + private void Unexpand() + { + _testFoldoutExpandStateLabel.text = "►"; + _testFoldoutButton.RemoveFromClassList("validator-test-foldout-expanded"); + _testContent.style.display = DisplayStyle.None; + _isExpanded = false; + } + + private void SubscribeToSceneChanges() + { + // Some result message objects only exist in specific scenes, + // therefore the UI must be refreshed on scene change + var windowToSubscribeTo = Resources.FindObjectsOfTypeAll().FirstOrDefault(); + UnityAction sceneChanged = null; + sceneChanged = new UnityAction((_, __) => RefreshObjects(windowToSubscribeTo)); + EditorSceneManager.activeSceneChangedInEditMode += sceneChanged; + + void RefreshObjects(ValidatorWindow subscribedWindow) + { + // Remove callback if validator window instance changed + var activeWindow = Resources.FindObjectsOfTypeAll().FirstOrDefault(); + if (subscribedWindow == null || subscribedWindow != activeWindow) + { + EditorSceneManager.activeSceneChangedInEditMode -= sceneChanged; + return; + } + + if (_resultMessagesBox != null) + _testContent.Remove(_resultMessagesBox); + + CreateTestMessages(); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestElement.cs.meta new file mode 100644 index 0000000..17794ff --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56c93e6f23ba5724da8cc38f832be4e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestGroupElement.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestGroupElement.cs new file mode 100644 index 0000000..7906a0d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestGroupElement.cs @@ -0,0 +1,102 @@ +using AssetStoreTools.Validator.UI.Data; +using AssetStoreTools.Validator.Utility; +using System.Collections.Generic; +using System.Linq; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Validator.UI.Elements +{ + internal class ValidatorTestGroupElement : VisualElement + { + // Data + private IValidatorTestGroup _group; + private bool _isExpanded; + + // UI + private Button _groupFoldoutButton; + private Label _groupExpandStateLabel; + private Label _groupFoldoutLabel; + private Image _groupStatusImage; + + private VisualElement _groupContent; + private List _testElements; + + public ValidatorTestGroupElement(IValidatorTestGroup group) + { + AddToClassList("validator-test-list-group"); + + _group = group; + + Create(); + } + + private void Create() + { + CreateGroupFoldout(); + CreateGroupContent(); + } + + private void CreateGroupFoldout() + { + _groupFoldoutButton = new Button(ToggleExpand); + _groupFoldoutButton.AddToClassList("validator-test-list-group-expander"); + + _groupExpandStateLabel = new Label { name = "ExpanderLabel", text = "►" }; + _groupExpandStateLabel.AddToClassList("validator-test-list-group-expander-arrow"); + + _groupStatusImage = new Image + { + name = "TestImage", + image = ValidatorUtility.GetStatusTexture(_group.Status) + }; + _groupStatusImage.AddToClassList("validator-test-list-group-expander-image"); + + _groupFoldoutLabel = new Label() { text = $"{_group.Name} ({_group.Tests.Count()})" }; + _groupFoldoutLabel.AddToClassList("validator-test-list-group-expander-label"); + + _groupFoldoutButton.Add(_groupExpandStateLabel); + _groupFoldoutButton.Add(_groupStatusImage); + _groupFoldoutButton.Add(_groupFoldoutLabel); + + Add(_groupFoldoutButton); + } + + private void CreateGroupContent() + { + _groupContent = new VisualElement(); + _groupContent.AddToClassList("validator-test-list-group-content"); + + Add(_groupContent); + + _testElements = new List(); + foreach (var test in _group.Tests) + { + var testElement = new ValidatorTestElement(test); + _testElements.Add(testElement); + _groupContent.Add(testElement); + } + } + + private void ToggleExpand() + { + if (!_isExpanded) + Expand(); + else + Unexpand(); + } + + private void Expand() + { + _groupExpandStateLabel.text = "▼"; + _groupContent.style.display = DisplayStyle.Flex; + _isExpanded = true; + } + + private void Unexpand() + { + _groupExpandStateLabel.text = "►"; + _groupContent.style.display = DisplayStyle.None; + _isExpanded = false; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestGroupElement.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestGroupElement.cs.meta new file mode 100644 index 0000000..eea4279 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Elements/ValidatorTestGroupElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d7c7a8788d0ea324e843a475244d8e18 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/ValidatorWindow.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/ValidatorWindow.cs new file mode 100644 index 0000000..cea8163 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/ValidatorWindow.cs @@ -0,0 +1,56 @@ +using AssetStoreTools.Utility; +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services; +using AssetStoreTools.Validator.UI.Views; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Validator.UI +{ + + internal class ValidatorWindow : AssetStoreToolsWindow + { + protected override string WindowTitle => "Asset Store Validator"; + + private ICachingService _cachingService; + + private ValidatorTestsView _validationTestsView; + + protected override void Init() + { + minSize = new Vector2(350, 350); + + this.SetAntiAliasing(4); + + VisualElement root = rootVisualElement; + + // Clean it out, in case the window gets initialized again + root.Clear(); + + // Getting a reference to the USS Document and adding stylesheet to the root + root.styleSheets.Add(StyleSelector.ValidatorWindow.ValidatorWindowStyle); + root.styleSheets.Add(StyleSelector.ValidatorWindow.ValidatorWindowTheme); + + GetServices(); + ConstructWindow(); + } + + private void GetServices() + { + _cachingService = ValidatorServiceProvider.Instance.GetService(); + } + + private void ConstructWindow() + { + _validationTestsView = new ValidatorTestsView(_cachingService); + rootVisualElement.Add(_validationTestsView); + } + + public void Load(ValidationSettings settings, ValidationResult result) + { + _validationTestsView.LoadSettings(settings); + _validationTestsView.LoadResult(result); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/ValidatorWindow.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/ValidatorWindow.cs.meta new file mode 100644 index 0000000..378aa82 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/ValidatorWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a0dc99b826513dd4f868f1cf405c3923 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Views.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Views.meta new file mode 100644 index 0000000..e90db60 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Views.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8a973656ad14b8941b790ed83c874e97 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Views/ValidatorTestsView.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Views/ValidatorTestsView.cs new file mode 100644 index 0000000..b25a8b4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Views/ValidatorTestsView.cs @@ -0,0 +1,103 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services; +using AssetStoreTools.Validator.UI.Data; +using AssetStoreTools.Validator.UI.Data.Serialization; +using AssetStoreTools.Validator.UI.Elements; +using UnityEditor; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Validator.UI.Views +{ + internal class ValidatorTestsView : VisualElement + { + // Data + private ValidatorStateData _stateData; + private IValidatorSettings _settings; + private IValidatorResults _results; + + private ICachingService _cachingService; + + // UI + private ValidatorSettingsElement _validatorSettingsElement; + private ValidatorButtonElement _validatorButtonElement; + private ValidatorResultsElement _validationTestListElement; + + public ValidatorTestsView(ICachingService cachingService) + { + _cachingService = cachingService; + + if (!_cachingService.GetCachedValidatorStateData(out _stateData)) + _stateData = new ValidatorStateData(); + + _settings = new ValidatorSettings(_stateData.GetSettings()); + _settings.OnRequireSerialize += Serialize; + + _results = new ValidatorResults(_settings, _stateData.GetResults()); + _results.OnRequireSerialize += Serialize; + + Create(); + } + + private void Create() + { + CreateValidatorDescription(); + CreateValidationSettings(); + CreateValidationButton(); + CreateValidatorResults(); + } + + private void CreateValidatorDescription() + { + var validationInfoElement = new ValidatorDescriptionElement(); + Add(validationInfoElement); + } + + private void CreateValidationSettings() + { + _validatorSettingsElement = new ValidatorSettingsElement(_settings); + Add(_validatorSettingsElement); + } + + private void CreateValidationButton() + { + _validatorButtonElement = new ValidatorButtonElement(_settings); + _validatorButtonElement.OnValidate += Validate; + Add(_validatorButtonElement); + } + + private void CreateValidatorResults() + { + _validationTestListElement = new ValidatorResultsElement(_results); + Add(_validationTestListElement); + } + + private void Validate() + { + var validator = _settings.CreateValidator(); + var result = validator.Validate(); + + if (result.Status == ValidationStatus.Failed) + { + EditorUtility.DisplayDialog("Validation failed", result.Exception.Message, "OK"); + return; + } + + LoadResult(result); + } + + public void LoadSettings(ValidationSettings settings) + { + _settings.LoadSettings(settings); + } + + public void LoadResult(ValidationResult result) + { + _results.LoadResult(result); + } + + private void Serialize() + { + _cachingService.CacheValidatorStateData(_stateData); + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Views/ValidatorTestsView.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Views/ValidatorTestsView.cs.meta new file mode 100644 index 0000000..90c2cdd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI/Views/ValidatorTestsView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c5e0da39c6638684c9d3faf8e62c60d3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Utility.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Utility.meta new file mode 100644 index 0000000..148dca9 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3bc3a78a4b494e44b75268ad1444ab81 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Utility/ValidatorUtility.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Utility/ValidatorUtility.cs new file mode 100644 index 0000000..0d58db5 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Utility/ValidatorUtility.cs @@ -0,0 +1,142 @@ +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services; +using AssetStoreTools.Validator.TestDefinitions; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using static AssetStoreTools.Constants; +using ValidatorConstants = AssetStoreTools.Constants.Validator; + +namespace AssetStoreTools.Validator.Utility +{ + internal static class ValidatorUtility + { + public enum SortType + { + Id, + Alphabetical + } + + public static ValidationTestScriptableObject[] GetAutomatedTestCases() => GetAutomatedTestCases(SortType.Id); + + public static ValidationTestScriptableObject[] GetAutomatedTestCases(SortType sortType) + { + string[] guids = AssetDatabase.FindAssets("t:AutomatedTestScriptableObject", new[] { ValidatorConstants.Tests.TestDefinitionsPath }); + ValidationTestScriptableObject[] tests = new ValidationTestScriptableObject[guids.Length]; + for (int i = 0; i < tests.Length; i++) + { + string testPath = AssetDatabase.GUIDToAssetPath(guids[i]); + AutomatedTestScriptableObject test = AssetDatabase.LoadAssetAtPath(testPath); + + tests[i] = test; + } + + switch (sortType) + { + default: + case SortType.Id: + tests = tests.Where(x => x != null).OrderBy(x => x.Id).ToArray(); + break; + case SortType.Alphabetical: + tests = tests.Where(x => x != null).OrderBy(x => x.Title).ToArray(); + break; + } + + return tests; + } + + public static MonoScript GenerateTestScript(string testName, ValidationType validationType) + { + var derivedType = nameof(ITestScript); + var configType = string.Empty; + var scriptPath = string.Empty; + switch (validationType) + { + case ValidationType.Generic: + configType = nameof(GenericTestConfig); + scriptPath = ValidatorConstants.Tests.GenericTestMethodsPath; + break; + case ValidationType.UnityPackage: + configType = nameof(GenericTestConfig); + scriptPath = ValidatorConstants.Tests.UnityPackageTestMethodsPath; + break; + default: + throw new System.Exception("Undefined validation type"); + } + + var newScriptPath = $"{scriptPath}/{testName}"; + if (!newScriptPath.EndsWith(".cs")) + newScriptPath += ".cs"; + + var existingScript = AssetDatabase.LoadAssetAtPath(newScriptPath); + if (existingScript != null) + return existingScript; + + var scriptContent = + $"using AssetStoreTools.Validator.Data;\n" + + $"using AssetStoreTools.Validator.TestDefinitions;\n\n" + + $"namespace AssetStoreTools.Validator.TestMethods\n" + + $"{{\n" + + $" internal class {testName} : {derivedType}\n" + + $" {{\n" + + $" private {configType} _config;\n\n" + + $" // Constructor also accepts dependency injection of registered {nameof(IValidatorService)} types\n" + + $" public {testName}({configType} config)\n" + + $" {{\n" + + $" _config = config;\n" + + $" }}\n\n" + + $" public {nameof(TestResult)} {nameof(ITestScript.Run)}()\n" + + $" {{\n" + + $" var result = new {nameof(TestResult)}() {{ {nameof(TestResult.Status)} = {nameof(TestResultStatus)}.{nameof(TestResultStatus.Undefined)} }};\n" + + $" return result;\n" + + $" }}\n" + + $" }}\n" + + $"}}\n"; + + File.WriteAllText(newScriptPath, scriptContent); + AssetDatabase.Refresh(); + return AssetDatabase.LoadAssetAtPath(newScriptPath); + } + + public static string GetLongestProjectPath() + { + var longPaths = GetProjectPaths(new string[] { "Assets", "Packages" }); + return longPaths.Aggregate("", (max, cur) => max.Length > cur.Length ? max : cur); + } + + public static IEnumerable GetProjectPaths(string[] rootPaths) + { + var longPaths = new List(); + var guids = AssetDatabase.FindAssets("*", rootPaths); + + foreach (var guid in guids) + { + var path = AssetDatabase.GUIDToAssetPath(guid); + longPaths.Add(path); + } + + return longPaths; + } + + public static Texture GetStatusTexture(TestResultStatus status) + { + var iconTheme = ""; + if (!EditorGUIUtility.isProSkin) + iconTheme = "_d"; + + switch (status) + { + case TestResultStatus.Pass: + return (Texture)EditorGUIUtility.Load($"{WindowStyles.ValidatorIconsPath}/success{iconTheme}.png"); + case TestResultStatus.Warning: + return (Texture)EditorGUIUtility.Load($"{WindowStyles.ValidatorIconsPath}/warning{iconTheme}.png"); + case TestResultStatus.Fail: + return (Texture)EditorGUIUtility.Load($"{WindowStyles.ValidatorIconsPath}/error{iconTheme}.png"); + default: + return (Texture)EditorGUIUtility.Load($"{WindowStyles.ValidatorIconsPath}/undefined{iconTheme}.png"); + } + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Utility/ValidatorUtility.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Utility/ValidatorUtility.cs.meta new file mode 100644 index 0000000..b89dc0e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Utility/ValidatorUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 24792af98b4d87746a4b945e2a45dc2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ValidatorBase.cs b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ValidatorBase.cs new file mode 100644 index 0000000..e65f137 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ValidatorBase.cs @@ -0,0 +1,108 @@ +using AssetStoreTools.Validator.Categories; +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.Services; +using AssetStoreTools.Validator.TestDefinitions; +using AssetStoreTools.Validator.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Validator +{ + internal abstract class ValidatorBase : IValidator + { + public ValidationSettings Settings { get; private set; } + + private CategoryEvaluator _categoryEvaluator; + private List _automatedTests; + + protected ICachingService CachingService; + + public ValidatorBase(ValidationSettings settings) + { + Settings = settings; + _categoryEvaluator = new CategoryEvaluator(settings?.Category); + + CachingService = ValidatorServiceProvider.Instance.GetService(); + + CreateAutomatedTestCases(); + } + + private void CreateAutomatedTestCases() + { + var testData = ValidatorUtility.GetAutomatedTestCases(ValidatorUtility.SortType.Alphabetical); + _automatedTests = new List(); + + foreach (var t in testData) + { + var test = new AutomatedTest(t); + _automatedTests.Add(test); + } + } + + protected abstract void ValidateSettings(); + protected abstract ValidationResult GenerateValidationResult(); + + public ValidationResult Validate() + { + try + { + ValidateSettings(); + } + catch (Exception e) + { + return new ValidationResult() { Status = ValidationStatus.Failed, Exception = e }; + } + + var result = GenerateValidationResult(); + return result; + } + + protected List GetApplicableTests(params ValidationType[] validationTypes) + { + return _automatedTests.Where(x => validationTypes.Any(y => y == x.ValidationType)).ToList(); + } + + protected ValidationResult RunTests(List tests, ITestConfig config) + { + var completedTests = new List(); + + for (int i = 0; i < tests.Count; i++) + { + var test = tests[i]; + + EditorUtility.DisplayProgressBar("Validating", $"Running validation: {i + 1} - {test.Title}", (float)i / _automatedTests.Count); + + test.Run(config); + + // Adjust result based on categories + var updatedStatus = _categoryEvaluator.Evaluate(test); + test.Result.Status = updatedStatus; + + // Add the result + completedTests.Add(test); + +#if AB_BUILDER + EditorUtility.UnloadUnusedAssetsImmediate(); +#endif + } + + EditorUtility.UnloadUnusedAssetsImmediate(); + EditorUtility.ClearProgressBar(); + + var projectPath = Application.dataPath.Substring(0, Application.dataPath.Length - "/Assets".Length); + var hasCompilationErrors = EditorUtility.scriptCompilationFailed; + var result = new ValidationResult() + { + Status = ValidationStatus.RanToCompletion, + Tests = completedTests, + ProjectPath = projectPath, + HadCompilationErrors = hasCompilationErrors + }; + + return result; + } + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ValidatorBase.cs.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ValidatorBase.cs.meta new file mode 100644 index 0000000..c7fbc8e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/ValidatorBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2360246050affaa458413c6569c1f925 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles.meta new file mode 100644 index 0000000..a5cbef6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 21f473cb130d5f0458b2823b3a67f789 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/Style.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/Style.uss new file mode 100644 index 0000000..4a79ecd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/Style.uss @@ -0,0 +1,337 @@ +/* Validator Description */ + +.validator-description { + flex-direction: column; + flex-shrink: 0; + + margin: 10px 5px 2px 5px; + padding: 2px 4px; +} + +.validator-description-simple-container { + flex-direction: column; + flex-wrap: wrap; +} + +.validator-description-simple-label { + white-space: normal; +} + +.validator-description-full-container { + margin-top: 12px; +} + +.validator-description-full-label { + white-space: normal; + margin-bottom: 10px; +} + +.validator-description-hyperlink-button { + margin: 0; + padding: 0; + + align-self: flex-start; + cursor: link; +} + +.validator-description-show-button { + margin-top: 12px; +} + +.validator-description-hide-button { + margin-top: 12px; +} + +/* Validator Settings */ + +.validator-settings { + flex-direction: column; + flex-shrink: 0; + + margin: 0px 5px 2px 5px; + padding: 2px 4px; +} + +.validator-settings-selection-row { + flex-direction: row; + flex-grow: 1; + + margin-top: 10px; + padding: 0 3px 0 2px; +} + +.validator-settings-selection-label-help-row { + flex-direction: row; + flex-shrink: 1; + flex-grow: 0; + + align-self: center; + align-items: center; + justify-content: flex-start; + + width: 120px; +} + +.validator-settings-selection-label-help-row > Label { + -unity-font-style: bold; +} + +.validator-settings-selection-label-help-row > Image { + height: 16px; + width: 16px; +} + +.validator-settings-selection-dropdown { + flex-grow: 1; + flex-shrink: 1; + + align-self: stretch; + + margin-right: 0; + margin-left: 3px; + padding: 1px 4px; +} + +/* Validate Button */ + +.validator-validate-button { + align-self: stretch; + + height: 25px; + margin-left: 2px; +} + +/* Validation Paths */ + +.validator-paths { + flex-direction: column; + flex-grow: 1; + flex-shrink: 0; + + margin-bottom: 10px; + padding: 0; +} + +.validator-paths-box { + flex-grow: 1; + flex-direction: column; +} + +.validator-paths-scroll-view { + flex-grow: 1; + height: 100px; + margin-left: 3px; +} + +.validator-paths-scroll-view > .unity-scroll-view__content-viewport +{ + margin-left: 1px; +} + +.validator-paths-scroll-view > * > .unity-scroll-view__content-container +{ + padding: 0 0 0 0; +} + +.validator-paths-scroll-view > * > .unity-scroll-view__vertical-scroller +{ + margin: -1px 0; +} + +.validator-paths-scroll-view-bottom-row { + flex-direction: row-reverse; + margin: -1px 0 0 3px; +} + +.validator-paths-add-button { + margin: 3px 0 0 0; + align-self: center; +} + +.validator-paths-path-row { + flex-direction: row; + flex-grow: 1; + + margin-top: 2px; + padding: 0 5px 0 2px; +} + +.validator-paths-path-row-input-field { + flex-grow: 1; + flex-shrink: 1; + + padding-left: 5px; + + white-space: normal; + -unity-text-align: middle-left; +} + +.validator-paths-path-row-remove-button { + width: 20px; + height: 20px; + margin-left: 2px; + margin-right: 1px; + padding: 1px 0 0 0; +} + +/* Tests List & Groups */ + +.validator-test-list { + flex-grow: 1; + flex-shrink: 1; +} + +.validator-test-list-group-separator { + height: 2px; + margin: 5px 15px; +} + +.validator-test-list-group { + overflow: hidden; +} + +.validator-test-list-group-expander { + flex-direction: row; + flex-grow: 0; + flex-shrink: 0; + + align-items: center; + + min-width: 200px; + min-height: 30px; + + margin: 10px -1px 2px -1px; +} + +.validator-test-list-group-expander-arrow { + align-self: center; + + width: 30px; + height: 30px; + + margin: 0; + padding: 0; +} + +.validator-test-list-group-expander-image { + flex-shrink: 0; + flex-grow: 0; + + width: 17px; + height: 17px; + + margin: 0 7px 0 2px; +} + +.validator-test-list-group-expander-label { + font-size: 14px; +} + +.validator-test-list-group-content { + margin: -2px -2px -2px -2px; +} + +/* Validation Test */ + +.validator-test { + flex-direction: column; + flex-shrink: 0; + flex-grow: 0; + + padding: 2px 0; +} + +.validator-test-foldout { + flex-direction: row; + flex-grow: 1; + flex-shrink: 1; + + align-items: center; + justify-content: space-between; + + min-width: 200px; + min-height: 35px; + + margin: 0; + padding: 5px 10px; +} + +.validator-test-expander { + flex-direction: row; + flex-grow: 1; +} + +.validator-test-expander-arrow { + font-size: 11px; + align-self: center; + + width: 30px; + height: 30px; + + margin: 0; + padding: 0; +} + +.validator-text-expander-label { + flex-grow: 1; + flex-shrink: 1; + + -unity-text-align: middle-left; + -unity-font-style: bold; + white-space: normal; +} + +.validator-test-expander-image { + flex-shrink: 0; + + width: 14px; + height: 14px; + + margin: 0 10px; +} + +.validator-test-content { + flex-grow: 1; + flex-shrink: 0; + + margin: 0; + padding: 5px 30px; +} + +.validator-test-content-textfield { + white-space: normal; +} + +.validator-test-content-result-messages { + flex-direction: column; + flex-shrink: 0; + flex-grow: 0; + + margin: 10px 0 5px 0; + padding: 0 3px 3px 3px; +} + +.validator-test-content-result-messages-content { + flex-basis: auto; + flex-direction: column; + + margin-top: 3px; +} + +.validator-test-content-result-messages-content-button { + align-self: stretch; + + -unity-font-style: normal; + -unity-text-align: middle-left; + + margin: 0; +} + +.validator-test-content-result-messages-content-label { + white-space: normal; +} + +.validator-test-content-result-messages-separator { + height: 3px; + margin: 5px -3px 0 -3px; +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/Style.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/Style.uss.meta new file mode 100644 index 0000000..f933a2f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/Style.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c67a10c292c653428af654599fc15aa +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeDark.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeDark.uss new file mode 100644 index 0000000..47c13ea --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeDark.uss @@ -0,0 +1,166 @@ +.primary-colors +{ + /* Light - lighter */ + background-color: rgb(220, 220, 220); + /* Light - middle */ + background-color: rgb(200, 200, 200); + /* Light - darker */ + background-color: rgb(180, 180, 180); + + /* Dark - lighter */ + background-color: rgb(78, 78, 78); + /* Dark - middle */ + background-color: rgb(68, 68, 68); + /* Dark - darker */ + background-color: rgb(58, 58, 58); + + /* Border color - light */ + border-color: rgb(200, 200, 200); + /* Border color - dark */ + border-color: rgb(33, 33, 33); +} + +/* Validator Description */ + +.validator-description-hyperlink-button { + color: rgb(68, 113, 229); + border-width: 0; + background-color: rgba(0, 0, 0, 0); +} + +.validator-description-hyperlink-button:hover { + color: rgb(68, 133, 229); +} + +.validator-description-hyperlink-button:active { + color: rgb(68, 93, 229); +} + +/* Validator Settings */ + +.validator-settings-selection-label-help-row > Image { + --unity-image: resource("d__Help@2x"); +} + +.validator-settings-selection-dropdown { + color: rgb(238, 238, 238); + background-color: rgb(88, 88, 88); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(36, 36, 36); +} + +/* Validation Paths */ + +.validator-paths-scroll-view { + border-width: 1px; + border-color: rgb(33, 33, 33); + background-color: rgb(58, 58, 58); +} + +.validator-paths-scroll-view > * > .unity-scroll-view__vertical-scroller { + border-right-width: 0; +} + +.validator-paths-path-row-input-field:hover { + background-color: rgb(78, 78, 78); +} + +/* Tests List & Groups */ + +.validator-test-list { + flex-grow: 1; + flex-shrink: 1; +} + +.validator-test-list-group-separator { + background-color: rgb(104, 104, 104); +} + +.validator-test-list-group-expander { + border-width: 0; + border-color: rgba(0, 0, 0, 0); + + background-color: rgba(0, 0, 0, 0); +} + +.validator-test-list-group-expander-arrow { + color: rgb(104, 104, 104); +} + +.validator-test-list-group-expander-label { + color: rgb(255, 255, 255); + -unity-font-style: bold; +} + +/* Validation Test */ + +.validator-test-foldout { + border-width: 0; + border-radius: 0; + background-color: rgb(56, 56, 56); +} + +.validator-test-foldout:hover { + background-color: rgb(68, 68, 68); +} + +.validator-test-foldout:active { + background-color: rgb(48, 48, 48); +} + +.validator-test-foldout-expanded { + background-color: rgb(68, 68, 68); +} + +.validator-test-expander-arrow { + color: rgb(104, 104, 104); +} + +.validator-test-content { + background-color: rgb(68, 68, 68); +} + +.validator-test-content-textfield > .unity-base-field__input { + border-width: 0; + border-color: rgba(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0); +} + +.validator-test-content-result-messages { + border-left-width: 2px; + border-color: rgb(33, 33, 33); + background-color: rgb(58, 58, 58); +} + +.validator-test-content-result-messages-pass { + border-color: rgb(40, 200, 40); +} + +.validator-test-content-result-messages-warning { + border-color: rgb(200, 140, 40); +} + +.validator-test-content-result-messages-fail { + border-color: rgb(200, 40, 40); +} + +.validator-test-content-result-messages-content-button { + border-width: 0; + border-radius: 0; + + background-color: rgba(0, 0, 0, 0); +} + +.validator-test-content-result-messages-content-button:hover { + background-color: rgb(78, 78, 78); +} + +.validator-test-content-result-messages-content-button:active { + background-color: rgba(0, 0, 0, 0); +} + +.validator-test-content-result-messages-separator { + background-color: rgb(68, 68, 68); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeDark.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeDark.uss.meta new file mode 100644 index 0000000..fc5c649 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeDark.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d09164f0be2befd40aac764571737ff7 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeLight.uss b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeLight.uss new file mode 100644 index 0000000..2c29c72 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeLight.uss @@ -0,0 +1,166 @@ +.primary-colors +{ + /* Light - lighter */ + background-color: rgb(220, 220, 220); + /* Light - middle */ + background-color: rgb(200, 200, 200); + /* Light - darker */ + background-color: rgb(180, 180, 180); + + /* Dark - lighter */ + background-color: rgb(50, 50, 50); + /* Dark - middle */ + background-color: rgb(28, 28, 28); + /* Dark - darker */ + background-color: rgb(0, 0, 0); + + /* Border color - light */ + border-color: rgb(200, 200, 200); + /* Border color - dark */ + border-color: rgb(33, 33, 33); +} + +/* Validator Description */ + +.validator-description-hyperlink-button { + color: rgb(68, 113, 229); + border-width: 0; + background-color: rgba(0, 0, 0, 0); +} + +.validator-description-hyperlink-button:hover { + color: rgb(68, 133, 229); +} + +.validator-description-hyperlink-button:active { + color: rgb(68, 93, 229); +} + +/* Validator Settings */ + +.validator-settings-selection-label-help-row > Image { + --unity-image: resource("_Help@2x"); +} + +.validator-settings-selection-dropdown { + color: rgb(9, 9, 9); + background-color: rgb(228, 228, 228); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(178, 178, 178); +} + +/* Validation Paths */ + +.validator-paths-scroll-view { + border-width: 1px; + border-color: rgb(33, 33, 33); + background-color: rgb(180, 180, 180); +} + +.validator-paths-scroll-view > * > .unity-scroll-view__vertical-scroller { + border-right-width: 0; +} + +.validator-paths-path-row-input-field:hover { + background-color: rgb(200, 200, 200); +} + +/* Tests List & Groups */ + +.validator-test-list { + flex-grow: 1; + flex-shrink: 1; +} + +.validator-test-list-group-separator { + background-color: rgb(77, 77, 77); +} + +.validator-test-list-group-expander { + border-width: 0; + border-color: rgba(0, 0, 0, 0); + + background-color: rgba(0, 0, 0, 0); +} + +.validator-test-list-group-expander-arrow { + color: rgb(77, 77, 77); +} + +.validator-test-list-group-expander-label { + color: rgb(48, 48, 48); + -unity-font-style: bold; +} + +/* Validation Test */ + +.validator-test-foldout { + border-width: 0; + border-radius: 0; + background-color: rgb(198, 198, 198); +} + +.validator-test-foldout:hover { + background-color: rgb(212, 212, 212); +} + +.validator-test-foldout:active { + background-color: rgb(180, 180, 180); +} + +.validator-test-foldout-expanded { + background-color: rgb(212, 212, 212); +} + +.validator-test-expander-arrow { + color: rgb(77, 77, 77); +} + +.validator-test-content { + background-color: rgb(212, 212, 212); +} + +.validator-test-content-textfield > .unity-base-field__input { + border-width: 0; + border-color: rgba(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0); +} + +.validator-test-content-result-messages { + border-left-width: 2px; + border-color: rgb(33, 33, 33); + background-color: rgb(198, 198, 198); +} + +.validator-test-content-result-messages-pass { + border-color: rgb(40, 200, 40); +} + +.validator-test-content-result-messages-warning { + border-color: rgb(200, 140, 40); +} + +.validator-test-content-result-messages-fail { + border-color: rgb(200, 40, 40); +} + +.validator-test-content-result-messages-content-button { + border-width: 0; + border-radius: 0; + + background-color: rgba(0, 0, 0, 0); +} + +.validator-test-content-result-messages-content-button:hover { + background-color: rgb(212, 212, 212); +} + +.validator-test-content-result-messages-content-button:active { + background-color: rgba(0, 0, 0, 0); +} + +.validator-test-content-result-messages-separator { + background-color: rgb(212, 212, 212); +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeLight.uss.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeLight.uss.meta new file mode 100644 index 0000000..c3b2570 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Styles/ThemeLight.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7404a65e6f9592846a20fd5190b12b1a +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests.meta new file mode 100644 index 0000000..4515443 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 82d68ee644bbbb44183019f731e9f205 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic.meta new file mode 100644 index 0000000..e10e15e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 38036e7f211469848b7cf706e3a1febf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Animation Clips.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Animation Clips.asset new file mode 100644 index 0000000..c9b418f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Animation Clips.asset @@ -0,0 +1,27 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Animation Clips + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 31 + Title: Check Animation Clips + Description: Animation Clips should not have the default name 'Take 001'. This + could lead to confusion when filtering assets by animation clips. + CategoryInfo: + IsFailFilter: 0 + IsInclusiveFilter: 0 + AppliesToSubCategories: 1 + Filter: + - Animation + ValidationType: 0 + TestScript: {fileID: 11500000, guid: 7a28985886f182c4bacc89a44777c742, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Animation Clips.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Animation Clips.asset.meta new file mode 100644 index 0000000..e647903 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Animation Clips.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e0426dd01b5136a4ca1d42d312e12fad +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Audio Clipping.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Audio Clipping.asset new file mode 100644 index 0000000..356c3ae --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Audio Clipping.asset @@ -0,0 +1,32 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Audio Clipping + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 27 + Title: Check Audio Clipping + Description: 'Audio files should not peak above the defined dB threshold of -0.3db + + + Please + note that lossless audio files that are imported into Unity with their Compression + Format set to anything other than PCM can still fail the Validator. If such cases + arise, please export your audio files with extra headroom, or set the Compression + Format to PCM if applicable.' + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: + - Audio + TestScript: {fileID: 11500000, guid: f604db0353da0cb46bb048f5cd37186f, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Audio Clipping.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Audio Clipping.asset.meta new file mode 100644 index 0000000..f7b5dcf --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Audio Clipping.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 03c6cd398931b3e41b0784e8589e153f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Colliders.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Colliders.asset new file mode 100644 index 0000000..079d75b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Colliders.asset @@ -0,0 +1,26 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Colliders + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 4 + Title: Check Colliders + Description: Prefabs with meshes inside them have to have colliders applied to + them, if the Prefabs are marked as Static. Please make sure you have appropriately + sized colliders applied to your prefabs. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 308b3d7b7a883b949a14f47cfd5c7ebe, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Colliders.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Colliders.asset.meta new file mode 100644 index 0000000..9b1b8d4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Colliders.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 28ab5af444cf3c849800ed0d8f4a3102 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Compressed Files.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Compressed Files.asset new file mode 100644 index 0000000..f8aa71f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Compressed Files.asset @@ -0,0 +1,31 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Compressed Files + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 22 + Title: Check Compressed Files + Description: "Package should not contain nested .unitypackage or archive files + that obscure most of, or the entirety of the content.\n\n.unitypackage files + are acceptable for including setup preferences, settings, supplemental files + for other Asset Store products, or alternative render pipeline content\n\n.zip + files are acceptable if they are compressing files that do not natively function + in the Unity Editor. (For example, Blender, HTML Documentation, or Visual Studio + Projects). Such files should include 'source' in the file name (e.g. \"Blender_source.zip\" + or \"PSDSource.zip\")." + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 84b23febe0d923842aef73b95da5f25b, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Compressed Files.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Compressed Files.asset.meta new file mode 100644 index 0000000..dbf1164 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Compressed Files.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 53189e6e51235b14192c4d5b3145dd27 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Empty Prefabs.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Empty Prefabs.asset new file mode 100644 index 0000000..6154efa --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Empty Prefabs.asset @@ -0,0 +1,31 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Empty Prefabs + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 5 + Title: Check Empty Prefabs + Description: Prefabs cannot be empty, please make sure that you set up your prefabs. + correctly. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: + - 2D + - 3D + - Animation + - Essentials + - Templates + - VFX + TestScript: {fileID: 11500000, guid: 8055bed9373283e4793463b90b42f08f, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Empty Prefabs.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Empty Prefabs.asset.meta new file mode 100644 index 0000000..d2f3da2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Empty Prefabs.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 08790ea0ed0fd274fb1df75ccc32d415 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check File Menu Names.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check File Menu Names.asset new file mode 100644 index 0000000..ff0f7a4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check File Menu Names.asset @@ -0,0 +1,26 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check File Menu Names + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 21 + Title: Check File Menu Names + Description: File menus should be placed under an existing menu, such as "Window/". + If no existing menus are a good fit, they should be placed under a custom menu + called "Tools". + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: d8e3b12ecc1fcd74d9a9f8d2b549fc63, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check File Menu Names.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check File Menu Names.asset.meta new file mode 100644 index 0000000..a5a922a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check File Menu Names.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eaf232919893db04b8e05e91f6815424 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check LODs.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check LODs.asset new file mode 100644 index 0000000..2b332d7 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check LODs.asset @@ -0,0 +1,33 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check LODs + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 17 + Title: Check LODs + Description: 'Prefabs containing meshes with ''LOD'' in their name must meet the + following requirements: + + - LOD Mesh must be referenced by an LOD Group Component + + - + LOD Mesh GameObject must be a child of an LOD Group Component.' + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: + - 3D + - Essentials + - Templates + TestScript: {fileID: 11500000, guid: 43b2158602f87704fa7b91561cfc8678, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check LODs.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check LODs.asset.meta new file mode 100644 index 0000000..deb5c41 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check LODs.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ad52ffa05767e9d4bb4d92093ad68b03 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Line Endings.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Line Endings.asset new file mode 100644 index 0000000..08e9426 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Line Endings.asset @@ -0,0 +1,26 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Line Endings + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 20 + Title: Check Line Endings + Description: Inconsistent line endings in scripts might lead to incorrect line + numbers in stacktraces and compiler errors. Many text editors can fix this using + Convert Line Endings menu commands. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 85885005d1c594f42826de3555e98365, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Line Endings.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Line Endings.asset.meta new file mode 100644 index 0000000..699185f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Line Endings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1e7b5480c1d8bda43ab4fa945939e243 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Mesh Prefabs.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Mesh Prefabs.asset new file mode 100644 index 0000000..3d8a99a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Mesh Prefabs.asset @@ -0,0 +1,26 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Mesh Prefabs + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 2 + Title: Check Mesh Prefabs + Description: Each mesh should have a corresponding prefab set up with all variations + of the texture/mesh/material that you are providing. Please create prefabs for + all of your imported objects. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 3c3d0d642ac6a6a48aa124a93dae3734, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Mesh Prefabs.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Mesh Prefabs.asset.meta new file mode 100644 index 0000000..cff122c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Mesh Prefabs.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 03b362b67028eb443b7ba8b84aedd5f2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Assets.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Assets.asset new file mode 100644 index 0000000..0a69e35 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Assets.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Missing Components in Assets + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 9 + Title: Check Missing Components in Assets + Description: We do not allow missing or broken material/texture/prefab/script connections + in your package. Please make sure none of your assets have missing components. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 22d8f814e2363e34ea220736a4042728, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Assets.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Assets.asset.meta new file mode 100644 index 0000000..6ba4103 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Assets.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1a3d0b3827fc16347867bee335e8f4ea +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Scenes.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Scenes.asset new file mode 100644 index 0000000..f5ecb1a --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Scenes.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Missing Components in Scenes + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 10 + Title: Check Missing Components in Scenes + Description: We do not allow missing or broken material/texture/prefab/script connections + in your package. Please make sure none of your scene objects have missing components. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 511e76d0ebcb23d40a7b49dda0e2980f, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Scenes.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Scenes.asset.meta new file mode 100644 index 0000000..42d6127 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Missing Components in Scenes.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bc2cb4e6635aa334ea4a52e2e3ce57c8 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Import Logs.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Import Logs.asset new file mode 100644 index 0000000..f5d9b0f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Import Logs.asset @@ -0,0 +1,26 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Model Import Logs + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 32 + Title: Check Model Import Logs + Description: Model assets should work without issues. Please make sure that there + are no errors or warnings when these models are imported. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + ValidationType: 0 + TestScript: {fileID: 11500000, guid: 98f3ec209166855408eaf4abe5bff591, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Import Logs.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Import Logs.asset.meta new file mode 100644 index 0000000..ba55c59 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Import Logs.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c889cdd91c2f41941a14363dad7a1a38 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Orientation.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Orientation.asset new file mode 100644 index 0000000..dfd8345 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Orientation.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Model Orientation + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 7 + Title: Check Model Orientation + Description: 'Meshes should be facing the correct way. The proper facing is: Z + vector is forward, Y vector is up, X vector is right.' + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 56cdcdc41a80fbc46b5b2b83ec8d66d7, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Orientation.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Orientation.asset.meta new file mode 100644 index 0000000..4f49313 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Orientation.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 45b2b11da67e8864aacc62d928524b4c +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Types.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Types.asset new file mode 100644 index 0000000..e9c9aac --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Types.asset @@ -0,0 +1,24 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Model Types + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 23 + Title: Check Model Types + Description: Mesh assets must be either .fbx, .dae, .abc, or .obj file types. + CategoryInfo: + IsFailFilter: 0 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 428b1fb838e6f5a469bbfd26ca3fbfd2, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Types.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Types.asset.meta new file mode 100644 index 0000000..c4aef07 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Model Types.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ffef800a102b0e04cae1a3b98549ef1b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Normal Map Textures.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Normal Map Textures.asset new file mode 100644 index 0000000..2f65269 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Normal Map Textures.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Normal Map Textures + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 26 + Title: Check Normal Map Textures + Description: Textures that are assigned to Materials as Normal Maps should have + their import Texture Type set to 'Normal Map' + CategoryInfo: + IsFailFilter: 0 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: d55cea510248f814eb2194c2b53f88d2, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Normal Map Textures.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Normal Map Textures.asset.meta new file mode 100644 index 0000000..66ff0da --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Normal Map Textures.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 241ad0174fcadb64da867011d196acbb +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Package Naming.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Package Naming.asset new file mode 100644 index 0000000..5c34623 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Package Naming.asset @@ -0,0 +1,30 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Package Naming + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 33 + Title: Check Package Naming + Description: Your package and its contents should be named appropriately and not + artificially force themselves up the hierarchy within commonly used project folders. + This can happen when a file or a folder name starts with a special character + (such as an underscore). + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 0 + AppliesToSubCategories: 1 + Filter: + - Essentials + - Templates + ValidationType: 0 + TestScript: {fileID: 11500000, guid: afe9e04825c7d904981a54404b222290, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Package Naming.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Package Naming.asset.meta new file mode 100644 index 0000000..f5e0fea --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Package Naming.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 04098aa074d151b4a908dfa79dfddec3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Particle Systems.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Particle Systems.asset new file mode 100644 index 0000000..62d55cd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Particle Systems.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Particle Systems + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 25 + Title: Check Particle Systems + Description: All Particle Systems should be saved as Prefabs + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: + - VFX + TestScript: {fileID: 11500000, guid: 6a623f7988c75884bb17b169ccd3e993, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Particle Systems.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Particle Systems.asset.meta new file mode 100644 index 0000000..5404fd4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Particle Systems.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 87da7eaed3cee0d4b8ada0b500e3a958 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Path Lengths.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Path Lengths.asset new file mode 100644 index 0000000..676ae48 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Path Lengths.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Path Lengths + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 28 + Title: Check Path Lengths + Description: Package content file paths should not be excessively lengthened. File + paths for assets must be under 140 characters + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: ae379305e9165e84584373a8272c09e7, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Path Lengths.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Path Lengths.asset.meta new file mode 100644 index 0000000..4ebd5ac --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Path Lengths.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 21f8ec0602ffac045b1f4a93f8a9b555 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Prefab Transforms.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Prefab Transforms.asset new file mode 100644 index 0000000..2cf336b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Prefab Transforms.asset @@ -0,0 +1,28 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Prefab Transforms + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 3 + Title: Check Prefab Transforms + Description: Prefabs must have their position/rotation set to 0, and their scale + set to 1. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: + - 3D + - Essentials + - Templates + TestScript: {fileID: 11500000, guid: f712c17a60bf2d049a4e61c8f79e56c2, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Prefab Transforms.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Prefab Transforms.asset.meta new file mode 100644 index 0000000..713d908 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Prefab Transforms.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 700026f446833f649a3c63b33a90a295 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Script Compilation.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Script Compilation.asset new file mode 100644 index 0000000..fae309f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Script Compilation.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Script Compilation + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 29 + Title: Check Script Compilation + Description: Scripts in the package must compile successfully and not result in + compilation errors + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 59db88f43969db8499299bce7f4fb967, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Script Compilation.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Script Compilation.asset.meta new file mode 100644 index 0000000..3a026fb --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Script Compilation.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 339e21c955642a04289482aa923e10b6 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Shader Compilation.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Shader Compilation.asset new file mode 100644 index 0000000..dcbd609 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Shader Compilation.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Shader Compilation + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 18 + Title: Check Shader Compilation + Description: Please make sure the shaders inside your package do not have errors + and compile successfully. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 7abb278a6082bde4391e0779394cb85b, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Shader Compilation.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Shader Compilation.asset.meta new file mode 100644 index 0000000..c9ebccf --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Shader Compilation.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1450037453608204a989ff95dca62fae +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Texture Dimensions.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Texture Dimensions.asset new file mode 100644 index 0000000..d676f14 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Texture Dimensions.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Texture Dimensions + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 24 + Title: Check Texture Dimensions + Description: Dimensions of textures, where appropriate, should have pixel counts + that are a power of 2 + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 073f1dacf3da34d4783140ae9d485d5f, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Texture Dimensions.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Texture Dimensions.asset.meta new file mode 100644 index 0000000..d0318d4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Texture Dimensions.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c23253393b8e28846b8e02aeaee7e152 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Type Namespaces.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Type Namespaces.asset new file mode 100644 index 0000000..8331d60 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Type Namespaces.asset @@ -0,0 +1,31 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Type Namespaces + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 19 + Title: Check Type Namespaces + Description: 'Types in your scripts (classes, interfaces, structs, enums) should + be nested under a namespace block to prevent them from being mistaken for other + potential types and for better organization as a whole. + + + It is not allowed + to nest your code under a Unity namespace.' + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + ValidationType: 0 + TestScript: {fileID: 11500000, guid: 279249fa7ef8c2446b3a9f013eeedbf0, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Type Namespaces.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Type Namespaces.asset.meta new file mode 100644 index 0000000..2aa250d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Check Type Namespaces.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd110ee16e8de4d48a602349ed7a0b25 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Executable Files.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Executable Files.asset new file mode 100644 index 0000000..1a00c2f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Executable Files.asset @@ -0,0 +1,27 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Remove Executable Files + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 14 + Title: Remove Executable Files + Description: Your package must not contain an .exe, installer programs or applications. + If your plugin requires an external program to run, please remove the installer + program from your package and write the instructions on how to download and install + the installer program in your documentation. + CategoryInfo: + IsFailFilter: 0 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 8e4450592cc60e54286ad089b66db94d, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Executable Files.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Executable Files.asset.meta new file mode 100644 index 0000000..b27033d --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Executable Files.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e996c53186de96e49a742d414648a809 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JPG Files.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JPG Files.asset new file mode 100644 index 0000000..cd2900f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JPG Files.asset @@ -0,0 +1,30 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Remove JPG Files + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 8 + Title: Remove JPG Files + Description: We do not allow texture images that are saved in lossy formats. Please + save all of your images as lossless format file types, such as PNG, TGA, or PSD. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: + - 2D + - 3D + - Animation + - Essentials + - VFX/Shaders + TestScript: {fileID: 11500000, guid: 5634a12b3a8544c4585bbc280ae59ce2, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JPG Files.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JPG Files.asset.meta new file mode 100644 index 0000000..45000c9 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JPG Files.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 781021ae3aa6570468e08d78e3195127 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JavaScript Files.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JavaScript Files.asset new file mode 100644 index 0000000..bc5102e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JavaScript Files.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Remove JavaScript Files + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 11 + Title: Remove JavaScript Files + Description: JavaScript / UnityScript files are not allowed, as they are no longer + supported. + CategoryInfo: + IsFailFilter: 0 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: ab1676bde9afba442b35fd3319c18063, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JavaScript Files.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JavaScript Files.asset.meta new file mode 100644 index 0000000..d41b9e6 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove JavaScript Files.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bf01c18b66907f54c99517f6a877e3e0 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Lossy Audio Files.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Lossy Audio Files.asset new file mode 100644 index 0000000..954f6fd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Lossy Audio Files.asset @@ -0,0 +1,32 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Remove Lossy Audio Files + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 12 + Title: Remove Lossy Audio Files + Description: 'We do not recommend audio files that are saved as .mp3 or .ogg. Please + save all of your audio as lossless format file types, such as .wav. If you have + non-lossy alternative files to your lossy audio files, please make sure to name + them identically to avoid rejection. For example: ''shout.mp3'', ''shout.wav''. + Listing of the format in the file name is also allowed and accounted for by the + validator. For example: ''taunt MP3.mp3'', ''taunt WAV.wav''.' + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: + - Audio + - Essentials + - Templates + TestScript: {fileID: 11500000, guid: b7205a85061273a4eb50586f13f35d35, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Lossy Audio Files.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Lossy Audio Files.asset.meta new file mode 100644 index 0000000..2026425 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Lossy Audio Files.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a48657926de5cfb47ac559a7108d03ee +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Mixamo Files.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Mixamo Files.asset new file mode 100644 index 0000000..8d3cc14 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Mixamo Files.asset @@ -0,0 +1,26 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Remove Mixamo Files + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 15 + Title: Remove Mixamo Files + Description: We do not allow or accept packages files that were made with third-party + software, such as Mixamo, Fuse, etc. because these files are under licensing + that does not agree with the Asset Store End User License Agreement (EULA). + CategoryInfo: + IsFailFilter: 0 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: 9df432e52aa958b44bb5e20c13d16552, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Mixamo Files.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Mixamo Files.asset.meta new file mode 100644 index 0000000..84abdb1 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Mixamo Files.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a0a44055f786ec64f86a07a214d5f831 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove SpeedTree Files.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove SpeedTree Files.asset new file mode 100644 index 0000000..14ff13f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove SpeedTree Files.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Remove SpeedTree Files + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 16 + Title: Remove SpeedTree Files + Description: You cannot redistribute SpeedTree files on other marketplaces. Please + remove all SpeedTree files that are in this package. + CategoryInfo: + IsFailFilter: 0 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: e06bb7e0aa4f9944abc18281c002dff4, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove SpeedTree Files.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove SpeedTree Files.asset.meta new file mode 100644 index 0000000..ffc10af --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove SpeedTree Files.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 305bbe67f7c644d18bc8a5b2273aa6a4 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Video Files.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Video Files.asset new file mode 100644 index 0000000..1df19b9 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Video Files.asset @@ -0,0 +1,26 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Remove Video Files + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 13 + Title: Remove Video Files + Description: You cannot include a video file in your package. Please upload your + video file to an online video hosting website (Youtube, Vimeo, etc.) and include + the link to the video in your written documentation. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + TestScript: {fileID: 11500000, guid: f99724c71b0de66419b5d6e8e9bfcc2d, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Video Files.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Video Files.asset.meta new file mode 100644 index 0000000..e62946c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/Generic/Remove Video Files.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 893a0df188c2026438be48eed39b301f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage.meta new file mode 100644 index 0000000..0a50cef --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8e978e836f2fb224fa11de94e913da49 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Demo Scenes.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Demo Scenes.asset new file mode 100644 index 0000000..af6ecc2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Demo Scenes.asset @@ -0,0 +1,34 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Demo Scenes + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 1 + Title: Check Demo Scenes + Description: If your product has content to show off, it should be displayed in + a demo scene. Please provide a practical demo with all of your assets set up. + If your asset is based on scripting or Editor extensions, please consider adding + a demo scene showcasing the asset or showing setup steps in the scene. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: + - 3D + - 2D + - Animation + - Essentials + - Templates + - VFX + ValidationType: 1 + TestScript: {fileID: 11500000, guid: f844c2dfa4669ff4eacf5591b544edaf, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Demo Scenes.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Demo Scenes.asset.meta new file mode 100644 index 0000000..d58914f --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Demo Scenes.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f108107be07f69045813d69eff580078 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Documentation.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Documentation.asset new file mode 100644 index 0000000..6ce011e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Documentation.asset @@ -0,0 +1,32 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Documentation + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 6 + Title: Check Documentation + Description: If your asset contains any code (scripts, shaders) - we ask that you + include offline documentation in the format of pdf or rtf with your submission, + as it is mandatory for all packages that include scripts or other components + that require set up. Your documentation must be organized with a table of contents + and numbered, written in English and have no grammar mistakes. Create a setup + guide with a step-by-step tutorial (pdf or video), as well as a script reference + if users will need to do any coding. If your asset contains art (3D models, sprites) + and you used code to set up a demo scene, you may skip this step. + CategoryInfo: + IsFailFilter: 1 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: [] + ValidationType: 1 + TestScript: {fileID: 11500000, guid: 3c8425198983eda4c9b35aa0d59ea33c, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Documentation.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Documentation.asset.meta new file mode 100644 index 0000000..4ee6335 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Documentation.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b03433f7977b29e4ca7e8d76393a6c26 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Package Size.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Package Size.asset new file mode 100644 index 0000000..e80b2c4 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Package Size.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Package Size + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 30 + Title: Check Package Size + Description: Package submissions should not be more than 6 GB in size + CategoryInfo: + IsFailFilter: 0 + IsInclusiveFilter: 0 + AppliesToSubCategories: 1 + Filter: [] + ValidationType: 1 + TestScript: {fileID: 11500000, guid: a8601b99f4afa5049954f3a2dd5996d6, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Package Size.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Package Size.asset.meta new file mode 100644 index 0000000..63433dd --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Package Size.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 25721b2d7384e5b4f936cf3b33b80a02 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Project Template Assets.asset b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Project Template Assets.asset new file mode 100644 index 0000000..645117c --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Project Template Assets.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: 11500000, guid: d813ff809ae82f643bf975031305d541, type: 3} + m_Name: Check Project Template Assets + m_EditorClassIdentifier: + HasBeenInitialized: 1 + Id: 34 + Title: Check Project Template Assets + Description: 'Assets in your package derived from project template assets should + be assigned a new GUID and be located under a different path. + + + This is to + minimize the risk of users accidentally overwriting their own assets when importing + your package. + + + Example commonly used asset: ''Assets/Scenes/SampleScene.unity''' + CategoryInfo: + IsFailFilter: 0 + IsInclusiveFilter: 1 + AppliesToSubCategories: 1 + Filter: + - Essentials + - Templates + ValidationType: 1 + TestScript: {fileID: 11500000, guid: f02d52a702c712e4e8089f7c2e65bae7, type: 3} diff --git a/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Project Template Assets.asset.meta b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Project Template Assets.asset.meta new file mode 100644 index 0000000..a00554e --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/Editor/Validator/Tests/UnityPackage/Check Project Template Assets.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5392e9de0549574419ff76897d1e0fa1 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/LICENSE.md b/Installer/Packages/com.unity.asset-store-tools/LICENSE.md new file mode 100644 index 0000000..b20d72b --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/LICENSE.md @@ -0,0 +1,5 @@ +Asset Store Tools v2 copyright © 2025 Unity Technologies + +Source code of the package is licensed under the Unity Companion License (see https://unity.com/legal/licenses/unity-companion-license); otherwise licensed under the Unity Package Distribution License (see https://unity.com/legal/licenses/unity-package-distribution-license ) + +Unless expressly provided otherwise, the software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions. \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/LICENSE.md.meta b/Installer/Packages/com.unity.asset-store-tools/LICENSE.md.meta new file mode 100644 index 0000000..b5792bf --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/LICENSE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: baeaa62ad0dc664428d6069db8fd986d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/com.unity.asset-store-tools/package.json b/Installer/Packages/com.unity.asset-store-tools/package.json new file mode 100644 index 0000000..8a64e18 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/package.json @@ -0,0 +1,11 @@ +{ + "name": "com.unity.asset-store-tools", + "displayName": "Asset Store Tools", + "version": "12.0.1", + "unity": "2019.4", + "description": "Whether you're a programmer, game designer, texture artist or 3D modeler, you're welcome to share your creations with everybody in the Unity developer community!", + "type": "tool", + "dependencies": { + "com.unity.nuget.newtonsoft-json": "3.2.1" + } +} \ No newline at end of file diff --git a/Installer/Packages/com.unity.asset-store-tools/package.json.meta b/Installer/Packages/com.unity.asset-store-tools/package.json.meta new file mode 100644 index 0000000..e319cf2 --- /dev/null +++ b/Installer/Packages/com.unity.asset-store-tools/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fca7c22c787fbfd4cb0d7f186668631a +PackageManifestImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Installer/Packages/manifest.json b/Installer/Packages/manifest.json new file mode 100644 index 0000000..94941c0 --- /dev/null +++ b/Installer/Packages/manifest.json @@ -0,0 +1,19 @@ +{ + "dependencies": { + "com.unity.test-framework": "1.1.33" + }, + "scopedRegistries": [ + { + "name": "package.openupm.com", + "url": "https://package.openupm.com", + "scopes": [ + "com.ivanmurzak", + "extensions.unity", + "org.nuget.com.ivanmurzak", + "org.nuget.microsoft", + "org.nuget.system", + "org.nuget.r3" + ] + } + ] +} \ No newline at end of file diff --git a/Installer/Packages/packages-lock.json b/Installer/Packages/packages-lock.json new file mode 100644 index 0000000..09f78c7 --- /dev/null +++ b/Installer/Packages/packages-lock.json @@ -0,0 +1,49 @@ +{ + "dependencies": { + "com.unity.asset-store-tools": { + "version": "file:com.unity.asset-store-tools", + "depth": 0, + "source": "embedded", + "dependencies": { + "com.unity.nuget.newtonsoft-json": "3.2.1" + } + }, + "com.unity.ext.nunit": { + "version": "1.0.6", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.nuget.newtonsoft-json": { + "version": "3.2.1", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.test-framework": { + "version": "1.1.33", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.modules.imgui": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.jsonserialize": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": {} + } + } +} diff --git a/ProjectSettings/AudioManager.asset b/Installer/ProjectSettings/AudioManager.asset similarity index 100% rename from ProjectSettings/AudioManager.asset rename to Installer/ProjectSettings/AudioManager.asset diff --git a/ProjectSettings/ClusterInputManager.asset b/Installer/ProjectSettings/ClusterInputManager.asset similarity index 100% rename from ProjectSettings/ClusterInputManager.asset rename to Installer/ProjectSettings/ClusterInputManager.asset diff --git a/Installer/ProjectSettings/DynamicsManager.asset b/Installer/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..1596c42 --- /dev/null +++ b/Installer/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0.1 + m_ClothInterCollisionStiffness: 0.2 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 0 + m_ReuseCollisionCallbacks: 0 + m_ClothInterCollisionSettingsToggle: 0 + m_ClothGravity: {x: 0, y: -9.81, z: 0} + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 + m_FrictionType: 0 + m_EnableEnhancedDeterminism: 0 + m_EnableUnifiedHeightmaps: 1 + m_SolverType: 0 + m_DefaultMaxAngularSpeed: 50 diff --git a/ProjectSettings/EditorBuildSettings.asset b/Installer/ProjectSettings/EditorBuildSettings.asset similarity index 100% rename from ProjectSettings/EditorBuildSettings.asset rename to Installer/ProjectSettings/EditorBuildSettings.asset diff --git a/Installer/ProjectSettings/EditorSettings.asset b/Installer/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..aaea47f --- /dev/null +++ b/Installer/ProjectSettings/EditorSettings.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_ExternalVersionControlSupport: Visible Meta Files + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 2 + m_DefaultBehaviorMode: 0 + m_PrefabRegularEnvironment: {fileID: 0} + m_PrefabUIEnvironment: {fileID: 0} + m_SpritePackerMode: 0 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref;rsp + m_ProjectGenerationRootNamespace: + m_CollabEditorSettings: + inProgressEnabled: 1 + m_EnableTextureStreamingInEditMode: 1 + m_EnableTextureStreamingInPlayMode: 1 + m_AsyncShaderCompilation: 1 + m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptions: 3 + m_ShowLightmapResolutionOverlay: 1 + m_UseLegacyProbeSampleCount: 0 + m_AssetPipelineMode: 1 + m_CacheServerMode: 0 + m_CacheServerEndpoint: + m_CacheServerNamespacePrefix: default + m_CacheServerEnableDownload: 1 + m_CacheServerEnableUpload: 1 + m_CacheServerValidationMode: 2 diff --git a/ProjectSettings/GraphicsSettings.asset b/Installer/ProjectSettings/GraphicsSettings.asset similarity index 92% rename from ProjectSettings/GraphicsSettings.asset rename to Installer/ProjectSettings/GraphicsSettings.asset index 6652e42..35a0fa7 100644 --- a/ProjectSettings/GraphicsSettings.asset +++ b/Installer/ProjectSettings/GraphicsSettings.asset @@ -3,7 +3,7 @@ --- !u!30 &1 GraphicsSettings: m_ObjectHideFlags: 0 - serializedVersion: 14 + serializedVersion: 13 m_Deferred: m_Mode: 1 m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} @@ -28,7 +28,6 @@ GraphicsSettings: m_LensFlare: m_Mode: 1 m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} - m_VideoShadersIncludeMode: 2 m_AlwaysIncludedShaders: - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} @@ -38,8 +37,8 @@ GraphicsSettings: - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} m_PreloadedShaders: [] - m_PreloadShadersBatchTimeLimit: -1 - m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, + type: 0} m_CustomRenderPipeline: {fileID: 0} m_TransparencySortMode: 0 m_TransparencySortAxis: {x: 0, y: 0, z: 1} @@ -61,6 +60,5 @@ GraphicsSettings: m_AlbedoSwatchInfos: [] m_LightsUseLinearIntensity: 0 m_LightsUseColorTemperature: 0 - m_DefaultRenderingLayerMask: 1 m_LogWhenShaderIsCompiled: 0 - m_SRPDefaultSettings: {} + m_AllowEnlightenSupportForUpgradedProject: 0 diff --git a/Installer/ProjectSettings/InputManager.asset b/Installer/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..17c8f53 --- /dev/null +++ b/Installer/ProjectSettings/InputManager.asset @@ -0,0 +1,295 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 diff --git a/Installer/ProjectSettings/NavMeshAreas.asset b/Installer/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..3b0b7c3 --- /dev/null +++ b/Installer/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid diff --git a/Installer/ProjectSettings/PackageManagerSettings.asset b/Installer/ProjectSettings/PackageManagerSettings.asset new file mode 100644 index 0000000..18dfb52 --- /dev/null +++ b/Installer/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,60 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_ScopedRegistriesSettingsExpanded: 1 + oneTimeWarningShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.com + m_Scopes: [] + m_IsDefault: 1 + - m_Id: scoped:package.openupm.com + m_Name: package.openupm.com + m_Url: https://package.openupm.com + m_Scopes: + - com.ivanmurzak + - extensions.unity + - org.nuget.com.ivanmurzak + - org.nuget.microsoft + - org.nuget.system + - org.nuget.r3 + m_IsDefault: 0 + m_UserSelectedRegistryName: + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_ErrorMessage: + m_Original: + m_Id: scoped:package.openupm.com + m_Name: package.openupm.com + m_Url: https://package.openupm.com + m_Scopes: + - com.ivanmurzak + - extensions.unity + - org.nuget.com.ivanmurzak + - org.nuget.microsoft + - org.nuget.system + - org.nuget.r3 + m_IsDefault: 0 + m_Modified: 0 + m_Name: package.openupm.com + m_Url: https://package.openupm.com + m_Scopes: + - com.ivanmurzak + - extensions.unity + - org.nuget.com.ivanmurzak + - org.nuget.microsoft + - org.nuget.system + - org.nuget.r3 + m_SelectedScopeIndex: 0 diff --git a/Installer/ProjectSettings/Physics2DSettings.asset b/Installer/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..6c5cf8a --- /dev/null +++ b/Installer/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,56 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 4 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_JobOptions: + serializedVersion: 2 + useMultithreading: 0 + useConsistencySorting: 0 + m_InterpolationPosesPerJob: 100 + m_NewContactsPerJob: 30 + m_CollideContactsPerJob: 100 + m_ClearFlagsPerJob: 200 + m_ClearBodyForcesPerJob: 200 + m_SyncDiscreteFixturesPerJob: 50 + m_SyncContinuousFixturesPerJob: 50 + m_FindNearestContactsPerJob: 100 + m_UpdateTriggerContactsPerJob: 100 + m_IslandSolverCostThreshold: 100 + m_IslandSolverBodyCostScale: 1 + m_IslandSolverContactCostScale: 10 + m_IslandSolverJointCostScale: 10 + m_IslandSolverBodiesPerJob: 50 + m_IslandSolverContactsPerJob: 50 + m_AutoSimulation: 1 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_CallbacksOnDisable: 1 + m_ReuseCollisionCallbacks: 0 + m_AutoSyncTransforms: 0 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/ProjectSettings/PresetManager.asset b/Installer/ProjectSettings/PresetManager.asset similarity index 100% rename from ProjectSettings/PresetManager.asset rename to Installer/ProjectSettings/PresetManager.asset diff --git a/Installer/ProjectSettings/ProjectSettings.asset b/Installer/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..c77ca5a --- /dev/null +++ b/Installer/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,653 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 20 + productGUID: bcf6e1937d8c09f42b50d1f476ab947b + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: Installer + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1024 + defaultScreenHeight: 768 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + iosUseCustomAppBackgroundBehavior: 0 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 1 + androidUseSwappy: 0 + androidBlitType: 0 + androidResizableWindow: 0 + androidDefaultWindowWidth: 1920 + androidDefaultWindowHeight: 1080 + androidMinimumWindowWidth: 400 + androidMinimumWindowHeight: 300 + androidFullscreenMode: 1 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 0 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + useFlipModelSwapchain: 1 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + stadiaPresentMode: 0 + stadiaTargetFramerate: 0 + vulkanNumSwapchainBuffers: 3 + vulkanEnableSetSRGBWrite: 0 + vulkanEnableLateAcquireNextImage: 0 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + vrSettings: + cardboard: + depthFormat: 0 + enableTransitionView: 0 + daydream: + depthFormat: 0 + useSustainedPerformanceMode: 0 + enableVideoLayer: 0 + useProtectedVideoMemory: 0 + minimumSupportedHeadTracking: 0 + maximumSupportedHeadTracking: 1 + hololens: + depthFormat: 1 + depthBufferSharingEnabled: 1 + lumin: + depthFormat: 0 + frameTiming: 2 + enableGLCache: 0 + glCacheMaxBlobSize: 524288 + glCacheMaxFileSize: 8388608 + oculus: + sharedDepthBuffer: 1 + dashSupport: 1 + lowOverheadMode: 0 + protectedContext: 0 + v2Signing: 1 + enable360StereoCapture: 0 + isWsaHolographicRemotingEnabled: 0 + enableFrameTimingStats: 0 + useHDRDisplay: 0 + D3DHDRBitDepth: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: {} + buildNumber: {} + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 19 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + VertexChannelCompressionMask: 4054 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: 10.0 + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 10.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSUseLaunchScreenStoryboard: 0 + iOSLaunchScreenCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + clonedFromGUID: 00000000000000000000000000000000 + templatePackageId: + templateDefaultScene: + AndroidTargetArchitectures: 1 + AndroidTargetDevices: 0 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 0 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + androidUseCustomKeystore: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + chromeosInputEmulation: 1 + AndroidValidateAppBundleSize: 1 + AndroidAppBundleSizeToValidate: 150 + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetGraphicsJobs: [] + m_BuildTargetGraphicsJobMode: [] + m_BuildTargetGraphicsAPIs: [] + m_BuildTargetVRSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + openGLRequireES32: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 22 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + switchUseMicroSleepForYield: 1 + switchEnableRamDiskSupport: 0 + switchMicroSleepForYieldTime: 25 + switchRamDiskSpaceSize: 12 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 2 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + playerPrefsMaxSize: 32768 + ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4UseLowGarlicFragmentationMode: 1 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 2 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + ps4attribVROutputEnabled: 0 + ps5ParamFilePath: + ps5VideoOutPixelFormat: 0 + ps5VideoOutInitialWidth: 1920 + ps5VideoOutOutputMode: 1 + ps5BackgroundImagePath: + ps5StartupImagePath: + ps5Pic2Path: + ps5StartupImagesFolder: + ps5IconImagesFolder: + ps5SaveDataImagePath: + ps5SdkOverride: + ps5BGMPath: + ps5ShareOverlayImagePath: + ps5NPConfigZipPath: + ps5Passcode: F69AzBlax3CF3EDNhm3soLBPh71Yexui + ps5UseResolutionFallback: 0 + ps5UseAudio3dBackend: 0 + ps5ScriptOptimizationLevel: 2 + ps5Audio3dVirtualSpeakerCount: 14 + ps5VrrSupport: 0 + ps5UpdateReferencePackage: + ps5disableAutoHideSplash: 0 + ps5OperatingSystemCanDisableSplashScreen: 0 + ps5IncludedModules: [] + ps5SharedBinaryContentLabels: [] + ps5SharedBinarySystemFolders: [] + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + blurSplashScreenBackground: 1 + spritePackerPolicy: + webGLMemorySize: 32 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 1 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 0 + webGLLinkerTarget: 1 + webGLThreadsSupport: 0 + webGLWasmStreaming: 0 + scriptingDefineSymbols: + 1: IVAN_MURZAK_INSTALLER_PROJECT + platformArchitecture: {} + scriptingBackend: {} + il2cppCompilerConfiguration: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + suppressCommonWarnings: 1 + allowUnsafeCode: 0 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + gcIncremental: 0 + assemblyVersionValidation: 1 + gcWBarrierValidation: 0 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: Installer + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: Installer + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, + a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnhancedXboxCompatibilityMode: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: + vrEditorSettings: + daydream: + daydreamIconForeground: {fileID: 0} + daydreamIconBackground: {fileID: 0} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_SignPackage: 1 + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + apiCompatibilityLevel: 6 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + projectName: + organizationId: + cloudEnabled: 0 + enableNativePlatformBackendsForNewInputSystem: 0 + disableOldInputManagerSupport: 0 + legacyClampBlendShapeWeights: 0 diff --git a/ProjectSettings/ProjectVersion.txt b/Installer/ProjectSettings/ProjectVersion.txt similarity index 100% rename from ProjectSettings/ProjectVersion.txt rename to Installer/ProjectSettings/ProjectVersion.txt diff --git a/Installer/ProjectSettings/QualitySettings.asset b/Installer/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..7c920b9 --- /dev/null +++ b/Installer/ProjectSettings/QualitySettings.asset @@ -0,0 +1,240 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 255 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + CloudRendering: 5 + GameCoreScarlett: 5 + GameCoreXboxOne: 5 + Lumin: 5 + Nintendo Switch: 5 + PS4: 5 + PS5: 5 + Stadia: 5 + Standalone: 5 + WebGL: 3 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/ProjectSettings/TagManager.asset b/Installer/ProjectSettings/TagManager.asset similarity index 100% rename from ProjectSettings/TagManager.asset rename to Installer/ProjectSettings/TagManager.asset diff --git a/ProjectSettings/TimeManager.asset b/Installer/ProjectSettings/TimeManager.asset similarity index 100% rename from ProjectSettings/TimeManager.asset rename to Installer/ProjectSettings/TimeManager.asset diff --git a/Installer/ProjectSettings/UnityConnectSettings.asset b/Installer/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..fa0b146 --- /dev/null +++ b/Installer/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,34 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 1 + m_Enabled: 0 + m_TestMode: 0 + m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events + m_EventUrl: https://cdp.cloud.unity3d.com/v1/events + m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com + m_Enabled: 0 + m_LogBufferSize: 10 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_TestMode: 0 + m_InitializeOnStartup: 1 + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/Installer/ProjectSettings/VFXManager.asset b/Installer/ProjectSettings/VFXManager.asset new file mode 100644 index 0000000..3a95c98 --- /dev/null +++ b/Installer/ProjectSettings/VFXManager.asset @@ -0,0 +1,12 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_StripUpdateShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 diff --git a/ProjectSettings/XRSettings.asset b/Installer/ProjectSettings/XRSettings.asset similarity index 100% rename from ProjectSettings/XRSettings.asset rename to Installer/ProjectSettings/XRSettings.asset diff --git a/LICENSE b/LICENSE index b6e71a8..4e88c79 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Ivan Murzak +Copyright (c) 2025 Ivan Murzak Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f6cb8e4..242d654 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,16 @@ -# Unity Image Loader +# [Unity Image Loader](https://github.com/IvanMurzak/Unity-ImageLoader) -![npm](https://img.shields.io/npm/v/extensions.unity.imageloader) [![openupm](https://img.shields.io/npm/v/extensions.unity.imageloader?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/extensions.unity.imageloader/) ![License](https://img.shields.io/github/license/IvanMurzak/Unity-ImageLoader) [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) +[![OpenUPM](https://img.shields.io/npm/v/extensions.unity.imageloader?label=OpenUPM®istry_uri=https://package.openupm.com&labelColor=333A41 'OpenUPM package')](https://openupm.com/packages/extensions.unity.imageloader/) +[![Unity Asset Store](https://img.shields.io/badge/Asset%20Store-View-blue?logo=unity&labelColor=333A41 'Asset Store')](https://u3d.as/3DTj) +[![Unity Editor](https://img.shields.io/badge/Editor-X?style=flat&logo=unity&labelColor=333A41&color=49BC5C 'Unity Editor supported')](https://unity.com/releases/editor/archive) +[![Unity Runtime](https://img.shields.io/badge/Runtime-X?style=flat&logo=unity&labelColor=333A41&color=49BC5C 'Unity Runtime supported')](https://unity.com/releases/editor/archive) +[![r](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg 'Tests Passed')](https://github.com/IvanMurzak/Unity-ImageLoader/actions/workflows/release.yml) -![2019.4.40f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2019.4.40f1_editor.yml?label=2019.4.40f1-Editor) ![2020.3.40f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2020.3.40f1_editor.yml?label=2020.3.40f1-Editor) ![2021.3.45f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2021.3.45f1_editor.yml?label=2021.3.45f1-Editor) ![2022.3.57f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2022.3.57f1_editor.yml?label=2022.3.57f1-Editor) ![2023.1.20f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2023.1.20f1_editor.yml?label=2023.1.20f1-Editor) ![2023.2.20f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2023.2.20f1_editor.yml?label=2023.2.20f1-Editor) ![6000.0.37f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/6000.0.37f1_editor.yml?label=6000.0.37f1-Editor) +[![Stars](https://img.shields.io/github/stars/IvanMurzak/Unity-ImageLoader 'Stars')](https://github.com/IvanMurzak/Unity-ImageLoader/stargazers) +[![License](https://img.shields.io/github/license/IvanMurzak/Unity-ImageLoader?label=License)](https://github.com/IvanMurzak/Unity-ImageLoader/blob/main/LICENSE) +[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) + +![Image Loader Animation](https://github.com/IvanMurza/Unity-ImageLoader/blob/main/docs/img/header.gif 'Image Loading Animation') Async image loader with two caching layers for Unity. It supports loading images from web or local paths and provides memory and disk caching to optimize performance. The package includes features for automatic image setting, cancellation handling, error handling, and lifecycle management. @@ -37,19 +45,35 @@ await ImageLoader.LoadSprite(imageURL).Consume(image); - ✔️ Set into RawImage `ImageLoader.LoadSprite(imageURL).Consume(rawImage);` - ✔️ Set into Material `ImageLoader.LoadSprite(imageURL).Consume("_MainTex", material);` - ✔️ Set into SpriteRenderer `ImageLoader.LoadSprite(imageURL).Consume(spriteRenderer);` -- ✔️ [Set into anything](#cancellation) +- ✔️ [Set into anything](#load-sprite-then-set-into-image) - ✔️ Cancellation `ImageLoader.LoadSprite(imageURL).Cancel();` - ✔️ Cancellation callback `ImageLoader.LoadSprite(imageURL).Cancelled(() => ...);` - ✔️ Error callback `ImageLoader.LoadSprite(imageURL).Failed(exception => ...);` - ✔️ Debug level for logging `ImageLoader.settings.debugLevel = DebugLevel.Error;` - ✔️ Debug level per each task `ImageLoader.LoadSprite(imageURL).SetLogLevel(DebugLevel.Trace);` +### Stability status + +| Unity Version | Editor test | Player test | Build test | +|---------------|-------------|-------------|------------| +| 2019.4.40f1 | ![2019.4.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2019-4-40f1-editmode) | ![2019.4.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2019-4-40f1-playmode) | ![2019.4.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2019-4-40f1-standalone) | +| 2020.3.40f1 | ![2020.3.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2020-3-40f1-editmode) | ![2020.3.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2020-3-40f1-playmode) | ![2020.3.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2020-3-40f1-standalone) | +| 2021.3.45f1 | ![2021.3.45f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2021-3-45f1-editmode) | ![2021.3.45f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2021-3-45f1-playmode) | ![2021.3.45f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2021-3-45f1-standalone) | +| 2022.3.57f1 | ![2022.3.57f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2022-3-57f1-editmode) | ![2022.3.57f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2022-3-57f1-playmode) | ![2022.3.57f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2022-3-57f1-standalone) | +| 2023.1.20f1 | ![2023.1.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-1-20f1-editmode) | ![2023.1.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-1-20f1-playmode) | ![2023.1.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-1-20f1-standalone) | +| 2023.2.20f1 | ![2023.2.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-2-20f1-editmode) | ![2023.2.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-2-20f1-playmode) | ![2023.2.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-2-20f1-standalone) | +| 6000.0.37f1 | ![6000.0.37f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-6000-0-37f1-editmode) | ![6000.0.37f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-6000-0-37f1-playmode) | ![6000.0.37f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-6000-0-37f1-standalone) | + ## Content - [Unity Image Loader](#unity-image-loader) - [Features](#features) + - [Stability status](#stability-status) - [Content](#content) - [Installation](#installation) + - [Option 1 - Installer](#option-1---installer) + - [Option 2 - OpenUPM-CLI](#option-2---openupm-cli) + - [Option 3 - Asset Store](#option-3---asset-store) - [Usage](#usage) - [Events lifecycle](#events-lifecycle) - [Load `Sprite` then set into `Image`](#load-sprite-then-set-into-image) @@ -93,14 +117,28 @@ await ImageLoader.LoadSprite(imageURL).Consume(image); ## Installation -- [Install OpenUPM-CLI](https://github.com/openupm/openupm-cli#installation) -- Open command line in Unity project folder -- Run the command +### Option 1 - Installer + +- **[⬇️ Download Installer](https://github.com/IvanMurzak/Unity-ImageLoader/releases/download/4.1.3/ImageLoader-Installer.unitypackage)** +- **📂 Import installer into Unity project** + > - You may use double click on the file - Unity will open it + > - OR: You may open Unity Editor first, then click on `Assets/Import Package/Custom Package`, then choose the file + +### Option 2 - OpenUPM-CLI -``` CLI +- [⬇️ Install OpenUPM-CLI](https://github.com/openupm/openupm-cli#installation) +- 📟 Open command line in Unity project folder + +```bash openupm add extensions.unity.imageloader ``` +### Option 3 - Asset Store + +- **[▶️ Open Asset Store](https://u3d.as/3DTj)** + +--- + # Usage In the main thread somewhere at the start of the project need to call `ImageLoader.Init();` once to initialize static properties in the right thread. It is required to make in the main thread. Then you can use `ImageLoader` from any thread and at any time. diff --git a/Unity-Package/.gitignore b/Unity-Package/.gitignore new file mode 100644 index 0000000..fa3c1e7 --- /dev/null +++ b/Unity-Package/.gitignore @@ -0,0 +1,80 @@ +# This .gitignore file should be placed at the root of your Unity project directory +# +# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore +# +/[Ll]ibrary/ +/[Tt]emp/ +/[Oo]bj/ +/[Bb]uild/ +/[Bb]uilds/ +/[Ll]ogs/ +/[Mm]emoryCaptures/ + +# Asset meta data should only be ignored when the corresponding asset is also ignored +!/[Aa]ssets/**/*.meta + +# Uncomment this line if you wish to ignore the asset store tools plugin +# /[Aa]ssets/AssetStoreTools* + +# Autogenerated Jetbrains Rider plugin +[Aa]ssets/Plugins/Editor/JetBrains* + +# Visual Studio cache directory +.vs/ + +# Gradle cache directory +.gradle/ + +# Autogenerated VS/MD/Consulo solution and project files +ExportedObj/ +.consulo/ +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db + +# Unity3D generated meta files +*.pidb.meta +*.pdb.meta +*.mdb.meta + +# Unity3D generated file on crash reports +sysinfo.txt + +# Builds +*.apk +*.unitypackage + +# Crashlytics generated file +crashlytics-build.properties + +*/AndroidLogcatSettings.asset +/Assets/StreamingAssets + +# Crashlytics generated file +crashlytics-build.properties + +# ODIN Ignore the auto-generated AOT compatibility dll. +/Assets/Plugins/Sirenix/Assemblies/AOT/* +/Assets/Plugins/Sirenix/Assemblies/AOT** + +# ODIN Ignore all unpacked demos. +/Assets/Plugins/Sirenix/Demos/* + +# ODIN plugin +/Assets/Plugins/Sirenix** + +# Other +UserSettings/Layouts/default-2021.dwlt +UserSettings/Layouts/default-2022.dwlt +UserSettings/Layouts/default-6000.dwlt diff --git a/.vscode/extensions.json b/Unity-Package/.vscode/extensions.json similarity index 100% rename from .vscode/extensions.json rename to Unity-Package/.vscode/extensions.json diff --git a/.vscode/launch.json b/Unity-Package/.vscode/launch.json similarity index 100% rename from .vscode/launch.json rename to Unity-Package/.vscode/launch.json diff --git a/.vscode/settings.json b/Unity-Package/.vscode/settings.json similarity index 96% rename from .vscode/settings.json rename to Unity-Package/.vscode/settings.json index 2c7bb13..fcea5cb 100644 --- a/.vscode/settings.json +++ b/Unity-Package/.vscode/settings.json @@ -65,5 +65,5 @@ "unitask", "WEBGL" ], - "dotnet.defaultSolution": "Unity-ImageLoader.sln" + "dotnet.defaultSolution": "Unity-Package.sln" } \ No newline at end of file diff --git a/.vscode/tasks.json b/Unity-Package/.vscode/tasks.json similarity index 100% rename from .vscode/tasks.json rename to Unity-Package/.vscode/tasks.json diff --git a/Assets/_PackageRoot.meta b/Unity-Package/Assets/root.meta similarity index 100% rename from Assets/_PackageRoot.meta rename to Unity-Package/Assets/root.meta diff --git a/Assets/_PackageRoot/Editor.meta b/Unity-Package/Assets/root/Editor.meta similarity index 100% rename from Assets/_PackageRoot/Editor.meta rename to Unity-Package/Assets/root/Editor.meta diff --git a/Assets/_PackageRoot/Editor/Gizmos.meta b/Unity-Package/Assets/root/Editor/Gizmos.meta similarity index 100% rename from Assets/_PackageRoot/Editor/Gizmos.meta rename to Unity-Package/Assets/root/Editor/Gizmos.meta diff --git a/Assets/_PackageRoot/Documentation~/.gitignore b/Unity-Package/Assets/root/Editor/Gizmos/.gitignore similarity index 100% rename from Assets/_PackageRoot/Documentation~/.gitignore rename to Unity-Package/Assets/root/Editor/Gizmos/.gitignore diff --git a/Assets/_PackageRoot/Editor/Gizmos/icon-cancel.png b/Unity-Package/Assets/root/Editor/Gizmos/icon-cancel.png similarity index 100% rename from Assets/_PackageRoot/Editor/Gizmos/icon-cancel.png rename to Unity-Package/Assets/root/Editor/Gizmos/icon-cancel.png diff --git a/Assets/_PackageRoot/Editor/Gizmos/icon-cancel.png.meta b/Unity-Package/Assets/root/Editor/Gizmos/icon-cancel.png.meta similarity index 100% rename from Assets/_PackageRoot/Editor/Gizmos/icon-cancel.png.meta rename to Unity-Package/Assets/root/Editor/Gizmos/icon-cancel.png.meta diff --git a/Assets/_PackageRoot/Editor/Scripts.meta b/Unity-Package/Assets/root/Editor/Scripts.meta similarity index 100% rename from Assets/_PackageRoot/Editor/Scripts.meta rename to Unity-Package/Assets/root/Editor/Scripts.meta diff --git a/Assets/_PackageRoot/Editor/Gizmos/.gitignore b/Unity-Package/Assets/root/Editor/Scripts/.gitignore similarity index 100% rename from Assets/_PackageRoot/Editor/Gizmos/.gitignore rename to Unity-Package/Assets/root/Editor/Scripts/.gitignore diff --git a/Assets/_PackageRoot/LICENSE b/Unity-Package/Assets/root/LICENSE similarity index 97% rename from Assets/_PackageRoot/LICENSE rename to Unity-Package/Assets/root/LICENSE index b6e71a8..4e88c79 100644 --- a/Assets/_PackageRoot/LICENSE +++ b/Unity-Package/Assets/root/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Ivan Murzak +Copyright (c) 2025 Ivan Murzak Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Assets/_PackageRoot/LICENSE.meta b/Unity-Package/Assets/root/LICENSE.meta similarity index 100% rename from Assets/_PackageRoot/LICENSE.meta rename to Unity-Package/Assets/root/LICENSE.meta diff --git a/Assets/_PackageRoot/Documentation~/README.md b/Unity-Package/Assets/root/README.md similarity index 83% rename from Assets/_PackageRoot/Documentation~/README.md rename to Unity-Package/Assets/root/README.md index f6cb8e4..242d654 100644 --- a/Assets/_PackageRoot/Documentation~/README.md +++ b/Unity-Package/Assets/root/README.md @@ -1,8 +1,16 @@ -# Unity Image Loader +# [Unity Image Loader](https://github.com/IvanMurzak/Unity-ImageLoader) -![npm](https://img.shields.io/npm/v/extensions.unity.imageloader) [![openupm](https://img.shields.io/npm/v/extensions.unity.imageloader?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/extensions.unity.imageloader/) ![License](https://img.shields.io/github/license/IvanMurzak/Unity-ImageLoader) [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) +[![OpenUPM](https://img.shields.io/npm/v/extensions.unity.imageloader?label=OpenUPM®istry_uri=https://package.openupm.com&labelColor=333A41 'OpenUPM package')](https://openupm.com/packages/extensions.unity.imageloader/) +[![Unity Asset Store](https://img.shields.io/badge/Asset%20Store-View-blue?logo=unity&labelColor=333A41 'Asset Store')](https://u3d.as/3DTj) +[![Unity Editor](https://img.shields.io/badge/Editor-X?style=flat&logo=unity&labelColor=333A41&color=49BC5C 'Unity Editor supported')](https://unity.com/releases/editor/archive) +[![Unity Runtime](https://img.shields.io/badge/Runtime-X?style=flat&logo=unity&labelColor=333A41&color=49BC5C 'Unity Runtime supported')](https://unity.com/releases/editor/archive) +[![r](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg 'Tests Passed')](https://github.com/IvanMurzak/Unity-ImageLoader/actions/workflows/release.yml) -![2019.4.40f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2019.4.40f1_editor.yml?label=2019.4.40f1-Editor) ![2020.3.40f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2020.3.40f1_editor.yml?label=2020.3.40f1-Editor) ![2021.3.45f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2021.3.45f1_editor.yml?label=2021.3.45f1-Editor) ![2022.3.57f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2022.3.57f1_editor.yml?label=2022.3.57f1-Editor) ![2023.1.20f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2023.1.20f1_editor.yml?label=2023.1.20f1-Editor) ![2023.2.20f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/2023.2.20f1_editor.yml?label=2023.2.20f1-Editor) ![6000.0.37f1](https://img.shields.io/github/actions/workflow/status/IvanMurzak/Unity-ImageLoader/6000.0.37f1_editor.yml?label=6000.0.37f1-Editor) +[![Stars](https://img.shields.io/github/stars/IvanMurzak/Unity-ImageLoader 'Stars')](https://github.com/IvanMurzak/Unity-ImageLoader/stargazers) +[![License](https://img.shields.io/github/license/IvanMurzak/Unity-ImageLoader?label=License)](https://github.com/IvanMurzak/Unity-ImageLoader/blob/main/LICENSE) +[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) + +![Image Loader Animation](https://github.com/IvanMurza/Unity-ImageLoader/blob/main/docs/img/header.gif 'Image Loading Animation') Async image loader with two caching layers for Unity. It supports loading images from web or local paths and provides memory and disk caching to optimize performance. The package includes features for automatic image setting, cancellation handling, error handling, and lifecycle management. @@ -37,19 +45,35 @@ await ImageLoader.LoadSprite(imageURL).Consume(image); - ✔️ Set into RawImage `ImageLoader.LoadSprite(imageURL).Consume(rawImage);` - ✔️ Set into Material `ImageLoader.LoadSprite(imageURL).Consume("_MainTex", material);` - ✔️ Set into SpriteRenderer `ImageLoader.LoadSprite(imageURL).Consume(spriteRenderer);` -- ✔️ [Set into anything](#cancellation) +- ✔️ [Set into anything](#load-sprite-then-set-into-image) - ✔️ Cancellation `ImageLoader.LoadSprite(imageURL).Cancel();` - ✔️ Cancellation callback `ImageLoader.LoadSprite(imageURL).Cancelled(() => ...);` - ✔️ Error callback `ImageLoader.LoadSprite(imageURL).Failed(exception => ...);` - ✔️ Debug level for logging `ImageLoader.settings.debugLevel = DebugLevel.Error;` - ✔️ Debug level per each task `ImageLoader.LoadSprite(imageURL).SetLogLevel(DebugLevel.Trace);` +### Stability status + +| Unity Version | Editor test | Player test | Build test | +|---------------|-------------|-------------|------------| +| 2019.4.40f1 | ![2019.4.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2019-4-40f1-editmode) | ![2019.4.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2019-4-40f1-playmode) | ![2019.4.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2019-4-40f1-standalone) | +| 2020.3.40f1 | ![2020.3.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2020-3-40f1-editmode) | ![2020.3.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2020-3-40f1-playmode) | ![2020.3.40f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2020-3-40f1-standalone) | +| 2021.3.45f1 | ![2021.3.45f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2021-3-45f1-editmode) | ![2021.3.45f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2021-3-45f1-playmode) | ![2021.3.45f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2021-3-45f1-standalone) | +| 2022.3.57f1 | ![2022.3.57f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2022-3-57f1-editmode) | ![2022.3.57f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2022-3-57f1-playmode) | ![2022.3.57f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2022-3-57f1-standalone) | +| 2023.1.20f1 | ![2023.1.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-1-20f1-editmode) | ![2023.1.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-1-20f1-playmode) | ![2023.1.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-1-20f1-standalone) | +| 2023.2.20f1 | ![2023.2.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-2-20f1-editmode) | ![2023.2.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-2-20f1-playmode) | ![2023.2.20f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-2023-2-20f1-standalone) | +| 6000.0.37f1 | ![6000.0.37f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-6000-0-37f1-editmode) | ![6000.0.37f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-6000-0-37f1-playmode) | ![6000.0.37f1](https://github.com/IvanMurzak/Unity-ImageLoader/workflows/release/badge.svg?job=test-unity-6000-0-37f1-standalone) | + ## Content - [Unity Image Loader](#unity-image-loader) - [Features](#features) + - [Stability status](#stability-status) - [Content](#content) - [Installation](#installation) + - [Option 1 - Installer](#option-1---installer) + - [Option 2 - OpenUPM-CLI](#option-2---openupm-cli) + - [Option 3 - Asset Store](#option-3---asset-store) - [Usage](#usage) - [Events lifecycle](#events-lifecycle) - [Load `Sprite` then set into `Image`](#load-sprite-then-set-into-image) @@ -93,14 +117,28 @@ await ImageLoader.LoadSprite(imageURL).Consume(image); ## Installation -- [Install OpenUPM-CLI](https://github.com/openupm/openupm-cli#installation) -- Open command line in Unity project folder -- Run the command +### Option 1 - Installer + +- **[⬇️ Download Installer](https://github.com/IvanMurzak/Unity-ImageLoader/releases/download/4.1.3/ImageLoader-Installer.unitypackage)** +- **📂 Import installer into Unity project** + > - You may use double click on the file - Unity will open it + > - OR: You may open Unity Editor first, then click on `Assets/Import Package/Custom Package`, then choose the file + +### Option 2 - OpenUPM-CLI -``` CLI +- [⬇️ Install OpenUPM-CLI](https://github.com/openupm/openupm-cli#installation) +- 📟 Open command line in Unity project folder + +```bash openupm add extensions.unity.imageloader ``` +### Option 3 - Asset Store + +- **[▶️ Open Asset Store](https://u3d.as/3DTj)** + +--- + # Usage In the main thread somewhere at the start of the project need to call `ImageLoader.Init();` once to initialize static properties in the right thread. It is required to make in the main thread. Then you can use `ImageLoader` from any thread and at any time. diff --git a/Assets/_PackageRoot/README.md.meta b/Unity-Package/Assets/root/README.md.meta similarity index 100% rename from Assets/_PackageRoot/README.md.meta rename to Unity-Package/Assets/root/README.md.meta diff --git a/Assets/_PackageRoot/Runtime.meta b/Unity-Package/Assets/root/Runtime.meta similarity index 100% rename from Assets/_PackageRoot/Runtime.meta rename to Unity-Package/Assets/root/Runtime.meta diff --git a/Assets/_PackageRoot/Editor/Scripts/.gitignore b/Unity-Package/Assets/root/Runtime/.gitignore similarity index 100% rename from Assets/_PackageRoot/Editor/Scripts/.gitignore rename to Unity-Package/Assets/root/Runtime/.gitignore diff --git a/Assets/_PackageRoot/Runtime/Extensions.Unity.ImageLoader.asmdef b/Unity-Package/Assets/root/Runtime/Extensions.Unity.ImageLoader.asmdef similarity index 100% rename from Assets/_PackageRoot/Runtime/Extensions.Unity.ImageLoader.asmdef rename to Unity-Package/Assets/root/Runtime/Extensions.Unity.ImageLoader.asmdef diff --git a/Assets/_PackageRoot/Runtime/Extensions.Unity.ImageLoader.asmdef.meta b/Unity-Package/Assets/root/Runtime/Extensions.Unity.ImageLoader.asmdef.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Extensions.Unity.ImageLoader.asmdef.meta rename to Unity-Package/Assets/root/Runtime/Extensions.Unity.ImageLoader.asmdef.meta diff --git a/Assets/_PackageRoot/Runtime/Future.meta b/Unity-Package/Assets/root/Runtime/Future.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future.meta rename to Unity-Package/Assets/root/Runtime/Future.meta diff --git a/Assets/_PackageRoot/Runtime/Future/CancelComponent.meta b/Unity-Package/Assets/root/Runtime/Future/CancelComponent.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/CancelComponent.meta rename to Unity-Package/Assets/root/Runtime/Future/CancelComponent.meta diff --git a/Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTrigger.cs b/Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTrigger.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTrigger.cs rename to Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTrigger.cs diff --git a/Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTrigger.cs.meta b/Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTrigger.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTrigger.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTrigger.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnDestroy.cs b/Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnDestroy.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnDestroy.cs rename to Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnDestroy.cs diff --git a/Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnDestroy.cs.meta b/Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnDestroy.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnDestroy.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnDestroy.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnDisable.cs b/Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnDisable.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnDisable.cs rename to Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnDisable.cs diff --git a/Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnDisable.cs.meta b/Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnDisable.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnDisable.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnDisable.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnEnable.cs b/Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnEnable.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnEnable.cs rename to Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnEnable.cs diff --git a/Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnEnable.cs.meta b/Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnEnable.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/CancelComponent/ComponentTriggerOnEnable.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/CancelComponent/ComponentTriggerOnEnable.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions.meta b/Unity-Package/Assets/root/Runtime/Future/Extensions.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions.meta rename to Unity-Package/Assets/root/Runtime/Future/Extensions.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.CancelOn.cs b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.CancelOn.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.CancelOn.cs rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.CancelOn.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.CancelOn.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.CancelOn.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.CancelOn.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.CancelOn.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Consume.cs b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Consume.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Consume.cs rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Consume.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Consume.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Consume.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Consume.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Consume.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.ConsumeRef.cs b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.ConsumeRef.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.ConsumeRef.cs rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.ConsumeRef.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.ConsumeRef.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.ConsumeRef.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.ConsumeRef.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.ConsumeRef.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.Color.cs b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.Color.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.Color.cs rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.Color.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.Color.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.Color.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.Color.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.Color.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.ColorHex.cs b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.ColorHex.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.ColorHex.cs rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.ColorHex.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.ColorHex.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.ColorHex.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.ColorHex.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.ColorHex.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.cs b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.cs rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Placeholder.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Placeholder.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Register.cs b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Register.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Register.cs rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Register.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Register.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Register.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Register.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Register.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Timeout.cs b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Timeout.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Timeout.cs rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Timeout.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Timeout.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Timeout.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Extensions/FutureEx.Timeout.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Extensions/FutureEx.Timeout.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.API.Set.cs b/Unity-Package/Assets/root/Runtime/Future/Future.API.Set.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.API.Set.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.API.Set.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.API.Set.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.API.Set.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.API.Set.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.API.Set.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.API.cs b/Unity-Package/Assets/root/Runtime/Future/Future.API.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.API.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.API.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.API.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.API.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.API.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.API.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Empty.cs b/Unity-Package/Assets/root/Runtime/Future/Future.Empty.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Empty.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.Empty.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Empty.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.Empty.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Empty.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.Empty.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.DiskCache.Static.cs b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.DiskCache.Static.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.DiskCache.Static.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.DiskCache.Static.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.DiskCache.Static.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.DiskCache.Static.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.DiskCache.Static.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.DiskCache.Static.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.DiskCache.cs b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.DiskCache.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.DiskCache.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.DiskCache.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.DiskCache.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.DiskCache.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.DiskCache.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.DiskCache.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.List.cs b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.List.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.List.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.List.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.List.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.List.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.List.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.List.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.MemoryCache.Static.cs b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.MemoryCache.Static.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.MemoryCache.Static.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.MemoryCache.Static.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.MemoryCache.Static.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.MemoryCache.Static.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.MemoryCache.Static.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.MemoryCache.Static.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.MemoryCache.cs b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.MemoryCache.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.MemoryCache.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.MemoryCache.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.MemoryCache.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.MemoryCache.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.MemoryCache.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.MemoryCache.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.cs b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Loading.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.Loading.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Loading.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.Loading.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Placeholder.cs b/Unity-Package/Assets/root/Runtime/Future/Future.Placeholder.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Placeholder.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.Placeholder.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.Placeholder.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.Placeholder.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.Placeholder.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.Placeholder.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.WebRequest.cs b/Unity-Package/Assets/root/Runtime/Future/Future.WebRequest.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.WebRequest.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.WebRequest.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.WebRequest.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.WebRequest.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.WebRequest.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.WebRequest.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Future.cs b/Unity-Package/Assets/root/Runtime/Future/Future.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.cs rename to Unity-Package/Assets/root/Runtime/Future/Future.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Future.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Future.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Future.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Future.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/IFuture.cs b/Unity-Package/Assets/root/Runtime/Future/IFuture.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/IFuture.cs rename to Unity-Package/Assets/root/Runtime/Future/IFuture.cs diff --git a/Assets/_PackageRoot/Runtime/Future/IFuture.cs.meta b/Unity-Package/Assets/root/Runtime/Future/IFuture.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/IFuture.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/IFuture.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Types.meta b/Unity-Package/Assets/root/Runtime/Future/Types.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Types.meta rename to Unity-Package/Assets/root/Runtime/Future/Types.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Types/FutureEmpty.cs b/Unity-Package/Assets/root/Runtime/Future/Types/FutureEmpty.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Types/FutureEmpty.cs rename to Unity-Package/Assets/root/Runtime/Future/Types/FutureEmpty.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Types/FutureEmpty.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Types/FutureEmpty.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Types/FutureEmpty.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Types/FutureEmpty.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Types/FutureReference.cs b/Unity-Package/Assets/root/Runtime/Future/Types/FutureReference.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Types/FutureReference.cs rename to Unity-Package/Assets/root/Runtime/Future/Types/FutureReference.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Types/FutureReference.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Types/FutureReference.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Types/FutureReference.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Types/FutureReference.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Types/FutureSprite.cs b/Unity-Package/Assets/root/Runtime/Future/Types/FutureSprite.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Types/FutureSprite.cs rename to Unity-Package/Assets/root/Runtime/Future/Types/FutureSprite.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Types/FutureSprite.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Types/FutureSprite.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Types/FutureSprite.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Types/FutureSprite.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Types/FutureTexture.cs b/Unity-Package/Assets/root/Runtime/Future/Types/FutureTexture.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Types/FutureTexture.cs rename to Unity-Package/Assets/root/Runtime/Future/Types/FutureTexture.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Types/FutureTexture.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Types/FutureTexture.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Types/FutureTexture.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Types/FutureTexture.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Utils.meta b/Unity-Package/Assets/root/Runtime/Future/Utils.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Utils.meta rename to Unity-Package/Assets/root/Runtime/Future/Utils.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Utils/FutureAwaiter.cs b/Unity-Package/Assets/root/Runtime/Future/Utils/FutureAwaiter.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Utils/FutureAwaiter.cs rename to Unity-Package/Assets/root/Runtime/Future/Utils/FutureAwaiter.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Utils/FutureAwaiter.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Utils/FutureAwaiter.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Utils/FutureAwaiter.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Utils/FutureAwaiter.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Utils/FutureEnums.cs b/Unity-Package/Assets/root/Runtime/Future/Utils/FutureEnums.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Utils/FutureEnums.cs rename to Unity-Package/Assets/root/Runtime/Future/Utils/FutureEnums.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Utils/FutureEnums.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Utils/FutureEnums.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Utils/FutureEnums.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Utils/FutureEnums.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Future/Utils/FutureEnumsEx.cs b/Unity-Package/Assets/root/Runtime/Future/Utils/FutureEnumsEx.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Utils/FutureEnumsEx.cs rename to Unity-Package/Assets/root/Runtime/Future/Utils/FutureEnumsEx.cs diff --git a/Assets/_PackageRoot/Runtime/Future/Utils/FutureEnumsEx.cs.meta b/Unity-Package/Assets/root/Runtime/Future/Utils/FutureEnumsEx.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Future/Utils/FutureEnumsEx.cs.meta rename to Unity-Package/Assets/root/Runtime/Future/Utils/FutureEnumsEx.cs.meta diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.Cache.Disk.cs b/Unity-Package/Assets/root/Runtime/ImageLoader.Cache.Disk.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.Cache.Disk.cs rename to Unity-Package/Assets/root/Runtime/ImageLoader.Cache.Disk.cs diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.Cache.Disk.cs.meta b/Unity-Package/Assets/root/Runtime/ImageLoader.Cache.Disk.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.Cache.Disk.cs.meta rename to Unity-Package/Assets/root/Runtime/ImageLoader.Cache.Disk.cs.meta diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.Cache.Memory.cs b/Unity-Package/Assets/root/Runtime/ImageLoader.Cache.Memory.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.Cache.Memory.cs rename to Unity-Package/Assets/root/Runtime/ImageLoader.Cache.Memory.cs diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.Cache.Memory.cs.meta b/Unity-Package/Assets/root/Runtime/ImageLoader.Cache.Memory.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.Cache.Memory.cs.meta rename to Unity-Package/Assets/root/Runtime/ImageLoader.Cache.Memory.cs.meta diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.LoadSprite.cs b/Unity-Package/Assets/root/Runtime/ImageLoader.LoadSprite.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.LoadSprite.cs rename to Unity-Package/Assets/root/Runtime/ImageLoader.LoadSprite.cs diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.LoadSprite.cs.meta b/Unity-Package/Assets/root/Runtime/ImageLoader.LoadSprite.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.LoadSprite.cs.meta rename to Unity-Package/Assets/root/Runtime/ImageLoader.LoadSprite.cs.meta diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.LoadSpriteRef.cs b/Unity-Package/Assets/root/Runtime/ImageLoader.LoadSpriteRef.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.LoadSpriteRef.cs rename to Unity-Package/Assets/root/Runtime/ImageLoader.LoadSpriteRef.cs diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.LoadSpriteRef.cs.meta b/Unity-Package/Assets/root/Runtime/ImageLoader.LoadSpriteRef.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.LoadSpriteRef.cs.meta rename to Unity-Package/Assets/root/Runtime/ImageLoader.LoadSpriteRef.cs.meta diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.LoadTexture.cs b/Unity-Package/Assets/root/Runtime/ImageLoader.LoadTexture.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.LoadTexture.cs rename to Unity-Package/Assets/root/Runtime/ImageLoader.LoadTexture.cs diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.LoadTexture.cs.meta b/Unity-Package/Assets/root/Runtime/ImageLoader.LoadTexture.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.LoadTexture.cs.meta rename to Unity-Package/Assets/root/Runtime/ImageLoader.LoadTexture.cs.meta diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.LoadTextureRef.cs b/Unity-Package/Assets/root/Runtime/ImageLoader.LoadTextureRef.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.LoadTextureRef.cs rename to Unity-Package/Assets/root/Runtime/ImageLoader.LoadTextureRef.cs diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.LoadTextureRef.cs.meta b/Unity-Package/Assets/root/Runtime/ImageLoader.LoadTextureRef.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.LoadTextureRef.cs.meta rename to Unity-Package/Assets/root/Runtime/ImageLoader.LoadTextureRef.cs.meta diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.Settings.cs b/Unity-Package/Assets/root/Runtime/ImageLoader.Settings.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.Settings.cs rename to Unity-Package/Assets/root/Runtime/ImageLoader.Settings.cs diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.Settings.cs.meta b/Unity-Package/Assets/root/Runtime/ImageLoader.Settings.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.Settings.cs.meta rename to Unity-Package/Assets/root/Runtime/ImageLoader.Settings.cs.meta diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.cs b/Unity-Package/Assets/root/Runtime/ImageLoader.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.cs rename to Unity-Package/Assets/root/Runtime/ImageLoader.cs diff --git a/Assets/_PackageRoot/Runtime/ImageLoader.cs.meta b/Unity-Package/Assets/root/Runtime/ImageLoader.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/ImageLoader.cs.meta rename to Unity-Package/Assets/root/Runtime/ImageLoader.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Reference.meta b/Unity-Package/Assets/root/Runtime/Reference.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Reference.meta rename to Unity-Package/Assets/root/Runtime/Reference.meta diff --git a/Assets/_PackageRoot/Runtime/Reference/Reference.Counter.cs b/Unity-Package/Assets/root/Runtime/Reference/Reference.Counter.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Reference/Reference.Counter.cs rename to Unity-Package/Assets/root/Runtime/Reference/Reference.Counter.cs diff --git a/Assets/_PackageRoot/Runtime/Reference/Reference.Counter.cs.meta b/Unity-Package/Assets/root/Runtime/Reference/Reference.Counter.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Reference/Reference.Counter.cs.meta rename to Unity-Package/Assets/root/Runtime/Reference/Reference.Counter.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Reference/Reference.DisposeOn.cs b/Unity-Package/Assets/root/Runtime/Reference/Reference.DisposeOn.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Reference/Reference.DisposeOn.cs rename to Unity-Package/Assets/root/Runtime/Reference/Reference.DisposeOn.cs diff --git a/Assets/_PackageRoot/Runtime/Reference/Reference.DisposeOn.cs.meta b/Unity-Package/Assets/root/Runtime/Reference/Reference.DisposeOn.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Reference/Reference.DisposeOn.cs.meta rename to Unity-Package/Assets/root/Runtime/Reference/Reference.DisposeOn.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Reference/Reference.cs b/Unity-Package/Assets/root/Runtime/Reference/Reference.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Reference/Reference.cs rename to Unity-Package/Assets/root/Runtime/Reference/Reference.cs diff --git a/Assets/_PackageRoot/Runtime/Reference/Reference.cs.meta b/Unity-Package/Assets/root/Runtime/Reference/Reference.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Reference/Reference.cs.meta rename to Unity-Package/Assets/root/Runtime/Reference/Reference.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Utils.meta b/Unity-Package/Assets/root/Runtime/Utils.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Utils.meta rename to Unity-Package/Assets/root/Runtime/Utils.meta diff --git a/Assets/_PackageRoot/Runtime/Utils/ColorExtensions.cs b/Unity-Package/Assets/root/Runtime/Utils/ColorExtensions.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Utils/ColorExtensions.cs rename to Unity-Package/Assets/root/Runtime/Utils/ColorExtensions.cs diff --git a/Assets/_PackageRoot/Runtime/Utils/ColorExtensions.cs.meta b/Unity-Package/Assets/root/Runtime/Utils/ColorExtensions.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Utils/ColorExtensions.cs.meta rename to Unity-Package/Assets/root/Runtime/Utils/ColorExtensions.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Utils/InternalExtensions.cs b/Unity-Package/Assets/root/Runtime/Utils/InternalExtensions.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Utils/InternalExtensions.cs rename to Unity-Package/Assets/root/Runtime/Utils/InternalExtensions.cs diff --git a/Assets/_PackageRoot/Runtime/Utils/InternalExtensions.cs.meta b/Unity-Package/Assets/root/Runtime/Utils/InternalExtensions.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Utils/InternalExtensions.cs.meta rename to Unity-Package/Assets/root/Runtime/Utils/InternalExtensions.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Utils/LimitedConcurrencyLevelTaskScheduler.cs b/Unity-Package/Assets/root/Runtime/Utils/LimitedConcurrencyLevelTaskScheduler.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Utils/LimitedConcurrencyLevelTaskScheduler.cs rename to Unity-Package/Assets/root/Runtime/Utils/LimitedConcurrencyLevelTaskScheduler.cs diff --git a/Assets/_PackageRoot/Runtime/Utils/LimitedConcurrencyLevelTaskScheduler.cs.meta b/Unity-Package/Assets/root/Runtime/Utils/LimitedConcurrencyLevelTaskScheduler.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Utils/LimitedConcurrencyLevelTaskScheduler.cs.meta rename to Unity-Package/Assets/root/Runtime/Utils/LimitedConcurrencyLevelTaskScheduler.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Utils/Safe.cs b/Unity-Package/Assets/root/Runtime/Utils/Safe.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Utils/Safe.cs rename to Unity-Package/Assets/root/Runtime/Utils/Safe.cs diff --git a/Assets/_PackageRoot/Runtime/Utils/Safe.cs.meta b/Unity-Package/Assets/root/Runtime/Utils/Safe.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Utils/Safe.cs.meta rename to Unity-Package/Assets/root/Runtime/Utils/Safe.cs.meta diff --git a/Assets/_PackageRoot/Runtime/Utils/WeakAction.cs b/Unity-Package/Assets/root/Runtime/Utils/WeakAction.cs similarity index 100% rename from Assets/_PackageRoot/Runtime/Utils/WeakAction.cs rename to Unity-Package/Assets/root/Runtime/Utils/WeakAction.cs diff --git a/Assets/_PackageRoot/Runtime/Utils/WeakAction.cs.meta b/Unity-Package/Assets/root/Runtime/Utils/WeakAction.cs.meta similarity index 100% rename from Assets/_PackageRoot/Runtime/Utils/WeakAction.cs.meta rename to Unity-Package/Assets/root/Runtime/Utils/WeakAction.cs.meta diff --git a/Assets/_PackageRoot/Samples.meta b/Unity-Package/Assets/root/Samples.meta similarity index 100% rename from Assets/_PackageRoot/Samples.meta rename to Unity-Package/Assets/root/Samples.meta diff --git a/Assets/_PackageRoot/Runtime/.gitignore b/Unity-Package/Assets/root/Samples/.gitignore similarity index 100% rename from Assets/_PackageRoot/Runtime/.gitignore rename to Unity-Package/Assets/root/Samples/.gitignore diff --git a/Assets/_PackageRoot/Samples/Extensions.Unity.ImageLoader.Samples.asmdef b/Unity-Package/Assets/root/Samples/Extensions.Unity.ImageLoader.Samples.asmdef similarity index 100% rename from Assets/_PackageRoot/Samples/Extensions.Unity.ImageLoader.Samples.asmdef rename to Unity-Package/Assets/root/Samples/Extensions.Unity.ImageLoader.Samples.asmdef diff --git a/Assets/_PackageRoot/Samples/Extensions.Unity.ImageLoader.Samples.asmdef.meta b/Unity-Package/Assets/root/Samples/Extensions.Unity.ImageLoader.Samples.asmdef.meta similarity index 100% rename from Assets/_PackageRoot/Samples/Extensions.Unity.ImageLoader.Samples.asmdef.meta rename to Unity-Package/Assets/root/Samples/Extensions.Unity.ImageLoader.Samples.asmdef.meta diff --git a/Assets/_PackageRoot/Samples/SampleAwaitAndForget.cs b/Unity-Package/Assets/root/Samples/SampleAwaitAndForget.cs similarity index 100% rename from Assets/_PackageRoot/Samples/SampleAwaitAndForget.cs rename to Unity-Package/Assets/root/Samples/SampleAwaitAndForget.cs diff --git a/Assets/_PackageRoot/Samples/SampleAwaitAndForget.cs.meta b/Unity-Package/Assets/root/Samples/SampleAwaitAndForget.cs.meta similarity index 100% rename from Assets/_PackageRoot/Samples/SampleAwaitAndForget.cs.meta rename to Unity-Package/Assets/root/Samples/SampleAwaitAndForget.cs.meta diff --git a/Assets/_PackageRoot/Samples/SampleCache.cs b/Unity-Package/Assets/root/Samples/SampleCache.cs similarity index 100% rename from Assets/_PackageRoot/Samples/SampleCache.cs rename to Unity-Package/Assets/root/Samples/SampleCache.cs diff --git a/Assets/_PackageRoot/Samples/SampleCache.cs.meta b/Unity-Package/Assets/root/Samples/SampleCache.cs.meta similarity index 100% rename from Assets/_PackageRoot/Samples/SampleCache.cs.meta rename to Unity-Package/Assets/root/Samples/SampleCache.cs.meta diff --git a/Assets/_PackageRoot/Samples/SampleCancellation.cs b/Unity-Package/Assets/root/Samples/SampleCancellation.cs similarity index 100% rename from Assets/_PackageRoot/Samples/SampleCancellation.cs rename to Unity-Package/Assets/root/Samples/SampleCancellation.cs diff --git a/Assets/_PackageRoot/Samples/SampleCancellation.cs.meta b/Unity-Package/Assets/root/Samples/SampleCancellation.cs.meta similarity index 100% rename from Assets/_PackageRoot/Samples/SampleCancellation.cs.meta rename to Unity-Package/Assets/root/Samples/SampleCancellation.cs.meta diff --git a/Assets/_PackageRoot/Samples/SampleErrorHandle.cs b/Unity-Package/Assets/root/Samples/SampleErrorHandle.cs similarity index 100% rename from Assets/_PackageRoot/Samples/SampleErrorHandle.cs rename to Unity-Package/Assets/root/Samples/SampleErrorHandle.cs diff --git a/Assets/_PackageRoot/Samples/SampleErrorHandle.cs.meta b/Unity-Package/Assets/root/Samples/SampleErrorHandle.cs.meta similarity index 100% rename from Assets/_PackageRoot/Samples/SampleErrorHandle.cs.meta rename to Unity-Package/Assets/root/Samples/SampleErrorHandle.cs.meta diff --git a/Assets/_PackageRoot/Samples/SampleLifecycle.cs b/Unity-Package/Assets/root/Samples/SampleLifecycle.cs similarity index 100% rename from Assets/_PackageRoot/Samples/SampleLifecycle.cs rename to Unity-Package/Assets/root/Samples/SampleLifecycle.cs diff --git a/Assets/_PackageRoot/Samples/SampleLifecycle.cs.meta b/Unity-Package/Assets/root/Samples/SampleLifecycle.cs.meta similarity index 100% rename from Assets/_PackageRoot/Samples/SampleLifecycle.cs.meta rename to Unity-Package/Assets/root/Samples/SampleLifecycle.cs.meta diff --git a/Assets/_PackageRoot/Samples/SampleLoadSpriteConsumeImage.cs b/Unity-Package/Assets/root/Samples/SampleLoadSpriteConsumeImage.cs similarity index 100% rename from Assets/_PackageRoot/Samples/SampleLoadSpriteConsumeImage.cs rename to Unity-Package/Assets/root/Samples/SampleLoadSpriteConsumeImage.cs diff --git a/Assets/_PackageRoot/Samples/SampleLoadSpriteConsumeImage.cs.meta b/Unity-Package/Assets/root/Samples/SampleLoadSpriteConsumeImage.cs.meta similarity index 100% rename from Assets/_PackageRoot/Samples/SampleLoadSpriteConsumeImage.cs.meta rename to Unity-Package/Assets/root/Samples/SampleLoadSpriteConsumeImage.cs.meta diff --git a/Assets/_PackageRoot/Samples/SampleLoadSpriteConsumeIntoMultipleImages.cs b/Unity-Package/Assets/root/Samples/SampleLoadSpriteConsumeIntoMultipleImages.cs similarity index 100% rename from Assets/_PackageRoot/Samples/SampleLoadSpriteConsumeIntoMultipleImages.cs rename to Unity-Package/Assets/root/Samples/SampleLoadSpriteConsumeIntoMultipleImages.cs diff --git a/Assets/_PackageRoot/Samples/SampleLoadSpriteConsumeIntoMultipleImages.cs.meta b/Unity-Package/Assets/root/Samples/SampleLoadSpriteConsumeIntoMultipleImages.cs.meta similarity index 100% rename from Assets/_PackageRoot/Samples/SampleLoadSpriteConsumeIntoMultipleImages.cs.meta rename to Unity-Package/Assets/root/Samples/SampleLoadSpriteConsumeIntoMultipleImages.cs.meta diff --git a/Assets/_PackageRoot/Samples/SampleLoadTextureConsumeMaterial.cs b/Unity-Package/Assets/root/Samples/SampleLoadTextureConsumeMaterial.cs similarity index 100% rename from Assets/_PackageRoot/Samples/SampleLoadTextureConsumeMaterial.cs rename to Unity-Package/Assets/root/Samples/SampleLoadTextureConsumeMaterial.cs diff --git a/Assets/_PackageRoot/Samples/SampleLoadTextureConsumeMaterial.cs.meta b/Unity-Package/Assets/root/Samples/SampleLoadTextureConsumeMaterial.cs.meta similarity index 100% rename from Assets/_PackageRoot/Samples/SampleLoadTextureConsumeMaterial.cs.meta rename to Unity-Package/Assets/root/Samples/SampleLoadTextureConsumeMaterial.cs.meta diff --git a/Assets/_PackageRoot/Samples/SamplePlaceholder.cs b/Unity-Package/Assets/root/Samples/SamplePlaceholder.cs similarity index 100% rename from Assets/_PackageRoot/Samples/SamplePlaceholder.cs rename to Unity-Package/Assets/root/Samples/SamplePlaceholder.cs diff --git a/Assets/_PackageRoot/Samples/SamplePlaceholder.cs.meta b/Unity-Package/Assets/root/Samples/SamplePlaceholder.cs.meta similarity index 100% rename from Assets/_PackageRoot/Samples/SamplePlaceholder.cs.meta rename to Unity-Package/Assets/root/Samples/SamplePlaceholder.cs.meta diff --git a/Assets/_PackageRoot/Samples/SampleReferences.cs b/Unity-Package/Assets/root/Samples/SampleReferences.cs similarity index 100% rename from Assets/_PackageRoot/Samples/SampleReferences.cs rename to Unity-Package/Assets/root/Samples/SampleReferences.cs diff --git a/Assets/_PackageRoot/Samples/SampleReferences.cs.meta b/Unity-Package/Assets/root/Samples/SampleReferences.cs.meta similarity index 100% rename from Assets/_PackageRoot/Samples/SampleReferences.cs.meta rename to Unity-Package/Assets/root/Samples/SampleReferences.cs.meta diff --git a/Assets/_PackageRoot/Samples/SampleTimeout.cs b/Unity-Package/Assets/root/Samples/SampleTimeout.cs similarity index 100% rename from Assets/_PackageRoot/Samples/SampleTimeout.cs rename to Unity-Package/Assets/root/Samples/SampleTimeout.cs diff --git a/Assets/_PackageRoot/Samples/SampleTimeout.cs.meta b/Unity-Package/Assets/root/Samples/SampleTimeout.cs.meta similarity index 100% rename from Assets/_PackageRoot/Samples/SampleTimeout.cs.meta rename to Unity-Package/Assets/root/Samples/SampleTimeout.cs.meta diff --git a/Assets/_PackageRoot/Tests.meta b/Unity-Package/Assets/root/Tests.meta similarity index 100% rename from Assets/_PackageRoot/Tests.meta rename to Unity-Package/Assets/root/Tests.meta diff --git a/Assets/_PackageRoot/Tests/Base.meta b/Unity-Package/Assets/root/Tests/Base.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base.meta rename to Unity-Package/Assets/root/Tests/Base.meta diff --git a/Assets/_PackageRoot/Tests/Base/Extensions.Unity.ImageLoader.Tests.asmdef b/Unity-Package/Assets/root/Tests/Base/Extensions.Unity.ImageLoader.Tests.asmdef similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Extensions.Unity.ImageLoader.Tests.asmdef rename to Unity-Package/Assets/root/Tests/Base/Extensions.Unity.ImageLoader.Tests.asmdef diff --git a/Assets/_PackageRoot/Tests/Base/Extensions.Unity.ImageLoader.Tests.asmdef.meta b/Unity-Package/Assets/root/Tests/Base/Extensions.Unity.ImageLoader.Tests.asmdef.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Extensions.Unity.ImageLoader.Tests.asmdef.meta rename to Unity-Package/Assets/root/Tests/Base/Extensions.Unity.ImageLoader.Tests.asmdef.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils.meta b/Unity-Package/Assets/root/Tests/Base/Utils.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils.meta rename to Unity-Package/Assets/root/Tests/Base/Utils.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/EventData.cs b/Unity-Package/Assets/root/Tests/Base/Utils/EventData.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/EventData.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/EventData.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/EventData.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/EventData.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/EventData.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/EventData.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/EventName.cs b/Unity-Package/Assets/root/Tests/Base/Utils/EventName.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/EventName.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/EventName.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/EventName.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/EventName.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/EventName.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/EventName.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FakeConsumer.cs b/Unity-Package/Assets/root/Tests/Base/Utils/FakeConsumer.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FakeConsumer.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/FakeConsumer.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FakeConsumer.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/FakeConsumer.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FakeConsumer.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/FakeConsumer.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Implementation.IFuture.cs b/Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Implementation.IFuture.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Implementation.IFuture.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Implementation.IFuture.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Implementation.IFuture.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Implementation.IFuture.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Implementation.IFuture.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Implementation.IFuture.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Implementation.IFutureInternal.cs b/Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Implementation.IFutureInternal.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Implementation.IFutureInternal.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Implementation.IFutureInternal.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Implementation.IFutureInternal.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Implementation.IFutureInternal.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Implementation.IFutureInternal.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Implementation.IFutureInternal.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Subscription.cs b/Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Subscription.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Subscription.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Subscription.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Subscription.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Subscription.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.Subscription.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.Subscription.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.cs b/Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FakeFuture.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/FakeFuture.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FutureEx.cs b/Unity-Package/Assets/root/Tests/Base/Utils/FutureEx.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FutureEx.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/FutureEx.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FutureEx.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/FutureEx.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FutureEx.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/FutureEx.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FutureListener.Assert.cs b/Unity-Package/Assets/root/Tests/Base/Utils/FutureListener.Assert.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FutureListener.Assert.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/FutureListener.Assert.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FutureListener.Assert.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/FutureListener.Assert.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FutureListener.Assert.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/FutureListener.Assert.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FutureListener.cs b/Unity-Package/Assets/root/Tests/Base/Utils/FutureListener.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FutureListener.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/FutureListener.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/FutureListener.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/FutureListener.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/FutureListener.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/FutureListener.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/TestUtils.Async.cs b/Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.Async.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/TestUtils.Async.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.Async.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/TestUtils.Async.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.Async.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/TestUtils.Async.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.Async.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/TestUtils.Load.cs b/Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.Load.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/TestUtils.Load.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.Load.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/TestUtils.Load.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.Load.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/TestUtils.Load.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.Load.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/TestUtils.LoadFail.cs b/Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.LoadFail.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/TestUtils.LoadFail.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.LoadFail.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/TestUtils.LoadFail.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.LoadFail.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/TestUtils.LoadFail.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.LoadFail.cs.meta diff --git a/Assets/_PackageRoot/Tests/Base/Utils/TestUtils.cs b/Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/TestUtils.cs rename to Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.cs diff --git a/Assets/_PackageRoot/Tests/Base/Utils/TestUtils.cs.meta b/Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Base/Utils/TestUtils.cs.meta rename to Unity-Package/Assets/root/Tests/Base/Utils/TestUtils.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor.meta b/Unity-Package/Assets/root/Tests/Editor.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor.meta rename to Unity-Package/Assets/root/Tests/Editor.meta diff --git a/Assets/_PackageRoot/Samples/.gitignore b/Unity-Package/Assets/root/Tests/Editor/.gitignore similarity index 100% rename from Assets/_PackageRoot/Samples/.gitignore rename to Unity-Package/Assets/root/Tests/Editor/.gitignore diff --git a/Assets/_PackageRoot/Tests/Editor/Extensions.Unity.ImageLoader.Tests.Editor.asmdef b/Unity-Package/Assets/root/Tests/Editor/Extensions.Unity.ImageLoader.Tests.Editor.asmdef similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/Extensions.Unity.ImageLoader.Tests.Editor.asmdef rename to Unity-Package/Assets/root/Tests/Editor/Extensions.Unity.ImageLoader.Tests.Editor.asmdef diff --git a/Assets/_PackageRoot/Tests/Editor/Extensions.Unity.ImageLoader.Tests.Editor.asmdef.meta b/Unity-Package/Assets/root/Tests/Editor/Extensions.Unity.ImageLoader.Tests.Editor.asmdef.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/Extensions.Unity.ImageLoader.Tests.Editor.asmdef.meta rename to Unity-Package/Assets/root/Tests/Editor/Extensions.Unity.ImageLoader.Tests.Editor.asmdef.meta diff --git a/Assets/_PackageRoot/Tests/Editor/Test.cs b/Unity-Package/Assets/root/Tests/Editor/Test.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/Test.cs rename to Unity-Package/Assets/root/Tests/Editor/Test.cs diff --git a/Assets/_PackageRoot/Tests/Editor/Test.cs.meta b/Unity-Package/Assets/root/Tests/Editor/Test.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/Test.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/Test.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestCache.cs b/Unity-Package/Assets/root/Tests/Editor/TestCache.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestCache.cs rename to Unity-Package/Assets/root/Tests/Editor/TestCache.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestCache.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestCache.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestCache.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestCache.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestConcurrency.cs b/Unity-Package/Assets/root/Tests/Editor/TestConcurrency.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestConcurrency.cs rename to Unity-Package/Assets/root/Tests/Editor/TestConcurrency.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestConcurrency.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestConcurrency.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestConcurrency.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestConcurrency.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.Load.AndCancel.cs b/Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.AndCancel.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.Load.AndCancel.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.AndCancel.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.Load.AndCancel.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.AndCancel.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.Load.AndCancel.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.AndCancel.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.Load.ThenCancel.cs b/Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.ThenCancel.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.Load.ThenCancel.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.ThenCancel.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.Load.ThenCancel.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.ThenCancel.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.Load.ThenCancel.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.ThenCancel.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.Load.cs b/Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.Load.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.Load.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.Load.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.Load.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.LoadFail.cs b/Unity-Package/Assets/root/Tests/Editor/TestFuture.LoadFail.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.LoadFail.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.LoadFail.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.LoadFail.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFuture.LoadFail.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.LoadFail.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.LoadFail.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.Placeholder.cs b/Unity-Package/Assets/root/Tests/Editor/TestFuture.Placeholder.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.Placeholder.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.Placeholder.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.Placeholder.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFuture.Placeholder.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.Placeholder.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.Placeholder.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.cs b/Unity-Package/Assets/root/Tests/Editor/TestFuture.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFuture.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFuture.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFuture.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFuture.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureOrder.cs b/Unity-Package/Assets/root/Tests/Editor/TestFutureOrder.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureOrder.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFutureOrder.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureOrder.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFutureOrder.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureOrder.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFutureOrder.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.AndCancel.cs b/Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.AndCancel.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.AndCancel.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.AndCancel.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.AndCancel.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.AndCancel.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.AndCancel.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.AndCancel.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs b/Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.cs b/Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.Load.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.Load.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.LoadFail.cs b/Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.LoadFail.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.LoadFail.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.LoadFail.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.LoadFail.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.LoadFail.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.LoadFail.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.LoadFail.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.cs b/Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.cs rename to Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestFutureWaitingAnotherFuture.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestFutureWaitingAnotherFuture.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestLoading.Sprite.cs b/Unity-Package/Assets/root/Tests/Editor/TestLoading.Sprite.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestLoading.Sprite.cs rename to Unity-Package/Assets/root/Tests/Editor/TestLoading.Sprite.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestLoading.Sprite.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestLoading.Sprite.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestLoading.Sprite.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestLoading.Sprite.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestLoading.Texture.cs b/Unity-Package/Assets/root/Tests/Editor/TestLoading.Texture.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestLoading.Texture.cs rename to Unity-Package/Assets/root/Tests/Editor/TestLoading.Texture.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestLoading.Texture.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestLoading.Texture.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestLoading.Texture.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestLoading.Texture.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestLoading.cs b/Unity-Package/Assets/root/Tests/Editor/TestLoading.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestLoading.cs rename to Unity-Package/Assets/root/Tests/Editor/TestLoading.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestLoading.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestLoading.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestLoading.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestLoading.cs.meta diff --git a/Assets/_PackageRoot/Tests/Editor/TestReference.cs b/Unity-Package/Assets/root/Tests/Editor/TestReference.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestReference.cs rename to Unity-Package/Assets/root/Tests/Editor/TestReference.cs diff --git a/Assets/_PackageRoot/Tests/Editor/TestReference.cs.meta b/Unity-Package/Assets/root/Tests/Editor/TestReference.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/TestReference.cs.meta rename to Unity-Package/Assets/root/Tests/Editor/TestReference.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime.meta b/Unity-Package/Assets/root/Tests/Runtime.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime.meta rename to Unity-Package/Assets/root/Tests/Runtime.meta diff --git a/Assets/_PackageRoot/Tests/Editor/.gitignore b/Unity-Package/Assets/root/Tests/Runtime/.gitignore similarity index 100% rename from Assets/_PackageRoot/Tests/Editor/.gitignore rename to Unity-Package/Assets/root/Tests/Runtime/.gitignore diff --git a/Assets/_PackageRoot/Tests/Runtime/Extensions.Unity.ImageLoader.Tests.Runtime.asmdef b/Unity-Package/Assets/root/Tests/Runtime/Extensions.Unity.ImageLoader.Tests.Runtime.asmdef similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/Extensions.Unity.ImageLoader.Tests.Runtime.asmdef rename to Unity-Package/Assets/root/Tests/Runtime/Extensions.Unity.ImageLoader.Tests.Runtime.asmdef diff --git a/Assets/_PackageRoot/Tests/Runtime/Extensions.Unity.ImageLoader.Tests.Runtime.asmdef.meta b/Unity-Package/Assets/root/Tests/Runtime/Extensions.Unity.ImageLoader.Tests.Runtime.asmdef.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/Extensions.Unity.ImageLoader.Tests.Runtime.asmdef.meta rename to Unity-Package/Assets/root/Tests/Runtime/Extensions.Unity.ImageLoader.Tests.Runtime.asmdef.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/Test.cs b/Unity-Package/Assets/root/Tests/Runtime/Test.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/Test.cs rename to Unity-Package/Assets/root/Tests/Runtime/Test.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/Test.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/Test.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/Test.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/Test.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestCache.cs b/Unity-Package/Assets/root/Tests/Runtime/TestCache.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestCache.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestCache.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestCache.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestCache.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestCache.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestCache.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestConcurrency.cs b/Unity-Package/Assets/root/Tests/Runtime/TestConcurrency.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestConcurrency.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestConcurrency.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestConcurrency.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestConcurrency.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestConcurrency.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestConcurrency.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.AndCancel.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.AndCancel.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.AndCancel.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.AndCancel.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.AndCancel.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.AndCancel.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.AndCancel.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.AndCancel.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.ThenCancel.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.ThenCancel.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.ThenCancel.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.ThenCancel.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.ThenCancel.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.ThenCancel.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.ThenCancel.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.ThenCancel.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.Load.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.Load.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.LoadFail.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.LoadFail.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.LoadFail.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.LoadFail.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.LoadFail.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.LoadFail.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.LoadFail.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.LoadFail.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.Placeholder.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.Placeholder.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.Placeholder.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.Placeholder.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.Placeholder.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.Placeholder.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.Placeholder.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.Placeholder.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFuture.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFuture.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFuture.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFuture.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureOrder.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFutureOrder.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureOrder.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureOrder.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureOrder.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFutureOrder.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureOrder.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureOrder.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.AndCancel.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.AndCancel.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.AndCancel.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.AndCancel.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.AndCancel.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.AndCancel.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.AndCancel.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.AndCancel.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.ThenCancel.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.Load.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.LoadFail.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.LoadFail.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.LoadFail.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.LoadFail.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.LoadFail.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.LoadFail.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.LoadFail.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.LoadFail.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.cs b/Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestFutureWaitingAnotherFuture.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestFutureWaitingAnotherFuture.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestLoading.Sprite.cs b/Unity-Package/Assets/root/Tests/Runtime/TestLoading.Sprite.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestLoading.Sprite.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestLoading.Sprite.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestLoading.Sprite.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestLoading.Sprite.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestLoading.Sprite.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestLoading.Sprite.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestLoading.Texture.cs b/Unity-Package/Assets/root/Tests/Runtime/TestLoading.Texture.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestLoading.Texture.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestLoading.Texture.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestLoading.Texture.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestLoading.Texture.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestLoading.Texture.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestLoading.Texture.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestLoading.cs b/Unity-Package/Assets/root/Tests/Runtime/TestLoading.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestLoading.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestLoading.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestLoading.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestLoading.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestLoading.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestLoading.cs.meta diff --git a/Assets/_PackageRoot/Tests/Runtime/TestReference.cs b/Unity-Package/Assets/root/Tests/Runtime/TestReference.cs similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestReference.cs rename to Unity-Package/Assets/root/Tests/Runtime/TestReference.cs diff --git a/Assets/_PackageRoot/Tests/Runtime/TestReference.cs.meta b/Unity-Package/Assets/root/Tests/Runtime/TestReference.cs.meta similarity index 100% rename from Assets/_PackageRoot/Tests/Runtime/TestReference.cs.meta rename to Unity-Package/Assets/root/Tests/Runtime/TestReference.cs.meta diff --git a/Unity-Package/Assets/root/package.json b/Unity-Package/Assets/root/package.json new file mode 100644 index 0000000..a680fbe --- /dev/null +++ b/Unity-Package/Assets/root/package.json @@ -0,0 +1,56 @@ +{ + "name": "extensions.unity.imageloader", + "displayName": "Image Loader", + "author": { + "name": "Ivan Murzak", + "email": "ivan.d.murzak@gmail.com", + "url": "https://github.com/IvanMurzak" + }, + "license": "MIT", + "version": "7.0.2", + "unity": "2019.4", + "description": "Asynchronous image loading from remote or local destination. It has two layers of configurable Memory and Disk cache systems.", + "keywords": [ + "image", + "image loader", + "image loading", + "image cache", + "image placeholder", + "async image loading", + "memory", + "disk", + "future", + "async", + "cache", + "promise" + ], + "homepage": "https://github.com/IvanMurzak/Unity-ImageLoader", + "bugs": { + "url": "https://github.com/IvanMurzak/Unity-ImageLoader/issues" + }, + "repository": { + "type": "git", + "url": "git@github.com:IvanMurzak/Unity-ImageLoader.git" + }, + "dependencies": { + "com.unity.test-framework": "1.1.33", + "com.unity.ugui": "1.0.0", + "com.cysharp.unitask": "2.5.10" + }, + "scopedRegistries": [ + { + "name": "package.openupm.com", + "url": "https://package.openupm.com", + "scopes": [ + "com.cysharp.unitask" + ] + } + ], + "samples": [ + { + "displayName": "Full Usage", + "description": "Full usage example of ImageLoader", + "path": "Samples" + } + ] +} \ No newline at end of file diff --git a/Assets/_PackageRoot/package.json.meta b/Unity-Package/Assets/root/package.json.meta similarity index 100% rename from Assets/_PackageRoot/package.json.meta rename to Unity-Package/Assets/root/package.json.meta diff --git a/Commands/NPM--AddUser.bat b/Unity-Package/Commands/NPM--AddUser.bat similarity index 100% rename from Commands/NPM--AddUser.bat rename to Unity-Package/Commands/NPM--AddUser.bat diff --git a/Commands/NPM--CopyREADME.bat b/Unity-Package/Commands/NPM--CopyREADME.bat similarity index 100% rename from Commands/NPM--CopyREADME.bat rename to Unity-Package/Commands/NPM--CopyREADME.bat diff --git a/Commands/NPM--UpdateDependencies.bat b/Unity-Package/Commands/NPM--UpdateDependencies.bat similarity index 100% rename from Commands/NPM--UpdateDependencies.bat rename to Unity-Package/Commands/NPM--UpdateDependencies.bat diff --git a/Commands/NPM--VersionMajor.bat b/Unity-Package/Commands/NPM--VersionMajor.bat similarity index 100% rename from Commands/NPM--VersionMajor.bat rename to Unity-Package/Commands/NPM--VersionMajor.bat diff --git a/Commands/NPM--VersionMinor.bat b/Unity-Package/Commands/NPM--VersionMinor.bat similarity index 100% rename from Commands/NPM--VersionMinor.bat rename to Unity-Package/Commands/NPM--VersionMinor.bat diff --git a/Commands/NPM--VersionPatch.bat b/Unity-Package/Commands/NPM--VersionPatch.bat similarity index 100% rename from Commands/NPM--VersionPatch.bat rename to Unity-Package/Commands/NPM--VersionPatch.bat diff --git a/Commands/[!!!]--GitHub--Release.bat b/Unity-Package/Commands/[!!!]--GitHub--Release.bat similarity index 100% rename from Commands/[!!!]--GitHub--Release.bat rename to Unity-Package/Commands/[!!!]--GitHub--Release.bat diff --git a/Commands/[!!!]--NPM--Publish.bat b/Unity-Package/Commands/[!!!]--NPM--Publish.bat similarity index 100% rename from Commands/[!!!]--NPM--Publish.bat rename to Unity-Package/Commands/[!!!]--NPM--Publish.bat diff --git a/Commands/[!]--GitHub--DraftRelease.bat b/Unity-Package/Commands/[!]--GitHub--DraftRelease.bat similarity index 100% rename from Commands/[!]--GitHub--DraftRelease.bat rename to Unity-Package/Commands/[!]--GitHub--DraftRelease.bat diff --git a/Packages/manifest.json b/Unity-Package/Packages/manifest.json similarity index 88% rename from Packages/manifest.json rename to Unity-Package/Packages/manifest.json index 444bbb0..9b8761e 100644 --- a/Packages/manifest.json +++ b/Unity-Package/Packages/manifest.json @@ -2,9 +2,8 @@ "dependencies": { "com.cysharp.unitask": "2.5.10", "com.unity.ext.nunit": "2.0.5", - "com.unity.ide.visualstudio": "2.0.22", + "com.unity.ide.visualstudio": "2.0.23", "com.unity.test-framework": "1.4.6", - "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", "com.unity.ugui": "1.0.0", "com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.imgui": "1.0.0", diff --git a/Packages/packages-lock.json b/Unity-Package/Packages/packages-lock.json similarity index 82% rename from Packages/packages-lock.json rename to Unity-Package/Packages/packages-lock.json index 9aeac40..19056c8 100644 --- a/Packages/packages-lock.json +++ b/Unity-Package/Packages/packages-lock.json @@ -15,7 +15,7 @@ "url": "https://packages.unity.com" }, "com.unity.ide.visualstudio": { - "version": "2.0.22", + "version": "2.0.23", "depth": 0, "source": "registry", "dependencies": { @@ -23,22 +23,6 @@ }, "url": "https://packages.unity.com" }, - "com.unity.sysroot": { - "version": "2.0.10", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.sysroot.linux-x86_64": { - "version": "2.0.9", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10" - }, - "url": "https://packages.unity.com" - }, "com.unity.test-framework": { "version": "1.4.6", "depth": 0, @@ -50,16 +34,6 @@ }, "url": "https://packages.unity.com" }, - "com.unity.toolchain.win-x86_64-linux-x86_64": { - "version": "2.0.10", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10", - "com.unity.sysroot.linux-x86_64": "2.0.9" - }, - "url": "https://packages.unity.com" - }, "com.unity.ugui": { "version": "1.0.0", "depth": 0, diff --git a/Unity-Package/ProjectSettings/AudioManager.asset b/Unity-Package/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..df1e809 --- /dev/null +++ b/Unity-Package/ProjectSettings/AudioManager.asset @@ -0,0 +1,20 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 1024 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_EnableOutputSuspension: 1 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 + m_RequestedDSPBufferSize: 0 diff --git a/Unity-Package/ProjectSettings/ClusterInputManager.asset b/Unity-Package/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..e7886b2 --- /dev/null +++ b/Unity-Package/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/ProjectSettings/DynamicsManager.asset b/Unity-Package/ProjectSettings/DynamicsManager.asset similarity index 100% rename from ProjectSettings/DynamicsManager.asset rename to Unity-Package/ProjectSettings/DynamicsManager.asset diff --git a/Unity-Package/ProjectSettings/EditorBuildSettings.asset b/Unity-Package/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..0147887 --- /dev/null +++ b/Unity-Package/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: [] + m_configObjects: {} diff --git a/ProjectSettings/EditorSettings.asset b/Unity-Package/ProjectSettings/EditorSettings.asset similarity index 100% rename from ProjectSettings/EditorSettings.asset rename to Unity-Package/ProjectSettings/EditorSettings.asset diff --git a/Unity-Package/ProjectSettings/GraphicsSettings.asset b/Unity-Package/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..a7bcfc3 --- /dev/null +++ b/Unity-Package/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,67 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, + type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 + m_LogWhenShaderIsCompiled: 0 + m_AllowEnlightenSupportForUpgradedProject: 0 diff --git a/ProjectSettings/InputManager.asset b/Unity-Package/ProjectSettings/InputManager.asset similarity index 100% rename from ProjectSettings/InputManager.asset rename to Unity-Package/ProjectSettings/InputManager.asset diff --git a/ProjectSettings/MemorySettings.asset b/Unity-Package/ProjectSettings/MemorySettings.asset similarity index 100% rename from ProjectSettings/MemorySettings.asset rename to Unity-Package/ProjectSettings/MemorySettings.asset diff --git a/ProjectSettings/NavMeshAreas.asset b/Unity-Package/ProjectSettings/NavMeshAreas.asset similarity index 100% rename from ProjectSettings/NavMeshAreas.asset rename to Unity-Package/ProjectSettings/NavMeshAreas.asset diff --git a/ProjectSettings/PackageManagerSettings.asset b/Unity-Package/ProjectSettings/PackageManagerSettings.asset similarity index 88% rename from ProjectSettings/PackageManagerSettings.asset rename to Unity-Package/ProjectSettings/PackageManagerSettings.asset index 5685308..06b7936 100644 --- a/ProjectSettings/PackageManagerSettings.asset +++ b/Unity-Package/ProjectSettings/PackageManagerSettings.asset @@ -27,12 +27,6 @@ MonoBehaviour: - com.cysharp.unitask - com.openupm m_IsDefault: 0 - - m_Id: scoped:Unity NuGet - m_Name: Unity NuGet - m_Url: https://unitynuget-registry.azurewebsites.net - m_Scopes: - - org.nuget - m_IsDefault: 0 m_UserSelectedRegistryName: package.openupm.com m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: diff --git a/ProjectSettings/Physics2DSettings.asset b/Unity-Package/ProjectSettings/Physics2DSettings.asset similarity index 100% rename from ProjectSettings/Physics2DSettings.asset rename to Unity-Package/ProjectSettings/Physics2DSettings.asset diff --git a/Unity-Package/ProjectSettings/PresetManager.asset b/Unity-Package/ProjectSettings/PresetManager.asset new file mode 100644 index 0000000..67a94da --- /dev/null +++ b/Unity-Package/ProjectSettings/PresetManager.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_DefaultPresets: {} diff --git a/ProjectSettings/ProjectSettings.asset b/Unity-Package/ProjectSettings/ProjectSettings.asset similarity index 79% rename from ProjectSettings/ProjectSettings.asset rename to Unity-Package/ProjectSettings/ProjectSettings.asset index 3d0fe1d..ebc2089 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/Unity-Package/ProjectSettings/ProjectSettings.asset @@ -3,7 +3,7 @@ --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 - serializedVersion: 24 + serializedVersion: 20 productGUID: bccd13822a2bbdf458ffe3f532a192e9 AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 @@ -49,8 +49,6 @@ PlayerSettings: m_StereoRenderingPath: 0 m_ActiveColorSpace: 0 m_MTRendering: 1 - mipStripping: 0 - numberOfMipsStripped: 0 m_StackTraceTypes: 010000000100000000000000000000000100000001000000 iosShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1 @@ -125,9 +123,7 @@ PlayerSettings: stadiaTargetFramerate: 0 vulkanNumSwapchainBuffers: 3 vulkanEnableSetSRGBWrite: 0 - vulkanEnablePreTransform: 0 vulkanEnableLateAcquireNextImage: 0 - vulkanEnableCommandBufferRecycling: 1 m_SupportedAspectRatios: 4:3: 1 5:4: 1 @@ -142,16 +138,39 @@ PlayerSettings: xboxOneDisableKinectGpuReservation: 1 xboxOneEnable7thCore: 1 vrSettings: + cardboard: + depthFormat: 0 + enableTransitionView: 0 + daydream: + depthFormat: 0 + useSustainedPerformanceMode: 0 + enableVideoLayer: 0 + useProtectedVideoMemory: 0 + minimumSupportedHeadTracking: 0 + maximumSupportedHeadTracking: 1 + hololens: + depthFormat: 1 + depthBufferSharingEnabled: 1 + lumin: + depthFormat: 0 + frameTiming: 2 + enableGLCache: 0 + glCacheMaxBlobSize: 524288 + glCacheMaxFileSize: 8388608 + oculus: + sharedDepthBuffer: 1 + dashSupport: 1 + lowOverheadMode: 0 + protectedContext: 0 + v2Signing: 1 enable360StereoCapture: 0 isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 - enableOpenGLProfilerGPURecorders: 1 useHDRDisplay: 0 D3DHDRBitDepth: 0 m_ColorGamuts: 00000000 targetPixelDensity: 30 resolutionScalingMode: 0 - resetResolutionOnWindowResize: 0 androidSupportedAspectRatio: 1 androidMaxAspectRatio: 2.1 applicationIdentifier: @@ -161,12 +180,11 @@ PlayerSettings: VisionOS: 0 iPhone: 0 tvOS: 0 - overrideDefaultApplicationIdentifier: 0 AndroidBundleVersionCode: 1 AndroidMinSdkVersion: 22 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 - aotOptions: + aotOptions: stripEngineCode: 1 iPhoneStrippingLevel: 0 iPhoneScriptCallOptimization: 0 @@ -206,7 +224,7 @@ PlayerSettings: rgba: 0 iOSLaunchScreenFillPct: 100 iOSLaunchScreenSize: 100 - iOSLaunchScreenCustomXibPath: + iOSLaunchScreenCustomXibPath: iOSLaunchScreeniPadType: 0 iOSLaunchScreeniPadImage: {fileID: 0} iOSLaunchScreeniPadBackgroundColor: @@ -214,44 +232,35 @@ PlayerSettings: rgba: 0 iOSLaunchScreeniPadFillPct: 100 iOSLaunchScreeniPadSize: 100 - iOSLaunchScreeniPadCustomXibPath: - iOSLaunchScreenCustomStoryboardPath: - iOSLaunchScreeniPadCustomStoryboardPath: + iOSLaunchScreeniPadCustomXibPath: + iOSUseLaunchScreenStoryboard: 0 + iOSLaunchScreenCustomStoryboardPath: iOSDeviceRequirements: [] iOSURLSchemes: [] - macOSURLSchemes: [] iOSBackgroundModes: 0 iOSMetalForceHardShadows: 0 metalEditorSupport: 1 metalAPIValidation: 1 iOSRenderExtraFrameOnPause: 0 iosCopyPluginsCodeInsteadOfSymlink: 0 - appleDeveloperTeamID: - iOSManualSigningProvisioningProfileID: - tvOSManualSigningProvisioningProfileID: + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: iOSManualSigningProvisioningProfileType: 0 tvOSManualSigningProvisioningProfileType: 0 appleEnableAutomaticSigning: 0 iOSRequireARKit: 0 iOSAutomaticallyDetectAndAddCapabilities: 1 appleEnableProMotion: 0 - shaderPrecisionModel: 0 clonedFromGUID: 00000000000000000000000000000000 - templatePackageId: - templateDefaultScene: - useCustomMainManifest: 0 - useCustomLauncherManifest: 0 - useCustomMainGradleTemplate: 0 - useCustomLauncherGradleManifest: 0 - useCustomBaseGradleTemplate: 0 - useCustomGradlePropertiesTemplate: 0 - useCustomProguardFile: 0 + templatePackageId: + templateDefaultScene: AndroidTargetArchitectures: 1 AndroidTargetDevices: 0 AndroidSplashScreenScale: 0 androidSplashScreen: {fileID: 0} - AndroidKeystoreName: - AndroidKeyaliasName: + AndroidKeystoreName: + AndroidKeyaliasName: AndroidBuildApkPerCpuArchitecture: 0 AndroidTVCompatibility: 0 AndroidIsGame: 1 @@ -265,9 +274,6 @@ PlayerSettings: banner: {fileID: 0} androidGamepadSupportLevel: 0 chromeosInputEmulation: 1 - AndroidMinifyWithR8: 0 - AndroidMinifyRelease: 0 - AndroidMinifyDebug: 0 AndroidValidateAppBundleSize: 1 AndroidAppBundleSizeToValidate: 150 m_BuildTargetIcons: [] @@ -278,129 +284,129 @@ PlayerSettings: m_Width: 432 m_Height: 432 m_Kind: 2 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 324 m_Height: 324 m_Kind: 2 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 216 m_Height: 216 m_Kind: 2 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 162 m_Height: 162 m_Kind: 2 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 108 m_Height: 108 m_Kind: 2 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 81 m_Height: 81 m_Kind: 2 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 192 m_Height: 192 m_Kind: 1 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 144 m_Height: 144 m_Kind: 1 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 96 m_Height: 96 m_Kind: 1 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 72 m_Height: 72 m_Kind: 1 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 48 m_Height: 48 m_Kind: 1 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 36 m_Height: 36 m_Kind: 1 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 192 m_Height: 192 m_Kind: 0 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 144 m_Height: 144 m_Kind: 0 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 96 m_Height: 96 m_Kind: 0 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 72 m_Height: 72 m_Kind: 0 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 48 m_Height: 48 m_Kind: 0 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 36 m_Height: 36 m_Kind: 0 - m_SubKind: + m_SubKind: - m_BuildTarget: tvOS m_Icons: - m_Textures: [] m_Width: 1280 m_Height: 768 m_Kind: 0 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 800 m_Height: 480 m_Kind: 0 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 400 m_Height: 240 m_Kind: 0 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 4640 m_Height: 1440 m_Kind: 1 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 2320 m_Height: 720 m_Kind: 1 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 3840 m_Height: 1440 m_Kind: 1 - m_SubKind: + m_SubKind: - m_Textures: [] m_Width: 1920 m_Height: 720 m_Kind: 1 - m_SubKind: + m_SubKind: - m_BuildTarget: iPhone m_Icons: - m_Textures: [] @@ -499,7 +505,6 @@ PlayerSettings: m_Kind: 4 m_SubKind: App Store m_BuildTargetBatching: [] - m_BuildTargetShaderSettings: [] m_BuildTargetGraphicsJobs: - m_BuildTarget: MacStandaloneSupport m_GraphicsJobs: 0 @@ -548,8 +553,6 @@ PlayerSettings: m_APIs: 0b00000008000000 m_Automatic: 0 m_BuildTargetVRSettings: [] - m_DefaultShaderChunkSizeInMB: 16 - m_DefaultShaderChunkCount: 0 openGLRequireES31: 0 openGLRequireES31AEP: 0 openGLRequireES32: 0 @@ -560,61 +563,55 @@ PlayerSettings: tvOS: 1 m_BuildTargetGroupLightmapEncodingQuality: [] m_BuildTargetGroupLightmapSettings: [] - m_BuildTargetNormalMapEncoding: [] - m_BuildTargetDefaultTextureCompressionFormat: [] playModeTestRunnerEnabled: 0 runPlayModeTestAsEditModeTest: 0 actionOnDotNetUnhandledException: 1 enableInternalProfiler: 0 logObjCUncaughtExceptions: 1 enableCrashReportAPI: 0 - cameraUsageDescription: - locationUsageDescription: - microphoneUsageDescription: - bluetoothUsageDescription: - switchNMETAOverride: - switchNetLibKey: + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + switchNetLibKey: switchSocketMemoryPoolSize: 6144 switchSocketAllocatorPoolSize: 128 switchSocketConcurrencyLimit: 14 switchScreenResolutionBehavior: 2 switchUseCPUProfiler: 0 - switchUseGOLDLinker: 0 - switchLTOSetting: 0 switchApplicationID: 0x01004b9000490000 - switchNSODependencies: - switchTitleNames_0: - switchTitleNames_1: - switchTitleNames_2: - switchTitleNames_3: - switchTitleNames_4: - switchTitleNames_5: - switchTitleNames_6: - switchTitleNames_7: - switchTitleNames_8: - switchTitleNames_9: - switchTitleNames_10: - switchTitleNames_11: - switchTitleNames_12: - switchTitleNames_13: - switchTitleNames_14: - switchTitleNames_15: - switchPublisherNames_0: - switchPublisherNames_1: - switchPublisherNames_2: - switchPublisherNames_3: - switchPublisherNames_4: - switchPublisherNames_5: - switchPublisherNames_6: - switchPublisherNames_7: - switchPublisherNames_8: - switchPublisherNames_9: - switchPublisherNames_10: - switchPublisherNames_11: - switchPublisherNames_12: - switchPublisherNames_13: - switchPublisherNames_14: - switchPublisherNames_15: + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: switchIcons_0: {fileID: 0} switchIcons_1: {fileID: 0} switchIcons_2: {fileID: 0} @@ -647,18 +644,19 @@ PlayerSettings: switchSmallIcons_13: {fileID: 0} switchSmallIcons_14: {fileID: 0} switchSmallIcons_15: {fileID: 0} - switchManualHTML: - switchAccessibleURLs: - switchLegalInformation: + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: switchMainThreadStackSize: 1048576 - switchPresenceGroupId: + switchPresenceGroupId: switchLogoHandling: 0 switchReleaseVersion: 0 switchDisplayVersion: 1.0.0 switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 switchSupportedLanguagesMask: 0 switchLogoType: 0 - switchApplicationErrorCodeCategory: + switchApplicationErrorCodeCategory: switchUserAccountSaveDataSize: 0 switchUserAccountSaveDataJournalSize: 0 switchApplicationAttribute: 0 @@ -678,14 +676,14 @@ PlayerSettings: switchRatingsInt_10: 0 switchRatingsInt_11: 0 switchRatingsInt_12: 0 - switchLocalCommunicationIds_0: - switchLocalCommunicationIds_1: - switchLocalCommunicationIds_2: - switchLocalCommunicationIds_3: - switchLocalCommunicationIds_4: - switchLocalCommunicationIds_5: - switchLocalCommunicationIds_6: - switchLocalCommunicationIds_7: + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: switchParentalControl: 0 switchAllowsScreenshot: 1 switchAllowsVideoCapturing: 1 @@ -697,7 +695,6 @@ PlayerSettings: switchNativeFsCacheSize: 32 switchIsHoldTypeHorizontal: 0 switchSupportedNpadCount: 8 - switchEnableTouchScreen: 1 switchSocketConfigEnabled: 0 switchTcpInitialSendBufferSize: 32 switchTcpInitialReceiveBufferSize: 64 @@ -709,42 +706,40 @@ PlayerSettings: switchSocketInitializeEnabled: 1 switchNetworkInterfaceManagerInitializeEnabled: 1 switchPlayerConnectionEnabled: 1 - switchUseNewStyleFilepaths: 0 - switchUseLegacyFmodPriorities: 1 switchUseMicroSleepForYield: 1 switchEnableRamDiskSupport: 0 switchMicroSleepForYieldTime: 25 switchRamDiskSpaceSize: 12 ps4NPAgeRating: 12 - ps4NPTitleSecret: - ps4NPTrophyPackPath: + ps4NPTitleSecret: + ps4NPTrophyPackPath: ps4ParentalLevel: 11 ps4ContentID: ED1633-NPXX51362_00-0000000000000000 ps4Category: 0 ps4MasterVersion: 01.00 ps4AppVersion: 01.00 ps4AppType: 0 - ps4ParamSfxPath: + ps4ParamSfxPath: ps4VideoOutPixelFormat: 0 ps4VideoOutInitialWidth: 1920 ps4VideoOutBaseModeInitialWidth: 1920 ps4VideoOutReprojectionRate: 60 - ps4PronunciationXMLPath: - ps4PronunciationSIGPath: - ps4BackgroundImagePath: - ps4StartupImagePath: - ps4StartupImagesFolder: - ps4IconImagesFolder: - ps4SaveDataImagePath: - ps4SdkOverride: - ps4BGMPath: - ps4ShareFilePath: - ps4ShareOverlayImagePath: - ps4PrivacyGuardImagePath: - ps4ExtraSceSysFile: - ps4NPtitleDatPath: + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: ps4RemotePlayKeyAssignment: -1 - ps4RemotePlayKeyMappingDir: + ps4RemotePlayKeyMappingDir: ps4PlayTogetherPlayerCount: 0 ps4EnterButtonAssignment: 2 ps4ApplicationParam1: 0 @@ -772,9 +767,9 @@ PlayerSettings: ps4ScriptOptimizationLevel: 2 ps4Audio3dVirtualSpeakerCount: 14 ps4attribCpuUsage: 0 - ps4PatchPkgPath: - ps4PatchLatestPkgPath: - ps4PatchChangeinfoPath: + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: ps4PatchDayOne: 0 ps4attribUserManagement: 0 ps4attribMoveSupport: 0 @@ -790,32 +785,55 @@ PlayerSettings: ps4attribEyeToEyeDistanceSettingVR: 0 ps4IncludedModules: [] ps4attribVROutputEnabled: 0 - monoEnv: + ps5ParamFilePath: + ps5VideoOutPixelFormat: 0 + ps5VideoOutInitialWidth: 1920 + ps5VideoOutOutputMode: 1 + ps5BackgroundImagePath: + ps5StartupImagePath: + ps5Pic2Path: + ps5StartupImagesFolder: + ps5IconImagesFolder: + ps5SaveDataImagePath: + ps5SdkOverride: + ps5BGMPath: + ps5ShareOverlayImagePath: + ps5NPConfigZipPath: + ps5Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps5UseResolutionFallback: 0 + ps5UseAudio3dBackend: 0 + ps5ScriptOptimizationLevel: 2 + ps5Audio3dVirtualSpeakerCount: 14 + ps5VrrSupport: 0 + ps5UpdateReferencePackage: + ps5disableAutoHideSplash: 0 + ps5OperatingSystemCanDisableSplashScreen: 0 + ps5IncludedModules: [] + ps5SharedBinaryContentLabels: [] + ps5SharedBinarySystemFolders: [] + monoEnv: splashScreenBackgroundSourceLandscape: {fileID: 0} splashScreenBackgroundSourcePortrait: {fileID: 0} blurSplashScreenBackground: 1 - spritePackerPolicy: + spritePackerPolicy: webGLMemorySize: 32 webGLExceptionSupport: 1 webGLNameFilesAsHashes: 0 webGLDataCaching: 1 webGLDebugSymbols: 0 - webGLEmscriptenArgs: - webGLModulesDirectory: + webGLEmscriptenArgs: + webGLModulesDirectory: webGLTemplate: APPLICATION:Default webGLAnalyzeBuildSize: 0 webGLUseEmbeddedResources: 0 webGLCompressionFormat: 0 - webGLWasmArithmeticExceptions: 0 webGLLinkerTarget: 1 webGLThreadsSupport: 0 - webGLDecompressionFallback: 0 - webGLPowerPreference: 2 + webGLWasmStreaming: 0 scriptingDefineSymbols: {} - additionalCompilerArguments: {} platformArchitecture: {} scriptingBackend: - Standalone: 1 + Standalone: 0 il2cppCompilerConfiguration: {} managedStrippingLevel: EmbeddedLinux: 1 @@ -835,10 +853,7 @@ PlayerSettings: incrementalIl2cppBuild: {} suppressCommonWarnings: 1 allowUnsafeCode: 0 - useDeterministicCompilation: 1 - enableRoslynAnalyzers: 1 - selectedPlatform: 0 - additionalIl2CppArgs: + additionalIl2CppArgs: scriptingRuntimeVersion: 1 gcIncremental: 1 assemblyVersionValidation: 0 @@ -847,15 +862,15 @@ PlayerSettings: m_RenderingPath: 1 m_MobileRenderingPath: 1 metroPackageName: Unity-ImageLoader - metroPackageVersion: - metroCertificatePath: - metroCertificatePassword: - metroCertificateSubject: - metroCertificateIssuer: + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: metroCertificateNotAfter: 0000000000000000 metroApplicationDescription: Unity-ImageLoader wsaImages: {} - metroTileShortName: + metroTileShortName: metroTileShowName: 0 metroMediumTileShowName: 0 metroLargeTileShowName: 0 @@ -870,23 +885,22 @@ PlayerSettings: metroSplashScreenUseBackgroundColor: 0 platformCapabilities: {} metroTargetDeviceFamilies: {} - metroFTAName: + metroFTAName: metroFTAFileTypes: [] - metroProtocolName: - vcxProjDefaultLanguage: - XboxOneProductId: - XboxOneUpdateKey: - XboxOneSandboxId: - XboxOneContentId: - XboxOneTitleId: - XboxOneSCId: - XboxOneGameOsOverridePath: - XboxOnePackagingOverridePath: - XboxOneAppManifestOverridePath: + metroProtocolName: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: XboxOneVersion: 1.0.0.0 XboxOnePackageEncryption: 0 XboxOnePackageUpdateGranularity: 2 - XboxOneDescription: + XboxOneDescription: XboxOneLanguage: - enus XboxOneCapability: [] @@ -899,31 +913,30 @@ PlayerSettings: XboxOneAllowedProductIds: [] XboxOnePersistentLocalStorageSize: 0 XboxOneXTitleMemory: 8 - XboxOneOverrideIdentityName: - XboxOneOverrideIdentityPublisher: - vrEditorSettings: {} + XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: + vrEditorSettings: + daydream: + daydreamIconForeground: {fileID: 0} + daydreamIconBackground: {fileID: 0} cloudServicesEnabled: {} luminIcon: - m_Name: - m_ModelFolderPath: - m_PortalFolderPath: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: luminCert: - m_CertPath: + m_CertPath: m_SignPackage: 1 luminIsChannelApp: 0 luminVersion: m_VersionCode: 1 - m_VersionName: + m_VersionName: apiCompatibilityLevel: 6 - activeInputHandler: 0 - windowsGamepadBackendHint: 0 - cloudProjectId: + cloudProjectId: framebufferDepthMemorylessMode: 0 - qualitySettingsNames: [] - projectName: - organizationId: + projectName: + organizationId: cloudEnabled: 0 + enableNativePlatformBackendsForNewInputSystem: 0 + disableOldInputManagerSupport: 0 legacyClampBlendShapeWeights: 0 - playerDataPath: - forceSRGBBlit: 1 - virtualTexturingSupportEnabled: 0 diff --git a/Unity-Package/ProjectSettings/ProjectVersion.txt b/Unity-Package/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..4c19129 --- /dev/null +++ b/Unity-Package/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2019.4.40f1 +m_EditorVersionWithRevision: 2019.4.40f1 (ffc62b691db5) diff --git a/ProjectSettings/QualitySettings.asset b/Unity-Package/ProjectSettings/QualitySettings.asset similarity index 100% rename from ProjectSettings/QualitySettings.asset rename to Unity-Package/ProjectSettings/QualitySettings.asset diff --git a/Unity-Package/ProjectSettings/TagManager.asset b/Unity-Package/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..1c92a78 --- /dev/null +++ b/Unity-Package/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/Unity-Package/ProjectSettings/TimeManager.asset b/Unity-Package/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..558a017 --- /dev/null +++ b/Unity-Package/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/ProjectSettings/UnityConnectSettings.asset b/Unity-Package/ProjectSettings/UnityConnectSettings.asset similarity index 100% rename from ProjectSettings/UnityConnectSettings.asset rename to Unity-Package/ProjectSettings/UnityConnectSettings.asset diff --git a/ProjectSettings/VFXManager.asset b/Unity-Package/ProjectSettings/VFXManager.asset similarity index 100% rename from ProjectSettings/VFXManager.asset rename to Unity-Package/ProjectSettings/VFXManager.asset diff --git a/ProjectSettings/VersionControlSettings.asset b/Unity-Package/ProjectSettings/VersionControlSettings.asset similarity index 100% rename from ProjectSettings/VersionControlSettings.asset rename to Unity-Package/ProjectSettings/VersionControlSettings.asset diff --git a/Unity-Package/ProjectSettings/XRSettings.asset b/Unity-Package/ProjectSettings/XRSettings.asset new file mode 100644 index 0000000..482590c --- /dev/null +++ b/Unity-Package/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file diff --git a/Test Images/ImageA.jpg b/Unity-Package/Test Images/ImageA.jpg similarity index 100% rename from Test Images/ImageA.jpg rename to Unity-Package/Test Images/ImageA.jpg diff --git a/Test Images/ImageB.png b/Unity-Package/Test Images/ImageB.png similarity index 100% rename from Test Images/ImageB.png rename to Unity-Package/Test Images/ImageB.png diff --git a/Test Images/ImageC.png b/Unity-Package/Test Images/ImageC.png similarity index 100% rename from Test Images/ImageC.png rename to Unity-Package/Test Images/ImageC.png diff --git a/UserSettings/EditorUserSettings.asset b/Unity-Package/UserSettings/EditorUserSettings.asset similarity index 100% rename from UserSettings/EditorUserSettings.asset rename to Unity-Package/UserSettings/EditorUserSettings.asset diff --git a/UserSettings/Search.index b/Unity-Package/UserSettings/Search.index similarity index 100% rename from UserSettings/Search.index rename to Unity-Package/UserSettings/Search.index diff --git a/UserSettings/Search.settings b/Unity-Package/UserSettings/Search.settings similarity index 100% rename from UserSettings/Search.settings rename to Unity-Package/UserSettings/Search.settings diff --git a/bump-version.ps1 b/bump-version.ps1 new file mode 100644 index 0000000..f57e8e3 --- /dev/null +++ b/bump-version.ps1 @@ -0,0 +1,206 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Automated version bumping script for the project + +.DESCRIPTION + Updates version numbers across all project files automatically to prevent human errors. + Supports preview mode for safe testing. + +.PARAMETER NewVersion + The new version number in semver format (e.g., "0.18.0") + +.PARAMETER WhatIf + Preview changes without applying them + +.EXAMPLE + .\bump-version.ps1 -NewVersion "0.18.0" + +.EXAMPLE + .\bump-version.ps1 -NewVersion "0.18.0" -WhatIf +#> + +param( + [Parameter(Mandatory = $true)] + [string]$NewVersion, + + [switch]$WhatIf +) + +# Script configuration +$ErrorActionPreference = "Stop" + +# Version file locations (relative to script root) +$VersionFiles = @( + @{ + Path = "README.md" + Pattern = "https://github\.com/IvanMurzak/Unity-ImageLoader/releases/download/[\d\.]+/ImageLoader-Installer\.unitypackage" + Replace = "https://github.com/IvanMurzak/Unity-ImageLoader/releases/download/{VERSION}/ImageLoader-Installer.unitypackage" + Description = "Root README download URL" + }, + @{ + Path = "Unity-Package/Assets/root/README.md" + Pattern = "https://github\.com/IvanMurzak/Unity-ImageLoader/releases/download/[\d\.]+/ImageLoader-Installer\.unitypackage" + Replace = "https://github.com/IvanMurzak/Unity-ImageLoader/releases/download/{VERSION}/ImageLoader-Installer.unitypackage" + Description = "Plugin README download URL" + }, + @{ + Path = "Installer/Assets/com.IvanMurzak/Image Loader Installer/Installer.cs" + Pattern = 'public const string Version = "[\d\.]+";' + Replace = 'public const string Version = "{VERSION}";' + Description = "Installer C# version constant" + }, + @{ + Path = "Unity-Package/Assets/root/package.json" + Pattern = '"version":\s*"[\d\.]+"' + Replace = '"version": "{VERSION}"' + Description = "Unity package version" + } +) + +function Write-ColorText { + param([string]$Text, [string]$Color = "White") + Write-Host $Text -ForegroundColor $Color +} + +function Test-SemanticVersion { + param([string]$Version) + + if ([string]::IsNullOrWhiteSpace($Version)) { + return $false + } + + # Basic semver pattern: major.minor.patch (with optional prerelease/build) + $pattern = '^\d+\.\d+\.\d+(-[a-zA-Z0-9\-\.]+)?(\+[a-zA-Z0-9\-\.]+)?$' + return $Version -match $pattern +} + +function Get-CurrentVersion { + # Extract current version from package.json + $packageJsonPath = Join-Path $PSScriptRoot "Unity-Package/Assets/root/package.json" + if (-not (Test-Path $packageJsonPath)) { + throw "Could not find package.json at: $packageJsonPath" + } + + $content = Get-Content $packageJsonPath -Raw + if ($content -match '"version":\s*"([\d\.]+)"') { + return $Matches[1] + } + + throw "Could not extract current version from package.json" +} + +function Update-VersionFiles { + param([string]$OldVersion, [string]$NewVersion, [bool]$PreviewOnly = $false) + + $changes = @() + + foreach ($file in $VersionFiles) { + $fullPath = Join-Path $PSScriptRoot $file.Path + + if (-not (Test-Path $fullPath)) { + Write-ColorText "⚠️ File not found: $($file.Path)" "Yellow" + continue + } + + $content = Get-Content $fullPath -Raw + $originalContent = $content + + # Create the replacement string + $replacement = $file.Replace -replace '\{VERSION\}', $NewVersion + + # Apply the replacement + $newContent = $content -replace $file.Pattern, $replacement + + # Check if any changes were made + if ($originalContent -ne $newContent) { + # Count matches for reporting + $matches = [regex]::Matches($originalContent, $file.Pattern) + + $changes += @{ + Path = $file.Path + Description = $file.Description + Matches = $matches.Count + Content = $newContent + OriginalContent = $originalContent + } + + Write-ColorText "📝 $($file.Description): $($matches.Count) occurrence(s)" "Green" + + # Show the actual changes + foreach ($match in $matches) { + $newValue = $match.Value -replace $file.Pattern, $replacement + Write-ColorText " $($match.Value) → $newValue" "Gray" + } + } + else { + Write-ColorText "⚠️ No matches found in: $($file.Path)" "Yellow" + Write-ColorText " Pattern: $($file.Pattern)" "Gray" + } + } + + if ($changes.Count -eq 0) { + Write-ColorText "❌ No version references found to update!" "Red" + exit 1 + } + + if ($PreviewOnly) { + Write-ColorText "`n📋 Preview Summary:" "Cyan" + Write-ColorText "Files to be modified: $($changes.Count)" "White" + Write-ColorText "Total replacements: $(($changes | Measure-Object -Property Matches -Sum).Sum)" "White" + return $null + } + + # Apply changes + foreach ($change in $changes) { + $fullPath = Join-Path $PSScriptRoot $change.Path + Set-Content -Path $fullPath -Value $change.Content -NoNewline + } + + return $changes +} + +# Main execution +try { + Write-ColorText "🚀 Version Bump Script" "Cyan" + Write-ColorText "=================================" "Cyan" + + # Validate semantic version format + if (-not (Test-SemanticVersion $NewVersion)) { + Write-ColorText "❌ Invalid semantic version format: $NewVersion" "Red" + Write-ColorText "Expected format: major.minor.patch (e.g., '1.2.3')" "Yellow" + exit 1 + } + + # Get current version + $currentVersion = Get-CurrentVersion + Write-ColorText "📋 Current version: $currentVersion" "White" + Write-ColorText "📋 New version: $NewVersion" "White" + + if ($currentVersion -eq $NewVersion) { + Write-ColorText "⚠️ New version is the same as current version" "Yellow" + exit 0 + } + + Write-ColorText "`n🔍 Scanning for version references..." "Cyan" + + # Update version files + $changes = Update-VersionFiles -OldVersion $currentVersion -NewVersion $NewVersion -PreviewOnly $WhatIf + + if ($WhatIf) { + Write-ColorText "`n✅ Preview completed. Use without -WhatIf to apply changes." "Green" + exit 0 + } + + if ($changes -and $changes.Count -gt 0) { + Write-ColorText "`n🎉 Version bump completed successfully!" "Green" + Write-ColorText " Updated $($changes.Count) files" "White" + Write-ColorText " Total replacements: $(($changes | Measure-Object -Property Matches -Sum).Sum)" "White" + Write-ColorText " Version: $currentVersion → $NewVersion" "White" + Write-ColorText "`n💡 Remember to commit these changes to git" "Cyan" + } +} +catch { + Write-ColorText "`n❌ Script failed: $($_.Exception.Message)" "Red" + exit 1 +} \ No newline at end of file diff --git a/docs/img/header.gif b/docs/img/header.gif new file mode 100644 index 0000000..3b3498d Binary files /dev/null and b/docs/img/header.gif differ diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 8359b3a..0000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Unity-ImageLoader", - "lockfileVersion": 3, - "requires": true, - "packages": {} -}