Merge branch 'feature_derpstances' of https://github.com/02JanDal/MultiMC5 into feature_derpstances

Conflicts:
	gui/dialogs/OneSixModEditDialog.cpp
	logic/OneSixUpdate.cpp
This commit is contained in:
Petr Mrázek 2014-02-01 19:37:16 +01:00
commit 1936bd181f
24 changed files with 1596 additions and 606 deletions

View File

@ -411,10 +411,7 @@ logic/LegacyUpdate.cpp
logic/LegacyForge.h logic/LegacyForge.h
logic/LegacyForge.cpp logic/LegacyForge.cpp
# 1.6 instances # OneSix instances
logic/OneSixInstance.h
logic/OneSixInstance.cpp
logic/OneSixInstance_p.h
logic/OneSixUpdate.h logic/OneSixUpdate.h
logic/OneSixUpdate.cpp logic/OneSixUpdate.cpp
logic/OneSixVersion.h logic/OneSixVersion.h
@ -425,10 +422,17 @@ logic/OneSixRule.h
logic/OneSixRule.cpp logic/OneSixRule.cpp
logic/OpSys.h logic/OpSys.h
logic/OpSys.cpp logic/OpSys.cpp
logic/BaseInstaller.h
logic/BaseInstaller.cpp
logic/ForgeInstaller.h logic/ForgeInstaller.h
logic/ForgeInstaller.cpp logic/ForgeInstaller.cpp
logic/LiteLoaderInstaller.h logic/LiteLoaderInstaller.h
logic/LiteLoaderInstaller.cpp logic/LiteLoaderInstaller.cpp
logic/OneSixInstance.h
logic/OneSixInstance.cpp
logic/OneSixInstance_p.h
logic/OneSixVersionBuilder.h
logic/OneSixVersionBuilder.cpp
# Nostalgia # Nostalgia
logic/NostalgiaInstance.h logic/NostalgiaInstance.h

View File

@ -55,7 +55,8 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
main_model->setSourceModel(m_version.get()); main_model->setSourceModel(m_version.get());
ui->libraryTreeView->setModel(main_model); ui->libraryTreeView->setModel(main_model);
ui->libraryTreeView->installEventFilter(this); ui->libraryTreeView->installEventFilter(this);
ui->mainClassEdit->setText(m_version->mainClass); connect(ui->libraryTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &OneSixModEditDialog::versionCurrent);
updateVersionControls(); updateVersionControls();
} }
else else
@ -81,6 +82,8 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
ui->resPackTreeView->installEventFilter(this); ui->resPackTreeView->installEventFilter(this);
m_resourcepacks->startWatching(); m_resourcepacks->startWatching();
} }
connect(m_inst, &OneSixInstance::versionReloaded, this, &OneSixModEditDialog::updateVersionControls);
} }
OneSixModEditDialog::~OneSixModEditDialog() OneSixModEditDialog::~OneSixModEditDialog()
@ -92,98 +95,76 @@ OneSixModEditDialog::~OneSixModEditDialog()
void OneSixModEditDialog::updateVersionControls() void OneSixModEditDialog::updateVersionControls()
{ {
bool customVersion = m_inst->versionIsCustom();
ui->customizeBtn->setEnabled(!customVersion);
ui->revertBtn->setEnabled(customVersion);
ui->forgeBtn->setEnabled(true); ui->forgeBtn->setEnabled(true);
ui->liteloaderBtn->setEnabled(LiteLoaderInstaller(m_inst->intendedVersionId()).canApply()); ui->liteloaderBtn->setEnabled(LiteLoaderInstaller().canApply(m_inst));
ui->customEditorBtn->setEnabled(customVersion); ui->mainClassEdit->setText(m_version->mainClass);
} }
void OneSixModEditDialog::disableVersionControls() void OneSixModEditDialog::disableVersionControls()
{ {
ui->customizeBtn->setEnabled(false);
ui->revertBtn->setEnabled(false);
ui->forgeBtn->setEnabled(false); ui->forgeBtn->setEnabled(false);
ui->liteloaderBtn->setEnabled(false); ui->liteloaderBtn->setEnabled(false);
ui->customEditorBtn->setEnabled(false); ui->reloadLibrariesBtn->setEnabled(false);
ui->removeLibraryBtn->setEnabled(false);
ui->mainClassEdit->setText("");
} }
void OneSixModEditDialog::on_customizeBtn_clicked() void OneSixModEditDialog::on_userEditorBtn_clicked()
{ {
if (m_inst->customizeVersion()) QDir root(m_inst->instanceRoot());
if (!root.exists("user.json"))
{ {
m_version = m_inst->getFullVersion(); QFile file(root.absoluteFilePath("user.json"));
main_model->setSourceModel(m_version.get()); if (!file.open(QFile::WriteOnly))
updateVersionControls();
}
}
void OneSixModEditDialog::on_revertBtn_clicked()
{
auto response = CustomMessageBox::selectable(
this, tr("Revert?"), tr("Do you want to revert the "
"version of this instance to its original configuration?"),
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No)->exec();
if (response == QMessageBox::Yes)
{
if (m_inst->revertCustomVersion())
{ {
m_version = m_inst->getFullVersion(); QMessageBox::critical(this, tr("Error"), tr("Couldn't write a skeletion user.json file: %1").arg(file.errorString()));
main_model->setSourceModel(m_version.get()); return;
updateVersionControls();
} }
file.write("{\n}");
file.close();
}
if (!MMC->openJsonEditor(root.absoluteFilePath("user.json")))
{
QMessageBox::warning(this, tr("Error"), tr("Unable to open user.json, check the settings"));
} }
} }
void OneSixModEditDialog::on_customEditorBtn_clicked() void OneSixModEditDialog::on_reloadLibrariesBtn_clicked()
{ {
if (m_inst->versionIsCustom()) m_inst->reloadVersion(this);
}
void OneSixModEditDialog::on_removeLibraryBtn_clicked()
{
if (ui->libraryTreeView->currentIndex().isValid())
{ {
if (!MMC->openJsonEditor(m_inst->instanceRoot() + "/custom.json")) if (!m_version->remove(ui->libraryTreeView->currentIndex().row()))
{ {
QMessageBox::warning(this, tr("Error"), QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
tr("Unable to open custom.json, check the settings")); }
else
{
m_inst->reloadVersion(this);
} }
} }
} }
void OneSixModEditDialog::on_forgeBtn_clicked() void OneSixModEditDialog::on_forgeBtn_clicked()
{ {
if (QDir(m_inst->instanceRoot()).exists("custom.json"))
{
if (QMessageBox::question(this, tr("Revert?"), tr("This action will remove your custom.json. Continue?")) != QMessageBox::Yes)
{
return;
}
QDir(m_inst->instanceRoot()).remove("custom.json");
}
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this); VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
vselect.setFilter(1, m_inst->currentVersionId()); vselect.setFilter(1, m_inst->currentVersionId());
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") + vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
m_inst->currentVersionId()); m_inst->currentVersionId());
if (vselect.exec() && vselect.selectedVersion()) if (vselect.exec() && vselect.selectedVersion())
{ {
if (m_inst->versionIsCustom())
{
auto reply = QMessageBox::question(
this, tr("Revert?"),
tr("This will revert any "
"changes you did to the version up to this point. Is that "
"OK?"),
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes)
{
m_inst->revertCustomVersion();
m_inst->customizeVersion();
{
m_version = m_inst->getFullVersion();
main_model->setSourceModel(m_version.get());
updateVersionControls();
}
}
else
return;
}
else
{
m_inst->customizeVersion();
m_version = m_inst->getFullVersion();
main_model->setSourceModel(m_version.get());
updateVersionControls();
}
ForgeVersionPtr forgeVersion = ForgeVersionPtr forgeVersion =
std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion()); std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion());
if (!forgeVersion) if (!forgeVersion)
@ -200,9 +181,9 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
// install // install
QString forgePath = entry->getFullPath(); QString forgePath = entry->getFullPath();
ForgeInstaller forge(forgePath, forgeVersion->universal_url); ForgeInstaller forge(forgePath, forgeVersion->universal_url);
if (!forge.apply(m_version)) if (!forge.add(m_inst))
{ {
// failure notice QLOG_ERROR() << "Failure installing forge";
} }
} }
else else
@ -215,18 +196,27 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
// install // install
QString forgePath = entry->getFullPath(); QString forgePath = entry->getFullPath();
ForgeInstaller forge(forgePath, forgeVersion->universal_url); ForgeInstaller forge(forgePath, forgeVersion->universal_url);
if (!forge.apply(m_version)) if (!forge.add(m_inst))
{ {
// failure notice QLOG_ERROR() << "Failure installing forge";
} }
} }
} }
m_inst->reloadVersion(this);
} }
void OneSixModEditDialog::on_liteloaderBtn_clicked() void OneSixModEditDialog::on_liteloaderBtn_clicked()
{ {
LiteLoaderInstaller liteloader(m_inst->intendedVersionId()); if (QDir(m_inst->instanceRoot()).exists("custom.json"))
if (!liteloader.canApply()) {
if (QMessageBox::question(this, tr("Revert?"), tr("This action will remove your custom.json. Continue?")) != QMessageBox::Yes)
{
return;
}
QDir(m_inst->instanceRoot()).remove("custom.json");
}
LiteLoaderInstaller liteloader;
if (!liteloader.canApply(m_inst))
{ {
QMessageBox::critical( QMessageBox::critical(
this, tr("LiteLoader"), this, tr("LiteLoader"),
@ -234,19 +224,16 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked()
"into this version of Minecraft")); "into this version of Minecraft"));
return; return;
} }
if (!m_inst->versionIsCustom()) if (!liteloader.add(m_inst))
{
m_inst->customizeVersion();
m_version = m_inst->getFullVersion();
main_model->setSourceModel(m_version.get());
updateVersionControls();
}
if (!liteloader.apply(m_version))
{ {
QMessageBox::critical(this, tr("LiteLoader"), QMessageBox::critical(this, tr("LiteLoader"),
tr("For reasons unknown, the LiteLoader installation failed. " tr("For reasons unknown, the LiteLoader installation failed. "
"Check your MultiMC log files for details.")); "Check your MultiMC log files for details."));
} }
else
{
m_inst->reloadVersion(this);
}
} }
bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent) bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent)
@ -365,3 +352,15 @@ void OneSixModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previou
Mod &m = m_mods->operator[](row); Mod &m = m_mods->operator[](row);
ui->frame->updateWithMod(m); ui->frame->updateWithMod(m);
} }
void OneSixModEditDialog::versionCurrent(const QModelIndex &current, const QModelIndex &previous)
{
if (!current.isValid())
{
ui->removeLibraryBtn->setDisabled(true);
}
else
{
ui->removeLibraryBtn->setEnabled(m_version->canRemove(current.row()));
}
}

