Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into feature/java-downloader

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97 2024-08-17 00:20:40 +03:00
commit b5bbe93775
73 changed files with 609 additions and 481 deletions

View File

@ -79,7 +79,7 @@ jobs:
qt_ver: 6
qt_host: windows
qt_arch: ""
qt_version: "6.7.1"
qt_version: "6.7.2"
qt_modules: "qt5compat qtimageformats qtnetworkauth"
- os: windows-2022
@ -90,7 +90,7 @@ jobs:
qt_ver: 6
qt_host: windows
qt_arch: "win64_msvc2019_arm64"
qt_version: "6.7.1"
qt_version: "6.7.2"
qt_modules: "qt5compat qtimageformats qtnetworkauth"
- os: macos-12
@ -99,7 +99,7 @@ jobs:
qt_ver: 6
qt_host: mac
qt_arch: ""
qt_version: "6.7.1"
qt_version: "6.7.2"
qt_modules: "qt5compat qtimageformats qtnetworkauth"
- os: macos-12
@ -160,7 +160,7 @@ jobs:
- name: Setup ccache
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
uses: hendrikmuhs/ccache-action@v1.2.13
uses: hendrikmuhs/ccache-action@v1.2.14
with:
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}

View File

@ -19,7 +19,7 @@ jobs:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b # v27
- uses: DeterminateSystems/update-flake-lock@v22
- uses: DeterminateSystems/update-flake-lock@v23
with:
commit-msg: "chore(nix): update lockfile"
pr-title: "chore(nix): update lockfile"

View File

@ -1,3 +0,0 @@
# Build Instructions
Full build instructions are available on [the website](https://prismlauncher.org/wiki/development/build-instructions/).

View File

@ -61,7 +61,12 @@ The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.
## Building
If you want to build Prism Launcher yourself, check the [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/).
If you want to build Prism Launcher yourself, check the build instructions:
- [Windows](https://prismlauncher.org/wiki/development/build-instructions/windows/)
- [Linux](https://prismlauncher.org/wiki/development/build-instructions/linux/)
- [MacOS](https://prismlauncher.org/wiki/development/build-instructions/macos/)
- [OpenBSD](https://prismlauncher.org/wiki/development/build-instructions/openbsd/)
## Sponsors & Partners

18
flake.lock generated
View File

@ -23,11 +23,11 @@
]
},
"locked": {
"lastModified": 1719994518,
"narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=",
"lastModified": 1722555600,
"narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7",
"rev": "8471fe90ad337a8074e957b69ca4d0089218391d",
"type": "github"
},
"original": {
@ -75,11 +75,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1720768451,
"narHash": "sha256-EYekUHJE2gxeo2pM/zM9Wlqw1Uw2XTJXOSAO79ksc4Y=",
"lastModified": 1723175592,
"narHash": "sha256-M0xJ3FbDUc4fRZ84dPGx5VvgFsOzds77KiBMW/mMTnI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7e7c39ea35c5cdd002cd4588b03a3fb9ece6fad9",
"rev": "5e0ca22929f3342b19569b21b2f3462f053e497b",
"type": "github"
},
"original": {
@ -103,11 +103,11 @@
]
},
"locked": {
"lastModified": 1720524665,
"narHash": "sha256-ni/87oHPZm6Gv0ECYxr1f6uxB0UKBWJ6HvS7lwLU6oY=",
"lastModified": 1723202784,
"narHash": "sha256-qbhjc/NEGaDbyy0ucycubq4N3//gDFFH3DOmp1D3u1Q=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "8d6a17d0cdf411c55f12602624df6368ad86fac1",
"rev": "c7012d0c18567c889b948781bc74a501e92275d1",
"type": "github"
},
"original": {

View File

@ -238,6 +238,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{ { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory)", "directory" },
{ { "l", "launch" }, "Launch the specified instance (by instance ID)", "instance" },
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
{ { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" },
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
@ -252,6 +253,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_instanceIdToLaunch = parser.value("launch");
m_serverToJoin = parser.value("server");
m_worldToJoin = parser.value("world");
m_profileToUse = parser.value("profile");
m_liveCheck = parser.isSet("alive");
@ -267,7 +269,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
// error if --launch is missing with --server or --profile
if ((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) {
if (((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty()) || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) {
std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl;
m_status = Application::Failed;
return;
@ -387,6 +389,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
if (!m_serverToJoin.isEmpty()) {
launch.args["server"] = m_serverToJoin;
} else if (!m_worldToJoin.isEmpty()) {
launch.args["world"] = m_worldToJoin;
}
if (!m_profileToUse.isEmpty()) {
launch.args["profile"] = m_profileToUse;
@ -525,6 +529,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
if (!m_serverToJoin.isEmpty()) {
qDebug() << "Address of server to join :" << m_serverToJoin;
} else if (!m_worldToJoin.isEmpty()) {
qDebug() << "Name of the world to join :" << m_worldToJoin;
}
qDebug() << "<> Paths set.";
}
@ -1163,14 +1169,17 @@ void Application::performMainStartupAction()
if (!m_instanceIdToLaunch.isEmpty()) {
auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
if (inst) {
MinecraftServerTargetPtr serverToJoin = nullptr;
MinecraftTarget::Ptr targetToJoin = nullptr;
MinecraftAccountPtr accountToUse = nullptr;
qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching";
if (!m_serverToJoin.isEmpty()) {
// FIXME: validate the server string
serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(m_serverToJoin)));
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_serverToJoin, false)));
qDebug() << " Launching with server" << m_serverToJoin;
} else if (!m_worldToJoin.isEmpty()) {
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_worldToJoin, true)));
qDebug() << " Launching with world" << m_worldToJoin;
}
if (!m_profileToUse.isEmpty()) {
@ -1181,7 +1190,7 @@ void Application::performMainStartupAction()
qDebug() << " Launching with account" << m_profileToUse;
}
launch(inst, true, false, serverToJoin, accountToUse);
launch(inst, true, false, targetToJoin, accountToUse);
return;
}
}
@ -1271,6 +1280,7 @@ void Application::messageReceived(const QByteArray& message)
} else if (command == "launch") {
QString id = received.args["id"];
QString server = received.args["server"];
QString world = received.args["world"];
QString profile = received.args["profile"];
InstancePtr instance;
@ -1285,11 +1295,12 @@ void Application::messageReceived(const QByteArray& message)
return;
}
MinecraftServerTargetPtr serverObject = nullptr;
MinecraftTarget::Ptr serverObject = nullptr;
if (!server.isEmpty()) {
serverObject = std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(server));
serverObject = std::make_shared<MinecraftTarget>(MinecraftTarget::parse(server, false));
} else if (!world.isEmpty()) {
serverObject = std::make_shared<MinecraftTarget>(MinecraftTarget::parse(world, true));
}
MinecraftAccountPtr accountObject;
if (!profile.isEmpty()) {
accountObject = accounts()->getAccountByProfileName(profile);
@ -1338,11 +1349,7 @@ bool Application::openJsonEditor(const QString& filename)
}
}
bool Application::launch(InstancePtr instance,
bool online,
bool demo,
MinecraftServerTargetPtr serverToJoin,
MinecraftAccountPtr accountToUse)
bool Application::launch(InstancePtr instance, bool online, bool demo, MinecraftTarget::Ptr targetToJoin, MinecraftAccountPtr accountToUse)
{
if (m_updateRunning) {
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
@ -1360,7 +1367,7 @@ bool Application::launch(InstancePtr instance,
controller->setOnline(online);
controller->setDemo(demo);
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
controller->setServerToJoin(serverToJoin);
controller->setTargetToJoin(targetToJoin);
controller->setAccountToUse(accountToUse);
if (window) {
controller->setParentWidget(window);

View File

@ -47,7 +47,7 @@
#include <BaseInstance.h>
#include "minecraft/launch/MinecraftServerTarget.h"
#include "minecraft/launch/MinecraftTarget.h"
class LaunchController;
class LocalPeer;
@ -203,7 +203,7 @@ class Application : public QApplication {
bool launch(InstancePtr instance,
bool online = true,
bool demo = false,
MinecraftServerTargetPtr serverToJoin = nullptr,
MinecraftTarget::Ptr targetToJoin = nullptr,
MinecraftAccountPtr accountToUse = nullptr);
bool kill(InstancePtr instance);
void closeCurrentWindow();
@ -291,6 +291,7 @@ class Application : public QApplication {
QString m_detectedOpenALPath;
QString m_instanceIdToLaunch;
QString m_serverToJoin;
QString m_worldToJoin;
QString m_profileToUse;
bool m_liveCheck = false;
QList<QUrl> m_urlsToImport;

View File

@ -56,7 +56,7 @@
#include "net/Mode.h"
#include "RuntimeContext.h"
#include "minecraft/launch/MinecraftServerTarget.h"
#include "minecraft/launch/MinecraftTarget.h"
class QDir;
class Task;
@ -184,7 +184,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
/// returns a valid launcher (task container)
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) = 0;
/// returns the current launch task (if any)
shared_qobject_ptr<LaunchTask> getLaunchTask();
@ -256,7 +256,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
/**
* 'print' a verbose description of the instance into a QStringList
*/
virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) = 0;
virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) = 0;
Status currentStatus() const;

View File

@ -140,7 +140,6 @@ set(NET_SOURCES
net/HeaderProxy.h
net/RawHeaderProxy.h
net/ApiHeaderProxy.h
net/StaticHeaderProxy.h
net/ApiDownload.h
net/ApiDownload.cpp
net/ApiUpload.cpp
@ -265,8 +264,8 @@ set(MINECRAFT_SOURCES
minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp
minecraft/launch/LauncherPartLaunch.h
minecraft/launch/MinecraftServerTarget.cpp
minecraft/launch/MinecraftServerTarget.h
minecraft/launch/MinecraftTarget.cpp
minecraft/launch/MinecraftTarget.h
minecraft/launch/PrintInstanceInfo.cpp
minecraft/launch/PrintInstanceInfo.h
minecraft/launch/ReconstructAssets.cpp

View File

@ -72,7 +72,7 @@ void appendSafe(const QString& filename, const QByteArray& data);
void append(const QString& filename, const QByteArray& data);
/**
* read data from a file safely\
* read data from a file safely
*/
QByteArray read(const QString& filename);

View File

@ -22,7 +22,7 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
public:
explicit InstancePageProvider(InstancePtr parent) { inst = parent; }
virtual ~InstancePageProvider() {};
virtual ~InstancePageProvider() = default;
virtual QList<BasePage*> getPages() override
{
QList<BasePage*> values;
@ -39,7 +39,7 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
values.append(new TexturePackPage(onesix.get(), onesix->texturePackList()));
values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList()));
values.append(new NotesPage(onesix.get()));
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
values.append(new WorldListPage(onesix, onesix->worldList()));
values.append(new ServersPage(onesix));
// values.append(new GameOptionsPage(onesix.get()));
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));

View File

@ -324,7 +324,7 @@ void LaunchController::launchInstance()
return;
}
m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin);
m_launcher = m_instance->createLaunchTask(m_session, m_targetToJoin);
if (!m_launcher) {
emitFailed(tr("Couldn't instantiate a launcher."));
return;

View File

@ -39,7 +39,7 @@
#include <QObject>
#include "minecraft/auth/MinecraftAccount.h"
#include "minecraft/launch/MinecraftServerTarget.h"
#include "minecraft/launch/MinecraftTarget.h"
class InstanceWindow;
class LaunchController : public Task {
@ -48,7 +48,7 @@ class LaunchController : public Task {
void executeTask() override;
LaunchController(QObject* parent = nullptr);
virtual ~LaunchController() {};
virtual ~LaunchController() = default;
void setInstance(InstancePtr instance) { m_instance = instance; }
@ -62,7 +62,7 @@ class LaunchController : public Task {
void setParentWidget(QWidget* widget) { m_parentWidget = widget; }
void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); }
void setTargetToJoin(MinecraftTarget::Ptr targetToJoin) { m_targetToJoin = std::move(targetToJoin); }
void setAccountToUse(MinecraftAccountPtr accountToUse) { m_accountToUse = std::move(accountToUse); }
@ -94,5 +94,5 @@ class LaunchController : public Task {
MinecraftAccountPtr m_accountToUse = nullptr;
AuthSessionPtr m_session;
shared_qobject_ptr<LaunchTask> m_launcher;
MinecraftServerTargetPtr m_serverToJoin;
MinecraftTarget::Ptr m_targetToJoin;
};

View File

@ -344,6 +344,17 @@ std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, con
qWarning() << (QObject::tr("Could not fix permissions for %1").arg(target_file_path));
}
}
} else if (fileInfo.isDir()) {
// Ensure the folder has the minimal required permissions
QFile::Permissions minimalPermissions = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup |
QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther;
QFile::Permissions currentPermissions = fileInfo.permissions();
if ((currentPermissions & minimalPermissions) != minimalPermissions) {
if (!QFile::setPermissions(target_file_path, minimalPermissions)) {
qWarning() << (QObject::tr("Could not fix permissions for %1").arg(target_file_path));
}
}
}
qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
} while (zip->goToNextFile());
@ -564,7 +575,7 @@ auto ExtractZipTask::extractZip() -> ZipResult
if (!file_name.startsWith(m_subdirectory))
continue;
auto relative_file_name = QDir::fromNativeSeparators(file_name.remove(0, m_subdirectory.size()));
auto relative_file_name = QDir::fromNativeSeparators(file_name.mid(m_subdirectory.size()));
auto original_name = relative_file_name;
setStatus("Unziping: " + relative_file_name);
@ -614,6 +625,17 @@ auto ExtractZipTask::extractZip() -> ZipResult
logWarning(tr("Could not fix permissions for %1").arg(target_file_path));
}
}
} else if (fileInfo.isDir()) {
// Ensure the folder has the minimal required permissions
QFile::Permissions minimalPermissions = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup |
QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther;
QFile::Permissions currentPermissions = fileInfo.permissions();
if ((currentPermissions & minimalPermissions) != minimalPermissions) {
if (!QFile::setPermissions(target_file_path, minimalPermissions)) {
logWarning(tr("Could not fix permissions for %1").arg(target_file_path));
}
}
}
qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;

View File

@ -40,8 +40,8 @@ namespace MangoHud {
QString getLibraryString()
{
/*
* Check for vulkan layers in this order:
/**
* Guess MangoHud install location by searching for vulkan layers in this order:
*
* $VK_LAYER_PATH
* $XDG_DATA_DIRS (/usr/local/share/:/usr/share/)
@ -49,8 +49,9 @@ QString getLibraryString()
* /etc
* $XDG_CONFIG_DIRS (/etc/xdg)
* $XDG_CONFIG_HOME (~/.config)
*
* @returns Absolute path of libMangoHud.so if found and empty QString otherwise.
*/
QStringList vkLayerList;
{
QString home = QDir::homePath();
@ -85,7 +86,7 @@ QString getLibraryString()
vkLayerList << FS::PathCombine(xdgConfigHome, "vulkan", "implicit_layer.d");
}
for (QString vkLayer : vkLayerList) {
for (const QString& vkLayer : vkLayerList) {
// prefer to use architecture specific vulkan layers
QString currentArch = QSysInfo::currentCpuArchitecture();
@ -95,8 +96,8 @@ QString getLibraryString()
QStringList manifestNames = { QString("MangoHud.%1.json").arg(currentArch), "MangoHud.json" };
QString filePath = "";
for (QString manifestName : manifestNames) {
QString filePath{};
for (const QString& manifestName : manifestNames) {
QString tryPath = FS::PathCombine(vkLayer, manifestName);
if (QFile::exists(tryPath)) {
filePath = tryPath;
@ -111,10 +112,23 @@ QString getLibraryString()
auto conf = Json::requireDocument(filePath, vkLayer);
auto confObject = Json::requireObject(conf, vkLayer);
auto layer = Json::ensureObject(confObject, "layer");
return Json::ensureString(layer, "library_path");
QString libraryName = Json::ensureString(layer, "library_path");
#ifdef __GLIBC__
// Check whether mangohud is usable on a glibc based system
if (!libraryName.isEmpty()) {
QString libraryPath = findLibrary(libraryName);
if (!libraryPath.isEmpty()) {
return libraryPath;
}
}
#else
// Without glibc return recorded shared library as-is.
return libraryName;
#endif
}
return QString();
return {};
}
QString findLibrary(QString libName)

View File

@ -46,13 +46,13 @@ class NullInstance : public BaseInstance {
{
setVersionBroken(true);
}
virtual ~NullInstance() {};
virtual ~NullInstance() = default;
void saveNow() override {}
void loadSpecificSettings() override { setSpecificSettingsLoaded(true); }
QString getStatusbarDescription() override { return tr("Unknown instance type"); };
QSet<QString> traits() const override { return {}; };
QString instanceConfigFolder() const override { return instanceRoot(); };
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; }
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftTarget::Ptr) override { return nullptr; }
shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; }
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
@ -64,7 +64,7 @@ class NullInstance : public BaseInstance {
bool canEdit() const override { return false; }
bool canLaunch() const override { return false; }
void populateLaunchMenu(QMenu* menu) override {}
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) override
{
QStringList out;
out << "Null instance - placeholder.";

View File

@ -182,56 +182,58 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
else if (keyType == KEY_WOW64_32KEY)
archType = "32";
HKEY jreKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName.toStdWString().c_str(), 0, KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) ==
ERROR_SUCCESS) {
// Read the current type version from the registry.
// This will be used to find any key that contains the JavaHome value.
for (HKEY baseRegistry : { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE }) {
HKEY jreKey;
if (RegOpenKeyExW(baseRegistry, keyName.toStdWString().c_str(), 0, KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) ==
ERROR_SUCCESS) {
// Read the current type version from the registry.
// This will be used to find any key that contains the JavaHome value.
WCHAR subKeyName[255];
DWORD subKeyNameSize, numSubKeys, retCode;
WCHAR subKeyName[255];
DWORD subKeyNameSize, numSubKeys, retCode;
// Get the number of subkeys
RegQueryInfoKeyW(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
// Get the number of subkeys
RegQueryInfoKeyW(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
// Iterate until RegEnumKeyEx fails
if (numSubKeys > 0) {
for (DWORD i = 0; i < numSubKeys; i++) {
subKeyNameSize = 255;
retCode = RegEnumKeyExW(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL);
QString newSubkeyName = QString::fromWCharArray(subKeyName);
if (retCode == ERROR_SUCCESS) {
// Now open the registry key for the version that we just got.
QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix;
// Iterate until RegEnumKeyEx fails
if (numSubKeys > 0) {
for (DWORD i = 0; i < numSubKeys; i++) {
subKeyNameSize = 255;
retCode = RegEnumKeyExW(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL);
QString newSubkeyName = QString::fromWCharArray(subKeyName);
if (retCode == ERROR_SUCCESS) {
// Now open the registry key for the version that we just got.
QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix;
HKEY newKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, KEY_READ | keyType, &newKey) ==
ERROR_SUCCESS) {
// Read the JavaHome value to find where Java is installed.
DWORD valueSz = 0;
if (RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, NULL, &valueSz) == ERROR_SUCCESS) {
WCHAR* value = new WCHAR[valueSz];
RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, (BYTE*)value, &valueSz);
HKEY newKey;
if (RegOpenKeyExW(baseRegistry, newKeyName.toStdWString().c_str(), 0, KEY_READ | keyType, &newKey) ==
ERROR_SUCCESS) {
// Read the JavaHome value to find where Java is installed.
DWORD valueSz = 0;
if (RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, NULL, &valueSz) == ERROR_SUCCESS) {
WCHAR* value = new WCHAR[valueSz];
RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, (BYTE*)value, &valueSz);
QString newValue = QString::fromWCharArray(value);
delete[] value;
QString newValue = QString::fromWCharArray(value);
delete[] value;
// Now, we construct the version object and add it to the list.
JavaInstallPtr javaVersion(new JavaInstall());
// Now, we construct the version object and add it to the list.
JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = newSubkeyName;
javaVersion->arch = archType;
javaVersion->path = QDir(FS::PathCombine(newValue, "bin")).absoluteFilePath("javaw.exe");
javas.append(javaVersion);
javaVersion->id = newSubkeyName;
javaVersion->arch = archType;
javaVersion->path = QDir(FS::PathCombine(newValue, "bin")).absoluteFilePath("javaw.exe");
javas.append(javaVersion);
}
RegCloseKey(newKey);
}
RegCloseKey(newKey);
}
}
}
}
RegCloseKey(jreKey);
RegCloseKey(jreKey);
}
}
return javas;

View File

@ -30,7 +30,7 @@ void LookupServerAddress::setLookupAddress(const QString& lookupAddress)
m_dnsLookup->setName(QString("_minecraft._tcp.%1").arg(lookupAddress));
}
void LookupServerAddress::setOutputAddressPtr(MinecraftServerTargetPtr output)
void LookupServerAddress::setOutputAddressPtr(MinecraftTarget::Ptr output)
{
m_output = std::move(output);
}

View File

@ -19,20 +19,20 @@
#include <launch/LaunchStep.h>
#include <QDnsLookup>
#include "minecraft/launch/MinecraftServerTarget.h"
#include "minecraft/launch/MinecraftTarget.h"
class LookupServerAddress : public LaunchStep {
Q_OBJECT
public:
explicit LookupServerAddress(LaunchTask* parent);
virtual ~LookupServerAddress() {};
virtual ~LookupServerAddress() = default;
virtual void executeTask();
virtual bool abort();
virtual bool canAbort() const { return true; }
void setLookupAddress(const QString& lookupAddress);
void setOutputAddressPtr(MinecraftServerTargetPtr output);
void setOutputAddressPtr(MinecraftTarget::Ptr output);
private slots:
void on_dnsLookupFinished();
@ -42,5 +42,5 @@ class LookupServerAddress : public LaunchStep {
QDnsLookup* m_dnsLookup;
QString m_lookupAddress;
MinecraftServerTargetPtr m_output;
MinecraftTarget::Ptr m_output;
};

View File

@ -193,8 +193,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);
@ -520,8 +521,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;
}
@ -605,7 +605,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(":")));
@ -653,7 +653,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();
@ -661,12 +661,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;
}
}
@ -710,7 +714,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;
@ -729,9 +733,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
@ -784,16 +792,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:";
@ -803,6 +810,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();
@ -838,6 +846,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);
@ -866,6 +875,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:";
@ -881,11 +891,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)";
@ -1031,7 +1043,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 ...
@ -1050,16 +1062,23 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
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);
}
@ -1098,7 +1117,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
@ -1121,7 +1140,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);
}

