NOISSUE use new mojang assets locations

This commit is contained in:
Petr Mrázek 2016-03-21 02:19:23 +01:00
parent 2929ca7413
commit d587720010
9 changed files with 104 additions and 60 deletions

View File

@ -13,6 +13,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include <QFileInfo>
#include <QDir> #include <QDir>
#include <QDirIterator> #include <QDirIterator>
#include <QCryptographicHash> #include <QCryptographicHash>
@ -23,7 +24,8 @@
#include <QDebug> #include <QDebug>
#include "AssetsUtils.h" #include "AssetsUtils.h"
#include <FileSystem.h> #include "FileSystem.h"
#include "net/MD5EtagDownload.h"
namespace AssetsUtils namespace AssetsUtils
{ {
@ -32,7 +34,7 @@ namespace AssetsUtils
* Returns true on success, with index populated * Returns true on success, with index populated
* index is undefined otherwise * index is undefined otherwise
*/ */
bool loadAssetsIndexJson(QString path, AssetsIndex *index) bool loadAssetsIndexJson(QString assetsId, QString path, AssetsIndex *index)
{ {
/* /*
{ {
@ -56,6 +58,7 @@ bool loadAssetsIndexJson(QString path, AssetsIndex *index)
qCritical() << "Failed to read assets index file" << path; qCritical() << "Failed to read assets index file" << path;
return false; return false;
} }
index->id = assetsId;
// Read the file and close it. // Read the file and close it.
QByteArray jsonData = file.readAll(); QByteArray jsonData = file.readAll();
@ -143,7 +146,7 @@ QDir reconstructAssets(QString assetsId)
<< objectDir.path() << virtualDir.path() << virtualRoot.path(); << objectDir.path() << virtualDir.path() << virtualRoot.path();
AssetsIndex index; AssetsIndex index;
bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(indexPath, &index); bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, &index);
if (loadAssetsIndex && index.isVirtual) if (loadAssetsIndex && index.isVirtual)
{ {
@ -182,3 +185,46 @@ QDir reconstructAssets(QString assetsId)
} }
} }
NetActionPtr AssetObject::getDownloadAction()
{
QFileInfo objectFile(getLocalPath());
if ((!objectFile.isFile()) || (objectFile.size() != size))
{
auto objectDL = MD5EtagDownload::make(getUrl(), objectFile.filePath());
objectDL->m_total_progress = size;
return objectDL;
}
return nullptr;
}
QString AssetObject::getLocalPath()
{
return "assets/objects/" + getRelPath();
}
QUrl AssetObject::getUrl()
{
return QUrl("http://resources.download.minecraft.net/" + getRelPath());
}
QString AssetObject::getRelPath()
{
return hash.left(2) + "/" + hash;
}
NetJobPtr AssetsIndex::getDownloadJob()
{
auto job = new NetJob(QObject::tr("Assets for %1").arg(id));
for (auto &object : objects.values())
{
auto dl = object.getDownloadAction();
if(dl)
{
job->addNetAction(dl);
}
}
if(job->size())
return job;
return nullptr;
}

View File

@ -17,22 +17,32 @@
#include <QString> #include <QString>
#include <QMap> #include <QMap>
#include "net/NetAction.h"
#include "net/NetJob.h"
struct AssetObject struct AssetObject
{ {
QString getRelPath();
QUrl getUrl();
QString getLocalPath();
NetActionPtr getDownloadAction();
QString hash; QString hash;
qint64 size; qint64 size;
}; };
struct AssetsIndex struct AssetsIndex
{ {
NetJobPtr getDownloadJob();
QString id;
QMap<QString, AssetObject> objects; QMap<QString, AssetObject> objects;
bool isVirtual = false; bool isVirtual = false;
}; };
namespace AssetsUtils namespace AssetsUtils
{ {
bool loadAssetsIndexJson(QString file, AssetsIndex* index); bool loadAssetsIndexJson(QString id, QString file, AssetsIndex* index);
/// Reconstruct a virtual assets folder for the given assets ID and return the folder /// Reconstruct a virtual assets folder for the given assets ID and return the folder
QDir reconstructAssets(QString assetsId); QDir reconstructAssets(QString assetsId);
} }

View File

@ -62,7 +62,7 @@ void MinecraftProfile::clear()
{ {
m_minecraftVersion.clear(); m_minecraftVersion.clear();
m_minecraftVersionType.clear(); m_minecraftVersionType.clear();
m_minecraftAssets.clear(); m_minecraftAssets.reset();
m_minecraftArguments.clear(); m_minecraftArguments.clear();
m_tweakers.clear(); m_tweakers.clear();
m_mainClass.clear(); m_mainClass.clear();
@ -420,9 +420,12 @@ void MinecraftProfile::applyMinecraftVersionType(const QString& type)
applyString(type, this->m_minecraftVersionType); applyString(type, this->m_minecraftVersionType);
} }
void MinecraftProfile::applyMinecraftAssets(const QString& assets) void MinecraftProfile::applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets)
{ {
applyString(assets, this->m_minecraftAssets); if(assets)
{
m_minecraftAssets = assets;
}
} }
void MinecraftProfile::applyTraits(const QSet<QString>& traits) void MinecraftProfile::applyTraits(const QSet<QString>& traits)
@ -544,18 +547,11 @@ QString MinecraftProfile::getMinecraftVersionType() const
return m_minecraftVersionType; return m_minecraftVersionType;
} }
QString MinecraftProfile::getMinecraftAssets() const std::shared_ptr<MojangAssetIndexInfo> MinecraftProfile::getMinecraftAssets() const
{ {
// HACK: deny april fools. my head hurts enough already. if(!m_minecraftAssets)
QDate now = QDate::currentDate();
bool isAprilFools = now.month() == 4 && now.day() == 1;
if (m_minecraftAssets.endsWith("_af") && !isAprilFools)
{ {
return m_minecraftAssets.left(m_minecraftAssets.length() - 3); return std::make_shared<MojangAssetIndexInfo>("legacy");
}
if (m_minecraftAssets.isEmpty())
{
return QLatin1Literal("legacy");
} }
return m_minecraftAssets; return m_minecraftAssets;
} }

View File

@ -24,6 +24,7 @@
#include "Library.h" #include "Library.h"
#include "VersionFile.h" #include "VersionFile.h"
#include "JarMod.h" #include "JarMod.h"
#include "MojangDownloadInfo.h"
#include "multimc_logic_export.h" #include "multimc_logic_export.h"
@ -90,19 +91,19 @@ public: /* application of profile variables from patches */
void applyAppletClass(const QString& appletClass); void applyAppletClass(const QString& appletClass);
void applyMinecraftArguments(const QString& minecraftArguments); void applyMinecraftArguments(const QString& minecraftArguments);
void applyMinecraftVersionType(const QString& type); void applyMinecraftVersionType(const QString& type);
void applyMinecraftAssets(const QString& assets); void applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets);
void applyTraits(const QSet<QString> &traits); void applyTraits(const QSet<QString> &traits);
void applyTweakers(const QStringList &tweakers); void applyTweakers(const QStringList &tweakers);
void applyJarMods(const QList<JarmodPtr> &jarMods); void applyJarMods(const QList<JarmodPtr> &jarMods);
void applyLibrary(LibraryPtr library); void applyLibrary(LibraryPtr library);
void applyProblemSeverity(ProblemSeverity severity); void applyProblemSeverity(ProblemSeverity severity);
public: /* getters for proifile variables */ public: /* getters for profile variables */
QString getMinecraftVersion() const; QString getMinecraftVersion() const;
QString getMainClass() const; QString getMainClass() const;
QString getAppletClass() const; QString getAppletClass() const;
QString getMinecraftVersionType() const; QString getMinecraftVersionType() const;
QString getMinecraftAssets() const; MojangAssetIndexInfo::Ptr getMinecraftAssets() const;
QString getMinecraftArguments() const; QString getMinecraftArguments() const;
const QSet<QString> & getTraits() const; const QSet<QString> & getTraits() const;
const QStringList & getTweakers() const; const QStringList & getTweakers() const;
@ -136,7 +137,7 @@ private: /* data */
QString m_minecraftVersionType; QString m_minecraftVersionType;
/// Assets type - "legacy" or a version ID /// Assets type - "legacy" or a version ID
QString m_minecraftAssets; MojangAssetIndexInfo::Ptr m_minecraftAssets;
/** /**
* arguments that should be used for launching minecraft * arguments that should be used for launching minecraft

View File

@ -157,11 +157,15 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFi
} }
Bits::readString(in, "type", out->type); Bits::readString(in, "type", out->type);
Bits::readString(in, "assets", out->assets);
if(in.contains("assetIndex")) if(in.contains("assetIndex"))
{ {
out->mojangAssetIndex = assetIndexFromJson(requireObject(in, "assetIndex")); out->mojangAssetIndex = assetIndexFromJson(requireObject(in, "assetIndex"));
} }
Bits::readString(in, "assets", out->assets); else if (!out->assets.isNull())
{
out->mojangAssetIndex = std::make_shared<MojangAssetIndexInfo>(out->assets);
}
out->m_releaseTime = timeFromS3Time(in.value("releaseTime").toString("")); out->m_releaseTime = timeFromS3Time(in.value("releaseTime").toString(""));
out->m_updateTime = timeFromS3Time(in.value("time").toString("")); out->m_updateTime = timeFromS3Time(in.value("time").toString(""));
@ -231,7 +235,6 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj
writeString(out, "mainClass", in->mainClass); writeString(out, "mainClass", in->mainClass);
writeString(out, "minecraftArguments", in->minecraftArguments); writeString(out, "minecraftArguments", in->minecraftArguments);
writeString(out, "type", in->type); writeString(out, "type", in->type);
writeString(out, "assets", in->assets);
if(!in->m_releaseTime.isNull()) if(!in->m_releaseTime.isNull())
{ {
writeString(out, "releaseTime", timeToS3Time(in->m_releaseTime)); writeString(out, "releaseTime", timeToS3Time(in->m_releaseTime));
@ -244,6 +247,7 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj
{ {
out.insert("minimumLauncherVersion", in->minimumLauncherVersion); out.insert("minimumLauncherVersion", in->minimumLauncherVersion);
} }
writeString(out, "assets", in->assets);
if(in->mojangAssetIndex && in->mojangAssetIndex->known) if(in->mojangAssetIndex && in->mojangAssetIndex->known)
{ {
out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex)); out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex));

View File

@ -40,7 +40,7 @@ void VersionFile::applyTo(MinecraftProfile *profile)
{ {
profile->applyMinecraftVersionType(type); profile->applyMinecraftVersionType(type);
} }
profile->applyMinecraftAssets(assets); profile->applyMinecraftAssets(mojangAssetIndex);
profile->applyTweakers(addTweakers); profile->applyTweakers(addTweakers);
profile->applyJarMods(jarMods); profile->applyJarMods(jarMods);

View File

@ -125,14 +125,15 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
QString absRootDir = QDir(minecraftRoot()).absolutePath(); QString absRootDir = QDir(minecraftRoot()).absolutePath();
token_mapping["game_directory"] = absRootDir; token_mapping["game_directory"] = absRootDir;
QString absAssetsDir = QDir("assets/").absolutePath(); QString absAssetsDir = QDir("assets/").absolutePath();
token_mapping["game_assets"] = AssetsUtils::reconstructAssets(m_profile->getMinecraftAssets()).absolutePath(); auto assets = m_profile->getMinecraftAssets();
token_mapping["game_assets"] = AssetsUtils::reconstructAssets(assets->id).absolutePath();
token_mapping["user_properties"] = session->serializeUserProperties(); token_mapping["user_properties"] = session->serializeUserProperties();
token_mapping["user_type"] = session->user_type; token_mapping["user_type"] = session->user_type;
// 1.7.3+ assets tokens // 1.7.3+ assets tokens
token_mapping["assets_root"] = absAssetsDir; token_mapping["assets_root"] = absAssetsDir;
token_mapping["assets_index_name"] = m_profile->getMinecraftAssets(); token_mapping["assets_index_name"] = assets->id;
QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts);
for (int i = 0; i < parts.length(); i++) for (int i = 0; i < parts.length(); i++)

View File

@ -88,9 +88,9 @@ void OneSixUpdate::assetIndexStart()
setStatus(tr("Updating assets index...")); setStatus(tr("Updating assets index..."));
OneSixInstance *inst = (OneSixInstance *)m_inst; OneSixInstance *inst = (OneSixInstance *)m_inst;
auto profile = inst->getMinecraftProfile(); auto profile = inst->getMinecraftProfile();
QString assetName = profile->getMinecraftAssets(); auto assets = profile->getMinecraftAssets();
QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json"; QUrl indexUrl = assets->url;
QString localPath = assetName + ".json"; QString localPath = assets->id + ".json";
auto job = new NetJob(tr("Asset index for %1").arg(inst->name())); auto job = new NetJob(tr("Asset index for %1").arg(inst->name()));
auto metacache = ENV.metacache(); auto metacache = ENV.metacache();
@ -114,36 +114,23 @@ void OneSixUpdate::assetIndexFinished()
OneSixInstance *inst = (OneSixInstance *)m_inst; OneSixInstance *inst = (OneSixInstance *)m_inst;
auto profile = inst->getMinecraftProfile(); auto profile = inst->getMinecraftProfile();
QString assetName = profile->getMinecraftAssets(); auto assets = profile->getMinecraftAssets();
QString asset_fname = "assets/indexes/" + assetName + ".json"; QString asset_fname = "assets/indexes/" + assets->id + ".json";
if (!AssetsUtils::loadAssetsIndexJson(asset_fname, &index)) // FIXME: this looks like a job for a generic validator based on json schema?
if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, &index))
{ {
auto metacache = ENV.metacache(); auto metacache = ENV.metacache();
auto entry = metacache->resolveEntry("asset_indexes", assetName + ".json"); auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json");
metacache->evictEntry(entry); metacache->evictEntry(entry);
emitFailed(tr("Failed to read the assets index!")); emitFailed(tr("Failed to read the assets index!"));
} }
QList<Md5EtagDownloadPtr> dls; auto job = index.getDownloadJob();
for (auto object : index.objects.values()) if(job)
{
QString objectName = object.hash.left(2) + "/" + object.hash;
QFileInfo objectFile("assets/objects/" + objectName);
if ((!objectFile.isFile()) || (objectFile.size() != object.size))
{
auto objectDL = MD5EtagDownload::make(QUrl("http://" + URLConstants::RESOURCE_BASE + objectName), objectFile.filePath());
objectDL->m_total_progress = object.size;
dls.append(objectDL);
}
}
if (dls.size())
{ {
setStatus(tr("Getting the assets files from Mojang...")); setStatus(tr("Getting the assets files from Mojang..."));
auto job = new NetJob(tr("Assets for %1").arg(inst->name())); jarlibDownloadJob = job;
for (auto dl : dls)
job->addNetAction(dl);
jarlibDownloadJob.reset(job);
connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetsFinished())); connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetsFinished()));
connect(jarlibDownloadJob.get(), &NetJob::failed, this, &OneSixUpdate::assetsFailed); connect(jarlibDownloadJob.get(), &NetJob::failed, this, &OneSixUpdate::assetsFailed);
connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));

View File

@ -35,16 +35,15 @@ class MULTIMC_LOGIC_EXPORT NetJob : public Task
public: public:
explicit NetJob(QString job_name) : Task(), m_job_name(job_name) {} explicit NetJob(QString job_name) : Task(), m_job_name(job_name) {}
virtual ~NetJob() {} virtual ~NetJob() {}
template <typename T> bool addNetAction(T action) bool addNetAction(NetActionPtr action)
{ {
NetActionPtr base = std::static_pointer_cast<NetAction>(action); action->m_index_within_job = downloads.size();
base->m_index_within_job = downloads.size();
downloads.append(action); downloads.append(action);
part_info pi; part_info pi;
{ {
pi.current_progress = base->currentProgress(); pi.current_progress = action->currentProgress();
pi.total_progress = base->totalProgress(); pi.total_progress = action->totalProgress();
pi.failures = base->numberOfFailures(); pi.failures = action->numberOfFailures();
} }
parts_progress.append(pi); parts_progress.append(pi);
total_progress += pi.total_progress; total_progress += pi.total_progress;
@ -52,11 +51,11 @@ public:
if (isRunning()) if (isRunning())
{ {
setProgress(current_progress, total_progress); setProgress(current_progress, total_progress);
connect(base.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); connect(action.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
connect(base.get(), SIGNAL(failed(int)), SLOT(partFailed(int))); connect(action.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
connect(base.get(), SIGNAL(netActionProgress(int, qint64, qint64)), connect(action.get(), SIGNAL(netActionProgress(int, qint64, qint64)),
SLOT(partProgress(int, qint64, qint64))); SLOT(partProgress(int, qint64, qint64)));
base->start(); action->start();
} }
return true; return true;
} }