Skip to content

Commit 4c9e096

Browse files
feat(auth): add support for multiprofile custom accounts (#148)
2 parents 88dc927 + 326f1a4 commit 4c9e096

6 files changed

Lines changed: 126 additions & 12 deletions

File tree

launcher/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ set(NET_SOURCES
121121
net/Download.h
122122
net/FileSink.cpp
123123
net/FileSink.h
124+
net/Head.cpp
125+
net/Head.h
124126
net/HttpMetaCache.cpp
125127
net/HttpMetaCache.h
126128
net/MetaCacheSink.cpp

launcher/minecraft/auth/custom/steps/CustomAuthStep.cpp

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
#include "CustomAuthStep.h"
2020

21+
#include <QInputDialog>
22+
2123
#include "Application.h"
2224
#include "Logging.h"
2325
#include "net/NetUtils.h"
@@ -77,7 +79,11 @@ QString CustomAuthStep::requestTemplate()
7779
{
7880
"accessToken": "%1",
7981
"clientToken": "%2",
80-
"requestUser": false
82+
"requestUser": false,
83+
"selectedProfile": {
84+
"id": "%3",
85+
"name": "%4"
86+
}
8187
}
8288
)XXX";
8389
}
@@ -88,7 +94,8 @@ QString CustomAuthStep::fillRequest()
8894
if (m_action == AuthFlow::Action::Login) {
8995
return requestTemplate().arg(m_data->accountLogin, m_password, clientID());
9096
} else {
91-
return requestTemplate().arg(m_data->yggdrasilToken.token, m_data->clientID);
97+
return requestTemplate().arg(m_data->yggdrasilToken.token, m_data->clientID, m_data->minecraftProfile.id,
98+
m_data->minecraftProfile.name);
9299
}
93100
}
94101

@@ -106,9 +113,43 @@ bool CustomAuthStep::parseResponse()
106113

107114
m_data->clientID = jsonResponse["clientToken"].toString();
108115

109-
auto profile = jsonResponse["selectedProfile"].toObject();
110-
m_data->minecraftProfile.id = profile["id"].toString();
111-
m_data->minecraftProfile.name = profile["name"].toString();
116+
if (!jsonResponse["selectedProfile"].isNull()) {
117+
auto profile = jsonResponse["selectedProfile"].toObject();
118+
m_data->minecraftProfile.id = profile["id"].toString();
119+
m_data->minecraftProfile.name = profile["name"].toString();
120+
}
121+
122+
const QJsonArray profiles = jsonResponse["availableProfiles"].toArray();
123+
if (profiles.size() > 1) {
124+
const auto profileName = [](const auto& profile) {
125+
auto obj = profile.toObject();
126+
return obj["name"].toString();
127+
};
128+
129+
QStringList list;
130+
std::ranges::transform(profiles, std::back_inserter(list), profileName);
131+
132+
bool ok = false;
133+
QString selectedProfileName =
134+
QInputDialog::getItem(nullptr, tr("Select profile"), tr("Select profile for this account"), list, 0, false, &ok);
135+
136+
if (!ok) {
137+
return false;
138+
}
139+
140+
const auto it = std::ranges::find(profiles, selectedProfileName, profileName);
141+
if (it != profiles.end()) {
142+
auto profileObj = it->toObject();
143+
m_data->minecraftProfile = MinecraftProfile{ .id = profileObj["id"].toString(), .name = profileObj["name"].toString() };
144+
} else {
145+
return false;
146+
}
147+
}
148+
149+
if (profiles.size() == 1 && m_data->minecraftProfile.id.isEmpty()) {
150+
auto profileObj = profiles.first().toObject();
151+
m_data->minecraftProfile = MinecraftProfile{ .id = profileObj["id"].toString(), .name = profileObj["name"].toString() };
152+
}
112153

113154
return true;
114155
}
@@ -121,4 +162,4 @@ void CustomAuthStep::onRequestDone()
121162
return;
122163
}
123164
emit finished(AccountTaskState::STATE_WORKING, tr("Got authorization for %1 account").arg(authType()));
124-
}
165+
}

launcher/net/Head.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
/*
3+
* Freesm Launcher - Minecraft Launcher
4+
* Copyright (C) 2026 so5iso4ka <so5iso4ka@icloud.com>
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, version 3.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
#include <memory>
20+
21+
#include "ByteArraySink.h"
22+
23+
#include "Head.h"
24+
25+
26+
Net::Head::Ptr Net::Head::makeHeaderPairs(QUrl url, Options options)
27+
{
28+
auto dl = makeShared<Head>();
29+
dl->m_url = url;
30+
dl->setObjectName(QString("HEAD:") + url.toString());
31+
dl->m_options = options;
32+
dl->m_sink = std::make_unique<ByteArraySink>(std::make_shared<QByteArray>());
33+
return dl;
34+
}
35+
36+
QNetworkReply* Net::Head::getReply(QNetworkRequest& request)
37+
{
38+
return m_network->head(request);
39+
}

launcher/net/Head.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
/*
3+
* Freesm Launcher - Minecraft Launcher
4+
* Copyright (C) 2026 so5iso4ka <so5iso4ka@icloud.com>
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, version 3.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
#pragma once
20+
21+
#include "net/NetRequest.h"
22+
23+
namespace Net {
24+
class Head : public NetRequest {
25+
Q_OBJECT
26+
public:
27+
using Ptr = shared_qobject_ptr<Head>;
28+
Head() { logCat = taskDownloadLogC; }
29+
30+
static Ptr makeHeaderPairs(QUrl url, Options options = Option::NoOptions);
31+
32+
protected:
33+
QNetworkReply* getReply(QNetworkRequest& request) override;
34+
};
35+
} // namespace Net

launcher/ui/dialogs/CustomLoginDialog.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ void CustomLoginDialog::accept()
6161
setUserInputsEnabled(false);
6262
ui->progressBar->setVisible(true);
6363

64-
m_response = std::make_shared<QByteArray>();
65-
66-
m_requestTask = Net::Download::makeByteArray(url, m_response);
64+
m_requestTask = Net::Head::makeHeaderPairs(url);
6765
m_requestTask->setNetwork(APPLICATION->network());
6866

6967
connect(m_requestTask.get(), &Task::finished, this, &CustomLoginDialog::onUrlResolving);

launcher/ui/dialogs/CustomLoginDialog.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#include <QDialog>
1919

2020
#include "minecraft/auth/custom/CustomAccount.h"
21-
#include "net/Download.h"
21+
#include "net/Head.h"
2222
#include "tasks/Task.h"
2323

2424
namespace Ui {
@@ -56,6 +56,5 @@ class CustomLoginDialog : public QDialog {
5656
Ui::CustomLoginDialog* ui;
5757
CustomAccountPtr m_account;
5858
Task::Ptr m_loginTask;
59-
Net::Download::Ptr m_requestTask;
60-
std::shared_ptr<QByteArray> m_response;
59+
Net::Head::Ptr m_requestTask;
6160
};

0 commit comments

Comments
 (0)