diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp index 4299b8865..2c5265a06 100644 --- a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -10,12 +10,24 @@ #if PLATFORM_WINDOWS +#include "Engine/Core/Collections/Sorting.h" #include "Engine/Platform/File.h" #include "Engine/Platform/Win32/IncludeWindowsHeaders.h" #include "Engine/Serialization/Json.h" namespace { + struct RiderInstallation + { + String path; + String version; + + RiderInstallation(const String& path_, const String& version_) + : path(path_), version(version_) + { + } + }; + bool FindRegistryKeyItems(HKEY hKey, Array& results) { Char nameBuffer[256]; @@ -31,7 +43,7 @@ namespace return true; } - void SearchDirectory(Array* output, const String& directory) + void SearchDirectory(Array* installations, const String& directory) { if (!FileSystem::DirectoryExists(directory)) return; @@ -46,24 +58,29 @@ namespace if (document.HasParseError()) return; + // Find version + auto versionMember = document.FindMember("version"); + if (versionMember == document.MemberEnd()) + return; + // Find executable file path auto launchMember = document.FindMember("launch"); - if (launchMember != document.MemberEnd() && launchMember->value.IsArray() && launchMember->value.Size() > 0) - { - auto launcherPathMember = launchMember->value[0].FindMember("launcherPath"); - if (launcherPathMember != launchMember->value[0].MemberEnd()) - { - auto launcherPath = launcherPathMember->value.GetText(); - auto exePath = directory / launcherPath; - if (launcherPath.HasChars() && FileSystem::FileExists(exePath)) - { - output->Add(New(exePath)); - } - } - } + if (launchMember == document.MemberEnd() || !launchMember->value.IsArray() || launchMember->value.Size() == 0) + return; + + auto launcherPathMember = launchMember->value[0].FindMember("launcherPath"); + if (launcherPathMember == launchMember->value[0].MemberEnd()) + return; + + auto launcherPath = launcherPathMember->value.GetText(); + auto exePath = directory / launcherPath; + if (!launcherPath.HasChars() || !FileSystem::FileExists(exePath)) + return; + + installations->Add(New(exePath, versionMember->value.GetText())); } - void SearchRegistry(Array* output, HKEY root, const Char* key) + void SearchRegistry(Array* installations, HKEY root, const Char* key, const Char* valueName = TEXT("")) { // Open key HKEY keyH; @@ -83,14 +100,14 @@ namespace // Read subkey value DWORD type; DWORD cbData; - if (RegQueryValueExW(subKeyH, TEXT(""), nullptr, &type, nullptr, &cbData) != ERROR_SUCCESS || type != REG_SZ) + if (RegQueryValueExW(subKeyH, valueName, nullptr, &type, nullptr, &cbData) != ERROR_SUCCESS || type != REG_SZ) { RegCloseKey(subKeyH); continue; } Array data; data.Resize((int32)cbData / sizeof(Char)); - if (RegQueryValueExW(subKeyH, TEXT(""), nullptr, nullptr, reinterpret_cast(data.Get()), &cbData) != ERROR_SUCCESS) + if (RegQueryValueExW(subKeyH, valueName, nullptr, nullptr, reinterpret_cast(data.Get()), &cbData) != ERROR_SUCCESS) { RegCloseKey(subKeyH); continue; @@ -98,7 +115,7 @@ namespace // Check if it's a valid installation path String path(data.Get(), data.Count() - 1); - SearchDirectory(output, path); + SearchDirectory(installations, path); RegCloseKey(subKeyH); } @@ -108,6 +125,31 @@ namespace } } +bool sortInstallations(RiderInstallation* const& i1, RiderInstallation* const& i2) +{ + Array values1, values2; + i1->version.Split('.', values1); + i2->version.Split('.', values2); + + int32 version1[3] = { 0 }; + int32 version2[3] = { 0 }; + StringUtils::Parse(values1[0].Get(), &version1[0]); + StringUtils::Parse(values1[1].Get(), &version1[1]); + StringUtils::Parse(values1[2].Get(), &version1[2]); + StringUtils::Parse(values2[0].Get(), &version2[0]); + StringUtils::Parse(values2[1].Get(), &version2[1]); + StringUtils::Parse(values2[2].Get(), &version2[2]); + + // Compare by MAJOR.MINOR.BUILD + if (version1[0] == version2[0]) + { + if (version1[1] == version2[1]) + return version1[2] > version2[2]; + return version1[1] > version2[1]; + } + return version1[0] > version2[0]; +} + #endif RiderCodeEditor::RiderCodeEditor(const String& execPath) @@ -119,8 +161,23 @@ RiderCodeEditor::RiderCodeEditor(const String& execPath) void RiderCodeEditor::FindEditors(Array* output) { #if PLATFORM_WINDOWS - SearchRegistry(output, HKEY_CURRENT_USER, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider")); - SearchRegistry(output, HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider")); + Array installations; + + // For versions 2021 or later + SearchRegistry(&installations, HKEY_CURRENT_USER, TEXT("SOFTWARE\\JetBrains\\Rider"), TEXT("InstallDir")); + + // For versions 2020 or earlier + SearchRegistry(&installations, HKEY_CURRENT_USER, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider")); + SearchRegistry(&installations, HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider")); + + // Sort found installations by version number + Sorting::QuickSort(installations.Get(), installations.Count(), &sortInstallations); + + for (RiderInstallation* installation : installations) + { + output->Add(New(installation->path)); + Delete(installation); + } #endif }