View File

@ -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;
@ -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();

View File

@ -11,7 +11,7 @@
#include "minecraft/auth/Parsers.h"
#include "net/Download.h"
#include "net/NetJob.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
#include "tasks/Task.h"
EntitlementsStep::EntitlementsStep(AccountData* data) : AuthStep(data) {}
@ -33,7 +33,7 @@ void EntitlementsStep::perform()
m_response.reset(new QByteArray());
m_request = Net::Download::makeByteArray(url, m_response);
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_task.reset(new NetJob("EntitlementsStep", APPLICATION->network()));
m_task->setAskRetry(false);

View File

@ -7,7 +7,7 @@
#include "Logging.h"
#include "minecraft/auth/Parsers.h"
#include "net/NetUtils.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
#include "net/Upload.h"
LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) {}
@ -38,7 +38,7 @@ void LauncherLoginStep::perform()
m_response.reset(new QByteArray());
m_request = Net::Upload::makeByteArray(url, m_response, requestBody.toUtf8());
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_task.reset(new NetJob("LauncherLoginStep", APPLICATION->network()));
m_task->setAskRetry(false);

View File

@ -40,7 +40,7 @@
#include "Application.h"
#include "Json.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
// https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-device-code
MSADeviceCodeStep::MSADeviceCodeStep(AccountData* data) : AuthStep(data)
@ -68,7 +68,7 @@ void MSADeviceCodeStep::perform()
};
m_response.reset(new QByteArray());
m_request = Net::Upload::makeByteArray(url, m_response, payload);
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_task.reset(new NetJob("MSADeviceCodeStep", APPLICATION->network()));
m_task->setAskRetry(false);
@ -183,7 +183,7 @@ void MSADeviceCodeStep::authenticateUser()
};
m_response.reset(new QByteArray());
m_request = Net::Upload::makeByteArray(url, m_response, payload);
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
connect(m_request.get(), &Task::finished, this, &MSADeviceCodeStep::authenticationFinished);

View File

@ -5,7 +5,7 @@
#include "Application.h"
#include "minecraft/auth/Parsers.h"
#include "net/NetUtils.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
MinecraftProfileStep::MinecraftProfileStep(AccountData* data) : AuthStep(data) {}
@ -23,7 +23,7 @@ void MinecraftProfileStep::perform()
m_response.reset(new QByteArray());
m_request = Net::Download::makeByteArray(url, m_response);
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_task.reset(new NetJob("MinecraftProfileStep", APPLICATION->network()));
m_task->setAskRetry(false);

View File

@ -8,7 +8,7 @@
#include "Logging.h"
#include "minecraft/auth/Parsers.h"
#include "net/NetUtils.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
#include "net/Upload.h"
XboxAuthorizationStep::XboxAuthorizationStep(AccountData* data, Token* token, QString relyingParty, QString authorizationKind)
@ -43,7 +43,7 @@ void XboxAuthorizationStep::perform()
};
m_response.reset(new QByteArray());
m_request = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8());
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_task.reset(new NetJob("XboxAuthorizationStep", APPLICATION->network()));
m_task->setAskRetry(false);

View File

@ -6,7 +6,7 @@
#include "Application.h"
#include "Logging.h"
#include "net/NetUtils.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
XboxProfileStep::XboxProfileStep(AccountData* data) : AuthStep(data) {}
@ -35,7 +35,7 @@ void XboxProfileStep::perform()
m_response.reset(new QByteArray());
m_request = Net::Download::makeByteArray(url, m_response);
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_task.reset(new NetJob("XboxProfileStep", APPLICATION->network()));
m_task->setAskRetry(false);

View File

