Commit e6bab2a4 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Route mods' Google Play sign-in requests to this launcher

Minecraft 1.26.50+ runs only with the mcpelauncher-updates mod, which asks
for the Google Play sign-in when the game starts. The engine looked for a
binary named mcpelauncher-ui-qt, which this app does not have, so the mod got
no credentials and the game crashed during startup.

- Engine patch: MCPELAUNCHER_UI_EXECUTABLE names the credentials helper; the
  launcher sets it to itself when it starts the game.
- The request window now has explicit Allow and Deny buttons, shows which mod
  is asking and for which account, and explains what to do when signed out
  instead of failing on a missing account.
- Play asks for Google sign-in first when not signed in (trial mode aside),
  including for versions installed by another launcher.
Co-Authored-By: 's avatarClaude Opus 5.5 <noreply@anthropic.com>
parent 7855d8b9
......@@ -43,6 +43,11 @@ right-click > Open (or System Settings > Privacy & Security > Open Anyway).
Sign in with the Google account that bought Minecraft on Google Play. The
sign-in is stored encrypted on disk; its key lives in the macOS Keychain.
Minecraft 1.26.50 and newer also needs the `mcpelauncher-updates` mod, which the
launcher offers to turn on. Each time the game starts, the mod asks to use your
Google Play sign-in to confirm you own the game; the launcher shows that request
and the game only starts once you allow it.
**Windows**: run `AL-Arcade-MC-Launcher-<version>-windows-x64-setup.exe` (per-user
install, no administrator rights needed) or unpack the portable `.zip`. The
installer is not code-signed yet, so SmartScreen asks for More info > Run anyway.
......@@ -140,6 +145,7 @@ overrides the built-in URL for testing.
| Patch | Fixes |
|---|---|
| `mcpelauncher-core/0001-arm64-step-over-debug-breaks` | Debug break instructions in the game crashed or hung it on Apple Silicon; they are now stepped over |
| `mcpelauncher-core/0002-ui-executable-from-environment` | Mods that ask for the Google Play sign-in reach this launcher (the engine looked for a binary named `mcpelauncher-ui-qt`) |
| `mcpelauncher-client/0001-ignore-sigpipe` | The game quit when a network connection dropped (for example with a VPN) |
| `mcpelauncher-client/0002-macos-fullscreen-shortcut` | Ctrl+Cmd+F toggles fullscreen; F11 is taken by macOS |
| `game-window/0001-optional-non-retina-rendering` | Setting to render at half resolution on Retina screens for more frames per second |
......
......@@ -2,6 +2,7 @@
#include "launchersettings.h"
#include "profilemanager.h"
#include "EnvPathUtil.h"
#include <QCoreApplication>
#include <QFile>
#include <QDir>
#include "supportedandroidabis.h"
......@@ -32,6 +33,9 @@ void GameLauncher::start(bool disableGameLog, QString arch, bool hasVerifiedLice
QStringList args;
QStringList cargs;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
// Mods ask for Google Play credentials through the launcher; tell the engine
// where it is (patches/engine/mcpelauncher-core/0002).
env.insert("MCPELAUNCHER_UI_EXECUTABLE", QCoreApplication::applicationFilePath());
#ifdef __APPLE__
if (!LauncherSettings().retinaRendering())
env.insert("ALARCADE_RETINA", "0");
......
......@@ -59,6 +59,10 @@ Item {
}
readonly property bool statusBlocking: !!statusMsg.title && statusMsg.tone !== "success"
readonly property bool signedIn: googleLoginHelper.account !== null
// Owning the game is checked through Google Play, and the compatibility mod
// for new game versions asks for the sign-in at start, so playing the full
// game needs it even when the version was installed earlier.
readonly property bool needsSignInToPlay: !signedIn && !launcherSettings.trialMode
// "1.26.52.3 (arm64-v8a, beta)" -> title "1.26.52.3", details "arm64-v8a, beta"
readonly property string versionTitle: activeVersionName.split(" (")[0]
readonly property string versionDetails: {
......@@ -179,10 +183,16 @@ Item {
text: {
if (playScreen.statusChecking || !playScreen.activeVersionName)
return ""
if (playScreen.needsSignInToPlay)
return qsTr("Sign in to play").toUpperCase()
return (playScreen.activeVersionNeedsDownload ? qsTr("Download and play") : qsTr("Play")).toUpperCase()
}
enabled: !(gameLauncher.running || playScreen.statusChecking || progressbarVisible || updateChecker.active || !playScreen.activeVersionSupported || !playScreen.activeVersionName || playScreen.statusBlocking)
onClicked: {
if (playScreen.needsSignInToPlay) {
actionSignIn()
return
}
if (playScreen.activeVersionNeedsDownload) {
playDownloadTask.versionCode = getDownloadVersionCode()
if (playDownloadTask.versionCode === 0)
......
import QtQuick
import QtQuick.Window
import QtQuick.Dialogs
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.platform
import io.mrarm.mcpelauncher 1.0
import "../Components"
import ".."
// Started by the game engine (--request-google-credentials --mod <path>) when a
// mod asks for the Google Play sign-in, before the game window opens. Allowing
// prints CRED=<email>:<token> on stderr and exits 0; anything else exits 1.
Window {
id: window
visible: true
width: 640
height: 480
title: qsTr("Request Google Credentials")
width: 560
height: 500
minimumWidth: 460
minimumHeight: 440
title: qsTr("%1: Google Play sign-in").arg(APP_NAME)
color: Theme.background
flags: Qt.Window | Qt.WindowStaysOnTopHint
// Read by the upstream screens shown here.
property bool hasUpdate: false
property bool hasAskedForKey: false
property string updateDownloadUrl: ""
property bool isVersionsInitialized: false
property string currentGameDataDir: ""
StackView {
id: stackView
anchors.fill: parent
property bool answered: false
property bool unlocking: googleLoginHelperInstance.hasEncryptedCredentials && googleLoginHelperInstance.account === null
readonly property var account: googleLoginHelperInstance.account
readonly property string mode: !googleLoginHelperInstance.isSupported() ? "unsupported" : account === null ? "signedOut" : "ask"
readonly property string modName: {
const parts = SOURCE_MOD.split("/").filter(part => part.length > 0)
const mods = parts.lastIndexOf("mods")
if (mods >= 0 && mods + 1 < parts.length)
return parts[mods + 1]
const file = parts.length > 0 ? parts[parts.length - 1] : ""
return file.replace(/^lib/, "").replace(/\.so$/, "") || qsTr("A mod")
}
GoogleLoginHelper {
id: googleLoginHelperInstance
includeIncompatible: launcherSettings.showUnsupported
singleArch: launcherSettings.singleArch
chromeOS: launcherSettings.chromeOSMode || launcherSettings.trialMode
readonly property string maskedEmail: {
if (account === null)
return ""
const mail = account.accountIdentifier
const at = mail.indexOf("@")
if (mail.length < 4)
return mail
return mail.substring(0, 2) + "*".repeat(Math.max(3, (at > 2 ? at : mail.length) - 2)) + (at > 0 ? mail.substring(at) : "")
}
Component {
id: panelError
ErrorScreen {
onFinished: defaultnext()
}
function allow() {
answered = true
stdio.error("CRED=" + account.accountIdentifier + ":" + account.accountToken)
stdio.error("CREDB64=" + Qt.btoa(JSON.stringify(account)))
application.exit(0)
}
StdioHelper {
id: stdio
function deny() {
answered = true
application.exit(1)
}
Component {
id: panelDefault
ErrorScreen {
message: qsTr("A mod at '%1'<br/>is requesting access to your google credentials,<br/>to reject this request close this window").arg(SOURCE_MOD)
onFinished: {
stdio.error("CRED=" + googleLoginHelperInstance.account.accountIdentifier + ":" + googleLoginHelperInstance.account.accountToken)
stdio.error("CREDB64=" + Qt.btoa(JSON.stringify(googleLoginHelperInstance.account)))
window.close()
application.quit()
}
}
onClosing: {
if (!answered)
deny()
}
Component {
id: panelUnlock
UnlockScreen {
onFinished: {
next()
}
}
Component.onCompleted: {
raise()
requestActivate()
}
LauncherSettings {
id: launcherSettings
}
Connections {
target: window
function onClosing() {
application.quit()
}
}
function needsToLogIn() {
return googleLoginHelperInstance.account == null && !googleLoginHelperInstance.hasEncryptedCredentials && versionManagerInstance.versions.size === 0
StdioHelper {
id: stdio
}
Component.onCompleted: {
next()
GoogleLoginHelper {
id: googleLoginHelperInstance
includeIncompatible: launcherSettings.showUnsupported
singleArch: launcherSettings.singleArch
chromeOS: launcherSettings.chromeOSMode || launcherSettings.trialMode
}
function next() {
if (!googleLoginHelperInstance.isSupported()) {
stackView.push(panelError)
} else if (googleLoginHelperInstance.hasEncryptedCredentials && !hasAskedForKey) {
hasAskedForKey = true
stackView.push(panelUnlock)
} else {
defaultnext()
// Credentials protected by a password (older launcher versions) need it first.
Loader {
anchors.fill: parent
active: window.unlocking
sourceComponent: UnlockScreen {
onFinished: window.unlocking = false
}
}
function defaultnext() {
stackView.push(panelDefault)
Card {
anchors.centerIn: parent
width: Math.min(parent.width - 2 * Theme.space6, 480)
padding: Theme.space6
visible: !window.unlocking
ColumnLayout {
width: parent.width
spacing: Theme.space4
Rectangle {
Layout.alignment: Qt.AlignHCenter
implicitWidth: 52
implicitHeight: 52
radius: Theme.radiusLarge
color: window.mode === "ask" ? Theme.accentSoft : Theme.warningSoft
AppIcon {
anchors.centerIn: parent
iconName: window.mode === "ask" ? "shield-check" : "triangle-alert"
size: 24
color: window.mode === "ask" ? Theme.accentText : Theme.warning
}
}
Text {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
color: Theme.text
font.pixelSize: Theme.fontHeading + 2
font.weight: Font.Bold
text: window.mode === "ask" ? qsTr("Allow %1 to use your Google Play sign-in?").arg(window.modName)
: window.mode === "signedOut" ? qsTr("Sign in to Google Play first")
: qsTr("Google Play is not available")
}
Text {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
lineHeight: 1.15
color: Theme.textSecondary
font.pixelSize: Theme.fontBody
text: window.mode === "ask"
? qsTr("The mod is asking for the Google account you signed in with (%1), usually to confirm that you own Minecraft on Google Play. It gets access to that Google Play account, so allow only mods you trust.").arg(window.maskedEmail)
: window.mode === "signedOut"
? qsTr("%1 needs your Google Play sign-in, but you are not signed in to %2. Open the launcher, sign in with the Google account that owns Minecraft and start the game again.").arg(window.modName).arg(APP_NAME)
: qsTr("This build of %1 cannot use Google Play, so %2 cannot get a sign-in.").arg(APP_NAME).arg(window.modName)
}
Text {
Layout.fillWidth: true
visible: window.mode === "ask"
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
color: Theme.textMuted
font.pixelSize: Theme.fontSmall + 1
text: qsTr("If you deny, game versions that need this mod may not start.")
}
Rectangle {
Layout.fillWidth: true
visible: SOURCE_MOD.length > 0
implicitHeight: modPath.implicitHeight + 2 * Theme.space2
radius: Theme.radius
color: Theme.surfaceRaised
Text {
id: modPath
anchors.fill: parent
anchors.margins: Theme.space2
text: SOURCE_MOD
color: Theme.textMuted
font.family: Theme.fontMono
font.pixelSize: Theme.fontCaption
elide: Text.ElideMiddle
verticalAlignment: Text.AlignVCenter
}
}
RowLayout {
Layout.fillWidth: true
Layout.topMargin: Theme.space2
spacing: Theme.space2
Item { Layout.fillWidth: true }
MButton {
visible: window.mode === "ask"
text: qsTr("Deny")
onClicked: window.deny()
}
MButton {
kind: "primary"
text: window.mode === "ask" ? qsTr("Allow") : qsTr("Close")
iconName: window.mode === "ask" ? "check" : ""
onClicked: window.mode === "ask" ? window.allow() : window.deny()
}
}
}
}
}
Subject: Let the launcher tell the engine where its Google credentials helper is
diff --git a/src/minecraft_utils.cpp b/src/minecraft_utils.cpp
index d7f2360..f7a601b 100644
--- a/src/minecraft_utils.cpp
+++ b/src/minecraft_utils.cpp
@@ -129,6 +129,10 @@ struct GoogleCredentials {
};
static std::string getUiExecutablePath() {
+ // The launcher that started the game names itself here, whatever its file name.
+ const char* launcher = getenv("MCPELAUNCHER_UI_EXECUTABLE");
+ if(launcher && *launcher)
+ return launcher;
std::string path;
#ifndef MCPELAUNCHER_UI_PATH
#define MCPELAUNCHER_UI_PATH "."
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment