diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ba5e6aa..89e3ff3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,7 +1,6 @@ -# This is a basic workflow to help you get started with Actions name: CI -# Controls when the workflow will run + on: # Triggers the workflow on push or pull request events but only for the master branch push: @@ -10,52 +9,183 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: -# A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: windows-2019 + runs-on: windows-2022 # Steps represent a sequence of tasks that will be executed as part of the job steps: - - name: Check Commit and Install 7Zip PowerShell Module + # Step to check if the commit message contains #GITBUILD + - name: Check Commit Message shell: powershell run: | - # cancel early, if not build commit - $strVal ='${{ github.event.commits[0].message }}' + # Get the commit message + $strVal = '${{ github.event.commits[0].message }}' # Convert commit message to a single line if multiline $singleLineStrVal = $strVal -replace "`r`n", " " -replace "`n", " " - if($singleLineStrVal -match '#GITBUILD') - { - Write-Host 'True' + if ($singleLineStrVal -match '#GITBUILD') { + Write-Host 'Build commit detected. Proceeding with build...' + echo "build_trigger=true" >> $env:GITHUB_ENV } else { - Write-Host 'False' - exit(1) + Write-Host 'No build commit. Skipping build steps...' + echo "build_trigger=false" >> $env:GITHUB_ENV } - Install-Module 7Zip4PowerShell -Force -Verbose + # Step to ensure the repository is checked out - uses: actions/checkout@v2 + # Inform if build steps are skipped + - name: Inform Skipped Build Steps + if: env.build_trigger != 'true' + shell: powershell + run: | + Write-Host "Skipping build steps because the commit message does not contain #GITBUILD." + + # Install 7Zip PowerShell module + - name: Install 7Zip PowerShell Module + if: env.build_trigger == 'true' + shell: powershell + run: Install-Module 7Zip4PowerShell -Force -Verbose + + # Restore NuGet packages - name: Restore NuGet packages + if: env.build_trigger == 'true' run: nuget restore UnityLauncherPro.sln - + + # Build the binary - name: Build Binary + if: env.build_trigger == 'true' shell: cmd run: call .\Build.cmd - + + # Build the artifact - name: Build Artifact + if: env.build_trigger == 'true' shell: cmd run: call .\ArtifactBuild.cmd + # Check that output exists + - name: Validate UnityLauncherPro.exe exists + if: env.build_trigger == 'true' + shell: cmd + run: | + if not exist "UnityLauncherPro\bin\Release\UnityLauncherPro.exe" ( + echo ERROR: UnityLauncherPro.exe not found. + exit /b 1 + ) + echo Found UnityLauncherPro.exe + + # 1) Compute a wrapped major.minor.build from the run number + - name: Compute installer version + if: env.build_trigger == 'true' + shell: bash + run: | + TOTAL=${{ github.run_number }} + BUILD=$(( TOTAL % 65536 )) + MINOR_TOTAL=$(( TOTAL / 65536 )) + MINOR=$(( MINOR_TOTAL % 256 )) + MAJOR=$(( MINOR_TOTAL / 256 )) + echo "INSTALLER_MAJOR=$MAJOR" >> $GITHUB_ENV + echo "INSTALLER_MINOR=$MINOR" >> $GITHUB_ENV + echo "INSTALLER_BUILD=$BUILD" >> $GITHUB_ENV + echo "INSTALLER_VER=$MAJOR.$MINOR.$BUILD" >> $GITHUB_ENV + echo "Computed installer version → $MAJOR.$MINOR.$BUILD (run #${{ github.run_number }})" + + # 2) Patch your .vdproj in place (inject ProductVersion and a new GUID) + - name: Patch .vdproj ProductVersion & ProductCode + if: env.build_trigger == 'true' + shell: pwsh + run: | + $proj = 'UnityLauncherProInstaller\UnityLauncherProInstaller.vdproj' + $ver = "${{ env.INSTALLER_VER }}" # e.g. 0.0.118 + $guid = [Guid]::NewGuid().ToString("B").ToUpper() # e.g. {E821A3F5-1F84-4A4B-BE9D-115D93E9E6F0} + + # Read & rewrite line-by-line + $fixed = Get-Content $proj | ForEach-Object { + if ($_ -match '^(\s*)"ProductVersion"') { + # preserve indentation, then inject exactly one pair of braces + "$($Matches[1])""ProductVersion"" = ""8:$ver""" + } + elseif ($_ -match '^(\s*)"ProductCode"') { + "$($Matches[1])""ProductCode"" = ""8:$guid""" + } + else { + $_ + } + } + + # Overwrite the project + Set-Content -Path $proj -Value $fixed + + Write-Host "→ ProductVersion patched to 8:$ver" + Write-Host "→ ProductCode patched to 8:$guid" + + + # 3) **DEBUG**: print out the patched .vdproj so you can inspect it + - name: Show patched .vdproj + if: env.build_trigger == 'true' + shell: pwsh + run: | + $proj = 'UnityLauncherProInstaller\UnityLauncherProInstaller.vdproj' + Write-Host "=== BEGIN .vdproj CONTENT ===" + Get-Content $proj + Write-Host "=== END .vdproj CONTENT ===" + + # locate VS 2022 + - name: Locate Visual Studio 2022 + id: vswhere + shell: pwsh + run: | + $installPath = vswhere -latest -products * -requires Microsoft.Component.MSBuild ` + -property installationPath + if (-not $installPath) { throw 'VS 2022 not found' } + Write-Host "##[set-output name=installPath]$installPath" + + # download vs installer builder (v2.0.1) + - name: Download Installer-Projects VSIX + shell: pwsh + run: | + $vsixUrl = "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/VisualStudioClient/vsextensions/MicrosoftVisualStudio2022InstallerProjects/2.0.1/vspackage" + Invoke-WebRequest $vsixUrl -OutFile installerprojects.vsix + + # install vs installer builder + - name: Install Installer-Projects extension + shell: pwsh + run: | + $vsixInstaller = Join-Path "${{ steps.vswhere.outputs.installPath }}" 'Common7\IDE\VSIXInstaller.exe' + Write-Host "Running: $vsixInstaller installerprojects.vsix /quiet" + & $vsixInstaller installerprojects.vsix /quiet + + # Build MSI installer project using Visual Studio 2022 workaround + - name: Build Installer MSI + if: env.build_trigger == 'true' + shell: cmd + run: | + echo === Running BuildInstaller.bat === + call .\BuildInstaller.bat || echo WARNING: BuildInstaller.bat exited with %ERRORLEVEL%, continuing... + echo === Verifying MSI === + if exist "UnityLauncherProInstaller\Release\UnityLauncherPro-Installer.msi" ( + echo Success: MSI found at UnityLauncherProInstaller\Release\UnityLauncherPro-Installer.msi + exit /b 0 + ) else ( + echo ERROR: MSI not found in UnityLauncherProInstaller\Release + exit /b 1 + ) + + # Get the current date and time - name: Get current date and time id: datetime + if: env.build_trigger == 'true' # Only run if build was triggered run: | + # Save the current date and time to an environment variable echo "current_datetime=$(date +'%d/%m/%Y %H:%M')" >> $GITHUB_ENV # Step to get previous tag and commits - name: Get commits since last release id: get_commits + if: env.build_trigger == 'true' # Only run if build was triggered shell: bash run: | # Get the most recent tag @@ -68,13 +198,13 @@ jobs: # List commits since last tag COMMITS=$(git log $PREV_TAG..HEAD --pretty=format:"* %s" --no-merges) fi - echo "Commits since last release: $COMMITS" - - # Save commits to environment file for later use + # Save commits to the environment echo "commits=$COMMITS" >> $GITHUB_ENV + # Create a release - name: Create Release id: create_release + if: env.build_trigger == 'true' # Execute only if build was triggered uses: actions/create-release@latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -87,10 +217,12 @@ jobs: ### Commits in this release: ${{ env.commits }} draft: false - prerelease: false - + prerelease: false + + # Upload the release asset - name: Upload Release Asset id: upload-release-asset + if: env.build_trigger == 'true' # Execute only if build was triggered uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -99,3 +231,15 @@ jobs: asset_path: ./UnityLauncherPro.zip asset_name: UnityLauncherPro.zip asset_content_type: application/zip + + # Upload MSI installer to release + - name: Upload MSI Installer + if: env.build_trigger == 'true' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./UnityLauncherProInstaller/Release/UnityLauncherPro-Installer.msi + asset_name: UnityLauncherPro-Installer.msi + asset_content_type: application/x-msi diff --git a/Build.cmd b/Build.cmd index 96f1597..80288d1 100644 --- a/Build.cmd +++ b/Build.cmd @@ -1,10 +1,10 @@ @echo off REM Default VS paths to check if no Paths.cmd file exists -set VISUAL_STUDIO_PATH_0="%programfiles(x86)%\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\msbuild.exe" -set VISUAL_STUDIO_PATH_1="%programfiles(x86)%\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" -set VISUAL_STUDIO_PATH_2="%programfiles(x86)%\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\msbuild.exe" -set VISUAL_STUDIO_PATH_3="%programfiles(x86)%\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\msbuild.exe" +set VISUAL_STUDIO_PATH_0="%programfiles%\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\msbuild.exe" +set VISUAL_STUDIO_PATH_1="%programfiles%\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\msbuild.exe" +set VISUAL_STUDIO_PATH_2="%programfiles(x86)%\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\msbuild.exe" +set VISUAL_STUDIO_PATH_3="%programfiles(x86)%\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" pushd "%~dp0" if exist Debug rd /s /q Debug diff --git a/BuildInstaller.bat b/BuildInstaller.bat new file mode 100644 index 0000000..97b0d01 --- /dev/null +++ b/BuildInstaller.bat @@ -0,0 +1,75 @@ +@ECHO OFF +SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION + +ECHO: +ECHO === Starting Installer Build Workaround === + +REM Store current directory +SET "current_path=%CD%" + +REM Try all known editions of Visual Studio 2022 +SET "vs_base_path=%ProgramFiles%\Microsoft Visual Studio\2022" +FOR %%E IN (Community Professional Enterprise) DO ( + IF EXIST "%vs_base_path%\%%E\Common7\IDE\CommonExtensions\Microsoft\VSI\DisableOutOfProcBuild\DisableOutOfProcBuild.exe" ( + SET "buildfix_path=%vs_base_path%\%%E\Common7\IDE\CommonExtensions\Microsoft\VSI\DisableOutOfProcBuild" + SET "devenv_path=%vs_base_path%\%%E\Common7\IDE\devenv.exe" + SET "vs_edition=%%E" + GOTO :FoundEdition + ) +) + +ECHO [ERROR] Could not find DisableOutOfProcBuild.exe in any known VS2022 edition. +EXIT /B 1 + +:FoundEdition +ECHO Found Visual Studio 2022 Edition: %vs_edition% +CD /D "%buildfix_path%" +CALL DisableOutOfProcBuild.exe + +REM Restore previous directory +CD /D "%current_path%" + +REM === Validate required files === +ECHO: +ECHO === Checking required files === + +SET "error=0" + +IF NOT EXIST "UnityLauncherPro\bin\Release\UnityLauncherPro.exe" ( + ECHO [ERROR] Missing file: UnityLauncherPro\bin\Release\UnityLauncherPro.exe + SET "error=1" +) +IF NOT EXIST "UnityLauncherPro\Images\icon.ico" ( + ECHO [ERROR] Missing file: UnityLauncherPro\Images\icon.ico + SET "error=1" +) + +IF %error% NEQ 0 ( + ECHO [ERROR] Required files are missing. Aborting installer build. + EXIT /B 1 +) + + +ECHO: +ECHO === Building Installer === +"%devenv_path%" UnityLauncherPro.sln /Rebuild Release /Project UnityLauncherProInstaller > build_output.log 2>&1 +SET "exitCode=%ERRORLEVEL%" + +TYPE build_output.log +ECHO: +ECHO === devenv.exe exit code: %exitCode% === + +IF NOT "%exitCode%"=="0" ( + ECHO [ERROR] Installer build failed. Check build_output.log for details. + EXIT /B %exitCode% +) + + +ECHO: +ECHO === Build Complete === + +REM Optional cleanup: disable workaround +REG DELETE "HKCU\Software\Microsoft\VisualStudio\Setup" /v VSDisableOutOfProcBuild /f >NUL 2>&1 + +ENDLOCAL +EXIT /B %exitCode% diff --git a/README.md b/README.md index c6af1d1..f221013 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # UnityLauncherPro -[![Build Status](https://ci.appveyor.com/api/projects/status/hajcaavcsg7904rx?svg=true)](https://github.com/unitycoder/UnityLauncherPro/releases/latest/download/UnityLauncherPro.zip) [![Downloads](https://img.shields.io/github/downloads/unitycoder/unitylauncherpro/total)](https://github.com/unitycoder/UnityLauncherPro/releases/latest/download/UnityLauncherPro.zip) [![GitHub license](https://img.shields.io/github/license/unitycoder/UnityLauncherPro)](https://github.com/unitycoder/UnityLauncherPro/blob/master/LICENSE) [](https://discord.gg/cXT97hU) [![VirusTotal scan now](https://img.shields.io/static/v1?label=VirusTotal&message=Scan)](https://www.virustotal.com/gui/url/e123b616cf4cbe3d3f7ba13b0d88cf5fff4638f72d5b9461088d0b11e9a41de3?nocache=1) [![CodeQL](https://github.com/unitycoder/UnityLauncherPro/actions/workflows/codeql.yml/badge.svg)](https://github.com/unitycoder/UnityLauncherPro/actions/workflows/codeql.yml) - +[![Build Status](https://github.com/unitycoder/UnityLauncherPro/actions/workflows/main.yml/badge.svg)](https://github.com/unitycoder/UnityLauncherPro/releases/latest/download/UnityLauncherPro.zip) [![Downloads](https://img.shields.io/github/downloads/unitycoder/unitylauncherpro/total)](https://github.com/unitycoder/UnityLauncherPro/releases/latest/download/UnityLauncherPro.zip) [![GitHub license](https://img.shields.io/github/license/unitycoder/UnityLauncherPro)](https://github.com/unitycoder/UnityLauncherPro/blob/master/LICENSE) [](https://discord.gg/cXT97hU) [![VirusTotal scan now](https://img.shields.io/static/v1?label=VirusTotal&message=Scan)](https://www.virustotal.com/gui/url/e123b616cf4cbe3d3f7ba13b0d88cf5fff4638f72d5b9461088d0b11e9a41de3?nocache=1) [![CodeQL](https://github.com/unitycoder/UnityLauncherPro/actions/workflows/codeql.yml/badge.svg)](https://github.com/unitycoder/UnityLauncherPro/actions/workflows/codeql.yml) Handle all your Unity versions and Projects easily! @@ -27,6 +26,9 @@ Handle all your Unity versions and Projects easily! - Select template for new project (template based on selected unity version) - And more: https://github.com/unitycoder/UnityLauncherPro/wiki/Launcher-Comparison +### Powered by +[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport) + ### Forum Thread https://forum.unity.com/threads/unity-launcher-launch-correct-unity-versions-for-each-project-automatically.488718/ @@ -39,7 +41,7 @@ Pre-releases are sometimes available from dev branch: https://github.com/unityco ### Screenshots -![image](https://user-images.githubusercontent.com/5438317/71485879-184b3a00-281c-11ea-97db-73c5dfa9bb4e.png) +![Image](https://github.com/user-attachments/assets/80bd8ff4-7e90-4c1a-9501-74cf3ea538f6) ![image](https://github.com/unitycoder/UnityLauncherPro/assets/5438317/21eb1fcd-3cb1-4dea-8133-9ce440de77d8) @@ -47,7 +49,7 @@ Pre-releases are sometimes available from dev branch: https://github.com/unityco ![image](https://github.com/unitycoder/UnityLauncherPro/assets/5438317/6f8dce07-c640-42db-a1ef-d8bcc7a80cc2) -![image](https://user-images.githubusercontent.com/5438317/206923022-4b4bb8ed-0351-408f-b1d3-82bd6eefbc45.png) +![Image](https://github.com/user-attachments/assets/fa4e004a-f3c6-47d5-996f-9b603048ad18) ### Perform tasks on selected project ![image](https://github.com/unitycoder/UnityLauncherPro/assets/5438317/a0b468ba-e3a6-420b-8155-78bc32814752) @@ -69,3 +71,5 @@ Pre-releases are sometimes available from dev branch: https://github.com/unityco
Old (winforms) version is here: https://github.com/unitycoder/UnityLauncher + + diff --git a/UnityLauncherPro.sln b/UnityLauncherPro.sln index 63fd938..2314601 100644 --- a/UnityLauncherPro.sln +++ b/UnityLauncherPro.sln @@ -1,14 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.539 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35931.197 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityLauncherPro", "UnityLauncherPro\UnityLauncherPro.csproj", "{EC78D91A-3E63-4CAA-8BC3-9673A30FDA45}" EndProject +Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "UnityLauncherProInstaller", "UnityLauncherProInstaller\UnityLauncherProInstaller.vdproj", "{249180EF-BFC1-9DD7-48CC-E2B9253A64E0}" +EndProject Global - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU @@ -18,6 +17,9 @@ Global {EC78D91A-3E63-4CAA-8BC3-9673A30FDA45}.Debug|Any CPU.Build.0 = Debug|Any CPU {EC78D91A-3E63-4CAA-8BC3-9673A30FDA45}.Release|Any CPU.ActiveCfg = Release|Any CPU {EC78D91A-3E63-4CAA-8BC3-9673A30FDA45}.Release|Any CPU.Build.0 = Release|Any CPU + {249180EF-BFC1-9DD7-48CC-E2B9253A64E0}.Debug|Any CPU.ActiveCfg = Debug + {249180EF-BFC1-9DD7-48CC-E2B9253A64E0}.Release|Any CPU.ActiveCfg = Release + {249180EF-BFC1-9DD7-48CC-E2B9253A64E0}.Release|Any CPU.Build.0 = Release EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -28,4 +30,7 @@ Global GlobalSection(Performance) = preSolution HasPerformanceSessions = true EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal diff --git a/UnityLauncherPro/App.xaml.cs b/UnityLauncherPro/App.xaml.cs index 180b5f2..d14e416 100644 --- a/UnityLauncherPro/App.xaml.cs +++ b/UnityLauncherPro/App.xaml.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; -using System.Linq; -using System.Threading.Tasks; -using System.Windows; +using System.Windows; namespace UnityLauncherPro { diff --git a/UnityLauncherPro/Converters/LastModifiedConverter.cs b/UnityLauncherPro/Converters/LastModifiedConverter.cs index 710dd14..a0f2b45 100644 --- a/UnityLauncherPro/Converters/LastModifiedConverter.cs +++ b/UnityLauncherPro/Converters/LastModifiedConverter.cs @@ -23,5 +23,23 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu return DateTime.ParseExact((string)value, MainWindow.currentDateFormat, culture); } + } + + // just for tooltip + [ValueConversion(typeof(DateTime), typeof(String))] + public class LastModifiedConverterTooltip : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) return null; + DateTime date = (DateTime)value; + return date.ToString(MainWindow.currentDateFormat); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return DateTime.ParseExact((string)value, MainWindow.currentDateFormat, culture); + } + } } diff --git a/UnityLauncherPro/Data/MessageType.cs b/UnityLauncherPro/Data/MessageType.cs new file mode 100644 index 0000000..febb9be --- /dev/null +++ b/UnityLauncherPro/Data/MessageType.cs @@ -0,0 +1,9 @@ +namespace UnityLauncherPro.Data +{ + public enum MessageType + { + Info, + Warning, + Error + } +} diff --git a/UnityLauncherPro/Data/Project.cs b/UnityLauncherPro/Data/Project.cs index 6bf8621..5f422d3 100644 --- a/UnityLauncherPro/Data/Project.cs +++ b/UnityLauncherPro/Data/Project.cs @@ -16,6 +16,7 @@ public class Project : IValueConverter public string TargetPlatform { set; get; } // TODO rename to Platform public string[] TargetPlatforms { set; get; } public bool folderExists { set; get; } + public string SRP { set; get; } // Scriptable Render Pipeline, TODO add version info? // WPF keeps calling this method from AppendFormatHelper, GetNameCore..? not sure if need to return something else or default would be faster? public override string ToString() diff --git a/UnityLauncherPro/Data/ThemeColor.cs b/UnityLauncherPro/Data/ThemeColor.cs index 24a6232..2dcbd02 100644 --- a/UnityLauncherPro/Data/ThemeColor.cs +++ b/UnityLauncherPro/Data/ThemeColor.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel; using System.Globalization; using System.Windows.Data; using System.Windows.Media; diff --git a/UnityLauncherPro/Data/UnityInstallation.cs b/UnityLauncherPro/Data/UnityInstallation.cs index 3647bf6..c43d7f8 100644 --- a/UnityLauncherPro/Data/UnityInstallation.cs +++ b/UnityLauncherPro/Data/UnityInstallation.cs @@ -9,13 +9,10 @@ public class UnityInstallation : IValueConverter public long VersionCode { set; get; } // version as number, cached for sorting public string Path { set; get; } // exe path public DateTime? Installed { set; get; } - public string PlatformsCombined { set; get; } public string[] Platforms { set; get; } public int ProjectCount { set; get; } - public bool IsPreferred { set; get; } - public string ReleaseType { set; get; } // Alpha, Beta, LTS.. TODO could be enum // https://stackoverflow.com/a/5551986/5452781 diff --git a/UnityLauncherPro/Data/UnityVersion.cs b/UnityLauncherPro/Data/UnityVersion.cs index f616aaa..e4a0649 100644 --- a/UnityLauncherPro/Data/UnityVersion.cs +++ b/UnityLauncherPro/Data/UnityVersion.cs @@ -8,6 +8,7 @@ public class UnityVersion public string Version { get; set; } public UnityVersionStream Stream { get; set; } public DateTime ReleaseDate { get; set; } + public string directURL { get; set; } = null; // if found from unofficial releases list public static UnityVersion FromJson(string json) { diff --git a/UnityLauncherPro/GetProjects.cs b/UnityLauncherPro/GetProjects.cs index caa05a9..3011595 100644 --- a/UnityLauncherPro/GetProjects.cs +++ b/UnityLauncherPro/GetProjects.cs @@ -15,7 +15,7 @@ public static class GetProjects // convert target platform name into valid buildtarget platform name, NOTE this depends on unity version, now only 2019 and later are supported public static Dictionary remapPlatformNames = new Dictionary { { "StandaloneWindows64", "Win64" }, { "StandaloneWindows", "Win" }, { "Android", "Android" }, { "WebGL", "WebGL" } }; - public static List Scan(bool getGitBranch = false, bool getPlasticBranch = false, bool getArguments = false, bool showMissingFolders = false, bool showTargetPlatform = false, StringCollection AllProjectPaths = null, bool searchGitbranchRecursivly = false) + public static List Scan(bool getGitBranch = false, bool getPlasticBranch = false, bool getArguments = false, bool showMissingFolders = false, bool showTargetPlatform = false, StringCollection AllProjectPaths = null, bool searchGitbranchRecursively = false, bool showSRP = false) { List projectsFound = new List(); @@ -53,12 +53,12 @@ public static List Scan(bool getGitBranch = false, bool getPlasticBranc projectPath = (string)key.GetValue(valueName); } - var p = GetProjectInfo(projectPath, getGitBranch, getPlasticBranch, getArguments, showMissingFolders, showTargetPlatform, searchGitbranchRecursivly); + var p = GetProjectInfo(projectPath, getGitBranch, getPlasticBranch, getArguments, showMissingFolders, showTargetPlatform, searchGitbranchRecursively, showSRP); //Console.WriteLine(projectPath+" "+p.TargetPlatform); // if want to hide project and folder path for screenshot - //p.Title = "Example Project "; - //p.Path = "C:/Projects/ExamplePath/MyProj"; + //p.Title = "Example Project"; + //p.Path = "C:/Projects/MyProj"; if (p != null) { @@ -94,7 +94,7 @@ public static List Scan(bool getGitBranch = false, bool getPlasticBranc // if not found from registry, add to recent projects list if (found == false) { - var p = GetProjectInfo(projectPath, getGitBranch, getPlasticBranch, getArguments, showMissingFolders, showTargetPlatform, searchGitbranchRecursivly); + var p = GetProjectInfo(projectPath, getGitBranch, getPlasticBranch, getArguments, showMissingFolders, showTargetPlatform, searchGitbranchRecursively, showSRP); if (p != null) projectsFound.Add(p); } } @@ -117,11 +117,10 @@ public static List Scan(bool getGitBranch = false, bool getPlasticBranc //Console.WriteLine("Trimming projects list to " + MainWindow.maxProjectCount + " projects"); projectsFound.RemoveRange(MainWindow.maxProjectCount, projectsFound.Count - MainWindow.maxProjectCount); } - return projectsFound; } // Scan() - static Project GetProjectInfo(string projectPath, bool getGitBranch = false, bool getPlasticBranch = false, bool getArguments = false, bool showMissingFolders = false, bool showTargetPlatform = false, bool searchGitbranchRecursivly = false) + static Project GetProjectInfo(string projectPath, bool getGitBranch = false, bool getPlasticBranch = false, bool getArguments = false, bool showMissingFolders = false, bool showTargetPlatform = false, bool searchGitbranchRecursively = false, bool showSRP = false) { bool folderExists = Directory.Exists(projectPath); @@ -166,11 +165,12 @@ static Project GetProjectInfo(string projectPath, bool getGitBranch = false, boo string gitBranch = ""; if (getGitBranch == true) { - gitBranch = folderExists ? Tools.ReadGitBranchInfo(projectPath, searchGitbranchRecursivly) : null; + gitBranch = folderExists ? Tools.ReadGitBranchInfo(projectPath, searchGitbranchRecursively) : null; + // check for plastic, if enabled if (getPlasticBranch == true && gitBranch == null) { - gitBranch = folderExists ? Tools.ReadPlasticBranchInfo(projectPath) : null; + gitBranch = folderExists ? Tools.ReadPlasticBranchInfo(projectPath, searchGitbranchRecursively) : null; } } @@ -209,6 +209,9 @@ static Project GetProjectInfo(string projectPath, bool getGitBranch = false, boo // bubblegum(TM) solution, fill available platforms for this unity version, for this project p.TargetPlatforms = Tools.GetPlatformsForUnityVersion(projectVersion); p.folderExists = folderExists; + + if (showSRP == true) p.SRP = Tools.GetSRP(projectPath); + return p; } diff --git a/UnityLauncherPro/GetUnityUpdates.cs b/UnityLauncherPro/GetUnityUpdates.cs index 3c614e0..631b7e7 100644 --- a/UnityLauncherPro/GetUnityUpdates.cs +++ b/UnityLauncherPro/GetUnityUpdates.cs @@ -17,21 +17,115 @@ public static class GetUnityUpdates private const string CacheFileName = "UnityVersionCache.json"; private static readonly HttpClient Client = new HttpClient(); - public static async Task> FetchAll() + static Dictionary unofficialReleaseURLs = new Dictionary(); + + public static async Task> FetchAll(bool useUnofficialList = false) { var cachedVersions = LoadCachedVersions(); var newVersions = await FetchNewVersions(cachedVersions); var allVersions = newVersions.Concat(cachedVersions).ToList(); - if (newVersions.Count > 0) + if (useUnofficialList == true) + { + var unofficialVersions = await FetchUnofficialVersions(cachedVersions); + unofficialReleaseURLs.Clear(); + // TODO modify FetchUnofficialVersions to put items in this dictionary directlys + foreach (var version in unofficialVersions) + { + //Console.WriteLine("unofficial: " + version.Version + " , " + version.directURL); + if (unofficialReleaseURLs.ContainsKey(version.Version) == false) + { + unofficialReleaseURLs.Add(version.Version, version.directURL); + } + } + allVersions = unofficialVersions.Concat(allVersions).ToList(); + } + + if (newVersions.Count > 0 || (useUnofficialList && unofficialReleaseURLs.Count > 0)) { SaveCachedVersions(allVersions); } + return allVersions; } + public static async Task> FetchUnofficialVersions(List cachedVersions) + { + var unofficialVersions = new List(); + var existingVersions = new HashSet(cachedVersions.Select(v => v.Version)); + + try + { + string url = "https://raw.githubusercontent.com/unitycoder/UnofficialUnityReleasesWatcher/refs/heads/main/unity-releases.md"; + + var content = await Client.GetStringAsync(url); + + // Parse the Markdown content + var lines = content.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + foreach (var line in lines) + { + if (line.StartsWith("- ")) // Identify Markdown list items + { + var urlPart = line.Substring(2).Trim(); + var version = ExtractVersionFromUrl(urlPart); + + if (!string.IsNullOrEmpty(version) && !existingVersions.Contains(version)) + { + var stream = InferStreamFromVersion(version); + + unofficialVersions.Add(new UnityVersion + { + Version = version, + Stream = stream, + ReleaseDate = DateTime.Now, // NOTE not correct, but we don't have known release date for unofficial versions (its only when they are found..) + //ReleaseDate = DateTime.MinValue // Release date is unavailable in the MD format, TODO add to md as #2021-01-01 ? + directURL = urlPart, // this is available only for unofficial releases + }); + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"Error fetching unofficial versions: {ex.Message}"); + } + + return unofficialVersions; + } + + // TODO fixme, f is not always LTS + private static UnityVersionStream InferStreamFromVersion(string version) + { + if (Tools.IsAlpha(version)) return UnityVersionStream.Alpha; + if (Tools.IsBeta(version)) return UnityVersionStream.Beta; + if (Tools.IsLTS(version)) return UnityVersionStream.LTS; + + //if (version.Contains("a")) return UnityVersionStream.Alpha; + //if (version.Contains("b")) return UnityVersionStream.Beta; + //if (version.Contains("f")) return UnityVersionStream.LTS; + return UnityVersionStream.Tech; // Default to Tech if no identifier is found + } + + /// + /// Extracts the Unity version from the given URL. + /// + /// The URL to parse. + /// The Unity version string. + private static string ExtractVersionFromUrl(string url) + { + try + { + var versionStart = url.LastIndexOf('#') + 1; + return versionStart > 0 && versionStart < url.Length ? url.Substring(versionStart) : null; + } + catch + { + return null; + } + } + public static async Task FetchDownloadUrl(string unityVersion) { if (string.IsNullOrEmpty(unityVersion)) @@ -39,6 +133,7 @@ public static async Task FetchDownloadUrl(string unityVersion) return null; } + // unity release api string apiUrl = $"{BaseApiUrl}?limit=1&version={unityVersion}&architecture=X86_64&platform=WINDOWS"; try @@ -53,8 +148,37 @@ public static async Task FetchDownloadUrl(string unityVersion) } } + static string ParseHashCodeFromURL(string url) + { + // https://beta.unity3d.com/download/330fbefc18b7/download.html#6000.1.0a8 > 330fbefc18b7 + + int hashStart = url.IndexOf("download/") + 9; + int hashEnd = url.IndexOf("/download.html", hashStart); + return url.Substring(hashStart, hashEnd - hashStart); + } + private static async Task ExtractDownloadUrlAsync(string json, string unityVersion) { + //Console.WriteLine("json: " + json + " vers: " + unityVersion); + + if (json.Contains("\"results\":[]")) + { + Console.WriteLine("No results found from releases API, checking unofficial list (if enabled)"); + + if (unofficialReleaseURLs.ContainsKey(unityVersion)) + { + Console.WriteLine("Unofficial release found in the list."); + + string unityHash = ParseHashCodeFromURL(unofficialReleaseURLs[unityVersion]); + // Console.WriteLine(unityHash); + string downloadURL = Tools.ParseDownloadURLFromWebpage(unityVersion, unityHash, false, true); + // Console.WriteLine("direct download url: "+downloadURL); + return downloadURL; + } + + return null; + } + int resultsIndex = json.IndexOf("\"results\":"); if (resultsIndex == -1) return null; @@ -84,7 +208,7 @@ private static async Task ExtractDownloadUrlAsync(string json, string un if (await CheckAssistantUrl(assistantUrl)) { - Console.WriteLine("Assistant download URL found."); + //Console.WriteLine("ExtractDownloadUrlAsync: Assistant download URL found."); return assistantUrl; } else @@ -279,6 +403,7 @@ private static void SaveCachedVersions(List versions) if (configDirectory == null) return; string cacheFilePath = Path.Combine(configDirectory, CacheFileName); + //Console.WriteLine("Saving cachedrelease: " + cacheFilePath); File.WriteAllText(cacheFilePath, json); } } diff --git a/UnityLauncherPro/MainWindow.xaml b/UnityLauncherPro/MainWindow.xaml index bf95317..49083d2 100644 --- a/UnityLauncherPro/MainWindow.xaml +++ b/UnityLauncherPro/MainWindow.xaml @@ -7,9 +7,10 @@ xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:converters="clr-namespace:UnityLauncherPro.Converters" x:Class="UnityLauncherPro.MainWindow" mc:Ignorable="d" - Title="UnityLauncherPro" Height="650" Width="880" WindowStartupLocation="CenterScreen" Background="{DynamicResource ThemeDarkestBackground}" MinWidth="780" MinHeight="650" AllowsTransparency="True" WindowStyle="None" Margin="0" KeyDown="OnWindowKeyDown" Closing="Window_Closing" SizeChanged="Window_SizeChanged" Icon="Images/icon.ico" SourceInitialized="Window_SourceInitialized" MouseDown="Window_MouseDown"> + Title="UnityLauncherPro" Height="670" Width="880" WindowStartupLocation="CenterScreen" Background="{DynamicResource ThemeDarkestBackground}" MinWidth="780" MinHeight="650" AllowsTransparency="True" WindowStyle="None" Margin="0" KeyDown="OnWindowKeyDown" Closing="Window_Closing" SizeChanged="Window_SizeChanged" Icon="Images/icon.ico" SourceInitialized="Window_SourceInitialized" MouseDown="Window_MouseDown"> + @@ -30,7 +31,7 @@ - +