NOISSUE add versioning to component metadata format and use it
This commit is contained in:
		| @@ -28,6 +28,11 @@ using namespace Json; | ||||
| namespace Meta | ||||
| { | ||||
|  | ||||
| MetadataVersion currentFormatVersion() | ||||
| { | ||||
| 	return MetadataVersion::InitialRelease; | ||||
| } | ||||
|  | ||||
| // Index | ||||
| static std::shared_ptr<Index> parseIndexInternal(const QJsonObject &obj) | ||||
| { | ||||
| @@ -49,7 +54,6 @@ static VersionPtr parseCommonVersion(const QString &uid, const QJsonObject &obj) | ||||
| 	VersionPtr version = std::make_shared<Version>(uid, requireString(obj, "version")); | ||||
| 	version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000); | ||||
| 	version->setType(ensureString(obj, "type", QString())); | ||||
| 	version->setParentUid(ensureString(obj, "parentUid", QString())); | ||||
| 	version->setRecommended(ensureBoolean(obj, QString("recommended"), false)); | ||||
| 	version->setVolatile(ensureBoolean(obj, QString("volatile"), false)); | ||||
| 	RequireSet requires, conflicts; | ||||
| @@ -86,56 +90,79 @@ static std::shared_ptr<VersionList> parseVersionListInternal(const QJsonObject & | ||||
|  | ||||
| 	VersionListPtr list = std::make_shared<VersionList>(uid); | ||||
| 	list->setName(ensureString(obj, "name", QString())); | ||||
| 	list->setParentUid(ensureString(obj, "parentUid", QString())); | ||||
| 	list->setVersions(versions); | ||||
| 	return list; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int formatVersion(const QJsonObject &obj) | ||||
| MetadataVersion parseFormatVersion(const QJsonObject &obj, bool required) | ||||
| { | ||||
| 	if (!obj.contains("formatVersion")) { | ||||
| 		throw ParseException(QObject::tr("Missing required field: 'formatVersion'")); | ||||
| 	if (!obj.contains("formatVersion")) | ||||
| 	{ | ||||
| 		if(required) | ||||
| 		{ | ||||
| 			return MetadataVersion::Invalid; | ||||
| 		} | ||||
| 		return MetadataVersion::InitialRelease; | ||||
| 	} | ||||
| 	if (!obj.value("formatVersion").isDouble()) { | ||||
| 		throw ParseException(QObject::tr("Required field has invalid type: 'formatVersion'")); | ||||
| 	if (!obj.value("formatVersion").isDouble()) | ||||
| 	{ | ||||
| 		return MetadataVersion::Invalid; | ||||
| 	} | ||||
| 	return obj.value("formatVersion").toInt(); | ||||
| 	switch(obj.value("formatVersion").toInt()) | ||||
| 	{ | ||||
| 		case 0: | ||||
| 			return MetadataVersion::InitialRelease; | ||||
| 		default: | ||||
| 			return MetadataVersion::Invalid; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void serializeFormatVersion(QJsonObject& obj, Meta::MetadataVersion version) | ||||
| { | ||||
| 	if(version == MetadataVersion::Invalid) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	obj.insert("formatVersion", int(version)); | ||||
| } | ||||
|  | ||||
| void parseIndex(const QJsonObject &obj, Index *ptr) | ||||
| { | ||||
| 	const int version = formatVersion(obj); | ||||
| 	switch (version) { | ||||
| 	case 0: | ||||
| 	const MetadataVersion version = parseFormatVersion(obj); | ||||
| 	switch (version) | ||||
| 	{ | ||||
| 	case MetadataVersion::InitialRelease: | ||||
| 		ptr->merge(parseIndexInternal(obj)); | ||||
| 		break; | ||||
| 	default: | ||||
| 		throw ParseException(QObject::tr("Unknown formatVersion: %1").arg(version)); | ||||
| 	case MetadataVersion::Invalid: | ||||
| 		throw ParseException(QObject::tr("Unknown format version!")); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void parseVersionList(const QJsonObject &obj, VersionList *ptr) | ||||
| { | ||||
| 	const int version = formatVersion(obj); | ||||
| 	switch (version) { | ||||
| 	case 0: | ||||
| 	const MetadataVersion version = parseFormatVersion(obj); | ||||
| 	switch (version) | ||||
| 	{ | ||||
| 	case MetadataVersion::InitialRelease: | ||||
| 		ptr->merge(parseVersionListInternal(obj)); | ||||
| 		break; | ||||
| 	default: | ||||
| 		throw ParseException(QObject::tr("Unknown formatVersion: %1").arg(version)); | ||||
| 	case MetadataVersion::Invalid: | ||||
| 		throw ParseException(QObject::tr("Unknown format version!")); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void parseVersion(const QJsonObject &obj, Version *ptr) | ||||
| { | ||||
| 	const int version = formatVersion(obj); | ||||
| 	switch (version) { | ||||
| 	case 0: | ||||
| 	const MetadataVersion version = parseFormatVersion(obj); | ||||
| 	switch (version) | ||||
| 	{ | ||||
| 	case MetadataVersion::InitialRelease: | ||||
| 		ptr->merge(parseVersionInternal(obj)); | ||||
| 		break; | ||||
| 	default: | ||||
| 		throw ParseException(QObject::tr("Unknown formatVersion: %1").arg(version)); | ||||
| 	case MetadataVersion::Invalid: | ||||
| 		throw ParseException(QObject::tr("Unknown format version!")); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -28,6 +28,12 @@ class Index; | ||||
| class Version; | ||||
| class VersionList; | ||||
|  | ||||
| enum class MetadataVersion | ||||
| { | ||||
| 	Invalid = -1, | ||||
| 	InitialRelease = 0 | ||||
| }; | ||||
|  | ||||
| class ParseException : public Exception | ||||
| { | ||||
| public: | ||||
| @@ -65,9 +71,13 @@ void parseIndex(const QJsonObject &obj, Index *ptr); | ||||
| void parseVersion(const QJsonObject &obj, Version *ptr); | ||||
| void parseVersionList(const QJsonObject &obj, VersionList *ptr); | ||||
|  | ||||
| MetadataVersion parseFormatVersion(const QJsonObject &obj, bool required = true); | ||||
| void serializeFormatVersion(QJsonObject &obj, MetadataVersion version); | ||||
|  | ||||
| // FIXME: this has a different shape than the others...FIX IT!? | ||||
| void parseRequires(const QJsonObject &obj, RequireSet * ptr, const char * keyName = "requires"); | ||||
| void serializeRequires(QJsonObject & objOut, RequireSet* ptr, const char * keyName = "requires"); | ||||
| MetadataVersion currentFormatVersion(); | ||||
| } | ||||
|  | ||||
| Q_DECLARE_METATYPE(std::set<Meta::Require>); | ||||
| @@ -79,10 +79,6 @@ void Meta::Version::mergeFromList(const Meta::VersionPtr& other) | ||||
| 	{ | ||||
| 		m_conflicts = other->m_conflicts; | ||||
| 	} | ||||
| 	if (m_parentUid != other->m_parentUid) | ||||
| 	{ | ||||
| 		setParentUid(other->m_parentUid); | ||||
| 	} | ||||
| 	if(m_volatile != other->m_volatile) | ||||
| 	{ | ||||
| 		setVolatile(other->m_volatile); | ||||
| @@ -103,12 +99,6 @@ QString Meta::Version::localFilename() const | ||||
| 	return m_uid + '/' + m_version + ".json"; | ||||
| } | ||||
|  | ||||
| void Meta::Version::setParentUid(const QString& parentUid) | ||||
| { | ||||
| 	m_parentUid = parentUid; | ||||
| 	emit requiresChanged(); | ||||
| } | ||||
|  | ||||
| void Meta::Version::setType(const QString &type) | ||||
| { | ||||
| 	m_type = type; | ||||
|   | ||||
| @@ -50,10 +50,6 @@ public: /* con/des */ | ||||
| 	{ | ||||
| 		return m_uid; | ||||
| 	} | ||||
| 	QString parentUid() const | ||||
| 	{ | ||||
| 		return m_parentUid; | ||||
| 	} | ||||
| 	QString version() const | ||||
| 	{ | ||||
| 		return m_version; | ||||
| @@ -91,7 +87,6 @@ public: /* con/des */ | ||||
| 	QString localFilename() const override; | ||||
|  | ||||
| public: // for usage by format parsers only | ||||
| 	void setParentUid(const QString &parentUid); | ||||
| 	void setType(const QString &type); | ||||
| 	void setTime(const qint64 time); | ||||
| 	void setRequires(const Meta::RequireSet &requires, const Meta::RequireSet &conflicts); | ||||
| @@ -110,7 +105,6 @@ private: | ||||
| 	bool m_recommended = false; | ||||
| 	QString m_name; | ||||
| 	QString m_uid; | ||||
| 	QString m_parentUid; | ||||
| 	QString m_version; | ||||
| 	QString m_type; | ||||
| 	qint64 m_time = 0; | ||||
|   | ||||
| @@ -76,20 +76,17 @@ QVariant VersionList::data(const QModelIndex &index, int role) const | ||||
| 		return version->version(); | ||||
| 	case ParentVersionRole: | ||||
| 	{ | ||||
| 		auto parentUid = this->parentUid(); | ||||
| 		if(parentUid.isEmpty()) | ||||
| 		{ | ||||
| 			return QVariant(); | ||||
| 		} | ||||
| 		// FIXME: HACK: this should be generic and be replaced by something else. Anything that is a hard 'equals' dep is a 'parent uid'. | ||||
| 		auto & reqs = version->requires(); | ||||
| 		auto iter = std::find_if(reqs.begin(), reqs.end(), [&parentUid](const Require & req) | ||||
| 		auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Require & req) | ||||
| 		{ | ||||
| 			return req.uid == parentUid; | ||||
| 			return req.uid == "net.minecraft"; | ||||
| 		}); | ||||
| 		if (iter != reqs.end()) | ||||
| 		{ | ||||
| 			return (*iter).equalsVersion; | ||||
| 		} | ||||
| 		return QVariant(); | ||||
| 	} | ||||
| 	case TypeRole: return version->type(); | ||||
|  | ||||
| @@ -196,11 +193,6 @@ void VersionList::mergeFromIndex(const VersionListPtr &other) | ||||
| 	{ | ||||
| 		setName(other->m_name); | ||||
| 	} | ||||
|  | ||||
| 	if(m_parentUid != other->m_parentUid) | ||||
| 	{ | ||||
| 		setParentUid(other->m_parentUid); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void VersionList::merge(const VersionListPtr &other) | ||||
| @@ -210,11 +202,6 @@ void VersionList::merge(const VersionListPtr &other) | ||||
| 		setName(other->m_name); | ||||
| 	} | ||||
|  | ||||
| 	if(m_parentUid != other->m_parentUid) | ||||
| 	{ | ||||
| 		setParentUid(other->m_parentUid); | ||||
| 	} | ||||
|  | ||||
| 	// TODO: do not reset the whole model. maybe? | ||||
| 	beginResetModel(); | ||||
| 	m_versions.clear(); | ||||
| @@ -256,8 +243,3 @@ BaseVersionPtr VersionList::getRecommended() const | ||||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
| void Meta::VersionList::setParentUid(const QString& parentUid) | ||||
| { | ||||
| 	m_parentUid = parentUid; | ||||
| } | ||||
|   | ||||
| @@ -55,10 +55,6 @@ public: | ||||
|  | ||||
| 	QString localFilename() const override; | ||||
|  | ||||
| 	QString parentUid() const | ||||
| 	{ | ||||
| 		return m_parentUid; | ||||
| 	} | ||||
| 	QString uid() const | ||||
| 	{ | ||||
| 		return m_uid; | ||||
| @@ -78,7 +74,6 @@ public: | ||||
|  | ||||
| public: // for usage only by parsers | ||||
| 	void setName(const QString &name); | ||||
| 	void setParentUid(const QString &parentUid); | ||||
| 	void setVersions(const QVector<VersionPtr> &versions); | ||||
| 	void merge(const VersionListPtr &other); | ||||
| 	void mergeFromIndex(const VersionListPtr &other); | ||||
| @@ -96,7 +91,6 @@ private: | ||||
| 	QVector<VersionPtr> m_versions; | ||||
| 	QHash<QString, VersionPtr> m_lookup; | ||||
| 	QString m_uid; | ||||
| 	QString m_parentUid; | ||||
| 	QString m_name; | ||||
|  | ||||
| 	VersionPtr m_recommended; | ||||
|   | ||||
| @@ -450,42 +450,30 @@ bool ComponentList::migratePreComponentConfig() | ||||
| 				intendedVersion = emptyVersion; | ||||
| 			} | ||||
| 			auto file = ProfileUtils::parseJsonFile(QFileInfo(jsonFilePath), false); | ||||
| 			bool fileChanged = false; | ||||
| 			// if uid is missing or incorrect, fix it | ||||
| 			if(file->uid != uid) | ||||
| 			{ | ||||
| 				file->uid = uid; | ||||
| 				fileChanged = true; | ||||
| 			} | ||||
| 			// fix uid | ||||
| 			file->uid = uid; | ||||
| 			// if version is missing, add it from the outside. | ||||
| 			if(file->version.isEmpty()) | ||||
| 			{ | ||||
| 				file->version = intendedVersion; | ||||
| 				fileChanged = true; | ||||
| 			} | ||||
| 			// if this is a dependency (LWJGL), mark it also as volatile | ||||
| 			if(asDependency) | ||||
| 			{ | ||||
| 				file->m_volatile = true; | ||||
| 				fileChanged = true; | ||||
| 			} | ||||
| 			// insert requirements if needed | ||||
| 			if(!req.uid.isEmpty()) | ||||
| 			{ | ||||
| 				file->requires.insert(req); | ||||
| 				fileChanged = true; | ||||
| 			} | ||||
| 			// insert conflicts if needed | ||||
| 			if(!conflict.uid.isEmpty()) | ||||
| 			{ | ||||
| 				file->conflicts.insert(conflict); | ||||
| 				fileChanged = true; | ||||
| 			} | ||||
| 			if(fileChanged) | ||||
| 			{ | ||||
| 				// FIXME: @QUALITY do not ignore return value | ||||
| 				ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), jsonFilePath); | ||||
| 			} | ||||
| 			// FIXME: @QUALITY do not ignore return value | ||||
| 			ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), jsonFilePath); | ||||
| 			component = new Component(this, uid, file); | ||||
| 			component->m_version = intendedVersion; | ||||
| 		} | ||||
| @@ -538,17 +526,9 @@ bool ComponentList::migratePreComponentConfig() | ||||
| 			QFile::remove(info.absoluteFilePath()); | ||||
| 			continue; | ||||
| 		} | ||||
| 		bool fileChanged = false; | ||||
| 		if(file->uid != uid) | ||||
| 		{ | ||||
| 			file->uid = uid; | ||||
| 			fileChanged = true; | ||||
| 		} | ||||
| 		if(fileChanged) | ||||
| 		{ | ||||
| 			// FIXME: @QUALITY do not ignore return value | ||||
| 			ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), info.absoluteFilePath()); | ||||
| 		} | ||||
| 		file->uid = uid; | ||||
| 		// FIXME: @QUALITY do not ignore return value | ||||
| 		ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), info.absoluteFilePath()); | ||||
|  | ||||
| 		auto component = new Component(this, file->uid, file); | ||||
| 		auto version = d->getOldConfigVersion(file->uid); | ||||
|   | ||||
| @@ -52,6 +52,15 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc | ||||
|  | ||||
| 	QJsonObject root = doc.object(); | ||||
|  | ||||
| 	Meta::MetadataVersion formatVersion = Meta::parseFormatVersion(root, false); | ||||
| 	switch(formatVersion) | ||||
| 	{ | ||||
| 		case Meta::MetadataVersion::InitialRelease: | ||||
| 			break; | ||||
| 		case Meta::MetadataVersion::Invalid: | ||||
| 			throw JSONValidationError(filename + " does not contain a recognizable version of the metadata format."); | ||||
| 	} | ||||
|  | ||||
| 	if (requireOrder) | ||||
| 	{ | ||||
| 		if (root.contains("order")) | ||||
| @@ -77,8 +86,6 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc | ||||
| 	} | ||||
|  | ||||
| 	out->version = root.value("version").toString(); | ||||
| 	out->dependsOnMinecraftVersion = root.value("mcVersion").toString(); | ||||
| 	// out->filename = filename; | ||||
|  | ||||
| 	MojangVersionFormat::readVersionProperties(root, out.get()); | ||||
|  | ||||
| @@ -196,6 +203,17 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc | ||||
| 	{ | ||||
| 		Meta::parseRequires(root, &out->requires); | ||||
| 	} | ||||
| 	QString dependsOnMinecraftVersion = root.value("mcVersion").toString(); | ||||
| 	if(!dependsOnMinecraftVersion.isEmpty()) | ||||
| 	{ | ||||
| 		Meta::Require mcReq; | ||||
| 		mcReq.uid = "net.minecraft"; | ||||
| 		mcReq.equalsVersion = dependsOnMinecraftVersion; | ||||
| 		if (out->requires.count(mcReq) == 0) | ||||
| 		{ | ||||
| 			out->requires.insert(mcReq); | ||||
| 		} | ||||
| 	} | ||||
| 	if (root.contains("conflicts")) | ||||
| 	{ | ||||
| 		Meta::parseRequires(root, &out->conflicts); | ||||
| @@ -237,7 +255,8 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch | ||||
| 	writeString(root, "uid", patch->uid); | ||||
|  | ||||
| 	writeString(root, "version", patch->version); | ||||
| 	writeString(root, "mcVersion", patch->dependsOnMinecraftVersion); | ||||
|  | ||||
| 	Meta::serializeFormatVersion(root, Meta::MetadataVersion::InitialRelease); | ||||
|  | ||||
| 	MojangVersionFormat::writeVersionProperties(patch.get(), root); | ||||
|  | ||||
|   | ||||
| @@ -18,7 +18,7 @@ class LaunchProfile; | ||||
| struct MojangDownloadInfo; | ||||
| struct MojangAssetIndexInfo; | ||||
|  | ||||
| typedef std::shared_ptr<VersionFile> VersionFilePtr; | ||||
| using VersionFilePtr = std::shared_ptr<VersionFile>; | ||||
| class VersionFile : public ProblemContainer | ||||
| { | ||||
| 	friend class MojangVersionFormat; | ||||
|   | ||||
| @@ -402,44 +402,6 @@ void VersionPage::on_forgeBtn_clicked() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TODO: use something like this... except the final decision of what to show has to be deferred until the lists are known | ||||
| /* | ||||
| void VersionPage::on_liteloaderBtn_clicked() | ||||
| { | ||||
| 	QString uid = "com.mumfrey.liteloader"; | ||||
| 	auto vlist = ENV.metadataIndex()->get(uid); | ||||
| 	if(!vlist) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	VersionSelectDialog vselect(vlist.get(), tr("Select %1 version").arg(vlist->name()), this); | ||||
| 	auto parentUid = vlist->parentUid(); | ||||
| 	if(!parentUid.isEmpty()) | ||||
| 	{ | ||||
| 		auto parentvlist = ENV.metadataIndex()->get(parentUid); | ||||
| 		vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion(parentUid)); | ||||
| 		vselect.setEmptyString( | ||||
| 			tr("No %1 versions are currently available for %2 %3") | ||||
| 				.arg(vlist->name()) | ||||
| 				.arg(parentvlist->name()) | ||||
| 				.arg(m_profile->getComponentVersion(parentUid))); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		vselect.setEmptyString(tr("No %1 versions are currently available")); | ||||
| 	} | ||||
| 	vselect.setEmptyErrorString(tr("Couldn't load or download the %1 version lists!").arg(vlist->name())); | ||||
| 	if (vselect.exec() && vselect.selectedVersion()) | ||||
| 	{ | ||||
| 		auto vsn = vselect.selectedVersion(); | ||||
| 		m_profile->setComponentVersion(uid, vsn->descriptor()); | ||||
| 		m_profile->resolve(); | ||||
| 		preselect(m_profile->rowCount(QModelIndex())-1); | ||||
| 		m_container->refreshContainer(); | ||||
| 	} | ||||
| } | ||||
| */ | ||||
|  | ||||
| void VersionPage::on_liteloaderBtn_clicked() | ||||
| { | ||||
| 	auto vlist = ENV.metadataIndex()->get("com.mumfrey.liteloader"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user