mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2025-06-13 05:37:42 +02:00
Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into metadata2
This commit is contained in:
@ -283,8 +283,7 @@ Net::NetRequest::Ptr AssetObject::getDownloadAction()
|
||||
if ((!objectFile.isFile()) || (objectFile.size() != size)) {
|
||||
auto objectDL = Net::ApiDownload::makeFile(getUrl(), objectFile.filePath());
|
||||
if (hash.size()) {
|
||||
auto rawHash = QByteArray::fromHex(hash.toLatin1());
|
||||
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
|
||||
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, hash));
|
||||
}
|
||||
objectDL->setProgress(objectDL->getProgress(), size);
|
||||
return objectDL;
|
||||
|
@ -56,18 +56,6 @@ Component::Component(PackProfile* parent, const QString& uid)
|
||||
m_uid = uid;
|
||||
}
|
||||
|
||||
Component::Component(PackProfile* parent, std::shared_ptr<Meta::Version> version)
|
||||
{
|
||||
assert(parent);
|
||||
m_parent = parent;
|
||||
|
||||
m_metaVersion = version;
|
||||
m_uid = version->uid();
|
||||
m_version = m_cachedVersion = version->version();
|
||||
m_cachedName = version->name();
|
||||
m_loaded = version->isLoaded();
|
||||
}
|
||||
|
||||
Component::Component(PackProfile* parent, const QString& uid, std::shared_ptr<VersionFile> file)
|
||||
{
|
||||
assert(parent);
|
||||
@ -102,9 +90,6 @@ void Component::applyTo(LaunchProfile* profile)
|
||||
std::shared_ptr<class VersionFile> Component::getVersionFile() const
|
||||
{
|
||||
if (m_metaVersion) {
|
||||
if (!m_metaVersion->isLoaded()) {
|
||||
m_metaVersion->load(Net::Mode::Online);
|
||||
}
|
||||
return m_metaVersion->data();
|
||||
} else {
|
||||
return m_file;
|
||||
@ -131,29 +116,35 @@ int Component::getOrder()
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Component::setOrder(int order)
|
||||
{
|
||||
m_orderOverride = true;
|
||||
m_order = order;
|
||||
}
|
||||
|
||||
QString Component::getID()
|
||||
{
|
||||
return m_uid;
|
||||
}
|
||||
|
||||
QString Component::getName()
|
||||
{
|
||||
if (!m_cachedName.isEmpty())
|
||||
return m_cachedName;
|
||||
return m_uid;
|
||||
}
|
||||
|
||||
QString Component::getVersion()
|
||||
{
|
||||
return m_cachedVersion;
|
||||
}
|
||||
|
||||
QString Component::getFilename()
|
||||
{
|
||||
return m_parent->patchFilePathForUid(m_uid);
|
||||
}
|
||||
|
||||
QDateTime Component::getReleaseDateTime()
|
||||
{
|
||||
if (m_metaVersion) {
|
||||
@ -198,17 +189,14 @@ bool Component::isCustom()
|
||||
|
||||
bool Component::isCustomizable()
|
||||
{
|
||||
if (m_metaVersion) {
|
||||
if (getVersionFile()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return m_metaVersion && getVersionFile();
|
||||
}
|
||||
|
||||
bool Component::isRemovable()
|
||||
{
|
||||
return !m_important;
|
||||
}
|
||||
|
||||
bool Component::isRevertible()
|
||||
{
|
||||
if (isCustom()) {
|
||||
@ -218,18 +206,18 @@ bool Component::isRevertible()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Component::isMoveable()
|
||||
{
|
||||
// HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Component::isVersionChangeable()
|
||||
{
|
||||
auto list = getVersionList();
|
||||
if (list) {
|
||||
if (!list->isLoaded()) {
|
||||
list->load(Net::Mode::Online);
|
||||
}
|
||||
list->waitToLoad();
|
||||
return list->count() != 0;
|
||||
}
|
||||
return false;
|
||||
|
@ -22,7 +22,6 @@ class Component : public QObject, public ProblemProvider {
|
||||
Component(PackProfile* parent, const QString& uid);
|
||||
|
||||
// DEPRECATED: remove these constructors?
|
||||
Component(PackProfile* parent, std::shared_ptr<Meta::Version> version);
|
||||
Component(PackProfile* parent, const QString& uid, std::shared_ptr<VersionFile> file);
|
||||
|
||||
virtual ~Component() {}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
/*
|
||||
* This is responsible for loading the components of a component list AND resolving dependency issues between them
|
||||
@ -93,9 +94,9 @@ static LoadResult loadComponent(ComponentPtr component, Task::Ptr& loadTask, Net
|
||||
component->m_loaded = true;
|
||||
result = LoadResult::LoadedLocal;
|
||||
} else {
|
||||
metaVersion->load(netmode);
|
||||
loadTask = metaVersion->getCurrentTask();
|
||||
if (loadTask)
|
||||
loadTask = APPLICATION->metadataIndex()->loadVersion(component->m_uid, component->m_version, netmode);
|
||||
loadTask->start();
|
||||
if (netmode == Net::Mode::Online)
|
||||
result = LoadResult::RequiresRemote;
|
||||
else if (metaVersion->isLoaded())
|
||||
result = LoadResult::LoadedLocal;
|
||||
@ -133,21 +134,6 @@ static LoadResult loadPackProfile(ComponentPtr component, Task::Ptr& loadTask, N
|
||||
}
|
||||
*/
|
||||
|
||||
static LoadResult loadIndex(Task::Ptr& loadTask, Net::Mode netmode)
|
||||
{
|
||||
// FIXME: DECIDE. do we want to run the update task anyway?
|
||||
if (APPLICATION->metadataIndex()->isLoaded()) {
|
||||
qDebug() << "Index is already loaded";
|
||||
return LoadResult::LoadedLocal;
|
||||
}
|
||||
APPLICATION->metadataIndex()->load(netmode);
|
||||
loadTask = APPLICATION->metadataIndex()->getCurrentTask();
|
||||
if (loadTask) {
|
||||
return LoadResult::RequiresRemote;
|
||||
}
|
||||
// FIXME: this is assuming the load succeeded... did it really?
|
||||
return LoadResult::LoadedLocal;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ComponentUpdateTask::loadComponents()
|
||||
@ -156,23 +142,7 @@ void ComponentUpdateTask::loadComponents()
|
||||
size_t taskIndex = 0;
|
||||
size_t componentIndex = 0;
|
||||
d->remoteLoadSuccessful = true;
|
||||
// load the main index (it is needed to determine if components can revert)
|
||||
{
|
||||
// FIXME: tear out as a method? or lambda?
|
||||
Task::Ptr indexLoadTask;
|
||||
auto singleResult = loadIndex(indexLoadTask, d->netmode);
|
||||
result = composeLoadResult(result, singleResult);
|
||||
if (indexLoadTask) {
|
||||
qDebug() << "Remote loading is being run for metadata index";
|
||||
RemoteLoadStatus status;
|
||||
status.type = RemoteLoadStatus::Type::Index;
|
||||
d->remoteLoadStatusList.append(status);
|
||||
connect(indexLoadTask.get(), &Task::succeeded, [=]() { remoteLoadSucceeded(taskIndex); });
|
||||
connect(indexLoadTask.get(), &Task::failed, [=](const QString& error) { remoteLoadFailed(taskIndex, error); });
|
||||
connect(indexLoadTask.get(), &Task::aborted, [=]() { remoteLoadFailed(taskIndex, tr("Aborted")); });
|
||||
taskIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// load all the components OR their lists...
|
||||
for (auto component : d->m_list->d->components) {
|
||||
Task::Ptr loadTask;
|
||||
@ -206,12 +176,13 @@ void ComponentUpdateTask::loadComponents()
|
||||
result = composeLoadResult(result, singleResult);
|
||||
if (loadTask) {
|
||||
qDebug() << "Remote loading is being run for" << component->getName();
|
||||
connect(loadTask.get(), &Task::succeeded, [=]() { remoteLoadSucceeded(taskIndex); });
|
||||
connect(loadTask.get(), &Task::failed, [=](const QString& error) { remoteLoadFailed(taskIndex, error); });
|
||||
connect(loadTask.get(), &Task::aborted, [=]() { remoteLoadFailed(taskIndex, tr("Aborted")); });
|
||||
connect(loadTask.get(), &Task::succeeded, this, [this, taskIndex]() { remoteLoadSucceeded(taskIndex); });
|
||||
connect(loadTask.get(), &Task::failed, this, [this, taskIndex](const QString& error) { remoteLoadFailed(taskIndex, error); });
|
||||
connect(loadTask.get(), &Task::aborted, this, [this, taskIndex]() { remoteLoadFailed(taskIndex, tr("Aborted")); });
|
||||
RemoteLoadStatus status;
|
||||
status.type = loadType;
|
||||
status.PackProfileIndex = componentIndex;
|
||||
status.task = loadTask;
|
||||
d->remoteLoadStatusList.append(status);
|
||||
taskIndex++;
|
||||
}
|
||||
@ -518,7 +489,14 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
|
||||
void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex)
|
||||
{
|
||||
if (static_cast<size_t>(d->remoteLoadStatusList.size()) < taskIndex) {
|
||||
qWarning() << "Got task index outside of results" << taskIndex;
|
||||
return;
|
||||
}
|
||||
auto& taskSlot = d->remoteLoadStatusList[taskIndex];
|
||||
disconnect(taskSlot.task.get(), &Task::succeeded, this, nullptr);
|
||||
disconnect(taskSlot.task.get(), &Task::failed, this, nullptr);
|
||||
disconnect(taskSlot.task.get(), &Task::aborted, this, nullptr);
|
||||
if (taskSlot.finished) {
|
||||
qWarning() << "Got multiple results from remote load task" << taskIndex;
|
||||
return;
|
||||
@ -538,7 +516,14 @@ void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex)
|
||||
|
||||
void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg)
|
||||
{
|
||||
if (static_cast<size_t>(d->remoteLoadStatusList.size()) < taskIndex) {
|
||||
qWarning() << "Got task index outside of results" << taskIndex;
|
||||
return;
|
||||
}
|
||||
auto& taskSlot = d->remoteLoadStatusList[taskIndex];
|
||||
disconnect(taskSlot.task.get(), &Task::succeeded, this, nullptr);
|
||||
disconnect(taskSlot.task.get(), &Task::failed, this, nullptr);
|
||||
disconnect(taskSlot.task.get(), &Task::aborted, this, nullptr);
|
||||
if (taskSlot.finished) {
|
||||
qWarning() << "Got multiple results from remote load task" << taskIndex;
|
||||
return;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <QString>
|
||||
#include <cstddef>
|
||||
#include "net/Mode.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class PackProfile;
|
||||
|
||||
@ -13,6 +14,7 @@ struct RemoteLoadStatus {
|
||||
bool finished = false;
|
||||
bool succeeded = false;
|
||||
QString error;
|
||||
Task::Ptr task;
|
||||
};
|
||||
|
||||
struct ComponentUpdateTaskData {
|
||||
|
@ -164,6 +164,11 @@ void LaunchProfile::applyCompatibleJavaMajors(QList<int>& javaMajor)
|
||||
{
|
||||
m_compatibleJavaMajors.append(javaMajor);
|
||||
}
|
||||
void LaunchProfile::applyCompatibleJavaName(QString javaName)
|
||||
{
|
||||
if (!javaName.isEmpty())
|
||||
m_compatibleJavaName = javaName;
|
||||
}
|
||||
|
||||
void LaunchProfile::applyLibrary(LibraryPtr library, const RuntimeContext& runtimeContext)
|
||||
{
|
||||
@ -334,6 +339,11 @@ const QList<int>& LaunchProfile::getCompatibleJavaMajors() const
|
||||
return m_compatibleJavaMajors;
|
||||
}
|
||||
|
||||
const QString LaunchProfile::getCompatibleJavaName() const
|
||||
{
|
||||
return m_compatibleJavaName;
|
||||
}
|
||||
|
||||
void LaunchProfile::getLibraryFiles(const RuntimeContext& runtimeContext,
|
||||
QStringList& jars,
|
||||
QStringList& nativeJars,
|
||||
|
@ -59,6 +59,7 @@ class LaunchProfile : public ProblemProvider {
|
||||
void applyMavenFile(LibraryPtr library, const RuntimeContext& runtimeContext);
|
||||
void applyAgent(AgentPtr agent, const RuntimeContext& runtimeContext);
|
||||
void applyCompatibleJavaMajors(QList<int>& javaMajor);
|
||||
void applyCompatibleJavaName(QString javaName);
|
||||
void applyMainJar(LibraryPtr jar);
|
||||
void applyProblemSeverity(ProblemSeverity severity);
|
||||
/// clear the profile
|
||||
@ -80,6 +81,7 @@ class LaunchProfile : public ProblemProvider {
|
||||
const QList<LibraryPtr>& getMavenFiles() const;
|
||||
const QList<AgentPtr>& getAgents() const;
|
||||
const QList<int>& getCompatibleJavaMajors() const;
|
||||
const QString getCompatibleJavaName() const;
|
||||
const LibraryPtr getMainJar() const;
|
||||
void getLibraryFiles(const RuntimeContext& runtimeContext,
|
||||
QStringList& jars,
|
||||
@ -150,5 +152,7 @@ class LaunchProfile : public ProblemProvider {
|
||||
/// compatible java major versions
|
||||
QList<int> m_compatibleJavaMajors;
|
||||
|
||||
QString m_compatibleJavaName;
|
||||
|
||||
ProblemSeverity m_problemSeverity = ProblemSeverity::None;
|
||||
};
|
||||
|
@ -147,9 +147,8 @@ QList<Net::NetRequest::Ptr> Library::getDownloads(const RuntimeContext& runtimeC
|
||||
options |= Net::Download::Option::MakeEternal;
|
||||
|
||||
if (sha1.size()) {
|
||||
auto rawSha1 = QByteArray::fromHex(sha1.toLatin1());
|
||||
auto dl = Net::ApiDownload::makeCached(url, entry, options);
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, sha1));
|
||||
qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
||||
out.append(dl);
|
||||
} else {
|
||||
|
@ -38,6 +38,8 @@
|
||||
#include "MinecraftInstance.h"
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "minecraft/launch/AutoInstallJava.h"
|
||||
#include "minecraft/launch/CreateGameFolders.h"
|
||||
#include "minecraft/launch/ExtractNatives.h"
|
||||
#include "minecraft/launch/PrintInstanceInfo.h"
|
||||
@ -134,25 +136,21 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
return;
|
||||
|
||||
// Java Settings
|
||||
auto javaOverride = m_settings->registerSetting("OverrideJava", false);
|
||||
auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false);
|
||||
auto argsOverride = m_settings->registerSetting("OverrideJavaArgs", false);
|
||||
|
||||
// combinations
|
||||
auto javaOrLocation = std::make_shared<OrSetting>("JavaOrLocationOverride", javaOverride, locationOverride);
|
||||
auto javaOrArgs = std::make_shared<OrSetting>("JavaOrArgsOverride", javaOverride, argsOverride);
|
||||
m_settings->registerSetting("AutomaticJava", false);
|
||||
|
||||
if (auto global_settings = globalSettings()) {
|
||||
m_settings->registerOverride(global_settings->getSetting("JavaPath"), javaOrLocation);
|
||||
m_settings->registerOverride(global_settings->getSetting("JvmArgs"), javaOrArgs);
|
||||
m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), javaOrLocation);
|
||||
m_settings->registerOverride(global_settings->getSetting("JavaPath"), locationOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("JvmArgs"), argsOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), locationOverride);
|
||||
|
||||
// special!
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaSignature"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaVendor"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaSignature"), locationOverride);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), locationOverride);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), locationOverride);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), locationOverride);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaVendor"), locationOverride);
|
||||
|
||||
// Window Size
|
||||
auto windowSetting = m_settings->registerSetting("OverrideWindow", false);
|
||||
@ -196,8 +194,9 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
}
|
||||
|
||||
// Join server on launch, this does not have a global override
|
||||
m_settings->registerSetting("JoinServerOnLaunch", false);
|
||||
m_settings->registerSetting({ "JoinServerOnLaunch", "JoinOnLaunch" }, false);
|
||||
m_settings->registerSetting("JoinServerOnLaunchAddress", "");
|
||||
m_settings->registerSetting("JoinWorldOnLaunch", "");
|
||||
|
||||
// Use account for instance, this does not have a global override
|
||||
m_settings->registerSetting("UseAccountForInstance", false);
|
||||
@ -523,8 +522,7 @@ QStringList MinecraftInstance::javaArguments()
|
||||
|
||||
if (javaVersion.isModular() && shouldApplyOnlineFixes())
|
||||
// allow reflective access to java.net - required by the skin fix
|
||||
args << "--add-opens"
|
||||
<< "java.base/java.net=ALL-UNNAMED";
|
||||
args << "--add-opens" << "java.base/java.net=ALL-UNNAMED";
|
||||
|
||||
return args;
|
||||
}
|
||||
@ -608,7 +606,7 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
|
||||
// dlsym variant is only needed for OpenGL and not included in the vulkan layer
|
||||
appendLib("libMangoHud_dlsym.so");
|
||||
appendLib("libMangoHud_opengl.so");
|
||||
appendLib(mangoHudLib.fileName());
|
||||
preloadList << mangoHudLibString;
|
||||
}
|
||||
|
||||
env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":")));
|
||||
@ -656,7 +654,7 @@ static QString replaceTokensIn(QString text, QMap<QString, QString> with)
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) const
|
||||
QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) const
|
||||
{
|
||||
auto profile = m_components->getProfile();
|
||||
QString args_pattern = profile->getMinecraftArguments();
|
||||
@ -664,12 +662,16 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine
|
||||
args_pattern += " --tweakClass " + tweaker;
|
||||
}
|
||||
|
||||
if (serverToJoin && !serverToJoin->address.isEmpty()) {
|
||||
if (profile->hasTrait("feature:is_quick_play_multiplayer")) {
|
||||
args_pattern += " --quickPlayMultiplayer " + serverToJoin->address + ':' + QString::number(serverToJoin->port);
|
||||
} else {
|
||||
args_pattern += " --server " + serverToJoin->address;
|
||||
args_pattern += " --port " + QString::number(serverToJoin->port);
|
||||
if (targetToJoin) {
|
||||
if (!targetToJoin->address.isEmpty()) {
|
||||
if (profile->hasTrait("feature:is_quick_play_multiplayer")) {
|
||||
args_pattern += " --quickPlayMultiplayer " + targetToJoin->address + ':' + QString::number(targetToJoin->port);
|
||||
} else {
|
||||
args_pattern += " --server " + targetToJoin->address;
|
||||
args_pattern += " --port " + QString::number(targetToJoin->port);
|
||||
}
|
||||
} else if (!targetToJoin->world.isEmpty() && profile->hasTrait("feature:is_quick_play_singleplayer")) {
|
||||
args_pattern += " --quickPlaySingleplayer " + targetToJoin->world;
|
||||
}
|
||||
}
|
||||
|
||||
@ -713,7 +715,7 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine
|
||||
return parts;
|
||||
}
|
||||
|
||||
QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin)
|
||||
{
|
||||
QString launchScript;
|
||||
|
||||
@ -732,9 +734,13 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
|
||||
launchScript += "appletClass " + appletClass + "\n";
|
||||
}
|
||||
|
||||
if (serverToJoin && !serverToJoin->address.isEmpty()) {
|
||||
launchScript += "serverAddress " + serverToJoin->address + "\n";
|
||||
launchScript += "serverPort " + QString::number(serverToJoin->port) + "\n";
|
||||
if (targetToJoin) {
|
||||
if (!targetToJoin->address.isEmpty()) {
|
||||
launchScript += "serverAddress " + targetToJoin->address + "\n";
|
||||
launchScript += "serverPort " + QString::number(targetToJoin->port) + "\n";
|
||||
} else if (!targetToJoin->world.isEmpty()) {
|
||||
launchScript += "worldName " + targetToJoin->world + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// generic minecraft params
|
||||
@ -787,16 +793,15 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
|
||||
return launchScript;
|
||||
}
|
||||
|
||||
QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin)
|
||||
{
|
||||
QStringList out;
|
||||
out << "Main Class:"
|
||||
<< " " + getMainClass() << "";
|
||||
out << "Native path:"
|
||||
<< " " + getNativePath() << "";
|
||||
out << "Main Class:" << " " + getMainClass() << "";
|
||||
out << "Native path:" << " " + getNativePath() << "";
|
||||
|
||||
auto profile = m_components->getProfile();
|
||||
|
||||
// traits
|
||||
auto alltraits = traits();
|
||||
if (alltraits.size()) {
|
||||
out << "Traits:";
|
||||
@ -806,6 +811,7 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
|
||||
out << "";
|
||||
}
|
||||
|
||||
// native libraries
|
||||
auto settings = this->settings();
|
||||
bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
|
||||
bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
|
||||
@ -841,6 +847,7 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
|
||||
out << "";
|
||||
}
|
||||
|
||||
// mods and core mods
|
||||
auto printModList = [&](const QString& label, ModFolderModel& model) {
|
||||
if (model.size()) {
|
||||
out << QString("%1:").arg(label);
|
||||
@ -869,6 +876,7 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
|
||||
printModList("Mods", *(loaderModList().get()));
|
||||
printModList("Core Mods", *(coreModList().get()));
|
||||
|
||||
// jar mods
|
||||
auto& jarMods = profile->getJarMods();
|
||||
if (jarMods.size()) {
|
||||
out << "Jar Mods:";
|
||||
@ -884,11 +892,13 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
|
||||
out << "";
|
||||
}
|
||||
|
||||
auto params = processMinecraftArgs(nullptr, serverToJoin);
|
||||
// minecraft arguments
|
||||
auto params = processMinecraftArgs(nullptr, targetToJoin);
|
||||
out << "Params:";
|
||||
out << " " + params.join(' ');
|
||||
out << "";
|
||||
|
||||
// window size
|
||||
QString windowParams;
|
||||
if (settings->get("LaunchMaximized").toBool()) {
|
||||
out << "Window size: max (if available)";
|
||||
@ -1034,7 +1044,7 @@ Task::Ptr MinecraftInstance::createUpdateTask(Net::Mode mode)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin)
|
||||
{
|
||||
updateRuntimeContext();
|
||||
// FIXME: get rid of shared_from_this ...
|
||||
@ -1048,26 +1058,28 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
process->appendStep(makeShared<TextPrint>(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::Launcher));
|
||||
}
|
||||
|
||||
// check java
|
||||
{
|
||||
process->appendStep(makeShared<CheckJava>(pptr));
|
||||
}
|
||||
|
||||
// create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732)
|
||||
{
|
||||
process->appendStep(makeShared<CreateGameFolders>(pptr));
|
||||
}
|
||||
|
||||
if (!serverToJoin && settings()->get("JoinServerOnLaunch").toBool()) {
|
||||
if (!targetToJoin && settings()->get("JoinOnLaunch").toBool()) {
|
||||
QString fullAddress = settings()->get("JoinServerOnLaunchAddress").toString();
|
||||
serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(fullAddress)));
|
||||
if (!fullAddress.isEmpty()) {
|
||||
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(fullAddress, false)));
|
||||
} else {
|
||||
QString world = settings()->get("JoinWorldOnLaunch").toString();
|
||||
if (!world.isEmpty()) {
|
||||
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(world, true)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serverToJoin && serverToJoin->port == 25565) {
|
||||
if (targetToJoin && targetToJoin->port == 25565) {
|
||||
// Resolve server address to join on launch
|
||||
auto step = makeShared<LookupServerAddress>(pptr);
|
||||
step->setLookupAddress(serverToJoin->address);
|
||||
step->setOutputAddressPtr(serverToJoin);
|
||||
step->setLookupAddress(targetToJoin->address);
|
||||
step->setOutputAddressPtr(targetToJoin);
|
||||
process->appendStep(step);
|
||||
}
|
||||
|
||||
@ -1088,6 +1100,12 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
process->appendStep(makeShared<Update>(pptr, Net::Mode::Offline));
|
||||
}
|
||||
|
||||
// check java
|
||||
{
|
||||
process->appendStep(makeShared<AutoInstallJava>(pptr));
|
||||
process->appendStep(makeShared<CheckJava>(pptr));
|
||||
}
|
||||
|
||||
// if there are any jar mods
|
||||
{
|
||||
process->appendStep(makeShared<ModMinecraftJar>(pptr));
|
||||
@ -1100,7 +1118,7 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
|
||||
// print some instance info here...
|
||||
{
|
||||
process->appendStep(makeShared<PrintInstanceInfo>(pptr, session, serverToJoin));
|
||||
process->appendStep(makeShared<PrintInstanceInfo>(pptr, session, targetToJoin));
|
||||
}
|
||||
|
||||
// extract native jars if needed
|
||||
@ -1123,7 +1141,7 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
auto step = makeShared<LauncherPartLaunch>(pptr);
|
||||
step->setWorkingDirectory(gameRoot());
|
||||
step->setAuthSession(session);
|
||||
step->setServerToJoin(serverToJoin);
|
||||
step->setTargetToJoin(targetToJoin);
|
||||
process->appendStep(step);
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
#include "BaseInstance.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
#include "minecraft/mod/Mod.h"
|
||||
|
||||
class ModFolderModel;
|
||||
@ -56,7 +56,7 @@ class MinecraftInstance : public BaseInstance {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir);
|
||||
virtual ~MinecraftInstance() {};
|
||||
virtual ~MinecraftInstance() = default;
|
||||
virtual void saveNow() override;
|
||||
|
||||
void loadSpecificSettings() override;
|
||||
@ -121,11 +121,11 @@ class MinecraftInstance : public BaseInstance {
|
||||
|
||||
////// Launch stuff //////
|
||||
Task::Ptr createUpdateTask(Net::Mode mode) override;
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override;
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) override;
|
||||
QStringList extraArguments() override;
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) override;
|
||||
QList<Mod*> getJarMods() const;
|
||||
QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin);
|
||||
QString createLaunchScript(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin);
|
||||
/// get arguments passed to java
|
||||
QStringList javaArguments();
|
||||
QString getLauncher();
|
||||
@ -155,7 +155,7 @@ class MinecraftInstance : public BaseInstance {
|
||||
virtual QString getMainClass() const;
|
||||
|
||||
// FIXME: remove
|
||||
virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) const;
|
||||
virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) const;
|
||||
|
||||
virtual JavaVersion getJavaVersion();
|
||||
|
||||
|
@ -16,32 +16,22 @@
|
||||
#include "MinecraftUpdate.h"
|
||||
#include "MinecraftInstance.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <FileSystem.h>
|
||||
#include "BaseInstance.h"
|
||||
#include "minecraft/Library.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "update/AssetUpdateTask.h"
|
||||
#include "update/FMLLibrariesTask.h"
|
||||
#include "update/FoldersTask.h"
|
||||
#include "update/LibrariesTask.h"
|
||||
|
||||
#include <meta/Index.h>
|
||||
#include <meta/Version.h>
|
||||
|
||||
MinecraftUpdate::MinecraftUpdate(MinecraftInstance* inst, QObject* parent) : Task(parent), m_inst(inst) {}
|
||||
MinecraftUpdate::MinecraftUpdate(MinecraftInstance* inst, QObject* parent) : SequentialTask(parent), m_inst(inst) {}
|
||||
|
||||
void MinecraftUpdate::executeTask()
|
||||
{
|
||||
m_tasks.clear();
|
||||
m_queue.clear();
|
||||
// create folders
|
||||
{
|
||||
m_tasks.append(makeShared<FoldersTask>(m_inst));
|
||||
addTask(makeShared<FoldersTask>(m_inst));
|
||||
}
|
||||
|
||||
// add metadata update task if necessary
|
||||
@ -50,121 +40,24 @@ void MinecraftUpdate::executeTask()
|
||||
components->reload(Net::Mode::Online);
|
||||
auto task = components->getCurrentTask();
|
||||
if (task) {
|
||||
m_tasks.append(task);
|
||||
addTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
// libraries download
|
||||
{
|
||||
m_tasks.append(makeShared<LibrariesTask>(m_inst));
|
||||
addTask(makeShared<LibrariesTask>(m_inst));
|
||||
}
|
||||
|
||||
// FML libraries download and copy into the instance
|
||||
{
|
||||
m_tasks.append(makeShared<FMLLibrariesTask>(m_inst));
|
||||
addTask(makeShared<FMLLibrariesTask>(m_inst));
|
||||
}
|
||||
|
||||
// assets update
|
||||
{
|
||||
m_tasks.append(makeShared<AssetUpdateTask>(m_inst));
|
||||
addTask(makeShared<AssetUpdateTask>(m_inst));
|
||||
}
|
||||
|
||||
if (!m_preFailure.isEmpty()) {
|
||||
emitFailed(m_preFailure);
|
||||
return;
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
void MinecraftUpdate::next()
|
||||
{
|
||||
if (m_abort) {
|
||||
emitFailed(tr("Aborted by user."));
|
||||
return;
|
||||
}
|
||||
if (m_failed_out_of_order) {
|
||||
emitFailed(m_fail_reason);
|
||||
return;
|
||||
}
|
||||
m_currentTask++;
|
||||
if (m_currentTask > 0) {
|
||||
auto task = m_tasks[m_currentTask - 1];
|
||||
disconnect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
|
||||
disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
|
||||
disconnect(task.get(), &Task::aborted, this, &Task::abort);
|
||||
disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
|
||||
disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propagateStepProgress);
|
||||
disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
|
||||
disconnect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails);
|
||||
}
|
||||
if (m_currentTask == m_tasks.size()) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
auto task = m_tasks[m_currentTask];
|
||||
// if the task is already finished by the time we look at it, skip it
|
||||
if (task->isFinished()) {
|
||||
qCritical() << "MinecraftUpdate: Skipping finished subtask" << m_currentTask << ":" << task.get();
|
||||
next();
|
||||
}
|
||||
connect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
|
||||
connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
|
||||
connect(task.get(), &Task::aborted, this, &Task::abort);
|
||||
connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
|
||||
connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propagateStepProgress);
|
||||
connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
|
||||
connect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails);
|
||||
// if the task is already running, do not start it again
|
||||
if (!task->isRunning()) {
|
||||
task->start();
|
||||
}
|
||||
}
|
||||
|
||||
void MinecraftUpdate::subtaskSucceeded()
|
||||
{
|
||||
if (isFinished()) {
|
||||
qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!";
|
||||
return;
|
||||
}
|
||||
auto senderTask = QObject::sender();
|
||||
auto currentTask = m_tasks[m_currentTask].get();
|
||||
if (senderTask != currentTask) {
|
||||
qDebug() << "MinecraftUpdate: Subtask" << sender() << "succeeded out of order.";
|
||||
return;
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
void MinecraftUpdate::subtaskFailed(QString error)
|
||||
{
|
||||
if (isFinished()) {
|
||||
qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!";
|
||||
return;
|
||||
}
|
||||
auto senderTask = QObject::sender();
|
||||
auto currentTask = m_tasks[m_currentTask].get();
|
||||
if (senderTask != currentTask) {
|
||||
qDebug() << "MinecraftUpdate: Subtask" << sender() << "failed out of order.";
|
||||
m_failed_out_of_order = true;
|
||||
m_fail_reason = error;
|
||||
return;
|
||||
}
|
||||
emitFailed(error);
|
||||
}
|
||||
|
||||
bool MinecraftUpdate::abort()
|
||||
{
|
||||
if (!m_abort) {
|
||||
m_abort = true;
|
||||
auto task = m_tasks[m_currentTask];
|
||||
if (task->canAbort()) {
|
||||
return task->abort();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MinecraftUpdate::canAbort() const
|
||||
{
|
||||
return true;
|
||||
SequentialTask::executeTask();
|
||||
}
|
||||
|
@ -15,43 +15,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
#include "tasks/SequentialTask.h"
|
||||
|
||||
#include <quazip/quazip.h>
|
||||
#include "minecraft/VersionFilterData.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class MinecraftVersion;
|
||||
class MinecraftInstance;
|
||||
|
||||
// FIXME: This looks very similar to a SequentialTask. Maybe we can reduce code duplications? :^)
|
||||
|
||||
class MinecraftUpdate : public Task {
|
||||
// this needs to be a task because components->reload does stuff that may block
|
||||
class MinecraftUpdate : public SequentialTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MinecraftUpdate(MinecraftInstance* inst, QObject* parent = 0);
|
||||
virtual ~MinecraftUpdate() {};
|
||||
virtual ~MinecraftUpdate() = default;
|
||||
|
||||
void executeTask() override;
|
||||
bool canAbort() const override;
|
||||
|
||||
private slots:
|
||||
bool abort() override;
|
||||
void subtaskSucceeded();
|
||||
void subtaskFailed(QString error);
|
||||
|
||||
private:
|
||||
void next();
|
||||
|
||||
private:
|
||||
MinecraftInstance* m_inst = nullptr;
|
||||
QList<Task::Ptr> m_tasks;
|
||||
QString m_preFailure;
|
||||
int m_currentTask = -1;
|
||||
bool m_abort = false;
|
||||
bool m_failed_out_of_order = false;
|
||||
QString m_fail_reason;
|
||||
};
|
||||
|
@ -185,6 +185,9 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject& in, VersionFi
|
||||
out->compatibleJavaMajors.append(requireInteger(compatible));
|
||||
}
|
||||
}
|
||||
if (in.contains("compatibleJavaName")) {
|
||||
out->compatibleJavaName = requireString(in.value("compatibleJavaName"));
|
||||
}
|
||||
|
||||
if (in.contains("downloads")) {
|
||||
auto downloadsObj = requireObject(in, "downloads");
|
||||
@ -259,6 +262,9 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj
|
||||
}
|
||||
out.insert("compatibleJavaMajors", compatibleJavaMajorsOut);
|
||||
}
|
||||
if (!in->compatibleJavaName.isEmpty()) {
|
||||
writeString(out, "compatibleJavaName", in->compatibleJavaName);
|
||||
}
|
||||
}
|
||||
|
||||
QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr& patch)
|
||||
|
@ -36,6 +36,8 @@
|
||||
#include "OneSixVersionFormat.h"
|
||||
#include <Json.h>
|
||||
#include <minecraft/MojangVersionFormat.h>
|
||||
#include <QList>
|
||||
#include "java/JavaMetadata.h"
|
||||
#include "minecraft/Agent.h"
|
||||
#include "minecraft/ParseUtils.h"
|
||||
|
||||
@ -255,6 +257,13 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument& doc
|
||||
out->m_volatile = requireBoolean(root, "volatile");
|
||||
}
|
||||
|
||||
if (root.contains("runtimes")) {
|
||||
out->runtimes = {};
|
||||
for (auto runtime : ensureArray(root, "runtimes")) {
|
||||
out->runtimes.append(Java::parseJavaMeta(ensureObject(runtime)));
|
||||
}
|
||||
}
|
||||
|
||||
/* removed features that shouldn't be used */
|
||||
if (root.contains("tweakers")) {
|
||||
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'"));
|
||||
|
@ -73,6 +73,7 @@ void VersionFile::applyTo(LaunchProfile* profile, const RuntimeContext& runtimeC
|
||||
profile->applyMods(mods);
|
||||
profile->applyTraits(traits);
|
||||
profile->applyCompatibleJavaMajors(compatibleJavaMajors);
|
||||
profile->applyCompatibleJavaName(compatibleJavaName);
|
||||
|
||||
for (auto library : libraries) {
|
||||
profile->applyLibrary(library, runtimeContext);
|
||||
|
@ -36,6 +36,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
@ -45,6 +47,7 @@
|
||||
#include "Agent.h"
|
||||
#include "Library.h"
|
||||
#include "ProblemProvider.h"
|
||||
#include "java/JavaMetadata.h"
|
||||
#include "minecraft/Rule.h"
|
||||
|
||||
class PackProfile;
|
||||
@ -98,6 +101,9 @@ class VersionFile : public ProblemContainer {
|
||||
/// Mojang: list of compatible java majors
|
||||
QList<int> compatibleJavaMajors;
|
||||
|
||||
/// Mojang: the name of recommended java version
|
||||
QString compatibleJavaName;
|
||||
|
||||
/// Mojang: type of the Minecraft version
|
||||
QString type;
|
||||
|
||||
@ -149,6 +155,8 @@ class VersionFile : public ProblemContainer {
|
||||
/// is volatile -- may be removed as soon as it is no longer needed by something else
|
||||
bool m_volatile = false;
|
||||
|
||||
QList<Java::MetadataPtr> runtimes;
|
||||
|
||||
public:
|
||||
// Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more.
|
||||
QMap<QString, std::shared_ptr<MojangDownloadInfo>> mojangDownloads;
|
||||
|
@ -74,6 +74,8 @@ void MSADeviceCodeStep::perform()
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &MSADeviceCodeStep::deviceAutorizationFinished);
|
||||
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
|
242
launcher/minecraft/launch/AutoInstallJava.cpp
Normal file
242
launcher/minecraft/launch/AutoInstallJava.cpp
Normal file
@ -0,0 +1,242 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "AutoInstallJava.h"
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <memory>
|
||||
|
||||
#include "Application.h"
|
||||
#include "FileSystem.h"
|
||||
#include "MessageLevel.h"
|
||||
#include "SysInfo.h"
|
||||
#include "java/JavaInstall.h"
|
||||
#include "java/JavaInstallList.h"
|
||||
#include "java/JavaUtils.h"
|
||||
#include "java/JavaVersion.h"
|
||||
#include "java/download/ArchiveDownloadTask.h"
|
||||
#include "java/download/ManifestDownloadTask.h"
|
||||
#include "meta/Index.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "net/Mode.h"
|
||||
|
||||
AutoInstallJava::AutoInstallJava(LaunchTask* parent)
|
||||
: LaunchStep(parent)
|
||||
, m_instance(std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance()))
|
||||
, m_supported_arch(SysInfo::getSupportedJavaArchitecture()) {};
|
||||
|
||||
void AutoInstallJava::executeTask()
|
||||
{
|
||||
auto settings = m_instance->settings();
|
||||
if (!APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() ||
|
||||
(settings->get("OverrideJavaLocation").toBool() && QFileInfo::exists(settings->get("JavaPath").toString()))) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
auto packProfile = m_instance->getPackProfile();
|
||||
if (!APPLICATION->settings()->get("AutomaticJavaDownload").toBool()) {
|
||||
auto javas = APPLICATION->javalist();
|
||||
m_current_task = javas->getLoadTask();
|
||||
connect(m_current_task.get(), &Task::finished, this, [this, javas, packProfile] {
|
||||
for (auto i = 0; i < javas->count(); i++) {
|
||||
auto java = std::dynamic_pointer_cast<JavaInstall>(javas->at(i));
|
||||
if (java && packProfile->getProfile()->getCompatibleJavaMajors().contains(java->id.major())) {
|
||||
if (!java->is_64bit) {
|
||||
emit logLine(tr("The automatic Java mechanism detected a 32-bit installation of Java."), MessageLevel::Info);
|
||||
}
|
||||
setJavaPath(java->path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
emit logLine(tr("No compatible Java version was found. Using the default one."), MessageLevel::Warning);
|
||||
emitSucceeded();
|
||||
});
|
||||
connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress);
|
||||
connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress);
|
||||
connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus);
|
||||
connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails);
|
||||
emit progressReportingRequest();
|
||||
return;
|
||||
}
|
||||
if (m_supported_arch.isEmpty()) {
|
||||
emit logLine(tr("Your system (%1-%2) is not compatible with automatic Java installation. Using the default Java path.")
|
||||
.arg(SysInfo::currentSystem(), SysInfo::useQTForArch()),
|
||||
MessageLevel::Warning);
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName();
|
||||
if (wantedJavaName.isEmpty()) {
|
||||
emit logLine(tr("Your meta information is out of date or doesn't have the information necessary to determine what installation of "
|
||||
"Java should be used. "
|
||||
"Using the default Java path."),
|
||||
MessageLevel::Warning);
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
QDir javaDir(APPLICATION->javaPath());
|
||||
auto wantedJavaPath = javaDir.absoluteFilePath(wantedJavaName);
|
||||
if (QFileInfo::exists(wantedJavaPath)) {
|
||||
setJavaPathFromPartial();
|
||||
return;
|
||||
}
|
||||
auto versionList = APPLICATION->metadataIndex()->get("net.minecraft.java");
|
||||
m_current_task = versionList->getLoadTask();
|
||||
connect(m_current_task.get(), &Task::succeeded, this, &AutoInstallJava::tryNextMajorJava);
|
||||
connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::emitFailed);
|
||||
connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress);
|
||||
connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress);
|
||||
connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus);
|
||||
connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails);
|
||||
if (!m_current_task->isRunning()) {
|
||||
m_current_task->start();
|
||||
}
|
||||
emit progressReportingRequest();
|
||||
}
|
||||
|
||||
void AutoInstallJava::setJavaPath(QString path)
|
||||
{
|
||||
auto settings = m_instance->settings();
|
||||
settings->set("OverrideJavaLocation", true);
|
||||
settings->set("JavaPath", path);
|
||||
settings->set("AutomaticJava", true);
|
||||
emit logLine(tr("Compatible Java found at: %1.").arg(path), MessageLevel::Info);
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void AutoInstallJava::setJavaPathFromPartial()
|
||||
{
|
||||
auto packProfile = m_instance->getPackProfile();
|
||||
auto javaName = packProfile->getProfile()->getCompatibleJavaName();
|
||||
QDir javaDir(APPLICATION->javaPath());
|
||||
// just checking if the executable is there should suffice
|
||||
// but if needed this can be achieved through refreshing the javalist
|
||||
// and retrieving the path that contains the java name
|
||||
auto relativeBinary = FS::PathCombine(javaName, "bin", JavaUtils::javaExecutable);
|
||||
auto finalPath = javaDir.absoluteFilePath(relativeBinary);
|
||||
if (QFileInfo::exists(finalPath)) {
|
||||
setJavaPath(finalPath);
|
||||
} else {
|
||||
emit logLine(tr("No compatible Java version was found (the binary file does not exist). Using the default one."),
|
||||
MessageLevel::Warning);
|
||||
emitSucceeded();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void AutoInstallJava::downloadJava(Meta::Version::Ptr version, QString javaName)
|
||||
{
|
||||
auto runtimes = version->data()->runtimes;
|
||||
for (auto java : runtimes) {
|
||||
if (java->runtimeOS == m_supported_arch && java->name() == javaName) {
|
||||
QDir javaDir(APPLICATION->javaPath());
|
||||
auto final_path = javaDir.absoluteFilePath(java->m_name);
|
||||
switch (java->downloadType) {
|
||||
case Java::DownloadType::Manifest:
|
||||
m_current_task = makeShared<Java::ManifestDownloadTask>(java->url, final_path, java->checksumType, java->checksumHash);
|
||||
break;
|
||||
case Java::DownloadType::Archive:
|
||||
m_current_task = makeShared<Java::ArchiveDownloadTask>(java->url, final_path, java->checksumType, java->checksumHash);
|
||||
break;
|
||||
case Java::DownloadType::Unknown:
|
||||
emitFailed(tr("Could not determine Java download type!"));
|
||||
return;
|
||||
}
|
||||
auto deletePath = [final_path] { FS::deletePath(final_path); };
|
||||
connect(m_current_task.get(), &Task::failed, this, [this, deletePath](QString reason) {
|
||||
deletePath();
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(this, &Task::aborted, this, [this, deletePath] {
|
||||
m_current_task->abort();
|
||||
deletePath();
|
||||
});
|
||||
connect(m_current_task.get(), &Task::succeeded, this, &AutoInstallJava::setJavaPathFromPartial);
|
||||
connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava);
|
||||
connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress);
|
||||
connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress);
|
||||
connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus);
|
||||
connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails);
|
||||
m_current_task->start();
|
||||
return;
|
||||
}
|
||||
}
|
||||
tryNextMajorJava();
|
||||
}
|
||||
|
||||
void AutoInstallJava::tryNextMajorJava()
|
||||
{
|
||||
if (!isRunning())
|
||||
return;
|
||||
auto versionList = APPLICATION->metadataIndex()->get("net.minecraft.java");
|
||||
auto packProfile = m_instance->getPackProfile();
|
||||
auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName();
|
||||
auto majorJavaVersions = packProfile->getProfile()->getCompatibleJavaMajors();
|
||||
if (m_majorJavaVersionIndex >= majorJavaVersions.length()) {
|
||||
emit logLine(
|
||||
tr("No versions of Java were found for your operating system: %1-%2").arg(SysInfo::currentSystem(), SysInfo::useQTForArch()),
|
||||
MessageLevel::Warning);
|
||||
emit logLine(tr("No compatible version of Java was found. Using the default one."), MessageLevel::Warning);
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
auto majorJavaVersion = majorJavaVersions[m_majorJavaVersionIndex];
|
||||
m_majorJavaVersionIndex++;
|
||||
|
||||
auto javaMajor = versionList->getVersion(QString("java%1").arg(majorJavaVersion));
|
||||
|
||||
if (javaMajor->isLoaded()) {
|
||||
downloadJava(javaMajor, wantedJavaName);
|
||||
} else {
|
||||
m_current_task = APPLICATION->metadataIndex()->loadVersion("net.minecraft.java", javaMajor->version(), Net::Mode::Online);
|
||||
connect(m_current_task.get(), &Task::succeeded, this,
|
||||
[this, javaMajor, wantedJavaName] { downloadJava(javaMajor, wantedJavaName); });
|
||||
connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava);
|
||||
connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress);
|
||||
connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress);
|
||||
connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus);
|
||||
connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails);
|
||||
if (!m_current_task->isRunning()) {
|
||||
m_current_task->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
bool AutoInstallJava::abort()
|
||||
{
|
||||
if (m_current_task && m_current_task->canAbort())
|
||||
return m_current_task->abort();
|
||||
return true;
|
||||
}
|
68
launcher/minecraft/launch/AutoInstallJava.h
Normal file
68
launcher/minecraft/launch/AutoInstallJava.h
Normal file
@ -0,0 +1,68 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <launch/LaunchStep.h>
|
||||
#include <launch/LaunchTask.h>
|
||||
#include "java/JavaMetadata.h"
|
||||
#include "meta/Version.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class AutoInstallJava : public LaunchStep {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AutoInstallJava(LaunchTask* parent);
|
||||
~AutoInstallJava() override = default;
|
||||
|
||||
void executeTask() override;
|
||||
bool canAbort() const override { return m_current_task ? m_current_task->canAbort() : false; }
|
||||
bool abort() override;
|
||||
|
||||
protected:
|
||||
void setJavaPath(QString path);
|
||||
void setJavaPathFromPartial();
|
||||
void downloadJava(Meta::Version::Ptr version, QString javaName);
|
||||
void tryNextMajorJava();
|
||||
|
||||
private:
|
||||
MinecraftInstancePtr m_instance;
|
||||
Task::Ptr m_current_task;
|
||||
|
||||
qsizetype m_majorJavaVersionIndex = 0;
|
||||
const QString m_supported_arch;
|
||||
};
|
@ -90,7 +90,7 @@ void LauncherPartLaunch::executeTask()
|
||||
}
|
||||
}
|
||||
|
||||
m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin);
|
||||
m_launchScript = minecraftInstance->createLaunchScript(m_session, m_targetToJoin);
|
||||
QStringList args = minecraftInstance->javaArguments();
|
||||
QString allArgs = args.join(", ");
|
||||
emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::Launcher);
|
||||
|
@ -19,13 +19,13 @@
|
||||
#include <launch/LaunchStep.h>
|
||||
#include <minecraft/auth/AuthSession.h>
|
||||
|
||||
#include "MinecraftServerTarget.h"
|
||||
#include "MinecraftTarget.h"
|
||||
|
||||
class LauncherPartLaunch : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LauncherPartLaunch(LaunchTask* parent);
|
||||
virtual ~LauncherPartLaunch() {};
|
||||
virtual ~LauncherPartLaunch() = default;
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool abort();
|
||||
@ -34,7 +34,7 @@ class LauncherPartLaunch : public LaunchStep {
|
||||
void setWorkingDirectory(const QString& wd);
|
||||
void setAuthSession(AuthSessionPtr session) { m_session = session; }
|
||||
|
||||
void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); }
|
||||
void setTargetToJoin(MinecraftTarget::Ptr targetToJoin) { m_targetToJoin = std::move(targetToJoin); }
|
||||
|
||||
private slots:
|
||||
void on_state(LoggedProcess::State state);
|
||||
@ -44,7 +44,7 @@ class LauncherPartLaunch : public LaunchStep {
|
||||
QString m_command;
|
||||
AuthSessionPtr m_session;
|
||||
QString m_launchScript;
|
||||
MinecraftServerTargetPtr m_serverToJoin;
|
||||
MinecraftTarget::Ptr m_targetToJoin;
|
||||
|
||||
bool mayProceed = false;
|
||||
};
|
||||
|
@ -13,13 +13,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "MinecraftServerTarget.h"
|
||||
#include "MinecraftTarget.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
// FIXME: the way this is written, it can't ever do any sort of validation and can accept total junk
|
||||
MinecraftServerTarget MinecraftServerTarget::parse(const QString& fullAddress)
|
||||
MinecraftTarget MinecraftTarget::parse(const QString& fullAddress, bool useWorld)
|
||||
{
|
||||
if (useWorld) {
|
||||
MinecraftTarget target;
|
||||
target.world = fullAddress;
|
||||
return target;
|
||||
}
|
||||
QStringList split = fullAddress.split(":");
|
||||
|
||||
// The logic below replicates the exact logic minecraft uses for parsing server addresses.
|
||||
@ -56,5 +61,5 @@ MinecraftServerTarget MinecraftServerTarget::parse(const QString& fullAddress)
|
||||
}
|
||||
}
|
||||
|
||||
return MinecraftServerTarget{ realAddress, realPort };
|
||||
return MinecraftTarget{ realAddress, realPort };
|
||||
}
|
@ -19,11 +19,11 @@
|
||||
|
||||
#include <QString>
|
||||
|
||||
struct MinecraftServerTarget {
|
||||
struct MinecraftTarget {
|
||||
QString address;
|
||||
quint16 port;
|
||||
|
||||
static MinecraftServerTarget parse(const QString& fullAddress);
|
||||
QString world;
|
||||
static MinecraftTarget parse(const QString& fullAddress, bool useWorld);
|
||||
using Ptr = std::shared_ptr<MinecraftTarget>;
|
||||
};
|
||||
|
||||
using MinecraftServerTargetPtr = std::shared_ptr<MinecraftServerTarget>;
|
@ -129,6 +129,6 @@ void PrintInstanceInfo::executeTask()
|
||||
#endif
|
||||
|
||||
logLines(log, MessageLevel::Launcher);
|
||||
logLines(instance->verboseDescription(m_session, m_serverToJoin), MessageLevel::Launcher);
|
||||
logLines(instance->verboseDescription(m_session, m_targetToJoin), MessageLevel::Launcher);
|
||||
emitSucceeded();
|
||||
}
|
||||
|
@ -16,22 +16,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <launch/LaunchStep.h>
|
||||
#include <memory>
|
||||
#include "minecraft/auth/AuthSession.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
|
||||
// FIXME: temporary wrapper for existing task.
|
||||
class PrintInstanceInfo : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PrintInstanceInfo(LaunchTask* parent, AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
: LaunchStep(parent), m_session(session), m_serverToJoin(serverToJoin) {};
|
||||
virtual ~PrintInstanceInfo() {};
|
||||
explicit PrintInstanceInfo(LaunchTask* parent, AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin)
|
||||
: LaunchStep(parent), m_session(session), m_targetToJoin(targetToJoin) {};
|
||||
virtual ~PrintInstanceInfo() = default;
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const { return false; }
|
||||
|
||||
private:
|
||||
AuthSessionPtr m_session;
|
||||
MinecraftServerTargetPtr m_serverToJoin;
|
||||
MinecraftTarget::Ptr m_targetToJoin;
|
||||
};
|
||||
|
@ -34,7 +34,12 @@
|
||||
*/
|
||||
|
||||
#include "VerifyJavaInstall.h"
|
||||
#include <memory>
|
||||
|
||||
#include "Application.h"
|
||||
#include "MessageLevel.h"
|
||||
#include "java/JavaInstall.h"
|
||||
#include "java/JavaInstallList.h"
|
||||
#include "java/JavaVersion.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
@ -46,6 +51,15 @@ void VerifyJavaInstall::executeTask()
|
||||
auto settings = instance->settings();
|
||||
auto storedVersion = settings->get("JavaVersion").toString();
|
||||
auto ignoreCompatibility = settings->get("IgnoreJavaCompatibility").toBool();
|
||||
auto javaArchitecture = settings->get("JavaArchitecture").toString();
|
||||
auto maxMemAlloc = settings->get("MaxMemAlloc").toInt();
|
||||
|
||||
if (javaArchitecture == "32" && maxMemAlloc > 2048) {
|
||||
emit logLine(tr("Max memory allocation exceeds the supported value.\n"
|
||||
"The selected installation of Java is 32-bit and doesn't support more than 2048MiB of RAM.\n"
|
||||
"The instance may not start due to this."),
|
||||
MessageLevel::Error);
|
||||
}
|
||||
|
||||
auto compatibleMajors = packProfile->getProfile()->getCompatibleJavaMajors();
|
||||
|
||||
|
@ -32,8 +32,7 @@ void AssetUpdateTask::executeTask()
|
||||
auto hexSha1 = assets->sha1.toLatin1();
|
||||
qDebug() << "Asset index SHA1:" << hexSha1;
|
||||
auto dl = Net::ApiDownload::makeCached(indexUrl, entry);
|
||||
auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1());
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, assets->sha1));
|
||||
job->addNetAction(dl);
|
||||
|
||||
downloadJob.reset(job);
|
||||
|
Reference in New Issue
Block a user