@ -5,7 +5,7 @@
#include "Application.h"
#include "minecraft/auth/Parsers.h"
#include "net/NetUtils.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
XboxUserStep::XboxUserStep(AccountData* data) : AuthStep(data) {}
@ -39,7 +39,7 @@ void XboxUserStep::perform()
};
m_response.reset(new QByteArray());
m_request = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8());
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_task.reset(new NetJob("XboxUserStep", APPLICATION->network()));
m_task->setAskRetry(false);

View File

@ -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);

View File

@ -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;
};

View File

@ -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 };
}

View File

@ -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>;

View File

@ -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();
}

View File

@ -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;
};

View File

@ -251,7 +251,7 @@ Task* ModFolderModel::createParseTask(Resource& resource)
bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata)
{
for (auto mod : allMods()) {
if (mod->fileinfo().fileName() == filename) {
if (mod->getOriginalFileName() == filename) {
auto index_dir = indexDir();
mod->destroy(index_dir, preserve_metadata, false);

View File

@ -197,3 +197,11 @@ bool Resource::isMoreThanOneHardLink() const
{
return FS::hardLinkCount(m_file_info.absoluteFilePath()) > 1;
}
auto Resource::getOriginalFileName() const -> QString
{
auto fileName = m_file_info.fileName();
if (!m_enabled)
fileName.chop(9);
return fileName;
}

View File

@ -80,6 +80,7 @@ class Resource : public QObject {
[[nodiscard]] auto internal_id() const -> QString { return m_internal_id; }
[[nodiscard]] auto type() const -> ResourceType { return m_type; }
[[nodiscard]] bool enabled() const { return m_enabled; }
[[nodiscard]] auto getOriginalFileName() const -> QString;
[[nodiscard]] QString sizeStr() const { return m_size_str; }
[[nodiscard]] qint64 sizeInfo() const { return m_size_info; }

View File

@ -39,9 +39,9 @@
#include <memory>
#include "net/ByteArraySink.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
CapeChange::CapeChange(QString token, QString cape) : NetRequest(), m_capeId(cape), m_token(token)
CapeChange::CapeChange(QString cape) : NetRequest(), m_capeId(cape)
{
logCat = taskMCSkinsLogC;
}
@ -57,18 +57,14 @@ QNetworkReply* CapeChange::getReply(QNetworkRequest& request)
}
}
void CapeChange::init()
{
addHeaderProxy(new Net::StaticHeaderProxy(QList<Net::HeaderPair>{
{ "Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit() },
}));
}
CapeChange::Ptr CapeChange::make(QString token, QString capeId)
{
auto up = makeShared<CapeChange>(token, capeId);
auto up = makeShared<CapeChange>(capeId);
up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active");
up->setObjectName(QString("BYTES:") + up->m_url.toString());
up->m_sink.reset(new Net::ByteArraySink(std::make_shared<QByteArray>()));
up->addHeaderProxy(new Net::RawHeaderProxy(QList<Net::HeaderPair>{
{ "Authorization", QString("Bearer %1").arg(token).toLocal8Bit() },
}));
return up;
}

View File

@ -24,16 +24,14 @@ class CapeChange : public Net::NetRequest {
Q_OBJECT
public:
using Ptr = shared_qobject_ptr<CapeChange>;
CapeChange(QString token, QString capeId);
CapeChange(QString capeId);
virtual ~CapeChange() = default;
static CapeChange::Ptr make(QString token, QString capeId);
void init() override;
protected:
virtual QNetworkReply* getReply(QNetworkRequest&) override;
private:
QString m_capeId;
QString m_token;
};

View File

@ -37,9 +37,9 @@
#include "SkinDelete.h"
#include "net/ByteArraySink.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
SkinDelete::SkinDelete(QString token) : NetRequest(), m_token(token)
SkinDelete::SkinDelete() : NetRequest()
{
logCat = taskMCSkinsLogC;
}
@ -50,17 +50,13 @@ QNetworkReply* SkinDelete::getReply(QNetworkRequest& request)
return m_network->deleteResource(request);
}
void SkinDelete::init()
{
addHeaderProxy(new Net::StaticHeaderProxy(QList<Net::HeaderPair>{
{ "Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit() },
}));
}
SkinDelete::Ptr SkinDelete::make(QString token)
{
auto up = makeShared<SkinDelete>(token);
auto up = makeShared<SkinDelete>();
up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active");
up->m_sink.reset(new Net::ByteArraySink(std::make_shared<QByteArray>()));
up->addHeaderProxy(new Net::RawHeaderProxy(QList<Net::HeaderPair>{
{ "Authorization", QString("Bearer %1").arg(token).toLocal8Bit() },
}));
return up;
}

View File

@ -24,15 +24,11 @@ class SkinDelete : public Net::NetRequest {
Q_OBJECT
public:
using Ptr = shared_qobject_ptr<SkinDelete>;
SkinDelete(QString token);
SkinDelete();
virtual ~SkinDelete() = default;
static SkinDelete::Ptr make(QString token);
void init() override;
protected:
virtual QNetworkReply* getReply(QNetworkRequest&) override;
private:
QString m_token;
};

View File

@ -40,9 +40,9 @@
#include "FileSystem.h"
#include "net/ByteArraySink.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
SkinUpload::SkinUpload(QString token, QString path, QString variant) : NetRequest(), m_token(token), m_path(path), m_variant(variant)
SkinUpload::SkinUpload(QString path, QString variant) : NetRequest(), m_path(path), m_variant(variant)
{
logCat = taskMCSkinsLogC;
}
@ -67,18 +67,14 @@ QNetworkReply* SkinUpload::getReply(QNetworkRequest& request)
return m_network->post(request, multiPart);
}
void SkinUpload::init()
{
addHeaderProxy(new Net::StaticHeaderProxy(QList<Net::HeaderPair>{
{ "Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit() },
}));
}
SkinUpload::Ptr SkinUpload::make(QString token, QString path, QString variant)
{
auto up = makeShared<SkinUpload>(token, path, variant);
auto up = makeShared<SkinUpload>(path, variant);
up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins");
up->setObjectName(QString("BYTES:") + up->m_url.toString());
up->m_sink.reset(new Net::ByteArraySink(std::make_shared<QByteArray>()));
up->addHeaderProxy(new Net::RawHeaderProxy(QList<Net::HeaderPair>{
{ "Authorization", QString("Bearer %1").arg(token).toLocal8Bit() },
}));
return up;
}

View File

@ -26,17 +26,15 @@ class SkinUpload : public Net::NetRequest {
using Ptr = shared_qobject_ptr<SkinUpload>;
// Note this class takes ownership of the file.
SkinUpload(QString token, QString path, QString variant);
SkinUpload(QString path, QString variant);
virtual ~SkinUpload() = default;
static SkinUpload::Ptr make(QString token, QString path, QString variant);
void init() override;
protected:
virtual QNetworkReply* getReply(QNetworkRequest&) override;
private:
QString m_token;
QString m_path;
QString m_variant;
};

View File

@ -104,7 +104,7 @@ void Flame::FileResolvingTask::netJobFinished()
auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash);
auto output = std::make_shared<QByteArray>();
auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output);
QObject::connect(dl.get(), &Net::ApiDownload::succeeded, [&out]() { out.resolved = true; });
QObject::connect(dl.get(), &Task::succeeded, [&out]() { out.resolved = true; });
m_checkJob->addNetAction(dl);
blockedProjects.insert(&out, output);
@ -176,7 +176,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId);
auto dl = Net::ApiDownload::makeByteArray(url, output);
qDebug() << "Fetching url slug for file:" << mod->fileName;
QObject::connect(dl.get(), &Net::ApiDownload::succeeded, [block, index, output]() {
QObject::connect(dl.get(), &Task::succeeded, [block, index, output]() {
auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
auto json = QJsonDocument::fromJson(*output);
auto base =

View File

@ -1,4 +1,5 @@
#include "FlameCheckUpdate.h"
#include "Application.h"
#include "FlameAPI.h"
#include "FlameModIndex.h"
@ -124,11 +125,6 @@ void FlameCheckUpdate::executeTask()
int i = 0;
for (auto* mod : m_mods) {
if (!mod->enabled()) {
emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!"));
continue;
}
setStatus(tr("Getting API response from CurseForge for '%1'...").arg(mod->name()));
setProgress(i++, m_mods.size());
@ -177,7 +173,7 @@ void FlameCheckUpdate::executeTask()
auto download_task = makeShared<ResourceDownloadTask>(pack, latest_ver.value(), m_mods_folder);
m_updatable.emplace_back(pack->name, mod->metadata()->hash, old_version, latest_ver->version, latest_ver->version_type,
api.getModFileChangelog(latest_ver->addonId.toInt(), latest_ver->fileId.toInt()),
ModPlatform::ResourceProvider::FLAME, download_task);
ModPlatform::ResourceProvider::FLAME, download_task, mod->enabled());
}
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, latest_ver.value()));
}

View File

@ -42,11 +42,6 @@ void ModrinthCheckUpdate::executeTask()
auto hashing_task =
makeShared<ConcurrentTask>(this, "MakeModrinthHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
for (auto* mod : m_mods) {
if (!mod->enabled()) {
emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!"));
continue;
}
auto hash = mod->metadata()->hash;
// Sadly the API can only handle one hash type per call, se we
@ -95,8 +90,7 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr<QByteArray> resp
// If the returned project is empty, but we have Modrinth metadata,
// it means this specific version is not available
if (project_obj.isEmpty()) {
qDebug() << "Mod " << m_mappings.find(hash).value()->name() << " got an empty response."
<< "Hash: " << hash;
qDebug() << "Mod " << m_mappings.find(hash).value()->name() << " got an empty response." << "Hash: " << hash;
continue;
}
@ -153,7 +147,7 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr<QByteArray> resp
auto download_task = makeShared<ResourceDownloadTask>(pack, project_ver, m_mods_folder);
m_updatable.emplace_back(pack->name, hash, mod->version(), project_ver.version_number, project_ver.version_type,
project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task);
project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task, mod->enabled());
}
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, project_ver));
}