View File

@ -45,9 +45,9 @@ slots:
void on_buttonBox_rejected(); void on_buttonBox_rejected();
void on_forgeBtn_clicked(); void on_forgeBtn_clicked();
void on_liteloaderBtn_clicked(); void on_liteloaderBtn_clicked();
void on_customizeBtn_clicked(); void on_userEditorBtn_clicked();
void on_revertBtn_clicked(); void on_reloadLibrariesBtn_clicked();
void on_customEditorBtn_clicked(); void on_removeLibraryBtn_clicked();
void updateVersionControls(); void updateVersionControls();
void disableVersionControls(); void disableVersionControls();
@ -66,4 +66,5 @@ private:
public public
slots: slots:
void loaderCurrent(QModelIndex current, QModelIndex previous); void loaderCurrent(QModelIndex current, QModelIndex previous);
void versionCurrent(const QModelIndex &current, const QModelIndex &previous);
}; };

View File

@ -26,7 +26,7 @@
</size> </size>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="libTab"> <widget class="QWidget" name="libTab">
<attribute name="title"> <attribute name="title">
@ -43,6 +43,9 @@
<property name="horizontalScrollBarPolicy"> <property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum> <enum>Qt::ScrollBarAlwaysOff</enum>
</property> </property>
<attribute name="headerVisible">
<bool>true</bool>
</attribute>
</widget> </widget>
</item> </item>
<item> <item>
@ -84,62 +87,24 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="customizeBtn">
<property name="toolTip">
<string>Create an customized copy of the base version</string>
</property>
<property name="text">
<string>Customize</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="revertBtn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Revert to original base version</string>
</property>
<property name="text">
<string>Revert</string>
</property>
</widget>
</item>
<item> <item>
<widget class="Line" name="line"> <widget class="Line" name="line">
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="addLibraryBtn"> <widget class="QPushButton" name="reloadLibrariesBtn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Add new libraries</string>
</property>
<property name="text"> <property name="text">
<string>&amp;Add</string> <string>Reload</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="removeLibraryBtn"> <widget class="QPushButton" name="removeLibraryBtn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Remove selected libraries</string>
</property>
<property name="text"> <property name="text">
<string>&amp;Remove</string> <string>Remove</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -151,9 +116,9 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="customEditorBtn"> <widget class="QPushButton" name="userEditorBtn">
<property name="text"> <property name="text">
<string>Open custom.json</string> <string>Open user.json</string>
</property> </property>
</widget> </widget>
</item> </item>

66
logic/BaseInstaller.cpp Normal file
View File

@ -0,0 +1,66 @@
/* 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
*
* 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.
*/
#include "BaseInstaller.h"
#include <QFile>
#include "OneSixVersion.h"
#include "OneSixLibrary.h"
#include "OneSixInstance.h"
#include "cmdutils.h"
BaseInstaller::BaseInstaller()
{
}
bool BaseInstaller::isApplied(OneSixInstance *on)
{
return QFile::exists(filename(on->instanceRoot()));
}
bool BaseInstaller::add(OneSixInstance *to)
{
if (!patchesDir(to->instanceRoot()).exists())
{
QDir(to->instanceRoot()).mkdir("patches");
}
if (isApplied(to))
{
if (!remove(to))
{
return false;
}
}
return true;
}
bool BaseInstaller::remove(OneSixInstance *from)
{
return QFile::remove(filename(from->instanceRoot()));
}
QString BaseInstaller::filename(const QString &root) const
{
return patchesDir(root).absoluteFilePath(id() + ".json");
}
QDir BaseInstaller::patchesDir(const QString &root) const
{
return QDir(root + "/patches/");
}

39
logic/BaseInstaller.h Normal file
View File

@ -0,0 +1,39 @@
/* 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
*
* 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.
*/
#pragma once
#include <memory>
class OneSixInstance;
class QDir;
class QString;
class BaseInstaller
{
public:
BaseInstaller();
virtual bool canApply(OneSixInstance *instance) const { return true; }
bool isApplied(OneSixInstance *on);
virtual bool add(OneSixInstance *to);
virtual bool remove(OneSixInstance *from);
protected:
virtual QString id() const = 0;
QString filename(const QString &root) const;
QDir patchesDir(const QString &root) const;
};

View File

@ -21,7 +21,15 @@
#include <quazipfile.h> #include <quazipfile.h>
#include <pathutils.h> #include <pathutils.h>
#include <QStringList> #include <QStringList>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include "MultiMC.h" #include "MultiMC.h"
#include "OneSixInstance.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QSaveFile>
#include <QCryptographicHash>
ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
{ {
@ -66,6 +74,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
QJsonObject installObj = installVal.toObject(); QJsonObject installObj = installVal.toObject();
QString libraryName = installObj.value("path").toString(); QString libraryName = installObj.value("path").toString();
internalPath = installObj.value("filePath").toString(); internalPath = installObj.value("filePath").toString();
m_forgeVersionString = installObj.value("version").toString().remove("Forge").trimmed();
// where do we put the library? decode the mojang path // where do we put the library? decode the mojang path
OneSixLibrary lib(libraryName); OneSixLibrary lib(libraryName);
@ -103,13 +112,22 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
realVersionId = m_forge_version->id = installObj.value("minecraft").toString(); realVersionId = m_forge_version->id = installObj.value("minecraft").toString();
} }
bool ForgeInstaller::apply(std::shared_ptr<OneSixVersion> to) bool ForgeInstaller::add(OneSixInstance *to)
{ {
if (!BaseInstaller::add(to))
{
return false;
}
QJsonObject obj;
obj.insert("order", 5);
if (!m_forge_version) if (!m_forge_version)
return false; return false;
to->externalUpdateStart();
int sliding_insert_window = 0; int sliding_insert_window = 0;
{ {
QJsonArray librariesPlus;
// for each library in the version we are adding (except for the blacklisted) // for each library in the version we are adding (except for the blacklisted)
QSet<QString> blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"}; QSet<QString> blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"};
for (auto lib : m_forge_version->libraries) for (auto lib : m_forge_version->libraries)
@ -128,28 +146,79 @@ bool ForgeInstaller::apply(std::shared_ptr<OneSixVersion> to)
if (blacklist.contains(libName)) if (blacklist.contains(libName))
continue; continue;
// find an entry that matches this one QJsonObject libObj = lib->toJson();
bool found = false; bool found = false;
for (auto tolib : to->libraries) bool equals = false;
// find an entry that matches this one
for (auto tolib : to->getNonCustomVersion()->libraries)
{ {
if (tolib->name() != libName) if (tolib->name() != libName)
continue; continue;
found = true; found = true;
if (tolib->toJson() == libObj)
{
equals = true;
}
// replace lib // replace lib
tolib = lib; libObj.insert("insert", QString("replace"));
break; break;
} }
if (equals)
{
continue;
}
if (!found) if (!found)
{ {
// add lib // add lib
to->libraries.insert(sliding_insert_window, lib); libObj.insert("insert", QString("prepend-if-not-exists"));
sliding_insert_window++; sliding_insert_window++;
} }
librariesPlus.prepend(libObj);
}
obj.insert("+libraries", librariesPlus);
obj.insert("mainClass", m_forge_version->mainClass);
QString args = m_forge_version->minecraftArguments;
QStringList tweakers;
{
QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)");
QRegularExpressionMatch match = expression.match(args);
while (match.hasMatch())
{
tweakers.append(match.captured(1));
args.remove(match.capturedStart(), match.capturedLength());
match = expression.match(args);
}
}
if (!args.isEmpty() && args != to->getNonCustomVersion()->minecraftArguments)
{
obj.insert("minecraftArguments", args);
}
if (!tweakers.isEmpty())
{
obj.insert("+tweakers", QJsonArray::fromStringList(tweakers));
}
if (!m_forge_version->processArguments.isEmpty() &&
m_forge_version->processArguments != to->getNonCustomVersion()->processArguments)
{
obj.insert("processArguments", m_forge_version->processArguments);
} }
to->mainClass = m_forge_version->mainClass;
to->minecraftArguments = m_forge_version->minecraftArguments;
to->processArguments = m_forge_version->processArguments;
} }
to->externalUpdateFinish();
return to->toOriginalFile(); obj.insert("name", QString("Forge"));
obj.insert("fileId", id());
obj.insert("version", m_forgeVersionString);
obj.insert("mcVersion", to->intendedVersionId());
QFile file(filename(to->instanceRoot()));
if (!file.open(QFile::WriteOnly))
{
QLOG_ERROR() << "Error opening" << file.fileName()
<< "for reading:" << file.errorString();
return false;
}
file.write(QJsonDocument(obj).toJson());
file.close();
return true;
} }

View File

@ -14,17 +14,22 @@
*/ */
#pragma once #pragma once
#include "BaseInstaller.h"
#include <QString> #include <QString>
#include <memory> #include <memory>
class OneSixVersion; class OneSixVersion;
class ForgeInstaller class ForgeInstaller : public BaseInstaller
{ {
public: public:
ForgeInstaller(QString filename, QString universal_url); ForgeInstaller(QString filename, QString universal_url);
bool apply(std::shared_ptr<OneSixVersion> to); bool add(OneSixInstance *to) override;
QString id() const override { return "net.minecraftforge"; }
private: private:
// the version, read from the installer // the version, read from the installer
@ -32,5 +37,6 @@ private:
QString internalPath; QString internalPath;
QString finalPath; QString finalPath;
QString realVersionId; QString realVersionId;
QString m_forgeVersionString;
QString m_universal_url; QString m_universal_url;
}; };

View File

