2016-08-10 22:44:01 +00:00
# include "LaunchController.h"
2016-08-06 13:39:29 +00:00
# include "MainWindow.h"
2021-07-26 19:44:11 +00:00
# include <minecraft/auth/AccountList.h>
2015-07-04 23:54:30 +00:00
# include "MultiMC.h"
# include "dialogs/CustomMessageBox.h"
2016-08-10 22:44:01 +00:00
# include "dialogs/ProfileSelectDialog.h"
2015-07-04 23:54:30 +00:00
# include "dialogs/ProgressDialog.h"
# include "dialogs/EditAccountDialog.h"
2016-08-06 13:39:29 +00:00
# include "InstanceWindow.h"
2015-07-04 23:54:30 +00:00
# include "BuildConfig.h"
# include "JavaCommon.h"
# include <QLineEdit>
# include <QInputDialog>
# include <tasks/Task.h>
2021-07-26 19:44:11 +00:00
# include <minecraft/auth/AccountTask.h>
2015-07-21 00:38:15 +00:00
# include <launch/steps/TextPrint.h>
2015-08-14 00:27:01 +00:00
# include <QStringList>
2021-06-19 01:15:21 +00:00
# include <QHostInfo>
# include <QList>
# include <QHostAddress>
2015-07-04 23:54:30 +00:00
2015-10-23 22:57:54 +00:00
LaunchController : : LaunchController ( QObject * parent ) : Task ( parent )
2015-07-04 23:54:30 +00:00
{
}
2015-10-23 22:57:54 +00:00
void LaunchController : : executeTask ( )
2015-07-04 23:54:30 +00:00
{
2018-07-15 12:51:05 +00:00
if ( ! m_instance )
{
2019-09-15 03:31:13 +00:00
emitFailed ( tr ( " No instance specified! " ) ) ;
2018-07-15 12:51:05 +00:00
return ;
}
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
login ( ) ;
2017-12-03 13:05:35 +00:00
}
// FIXME: minecraft specific
2021-07-26 19:44:11 +00:00
void LaunchController : : login ( ) {
2018-07-15 12:51:05 +00:00
JavaCommon : : checkJVMArgs ( m_instance - > settings ( ) - > get ( " JvmArgs " ) . toString ( ) , m_parentWidget ) ;
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
// Find an account to use.
2021-07-26 19:44:11 +00:00
std : : shared_ptr < AccountList > accounts = MMC - > accounts ( ) ;
2018-07-15 12:51:05 +00:00
if ( accounts - > count ( ) < = 0 )
{
// Tell the user they need to log in at least one account in order to play.
auto reply = CustomMessageBox : : selectable (
2021-07-26 19:44:11 +00:00
m_parentWidget ,
tr ( " No Accounts " ) ,
2018-07-15 12:51:05 +00:00
tr ( " In order to play Minecraft, you must have at least one Mojang or Minecraft "
" account logged in to MultiMC. "
" Would you like to open the account manager to add an account now? " ) ,
2021-07-26 19:44:11 +00:00
QMessageBox : : Information ,
QMessageBox : : Yes | QMessageBox : : No
) - > exec ( ) ;
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
if ( reply = = QMessageBox : : Yes )
{
// Open the account manager.
2019-06-01 10:28:53 +00:00
MMC - > ShowGlobalSettings ( m_parentWidget , " accounts " ) ;
2018-07-15 12:51:05 +00:00
}
}
2021-07-26 19:44:11 +00:00
MinecraftAccountPtr account = accounts - > activeAccount ( ) ;
if ( account . get ( ) = = nullptr )
2018-07-15 12:51:05 +00:00
{
// If no default account is set, ask the user which one to use.
2021-07-26 19:44:11 +00:00
ProfileSelectDialog selectDialog (
tr ( " Which account would you like to use? " ) ,
ProfileSelectDialog : : GlobalDefaultCheckbox ,
m_parentWidget
) ;
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
selectDialog . exec ( ) ;
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
// Launch the instance with the selected account.
account = selectDialog . selectedAccount ( ) ;
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
// If the user said to use the account as default, do that.
2021-07-26 19:44:11 +00:00
if ( selectDialog . useAsGlobalDefault ( ) & & account . get ( ) ! = nullptr ) {
accounts - > setActiveAccount ( account - > profileId ( ) ) ;
}
2018-07-15 12:51:05 +00:00
}
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
// if no account is selected, we bail
if ( ! account . get ( ) )
{
2019-09-15 03:31:13 +00:00
emitFailed ( tr ( " No account selected for launch. " ) ) ;
2018-07-15 12:51:05 +00:00
return ;
}
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
// 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.
2019-09-15 03:12:23 +00:00
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. " ) ;
2018-07-15 12:51:05 +00:00
QString failReason = needLoginAgain ;
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
while ( tryagain )
{
m_session = std : : make_shared < AuthSession > ( ) ;
m_session - > wants_online = m_online ;
2021-07-26 19:44:11 +00:00
std : : shared_ptr < AccountTask > task ;
if ( ! password . isNull ( ) ) {
task = account - > login ( m_session , password ) ;
}
else {
task = account - > refresh ( m_session ) ;
}
2018-07-15 12:51:05 +00:00
if ( task )
{
// We'll need to validate the access token to make sure the account
// is still logged in.
ProgressDialog progDialog ( m_parentWidget ) ;
if ( m_online )
{
progDialog . setSkipButton ( true , tr ( " Play Offline " ) ) ;
}
progDialog . execWithTask ( task . get ( ) ) ;
if ( ! task - > wasSuccessful ( ) )
{
auto failReasonNew = task - > failReason ( ) ;
2021-07-26 19:44:11 +00:00
if ( failReasonNew = = " Invalid token. " | | failReasonNew = = " Invalid Signature " )
2018-07-15 12:51:05 +00:00
{
2021-07-26 19:44:11 +00:00
// account->invalidateClientToken();
2018-07-15 12:51:05 +00:00
failReason = needLoginAgain ;
}
else failReason = failReasonNew ;
}
}
switch ( m_session - > status )
{
2021-07-26 19:44:11 +00:00
case AuthSession : : Undetermined : {
qCritical ( ) < < " Received undetermined session status during login. Bye. " ;
tryagain = false ;
emitFailed ( tr ( " Received undetermined session status during login. " ) ) ;
return ;
}
case AuthSession : : RequiresPassword : {
// FIXME: this needs to understand MSA
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 ( ' @ ' ) )
2018-07-15 12:51:05 +00:00
{
2021-07-26 19:44:11 +00:00
auto parts = username . split ( ' @ ' ) ;
auto mailbox = chopN ( parts [ 0 ] , 3 ) ;
QString domain = chopN ( parts [ 1 ] , 3 ) ;
username = mailbox + ' @ ' + domain ;
2018-07-15 12:51:05 +00:00
}
2021-07-26 19:44:11 +00:00
passDialog . setUsername ( username ) ;
if ( passDialog . exec ( ) = = QDialog : : Accepted )
{
password = passDialog . password ( ) ;
}
else
{
tryagain = false ;
emitFailed ( tr ( " Received undetermined session status during login. " ) ) ;
}
break ;
2018-07-15 12:51:05 +00:00
}
2021-07-26 19:44:11 +00:00
case AuthSession : : RequiresOAuth : {
// FIXME: add UI for expired / broken MS accounts
2018-07-15 12:51:05 +00:00
tryagain = false ;
2021-07-26 19:44:11 +00:00
emitFailed ( tr ( " Microsoft account has expired and needs to be logged into again. " ) ) ;
return ;
2018-07-15 12:51:05 +00:00
}
2021-07-26 19:44:11 +00:00
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 )
{
tryagain = false ;
break ;
}
if ( name . length ( ) )
{
usedname = name ;
}
m_session - > MakeOffline ( usedname ) ;
// offline flavored game from here :3
2018-07-15 12:51:05 +00:00
}
2021-07-26 19:44:11 +00:00
case AuthSession : : PlayableOnline :
2018-07-15 12:51:05 +00:00
{
2021-07-26 19:44:11 +00:00
launchInstance ( ) ;
tryagain = false ;
return ;
2018-07-15 12:51:05 +00:00
}
}
}
emitFailed ( tr ( " Failed to launch. " ) ) ;
2015-07-04 23:54:30 +00:00
}
void LaunchController : : launchInstance ( )
{
2018-07-15 12:51:05 +00:00
Q_ASSERT_X ( m_instance ! = NULL , " launchInstance " , " instance is NULL " ) ;
Q_ASSERT_X ( m_session . get ( ) ! = nullptr , " launchInstance " , " session is NULL " ) ;
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
if ( ! m_instance - > reloadSettings ( ) )
{
2019-09-15 03:31:13 +00:00
QMessageBox : : critical ( m_parentWidget , tr ( " Error! " ) , tr ( " Couldn't load the instance profile. " ) ) ;
2018-07-15 12:51:05 +00:00
emitFailed ( tr ( " Couldn't load the instance profile. " ) ) ;
return ;
}
2015-07-04 23:54:30 +00:00
2021-05-22 16:07:08 +00:00
m_launcher = m_instance - > createLaunchTask ( m_session , m_serverToJoin ) ;
2018-07-15 12:51:05 +00:00
if ( ! m_launcher )
{
emitFailed ( tr ( " Couldn't instantiate a launcher. " ) ) ;
return ;
}
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
auto console = qobject_cast < InstanceWindow * > ( m_parentWidget ) ;
auto showConsole = m_instance - > settings ( ) - > get ( " ShowConsole " ) . toBool ( ) ;
if ( ! console & & showConsole )
{
MMC - > showInstanceWindow ( m_instance ) ;
}
connect ( m_launcher . get ( ) , & LaunchTask : : readyForLaunch , this , & LaunchController : : readyForLaunch ) ;
connect ( m_launcher . get ( ) , & LaunchTask : : succeeded , this , & LaunchController : : onSucceeded ) ;
connect ( m_launcher . get ( ) , & LaunchTask : : failed , this , & LaunchController : : onFailed ) ;
connect ( m_launcher . get ( ) , & LaunchTask : : requestProgress , this , & LaunchController : : onProgressRequested ) ;
2016-11-01 00:25:04 +00:00
2021-06-19 01:15:21 +00:00
// Prepend Online and Auth Status
QString online_mode ;
if ( m_session - > wants_online ) {
online_mode = " online " ;
2015-07-04 23:54:30 +00:00
2021-06-19 01:15:21 +00:00
// Prepend Server Status
QStringList servers = { " authserver.mojang.com " , " session.minecraft.net " , " textures.minecraft.net " , " api.mojang.com " } ;
QString resolved_servers = " " ;
QHostInfo host_info ;
for ( QString server : servers ) {
host_info = QHostInfo : : fromName ( server ) ;
resolved_servers = resolved_servers + server + " resolves to: \n [ " ;
if ( ! host_info . addresses ( ) . isEmpty ( ) ) {
for ( QHostAddress address : host_info . addresses ( ) ) {
resolved_servers = resolved_servers + address . toString ( ) ;
if ( ! host_info . addresses ( ) . endsWith ( address ) ) {
resolved_servers = resolved_servers + " , " ;
}
}
} else {
resolved_servers = resolved_servers + " N/A " ;
}
resolved_servers = resolved_servers + " ] \n \n " ;
}
m_launcher - > prependStep ( new TextPrint ( m_launcher . get ( ) , resolved_servers , MessageLevel : : MultiMC ) ) ;
} else {
online_mode = " offline " ;
}
QString auth_server_status ;
if ( m_session - > auth_server_online ) {
auth_server_status = " online " ;
} else {
auth_server_status = " offline " ;
}
m_launcher - > prependStep ( new TextPrint ( m_launcher . get ( ) , " Launched instance in " + online_mode + " mode \n Authentication server is " + auth_server_status + " \n " , MessageLevel : : MultiMC ) ) ;
// Prepend Version
2019-04-07 21:59:04 +00:00
m_launcher - > prependStep ( new TextPrint ( m_launcher . get ( ) , " MultiMC version: " + BuildConfig . printableVersionString ( ) + " \n \n " , MessageLevel : : MultiMC ) ) ;
2018-07-15 12:51:05 +00:00
m_launcher - > start ( ) ;
2015-07-04 23:54:30 +00:00
}
void LaunchController : : readyForLaunch ( )
{
2018-07-15 12:51:05 +00:00
if ( ! m_profiler )
{
m_launcher - > proceed ( ) ;
return ;
}
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
QString error ;
if ( ! m_profiler - > check ( & error ) )
{
m_launcher - > abort ( ) ;
2019-09-15 03:31:13 +00:00
QMessageBox : : critical ( m_parentWidget , tr ( " Error! " ) , tr ( " Couldn't start profiler: %1 " ) . arg ( error ) ) ;
emitFailed ( " Profiler startup failed! " ) ;
2018-07-15 12:51:05 +00:00
return ;
}
BaseProfiler * profilerInstance = m_profiler - > createProfiler ( m_launcher - > instance ( ) , this ) ;
2015-07-04 23:54:30 +00:00
2018-07-15 12:51:05 +00:00
connect ( profilerInstance , & BaseProfiler : : readyToLaunch , [ this ] ( const QString & message )
{
QMessageBox msg ;
msg . setText ( tr ( " The game launch is delayed until you press the "
" button. This is the right time to setup the profiler, as the "
" profiler server is running now. \n \n %1 " ) . arg ( message ) ) ;
2019-09-15 03:31:13 +00:00
msg . setWindowTitle ( tr ( " Waiting. " ) ) ;
2018-07-15 12:51:05 +00:00
msg . setIcon ( QMessageBox : : Information ) ;
msg . addButton ( tr ( " Launch " ) , QMessageBox : : AcceptRole ) ;
msg . setModal ( true ) ;
msg . exec ( ) ;
m_launcher - > proceed ( ) ;
} ) ;
connect ( profilerInstance , & BaseProfiler : : abortLaunch , [ this ] ( const QString & message )
{
QMessageBox msg ;
msg . setText ( tr ( " Couldn't start the profiler: %1 " ) . arg ( message ) ) ;
msg . setWindowTitle ( tr ( " Error " ) ) ;
msg . setIcon ( QMessageBox : : Critical ) ;
msg . addButton ( QMessageBox : : Ok ) ;
msg . setModal ( true ) ;
msg . exec ( ) ;
m_launcher - > abort ( ) ;
2019-09-15 03:31:13 +00:00
emitFailed ( " Profiler startup failed! " ) ;
2018-07-15 12:51:05 +00:00
} ) ;
profilerInstance - > beginProfiling ( m_launcher ) ;
2015-07-04 23:54:30 +00:00
}
2016-11-01 00:25:04 +00:00
void LaunchController : : onSucceeded ( )
{
2018-07-15 12:51:05 +00:00
emitSucceeded ( ) ;
2016-11-01 00:25:04 +00:00
}
void LaunchController : : onFailed ( QString reason )
{
2018-07-15 12:51:05 +00:00
if ( m_instance - > settings ( ) - > get ( " ShowConsoleOnError " ) . toBool ( ) )
{
MMC - > showInstanceWindow ( m_instance , " console " ) ;
}
emitFailed ( reason ) ;
2016-11-01 00:25:04 +00:00
}
void LaunchController : : onProgressRequested ( Task * task )
{
2018-07-15 12:51:05 +00:00
ProgressDialog progDialog ( m_parentWidget ) ;
progDialog . setSkipButton ( true , tr ( " Abort " ) ) ;
m_launcher - > proceed ( ) ;
progDialog . execWithTask ( task ) ;
2016-11-01 00:25:04 +00:00
}
2016-11-26 17:06:08 +00:00
bool LaunchController : : abort ( )
{
2018-07-15 12:51:05 +00:00
if ( ! m_launcher )
{
return true ;
}
if ( ! m_launcher - > canAbort ( ) )
{
return false ;
}
auto response = CustomMessageBox : : selectable (
m_parentWidget , tr ( " Kill Minecraft? " ) ,
tr ( " This can cause the instance to get corrupted and should only be used if Minecraft "
" is frozen for some reason " ) ,
QMessageBox : : Question , QMessageBox : : Yes | QMessageBox : : No , QMessageBox : : Yes ) - > exec ( ) ;
if ( response = = QMessageBox : : Yes )
{
return m_launcher - > abort ( ) ;
}
return false ;
2016-11-26 17:06:08 +00:00
}