diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6ffb7f6dee..c2fec3b072 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1272,49 +1272,48 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Make sure we don't time out during slow operations at startup updateHeartbeat(); - // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. - // The value will be 0 if the user blew away settings this session, which is both a feature and a bug. - static const QString TESTER = "HIFI_TESTER"; - auto gpuIdent = GPUIdent::getInstance(); - auto glContextData = getGLContextData(); - QJsonObject properties = { - { "version", applicationVersion() }, - { "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) }, - { "previousSessionCrashed", _previousSessionCrashed }, - { "previousSessionRuntime", sessionRunTime.get() }, - { "cpu_architecture", QSysInfo::currentCpuArchitecture() }, - { "kernel_type", QSysInfo::kernelType() }, - { "kernel_version", QSysInfo::kernelVersion() }, - { "os_type", QSysInfo::productType() }, - { "os_version", QSysInfo::productVersion() }, - { "gpu_name", gpuIdent->getName() }, - { "gpu_driver", gpuIdent->getDriver() }, - { "gpu_memory", static_cast(gpuIdent->getMemory()) }, - { "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) }, - { "gl_version", glContextData["version"] }, - { "gl_vender", glContextData["vendor"] }, - { "gl_sl_version", glContextData["sl_version"] }, - { "gl_renderer", glContextData["renderer"] }, - { "ideal_thread_count", QThread::idealThreadCount() } - }; - auto macVersion = QSysInfo::macVersion(); - if (macVersion != QSysInfo::MV_None) { - properties["os_osx_version"] = QSysInfo::macVersion(); - } - auto windowsVersion = QSysInfo::windowsVersion(); - if (windowsVersion != QSysInfo::WV_None) { - properties["os_win_version"] = QSysInfo::windowsVersion(); + constexpr auto INSTALLER_INI_NAME = "installer.ini"; + auto iniPath = QDir(applicationDirPath()).filePath(INSTALLER_INI_NAME); + QFile installerFile { iniPath }; + std::unordered_map installerKeyValues; + if (installerFile.open(QIODevice::ReadOnly)) { + while (!installerFile.atEnd()) { + auto line = installerFile.readLine(); + if (!line.isEmpty()) { + auto index = line.indexOf("="); + if (index >= 0) { + installerKeyValues[line.mid(0, index).trimmed()] = line.mid(index + 1).trimmed(); + } + } + } } - ProcessorInfo procInfo; - if (getProcessorInfo(procInfo)) { - properties["processor_core_count"] = procInfo.numProcessorCores; - properties["logical_processor_count"] = procInfo.numLogicalProcessors; - properties["processor_l1_cache_count"] = procInfo.numProcessorCachesL1; - properties["processor_l2_cache_count"] = procInfo.numProcessorCachesL2; - properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3; + // In practice we shouldn't run across installs that don't have a known installer type. + // Client or Client+Server installs should always have the installer.ini next to their + // respective interface.exe, and Steam installs will be detected as such. If a user were + // to delete the installer.ini, though, and as an example, we won't know the context of the + // original install. + constexpr auto INSTALLER_KEY_TYPE = "type"; + constexpr auto INSTALLER_KEY_CAMPAIGN = "campaign"; + constexpr auto INSTALLER_TYPE_UNKNOWN = "unknown"; + constexpr auto INSTALLER_TYPE_STEAM = "steam"; + + auto typeIt = installerKeyValues.find(INSTALLER_KEY_TYPE); + QString installerType = INSTALLER_TYPE_UNKNOWN; + if (typeIt == installerKeyValues.end()) { + if (property(hifi::properties::STEAM).toBool()) { + installerType = INSTALLER_TYPE_STEAM; + } + } else { + installerType = typeIt->second; } + auto campaignIt = installerKeyValues.find(INSTALLER_KEY_CAMPAIGN); + QString installerCampaign = campaignIt != installerKeyValues.end() ? campaignIt->second : ""; + + qDebug() << "Detected installer type:" << installerType; + qDebug() << "Detected installer campaign:" << installerCampaign; + // add firstRun flag from settings to launch event Setting::Handle firstRun { Settings::firstRun, true }; @@ -1336,6 +1335,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QJsonObject properties = { { "version", applicationVersion() }, { "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) }, + { "installer_campaign", installerCampaign }, + { "installer_type", installerType }, { "previousSessionCrashed", _previousSessionCrashed }, { "previousSessionRuntime", sessionRunTime.get() }, { "cpu_architecture", QSysInfo::currentCpuArchitecture() }, @@ -1654,7 +1655,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo const QString HIFI_NO_UPDATER_COMMAND_LINE_KEY = "--no-updater"; bool noUpdater = arguments().indexOf(HIFI_NO_UPDATER_COMMAND_LINE_KEY) != -1; if (!noUpdater) { + constexpr auto INSTALLER_TYPE_CLIENT_ONLY = "client_only"; + auto applicationUpdater = DependencyManager::get(); + + AutoUpdater::InstallerType type = installerType == INSTALLER_TYPE_CLIENT_ONLY + ? AutoUpdater::InstallerType::CLIENT_ONLY : AutoUpdater::InstallerType::FULL; + + applicationUpdater->setInstallerType(type); + applicationUpdater->setInstallerCampaign(installerCampaign); connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); applicationUpdater->checkForUpdate(); } diff --git a/libraries/auto-updater/src/AutoUpdater.cpp b/libraries/auto-updater/src/AutoUpdater.cpp index 43563b1d71..e58ac067a6 100644 --- a/libraries/auto-updater/src/AutoUpdater.cpp +++ b/libraries/auto-updater/src/AutoUpdater.cpp @@ -13,6 +13,7 @@ #include #include +#include AutoUpdater::AutoUpdater() { #if defined Q_OS_WIN32 @@ -43,63 +44,114 @@ void AutoUpdater::parseLatestVersionData() { QNetworkReply* sender = qobject_cast(QObject::sender()); QXmlStreamReader xml(sender); + + struct InstallerURLs { + QString full; + QString clientOnly; + }; - int version; + int version { 0 }; QString downloadUrl; QString releaseTime; QString releaseNotes; QString commitSha; QString pullRequestNumber; - while (!xml.atEnd() && !xml.hasError()) { - if (xml.name().toString() == "project" && - xml.attributes().hasAttribute("name") && - xml.attributes().value("name").toString() == "interface") { - xml.readNext(); - - while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "project") { - if (xml.name().toString() == "platform" && + while (xml.readNextStartElement()) { + if (xml.name() == "projects") { + while (xml.readNextStartElement()) { + if (xml.name().toString() == "project" && xml.attributes().hasAttribute("name") && - xml.attributes().value("name").toString() == _operatingSystem) { - xml.readNext(); - while (!xml.atEnd() && !xml.hasError() && - xml.name().toString() != "platform") { - - if (xml.name().toString() == "build" && xml.tokenType() != QXmlStreamReader::EndElement) { - xml.readNext(); - version = xml.readElementText().toInt(); - xml.readNext(); - downloadUrl = xml.readElementText(); - xml.readNext(); - releaseTime = xml.readElementText(); - xml.readNext(); - if (xml.name().toString() == "notes" && xml.tokenType() != QXmlStreamReader::EndElement) { - xml.readNext(); - while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "notes") { - if (xml.name().toString() == "note" && xml.tokenType() != QXmlStreamReader::EndElement) { - releaseNotes = releaseNotes + "\n" + xml.readElementText(); + xml.attributes().value("name").toString() == "interface") { + + while (xml.readNextStartElement()) { + + if (xml.name().toString() == "platform" && + xml.attributes().hasAttribute("name") && + xml.attributes().value("name").toString() == _operatingSystem) { + + while (xml.readNextStartElement()) { + if (xml.name() == "build") { + QHash campaignInstallers; + + while (xml.readNextStartElement()) { + if (xml.name() == "version") { + version = xml.readElementText().toInt(); + } else if (xml.name() == "url") { + downloadUrl = xml.readElementText(); + } else if (xml.name() == "installers") { + while (xml.readNextStartElement()) { + QString campaign = xml.name().toString(); + QString full; + QString clientOnly; + while (xml.readNextStartElement()) { + if (xml.name() == "full") { + full = xml.readElementText(); + } else if (xml.name() == "client_only") { + clientOnly = xml.readElementText(); + } else { + xml.skipCurrentElement(); + } + } + campaignInstallers[campaign] = { full, clientOnly }; + } + } else if (xml.name() == "timestamp") { + releaseTime = xml.readElementText(); + } else if (xml.name() == "notes") { + while (xml.readNextStartElement()) { + if (xml.name() == "note") { + releaseNotes = releaseNotes + "\n" + xml.readElementText(); + } else { + xml.skipCurrentElement(); + } + } + } else if (xml.name() == "sha") { + commitSha = xml.readElementText(); + } else if (xml.name() == "pull_request") { + pullRequestNumber = xml.readElementText(); + } else { + xml.skipCurrentElement(); + } } - xml.readNext(); + + static const QString DEFAULT_INSTALLER_CAMPAIGN_NAME = "standard"; + for (auto& campaign : { _installerCampaign, DEFAULT_INSTALLER_CAMPAIGN_NAME }) { + auto it = campaignInstallers.find(campaign); + if (it != campaignInstallers.end()) { + auto& urls = *it; + if (_installerType == InstallerType::CLIENT_ONLY) { + if (!urls.clientOnly.isEmpty()) { + downloadUrl = urls.clientOnly; + break; + } + } else { + if (!urls.full.isEmpty()) { + downloadUrl = urls.full; + break; + } + } + } + } + + appendBuildData(version, downloadUrl, releaseTime, releaseNotes, pullRequestNumber); + releaseNotes = ""; + } else { + xml.skipCurrentElement(); } } - xml.readNext(); - commitSha = xml.readElementText(); - xml.readNext(); - pullRequestNumber = xml.readElementText(); - appendBuildData(version, downloadUrl, releaseTime, releaseNotes, pullRequestNumber); - releaseNotes = ""; + } else { + xml.skipCurrentElement(); } - - xml.readNext(); } + } else { + xml.skipCurrentElement(); } - xml.readNext(); } - } else { - xml.readNext(); + xml.skipCurrentElement(); } } + sender->deleteLater(); emit latestVersionDataParsed(); } diff --git a/libraries/auto-updater/src/AutoUpdater.h b/libraries/auto-updater/src/AutoUpdater.h index 1e62ce0283..f56d7993e9 100644 --- a/libraries/auto-updater/src/AutoUpdater.h +++ b/libraries/auto-updater/src/AutoUpdater.h @@ -36,10 +36,17 @@ class AutoUpdater : public QObject, public Dependency { public: AutoUpdater(); + + enum class InstallerType { + CLIENT_ONLY = 0, + FULL + }; void checkForUpdate(); const QMap>& getBuildData() { return _builds; } void performAutoUpdate(int version); + void setInstallerType(InstallerType type) { _installerType = type; } + void setInstallerCampaign(QString campaign) { _installerCampaign = campaign; } signals: void latestVersionDataParsed(); @@ -49,6 +56,8 @@ signals: private: QMap> _builds; QString _operatingSystem; + InstallerType _installerType { InstallerType::FULL }; + QString _installerCampaign { "" }; void getLatestVersionData(); void downloadUpdateVersion(int version);