feat: block launch if Java is incompatible

Keep track of compatible Java versions from meta. Launch-step
VerifyJavaInstall will check if current instance's Java version is
compatible.
Also add override option both globally and per-instance in-case the user
doesn't care about the requirement.
This commit is contained in:
Sefa Eyeoglu 2022-03-24 14:47:22 +01:00
parent c7fdfb8116
commit 82c35f2746
No known key found for this signature in database
GPG Key ID: C10411294912A422
13 changed files with 93 additions and 41 deletions

View File

@ -682,6 +682,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("JavaVendor", ""); m_settings->registerSetting("JavaVendor", "");
m_settings->registerSetting("LastHostname", ""); m_settings->registerSetting("LastHostname", "");
m_settings->registerSetting("JvmArgs", ""); m_settings->registerSetting("JvmArgs", "");
m_settings->registerSetting("IgnoreJavaCompatibility", false);
// Native library workarounds // Native library workarounds
m_settings->registerSetting("UseNativeOpenAL", false); m_settings->registerSetting("UseNativeOpenAL", false);

View File

@ -126,6 +126,11 @@ void LaunchProfile::applyMods(const QList<LibraryPtr>& mods)
} }
} }
void LaunchProfile::applyCompatibleJavaMajors(QList<int>& javaMajor)
{
m_compatibleJavaMajors.append(javaMajor);
}
void LaunchProfile::applyLibrary(LibraryPtr library) void LaunchProfile::applyLibrary(LibraryPtr library)
{ {
if(!library->isActive()) if(!library->isActive())
@ -275,6 +280,11 @@ const QList<LibraryPtr> & LaunchProfile::getMavenFiles() const
return m_mavenFiles; return m_mavenFiles;
} }
const QList<int> & LaunchProfile::getCompatibleJavaMajors() const
{
return m_compatibleJavaMajors;
}
void LaunchProfile::getLibraryFiles( void LaunchProfile::getLibraryFiles(
const QString& architecture, const QString& architecture,
QStringList& jars, QStringList& jars,

View File

@ -21,6 +21,7 @@ public: /* application of profile variables from patches */
void applyMods(const QList<LibraryPtr> &jarMods); void applyMods(const QList<LibraryPtr> &jarMods);
void applyLibrary(LibraryPtr library); void applyLibrary(LibraryPtr library);
void applyMavenFile(LibraryPtr library); void applyMavenFile(LibraryPtr library);
void applyCompatibleJavaMajors(QList<int>& javaMajor);
void applyMainJar(LibraryPtr jar); void applyMainJar(LibraryPtr jar);
void applyProblemSeverity(ProblemSeverity severity); void applyProblemSeverity(ProblemSeverity severity);
/// clear the profile /// clear the profile
@ -39,6 +40,7 @@ public: /* getters for profile variables */
const QList<LibraryPtr> & getLibraries() const; const QList<LibraryPtr> & getLibraries() const;
const QList<LibraryPtr> & getNativeLibraries() const; const QList<LibraryPtr> & getNativeLibraries() const;
const QList<LibraryPtr> & getMavenFiles() const; const QList<LibraryPtr> & getMavenFiles() const;
const QList<int> & getCompatibleJavaMajors() const;
const LibraryPtr getMainJar() const; const LibraryPtr getMainJar() const;
void getLibraryFiles( void getLibraryFiles(
const QString & architecture, const QString & architecture,
@ -99,6 +101,9 @@ private:
/// the list of mods /// the list of mods
QList<LibraryPtr> m_mods; QList<LibraryPtr> m_mods;
/// compatible java major versions
QList<int> m_compatibleJavaMajors;
ProblemSeverity m_problemSeverity = ProblemSeverity::None; ProblemSeverity m_problemSeverity = ProblemSeverity::None;
}; };

View File

@ -88,6 +88,7 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
m_settings->registerOverride(globalSettings->getSetting("JavaPath"), javaOrLocation); m_settings->registerOverride(globalSettings->getSetting("JavaPath"), javaOrLocation);
m_settings->registerOverride(globalSettings->getSetting("JvmArgs"), javaOrArgs); m_settings->registerOverride(globalSettings->getSetting("JvmArgs"), javaOrArgs);
m_settings->registerOverride(globalSettings->getSetting("IgnoreJavaCompatibility"), javaOrLocation);
// special! // special!
m_settings->registerPassthrough(globalSettings->getSetting("JavaTimestamp"), javaOrLocation); m_settings->registerPassthrough(globalSettings->getSetting("JavaTimestamp"), javaOrLocation);

View File

@ -183,6 +183,15 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFi
); );
} }
} }
if (in.contains("compatibleJavaMajors"))
{
for (auto compatible : requireArray(in.value("compatibleJavaMajors")))
{
out->compatibleJavaMajors.append(requireInteger(compatible));
}
}
if(in.contains("downloads")) if(in.contains("downloads"))
{ {
auto downloadsObj = requireObject(in, "downloads"); auto downloadsObj = requireObject(in, "downloads");

View File

@ -36,6 +36,7 @@ void VersionFile::applyTo(LaunchProfile *profile)
profile->applyJarMods(jarMods); profile->applyJarMods(jarMods);
profile->applyMods(mods); profile->applyMods(mods);
profile->applyTraits(traits); profile->applyTraits(traits);
profile->applyCompatibleJavaMajors(compatibleJavaMajors);
for (auto library : libraries) for (auto library : libraries)
{ {

View File

@ -57,6 +57,9 @@ public: /* data */
/// Mojang: Minecraft launch arguments (may contain placeholders for variable substitution) /// Mojang: Minecraft launch arguments (may contain placeholders for variable substitution)
QString minecraftArguments; QString minecraftArguments;
/// Mojang: list of compatible java majors
QList<int> compatibleJavaMajors;
/// Mojang: type of the Minecraft version /// Mojang: type of the Minecraft version
QString type; QString type;

View File

@ -1,50 +1,40 @@
#include "VerifyJavaInstall.h" #include "VerifyJavaInstall.h"
#include <launch/LaunchTask.h> #include "java/JavaVersion.h"
#include <minecraft/MinecraftInstance.h> #include "minecraft/PackProfile.h"
#include <minecraft/PackProfile.h> #include "minecraft/MinecraftInstance.h"
#include <minecraft/VersionFilterData.h>
#ifdef major
#undef major
#endif
#ifdef minor
#undef minor
#endif
void VerifyJavaInstall::executeTask() { void VerifyJavaInstall::executeTask() {
auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance()); auto instance = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
auto packProfile = instance->getPackProfile();
auto settings = instance->settings();
auto storedVersion = settings->get("JavaVersion").toString();
auto ignoreCompatibility = settings->get("IgnoreJavaCompatibility").toBool();
auto javaVersion = m_inst->getJavaVersion(); auto compatibleMajors = packProfile->getProfile()->getCompatibleJavaMajors();
auto minecraftComponent = m_inst->getPackProfile()->getComponent("net.minecraft");
// Java 17 requirement JavaVersion javaVersion(storedVersion);
if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java17BeginsDate) {
if (javaVersion.major() < 17) {
emit logLine("Minecraft 1.18 Pre Release 2 and above require the use of Java 17",
MessageLevel::Fatal);
emitFailed(tr("Minecraft 1.18 Pre Release 2 and above require the use of Java 17"));
return;
}
}
// Java 16 requirement
else if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java16BeginsDate) {
if (javaVersion.major() < 16) {
emit logLine("Minecraft 21w19a and above require the use of Java 16",
MessageLevel::Fatal);
emitFailed(tr("Minecraft 21w19a and above require the use of Java 16"));
return;
}
}
// Java 8 requirement
else if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java8BeginsDate) {
if (javaVersion.major() < 8) {
emit logLine("Minecraft 17w13a and above require the use of Java 8",
MessageLevel::Fatal);
emitFailed(tr("Minecraft 17w13a and above require the use of Java 8"));
return;
}
}
if (compatibleMajors.isEmpty() || compatibleMajors.contains(javaVersion.major()))
{
emitSucceeded(); emitSucceeded();
return;
}
if (ignoreCompatibility)
{
emit logLine(tr("Java major version is incompatible. Things might break."), MessageLevel::Warning);
emitSucceeded();
return;
}
emit logLine(tr("Instance not compatible with Java major version %1.\n"
"Switch the Java version of this instance to one of the following:").arg(javaVersion.major()),
MessageLevel::Error);
for (auto major: compatibleMajors)
{
emit logLine(tr("Java %1").arg(major), MessageLevel::Error);
}
emitFailed(QString("Incompatible Java major version"));
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <launch/LaunchStep.h> #include <launch/LaunchStep.h>
#include <launch/LaunchTask.h>
class VerifyJavaInstall : public LaunchStep { class VerifyJavaInstall : public LaunchStep {
Q_OBJECT Q_OBJECT

View File

@ -95,6 +95,7 @@ void JavaPage::applySettings()
// Java Settings // Java Settings
s->set("JavaPath", ui->javaPathTextBox->text()); s->set("JavaPath", ui->javaPathTextBox->text());
s->set("JvmArgs", ui->jvmArgsTextBox->text()); s->set("JvmArgs", ui->jvmArgsTextBox->text());
s->set("IgnoreJavaCompatibility", ui->skipCompatibilityCheckbox->isChecked());
JavaCommon::checkJVMArgs(s->get("JvmArgs").toString(), this->parentWidget()); JavaCommon::checkJVMArgs(s->get("JvmArgs").toString(), this->parentWidget());
} }
void JavaPage::loadSettings() void JavaPage::loadSettings()
@ -118,6 +119,7 @@ void JavaPage::loadSettings()
// Java Settings // Java Settings
ui->javaPathTextBox->setText(s->get("JavaPath").toString()); ui->javaPathTextBox->setText(s->get("JavaPath").toString());
ui->jvmArgsTextBox->setText(s->get("JvmArgs").toString()); ui->jvmArgsTextBox->setText(s->get("JvmArgs").toString());
ui->skipCompatibilityCheckbox->setChecked(s->get("IgnoreJavaCompatibility").toBool());
} }
void JavaPage::on_javaDetectBtn_clicked() void JavaPage::on_javaDetectBtn_clicked()

