Skip to content
This repository has been archived by the owner on Mar 21, 2022. It is now read-only.

Commit

Permalink
Offline patch
Browse files Browse the repository at this point in the history
  • Loading branch information
Ponywka committed Aug 31, 2020
1 parent 8a0027c commit 8e3ddd6
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 109 deletions.
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Before submitting this issue, please make sure you have:
5. Place all information below the ---- of lines.
- It makes the issue look pretty
If you believe your issue to be a bug, please make sure you check the wiki page: https://github.com/MultiMC/MultiMC5/wiki/Report-a-Bug
If you believe your issue to be a bug, please make sure you check the wiki page: https://github.com/Ponywka/MultiMC5-with-offline/wiki/Report-a-Bug
-->

System Information
Expand Down Expand Up @@ -44,7 +44,7 @@ Suspected cause:

Logs/Screenshots:
----------------------------
[//]: # (Please refer to https://github.com/MultiMC/MultiMC5/wiki/Log-Upload for instructions on how to attach your logs.)
[//]: # (Please refer to https://github.com/Ponywka/MultiMC5-with-offline/wiki/Log-Upload for instructions on how to attach your logs.)


Additional Info:
Expand Down
8 changes: 4 additions & 4 deletions BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ an administrator/root level account. Don't use `sudo`. It won't work and it's no
Clone the source code using git and grab all the submodules:

```
git clone git@github.com:MultiMC/MultiMC5.git
git clone git@github.com:Ponywka/MultiMC5-Cracked.git
git submodule init
git submodule update
```
Expand Down Expand Up @@ -50,7 +50,7 @@ mkdir ~/MultiMC && cd ~/MultiMC
mkdir build
mkdir install
# clone the complete source
git clone --recursive https://github.com/MultiMC/MultiMC5.git src
git clone --recursive https://github.com/Ponywka/MultiMC5-Cracked.git src
# configure the project
cd build
cmake -DCMAKE_INSTALL_PREFIX=../install ../src
Expand Down Expand Up @@ -143,7 +143,7 @@ Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt
**These build instructions worked for me (Drayshak) on a fresh Windows 8 x64 Professional install. If they don't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
### Compile from command line on Windows
1. If you installed Qt with the web installer, there should be a shortcut called `Qt 5.4 for Desktop (MinGW 4.9 32-bit)` in the Start menu on Windows 7 and 10. Best way to find it is to search for it. Do note you cannot just use cmd.exe, you have to use the shortcut, otherwise the proper MinGW software will not be on the PATH.
2. Once that is open, change into your user directory, and clone MultiMC by doing `git clone --recursive https://github.com/MultiMC/MultiMC5.git`, and change directory to the folder you cloned to.
2. Once that is open, change into your user directory, and clone MultiMC by doing `git clone --recursive https://github.com/Ponywka/MultiMC5-Cracked.git`, and change directory to the folder you cloned to.
3. Make a build directory, and change directory to the directory and do `cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=C:\Path\that\makes\sense\for\you`. By default, it will install to C:\Program Files (x86), which you might not want, if you want a local installation. If you want to install it to that directory, make sure to run the command window as administrator.
3. Do `mingw32-make -jX`, where X is the number of cores your CPU has plus one.
4. Now to wait for it to compile. This could take some time. Hopefully it compiles properly.
Expand All @@ -168,7 +168,7 @@ brew install cmake
Pick an installation path - this is where the final `.app` will be constructed when you run `make install`. Supply it as the `CMAKE_INSTALL_PREFIX` argument during CMake configuration.

```
git clone https://github.com/MultiMC/MultiMC5.git
git clone https://github.com/Ponywka/MultiMC5-with-offline.git
cd MultiMC5
git submodule init
git submodule update
Expand Down
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<p align="center">
<img src="https://avatars2.githubusercontent.com/u/5411890" alt="MultiMC logo"/>
</p>
**This is a "cracked" version of a popular Minecraft launcher that lets you play the game without a Mojang account.**
This software is not related to MultiMC developers and provided without any warranty. Please don't bomb MultiMC developers if something gets wrong using this launcher.

Offline mode based by this code:
https://github.com/MultiMC/MultiMC5/commit/6ede3c13b2bcda315e65dd78f2bfd729bc8b699b

Rewrited for use license and offline accounts at the same time

Details about the original launcher below:

MultiMC 5
=========
Expand All @@ -13,7 +19,7 @@ The project uses C++ and Qt5 as the language and base framework. This might seem

We can do more, with less, on worse hardware and leave more resources for the game while keeping the launcher running and providing extra features.

If you want to contribute, either talk to us on [Discord](https://discord.gg/0k2zsXGNHs0fE4Wm), [IRC](http://webchat.esper.net/?nick=&channels=MultiMC)(esper.net/#MultiMC) or pick up some item from the github issues [workflowy](https://github.com/MultiMC/MultiMC5/issues) - there is always plenty of ideas around.
If you want to contribute, either talk to us on [Discord](https://discord.gg/0k2zsXGNHs0fE4Wm), [IRC](http://webchat.esper.net/?nick=&channels=MultiMC)(esper.net/#MultiMC) or pick up some item from the github issues [workflowy](https://github.com/Ponywka/MultiMC5-with-offline/issues) - there is always plenty of ideas around.

### Building
If you want to build MultiMC yourself, check [BUILD.md](BUILD.md) for build instructions.
Expand Down
4 changes: 3 additions & 1 deletion api/logic/minecraft/MinecraftInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,9 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
}
else
{
process->appendStep(new Update(pptr, Net::Mode::Offline));
// Offline mode based by this code:
// https://github.com/MultiMC/MultiMC5/commit/6ede3c13b2bcda315e65dd78f2bfd729bc8b699b
process->appendStep(new Update(pptr, Net::Mode::Online));
}

// if there are any jar mods
Expand Down
18 changes: 18 additions & 0 deletions api/logic/minecraft/auth/MojangAccount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

#include <QDebug>

#include <QCryptographicHash>

MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
{
// The JSON object must at least have a username for it to be valid.
Expand Down Expand Up @@ -103,6 +105,22 @@ MojangAccountPtr MojangAccount::createFromUsername(const QString &username)
return account;
}

MojangAccountPtr MojangAccount::createFromUsernameOffline(const QString &username)
{
// Offline mode based by this code:
// https://github.com/MultiMC/MultiMC5/commit/6ede3c13b2bcda315e65dd78f2bfd729bc8b699b
MojangAccountPtr account(new MojangAccount());
account->m_clientToken = "ff64ff64ff64ff64ff64ff64ff64ff64";
account->m_accessToken = "ff64ff64ff64ff64ff64ff64ff64ff64";
account->m_username = username;
QList<AccountProfile> profiles;
QString uuid = QCryptographicHash::hash(username.toLocal8Bit(), QCryptographicHash::Md5).toHex();
profiles.append({uuid, username, false});
account->m_profiles = profiles;
account->setCurrentProfile(uuid);
return account;
}

QJsonObject MojangAccount::saveToJson() const
{
QJsonObject json;
Expand Down
3 changes: 3 additions & 0 deletions api/logic/minecraft/auth/MojangAccount.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ class MULTIMC_LOGIC_EXPORT MojangAccount :
//! Creates an empty account for the specified user name.
static MojangAccountPtr createFromUsername(const QString &username);

//! Creates an offline account
static MojangAccountPtr createFromUsernameOffline(const QString &username);

//! Loads a MojangAccount from the given JSON object.
static MojangAccountPtr loadFromJson(const QJsonObject &json);

Expand Down
184 changes: 98 additions & 86 deletions application/LaunchController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,111 +78,123 @@ void LaunchController::login()
return;
}

// we try empty password first :)
QString password;
// we loop until the user succeeds in logging in or gives up
bool tryagain = true;
// the failure. the default failure.
const QString needLoginAgain = tr("Your account is currently not logged in. Please enter your password to log in again. <br /> <br /> This could be caused by a password change.");
QString failReason = needLoginAgain;
if(account->clientToken() != "ff64ff64ff64ff64ff64ff64ff64ff64") {
// Online
// we try empty password first :)
QString password;
// we loop until the user succeeds in logging in or gives up
bool tryagain = true;
// the failure. the default failure.
const QString needLoginAgain = tr("Your account is currently not logged in. Please enter your password to log in again. <br /> <br /> This could be caused by a password change.");
QString failReason = needLoginAgain;

while (tryagain)
{
m_session = std::make_shared<AuthSession>();
m_session->wants_online = m_online;
auto task = account->login(m_session, password);
if (task)
while (tryagain)
{
// We'll need to validate the access token to make sure the account
// is still logged in.
ProgressDialog progDialog(m_parentWidget);
if (m_online)
m_session = std::make_shared<AuthSession>();
m_session->wants_online = m_online;
auto task = account->login(m_session, password);
if (task)
{
progDialog.setSkipButton(true, tr("Play Offline"));
}
progDialog.execWithTask(task.get());
if (!task->wasSuccessful())
{
auto failReasonNew = task->failReason();
if(failReasonNew == "Invalid token.")
// We'll need to validate the access token to make sure the account
// is still logged in.
ProgressDialog progDialog(m_parentWidget);
if (m_online)
{
account->invalidateClientToken();
failReason = needLoginAgain;
progDialog.setSkipButton(true, tr("Play Offline"));
}
else failReason = failReasonNew;
}
}
switch (m_session->status)
{
case AuthSession::Undetermined:
{
qCritical() << "Received undetermined session status during login. Bye.";
tryagain = false;
emitFailed(tr("Received undetermined session status during login."));
break;
}
case AuthSession::RequiresPassword:
{
EditAccountDialog passDialog(failReason, m_parentWidget, EditAccountDialog::PasswordField);
auto username = m_session->username;
auto chopN = [](QString toChop, int N) -> QString
{
if(toChop.size() > N)
progDialog.execWithTask(task.get());
if (!task->wasSuccessful())
{
auto left = toChop.left(N);
left += QString("\u25CF").repeated(toChop.size() - N);
return left;
auto failReasonNew = task->failReason();
if(failReasonNew == "Invalid token.")
{
account->invalidateClientToken();
failReason = needLoginAgain;
}
else failReason = failReasonNew;
}
return toChop;
};

if(username.contains('@'))
{
auto parts = username.split('@');
auto mailbox = chopN(parts[0],3);
QString domain = chopN(parts[1], 3);
username = mailbox + '@' + domain;
}
passDialog.setUsername(username);
if (passDialog.exec() == QDialog::Accepted)
switch (m_session->status)
{
password = passDialog.password();
}
else
case AuthSession::Undetermined:
{
qCritical() << "Received undetermined session status during login. Bye.";
tryagain = false;
emitFailed(tr("Received undetermined session status during login."));
break;
}
break;
}
case AuthSession::PlayableOffline:
{
// we ask the user for a player name
bool ok = false;
QString usedname = m_session->player_name;
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"),
tr("Choose your offline mode player name."),
QLineEdit::Normal, m_session->player_name, &ok);
if (!ok)
case AuthSession::RequiresPassword:
{
tryagain = false;
EditAccountDialog passDialog(failReason, m_parentWidget, EditAccountDialog::PasswordField);
auto username = m_session->username;
auto chopN = [](QString toChop, int N) -> QString
{
if(toChop.size() > N)
{
auto left = toChop.left(N);
left += QString("\u25CF").repeated(toChop.size() - N);
return left;
}
return toChop;
};

if(username.contains('@'))
{
auto parts = username.split('@');
auto mailbox = chopN(parts[0],3);
QString domain = chopN(parts[1], 3);
username = mailbox + '@' + domain;
}
passDialog.setUsername(username);
if (passDialog.exec() == QDialog::Accepted)
{
password = passDialog.password();
}
else
{
tryagain = false;
}
break;
}
if (name.length())
case AuthSession::PlayableOffline:
{
usedname = name;
// we ask the user for a player name
bool ok = false;
QString usedname = m_session->player_name;
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"),
tr("Choose your offline mode player name."),
QLineEdit::Normal, m_session->player_name, &ok);
if (!ok)
{
tryagain = false;
break;
}
if (name.length())
{
usedname = name;
}
m_session->MakeOffline(usedname);
// offline flavored game from here :3
}
case AuthSession::PlayableOnline:
{
launchInstance();
tryagain = false;
return;
}
}
m_session->MakeOffline(usedname);
// offline flavored game from here :3
}
case AuthSession::PlayableOnline:
{
launchInstance();
tryagain = false;
return;
}
}
emitFailed(tr("Failed to launch."));
}else{
// Offline
m_session = std::make_shared<AuthSession>();
m_session->client_token = account->clientToken();
m_session->access_token = account->accessToken();
m_session->uuid = account->currentProfile()->id;
m_session->status = AuthSession::PlayableOffline;
m_session->MakeOffline(account->currentProfile()->name);
launchInstance();
}
emitFailed(tr("Failed to launch."));
}

void LaunchController::launchInstance()
Expand Down
29 changes: 18 additions & 11 deletions application/dialogs/LoginDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,22 @@ void LoginDialog::accept()
setUserInputsEnabled(false);
ui->progressBar->setVisible(true);

// Setup the login task and start it
m_account = MojangAccount::createFromUsername(ui->userTextBox->text());
m_loginTask = m_account->login(nullptr, ui->passTextBox->text());
connect(m_loginTask.get(), &Task::failed, this, &LoginDialog::onTaskFailed);
connect(m_loginTask.get(), &Task::succeeded, this,
&LoginDialog::onTaskSucceeded);
connect(m_loginTask.get(), &Task::status, this, &LoginDialog::onTaskStatus);
connect(m_loginTask.get(), &Task::progress, this, &LoginDialog::onTaskProgress);
m_loginTask->start();
if(!ui->passTextBox->text().isEmpty()){
// Online mode
// Setup the login task and start it
m_account = MojangAccount::createFromUsername(ui->userTextBox->text());
m_loginTask = m_account->login(nullptr, ui->passTextBox->text());
connect(m_loginTask.get(), &Task::failed, this, &LoginDialog::onTaskFailed);
connect(m_loginTask.get(), &Task::succeeded, this,
&LoginDialog::onTaskSucceeded);
connect(m_loginTask.get(), &Task::status, this, &LoginDialog::onTaskStatus);
connect(m_loginTask.get(), &Task::progress, this, &LoginDialog::onTaskProgress);
m_loginTask->start();
}else{
// Offline mode
m_account = MojangAccount::createFromUsernameOffline(ui->userTextBox->text());
QDialog::accept();
}
}

void LoginDialog::setUserInputsEnabled(bool enable)
Expand All @@ -63,12 +70,12 @@ void LoginDialog::setUserInputsEnabled(bool enable)
void LoginDialog::on_userTextBox_textEdited(const QString &newText)
{
ui->buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(!newText.isEmpty() && !ui->passTextBox->text().isEmpty());
->setEnabled(!newText.isEmpty());
}
void LoginDialog::on_passTextBox_textEdited(const QString &newText)
{
ui->buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(!newText.isEmpty() && !ui->userTextBox->text().isEmpty());
->setEnabled(!ui->userTextBox->text().isEmpty());
}

void LoginDialog::onTaskFailed(const QString &reason)
Expand Down
Loading

0 comments on commit 8e3ddd6

Please sign in to comment.