View File

@ -18,47 +18,29 @@
*/
#include "net/ApiDownload.h"
#include "ByteArraySink.h"
#include "ChecksumValidator.h"
#include "MetaCacheSink.h"
#include "net/ApiHeaderProxy.h"
namespace Net {
auto ApiDownload::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr
Download::Ptr ApiDownload::makeCached(QUrl url, MetaEntryPtr entry, Download::Options options)
{
auto dl = makeShared<ApiDownload>();
dl->m_url = url;
dl->setObjectName(QString("CACHE:") + url.toString());
dl->m_options = options;
auto md5Node = new ChecksumValidator(QCryptographicHash::Md5);
auto cachedNode = new MetaCacheSink(entry, md5Node, options.testFlag(Option::MakeEternal));
dl->m_sink.reset(cachedNode);
auto dl = Download::makeCached(url, entry, options);
dl->addHeaderProxy(new ApiHeaderProxy());
return dl;
}
auto ApiDownload::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options) -> Download::Ptr
Download::Ptr ApiDownload::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Download::Options options)
{
auto dl = makeShared<ApiDownload>();
dl->m_url = url;
dl->setObjectName(QString("BYTES:") + url.toString());
dl->m_options = options;
dl->m_sink.reset(new ByteArraySink(output));
auto dl = Download::makeByteArray(url, output, options);
dl->addHeaderProxy(new ApiHeaderProxy());
return dl;
}
auto ApiDownload::makeFile(QUrl url, QString path, Options options) -> Download::Ptr
Download::Ptr ApiDownload::makeFile(QUrl url, QString path, Download::Options options)
{
auto dl = makeShared<ApiDownload>();
dl->m_url = url;
dl->setObjectName(QString("FILE:") + url.toString());
dl->m_options = options;
dl->m_sink.reset(new FileSink(path));
auto dl = Download::makeFile(url, path, options);
dl->addHeaderProxy(new ApiHeaderProxy());
return dl;
}
void ApiDownload::init()
{
auto api_headers = new ApiHeaderProxy();
addHeaderProxy(api_headers);
}
} // namespace Net

View File

@ -19,20 +19,14 @@
#pragma once
#include "ApiHeaderProxy.h"
#include "Download.h"
namespace Net {
class ApiDownload : public Download {
public:
virtual ~ApiDownload() = default;
static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr;
static auto makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options = Option::NoOptions) -> Download::Ptr;
static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr;
void init() override;
};
namespace ApiDownload {
Download::Ptr makeCached(QUrl url, MetaEntryPtr entry, Download::Options options = Download::Option::NoOptions);
Download::Ptr makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Download::Options options = Download::Option::NoOptions);
Download::Ptr makeFile(QUrl url, QString path, Download::Options options = Download::Option::NoOptions);
}; // namespace ApiDownload
} // namespace Net

View File

@ -18,22 +18,15 @@
*/
#include "net/ApiUpload.h"
#include "ByteArraySink.h"
#include "net/ApiHeaderProxy.h"
namespace Net {
Upload::Ptr ApiUpload::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data)
{
auto up = makeShared<ApiUpload>();
up->m_url = std::move(url);
up->m_sink.reset(new ByteArraySink(output));
up->m_post_data = std::move(m_post_data);
auto up = Upload::makeByteArray(url, output, m_post_data);
up->addHeaderProxy(new ApiHeaderProxy());
return up;
}
void ApiUpload::init()
{
auto api_headers = new ApiHeaderProxy();
addHeaderProxy(api_headers);
}
} // namespace Net

View File

@ -19,18 +19,12 @@
#pragma once
#include "ApiHeaderProxy.h"
#include "Upload.h"
namespace Net {
class ApiUpload : public Upload {
public:
virtual ~ApiUpload() = default;
static Upload::Ptr makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data);
void init() override;
namespace ApiUpload {
Upload::Ptr makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data);
};
} // namespace Net

View File

@ -62,8 +62,6 @@ void NetRequest::addValidator(Validator* v)
void NetRequest::executeTask()
{
init();
setStatus(tr("Requesting %1").arg(StringUtils::truncateUrlHumanFriendly(m_url, 80)));
if (getState() == Task::State::AbortedByUser) {

View File

@ -72,8 +72,6 @@ class NetRequest : public Task {
void setNetwork(shared_qobject_ptr<QNetworkAccessManager> network) { m_network = network; }
void addHeaderProxy(Net::HeaderProxy* proxy) { m_headerProxies.push_back(std::shared_ptr<Net::HeaderProxy>(proxy)); }
virtual void init() {}
QUrl url() const;
void setUrl(QUrl url) { m_url = url; }
int replyStatusCode() const;

View File

@ -4,6 +4,7 @@
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
* Copyright (c) 2023 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
@ -27,7 +28,7 @@ namespace Net {
class RawHeaderProxy : public HeaderProxy {
public:
RawHeaderProxy() : HeaderProxy() {}
RawHeaderProxy(QList<HeaderPair> headers = {}) : HeaderProxy(), m_headers(std::move(headers)) {};
virtual ~RawHeaderProxy() = default;
public:
@ -36,6 +37,7 @@ class RawHeaderProxy : public HeaderProxy {
void addHeader(const HeaderPair& header) { m_headers.append(header); }
void addHeader(const QByteArray& headerName, const QByteArray& headerValue) { m_headers.append({ headerName, headerValue }); }
void addHeaders(const QList<HeaderPair>& headers) { m_headers.append(headers); }
void setHeaders(QList<HeaderPair> headers) { m_headers = headers; };
private:
QList<HeaderPair> m_headers;

View File

@ -1,39 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 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/>.
*
*/
#pragma once
#include "net/HeaderProxy.h"
namespace Net {
class StaticHeaderProxy : public HeaderProxy {
public:
StaticHeaderProxy(QList<HeaderPair> hdrs = {}) : HeaderProxy(), m_hdrs(hdrs) {};
virtual ~StaticHeaderProxy() = default;
public:
virtual QList<HeaderPair> headers(const QNetworkRequest&) const override { return m_hdrs; };
void setHeaders(QList<HeaderPair> hdrs) { m_hdrs = hdrs; };
private:
QList<HeaderPair> m_hdrs;
};
} // namespace Net

View File

@ -46,7 +46,7 @@
#include <memory>
#include "BuildConfig.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
Net::NetRequest::Ptr ImgurAlbumCreation::make(std::shared_ptr<ImgurAlbumCreation::Result> output, QList<ScreenShot::Ptr> screenshots)
{
@ -54,6 +54,10 @@ Net::NetRequest::Ptr ImgurAlbumCreation::make(std::shared_ptr<ImgurAlbumCreation
up->m_url = BuildConfig.IMGUR_BASE_URL + "album";
up->m_sink.reset(new Sink(output));
up->m_screenshots = screenshots;
up->addHeaderProxy(new Net::RawHeaderProxy(
QList<Net::HeaderPair>{ { "Content-Type", "application/x-www-form-urlencoded" },
{ "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() },
{ "Accept", "application/json" } }));
return up;
}
@ -67,16 +71,6 @@ QNetworkReply* ImgurAlbumCreation::getReply(QNetworkRequest& request)
return m_network->post(request, data);
}
void ImgurAlbumCreation::init()
{
qDebug() << "Setting up imgur upload";
auto api_headers = new Net::StaticHeaderProxy(
QList<Net::HeaderPair>{ { "Content-Type", "application/x-www-form-urlencoded" },
{ "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() },
{ "Accept", "application/json" } });
addHeaderProxy(api_headers);
}
auto ImgurAlbumCreation::Sink::init(QNetworkRequest& request) -> Task::State
{
m_output.clear();

View File

@ -67,8 +67,6 @@ class ImgurAlbumCreation : public Net::NetRequest {
static NetRequest::Ptr make(std::shared_ptr<Result> output, QList<ScreenShot::Ptr> screenshots);
QNetworkReply* getReply(QNetworkRequest& request) override;
void init() override;
private:
QList<ScreenShot::Ptr> m_screenshots;
};

View File

@ -36,7 +36,7 @@
#include "ImgurUpload.h"
#include "BuildConfig.h"
#include "net/StaticHeaderProxy.h"
#include "net/RawHeaderProxy.h"
#include <QDebug>
#include <QFile>
@ -47,14 +47,6 @@
#include <QNetworkRequest>
#include <QUrl>
void ImgurUpload::init()
{
qDebug() << "Setting up imgur upload";
auto api_headers = new Net::StaticHeaderProxy(QList<Net::HeaderPair>{
{ "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() }, { "Accept", "application/json" } });
addHeaderProxy(api_headers);
}
QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request)
{
auto file = new QFile(m_fileInfo.absoluteFilePath(), this);
@ -125,5 +117,7 @@ Net::NetRequest::Ptr ImgurUpload::make(ScreenShot::Ptr m_shot)
auto up = makeShared<ImgurUpload>(m_shot->m_file);
up->m_url = std::move(BuildConfig.IMGUR_BASE_URL + "image");
up->m_sink.reset(new Sink(m_shot));
up->addHeaderProxy(new Net::RawHeaderProxy(QList<Net::HeaderPair>{
{ "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() }, { "Accept", "application/json" } }));
return up;
}

View File

@ -62,8 +62,6 @@ class ImgurUpload : public Net::NetRequest {
static NetRequest::Ptr make(ScreenShot::Ptr m_shot);
void init() override;
private:
virtual QNetworkReply* getReply(QNetworkRequest&) override;
const QFileInfo m_fileInfo;

View File

@ -34,6 +34,7 @@
*/
#include "ProfileSetupDialog.h"
#include "net/RawHeaderProxy.h"
#include "ui_ProfileSetupDialog.h"
#include <QAction>
@ -46,7 +47,6 @@
#include <Application.h>
#include "minecraft/auth/Parsers.h"
#include "net/StaticHeaderProxy.h"
#include "net/Upload.h"
ProfileSetupDialog::ProfileSetupDialog(MinecraftAccountPtr accountToSetup, QWidget* parent)
@ -160,7 +160,7 @@ void ProfileSetupDialog::checkName(const QString& name)
if (m_check_task)
disconnect(m_check_task.get(), nullptr, this, nullptr);
m_check_task = Net::Download::makeByteArray(url, m_check_response);
m_check_task->addHeaderProxy(new Net::StaticHeaderProxy(headers));
m_check_task->addHeaderProxy(new Net::RawHeaderProxy(headers));
connect(m_check_task.get(), &Task::finished, this, &ProfileSetupDialog::checkFinished);
@ -204,7 +204,7 @@ void ProfileSetupDialog::setupProfile(const QString& profileName)
m_profile_response.reset(new QByteArray());
m_profile_task = Net::Upload::makeByteArray(url, m_profile_response, payloadTemplate.arg(profileName).toUtf8());
m_profile_task->addHeaderProxy(new Net::StaticHeaderProxy(headers));
m_profile_task->addHeaderProxy(new Net::RawHeaderProxy(headers));
connect(m_profile_task.get(), &Task::finished, this, &ProfileSetupDialog::setupProfileFinished);

View File

@ -36,6 +36,8 @@
*/
#include "InstanceSettingsPage.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/WorldList.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/java/InstallJavaDialog.h"
#include "ui_InstanceSettingsPage.h"
@ -75,6 +77,22 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance* inst, QWidget* parent)
connect(ui->useNativeGLFWCheck, &QAbstractButton::toggled, this, &InstanceSettingsPage::onUseNativeGLFWChanged);
connect(ui->useNativeOpenALCheck, &QAbstractButton::toggled, this, &InstanceSettingsPage::onUseNativeOpenALChanged);
auto mInst = dynamic_cast<MinecraftInstance*>(inst);
m_world_quickplay_supported = mInst && mInst->traits().contains("feature:is_quick_play_singleplayer");
if (m_world_quickplay_supported) {
auto worlds = mInst->worldList();
worlds->update();
for (const auto& world : worlds->allWorlds()) {
ui->worldsCb->addItem(world.folderName());
}
} else {
ui->worldsCb->hide();
ui->worldJoinButton->hide();
ui->serverJoinAddressButton->setChecked(true);
ui->serverJoinAddress->setEnabled(true);
ui->serverJoinAddressButton->setStyleSheet("QRadioButton::indicator { width: 0px; height: 0px; }");
}
loadSettings();
updateThresholds();
@ -257,9 +275,16 @@ void InstanceSettingsPage::applySettings()
bool joinServerOnLaunch = ui->serverJoinGroupBox->isChecked();
m_settings->set("JoinServerOnLaunch", joinServerOnLaunch);
if (joinServerOnLaunch) {
m_settings->set("JoinServerOnLaunchAddress", ui->serverJoinAddress->text());
if (ui->serverJoinAddressButton->isChecked() || !m_world_quickplay_supported) {
m_settings->set("JoinServerOnLaunchAddress", ui->serverJoinAddress->text());
m_settings->reset("JoinWorldOnLaunch");
} else {
m_settings->set("JoinWorldOnLaunch", ui->worldsCb->currentText());
m_settings->reset("JoinServerOnLaunchAddress");
}
} else {
m_settings->reset("JoinServerOnLaunchAddress");
m_settings->reset("JoinWorldOnLaunch");
}
// Use an account for this instance
@ -379,7 +404,25 @@ void InstanceSettingsPage::loadSettings()
ui->recordGameTime->setChecked(m_settings->get("RecordGameTime").toBool());
ui->serverJoinGroupBox->setChecked(m_settings->get("JoinServerOnLaunch").toBool());
ui->serverJoinAddress->setText(m_settings->get("JoinServerOnLaunchAddress").toString());
if (auto server = m_settings->get("JoinServerOnLaunchAddress").toString(); !server.isEmpty()) {
ui->serverJoinAddress->setText(server);
ui->serverJoinAddressButton->setChecked(true);
ui->worldJoinButton->setChecked(false);
ui->serverJoinAddress->setEnabled(true);
ui->worldsCb->setEnabled(false);
} else if (auto world = m_settings->get("JoinWorldOnLaunch").toString(); !world.isEmpty() && m_world_quickplay_supported) {
ui->worldsCb->setCurrentText(world);
ui->serverJoinAddressButton->setChecked(false);
ui->worldJoinButton->setChecked(true);
ui->serverJoinAddress->setEnabled(false);
ui->worldsCb->setEnabled(true);
} else {
ui->serverJoinAddressButton->setChecked(true);
ui->worldJoinButton->setChecked(false);
ui->serverJoinAddress->setEnabled(true);
ui->worldsCb->setEnabled(false);
}
ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool());
updateAccountsMenu();
@ -549,3 +592,13 @@ void InstanceSettingsPage::updateThresholds()
ui->labelMaxMemIcon->setPixmap(pix);
}
}
void InstanceSettingsPage::on_serverJoinAddressButton_toggled(bool checked)
{
ui->serverJoinAddress->setEnabled(checked);
}
void InstanceSettingsPage::on_worldJoinButton_toggled(bool checked)
{
ui->worldsCb->setEnabled(checked);
}

View File

@ -71,6 +71,8 @@ class InstanceSettingsPage : public QWidget, public BasePage {
void on_javaBrowseBtn_clicked();
void on_javaDownloadBtn_clicked();
void on_maxMemSpinBox_valueChanged(int i);
void on_serverJoinAddressButton_toggled(bool checked);
void on_worldJoinButton_toggled(bool checked);
void onUseNativeGLFWChanged(bool checked);
void onUseNativeOpenALChanged(bool checked);
@ -91,4 +93,5 @@ class InstanceSettingsPage : public QWidget, public BasePage {
BaseInstance* m_instance;
SettingsObjectPtr m_settings;
unique_qobject_ptr<JavaCommon::TestCheck> checker;
bool m_world_quickplay_supported;
};

View File

@ -667,7 +667,7 @@
<item>
<widget class="QGroupBox" name="serverJoinGroupBox">
<property name="title">
<string>Set a server to join on launch</string>
<string>Set a target to join on launch</string>
</property>
<property name="checkable">
<bool>true</bool>
@ -675,26 +675,26 @@
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<layout class="QGridLayout" name="serverJoinLayout">
<item row="0" column="0">
<widget class="QLabel" name="serverJoinAddressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Server address:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="serverJoinAddress"/>
</item>
</layout>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QRadioButton" name="serverJoinAddressButton">
<property name="text">
<string>Server address:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="serverJoinAddress"/>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="worldJoinButton">
<property name="text">
<string>Singleplayer world</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="worldsCb"/>
</item>
</layout>
</widget>

View File

@ -168,7 +168,7 @@ class ServersModel : public QAbstractListModel {
m_saveTimer.setInterval(5000);
connect(&m_saveTimer, &QTimer::timeout, this, &ServersModel::save_internal);
}
virtual ~ServersModel() {};
virtual ~ServersModel() = default;
void observe()
{
@ -731,7 +731,7 @@ void ServersPage::on_actionMove_Down_triggered()
void ServersPage::on_actionJoin_triggered()
{
const auto& address = m_model->at(currentServer)->m_address;
APPLICATION->launch(m_inst, true, false, std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(address)));
APPLICATION->launch(m_inst, true, false, std::make_shared<MinecraftTarget>(MinecraftTarget::parse(address, false)));
}
#include "ServersPage.moc"

View File

@ -82,7 +82,7 @@ class WorldListProxyModel : public QSortFilterProxyModel {
}
};
WorldListPage::WorldListPage(BaseInstance* inst, std::shared_ptr<WorldList> worlds, QWidget* parent)
WorldListPage::WorldListPage(InstancePtr inst, std::shared_ptr<WorldList> worlds, QWidget* parent)
: QMainWindow(parent), m_inst(inst), ui(new Ui::WorldListPage), m_worlds(worlds)
{
ui->setupUi(this);
@ -113,6 +113,11 @@ void WorldListPage::openedImpl()
{
m_worlds->startWatching();
auto mInst = std::dynamic_pointer_cast<MinecraftInstance>(m_inst);
if (!mInst || !mInst->traits().contains("feature:is_quick_play_singleplayer")) {
ui->toolBar->removeAction(ui->actionJoin);
}
auto const setting_name = QString("WideBarVisibility_%1").arg(id());
if (!APPLICATION->settings()->contains(setting_name))
m_wide_bar_setting = APPLICATION->settings()->registerSetting(setting_name);
@ -339,6 +344,14 @@ void WorldListPage::worldChanged([[maybe_unused]] const QModelIndex& current, [[
ui->actionDatapacks->setEnabled(enable);
bool hasIcon = !index.data(WorldList::IconFileRole).isNull();
ui->actionReset_Icon->setEnabled(enable && hasIcon);
auto mInst = std::dynamic_pointer_cast<MinecraftInstance>(m_inst);
auto supportsJoin = mInst && mInst->traits().contains("feature:is_quick_play_singleplayer");
ui->actionJoin->setEnabled(enable && supportsJoin);
if (!supportsJoin) {
ui->toolBar->removeAction(ui->actionJoin);
}
}
void WorldListPage::on_actionAdd_triggered()
@ -418,4 +431,15 @@ void WorldListPage::on_actionRefresh_triggered()
m_worlds->update();
}
void WorldListPage::on_actionJoin_triggered()
{
QModelIndex index = getSelectedWorld();
if (!index.isValid()) {
return;
}
auto worldVariant = m_worlds->data(index, WorldList::ObjectRole);
auto world = (World*)worldVariant.value<void*>();
APPLICATION->launch(m_inst, true, false, std::make_shared<MinecraftTarget>(MinecraftTarget::parse(world->folderName(), true)));
}
#include "WorldListPage.moc"

View File

@ -53,7 +53,7 @@ class WorldListPage : public QMainWindow, public BasePage {
Q_OBJECT
public:
explicit WorldListPage(BaseInstance* inst, std::shared_ptr<WorldList> worlds, QWidget* parent = 0);
explicit WorldListPage(InstancePtr inst, std::shared_ptr<WorldList> worlds, QWidget* parent = 0);
virtual ~WorldListPage();
virtual QString displayName() const override { return tr("Worlds"); }
@ -72,7 +72,7 @@ class WorldListPage : public QMainWindow, public BasePage {
QMenu* createPopupMenu() override;
protected:
BaseInstance* m_inst;
InstancePtr m_inst;
private:
QModelIndex getSelectedWorld();
@ -101,6 +101,7 @@ class WorldListPage : public QMainWindow, public BasePage {
void on_actionReset_Icon_triggered();
void worldChanged(const QModelIndex& current, const QModelIndex& previous);
void mceditState(LoggedProcess::State state);
void on_actionJoin_triggered();
void ShowContextMenu(const QPoint& pos);
};

View File

@ -81,6 +81,7 @@
</attribute>
<addaction name="actionAdd"/>
<addaction name="separator"/>
<addaction name="actionJoin"/>
<addaction name="actionRename"/>
<addaction name="actionCopy"/>
<addaction name="actionRemove"/>
@ -97,6 +98,11 @@
<string>Add</string>
</property>
</action>
<action name="actionJoin">
<property name="text">
<string>Join</string>
</property>
</action>
<action name="actionRename">
<property name="text">
<string>Rename</string>

View File

@ -309,4 +309,15 @@ bool WideBar::checkHash(QByteArray const& old_hash) const
return old_hash == getHash();
}
void WideBar::removeAction(QAction* action)
{
auto iter = getMatching(action);
if (iter == m_entries.end())
return;
iter->bar_action->setVisible(false);
removeAction(iter->bar_action);
m_entries.erase(iter);
}
#include "WideBar.moc"

View File

@ -38,6 +38,8 @@ class WideBar : public QToolBar {
[[nodiscard]] QByteArray getVisibilityState() const;
void setVisibilityState(QByteArray&&);
void removeAction(QAction* action);
private:
struct BarEntry {
enum class Type { None, Action, Separator, Spacer } type = Type::None;

View File

@ -70,7 +70,7 @@ public abstract class AbstractLauncher implements Launcher {
// secondary parameters
protected final int width, height;
protected final boolean maximize;
protected final String serverAddress, serverPort;
protected final String serverAddress, serverPort, worldName;
protected final String mainClassName;
@ -80,6 +80,7 @@ public abstract class AbstractLauncher implements Launcher {
serverAddress = params.getString("serverAddress", null);
serverPort = params.getString("serverPort", null);
worldName = params.getString("worldName", null);
String windowParams = params.getString("windowParams", null);

View File

@ -62,13 +62,15 @@ import java.util.Collections;
import java.util.List;
public final class StandardLauncher extends AbstractLauncher {
private final boolean quickPlaySupported;
private final boolean quickPlayMultiplayerSupported;
private final boolean quickPlaySingleplayerSupported;
public StandardLauncher(Parameters params) {
super(params);
List<String> traits = params.getList("traits", Collections.<String>emptyList());
quickPlaySupported = traits.contains("feature:is_quick_play_multiplayer");
quickPlayMultiplayerSupported = traits.contains("feature:is_quick_play_multiplayer");
quickPlaySingleplayerSupported = traits.contains("feature:is_quick_play_singleplayer");
}
@Override
@ -83,7 +85,7 @@ public final class StandardLauncher extends AbstractLauncher {
}
if (serverAddress != null) {
if (quickPlaySupported) {
if (quickPlayMultiplayerSupported) {
// as of 23w14a
gameArgs.add("--quickPlayMultiplayer");
gameArgs.add(serverAddress + ':' + serverPort);
@ -93,6 +95,9 @@ public final class StandardLauncher extends AbstractLauncher {
gameArgs.add("--port");
gameArgs.add(serverPort);
}
} else if (worldName != null && quickPlaySingleplayerSupported) {
gameArgs.add("--quickPlaySingleplayer");
gameArgs.add(worldName);
}
// find and invoke the main method

View File

@ -24,9 +24,8 @@
overlays.default = final: prev: let
version = builtins.substring 0 8 self.lastModifiedDate or "dirty";
in {
prismlauncher-unwrapped = prev.qt6Packages.callPackage ./pkg {
prismlauncher-unwrapped = prev.callPackage ./pkg {
inherit (inputs) libnbtplusplus;
inherit ((final.darwin or prev.darwin).apple_sdk.frameworks) Cocoa;
inherit version;
};

View File

@ -1,27 +1,27 @@
{
lib,
stdenv,
canonicalize-jars-hook,
cmake,
cmark,
Cocoa,
ninja,
jdk17,
zlib,
qtbase,
qtnetworkauth,
quazip,
darwin,
extra-cmake-modules,
tomlplusplus,
ghc_filesystem,
gamemode,
ghc_filesystem,
jdk17,
kdePackages,
ninja,
stripJavaArchivesHook,
tomlplusplus,
zlib,
msaClientID ? null,
gamemodeSupport ? stdenv.isLinux,
version,
libnbtplusplus,
}:
assert lib.assertMsg (stdenv.isLinux || !gamemodeSupport) "gamemodeSupport is only available on Linux";
stdenv.mkDerivation rec {
assert lib.assertMsg (
gamemodeSupport -> stdenv.isLinux
) "gamemodeSupport is only available on Linux.";
stdenv.mkDerivation {
pname = "prismlauncher-unwrapped";
inherit version;
@ -39,49 +39,68 @@ assert lib.assertMsg (stdenv.isLinux || !gamemodeSupport) "gamemodeSupport is on
]);
};
nativeBuildInputs = [extra-cmake-modules cmake jdk17 ninja canonicalize-jars-hook];
buildInputs =
[
qtbase
qtnetworkauth
zlib
quazip
ghc_filesystem
tomlplusplus
cmark
]
++ lib.optional gamemodeSupport gamemode
++ lib.optionals stdenv.isDarwin [Cocoa];
hardeningEnable = lib.optionals stdenv.isLinux ["pie"];
cmakeFlags =
[
"-DLauncher_BUILD_PLATFORM=nixpkgs"
]
++ lib.optionals (msaClientID != null) ["-DLauncher_MSA_CLIENT_ID=${msaClientID}"]
++ lib.optionals (lib.versionOlder qtbase.version "6") ["-DLauncher_QT_VERSION_MAJOR=5"]
++ lib.optionals stdenv.isDarwin ["-DINSTALL_BUNDLE=nodeps" "-DMACOSX_SPARKLE_UPDATE_FEED_URL=''"];
postUnpack = ''
rm -rf source/libraries/libnbtplusplus
ln -s ${libnbtplusplus} source/libraries/libnbtplusplus
'';
nativeBuildInputs = [
cmake
ninja
extra-cmake-modules
jdk17
stripJavaArchivesHook
];
buildInputs =
[
cmark
ghc_filesystem
kdePackages.qtbase
kdePackages.qtnetworkauth
kdePackages.quazip
tomlplusplus
zlib
]
++ lib.optionals stdenv.isDarwin [darwin.apple_sdk.frameworks.Cocoa]
++ lib.optional gamemodeSupport gamemode;
hardeningEnable = lib.optionals stdenv.isLinux ["pie"];
cmakeFlags =
[
(lib.cmakeFeature "Launcher_BUILD_PLATFORM" "nixpkgs")
]
++ lib.optionals (msaClientID != null) [
(lib.cmakeFeature "Launcher_MSA_CLIENT_ID" (toString msaClientID))
]
++ lib.optionals (lib.versionOlder kdePackages.qtbase.version "6") [
(lib.cmakeFeature "Launcher_QT_VERSION_MAJOR" "5")
]
++ lib.optionals stdenv.isDarwin [
# we wrap our binary manually
(lib.cmakeFeature "INSTALL_BUNDLE" "nodeps")
# disable built-in updater
(lib.cmakeFeature "MACOSX_SPARKLE_UPDATE_FEED_URL" "''")
(lib.cmakeFeature "CMAKE_INSTALL_PREFIX" "${placeholder "out"}/Applications/")
];
dontWrapQtApps = true;
meta = with lib; {
mainProgram = "prismlauncher";
homepage = "https://prismlauncher.org/";
description = "A free, open source launcher for Minecraft";
meta = {
description = "Free, open source launcher for Minecraft";
longDescription = ''
Allows you to have multiple, separate instances of Minecraft (each with
their own mods, texture packs, saves, etc) and helps you manage them and
their associated options with a simple interface.
'';
platforms = with platforms; linux ++ darwin;
changelog = "https://github.com/PrismLauncher/PrismLauncher/releases/tag/${version}";
license = licenses.gpl3Only;
maintainers = with maintainers; [minion3665 Scrumplex getchoo];
homepage = "https://prismlauncher.org/";
license = lib.licenses.gpl3Only;
maintainers = with lib.maintainers; [
Scrumplex
getchoo
];
mainProgram = "prismlauncher";
platforms = lib.platforms.linux ++ lib.platforms.darwin;
};
}

View File

@ -3,94 +3,143 @@
stdenv,
symlinkJoin,
prismlauncher-unwrapped,
wrapQtAppsHook,
addOpenGLRunpath,
qtbase, # needed for wrapQtAppsHook
qtsvg,
qtwayland,
xorg,
libpulseaudio,
libGL,
flite,
gamemode,
glfw,
openal,
glfw-wayland-minecraft,
glxinfo,
jdk8,
jdk17,
jdk21,
gamemode,
flite,
glxinfo,
udev,
kdePackages,
libGL,
libpulseaudio,
libusb1,
msaClientID ? null,
gamemodeSupport ? stdenv.isLinux,
textToSpeechSupport ? stdenv.isLinux,
controllerSupport ? stdenv.isLinux,
jdks ? [jdk21 jdk17 jdk8],
makeWrapper,
openal,
pciutils,
udev,
vulkan-loader,
xorg,
additionalLibs ? [],
additionalPrograms ? [],
}: let
prismlauncherFinal = prismlauncher-unwrapped.override {
inherit msaClientID gamemodeSupport;
};
controllerSupport ? stdenv.isLinux,
gamemodeSupport ? stdenv.isLinux,
jdks ? [
jdk21
jdk17
jdk8
],
msaClientID ? null,
textToSpeechSupport ? stdenv.isLinux,
# Adds `glfw-wayland-minecraft` to `LD_LIBRARY_PATH`
# when launched on wayland, allowing for the game to be run natively.
# Make sure to enable "Use system installation of GLFW" in instance settings
# for this to take effect
#
# Warning: This build of glfw may be unstable, and the launcher
# itself can take slightly longer to start
withWaylandGLFW ? false,
}:
assert lib.assertMsg (
controllerSupport -> stdenv.isLinux
) "controllerSupport only has an effect on Linux.";
assert lib.assertMsg (
textToSpeechSupport -> stdenv.isLinux
) "textToSpeechSupport only has an effect on Linux.";
assert lib.assertMsg (
withWaylandGLFW -> stdenv.isLinux
) "withWaylandGLFW is only available on Linux."; let
prismlauncher' = prismlauncher-unwrapped.override {inherit msaClientID gamemodeSupport;};
in
symlinkJoin {
name = "prismlauncher-${prismlauncherFinal.version}";
name = "prismlauncher-${prismlauncher'.version}";
paths = [prismlauncherFinal];
paths = [prismlauncher'];
nativeBuildInputs = [
wrapQtAppsHook
];
nativeBuildInputs =
[kdePackages.wrapQtAppsHook]
# purposefully using a shell wrapper here for variable expansion
# see https://github.com/NixOS/nixpkgs/issues/172583
++ lib.optional withWaylandGLFW makeWrapper;
buildInputs =
[
qtbase
qtsvg
kdePackages.qtbase
kdePackages.qtsvg
]
++ lib.optional (lib.versionAtLeast qtbase.version "6" && stdenv.isLinux) qtwayland;
++ lib.optional (
lib.versionAtLeast kdePackages.qtbase.version "6" && stdenv.isLinux
)
kdePackages.qtwayland;
postBuild = ''
wrapQtAppsHook
'';
env = {
waylandPreExec = lib.optionalString withWaylandGLFW ''
if [ -n "$WAYLAND_DISPLAY" ]; then
export LD_LIBRARY_PATH=${lib.getLib glfw-wayland-minecraft}/lib:"$LD_LIBRARY_PATH"
fi
'';
};
postBuild =
lib.optionalString withWaylandGLFW ''
qtWrapperArgs+=(--run "$waylandPreExec")
''
+ ''
wrapQtAppsHook
'';
qtWrapperArgs = let
runtimeLibs =
(with xorg; [
libX11
libXext
libXcursor
libXrandr
libXxf86vm
])
++ [
[
# lwjgl
glfw
libpulseaudio
libGL
glfw
openal
stdenv.cc.cc.lib
# oshi
udev
vulkan-loader # VulkanMod's lwjgl
udev # oshi
xorg.libX11
xorg.libXext
xorg.libXcursor
xorg.libXrandr
xorg.libXxf86vm
]
++ lib.optional gamemodeSupport gamemode.lib
++ lib.optional textToSpeechSupport flite
++ lib.optional gamemodeSupport gamemode.lib
++ lib.optional controllerSupport libusb1
++ additionalLibs;
runtimePrograms =
[
xorg.xrandr
glxinfo
pciutils # need lspci
xorg.xrandr # needed for LWJGL [2.9.2, 3) https://github.com/LWJGL/lwjgl/issues/128
]
++ additionalPrograms;
in
["--prefix PRISMLAUNCHER_JAVA_PATHS : ${lib.makeSearchPath "bin/java" jdks}"]
++ lib.optionals stdenv.isLinux [
"--set LD_LIBRARY_PATH ${addOpenGLRunpath.driverLink}/lib:${lib.makeLibraryPath runtimeLibs}"
# xorg.xrandr needed for LWJGL [2.9.2, 3) https://github.com/LWJGL/lwjgl/issues/128
"--prefix PATH : ${lib.makeBinPath runtimePrograms}"
];
inherit (prismlauncherFinal) meta;
meta = {
inherit
(prismlauncher'.meta)
description
longDescription
homepage
changelog
license
maintainers
mainProgram
platforms
;
};
}