View File

@ -222,6 +222,22 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1">
<widget class="QCheckBox" name="skipCompatibilityCheckbox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>If enabled, the launcher will not check if an instance is compatible with the selected Java version.</string>
</property>
<property name="text">
<string>Skip Java compatibility checks</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -165,10 +165,12 @@ void InstanceSettingsPage::applySettings()
if (javaInstall) if (javaInstall)
{ {
m_settings->set("JavaPath", ui->javaPathTextBox->text()); m_settings->set("JavaPath", ui->javaPathTextBox->text());
m_settings->set("IgnoreJavaCompatibility", ui->skipCompatibilityCheckbox->isChecked());
} }
else else
{ {
m_settings->reset("JavaPath"); m_settings->reset("JavaPath");
m_settings->reset("IgnoreJavaCompatibility");
} }
// Java arguments // Java arguments
@ -286,6 +288,7 @@ void InstanceSettingsPage::loadSettings()
ui->javaSettingsGroupBox->setChecked(overrideLocation); ui->javaSettingsGroupBox->setChecked(overrideLocation);
ui->javaPathTextBox->setText(m_settings->get("JavaPath").toString()); ui->javaPathTextBox->setText(m_settings->get("JavaPath").toString());
ui->skipCompatibilityCheckbox->setChecked(m_settings->get("IgnoreJavaCompatibility").toBool());
ui->javaArgumentsGroupBox->setChecked(overrideArgs); ui->javaArgumentsGroupBox->setChecked(overrideArgs);
ui->jvmArgsTextBox->setPlainText(m_settings->get("JvmArgs").toString()); ui->jvmArgsTextBox->setPlainText(m_settings->get("JvmArgs").toString());

View File

@ -85,6 +85,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QCheckBox" name="skipCompatibilityCheckbox">
<property name="toolTip">
<string>If enabled, the launcher will not check if an instance is compatible with the selected Java version.</string>
</property>
<property name="text">
<string>Skip Java compatibility checks</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>