2013-02-22 05:40:17 +05:30
/* Copyright 2013 MultiMC Contributors
*
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
2013-09-22 07:51:36 +05:30
*
2013-02-22 05:40:17 +05:30
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
2013-11-04 07:23:05 +05:30
2013-09-08 05:45:20 +05:30
# include "MultiMC.h"
2013-08-04 18:16:33 +05:30
# include "OneSixUpdate.h"
2013-02-22 05:40:17 +05:30
2013-05-08 23:26:43 +05:30
# include <QtNetwork>
# include <QFile>
# include <QFileInfo>
# include <QTextStream>
# include <QDataStream>
2013-08-04 03:28:39 +05:30
# include "BaseInstance.h"
2013-07-29 04:29:35 +05:30
# include "lists/MinecraftVersionList.h"
2014-03-02 03:36:47 +05:30
# include "VersionFinal.h"
2013-09-12 03:13:17 +05:30
# include "OneSixLibrary.h"
2013-08-05 06:59:50 +05:30
# include "OneSixInstance.h"
2013-11-17 16:14:18 +05:30
# include "net/ForgeMirrors.h"
2013-12-13 20:28:11 +05:30
# include "net/URLConstants.h"
2013-12-10 11:42:52 +05:30
# include "assets/AssetsUtils.h"
2013-05-08 23:26:43 +05:30
# include "pathutils.h"
2013-11-25 05:16:52 +05:30
# include <JlCompress.h>
2013-05-08 23:26:43 +05:30
2014-02-21 23:45:59 +05:30
OneSixUpdate : : OneSixUpdate ( OneSixInstance * inst , QObject * parent )
2014-01-27 07:30:49 +05:30
: Task ( parent ) , m_inst ( inst )
2013-09-22 07:51:36 +05:30
{
}
2013-05-08 23:26:43 +05:30
2013-08-04 18:16:33 +05:30
void OneSixUpdate : : executeTask ( )
2013-05-08 23:26:43 +05:30
{
2013-08-05 06:59:50 +05:30
QString intendedVersion = m_inst - > intendedVersionId ( ) ;
2013-09-22 07:51:36 +05:30
2013-08-12 04:09:19 +05:30
// Make directories
QDir mcDir ( m_inst - > minecraftRoot ( ) ) ;
if ( ! mcDir . exists ( ) & & ! mcDir . mkpath ( " . " ) )
{
2014-03-10 04:12:25 +05:30
emitFailed ( tr ( " Failed to create folder for minecraft binaries. " ) ) ;
2013-08-12 04:09:19 +05:30
return ;
}
2013-09-22 07:51:36 +05:30
if ( m_inst - > shouldUpdate ( ) )
2013-08-05 06:59:50 +05:30
{
2013-11-25 05:16:52 +05:30
// Get a pointer to the version object that corresponds to the instance's version.
targetVersion = std : : dynamic_pointer_cast < MinecraftVersion > (
MMC - > minecraftlist ( ) - > findVersion ( intendedVersion ) ) ;
if ( targetVersion = = nullptr )
{
// don't do anything if it was invalid
2014-03-10 04:12:25 +05:30
emitFailed ( tr ( " The specified Minecraft version is invalid. Choose a different one. " ) ) ;
2013-11-25 05:16:52 +05:30
return ;
}
2013-08-05 06:59:50 +05:30
versionFileStart ( ) ;
}
else
{
jarlibStart ( ) ;
}
}
void OneSixUpdate : : versionFileStart ( )
{
2014-02-21 23:45:59 +05:30
if ( m_inst - > providesVersionFile ( ) )
{
jarlibStart ( ) ;
return ;
}
2013-11-25 05:16:52 +05:30
QLOG_INFO ( ) < < m_inst - > name ( ) < < " : getting version file. " ;
2013-12-23 21:16:01 +05:30
setStatus ( tr ( " Getting the version files from Mojang... " ) ) ;
2013-09-22 07:51:36 +05:30
2014-01-14 05:43:35 +05:30
QString urlstr = " http:// " + URLConstants : : AWS_DOWNLOAD_VERSIONS +
targetVersion - > descriptor ( ) + " / " + targetVersion - > descriptor ( ) + " .json " ;
2013-10-26 23:25:48 +05:30
auto job = new NetJob ( " Version index " ) ;
job - > addNetAction ( ByteArrayDownload : : make ( QUrl ( urlstr ) ) ) ;
2013-09-08 05:45:20 +05:30
specificVersionDownloadJob . reset ( job ) ;
2013-10-06 04:43:40 +05:30
connect ( specificVersionDownloadJob . get ( ) , SIGNAL ( succeeded ( ) ) , SLOT ( versionFileFinished ( ) ) ) ;
connect ( specificVersionDownloadJob . get ( ) , SIGNAL ( failed ( ) ) , SLOT ( versionFileFailed ( ) ) ) ;
connect ( specificVersionDownloadJob . get ( ) , SIGNAL ( progress ( qint64 , qint64 ) ) ,
2013-09-22 07:51:36 +05:30
SIGNAL ( progress ( qint64 , qint64 ) ) ) ;
2013-09-02 03:55:40 +05:30
specificVersionDownloadJob - > start ( ) ;
2013-07-08 03:21:26 +05:30
}
2013-08-04 18:16:33 +05:30
void OneSixUpdate : : versionFileFinished ( )
2013-07-08 03:21:26 +05:30
{
2013-10-26 23:25:48 +05:30
NetActionPtr DlJob = specificVersionDownloadJob - > first ( ) ;
2013-09-22 07:51:36 +05:30
2013-09-16 04:24:39 +05:30
QString version_id = targetVersion - > descriptor ( ) ;
2013-08-12 04:09:19 +05:30
QString inst_dir = m_inst - > instanceRoot ( ) ;
2013-08-05 06:59:50 +05:30
// save the version file in $instanceId/version.json
2013-07-08 03:21:26 +05:30
{
2013-09-22 07:51:36 +05:30
QString version1 = PathCombine ( inst_dir , " /version.json " ) ;
2013-08-24 06:39:46 +05:30
ensureFilePathExists ( version1 ) ;
2013-08-14 11:43:41 +05:30
// FIXME: detect errors here, download to a temp file, swap
2013-09-22 07:51:36 +05:30
QSaveFile vfile1 ( version1 ) ;
if ( ! vfile1 . open ( QIODevice : : Truncate | QIODevice : : WriteOnly ) )
2013-09-16 04:24:39 +05:30
{
2014-03-10 04:12:25 +05:30
emitFailed ( tr ( " Can't open %1 for writing. " ) . arg ( version1 ) ) ;
2013-09-16 04:24:39 +05:30
return ;
}
2013-10-06 04:43:40 +05:30
auto data = std : : dynamic_pointer_cast < ByteArrayDownload > ( DlJob ) - > m_data ;
2013-09-16 04:24:39 +05:30
qint64 actual = 0 ;
2013-09-22 07:51:36 +05:30
if ( ( actual = vfile1 . write ( data ) ) ! = data . size ( ) )
2013-09-16 04:24:39 +05:30
{
2014-03-10 04:12:25 +05:30
emitFailed ( tr ( " Failed to write into %1. Written %2 out of %3. " ) . arg ( version1 ) . arg ( actual ) . arg ( data . size ( ) ) ) ;
2013-09-16 04:24:39 +05:30
return ;
}
2013-09-22 07:51:36 +05:30
if ( ! vfile1 . commit ( ) )
2013-09-16 04:24:39 +05:30
{
2014-03-10 04:12:25 +05:30
emitFailed ( tr ( " Can't commit changes to %1 " ) . arg ( version1 ) ) ;
2013-09-16 04:24:39 +05:30
return ;
}
2013-07-08 03:21:26 +05:30
}
2013-09-22 07:51:36 +05:30
2013-08-14 11:43:41 +05:30
// the version is downloaded safely. update is 'done' at this point
m_inst - > setShouldUpdate ( false ) ;
2013-09-22 07:51:36 +05:30
2013-09-16 04:24:39 +05:30
// delete any custom version inside the instance (it's no longer relevant, we did an update)
2013-09-22 07:51:36 +05:30
QString custom = PathCombine ( inst_dir , " /custom.json " ) ;
2013-09-16 04:24:39 +05:30
QFile finfo ( custom ) ;
2013-09-22 07:51:36 +05:30
if ( finfo . exists ( ) )
2013-09-16 04:24:39 +05:30
{
finfo . remove ( ) ;
}
2014-03-10 04:12:25 +05:30
// NOTE: Version is reloaded in jarlibStart
2014-01-14 05:43:35 +05:30
jarlibStart ( ) ;
2013-08-05 06:59:50 +05:30
}
void OneSixUpdate : : versionFileFailed ( )
{
2014-03-10 04:12:25 +05:30
emitFailed ( tr ( " Failed to download the version description. Try again. " ) ) ;
2013-08-05 06:59:50 +05:30
}
2013-12-10 11:42:52 +05:30
void OneSixUpdate : : assetIndexStart ( )
{
2013-12-23 21:16:01 +05:30
setStatus ( tr ( " Updating assets index... " ) ) ;
2013-12-10 11:42:52 +05:30
OneSixInstance * inst = ( OneSixInstance * ) m_inst ;
2014-03-02 03:36:47 +05:30
std : : shared_ptr < VersionFinal > version = inst - > getFullVersion ( ) ;
2013-12-10 11:42:52 +05:30
QString assetName = version - > assets ;
2013-12-13 20:28:11 +05:30
QUrl indexUrl = " http:// " + URLConstants : : AWS_DOWNLOAD_INDEXES + assetName + " .json " ;
2013-12-10 11:42:52 +05:30
QString localPath = assetName + " .json " ;
2014-03-23 23:37:13 +05:30
auto job = new NetJob ( tr ( " Asset index for %1 " ) . arg ( inst - > name ( ) ) ) ;
2013-12-10 11:42:52 +05:30
auto metacache = MMC - > metacache ( ) ;
auto entry = metacache - > resolveEntry ( " asset_indexes " , localPath ) ;
job - > addNetAction ( CacheDownload : : make ( indexUrl , entry ) ) ;
jarlibDownloadJob . reset ( job ) ;
connect ( jarlibDownloadJob . get ( ) , SIGNAL ( succeeded ( ) ) , SLOT ( assetIndexFinished ( ) ) ) ;
connect ( jarlibDownloadJob . get ( ) , SIGNAL ( failed ( ) ) , SLOT ( assetIndexFailed ( ) ) ) ;
connect ( jarlibDownloadJob . get ( ) , SIGNAL ( progress ( qint64 , qint64 ) ) ,
SIGNAL ( progress ( qint64 , qint64 ) ) ) ;
jarlibDownloadJob - > start ( ) ;
}
void OneSixUpdate : : assetIndexFinished ( )
{
AssetsIndex index ;
OneSixInstance * inst = ( OneSixInstance * ) m_inst ;
2014-03-02 03:36:47 +05:30
std : : shared_ptr < VersionFinal > version = inst - > getFullVersion ( ) ;
2013-12-10 11:42:52 +05:30
QString assetName = version - > assets ;
QString asset_fname = " assets/indexes/ " + assetName + " .json " ;
if ( ! AssetsUtils : : loadAssetsIndexJson ( asset_fname , & index ) )
{
2014-03-10 04:12:25 +05:30
emitFailed ( tr ( " Failed to read the assets index! " ) ) ;
2013-12-10 11:42:52 +05:30
}
2014-01-14 05:43:35 +05:30
2013-12-10 11:42:52 +05:30
QList < Md5EtagDownloadPtr > dls ;
for ( auto object : index . objects . values ( ) )
{
QString objectName = object . hash . left ( 2 ) + " / " + object . hash ;
QFileInfo objectFile ( " assets/objects/ " + objectName ) ;
if ( ( ! objectFile . isFile ( ) ) | | ( objectFile . size ( ) ! = object . size ) )
{
auto objectDL = MD5EtagDownload : : make (
2013-12-13 20:28:11 +05:30
QUrl ( " http:// " + URLConstants : : RESOURCE_BASE + objectName ) ,
2013-12-10 11:42:52 +05:30
objectFile . filePath ( ) ) ;
2013-12-15 19:30:09 +05:30
objectDL - > m_total_progress = object . size ;
2013-12-10 11:42:52 +05:30
dls . append ( objectDL ) ;
}
}
2014-01-14 05:43:35 +05:30
if ( dls . size ( ) )
2013-12-10 11:42:52 +05:30
{
2013-12-23 21:16:01 +05:30
setStatus ( tr ( " Getting the assets files from Mojang... " ) ) ;
2014-03-23 23:37:13 +05:30
auto job = new NetJob ( tr ( " Assets for %1 " ) . arg ( inst - > name ( ) ) ) ;
2014-01-14 05:43:35 +05:30
for ( auto dl : dls )
2013-12-10 11:42:52 +05:30
job - > addNetAction ( dl ) ;
jarlibDownloadJob . reset ( job ) ;
connect ( jarlibDownloadJob . get ( ) , SIGNAL ( succeeded ( ) ) , SLOT ( assetsFinished ( ) ) ) ;
connect ( jarlibDownloadJob . get ( ) , SIGNAL ( failed ( ) ) , SLOT ( assetsFailed ( ) ) ) ;
connect ( jarlibDownloadJob . get ( ) , SIGNAL ( progress ( qint64 , qint64 ) ) ,
2014-01-14 05:43:35 +05:30
SIGNAL ( progress ( qint64 , qint64 ) ) ) ;
2013-12-10 11:42:52 +05:30
jarlibDownloadJob - > start ( ) ;
return ;
}
assetsFinished ( ) ;
}
void OneSixUpdate : : assetIndexFailed ( )
{
2014-03-10 04:12:25 +05:30
emitFailed ( tr ( " Failed to download the assets index! " ) ) ;
2013-12-10 11:42:52 +05:30
}
void OneSixUpdate : : assetsFinished ( )
{
2014-01-27 07:30:49 +05:30
emitSucceeded ( ) ;
2013-12-10 11:42:52 +05:30
}
void OneSixUpdate : : assetsFailed ( )
{
2014-03-10 04:12:25 +05:30
emitFailed ( tr ( " Failed to download assets! " ) ) ;
2013-12-10 11:42:52 +05:30
}
2013-08-05 06:59:50 +05:30
void OneSixUpdate : : jarlibStart ( )
{
2013-12-23 21:16:01 +05:30
setStatus ( tr ( " Getting the library files from Mojang... " ) ) ;
2013-11-25 05:16:52 +05:30
QLOG_INFO ( ) < < m_inst - > name ( ) < < " : downloading libraries " ;
2013-09-22 07:51:36 +05:30
OneSixInstance * inst = ( OneSixInstance * ) m_inst ;
2014-03-10 04:12:25 +05:30
try
{
inst - > reloadVersion ( ) ;
}
catch ( MMCError & e )
{
emitFailed ( e . cause ( ) ) ;
return ;
}
catch ( . . . )
2013-08-05 06:59:50 +05:30
{
2014-03-10 04:12:25 +05:30
emitFailed ( tr ( " Failed to load the version description file for reasons unknown. " ) ) ;
2013-08-05 06:59:50 +05:30
return ;
}
2013-09-22 07:51:36 +05:30
2013-12-11 01:03:24 +05:30
// Build a list of URLs that will need to be downloaded.
2014-03-02 03:36:47 +05:30
std : : shared_ptr < VersionFinal > version = inst - > getFullVersion ( ) ;
2013-12-11 01:03:24 +05:30
// minecraft.jar for this version
{
QString version_id = version - > id ;
QString localPath = version_id + " / " + version_id + " .jar " ;
2013-12-13 20:28:11 +05:30
QString urlstr = " http:// " + URLConstants : : AWS_DOWNLOAD_VERSIONS + localPath ;
2013-09-22 07:51:36 +05:30
2014-03-23 23:37:13 +05:30
auto job = new NetJob ( tr ( " Libraries for instance %1 " ) . arg ( inst - > name ( ) ) ) ;
2013-09-22 07:51:36 +05:30
2013-12-11 01:03:24 +05:30
auto metacache = MMC - > metacache ( ) ;
auto entry = metacache - > resolveEntry ( " versions " , localPath ) ;
job - > addNetAction ( CacheDownload : : make ( QUrl ( urlstr ) , entry ) ) ;
jarlibDownloadJob . reset ( job ) ;
}
2013-09-22 07:51:36 +05:30
2013-08-05 06:59:50 +05:30
auto libs = version - > getActiveNativeLibs ( ) ;
libs . append ( version - > getActiveNormalLibs ( ) ) ;
2013-09-22 07:51:36 +05:30
2013-09-08 05:45:20 +05:30
auto metacache = MMC - > metacache ( ) ;
2013-11-17 16:14:18 +05:30
QList < ForgeXzDownloadPtr > ForgeLibs ;
2014-04-14 02:36:28 +05:30
QList < std : : shared_ptr < OneSixLibrary > > brokenLocalLibs ;
2013-09-22 07:51:36 +05:30
for ( auto lib : libs )
2013-08-05 06:59:50 +05:30
{
2013-10-21 02:48:40 +05:30
if ( lib - > hint ( ) = = " local " )
2014-04-14 02:36:28 +05:30
{
if ( ! lib - > filesExist ( ) )
brokenLocalLibs . append ( lib ) ;
2013-10-21 02:48:40 +05:30
continue ;
2014-04-14 02:36:28 +05:30
}
2013-11-25 05:16:52 +05:30
2014-01-14 05:43:35 +05:30
QString raw_storage = lib - > storagePath ( ) ;
QString raw_dl = lib - > downloadUrl ( ) ;
2013-11-25 05:16:52 +05:30
2014-01-14 05:43:35 +05:30
auto f = [ & ] ( QString storage , QString dl )
2013-09-08 05:45:20 +05:30
{
2014-01-14 05:43:35 +05:30
auto entry = metacache - > resolveEntry ( " libraries " , storage ) ;
if ( entry - > stale )
2013-11-17 16:14:18 +05:30
{
2014-01-14 05:43:35 +05:30
if ( lib - > hint ( ) = = " forge-pack-xz " )
{
ForgeLibs . append ( ForgeXzDownload : : make ( storage , entry ) ) ;
}
else
{
jarlibDownloadJob - > addNetAction ( CacheDownload : : make ( dl , entry ) ) ;
}
2013-11-17 16:14:18 +05:30
}
2014-01-14 05:43:35 +05:30
} ;
if ( raw_storage . contains ( " ${arch} " ) )
{
QString cooked_storage = raw_storage ;
QString cooked_dl = raw_dl ;
f ( cooked_storage . replace ( " ${arch} " , " 32 " ) , cooked_dl . replace ( " ${arch} " , " 32 " ) ) ;
cooked_storage = raw_storage ;
cooked_dl = raw_dl ;
f ( cooked_storage . replace ( " ${arch} " , " 64 " ) , cooked_dl . replace ( " ${arch} " , " 64 " ) ) ;
}
else
{
f ( raw_storage , raw_dl ) ;
2013-09-08 05:45:20 +05:30
}
2013-08-05 06:59:50 +05:30
}
2014-04-14 02:36:28 +05:30
if ( ! brokenLocalLibs . empty ( ) )
{
jarlibDownloadJob . reset ( ) ;
QStringList failed ;
for ( auto brokenLib : brokenLocalLibs )
{
failed . append ( brokenLib - > files ( ) ) ;
}
QString failed_all = failed . join ( " \n " ) ;
emitFailed ( tr ( " Some libraries marked as 'local' are missing their jar files: \n %1 \n \n You'll have to correct this problem manually. If this is an externally tracked instance, make sure to run it at least once outside of MultiMC. " ) . arg ( failed_all ) ) ;
return ;
}
2013-11-17 16:14:18 +05:30
// TODO: think about how to propagate this from the original json file... or IF AT ALL
QString forgeMirrorList = " http://files.minecraftforge.net/mirror-brand.list " ;
if ( ! ForgeLibs . empty ( ) )
{
jarlibDownloadJob - > addNetAction (
ForgeMirrors : : make ( ForgeLibs , jarlibDownloadJob , forgeMirrorList ) ) ;
}
2013-10-06 04:43:40 +05:30
connect ( jarlibDownloadJob . get ( ) , SIGNAL ( succeeded ( ) ) , SLOT ( jarlibFinished ( ) ) ) ;
connect ( jarlibDownloadJob . get ( ) , SIGNAL ( failed ( ) ) , SLOT ( jarlibFailed ( ) ) ) ;
connect ( jarlibDownloadJob . get ( ) , SIGNAL ( progress ( qint64 , qint64 ) ) ,
2013-09-22 07:51:36 +05:30
SIGNAL ( progress ( qint64 , qint64 ) ) ) ;
2013-08-05 06:59:50 +05:30
2013-09-02 03:55:40 +05:30
jarlibDownloadJob - > start ( ) ;
2013-07-10 02:16:33 +05:30
}
2013-08-04 18:16:33 +05:30
void OneSixUpdate : : jarlibFinished ( )
2013-07-10 02:16:33 +05:30
{
2013-12-10 11:42:52 +05:30
assetIndexStart ( ) ;
2013-07-10 02:16:33 +05:30
}
2013-08-04 18:16:33 +05:30
void OneSixUpdate : : jarlibFailed ( )
2013-07-10 02:16:33 +05:30
{
2013-09-26 06:28:09 +05:30
QStringList failed = jarlibDownloadJob - > getFailedFiles ( ) ;
QString failed_all = failed . join ( " \n " ) ;
2014-03-10 04:12:25 +05:30
emitFailed ( tr ( " Failed to download the following files: \n %1 \n \n Please try again. " ) . arg ( failed_all ) ) ;
2013-07-08 03:21:26 +05:30
}