@ -24,6 +24,7 @@
#include "OneSixInstance.h" #include "OneSixInstance.h"
#include "OneSixFTBInstance.h" #include "OneSixFTBInstance.h"
#include "NostalgiaInstance.h" #include "NostalgiaInstance.h"
#include "OneSixInstance.h"
#include "BaseVersion.h" #include "BaseVersion.h"
#include "MinecraftVersion.h" #include "MinecraftVersion.h"
@ -50,14 +51,14 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst
QString inst_type = m_settings->get("InstanceType").toString(); QString inst_type = m_settings->get("InstanceType").toString();
// FIXME: replace with a map lookup, where instance classes register their types // FIXME: replace with a map lookup, where instance classes register their types
if (inst_type == "Legacy") if (inst_type == "OneSix")
{
inst = new LegacyInstance(instDir, m_settings, this);
}
else if (inst_type == "OneSix")
{ {
inst = new OneSixInstance(instDir, m_settings, this); inst = new OneSixInstance(instDir, m_settings, this);
} }
else if (inst_type == "Legacy")
{
inst = new LegacyInstance(instDir, m_settings, this);
}
else if (inst_type == "Nostalgia") else if (inst_type == "Nostalgia")
{ {
inst = new NostalgiaInstance(instDir, m_settings, this); inst = new NostalgiaInstance(instDir, m_settings, this);
@ -101,6 +102,7 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *&
switch (mcVer->type) switch (mcVer->type)
{ {
case MinecraftVersion::Legacy: case MinecraftVersion::Legacy:
// TODO new instance type
m_settings->set("InstanceType", "Legacy"); m_settings->set("InstanceType", "Legacy");
inst = new LegacyInstance(instDir, m_settings, this); inst = new LegacyInstance(instDir, m_settings, this);
inst->setIntendedVersionId(version->descriptor()); inst->setIntendedVersionId(version->descriptor());

View File

@ -15,12 +15,19 @@
#include "LiteLoaderInstaller.h" #include "LiteLoaderInstaller.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "logger/QsLog.h"
#include "OneSixVersion.h" #include "OneSixVersion.h"
#include "OneSixLibrary.h" #include "OneSixLibrary.h"
#include "OneSixInstance.h"
QMap<QString, QString> LiteLoaderInstaller::m_launcherWrapperVersionMapping; QMap<QString, QString> LiteLoaderInstaller::m_launcherWrapperVersionMapping;
LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion(mcVersion) LiteLoaderInstaller::LiteLoaderInstaller()
: BaseInstaller()
{ {
if (m_launcherWrapperVersionMapping.isEmpty()) if (m_launcherWrapperVersionMapping.isEmpty())
{ {
@ -31,72 +38,59 @@ LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion
} }
} }
bool LiteLoaderInstaller::canApply() const bool LiteLoaderInstaller::canApply(OneSixInstance *instance) const
{ {
return m_launcherWrapperVersionMapping.contains(m_mcVersion); return m_launcherWrapperVersionMapping.contains(instance->intendedVersionId());
} }
bool LiteLoaderInstaller::apply(std::shared_ptr<OneSixVersion> to) bool LiteLoaderInstaller::add(OneSixInstance *to)
{ {
to->externalUpdateStart(); if (!BaseInstaller::add(to))
applyLaunchwrapper(to);
applyLiteLoader(to);
to->mainClass = "net.minecraft.launchwrapper.Launch";
if (!to->minecraftArguments.contains(
" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"))
{ {
to->minecraftArguments.append( return false;
" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker");
} }
to->externalUpdateFinish(); QJsonObject obj;
return to->toOriginalFile();
}
void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr<OneSixVersion> to) obj.insert("mainClass", QString("net.minecraft.launchwrapper.Launch"));
{ obj.insert("+tweakers", QJsonArray::fromStringList(QStringList() << "com.mumfrey.liteloader.launch.LiteLoaderTweaker"));
const QString intendedVersion = m_launcherWrapperVersionMapping[m_mcVersion]; obj.insert("order", 10);
QMutableListIterator<std::shared_ptr<OneSixLibrary>> it(to->libraries); QJsonArray libraries;
while (it.hasNext())
// launchwrapper
{ {
it.next(); OneSixLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]);
if (it.value()->rawName().startsWith("net.minecraft:launchwrapper:")) launchwrapperLib.finalize();
{ QJsonObject lwLibObj = launchwrapperLib.toJson();
if (it.value()->version() >= intendedVersion) lwLibObj.insert("insert", QString("prepend-if-not-exists"));
{ libraries.append(lwLibObj);
return;
}
else
{
it.remove();
}
}
} }
std::shared_ptr<OneSixLibrary> lib(new OneSixLibrary( // liteloader
"net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[m_mcVersion]));
lib->finalize();
to->libraries.prepend(lib);
}
void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr<OneSixVersion> to)
{
QMutableListIterator<std::shared_ptr<OneSixLibrary>> it(to->libraries);
while (it.hasNext())
{ {
it.next(); OneSixLibrary liteloaderLib("com.mumfrey:liteloader:" + to->intendedVersionId());
if (it.value()->rawName().startsWith("com.mumfrey:liteloader:")) liteloaderLib.setBaseUrl("http://dl.liteloader.com/versions/");
{ liteloaderLib.finalize();
it.remove(); QJsonObject llLibObj = liteloaderLib.toJson();
} llLibObj.insert("insert", QString("prepend"));
libraries.append(llLibObj);
} }
std::shared_ptr<OneSixLibrary> lib( obj.insert("+libraries", libraries);
new OneSixLibrary("com.mumfrey:liteloader:" + m_mcVersion)); obj.insert("name", QString("LiteLoader"));
lib->setBaseUrl("http://dl.liteloader.com/versions/"); obj.insert("fileId", id());
lib->finalize(); obj.insert("version", to->intendedVersionId());
to->libraries.prepend(lib); obj.insert("mcVersion", to->intendedVersionId());
QFile file(filename(to->instanceRoot()));
if (!file.open(QFile::WriteOnly))
{
QLOG_ERROR() << "Error opening" << file.fileName() << "for reading:" << file.errorString();
return false;
}
file.write(QJsonDocument(obj).toJson());
file.close();
return true;
} }

View File

@ -14,26 +14,22 @@
*/ */
#pragma once #pragma once
#include "BaseInstaller.h"
#include <QString> #include <QString>
#include <QMap> #include <QMap>
#include <memory>
class OneSixVersion; class LiteLoaderInstaller : public BaseInstaller
class LiteLoaderInstaller
{ {
public: public:
LiteLoaderInstaller(const QString &mcVersion); LiteLoaderInstaller();
bool canApply() const; bool canApply(OneSixInstance *instance) const override;
bool add(OneSixInstance *to) override;
bool apply(std::shared_ptr<OneSixVersion> to);
private: private:
QString m_mcVersion; virtual QString id() const override { return "com.mumfrey.liteloader"; }
void applyLaunchwrapper(std::shared_ptr<OneSixVersion> to);
void applyLiteLoader(std::shared_ptr<OneSixVersion> to);
static QMap<QString, QString> m_launcherWrapperVersionMapping; static QMap<QString, QString> m_launcherWrapperVersionMapping;
}; };

View File

@ -55,15 +55,13 @@ slots:
setStatus(tr("Installing Forge...")); setStatus(tr("Installing Forge..."));
QString forgePath = entry->getFullPath(); QString forgePath = entry->getFullPath();
ForgeInstaller forge(forgePath, forgeVersion->universal_url); ForgeInstaller forge(forgePath, forgeVersion->universal_url);
if (!instance->reloadFullVersion()) if (!instance->reloadVersion())
{ {
emitFailed(tr("Couldn't load the version config")); emitFailed(tr("Couldn't load the version config"));
return; return;
} }
instance->revertCustomVersion();
instance->customizeVersion();
auto version = instance->getFullVersion(); auto version = instance->getFullVersion();
if (!forge.apply(version)) if (!forge.add(instance))
{ {
emitFailed(tr("Couldn't install Forge")); emitFailed(tr("Couldn't install Forge"));
return; return;

View File

@ -13,32 +13,37 @@
* limitations under the License. * limitations under the License.
*/ */
#include "MultiMC.h"
#include "OneSixInstance.h" #include "OneSixInstance.h"
#include "OneSixInstance_p.h"
#include "OneSixUpdate.h"
#include "MinecraftProcess.h"
#include "OneSixVersion.h"
#include "JavaChecker.h"
#include "logic/icons/IconList.h"
#include <setting.h>
#include <pathutils.h>
#include <cmdutils.h>
#include <JlCompress.h>
#include "gui/dialogs/OneSixModEditDialog.h"
#include "logger/QsLog.h"
#include "logic/assets/AssetsUtils.h"
#include <QIcon> #include <QIcon>
OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj, #include "OneSixInstance_p.h"
QObject *parent) #include "OneSixUpdate.h"
: BaseInstance(new OneSixInstancePrivate(), rootDir, setting_obj, parent) #include "OneSixVersion.h"
#include "pathutils.h"
#include "logger/QsLog.h"
#include "assets/AssetsUtils.h"
#include "MultiMC.h"
#include "icons/IconList.h"
#include "MinecraftProcess.h"
#include "gui/dialogs/OneSixModEditDialog.h"
OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
: BaseInstance(new OneSixInstancePrivate(), rootDir, settings, parent)
{ {
I_D(OneSixInstance); I_D(OneSixInstance);
d->m_settings->registerSetting("IntendedVersion", ""); d->m_settings->registerSetting("IntendedVersion", "");
d->m_settings->registerSetting("ShouldUpdate", false); d->m_settings->registerSetting("ShouldUpdate", false);
reloadFullVersion(); d->version.reset(new OneSixVersion(this, this));
d->nonCustomVersion.reset(new OneSixVersion(this, this));
if (QDir(instanceRoot()).exists("version.json"))
{
reloadVersion();
}
else
{
clearVersion();
}
} }
std::shared_ptr<Task> OneSixInstance::doUpdate() std::shared_ptr<Task> OneSixInstance::doUpdate()
@ -135,6 +140,10 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
I_D(OneSixInstance); I_D(OneSixInstance);
auto version = d->version; auto version = d->version;
QString args_pattern = version->minecraftArguments; QString args_pattern = version->minecraftArguments;
for (auto tweaker : version->tweakers)
{
args_pattern += " --tweakClass " + tweaker;
}
QMap<QString, QString> token_mapping; QMap<QString, QString> token_mapping;
// yggdrasil! // yggdrasil!
@ -267,11 +276,8 @@ bool OneSixInstance::setIntendedVersionId(QString version)
{ {
settings().set("IntendedVersion", version); settings().set("IntendedVersion", version);
setShouldUpdate(true); setShouldUpdate(true);
auto pathCustom = PathCombine(instanceRoot(), "custom.json"); QFile::remove(PathCombine(instanceRoot(), "version.json"));
auto pathOrig = PathCombine(instanceRoot(), "version.json"); clearVersion();
QFile::remove(pathCustom);
QFile::remove(pathOrig);
reloadFullVersion();
return true; return true;
} }
@ -297,9 +303,10 @@ bool OneSixInstance::shouldUpdate() const
bool OneSixInstance::versionIsCustom() bool OneSixInstance::versionIsCustom()
{ {
QString verpath_custom = PathCombine(instanceRoot(), "custom.json"); QDir patches(PathCombine(instanceRoot(), "patches/"));
QFile versionfile(verpath_custom); return (patches.exists() && patches.count() >= 0)
return versionfile.exists(); || QFile::exists(PathCombine(instanceRoot(), "custom.json"))
|| QFile::exists(PathCombine(instanceRoot(), "user.json"));
} }
QString OneSixInstance::currentVersionId() const QString OneSixInstance::currentVersionId() const
@ -307,62 +314,39 @@ QString OneSixInstance::currentVersionId() const
return intendedVersionId(); return intendedVersionId();
} }
bool OneSixInstance::customizeVersion() bool OneSixInstance::reloadVersion(QWidget *widgetParent)
{
if (!versionIsCustom())
{
auto pathCustom = PathCombine(instanceRoot(), "custom.json");
auto pathOrig = PathCombine(instanceRoot(), "version.json");
QFile::copy(pathOrig, pathCustom);
return reloadFullVersion();
}
else
return true;
}
bool OneSixInstance::revertCustomVersion()
{
if (versionIsCustom())
{
auto path = PathCombine(instanceRoot(), "custom.json");
QFile::remove(path);
return reloadFullVersion();
}
else
return true;
}
bool OneSixInstance::reloadFullVersion()
{ {
I_D(OneSixInstance); I_D(OneSixInstance);
QString verpath = PathCombine(instanceRoot(), "version.json"); bool ret = d->version->reload(widgetParent);
if (ret)
{ {
QString verpath_custom = PathCombine(instanceRoot(), "custom.json"); ret = d->nonCustomVersion->reload(widgetParent, true);
QFile versionfile(verpath_custom);
if (versionfile.exists())
verpath = verpath_custom;
}
auto version = OneSixVersion::fromFile(verpath);
if (version)
{
d->version = version;
return true;
}
else
{
d->version.reset();
return false;
} }
emit versionReloaded();
return ret;
} }
std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion() void OneSixInstance::clearVersion()
{ {
I_D(OneSixInstance); I_D(OneSixInstance);
d->version->clear();
d->nonCustomVersion->clear();
emit versionReloaded();
}
std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion() const
{
I_D(const OneSixInstance);
return d->version; return d->version;
} }
std::shared_ptr<OneSixVersion> OneSixInstance::getNonCustomVersion() const
{
I_D(const OneSixInstance);
return d->nonCustomVersion;
}
QString OneSixInstance::defaultBaseJar() const QString OneSixInstance::defaultBaseJar() const
{ {
return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
@ -382,7 +366,7 @@ bool OneSixInstance::menuActionEnabled(QString action_name) const
QString OneSixInstance::getStatusbarDescription() QString OneSixInstance::getStatusbarDescription()
{ {
QString descr = "One Six : " + intendedVersionId(); QString descr = "OneSix : " + intendedVersionId();
if (versionIsCustom()) if (versionIsCustom())
{ {
descr + " (custom)"; descr + " (custom)";

View File

@ -15,21 +15,17 @@
#pragma once #pragma once
#include <QStringList>
#include <QDir>
#include "BaseInstance.h" #include "BaseInstance.h"
class OneSixVersion; #include "OneSixVersion.h"
class Task; #include "ModList.h"
class ModList;
class OneSixInstance : public BaseInstance class OneSixInstance : public BaseInstance
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit OneSixInstance(const QString &rootDir, SettingsObject *settings, explicit OneSixInstance(const QString &rootDir, SettingsObject *settings,
QObject *parent = 0); QObject *parent = 0);
////// Mod Lists ////// ////// Mod Lists //////
std::shared_ptr<ModList> loaderModList(); std::shared_ptr<ModList> loaderModList();
@ -55,14 +51,14 @@ public:
virtual QDialog *createModEditDialog(QWidget *parent) override; virtual QDialog *createModEditDialog(QWidget *parent) override;
/// reload the full version json file. return true on success! /// reload the full version json files. return true on success!
bool reloadFullVersion(); bool reloadVersion(QWidget *widgetParent = 0);
/// clears all version information in preparation for an update
void clearVersion();
/// get the current full version info /// get the current full version info
std::shared_ptr<OneSixVersion> getFullVersion(); std::shared_ptr<OneSixVersion> getFullVersion() const;
/// revert the current custom version back to base /// gets the current version info, excluding custom.json
bool revertCustomVersion(); std::shared_ptr<OneSixVersion> getNonCustomVersion() const;
/// customize the current base version
bool customizeVersion();
/// is the current version original, or custom? /// is the current version original, or custom?
virtual bool versionIsCustom() override; virtual bool versionIsCustom() override;
@ -72,6 +68,9 @@ public:
virtual bool menuActionEnabled(QString action_name) const override; virtual bool menuActionEnabled(QString action_name) const override;
virtual QString getStatusbarDescription() override; virtual QString getStatusbarDescription() override;
signals:
void versionReloaded();
private: private:
QStringList processMinecraftArgs(AuthSessionPtr account); QStringList processMinecraftArgs(AuthSessionPtr account);
QDir reconstructAssets(std::shared_ptr<OneSixVersion> version); QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);

View File

@ -15,16 +15,14 @@
#pragma once #pragma once
#include <memory> #include "BaseInstance_p.h"
#include "OneSixVersion.h"
#include "logic/BaseInstance_p.h" #include "ModList.h"
#include "logic/OneSixVersion.h"
#include "logic/OneSixLibrary.h"
#include "logic/ModList.h"
struct OneSixInstancePrivate : public BaseInstancePrivate struct OneSixInstancePrivate : public BaseInstancePrivate
{ {
std::shared_ptr<OneSixVersion> version; std::shared_ptr<OneSixVersion> version;
std::shared_ptr<OneSixVersion> nonCustomVersion;
std::shared_ptr<ModList> loader_mod_list; std::shared_ptr<ModList> loader_mod_list;
std::shared_ptr<ModList> resource_pack_list; std::shared_ptr<ModList> resource_pack_list;
}; };

View File

@ -76,11 +76,11 @@ void OneSixLibrary::finalize()
} }
} }
void OneSixLibrary::setName(QString name) void OneSixLibrary::setName(const QString &name)
{ {
m_name = name; m_name = name;
} }
void OneSixLibrary::setBaseUrl(QString base_url) void OneSixLibrary::setBaseUrl(const QString &base_url)
{ {
m_base_url = base_url; m_base_url = base_url;
} }
@ -88,50 +88,54 @@ void OneSixLibrary::setIsNative()
{ {
m_is_native = true; m_is_native = true;
} }
void OneSixLibrary::addNative(OpSys os, QString suffix) void OneSixLibrary::addNative(OpSys os, const QString &suffix)
{ {
m_is_native = true; m_is_native = true;
m_native_suffixes[os] = suffix; m_native_suffixes[os] = suffix;
} }
void OneSixLibrary::clearSuffixes()
{
m_native_suffixes.clear();
}
void OneSixLibrary::setRules(QList<std::shared_ptr<Rule>> rules) void OneSixLibrary::setRules(QList<std::shared_ptr<Rule>> rules)
{ {
m_rules = rules; m_rules = rules;
} }
bool OneSixLibrary::isActive() bool OneSixLibrary::isActive() const
{ {
return m_is_active; return m_is_active;
} }
bool OneSixLibrary::isNative() bool OneSixLibrary::isNative() const
{ {
return m_is_native; return m_is_native;
} }
QString OneSixLibrary::downloadUrl() QString OneSixLibrary::downloadUrl() const
{ {
if (m_absolute_url.size()) if (m_absolute_url.size())
return m_absolute_url; return m_absolute_url;
return m_download_url; return m_download_url;
} }
QString OneSixLibrary::storagePath() QString OneSixLibrary::storagePath() const
{ {
return m_storage_path; return m_storage_path;
} }
void OneSixLibrary::setAbsoluteUrl(QString absolute_url) void OneSixLibrary::setAbsoluteUrl(const QString &absolute_url)
{ {
m_absolute_url = absolute_url; m_absolute_url = absolute_url;
} }
QString OneSixLibrary::absoluteUrl() QString OneSixLibrary::absoluteUrl() const
{ {
return m_absolute_url; return m_absolute_url;
} }
void OneSixLibrary::setHint(QString hint) void OneSixLibrary::setHint(const QString &hint)
{ {
m_hint = hint; m_hint = hint;
} }
QString OneSixLibrary::hint() QString OneSixLibrary::hint() const
{ {
return m_hint; return m_hint;
} }
@ -176,7 +180,7 @@ bool OneSixLibrary::extractTo(QString target_dir)
cooked_storage.replace("${arch}", "32"); cooked_storage.replace("${arch}", "32");
QString origin = PathCombine("libraries", cooked_storage); QString origin = PathCombine("libraries", cooked_storage);
QString target_dir_cooked = PathCombine(target_dir, "32"); QString target_dir_cooked = PathCombine(target_dir, "32");
if(!ensureFolderPathExists(target_dir_cooked)) if (!ensureFolderPathExists(target_dir_cooked))
{ {
QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked;
return false; return false;
@ -191,7 +195,7 @@ bool OneSixLibrary::extractTo(QString target_dir)
cooked_storage.replace("${arch}", "64"); cooked_storage.replace("${arch}", "64");
origin = PathCombine("libraries", cooked_storage); origin = PathCombine("libraries", cooked_storage);
target_dir_cooked = PathCombine(target_dir, "64"); target_dir_cooked = PathCombine(target_dir, "64");
if(!ensureFolderPathExists(target_dir_cooked)) if (!ensureFolderPathExists(target_dir_cooked))
{ {
QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked;
return false; return false;
@ -205,7 +209,7 @@ bool OneSixLibrary::extractTo(QString target_dir)
} }
else else
{ {
if(!ensureFolderPathExists(target_dir)) if (!ensureFolderPathExists(target_dir))
{ {
QLOG_ERROR() << "Couldn't create folder " + target_dir; QLOG_ERROR() << "Couldn't create folder " + target_dir;
return false; return false;
@ -230,8 +234,10 @@ QJsonObject OneSixLibrary::toJson()
libRoot.insert("MMC-hint", m_hint); libRoot.insert("MMC-hint", m_hint);
if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
m_base_url != "https://" + URLConstants::LIBRARY_BASE) m_base_url != "https://" + URLConstants::LIBRARY_BASE && !m_base_url.isEmpty())
{
libRoot.insert("url", m_base_url); libRoot.insert("url", m_base_url);
}
if (isNative() && m_native_suffixes.size()) if (isNative() && m_native_suffixes.size())
{ {
QJsonObject nativeList; QJsonObject nativeList;

View File

@ -63,7 +63,7 @@ public:
public: public:
/// Constructor /// Constructor
OneSixLibrary(QString name) OneSixLibrary(const QString &name)
{ {
m_name = name; m_name = name;
} }
@ -84,48 +84,50 @@ public:
void finalize(); void finalize();
/// Set the library composite name /// Set the library composite name
void setName(QString name); void setName(const QString &name);
/// get a decent-looking name /// get a decent-looking name
QString name() QString name() const
{ {
return m_decentname; return m_decentname;
} }
/// get a decent-looking version /// get a decent-looking version
QString version() QString version() const
{ {
return m_decentversion; return m_decentversion;
} }
/// what kind of library is it? (for display) /// what kind of library is it? (for display)
QString type() QString type() const
{ {
return m_decenttype; return m_decenttype;
} }
/// Set the url base for downloads /// Set the url base for downloads
void setBaseUrl(QString base_url); void setBaseUrl(const QString &base_url);
/// Call this to mark the library as 'native' (it's a zip archive with DLLs) /// Call this to mark the library as 'native' (it's a zip archive with DLLs)
void setIsNative(); void setIsNative();
/// Attach a name suffix to the specified OS native /// Attach a name suffix to the specified OS native
void addNative(OpSys os, QString suffix); void addNative(OpSys os, const QString &suffix);
/// Clears all suffixes
void clearSuffixes();
/// Set the load rules /// Set the load rules
void setRules(QList<std::shared_ptr<Rule>> rules); void setRules(QList<std::shared_ptr<Rule>> rules);
/// Returns true if the library should be loaded (or extracted, in case of natives) /// Returns true if the library should be loaded (or extracted, in case of natives)
bool isActive(); bool isActive() const;
/// Returns true if the library is native /// Returns true if the library is native
bool isNative(); bool isNative() const;
/// Get the URL to download the library from /// Get the URL to download the library from
QString downloadUrl(); QString downloadUrl() const;
/// Get the relative path where the library should be saved /// Get the relative path where the library should be saved
QString storagePath(); QString storagePath() const;
/// set an absolute URL for the library. This is an MMC extension. /// set an absolute URL for the library. This is an MMC extension.
void setAbsoluteUrl(QString absolute_url); void setAbsoluteUrl(const QString &absolute_url);
QString absoluteUrl(); QString absoluteUrl() const;
/// set a hint about how to treat the library. This is an MMC extension. /// set a hint about how to treat the library. This is an MMC extension.
void setHint(QString hint); void setHint(const QString &hint);
QString hint(); QString hint() const;
bool extractTo(QString target_dir); bool extractTo(QString target_dir);
bool filesExist(); bool filesExist();

View File

@ -18,7 +18,7 @@
#include "OneSixRule.h" #include "OneSixRule.h"
QList<std::shared_ptr<Rule>> rulesFromJsonV4(QJsonObject &objectWithRules) QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules)
{ {
QList<std::shared_ptr<Rule>> rules; QList<std::shared_ptr<Rule>> rules;
auto rulesVal = objectWithRules.value("rules"); auto rulesVal = objectWithRules.value("rules");

View File

@ -27,7 +27,7 @@ enum RuleAction
}; };
RuleAction RuleAction_fromString(QString); RuleAction RuleAction_fromString(QString);
QList<std::shared_ptr<Rule>> rulesFromJsonV4(QJsonObject &objectWithRules); QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules);
class Rule class Rule
{ {

View File

@ -131,7 +131,7 @@ void OneSixUpdate::versionFileFinished()
{ {
finfo.remove(); finfo.remove();
} }
inst->reloadFullVersion(); inst->reloadVersion();
jarlibStart(); jarlibStart();
} }
@ -229,7 +229,7 @@ void OneSixUpdate::jarlibStart()
setStatus(tr("Getting the library files from Mojang...")); setStatus(tr("Getting the library files from Mojang..."));
QLOG_INFO() << m_inst->name() << ": downloading libraries"; QLOG_INFO() << m_inst->name() << ": downloading libraries";
OneSixInstance *inst = (OneSixInstance *)m_inst; OneSixInstance *inst = (OneSixInstance *)m_inst;
bool successful = inst->reloadFullVersion(); bool successful = inst->reloadVersion();
if (!successful) if (!successful)
{ {
emitFailed("Failed to load the version description file. It might be " emitFailed("Failed to load the version description file. It might be "

View File

@ -13,228 +13,86 @@
* limitations under the License. * limitations under the License.
*/ */
#include "logic/OneSixVersion.h" #include "OneSixVersion.h"
#include "logic/OneSixLibrary.h"
#include "logic/OneSixRule.h"
#include "logger/QsLog.h" #include <QDebug>
#include <QFile>
std::shared_ptr<OneSixVersion> fromJsonV4(QJsonObject root, #include "OneSixVersionBuilder.h"
std::shared_ptr<OneSixVersion> fullVersion)
OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent)
: QAbstractListModel(parent), m_instance(instance)
{ {
fullVersion->id = root.value("id").toString(); clear();
fullVersion->mainClass = root.value("mainClass").toString();
auto procArgsValue = root.value("processArguments");
if (procArgsValue.isString())
{
fullVersion->processArguments = procArgsValue.toString();
QString toCompare = fullVersion->processArguments.toLower();
if (toCompare == "legacy")
{
fullVersion->minecraftArguments = " ${auth_player_name} ${auth_session}";
}
else if (toCompare == "username_session")
{
fullVersion->minecraftArguments =
"--username ${auth_player_name} --session ${auth_session}";
}
else if (toCompare == "username_session_version")
{
fullVersion->minecraftArguments = "--username ${auth_player_name} "
"--session ${auth_session} "
"--version ${profile_name}";
}
}
auto minecraftArgsValue = root.value("minecraftArguments");
if (minecraftArgsValue.isString())
{
fullVersion->minecraftArguments = minecraftArgsValue.toString();
}
auto minecraftTypeValue = root.value("type");
if (minecraftTypeValue.isString())
{
fullVersion->type = minecraftTypeValue.toString();
}
fullVersion->releaseTime = root.value("releaseTime").toString();
fullVersion->time = root.value("time").toString();
auto assetsID = root.value("assets");
if (assetsID.isString())
{
fullVersion->assets = assetsID.toString();
}
else
{
fullVersion->assets = "legacy";
}
QLOG_DEBUG() << "Assets version:" << fullVersion->assets;
// Iterate through the list, if it's a list.
auto librariesValue = root.value("libraries");
if (!librariesValue.isArray())
return fullVersion;
QJsonArray libList = root.value("libraries").toArray();
for (auto libVal : libList)
{
if (!libVal.isObject())
{
continue;
}
QJsonObject libObj = libVal.toObject();
// Library name
auto nameVal = libObj.value("name");
if (!nameVal.isString())
continue;
std::shared_ptr<OneSixLibrary> library(new OneSixLibrary(nameVal.toString()));
auto urlVal = libObj.value("url");
if (urlVal.isString())
{
library->setBaseUrl(urlVal.toString());
}
auto hintVal = libObj.value("MMC-hint");
if (hintVal.isString())
{
library->setHint(hintVal.toString());
}
auto urlAbsVal = libObj.value("MMC-absoluteUrl");
auto urlAbsuVal = libObj.value("MMC-absulute_url"); // compatibility
if (urlAbsVal.isString())
{
library->setAbsoluteUrl(urlAbsVal.toString());
}
else if (urlAbsuVal.isString())
{
library->setAbsoluteUrl(urlAbsuVal.toString());
}
// Extract excludes (if any)
auto extractVal = libObj.value("extract");
if (extractVal.isObject())
{
QStringList excludes;
auto extractObj = extractVal.toObject();
auto excludesVal = extractObj.value("exclude");
if (excludesVal.isArray())
{
auto excludesList = excludesVal.toArray();
for (auto excludeVal : excludesList)
{
if (excludeVal.isString())
excludes.append(excludeVal.toString());
}
library->extract_excludes = excludes;
}
}
auto nativesVal = libObj.value("natives");
if (nativesVal.isObject())
{
library->setIsNative();
auto nativesObj = nativesVal.toObject();
auto iter = nativesObj.begin();
while (iter != nativesObj.end())
{
auto osType = OpSys_fromString(iter.key());
if (osType == Os_Other)
continue;
if (!iter.value().isString())
continue;
library->addNative(osType, iter.value().toString());
iter++;
}
}
library->setRules(rulesFromJsonV4(libObj));
library->finalize();
fullVersion->libraries.append(library);
}
return fullVersion;
} }
std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(QJsonObject root) bool OneSixVersion::reload(QWidget *widgetParent, const bool excludeCustom)
{ {
std::shared_ptr<OneSixVersion> readVersion(new OneSixVersion()); beginResetModel();
int launcher_ver = readVersion->minimumLauncherVersion = bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, excludeCustom);
root.value("minimumLauncherVersion").toDouble(); endResetModel();
return ret;
// ADD MORE HERE :D
if (launcher_ver > 0 && launcher_ver <= 13)
return fromJsonV4(root, readVersion);
else
{
return std::shared_ptr<OneSixVersion>();
}
} }
std::shared_ptr<OneSixVersion> OneSixVersion::fromFile(QString filepath) void OneSixVersion::clear()
{ {
QFile file(filepath); beginResetModel();
if (!file.open(QIODevice::ReadOnly)) id.clear();
{ time.clear();
return std::shared_ptr<OneSixVersion>(); releaseTime.clear();
} type.clear();
assets.clear();
auto data = file.readAll(); processArguments.clear();
QJsonParseError jsonError; minecraftArguments.clear();
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); minimumLauncherVersion = 0xDEADBEAF;
mainClass.clear();
if (jsonError.error != QJsonParseError::NoError) libraries.clear();
{ tweakers.clear();
return std::shared_ptr<OneSixVersion>(); versionFiles.clear();
} endResetModel();
if (!jsonDoc.isObject())
{
return std::shared_ptr<OneSixVersion>();
}
QJsonObject root = jsonDoc.object();
auto version = fromJson(root);
if (version)
version->original_file = filepath;
return version;
} }
bool OneSixVersion::toOriginalFile() void OneSixVersion::dump() const
{ {
if (original_file.isEmpty()) qDebug().nospace() << "OneSixVersion("
return false; << "\n\tid=" << id
QSaveFile file(original_file); << "\n\ttime=" << time
if (!file.open(QIODevice::WriteOnly)) << "\n\treleaseTime=" << releaseTime
<< "\n\ttype=" << type
<< "\n\tassets=" << assets
<< "\n\tprocessArguments=" << processArguments
<< "\n\tminecraftArguments=" << minecraftArguments
<< "\n\tminimumLauncherVersion=" << minimumLauncherVersion
<< "\n\tmainClass=" << mainClass
<< "\n\tlibraries=";
for (auto lib : libraries)
{ {
return false; qDebug().nospace() << "\n\t\t" << lib.get();
} }
// serialize base attributes (those we care about anyway) qDebug().nospace() << "\n)";
QJsonObject root;
root.insert("minecraftArguments", minecraftArguments);
root.insert("mainClass", mainClass);
root.insert("minimumLauncherVersion", minimumLauncherVersion);
root.insert("time", time);
root.insert("id", id);
root.insert("type", type);
// screw processArguments
root.insert("releaseTime", releaseTime);
QJsonArray libarray;
for (const auto &lib : libraries)
{
libarray.append(lib->toJson());
}
if (libarray.count())
root.insert("libraries", libarray);
QJsonDocument doc(root);
file.write(doc.toJson());
return file.commit();
} }
QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNormalLibs() bool OneSixVersion::canRemove(const int index) const
{ {
QList<std::shared_ptr<OneSixLibrary>> output; if (index < versionFiles.size())
{
return versionFiles.at(index).id != "org.multimc.version.json";
}
return false;
}
bool OneSixVersion::remove(const int index)
{
if (canRemove(index))
{
return QFile::remove(versionFiles.at(index).filename);
}
return false;
}
QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNormalLibs()
{
QList<std::shared_ptr<OneSixLibrary> > output;
for (auto lib : libraries) for (auto lib : libraries)
{ {
if (lib->isActive() && !lib->isNative()) if (lib->isActive() && !lib->isNative())
@ -245,9 +103,9 @@ QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNormalLibs()
return output; return output;
} }
QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNativeLibs() QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNativeLibs()
{ {
QList<std::shared_ptr<OneSixLibrary>> output; QList<std::shared_ptr<OneSixLibrary> > output;
for (auto lib : libraries) for (auto lib : libraries)
{ {
if (lib->isActive() && lib->isNative()) if (lib->isActive() && lib->isNative())
@ -258,14 +116,14 @@ QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNativeLibs()
return output; return output;
} }
void OneSixVersion::externalUpdateStart() std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(const QJsonObject &obj)
{ {
beginResetModel(); std::shared_ptr<OneSixVersion> version(new OneSixVersion(0));
} if (OneSixVersionBuilder::read(version.get(), obj))
{
void OneSixVersion::externalUpdateFinish() return version;
{ }
endResetModel(); return 0;
} }
QVariant OneSixVersion::data(const QModelIndex &index, int role) const QVariant OneSixVersion::data(const QModelIndex &index, int role) const
@ -276,7 +134,7 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const
int row = index.row(); int row = index.row();
int column = index.column(); int column = index.column();
if (row < 0 || row >= libraries.size()) if (row < 0 || row >= versionFiles.size())
return QVariant(); return QVariant();
if (role == Qt::DisplayRole) if (role == Qt::DisplayRole)
@ -284,11 +142,9 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const
switch (column) switch (column)
{ {
case 0: case 0:
return libraries[row]->name(); return versionFiles.at(row).name;
case 1: case 1:
return libraries[row]->type(); return versionFiles.at(row).version;
case 2:
return libraries[row]->version();
default: default:
return QVariant(); return QVariant();
} }
@ -296,45 +152,61 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal)
{
if (role == Qt::DisplayRole)
{
switch (section)
{
case 0:
return tr("Name");
case 1:
return tr("Version");
default:
return QVariant();
}
}
}
return QVariant();
}
Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const
{ {
if (!index.isValid()) if (!index.isValid())
return Qt::NoItemFlags; return Qt::NoItemFlags;
int row = index.row(); return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
if (libraries[row]->isActive())
{
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;
}
else
{
return Qt::ItemNeverHasChildren;
}
// return QAbstractListModel::flags(index);
}
QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
return QVariant();
switch (section)
{
case 0:
return QString("Name");
case 1:
return QString("Type");
case 2:
return QString("Version");
default:
return QString();
}
} }
int OneSixVersion::rowCount(const QModelIndex &parent) const int OneSixVersion::rowCount(const QModelIndex &parent) const
{ {
return libraries.size(); return versionFiles.size();
} }
int OneSixVersion::columnCount(const QModelIndex &parent) const int OneSixVersion::columnCount(const QModelIndex &parent) const
{ {
return 3; return 2;
}
QDebug operator<<(QDebug &dbg, const OneSixVersion *version)
{
version->dump();
return dbg.maybeSpace();
}
QDebug operator<<(QDebug &dbg, const OneSixLibrary *library)
{
dbg.nospace() << "OneSixLibrary("
<< "\n\t\t\trawName=" << library->rawName()
<< "\n\t\t\tname=" << library->name()
<< "\n\t\t\tversion=" << library->version()
<< "\n\t\t\ttype=" << library->type()
<< "\n\t\t\tisActive=" << library->isActive()
<< "\n\t\t\tisNative=" << library->isNative()
<< "\n\t\t\tdownloadUrl=" << library->downloadUrl()
<< "\n\t\t\tstoragePath=" << library->storagePath()
<< "\n\t\t\tabsolutePath=" << library->absoluteUrl()
<< "\n\t\t\thint=" << library->hint();
dbg.nospace() << "\n\t\t)";
return dbg.maybeSpace();
} }

View File

@ -14,40 +14,48 @@
*/ */
#pragma once #pragma once
#include <QtCore>
#include <QAbstractListModel>
#include <QString>
#include <QList>
#include <memory> #include <memory>
class OneSixLibrary; #include "OneSixLibrary.h"
class OneSixInstance;
class OneSixVersion : public QAbstractListModel class OneSixVersion : public QAbstractListModel
{ {
// Things required to implement the Qt list model Q_OBJECT
public: public:
explicit OneSixVersion(OneSixInstance *instance, QObject *parent = 0);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
virtual int columnCount(const QModelIndex &parent) const; virtual int columnCount(const QModelIndex &parent) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const;
// serialization/deserialization bool reload(QWidget *widgetParent, const bool excludeCustom = false);
public: void clear();
bool toOriginalFile();
static std::shared_ptr<OneSixVersion> fromJson(QJsonObject root); void dump() const;
static std::shared_ptr<OneSixVersion> fromFile(QString filepath);
bool canRemove(const int index) const;
public
slots:
bool remove(const int index);
public: public:
QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs(); QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs(); QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
// called when something starts/stops messing with the object
// FIXME: these are ugly in every possible way. static std::shared_ptr<OneSixVersion> fromJson(const QJsonObject &obj);
void externalUpdateStart();
void externalUpdateFinish();
// data members // data members
public: public:
/// file this was read from. blank, if none
QString original_file;
/// the ID - determines which jar to use! ACTUALLY IMPORTANT! /// the ID - determines which jar to use! ACTUALLY IMPORTANT!
QString id; QString id;
/// Last updated time - as a string /// Last updated time - as a string
@ -75,6 +83,10 @@ public:
* writing) * writing)
*/ */
int minimumLauncherVersion = 0xDEADBEEF; int minimumLauncherVersion = 0xDEADBEEF;
/**
* A list of all tweaker classes
*/
QStringList tweakers;
/** /**
* The main class to load first * The main class to load first
*/ */
@ -103,4 +115,20 @@ public:
} }
*/ */
// QList<Rule> rules; // QList<Rule> rules;
struct VersionFile
{
QString name;
QString id;
QString version;
QString mcVersion;
QString filename;
};
QList<VersionFile> versionFiles;
private:
OneSixInstance *m_instance;
}; };
QDebug operator<<(QDebug &dbg, const OneSixVersion *version);
QDebug operator<<(QDebug &dbg, const OneSixLibrary *library);

View File

@ -0,0 +1,919 @@
/* 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
*
* 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.
*/
#include "OneSixVersionBuilder.h"
#include <QList>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QFile>
#include <QFileInfo>
#include <QMessageBox>
#include <QObject>
#include <QDir>
#include <QDebug>
#include "OneSixVersion.h"
#include "OneSixInstance.h"
#include "OneSixRule.h"
#include "logger/QsLog.h"
struct VersionFile
{
int order;
QString name;
QString fileId;
QString version;
// TODO use the mcVersion to determine if a version file should be removed on update
QString mcVersion;
QString filename;
// TODO requirements
// QMap<QString, QString> requirements;
QString id;
QString mainClass;
QString overwriteMinecraftArguments;
QString addMinecraftArguments;
QString removeMinecraftArguments;
QString processArguments;
QString type;
QString releaseTime;
QString time;
QString assets;
int minimumLauncherVersion = -1;
bool shouldOverwriteTweakers = false;
QStringList overwriteTweakers;
QStringList addTweakers;
QStringList removeTweakers;
struct Library
{
QString name;
QString url;
QString hint;
QString absoluteUrl;
bool applyExcludes = false;
QStringList excludes;
bool applyNatives = false;
QList<QPair<OpSys, QString>> natives;
bool applyRules = false;
QList<std::shared_ptr<Rule>> rules;
// user for '+' libraries
enum InsertType
{
Apply,
Append,
Prepend,
AppendIfNotExists,
PrependIfNotExists,
Replace
};
InsertType insertType;
QString insertData;
};
bool shouldOverwriteLibs = false;
QList<Library> overwriteLibs;
QList<Library> addLibs;
QList<QString> removeLibs;
static Library fromLibraryJson(const QJsonObject &libObj, const QString &filename,
bool &isError)
{
isError = true;
Library out;
if (!libObj.contains("name"))
{
QLOG_ERROR() << filename << "contains a library that doesn't have a 'name' field";
return out;
}
out.name = libObj.value("name").toString();
auto readString = [libObj, filename](const QString &key, QString &variable)
{
if (libObj.contains(key))
{
QJsonValue val = libObj.value(key);
if (!val.isString())
{
QLOG_WARN() << key << "is not a string in" << filename << "(skipping)";
}
else
{
variable = val.toString();
}
}
};
readString("url", out.url);
readString("MMC-hint", out.hint);
readString("MMC-absulute_url", out.absoluteUrl);
readString("MMC-absoluteUrl", out.absoluteUrl);
if (libObj.contains("extract"))
{
if (!libObj.value("extract").isObject())
{
QLOG_ERROR()
<< filename
<< "contains a library with an 'extract' field that's not an object";
return out;
}
QJsonObject extractObj = libObj.value("extract").toObject();
if (!extractObj.contains("exclude") || !extractObj.value("exclude").isArray())
{
QLOG_ERROR() << filename
<< "contains a library with an invalid 'extract' field";
return out;
}
out.applyExcludes = true;
QJsonArray excludeArray = extractObj.value("exclude").toArray();
for (auto excludeVal : excludeArray)
{
if (!excludeVal.isString())
{
QLOG_WARN() << filename << "contains a library that contains an 'extract' "
"field that contains an invalid 'exclude' entry "
"(skipping)";
}
else
{
out.excludes.append(excludeVal.toString());
}
}
}
if (libObj.contains("natives"))
{
if (!libObj.value("natives").isObject())
{
QLOG_ERROR()
<< filename
<< "contains a library with a 'natives' field that's not an object";
return out;
}
out.applyNatives = true;
QJsonObject nativesObj = libObj.value("natives").toObject();
for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
{
if (!it.value().isString())
{
QLOG_WARN() << filename << "contains an invalid native (skipping)";
}
OpSys opSys = OpSys_fromString(it.key());
if (opSys != Os_Other)
{
out.natives.append(qMakePair(opSys, it.value().toString()));
}
}
}
if (libObj.contains("rules"))
{
out.applyRules = true;
out.rules = rulesFromJsonV4(libObj);
}
isError = false;
return out;
}
static VersionFile fromJson(const QJsonDocument &doc, const QString &filename,
const bool requireOrder, bool &isError)
{
VersionFile out;
isError = true;
if (doc.isEmpty() || doc.isNull())
{
QLOG_ERROR() << filename << "is empty or null";
return out;
}
if (!doc.isObject())
{
QLOG_ERROR() << "The root of" << filename << "is not an object";
return out;
}
QJsonObject root = doc.object();
if (requireOrder)
{
if (root.contains("order"))
{
if (root.value("order").isDouble())
{
out.order = root.value("order").toDouble();
}
else
{
QLOG_ERROR() << "'order' field contains an invalid value in" << filename;
return out;
}
}
else
{
QLOG_ERROR() << filename << "doesn't contain an order field";
}
}
out.name = root.value("name").toString();
out.fileId = root.value("fileId").toString();
out.version = root.value("version").toString();
out.mcVersion = root.value("mcVersion").toString();
out.filename = filename;
auto readString = [root, filename](const QString &key, QString &variable)
{
if (root.contains(key))
{
QJsonValue val = root.value(key);
if (!val.isString())
{
QLOG_WARN() << key << "is not a string in" << filename << "(skipping)";
}
else
{
variable = val.toString();
}
}
};
readString("id", out.id);
readString("mainClass", out.mainClass);
readString("processArguments", out.processArguments);
readString("minecraftArguments", out.overwriteMinecraftArguments);
readString("+minecraftArguments", out.addMinecraftArguments);
readString("-minecraftArguments", out.removeMinecraftArguments);
readString("type", out.type);
readString("releaseTime", out.releaseTime);
readString("time", out.time);
readString("assets", out.assets);
if (root.contains("minimumLauncherVersion"))
{
QJsonValue val = root.value("minimumLauncherVersion");
if (!val.isDouble())
{
QLOG_WARN() << "minimumLauncherVersion is not an int in" << filename
<< "(skipping)";
}
else
{
out.minimumLauncherVersion = val.toDouble();
}
}
if (root.contains("tweakers"))
{
QJsonValue tweakersVal = root.value("tweakers");
if (!tweakersVal.isArray())
{
QLOG_ERROR() << filename << "contains a 'tweakers' field, but it's not an array";
return out;
}
out.shouldOverwriteTweakers = true;
QJsonArray tweakers = root.value("tweakers").toArray();
for (auto tweakerVal : tweakers)
{
if (!tweakerVal.isString())
{
QLOG_ERROR() << filename << "contains a 'tweakers' field entry that's not a string";
return out;
}
out.overwriteTweakers.append(tweakerVal.toString());
}
}
if (root.contains("+tweakers"))
{
QJsonValue tweakersVal = root.value("+tweakers");
if (!tweakersVal.isArray())
{
QLOG_ERROR() << filename << "contains a '+tweakers' field, but it's not an array";
return out;
}
QJsonArray tweakers = root.value("+tweakers").toArray();
for (auto tweakerVal : tweakers)
{
if (!tweakerVal.isString())
{
QLOG_ERROR() << filename << "contains a '+tweakers' field entry that's not a string";
return out;
}
out.addTweakers.append(tweakerVal.toString());
}
}
if (root.contains("-tweakers"))
{
QJsonValue tweakersVal = root.value("-tweakers");
if (!tweakersVal.isArray())
{
QLOG_ERROR() << filename << "contains a '-tweakers' field, but it's not an array";
return out;
}
out.shouldOverwriteTweakers = true;
QJsonArray tweakers = root.value("-tweakers").toArray();
for (auto tweakerVal : tweakers)
{
if (!tweakerVal.isString())
{
QLOG_ERROR() << filename << "contains a '-tweakers' field entry that's not a string";
return out;
}
out.removeTweakers.append(tweakerVal.toString());
}
}
if (root.contains("libraries"))
{
out.shouldOverwriteLibs = true;
QJsonValue librariesVal = root.value("libraries");
if (!librariesVal.isArray())
{
QLOG_ERROR() << filename
<< "contains a 'libraries' field, but its not an array";
return out;
}
QJsonArray librariesArray = librariesVal.toArray();
for (auto libVal : librariesArray)
{
if (!libVal.isObject())
{
QLOG_ERROR() << filename << "contains a library that's not an object";
return out;
}
QJsonObject libObj = libVal.toObject();
bool error;
Library lib = fromLibraryJson(libObj, filename, error);
if (error)
{
QLOG_ERROR() << "Error while reading a library entry in" << filename;
return out;
}
out.overwriteLibs.append(lib);
}
}
if (root.contains("+libraries"))
{
QJsonValue librariesVal = root.value("+libraries");
if (!librariesVal.isArray())
{
QLOG_ERROR() << filename
<< "contains a '+libraries' field, but its not an array";
return out;
}
QJsonArray librariesArray = librariesVal.toArray();
for (auto libVal : librariesArray)
{
if (!libVal.isObject())
{
QLOG_ERROR() << filename << "contains a library that's not an object";
return out;
}
QJsonObject libObj = libVal.toObject();
bool error;
Library lib = fromLibraryJson(libObj, filename, error);
if (error)
{
QLOG_ERROR() << "Error while reading a library entry in" << filename;
return out;
}
if (!libObj.contains("insert"))
{
QLOG_ERROR() << "Missing 'insert' field in '+libraries' field in"
<< filename;
return out;
}
QJsonValue insertVal = libObj.value("insert");
QString insertString;
{
if (insertVal.isString())
{
insertString = insertVal.toString();
}
else if (insertVal.isObject())
{
QJsonObject insertObj = insertVal.toObject();
if (insertObj.isEmpty())
{
QLOG_ERROR() << "One library has an empty insert object in"
<< filename;
return out;
}
insertString = insertObj.keys().first();
lib.insertData = insertObj.value(insertString).toString();
}
}
if (insertString == "apply")
{
lib.insertType = Library::Apply;
}
else if (insertString == "append")
{
lib.insertType = Library::Append;
}
else if (insertString == "prepend")
{
lib.insertType = Library::Prepend;
}
else if (insertString == "prepend-if-not-exists")
{
lib.insertType = Library::PrependIfNotExists;
}
else if (insertString == "append-if-not-exists")
{
lib.insertType = Library::PrependIfNotExists;
}
else if (insertString == "replace")
{
lib.insertType = Library::Replace;
}
else
{
QLOG_ERROR() << "A '+' library in" << filename
<< "contains an invalid insert type";
return out;
}
out.addLibs.append(lib);
}
}
if (root.contains("-libraries"))
{
QJsonValue librariesVal = root.value("-libraries");
if (!librariesVal.isArray())
{
QLOG_ERROR() << filename
<< "contains a '-libraries' field, but its not an array";
return out;
}
QJsonArray librariesArray = librariesVal.toArray();
for (auto libVal : librariesArray)
{
if (!libVal.isObject())
{
QLOG_ERROR() << filename << "contains a library that's not an object";
return out;
}
QJsonObject libObj = libVal.toObject();
if (!libObj.contains("name"))
{
QLOG_ERROR() << filename << "contains a library without a name";
return out;
}
if (!libObj.value("name").isString())
{
QLOG_ERROR() << filename
<< "contains a library without a valid 'name' field";
return out;
}
out.removeLibs.append(libObj.value("name").toString());
}
}
isError = false;
return out;
}
static std::shared_ptr<OneSixLibrary> createLibrary(const Library &lib)
{
std::shared_ptr<OneSixLibrary> out(new OneSixLibrary(lib.name));
out->setBaseUrl(lib.url);
out->setHint(lib.hint);
out->setAbsoluteUrl(lib.absoluteUrl);
out->extract_excludes = lib.excludes;
for (auto native : lib.natives)
{
out->addNative(native.first, native.second);
}
out->setRules(lib.rules);
out->finalize();
return out;
}
int findLibrary(QList<std::shared_ptr<OneSixLibrary>> haystack, const QString &needle)
{
for (int i = 0; i < haystack.size(); ++i)
{
if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix)
.indexIn(haystack.at(i)->rawName()) != -1)
{
return i;
}
}
return -1;
}
void applyTo(OneSixVersion *version, bool &isError)
{
isError = true;
if (!id.isNull())
{
version->id = id;
}
if (!mainClass.isNull())
{
version->mainClass = mainClass;
}
if (!processArguments.isNull())
{
version->processArguments = processArguments;
}
if (!type.isNull())
{
version->type = type;
}
if (!releaseTime.isNull())
{
version->releaseTime = releaseTime;
}
if (!time.isNull())
{
version->time = time;
}
if (!assets.isNull())
{
version->assets = assets;
}
if (minimumLauncherVersion >= 0)
{
version->minimumLauncherVersion = minimumLauncherVersion;
}
if (!overwriteMinecraftArguments.isNull())
{
version->minecraftArguments = overwriteMinecraftArguments;
}
if (!addMinecraftArguments.isNull())
{
version->minecraftArguments += addMinecraftArguments;
}
if (!removeMinecraftArguments.isNull())
{
version->minecraftArguments.remove(removeMinecraftArguments);
}
if (shouldOverwriteTweakers)
{
version->tweakers = overwriteTweakers;
}
for (auto tweaker : addTweakers)
{
version->tweakers += tweaker;
}
for (auto tweaker : removeTweakers)
{
version->tweakers.removeAll(tweaker);
}
if (shouldOverwriteLibs)
{
version->libraries.clear();
for (auto lib : overwriteLibs)
{
version->libraries.append(createLibrary(lib));
}
}
for (auto lib : addLibs)
{
switch (lib.insertType)
{
case Library::Apply:
{
int index = findLibrary(version->libraries, lib.name);
if (index >= 0)
{
auto library = version->libraries[index];
if (!lib.url.isNull())
{
library->setBaseUrl(lib.url);
}
if (!lib.hint.isNull())
{
library->setHint(lib.hint);
}
if (!lib.absoluteUrl.isNull())
{
library->setAbsoluteUrl(lib.absoluteUrl);
}
if (lib.applyExcludes)
{
library->extract_excludes = lib.excludes;
}
if (lib.applyNatives)
{
library->clearSuffixes();
for (auto native : lib.natives)
{
library->addNative(native.first, native.second);
}
}
if (lib.applyRules)
{
library->setRules(lib.rules);
}
library->finalize();
}
else
{
QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)";
}
break;
}
case Library::Append:
version->libraries.append(createLibrary(lib));
break;
case Library::Prepend:
version->libraries.prepend(createLibrary(lib));
break;
case Library::AppendIfNotExists:
{
int index = findLibrary(version->libraries, lib.name);
if (index < 0)
{
version->libraries.append(createLibrary(lib));
}
break;
}
case Library::PrependIfNotExists:
{
int index = findLibrary(version->libraries, lib.name);
if (index < 0)
{
version->libraries.prepend(createLibrary(lib));
}
break;
}
case Library::Replace:
{
int index = findLibrary(version->libraries, lib.insertData);
if (index >= 0)
{
version->libraries.replace(index, createLibrary(lib));
}
else
{
QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)";
}
break;
}
}
}
for (auto lib : removeLibs)
{
int index = findLibrary(version->libraries, lib);
if (index >= 0)
{
version->libraries.removeAt(index);
}
else
{
QLOG_WARN() << "Couldn't find" << lib << "(skipping)";
}
}
OneSixVersion::VersionFile versionFile;
versionFile.name = name;
versionFile.id = fileId;
versionFile.version = this->version;
versionFile.mcVersion = mcVersion;
versionFile.filename = filename;
version->versionFiles.append(versionFile);
isError = false;
}
};
OneSixVersionBuilder::OneSixVersionBuilder()
{
}
bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance,
QWidget *widgetParent, const bool excludeCustom)
{
OneSixVersionBuilder builder;
builder.m_version = version;
builder.m_instance = instance;
builder.m_widgetParent = widgetParent;
return builder.build(excludeCustom);
}
bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj)
{
OneSixVersionBuilder builder;
builder.m_version = version;
builder.m_instance = 0;
builder.m_widgetParent = 0;
return builder.read(obj);
}
bool OneSixVersionBuilder::build(const bool excludeCustom)
{
m_version->clear();
QDir root(m_instance->instanceRoot());
QDir patches(root.absoluteFilePath("patches/"));
if (QFile::exists(root.absoluteFilePath("custom.json")))
{
QLOG_INFO() << "Reading custom.json";
VersionFile file;
if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file))
{
return false;
}
bool isError = false;
file.applyTo(m_version, isError);
if (isError)
{
QMessageBox::critical(
m_widgetParent, QObject::tr("Error"),
QObject::tr(
"Error while applying %1. Please check MultiMC-0.log for more info.")
.arg(root.absoluteFilePath("custom.json")));
return false;
}
}
else
{
// version.json -> patches/*.json -> instance.json
// version.json
{
QLOG_INFO() << "Reading version.json";
VersionFile file;
if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file))
{
return false;
}
file.name = "version.json";
file.fileId = "org.multimc.version.json";
file.version = m_instance->intendedVersionId();
file.mcVersion = m_instance->intendedVersionId();
bool isError = false;
file.applyTo(m_version, isError);
if (isError)
{
QMessageBox::critical(
m_widgetParent, QObject::tr("Error"),
QObject::tr(
"Error while applying %1. Please check MultiMC-0.log for more info.")
.arg(root.absoluteFilePath("version.json")));
return false;
}
}
// patches/
{
// load all, put into map for ordering, apply in the right order
QMap<int, QPair<QString, VersionFile>> files;
for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
{
QLOG_INFO() << "Reading" << info.fileName();
VersionFile file;
if (!read(info, true, &file))
{
return false;
}
files.insert(file.order, qMakePair(info.fileName(), file));
}
for (auto order : files.keys())
{
QLOG_DEBUG() << "Applying file with order" << order;
auto filePair = files[order];
bool isError = false;
filePair.second.applyTo(m_version, isError);
if (isError)
{
QMessageBox::critical(
m_widgetParent, QObject::tr("Error"),
QObject::tr(
"Error while applying %1. Please check MultiMC-0.log for more info.")
.arg(filePair.first));
return false;
}
}
}
// user.json
if (!excludeCustom)
{
if (QFile::exists(root.absoluteFilePath("user.json")))
{
QLOG_INFO() << "Reading user.json";
VersionFile file;
if (!read(QFileInfo(root.absoluteFilePath("user.json")), false, &file))
{
return false;
}
file.name = "user.json";
file.fileId = "org.multimc.user.json";
file.version = QString();
file.mcVersion = QString();
bool isError = false;
file.applyTo(m_version, isError);
if (isError)
{
QMessageBox::critical(
m_widgetParent, QObject::tr("Error"),
QObject::tr(
"Error while applying %1. Please check MultiMC-0.log for more info.")
.arg(root.absoluteFilePath("user.json")));
return false;
}
}
}
}
// some final touches
{
if (m_version->assets.isEmpty())
{
m_version->assets = "legacy";
}
if (m_version->minecraftArguments.isEmpty())
{
QString toCompare = m_version->processArguments.toLower();
if (toCompare == "legacy")
{
m_version->minecraftArguments = " ${auth_player_name} ${auth_session}";
}
else if (toCompare == "username_session")
{
m_version->minecraftArguments =
"--username ${auth_player_name} --session ${auth_session}";
}
else if (toCompare == "username_session_version")
{
m_version->minecraftArguments = "--username ${auth_player_name} "
"--session ${auth_session} "
"--version ${profile_name}";
}
}
}
return true;
}
bool OneSixVersionBuilder::read(const QJsonObject &obj)
{
m_version->clear();
bool isError = false;
VersionFile file = VersionFile::fromJson(QJsonDocument(obj), QString(), false, isError);
if (isError)
{
QMessageBox::critical(
m_widgetParent, QObject::tr("Error"),
QObject::tr("Error while reading. Please check MultiMC-0.log for more info."));
return false;
}
file.applyTo(m_version, isError);
if (isError)
{
QMessageBox::critical(
m_widgetParent, QObject::tr("Error"),
QObject::tr("Error while applying. Please check MultiMC-0.log for more info."));
return false;
}
return true;
}
bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, const bool requireOrder,
VersionFile *out)
{
QFile file(fileInfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly))
{
QMessageBox::critical(
m_widgetParent, QObject::tr("Error"),
QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString()));
return false;
}
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
if (error.error != QJsonParseError::NoError)
{
QMessageBox::critical(m_widgetParent, QObject::tr("Error"),
QObject::tr("Unable to parse %1: %2 at %3")
.arg(file.fileName(), error.errorString())
.arg(error.offset));
return false;
}
bool isError = false;
*out = VersionFile::fromJson(doc, file.fileName(), requireOrder, isError);
if (isError)
{
QMessageBox::critical(
m_widgetParent, QObject::tr("Error"),
QObject::tr("Error while reading %1. Please check MultiMC-0.log for more info.")
.arg(file.fileName()));
;
}
return true;
}

View File

@ -0,0 +1,43 @@
/* 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
*
* 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.
*/
#pragma once
#include <QString>
class OneSixVersion;
class OneSixInstance;
class QWidget;
class QJsonObject;
class QFileInfo;
class VersionFile;
class OneSixVersionBuilder
{
OneSixVersionBuilder();
public:
static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool excludeCustom);
static bool read(OneSixVersion *version, const QJsonObject &obj);
private:
OneSixVersion *m_version;
OneSixInstance *m_instance;
QWidget *m_widgetParent;
bool build(const bool excludeCustom);
bool read(const QJsonObject &obj);
bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out);
};