Merge branch 'develop' into feature_badges
Conflicts: logic/OneSixInstance.cpp
This commit is contained in:
commit
fcc5bc2ce0
2
.gitignore
vendored
2
.gitignore
vendored
@ -13,6 +13,8 @@ MultiMC5.kdev4
|
||||
MultiMC.pro.user
|
||||
CMakeLists.txt.user
|
||||
CMakeLists.txt.user.*
|
||||
/.project
|
||||
/.settings
|
||||
|
||||
# Build dirs
|
||||
build
|
||||
|
@ -257,6 +257,7 @@ SET(MULTIMC_SOURCES
|
||||
MultiMC.h
|
||||
MultiMC.cpp
|
||||
MultiMCVersion.h
|
||||
MMCError.h
|
||||
|
||||
# Logging
|
||||
logger/QsDebugOutput.cpp
|
||||
@ -353,6 +354,10 @@ logic/ModList.cpp
|
||||
logic/InstanceLauncher.h
|
||||
logic/InstanceLauncher.cpp
|
||||
|
||||
# JSON parsing helpers
|
||||
logic/MMCJson.h
|
||||
logic/MMCJson.cpp
|
||||
|
||||
# network stuffs
|
||||
logic/net/NetAction.h
|
||||
logic/net/MD5EtagDownload.h
|
||||
@ -414,31 +419,38 @@ logic/LegacyInstance.cpp
|
||||
logic/LegacyInstance_p.h
|
||||
logic/LegacyUpdate.h
|
||||
logic/LegacyUpdate.cpp
|
||||
|
||||
logic/LegacyForge.h
|
||||
logic/LegacyForge.cpp
|
||||
|
||||
# OneSix instances
|
||||
logic/OneSixUpdate.h
|
||||
logic/OneSixUpdate.cpp
|
||||
logic/OneSixVersion.h
|
||||
logic/OneSixVersion.cpp
|
||||
logic/OneSixInstance.h
|
||||
logic/OneSixInstance.cpp
|
||||
logic/OneSixInstance_p.h
|
||||
|
||||
# OneSix version json infrastructure
|
||||
logic/OneSixVersionBuilder.h
|
||||
logic/OneSixVersionBuilder.cpp
|
||||
logic/VersionFile.h
|
||||
logic/VersionFile.cpp
|
||||
logic/VersionFinal.h
|
||||
logic/VersionFinal.cpp
|
||||
logic/OneSixLibrary.h
|
||||
logic/OneSixLibrary.cpp
|
||||
logic/OneSixRule.h
|
||||
logic/OneSixRule.cpp
|
||||
logic/OpSys.h
|
||||
logic/OpSys.cpp
|
||||
|
||||
# Mod installers
|
||||
logic/BaseInstaller.h
|
||||
logic/BaseInstaller.cpp
|
||||
logic/ForgeInstaller.h
|
||||
logic/ForgeInstaller.cpp
|
||||
logic/LiteLoaderInstaller.h
|
||||
logic/LiteLoaderInstaller.cpp
|
||||
logic/OneSixInstance.h
|
||||
logic/OneSixInstance.cpp
|
||||
logic/OneSixInstance_p.h
|
||||
logic/OneSixVersionBuilder.h
|
||||
logic/OneSixVersionBuilder.cpp
|
||||
|
||||
# Nostalgia
|
||||
logic/NostalgiaInstance.h
|
||||
@ -758,24 +770,8 @@ INCLUDE(CPack)
|
||||
|
||||
include_directories(${PROJECT_BINARY_DIR}/include)
|
||||
|
||||
### translation stuff
|
||||
|
||||
file (GLOB TRANSLATIONS_FILES translations/*.ts)
|
||||
|
||||
option (UPDATE_TRANSLATIONS "Update source translation translations/*.ts files (WARNING: make clean will delete the source .ts files! Danger!)")
|
||||
IF(UPDATE_TRANSLATIONS)
|
||||
qt5_create_translation(QM_FILES ${FILES_TO_TRANSLATE} ${TRANSLATIONS_FILES})
|
||||
ELSE()
|
||||
qt5_add_translation(QM_FILES ${TRANSLATIONS_FILES})
|
||||
ENDIF()
|
||||
|
||||
add_custom_target (translations DEPENDS ${QM_FILES})
|
||||
IF(APPLE AND UNIX) ## OSX
|
||||
install(FILES ${QM_FILES} DESTINATION MultiMC.app/Contents/MacOS/translations)
|
||||
ELSE()
|
||||
install(FILES ${QM_FILES} DESTINATION translations)
|
||||
ENDIF()
|
||||
|
||||
# Translations
|
||||
add_subdirectory(translations)
|
||||
|
||||
# Tests
|
||||
add_subdirectory(tests)
|
||||
|
25
MMCError.h
Normal file
25
MMCError.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <exception>
|
||||
#include <QString>
|
||||
#include <logger/QsLog.h>
|
||||
|
||||
class MMCError : public std::exception
|
||||
{
|
||||
public:
|
||||
MMCError(QString cause)
|
||||
{
|
||||
exceptionCause = cause;
|
||||
QLOG_ERROR() << "Exception: " + cause;
|
||||
};
|
||||
virtual ~MMCError() noexcept {}
|
||||
virtual const char *what() const noexcept
|
||||
{
|
||||
return exceptionCause.toLocal8Bit();
|
||||
};
|
||||
virtual QString cause() const
|
||||
{
|
||||
return exceptionCause;
|
||||
}
|
||||
private:
|
||||
QString exceptionCause;
|
||||
};
|
18
MultiMC.cpp
18
MultiMC.cpp
@ -1,4 +1,3 @@
|
||||
|
||||
#include "MultiMC.h"
|
||||
#include <iostream>
|
||||
#include <QDir>
|
||||
@ -43,6 +42,11 @@
|
||||
#include "logger/QsLog.h"
|
||||
#include <logger/QsLogDest.h>
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
static const int APPDATA_BUFFER_SIZE = 1024;
|
||||
#endif
|
||||
|
||||
using namespace Util::Commandline;
|
||||
|
||||
MultiMC::MultiMC(int &argc, char **argv, bool root_override)
|
||||
@ -340,7 +344,16 @@ void MultiMC::initGlobalSettings()
|
||||
#ifdef Q_OS_LINUX
|
||||
QString ftbDefault = QDir::home().absoluteFilePath(".ftblauncher");
|
||||
#elif defined(Q_OS_WIN32)
|
||||
QString ftbDefault = PathCombine(QStandardPaths::writableLocation(QStandardPaths::DataLocation), "/ftblauncher");
|
||||
wchar_t buf[APPDATA_BUFFER_SIZE];
|
||||
QString ftbDefault;
|
||||
if(!GetEnvironmentVariableW(L"APPDATA", buf, APPDATA_BUFFER_SIZE))
|
||||
{
|
||||
QLOG_FATAL() << "Your APPDATA folder is missing! If you are on windows, this means your system is broken. If you aren't on windows, how the **** are you running the windows build????";
|
||||
}
|
||||
else
|
||||
{
|
||||
ftbDefault = PathCombine(QString::fromWCharArray(buf), "ftblauncher");
|
||||
}
|
||||
#elif defined(Q_OS_MAC)
|
||||
QString ftbDefault =
|
||||
PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher");
|
||||
@ -457,6 +470,7 @@ void MultiMC::initHttpMetaCache()
|
||||
m_metacache->addBase("versions", QDir("versions").absolutePath());
|
||||
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
|
||||
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
|
||||
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
|
||||
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
|
||||
m_metacache->addBase("root", QDir(root()).absolutePath());
|
||||
m_metacache->Load();
|
||||
|
@ -28,6 +28,11 @@ void INISettingsObject::setFilePath(const QString &filePath)
|
||||
m_filePath = filePath;
|
||||
}
|
||||
|
||||
bool INISettingsObject::reload()
|
||||
{
|
||||
return m_ini.loadFile(m_filePath) && SettingsObject::reload();
|
||||
}
|
||||
|
||||
void INISettingsObject::changeSetting(const Setting &setting, QVariant value)
|
||||
{
|
||||
if (contains(setting.id()))
|
||||
|
@ -47,6 +47,8 @@ public:
|
||||
*/
|
||||
virtual void setFilePath(const QString &filePath);
|
||||
|
||||
bool reload() override;
|
||||
|
||||
protected
|
||||
slots:
|
||||
virtual void changeSetting(const Setting &setting, QVariant value);
|
||||
|
@ -126,6 +126,15 @@ bool SettingsObject::contains(const QString &id)
|
||||
return m_settings.contains(id);
|
||||
}
|
||||
|
||||
bool SettingsObject::reload()
|
||||
{
|
||||
for (auto setting : m_settings.values())
|
||||
{
|
||||
setting->set(setting->get());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SettingsObject::connectSignals(const Setting &setting)
|
||||
{
|
||||
connect(&setting, SIGNAL(settingChanged(const Setting &, QVariant)),
|
||||
|
@ -113,6 +113,12 @@ public:
|
||||
*/
|
||||
bool contains(const QString &id);
|
||||
|
||||
/*!
|
||||
* \brief Reloads the settings and emit signals for changed settings
|
||||
* \return True if reloading was successful
|
||||
*/
|
||||
virtual bool reload();
|
||||
|
||||
signals:
|
||||
/*!
|
||||
* \brief Signal emitted when one of this SettingsObject object's settings changes.
|
||||
|
@ -34,7 +34,7 @@
|
||||
#include "gui/dialogs/ProgressDialog.h"
|
||||
|
||||
#include "logic/ModList.h"
|
||||
#include "logic/OneSixVersion.h"
|
||||
#include "logic/VersionFinal.h"
|
||||
#include "logic/EnabledItemFilter.h"
|
||||
#include "logic/lists/ForgeVersionList.h"
|
||||
#include "logic/lists/LiteLoaderVersionList.h"
|
||||
@ -42,8 +42,7 @@
|
||||
#include "logic/LiteLoaderInstaller.h"
|
||||
#include "logic/OneSixVersionBuilder.h"
|
||||
|
||||
template<typename A, typename B>
|
||||
QMap<A, B> invert(const QMap<B, A> &in)
|
||||
template <typename A, typename B> QMap<A, B> invert(const QMap<B, A> &in)
|
||||
{
|
||||
QMap<A, B> out;
|
||||
for (auto it = in.begin(); it != in.end(); ++it)
|
||||
@ -96,7 +95,8 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
|
||||
m_resourcepacks->startWatching();
|
||||
}
|
||||
|
||||
connect(m_inst, &OneSixInstance::versionReloaded, this, &OneSixModEditDialog::updateVersionControls);
|
||||
connect(m_inst, &OneSixInstance::versionReloaded, this,
|
||||
&OneSixModEditDialog::updateVersionControls);
|
||||
}
|
||||
|
||||
OneSixModEditDialog::~OneSixModEditDialog()
|
||||
@ -110,7 +110,6 @@ void OneSixModEditDialog::updateVersionControls()
|
||||
{
|
||||
ui->forgeBtn->setEnabled(true);
|
||||
ui->liteloaderBtn->setEnabled(true);
|
||||
ui->mainClassEdit->setText(m_version->mainClass);
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::disableVersionControls()
|
||||
@ -119,120 +118,81 @@ void OneSixModEditDialog::disableVersionControls()
|
||||
ui->liteloaderBtn->setEnabled(false);
|
||||
ui->reloadLibrariesBtn->setEnabled(false);
|
||||
ui->removeLibraryBtn->setEnabled(false);
|
||||
ui->mainClassEdit->setText("");
|
||||
}
|
||||
|
||||
bool OneSixModEditDialog::reloadInstanceVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_inst->reloadVersion();
|
||||
return true;
|
||||
}
|
||||
catch (MMCError &e)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), e.cause());
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
QMessageBox::critical(
|
||||
this, tr("Error"),
|
||||
tr("Failed to load the version description file for reasons unknown."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_reloadLibrariesBtn_clicked()
|
||||
{
|
||||
m_inst->reloadVersion(this);
|
||||
reloadInstanceVersion();
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_removeLibraryBtn_clicked()
|
||||
{
|
||||
if (ui->libraryTreeView->currentIndex().isValid())
|
||||
{
|
||||
// FIXME: use actual model, not reloading.
|
||||
if (!m_version->remove(ui->libraryTreeView->currentIndex().row()))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_inst->reloadVersion(this);
|
||||
reloadInstanceVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_resetLibraryOrderBtn_clicked()
|
||||
{
|
||||
QDir(m_inst->instanceRoot()).remove("order.json");
|
||||
m_inst->reloadVersion(this);
|
||||
// FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_moveLibraryUpBtn_clicked()
|
||||
{
|
||||
|
||||
QMap<QString, int> order = getExistingOrder();
|
||||
if (order.size() < 2 || ui->libraryTreeView->selectionModel()->selectedIndexes().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int ourRow = ui->libraryTreeView->selectionModel()->selectedIndexes().first().row();
|
||||
const QString ourId = m_version->versionFileId(ourRow);
|
||||
const int ourOrder = order[ourId];
|
||||
if (ourId.isNull() || ourId.startsWith("org.multimc."))
|
||||
{
|
||||
return;
|
||||
// FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
|
||||
}
|
||||
|
||||
QMap<int, QString> sortedOrder = invert(order);
|
||||
|
||||
QList<int> sortedOrders = sortedOrder.keys();
|
||||
const int ourIndex = sortedOrders.indexOf(ourOrder);
|
||||
if (ourIndex <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int ourNewOrder = sortedOrders.at(ourIndex - 1);
|
||||
order[ourId] = ourNewOrder;
|
||||
order[sortedOrder[sortedOrders[ourIndex - 1]]] = ourOrder;
|
||||
|
||||
if (!OneSixVersionBuilder::writeOverrideOrders(order, m_inst))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Couldn't save the new order"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_inst->reloadVersion(this);
|
||||
ui->libraryTreeView->selectionModel()->select(m_version->index(ourRow - 1), QItemSelectionModel::SelectCurrent);
|
||||
}
|
||||
}
|
||||
void OneSixModEditDialog::on_moveLibraryDownBtn_clicked()
|
||||
{
|
||||
QMap<QString, int> order = getExistingOrder();
|
||||
if (order.size() < 2 || ui->libraryTreeView->selectionModel()->selectedIndexes().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int ourRow = ui->libraryTreeView->selectionModel()->selectedIndexes().first().row();
|
||||
const QString ourId = m_version->versionFileId(ourRow);
|
||||
const int ourOrder = order[ourId];
|
||||
if (ourId.isNull() || ourId.startsWith("org.multimc."))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMap<int, QString> sortedOrder = invert(order);
|
||||
|
||||
QList<int> sortedOrders = sortedOrder.keys();
|
||||
const int ourIndex = sortedOrders.indexOf(ourOrder);
|
||||
if ((ourIndex + 1) >= sortedOrders.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int ourNewOrder = sortedOrders.at(ourIndex + 1);
|
||||
order[ourId] = ourNewOrder;
|
||||
order[sortedOrder[sortedOrders[ourIndex + 1]]] = ourOrder;
|
||||
|
||||
if (!OneSixVersionBuilder::writeOverrideOrders(order, m_inst))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Couldn't save the new order"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_inst->reloadVersion(this);
|
||||
ui->libraryTreeView->selectionModel()->select(m_version->index(ourRow + 1), QItemSelectionModel::SelectCurrent);
|
||||
}
|
||||
// FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_forgeBtn_clicked()
|
||||
{
|
||||
// FIXME: use actual model, not reloading. Move logic to model.
|
||||
|
||||
// FIXME: model::isCustom();
|
||||
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)
|
||||
if (QMessageBox::question(this, tr("Revert?"),
|
||||
tr("This action will remove your custom.json. Continue?")) !=
|
||||
QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// FIXME: model::revertToBase();
|
||||
QDir(m_inst->instanceRoot()).remove("custom.json");
|
||||
m_inst->reloadVersion(this);
|
||||
reloadInstanceVersion();
|
||||
}
|
||||
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
|
||||
vselect.setFilter(1, m_inst->currentVersionId());
|
||||
@ -277,21 +237,25 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
m_inst->reloadVersion(this);
|
||||
reloadInstanceVersion();
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_liteloaderBtn_clicked()
|
||||
{
|
||||
// FIXME: model...
|
||||
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)
|
||||
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");
|
||||
m_inst->reloadVersion(this);
|
||||
reloadInstanceVersion();
|
||||
}
|
||||
VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"), this);
|
||||
VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"),
|
||||
this);
|
||||
vselect.setFilter(1, m_inst->currentVersionId());
|
||||
vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") +
|
||||
m_inst->currentVersionId());
|
||||
@ -310,7 +274,7 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked()
|
||||
}
|
||||
else
|
||||
{
|
||||
m_inst->reloadVersion(this);
|
||||
reloadInstanceVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -347,35 +311,6 @@ bool OneSixModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent)
|
||||
return QDialog::eventFilter(ui->resPackTreeView, keyEvent);
|
||||
}
|
||||
|
||||
QMap<QString, int> OneSixModEditDialog::getExistingOrder() const
|
||||
{
|
||||
|
||||
QMap<QString, int> order;
|
||||
// default
|
||||
{
|
||||
for (OneSixVersion::VersionFile file : m_version->versionFiles)
|
||||
{
|
||||
if (file.id.startsWith("org.multimc."))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
order.insert(file.id, file.order);
|
||||
}
|
||||
}
|
||||
// overriden
|
||||
{
|
||||
QMap<QString, int> overridenOrder = OneSixVersionBuilder::readOverrideOrders(m_inst);
|
||||
for (auto id : order.keys())
|
||||
{
|
||||
if (overridenOrder.contains(id))
|
||||
{
|
||||
order[id] = overridenOrder[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
bool OneSixModEditDialog::eventFilter(QObject *obj, QEvent *ev)
|
||||
{
|
||||
if (ev->type() != QEvent::KeyPress)
|
||||
@ -461,7 +396,8 @@ void OneSixModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previou
|
||||
ui->frame->updateWithMod(m);
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::versionCurrent(const QModelIndex ¤t, const QModelIndex &previous)
|
||||
void OneSixModEditDialog::versionCurrent(const QModelIndex ¤t,
|
||||
const QModelIndex &previous)
|
||||
{
|
||||
if (!current.isValid())
|
||||
{
|
||||
|
@ -57,17 +57,17 @@ protected:
|
||||
bool eventFilter(QObject *obj, QEvent *ev);
|
||||
bool loaderListFilter(QKeyEvent *ev);
|
||||
bool resourcePackListFilter(QKeyEvent *ev);
|
||||
/// FIXME: this shouldn't be necessary!
|
||||
bool reloadInstanceVersion();
|
||||
|
||||
private:
|
||||
Ui::OneSixModEditDialog *ui;
|
||||
std::shared_ptr<OneSixVersion> m_version;
|
||||
std::shared_ptr<VersionFinal> m_version;
|
||||
std::shared_ptr<ModList> m_mods;
|
||||
std::shared_ptr<ModList> m_resourcepacks;
|
||||
EnabledItemFilter *main_model;
|
||||
OneSixInstance *m_inst;
|
||||
|
||||
QMap<QString, int> getExistingOrder() const;
|
||||
|
||||
public
|
||||
slots:
|
||||
void loaderCurrent(QModelIndex current, QModelIndex previous);
|
||||
|
@ -48,24 +48,6 @@
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Main Class:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="mainClassEdit">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@ -108,13 +90,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="resetLibraryOrderBtn">
|
||||
<property name="text">
|
||||
<string>Reset order</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
@ -124,6 +99,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="moveLibraryUpBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>This isn't implemented yet.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move up</string>
|
||||
</property>
|
||||
@ -131,11 +112,30 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="moveLibraryDownBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>This isn't implemented yet.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move down</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="resetLibraryOrderBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>This isn't implemented yet.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reset order</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_7">
|
||||
<property name="orientation">
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include "OneSixVersion.h"
|
||||
#include "VersionFinal.h"
|
||||
#include "OneSixLibrary.h"
|
||||
#include "OneSixInstance.h"
|
||||
|
||||
|
@ -168,6 +168,11 @@ bool BaseInstance::canLaunch() const
|
||||
return !flags().contains(VersionBrokenFlag);
|
||||
}
|
||||
|
||||
bool BaseInstance::reload()
|
||||
{
|
||||
return settings().reload();
|
||||
}
|
||||
|
||||
QString BaseInstance::baseJar() const
|
||||
{
|
||||
I_D(BaseInstance);
|
||||
|
@ -190,6 +190,8 @@ public:
|
||||
|
||||
bool canLaunch() const;
|
||||
|
||||
virtual bool reload();
|
||||
|
||||
signals:
|
||||
/*!
|
||||
* \brief Signal emitted when properties relevant to the instance view change
|
||||
|
@ -14,7 +14,7 @@
|
||||
*/
|
||||
|
||||
#include "ForgeInstaller.h"
|
||||
#include "OneSixVersion.h"
|
||||
#include "VersionFinal.h"
|
||||
#include "OneSixLibrary.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
#include <quazip.h>
|
||||
@ -33,7 +33,7 @@
|
||||
|
||||
ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
|
||||
{
|
||||
std::shared_ptr<OneSixVersion> newVersion;
|
||||
std::shared_ptr<VersionFinal> newVersion;
|
||||
m_universal_url = universal_url;
|
||||
|
||||
QuaZip zip(filename);
|
||||
@ -66,7 +66,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
|
||||
|
||||
// read the forge version info
|
||||
{
|
||||
newVersion = OneSixVersion::fromJson(versionInfoVal.toObject());
|
||||
newVersion = VersionFinal::fromJson(versionInfoVal.toObject());
|
||||
if (!newVersion)
|
||||
return;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
|
||||
class OneSixVersion;
|
||||
class VersionFinal;
|
||||
|
||||
class ForgeInstaller : public BaseInstaller
|
||||
{
|
||||
@ -33,7 +33,7 @@ public:
|
||||
|
||||
private:
|
||||
// the version, read from the installer
|
||||
std::shared_ptr<OneSixVersion> m_forge_version;
|
||||
std::shared_ptr<VersionFinal> m_forge_version;
|
||||
QString internalPath;
|
||||
QString finalPath;
|
||||
QString realVersionId;
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
#include "logger/QsLog.h"
|
||||
|
||||
#include "OneSixVersion.h"
|
||||
#include "VersionFinal.h"
|
||||
#include "OneSixLibrary.h"
|
||||
#include "OneSixInstance.h"
|
||||
|
||||
@ -69,7 +69,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to)
|
||||
obj.insert("+libraries", libraries);
|
||||
obj.insert("name", QString("LiteLoader"));
|
||||
obj.insert("fileId", id());
|
||||
obj.insert("version", to->intendedVersionId());
|
||||
obj.insert("version", m_version->version);
|
||||
obj.insert("mcVersion", to->intendedVersionId());
|
||||
|
||||
QFile file(filename(to->instanceRoot()));
|
||||
|
61
logic/MMCJson.cpp
Normal file
61
logic/MMCJson.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include "MMCJson.h"
|
||||
#include <QString>
|
||||
#include <math.h>
|
||||
|
||||
bool MMCJson::ensureBoolean(const QJsonValue val, const QString what)
|
||||
{
|
||||
if (!val.isBool())
|
||||
throw JSONValidationError(what + " is not boolean");
|
||||
return val.isBool();
|
||||
}
|
||||
|
||||
QJsonValue MMCJson::ensureExists(QJsonValue val, const QString what)
|
||||
{
|
||||
if(val.isNull())
|
||||
throw JSONValidationError(what + " does not exist");
|
||||
return val;
|
||||
}
|
||||
|
||||
QJsonArray MMCJson::ensureArray(const QJsonValue val, const QString what)
|
||||
{
|
||||
if (!val.isArray())
|
||||
throw JSONValidationError(what + " is not an array");
|
||||
return val.toArray();
|
||||
}
|
||||
|
||||
double MMCJson::ensureDouble(const QJsonValue val, const QString what)
|
||||
{
|
||||
if (!val.isDouble())
|
||||
throw JSONValidationError(what + " is not a number");
|
||||
double ret = val.toDouble();
|
||||
}
|
||||
|
||||
int MMCJson::ensureInteger(const QJsonValue val, const QString what)
|
||||
{
|
||||
double ret = ensureDouble(val, what);
|
||||
if (fmod(ret, 1) != 0)
|
||||
throw JSONValidationError(what + " is not an integer");
|
||||
return ret;
|
||||
}
|
||||
|
||||
QJsonObject MMCJson::ensureObject(const QJsonValue val, const QString what)
|
||||
{
|
||||
if (!val.isObject())
|
||||
throw JSONValidationError(what + " is not an object");
|
||||
return val.toObject();
|
||||
}
|
||||
|
||||
QJsonObject MMCJson::ensureObject(const QJsonDocument val, const QString what)
|
||||
{
|
||||
if (!val.isObject())
|
||||
throw JSONValidationError(what + " is not an object");
|
||||
return val.object();
|
||||
}
|
||||
|
||||
QString MMCJson::ensureString(const QJsonValue val, const QString what)
|
||||
{
|
||||
if (!val.isString())
|
||||
throw JSONValidationError(what + " is not a string");
|
||||
return val.toString();
|
||||
}
|
||||
|
46
logic/MMCJson.h
Normal file
46
logic/MMCJson.h
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Some de-bullshitting for Qt JSON failures.
|
||||
*
|
||||
* Simple exception-throwing
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <QJsonValue>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include "MMCError.h"
|
||||
|
||||
class JSONValidationError : public MMCError
|
||||
{
|
||||
public:
|
||||
JSONValidationError(QString cause) : MMCError(cause) {};
|
||||
virtual ~JSONValidationError() noexcept {}
|
||||
};
|
||||
|
||||
namespace MMCJson
|
||||
{
|
||||
/// make sure the value exists. throw otherwise.
|
||||
QJsonValue ensureExists(QJsonValue val, const QString what = "value");
|
||||
|
||||
/// make sure the value is converted into an object. throw otherwise.
|
||||
QJsonObject ensureObject(const QJsonValue val, const QString what = "value");
|
||||
|
||||
/// make sure the document is converted into an object. throw otherwise.
|
||||
QJsonObject ensureObject(const QJsonDocument val, const QString what = "value");
|
||||
|
||||
/// make sure the value is converted into an array. throw otherwise.
|
||||
QJsonArray ensureArray(const QJsonValue val, QString what = "value");
|
||||
|
||||
/// make sure the value is converted into a string. throw otherwise.
|
||||
QString ensureString(const QJsonValue val, QString what = "value");
|
||||
|
||||
/// make sure the value is converted into a boolean. throw otherwise.
|
||||
bool ensureBoolean(const QJsonValue val, QString what = "value");
|
||||
|
||||
/// make sure the value is converted into an integer. throw otherwise.
|
||||
int ensureInteger(const QJsonValue val, QString what = "value");
|
||||
|
||||
/// make sure the value is converted into a double precision floating number. throw otherwise.
|
||||
double ensureDouble(const QJsonValue val, QString what = "value");
|
||||
}
|
@ -49,9 +49,11 @@ MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst)
|
||||
#endif
|
||||
|
||||
// export some infos
|
||||
env.insert("INST_NAME", inst->name());
|
||||
env.insert("INST_ID", inst->id());
|
||||
env.insert("INST_DIR", QDir(inst->instanceRoot()).absolutePath());
|
||||
auto variables = getVariables();
|
||||
for (auto it = variables.begin(); it != variables.end(); ++it)
|
||||
{
|
||||
env.insert(it.key(), it.value());
|
||||
}
|
||||
|
||||
this->setProcessEnvironment(env);
|
||||
m_prepostlaunchprocess.setProcessEnvironment(env);
|
||||
@ -63,10 +65,10 @@ MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst)
|
||||
// Log prepost launch command output (can be disabled.)
|
||||
if (m_instance->settings().get("LogPrePostOutput").toBool())
|
||||
{
|
||||
connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError,
|
||||
this, &MinecraftProcess::on_prepost_stdErr);
|
||||
connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput,
|
||||
this, &MinecraftProcess::on_prepost_stdOut);
|
||||
connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError, this,
|
||||
&MinecraftProcess::on_prepost_stdErr);
|
||||
connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput, this,
|
||||
&MinecraftProcess::on_prepost_stdOut);
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,17 +141,15 @@ MessageLevel::Enum MinecraftProcess::getLevel(const QString &levelName)
|
||||
return MessageLevel::Message;
|
||||
}
|
||||
|
||||
void MinecraftProcess::logOutput(const QStringList &lines,
|
||||
MessageLevel::Enum defaultLevel,
|
||||
void MinecraftProcess::logOutput(const QStringList &lines, MessageLevel::Enum defaultLevel,
|
||||
bool guessLevel, bool censor)
|
||||
{
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
logOutput(lines[i], defaultLevel, guessLevel, censor);
|
||||
}
|
||||
|
||||
void MinecraftProcess::logOutput(QString line,
|
||||
MessageLevel::Enum defaultLevel,
|
||||
bool guessLevel, bool censor)
|
||||
void MinecraftProcess::logOutput(QString line, MessageLevel::Enum defaultLevel, bool guessLevel,
|
||||
bool censor)
|
||||
{
|
||||
MessageLevel::Enum level = defaultLevel;
|
||||
|
||||
@ -251,33 +251,7 @@ void MinecraftProcess::finish(int code, ExitStatus status)
|
||||
m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code));
|
||||
|
||||
// run post-exit
|
||||
QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString();
|
||||
if (!postlaunch_cmd.isEmpty())
|
||||
{
|
||||
emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd));
|
||||
m_prepostlaunchprocess.start(postlaunch_cmd);
|
||||
m_prepostlaunchprocess.waitForFinished();
|
||||
// Flush console window
|
||||
if (!m_err_leftover.isEmpty())
|
||||
{
|
||||
logOutput(m_err_leftover, MessageLevel::PrePost);
|
||||
m_err_leftover.clear();
|
||||
}
|
||||
if (!m_out_leftover.isEmpty())
|
||||
{
|
||||
logOutput(m_out_leftover, MessageLevel::PrePost);
|
||||
m_out_leftover.clear();
|
||||
}
|
||||
if (m_prepostlaunchprocess.exitStatus() != NormalExit)
|
||||
{
|
||||
emit log(tr("Post-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()),
|
||||
MessageLevel::Error);
|
||||
emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
|
||||
m_prepostlaunchprocess.exitStatus());
|
||||
}
|
||||
else
|
||||
emit log(tr("Post-Launch command ran successfully.\n\n"));
|
||||
}
|
||||
postLaunch();
|
||||
m_instance->cleanupAfterRun();
|
||||
emit ended(m_instance, code, status);
|
||||
}
|
||||
@ -288,14 +262,12 @@ void MinecraftProcess::killMinecraft()
|
||||
kill();
|
||||
}
|
||||
|
||||
void MinecraftProcess::arm()
|
||||
bool MinecraftProcess::preLaunch()
|
||||
{
|
||||
emit log("MultiMC version: " + MMC->version().toString() + "\n\n");
|
||||
emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n");
|
||||
|
||||
QString prelaunch_cmd = m_instance->settings().get("PreLaunchCommand").toString();
|
||||
if (!prelaunch_cmd.isEmpty())
|
||||
{
|
||||
prelaunch_cmd = substituteVariables(prelaunch_cmd);
|
||||
// Launch
|
||||
emit log(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd));
|
||||
m_prepostlaunchprocess.start(prelaunch_cmd);
|
||||
@ -315,23 +287,88 @@ void MinecraftProcess::arm()
|
||||
// Process return values
|
||||
if (m_prepostlaunchprocess.exitStatus() != NormalExit)
|
||||
{
|
||||
emit log(tr("Pre-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()),
|
||||
emit log(tr("Pre-Launch command failed with code %1.\n\n")
|
||||
.arg(m_prepostlaunchprocess.exitCode()),
|
||||
MessageLevel::Fatal);
|
||||
m_instance->cleanupAfterRun();
|
||||
emit prelaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
|
||||
m_prepostlaunchprocess.exitStatus());
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
emit log(tr("Pre-Launch command ran successfully.\n\n"));
|
||||
|
||||
return m_instance->reload();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool MinecraftProcess::postLaunch()
|
||||
{
|
||||
QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString();
|
||||
if (!postlaunch_cmd.isEmpty())
|
||||
{
|
||||
postlaunch_cmd = substituteVariables(postlaunch_cmd);
|
||||
emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd));
|
||||
m_prepostlaunchprocess.start(postlaunch_cmd);
|
||||
m_prepostlaunchprocess.waitForFinished();
|
||||
// Flush console window
|
||||
if (!m_err_leftover.isEmpty())
|
||||
{
|
||||
logOutput(m_err_leftover, MessageLevel::PrePost);
|
||||
m_err_leftover.clear();
|
||||
}
|
||||
if (!m_out_leftover.isEmpty())
|
||||
{
|
||||
logOutput(m_out_leftover, MessageLevel::PrePost);
|
||||
m_out_leftover.clear();
|
||||
}
|
||||
if (m_prepostlaunchprocess.exitStatus() != NormalExit)
|
||||
{
|
||||
emit log(tr("Post-Launch command failed with code %1.\n\n")
|
||||
.arg(m_prepostlaunchprocess.exitCode()),
|
||||
MessageLevel::Error);
|
||||
emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
|
||||
m_prepostlaunchprocess.exitStatus());
|
||||
}
|
||||
else
|
||||
emit log(tr("Post-Launch command ran successfully.\n\n"));
|
||||
|
||||
return m_instance->reload();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
m_instance->setLastLaunch();
|
||||
auto &settings = m_instance->settings();
|
||||
|
||||
//////////// java arguments ////////////
|
||||
QStringList args;
|
||||
QMap<QString, QString> MinecraftProcess::getVariables() const
|
||||
{
|
||||
QMap<QString, QString> out;
|
||||
out.insert("INST_NAME", m_instance->name());
|
||||
out.insert("INST_ID", m_instance->id());
|
||||
out.insert("INST_DIR", QDir(m_instance->instanceRoot()).absolutePath());
|
||||
out.insert("INST_MC_DIR", QDir(m_instance->minecraftRoot()).absolutePath());
|
||||
out.insert("INST_JAVA", m_instance->settings().get("JavaPath").toString());
|
||||
out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
|
||||
return out;
|
||||
}
|
||||
QString MinecraftProcess::substituteVariables(const QString &cmd) const
|
||||
{
|
||||
QString out = cmd;
|
||||
auto variables = getVariables();
|
||||
for (auto it = variables.begin(); it != variables.end(); ++it)
|
||||
{
|
||||
out.replace("$" + it.key(), it.value());
|
||||
}
|
||||
auto env = QProcessEnvironment::systemEnvironment();
|
||||
for (auto var : env.keys())
|
||||
{
|
||||
out.replace("$" + var, env.value(var));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
QStringList MinecraftProcess::javaArguments() const
|
||||
{
|
||||
QStringList args;
|
||||
|
||||
// custom args go first. we want to override them if we have our own here.
|
||||
args.append(m_instance->extraArguments());
|
||||
|
||||
@ -347,14 +384,32 @@ void MinecraftProcess::arm()
|
||||
"minecraft.exe.heapdump");
|
||||
#endif
|
||||
|
||||
args << QString("-Xms%1m").arg(settings.get("MinMemAlloc").toInt());
|
||||
args << QString("-Xmx%1m").arg(settings.get("MaxMemAlloc").toInt());
|
||||
args << QString("-XX:PermSize=%1m").arg(settings.get("PermGen").toInt());
|
||||
args << QString("-Xms%1m").arg(m_instance->settings().get("MinMemAlloc").toInt());
|
||||
args << QString("-Xmx%1m").arg(m_instance->settings().get("MaxMemAlloc").toInt());
|
||||
args << QString("-XX:PermSize=%1m").arg(m_instance->settings().get("PermGen").toInt());
|
||||
args << "-Duser.language=en";
|
||||
if (!m_nativeFolder.isEmpty())
|
||||
args << QString("-Djava.library.path=%1").arg(m_nativeFolder);
|
||||
args << "-jar" << PathCombine(MMC->bin(), "jars", "NewLaunch.jar");
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
void MinecraftProcess::arm()
|
||||
{
|
||||
emit log("MultiMC version: " + MMC->version().toString() + "\n\n");
|
||||
emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n");
|
||||
|
||||
if (!preLaunch())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_instance->setLastLaunch();
|
||||
auto &settings = m_instance->settings();
|
||||
|
||||
QStringList args = javaArguments();
|
||||
|
||||
QString JavaPath = m_instance->settings().get("JavaPath").toString();
|
||||
emit log("Java path is:\n" + JavaPath + "\n\n");
|
||||
QString allArgs = args.join(", ");
|
||||
|
@ -131,6 +131,13 @@ protected:
|
||||
QString launchScript;
|
||||
QString m_nativeFolder;
|
||||
|
||||
bool preLaunch();
|
||||
bool postLaunch();
|
||||
QMap<QString, QString> getVariables() const;
|
||||
QString substituteVariables(const QString &cmd) const;
|
||||
|
||||
QStringList javaArguments() const;
|
||||
|
||||
protected
|
||||
slots:
|
||||
void finish(int, QProcess::ExitStatus status);
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "OneSixFTBInstance.h"
|
||||
|
||||
#include "OneSixVersion.h"
|
||||
#include "VersionFinal.h"
|
||||
#include "OneSixLibrary.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "ForgeInstaller.h"
|
||||
@ -10,85 +10,22 @@
|
||||
#include "MultiMC.h"
|
||||
#include "pathutils.h"
|
||||
|
||||
class OneSixFTBInstanceForge : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OneSixFTBInstanceForge(const QString &version, OneSixFTBInstance *inst, QObject *parent = 0) :
|
||||
Task(parent), instance(inst), version("Forge " + version)
|
||||
{
|
||||
}
|
||||
|
||||
void executeTask()
|
||||
{
|
||||
for (int i = 0; i < MMC->forgelist()->count(); ++i)
|
||||
{
|
||||
if (MMC->forgelist()->at(i)->name() == version)
|
||||
{
|
||||
forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(MMC->forgelist()->at(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!forgeVersion)
|
||||
{
|
||||
emitFailed(QString("Couldn't find forge version ") + version );
|
||||
return;
|
||||
}
|
||||
entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
|
||||
if (entry->stale)
|
||||
{
|
||||
setStatus(tr("Downloading Forge..."));
|
||||
fjob = new NetJob("Forge download");
|
||||
fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
|
||||
connect(fjob, &NetJob::failed, [this](){emitFailed(m_failReason);});
|
||||
connect(fjob, &NetJob::succeeded, this, &OneSixFTBInstanceForge::installForge);
|
||||
connect(fjob, &NetJob::progress, [this](qint64 c, qint64 total){ setProgress(100 * c / total); });
|
||||
fjob->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
installForge();
|
||||
}
|
||||
}
|
||||
|
||||
private
|
||||
slots:
|
||||
void installForge()
|
||||
{
|
||||
setStatus(tr("Installing Forge..."));
|
||||
QString forgePath = entry->getFullPath();
|
||||
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
|
||||
if (!instance->reloadVersion())
|
||||
{
|
||||
emitFailed(tr("Couldn't load the version config"));
|
||||
return;
|
||||
}
|
||||
auto version = instance->getFullVersion();
|
||||
if (!forge.add(instance))
|
||||
{
|
||||
emitFailed(tr("Couldn't install Forge"));
|
||||
return;
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
private:
|
||||
OneSixFTBInstance *instance;
|
||||
QString version;
|
||||
ForgeVersionPtr forgeVersion;
|
||||
MetaEntryPtr entry;
|
||||
NetJob *fjob;
|
||||
};
|
||||
|
||||
OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) :
|
||||
OneSixInstance(rootDir, settings, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void OneSixFTBInstance::init()
|
||||
{
|
||||
try
|
||||
{
|
||||
reloadVersion();
|
||||
}
|
||||
catch(MMCError & e)
|
||||
{
|
||||
// QLOG_ERROR() << "Caught exception on instance init: " << e.cause();
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixFTBInstance::copy(const QDir &newDir)
|
||||
{
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include "OneSixInstance_p.h"
|
||||
#include "OneSixUpdate.h"
|
||||
#include "OneSixVersion.h"
|
||||
#include "VersionFinal.h"
|
||||
#include "pathutils.h"
|
||||
#include "logger/QsLog.h"
|
||||
#include "assets/AssetsUtils.h"
|
||||
@ -27,6 +27,7 @@
|
||||
#include "icons/IconList.h"
|
||||
#include "MinecraftProcess.h"
|
||||
#include "gui/dialogs/OneSixModEditDialog.h"
|
||||
#include <MMCError.h>
|
||||
|
||||
OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
|
||||
: BaseInstance(new OneSixInstancePrivate(), rootDir, settings, parent)
|
||||
@ -34,16 +35,24 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings,
|
||||
I_D(OneSixInstance);
|
||||
d->m_settings->registerSetting("IntendedVersion", "");
|
||||
d->m_settings->registerSetting("ShouldUpdate", false);
|
||||
d->version.reset(new OneSixVersion(this, this));
|
||||
d->vanillaVersion.reset(new OneSixVersion(this, this));
|
||||
d->version.reset(new VersionFinal(this, this));
|
||||
d->vanillaVersion.reset(new VersionFinal(this, this));
|
||||
}
|
||||
|
||||
void OneSixInstance::init()
|
||||
{
|
||||
// FIXME: why is this decided here? what does this even mean?
|
||||
if (QDir(instanceRoot()).exists("version.json"))
|
||||
{
|
||||
try
|
||||
{
|
||||
reloadVersion();
|
||||
}
|
||||
catch(MMCError & e)
|
||||
{
|
||||
// QLOG_ERROR() << "Caught exception on instance init: " << e.cause();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
clearVersion();
|
||||
@ -79,7 +88,7 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with)
|
||||
return result;
|
||||
}
|
||||
|
||||
QDir OneSixInstance::reconstructAssets(std::shared_ptr<OneSixVersion> version)
|
||||
QDir OneSixInstance::reconstructAssets(std::shared_ptr<VersionFinal> version)
|
||||
{
|
||||
QDir assetsDir = QDir("assets/");
|
||||
QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes"));
|
||||
@ -316,25 +325,26 @@ QString OneSixInstance::currentVersionId() const
|
||||
return intendedVersionId();
|
||||
}
|
||||
|
||||
bool OneSixInstance::reloadVersion(QWidget *widgetParent)
|
||||
void OneSixInstance::reloadVersion()
|
||||
{
|
||||
I_D(OneSixInstance);
|
||||
|
||||
bool ret = d->version->reload(widgetParent, false, externalPatches());
|
||||
if (ret)
|
||||
{
|
||||
ret = d->vanillaVersion->reload(widgetParent, true, externalPatches());
|
||||
}
|
||||
if (ret)
|
||||
try
|
||||
{
|
||||
d->version->reload(false, externalPatches());
|
||||
d->vanillaVersion->reload(true, externalPatches());
|
||||
d->m_flags.remove(VersionBrokenFlag);
|
||||
emit versionReloaded();
|
||||
}
|
||||
else
|
||||
catch(MMCError & error)
|
||||
{
|
||||
d->version->clear();
|
||||
d->vanillaVersion->clear();
|
||||
d->m_flags.insert(VersionBrokenFlag);
|
||||
//TODO: rethrow to show some error message(s)?
|
||||
emit versionReloaded();
|
||||
throw;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void OneSixInstance::clearVersion()
|
||||
@ -345,13 +355,13 @@ void OneSixInstance::clearVersion()
|
||||
emit versionReloaded();
|
||||
}
|
||||
|
||||
std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion() const
|
||||
std::shared_ptr<VersionFinal> OneSixInstance::getFullVersion() const
|
||||
{
|
||||
I_D(const OneSixInstance);
|
||||
return d->version;
|
||||
}
|
||||
|
||||
std::shared_ptr<OneSixVersion> OneSixInstance::getVanillaVersion() const
|
||||
std::shared_ptr<VersionFinal> OneSixInstance::getVanillaVersion() const
|
||||
{
|
||||
I_D(const OneSixInstance);
|
||||
return d->vanillaVersion;
|
||||
@ -413,6 +423,23 @@ bool OneSixInstance::providesVersionFile() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OneSixInstance::reload()
|
||||
{
|
||||
if(BaseInstance::reload())
|
||||
{
|
||||
try
|
||||
{
|
||||
reloadVersion();
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QString OneSixInstance::loaderModsDir() const
|
||||
{
|
||||
return PathCombine(minecraftRoot(), "mods");
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
#include "BaseInstance.h"
|
||||
|
||||
#include "OneSixVersion.h"
|
||||
#include "VersionFinal.h"
|
||||
#include "ModList.h"
|
||||
|
||||
class OneSixInstance : public BaseInstance
|
||||
@ -53,14 +53,18 @@ public:
|
||||
|
||||
virtual QDialog *createModEditDialog(QWidget *parent) override;
|
||||
|
||||
/// reload the full version json files. return true on success!
|
||||
bool reloadVersion(QWidget *widgetParent = 0);
|
||||
/**
|
||||
* reload the full version json files. return true on success!
|
||||
*
|
||||
* throws various exceptions :3
|
||||
*/
|
||||
void reloadVersion();
|
||||
/// clears all version information in preparation for an update
|
||||
void clearVersion();
|
||||
/// get the current full version info
|
||||
std::shared_ptr<OneSixVersion> getFullVersion() const;
|
||||
std::shared_ptr<VersionFinal> getFullVersion() const;
|
||||
/// gets the current version info, but only for version.json
|
||||
std::shared_ptr<OneSixVersion> getVanillaVersion() const;
|
||||
std::shared_ptr<VersionFinal> getVanillaVersion() const;
|
||||
/// is the current version original, or custom?
|
||||
virtual bool versionIsCustom() override;
|
||||
|
||||
@ -75,10 +79,12 @@ public:
|
||||
virtual QStringList externalPatches() const;
|
||||
virtual bool providesVersionFile() const;
|
||||
|
||||
bool reload() override;
|
||||
|
||||
signals:
|
||||
void versionReloaded();
|
||||
|
||||
private:
|
||||
QStringList processMinecraftArgs(AuthSessionPtr account);
|
||||
QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);
|
||||
QDir reconstructAssets(std::shared_ptr<VersionFinal> version);
|
||||
};
|
||||
|
@ -16,13 +16,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseInstance_p.h"
|
||||
#include "OneSixVersion.h"
|
||||
#include "VersionFinal.h"
|
||||
#include "ModList.h"
|
||||
|
||||
struct OneSixInstancePrivate : public BaseInstancePrivate
|
||||
{
|
||||
std::shared_ptr<OneSixVersion> version;
|
||||
std::shared_ptr<OneSixVersion> vanillaVersion;
|
||||
std::shared_ptr<VersionFinal> version;
|
||||
std::shared_ptr<VersionFinal> vanillaVersion;
|
||||
std::shared_ptr<ModList> loader_mod_list;
|
||||
std::shared_ptr<ModList> resource_pack_list;
|
||||
};
|
||||
|
@ -26,6 +26,9 @@
|
||||
|
||||
class Rule;
|
||||
|
||||
class OneSixLibrary;
|
||||
typedef std::shared_ptr<OneSixLibrary> OneSixLibraryPtr;
|
||||
|
||||
class OneSixLibrary
|
||||
{
|
||||
private:
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include "lists/MinecraftVersionList.h"
|
||||
#include "OneSixVersion.h"
|
||||
#include "VersionFinal.h"
|
||||
#include "OneSixLibrary.h"
|
||||
#include "OneSixInstance.h"
|
||||
#include "net/ForgeMirrors.h"
|
||||
@ -48,7 +48,7 @@ void OneSixUpdate::executeTask()
|
||||
QDir mcDir(m_inst->minecraftRoot());
|
||||
if (!mcDir.exists() && !mcDir.mkpath("."))
|
||||
{
|
||||
emitFailed("Failed to create bin folder.");
|
||||
emitFailed(tr("Failed to create folder for minecraft binaries."));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ void OneSixUpdate::executeTask()
|
||||
if (targetVersion == nullptr)
|
||||
{
|
||||
// don't do anything if it was invalid
|
||||
emitFailed("The specified Minecraft version is invalid. Choose a different one.");
|
||||
emitFailed(tr("The specified Minecraft version is invalid. Choose a different one."));
|
||||
return;
|
||||
}
|
||||
versionFileStart();
|
||||
@ -108,20 +108,19 @@ void OneSixUpdate::versionFileFinished()
|
||||
QSaveFile vfile1(version1);
|
||||
if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly))
|
||||
{
|
||||
emitFailed("Can't open " + version1 + " for writing.");
|
||||
emitFailed(tr("Can't open %1 for writing.").arg(version1));
|
||||
return;
|
||||
}
|
||||
auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data;
|
||||
qint64 actual = 0;
|
||||
if ((actual = vfile1.write(data)) != data.size())
|
||||
{
|
||||
emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " +
|
||||
data.size() + '.');
|
||||
emitFailed(tr("Failed to write into %1. Written %2 out of %3.").arg(version1).arg(actual).arg(data.size()));
|
||||
return;
|
||||
}
|
||||
if (!vfile1.commit())
|
||||
{
|
||||
emitFailed("Can't commit changes to " + version1);
|
||||
emitFailed(tr("Can't commit changes to %1").arg(version1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -136,21 +135,20 @@ void OneSixUpdate::versionFileFinished()
|
||||
{
|
||||
finfo.remove();
|
||||
}
|
||||
inst->reloadVersion();
|
||||
|
||||
// NOTE: Version is reloaded in jarlibStart
|
||||
jarlibStart();
|
||||
}
|
||||
|
||||
void OneSixUpdate::versionFileFailed()
|
||||
{
|
||||
emitFailed("Failed to download the version description. Try again.");
|
||||
emitFailed(tr("Failed to download the version description. Try again."));
|
||||
}
|
||||
|
||||
void OneSixUpdate::assetIndexStart()
|
||||
{
|
||||
setStatus(tr("Updating assets index..."));
|
||||
OneSixInstance *inst = (OneSixInstance *)m_inst;
|
||||
std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
|
||||
std::shared_ptr<VersionFinal> version = inst->getFullVersion();
|
||||
QString assetName = version->assets;
|
||||
QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json";
|
||||
QString localPath = assetName + ".json";
|
||||
@ -174,13 +172,13 @@ void OneSixUpdate::assetIndexFinished()
|
||||
AssetsIndex index;
|
||||
|
||||
OneSixInstance *inst = (OneSixInstance *)m_inst;
|
||||
std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
|
||||
std::shared_ptr<VersionFinal> version = inst->getFullVersion();
|
||||
QString assetName = version->assets;
|
||||
|
||||
QString asset_fname = "assets/indexes/" + assetName + ".json";
|
||||
if (!AssetsUtils::loadAssetsIndexJson(asset_fname, &index))
|
||||
{
|
||||
emitFailed("Failed to read the assets index!");
|
||||
emitFailed(tr("Failed to read the assets index!"));
|
||||
}
|
||||
|
||||
QList<Md5EtagDownloadPtr> dls;
|
||||
@ -216,7 +214,7 @@ void OneSixUpdate::assetIndexFinished()
|
||||
|
||||
void OneSixUpdate::assetIndexFailed()
|
||||
{
|
||||
emitFailed("Failed to download the assets index!");
|
||||
emitFailed(tr("Failed to download the assets index!"));
|
||||
}
|
||||
|
||||
void OneSixUpdate::assetsFinished()
|
||||
@ -226,7 +224,7 @@ void OneSixUpdate::assetsFinished()
|
||||
|
||||
void OneSixUpdate::assetsFailed()
|
||||
{
|
||||
emitFailed("Failed to download assets!");
|
||||
emitFailed(tr("Failed to download assets!"));
|
||||
}
|
||||
|
||||
void OneSixUpdate::jarlibStart()
|
||||
@ -234,16 +232,23 @@ void OneSixUpdate::jarlibStart()
|
||||
setStatus(tr("Getting the library files from Mojang..."));
|
||||
QLOG_INFO() << m_inst->name() << ": downloading libraries";
|
||||
OneSixInstance *inst = (OneSixInstance *)m_inst;
|
||||
bool successful = inst->reloadVersion();
|
||||
if (!successful)
|
||||
try
|
||||
{
|
||||
emitFailed("Failed to load the version description file. It might be "
|
||||
"corrupted, missing or simply too new.");
|
||||
inst->reloadVersion();
|
||||
}
|
||||
catch(MMCError & e)
|
||||
{
|
||||
emitFailed(e.cause());
|
||||
return;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
emitFailed(tr("Failed to load the version description file for reasons unknown."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a list of URLs that will need to be downloaded.
|
||||
std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
|
||||
std::shared_ptr<VersionFinal> version = inst->getFullVersion();
|
||||
// minecraft.jar for this version
|
||||
{
|
||||
QString version_id = version->id;
|
||||
@ -326,6 +331,5 @@ void OneSixUpdate::jarlibFailed()
|
||||
{
|
||||
QStringList failed = jarlibDownloadJob->getFailedFiles();
|
||||
QString failed_all = failed.join("\n");
|
||||
emitFailed("Failed to download the following files:\n" + failed_all +
|
||||
"\n\nPlease try again.");
|
||||
emitFailed(tr("Failed to download the following files:\n%1\n\nPlease try again.").arg(failed_all));
|
||||
}
|
||||
|
@ -1,221 +0,0 @@
|
||||
/* 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 "OneSixVersion.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
#include "OneSixVersionBuilder.h"
|
||||
|
||||
OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent)
|
||||
: QAbstractListModel(parent), m_instance(instance)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
bool OneSixVersion::reload(QWidget *widgetParent, const bool onlyVanilla, const QStringList &external)
|
||||
{
|
||||
beginResetModel();
|
||||
bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, onlyVanilla, external);
|
||||
endResetModel();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void OneSixVersion::clear()
|
||||
{
|
||||
beginResetModel();
|
||||
id.clear();
|
||||
time.clear();
|
||||
releaseTime.clear();
|
||||
type.clear();
|
||||
assets.clear();
|
||||
processArguments.clear();
|
||||
minecraftArguments.clear();
|
||||
minimumLauncherVersion = 0xDEADBEAF;
|
||||
mainClass.clear();
|
||||
libraries.clear();
|
||||
tweakers.clear();
|
||||
versionFiles.clear();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void OneSixVersion::dump() const
|
||||
{
|
||||
qDebug().nospace() << "OneSixVersion("
|
||||
<< "\n\tid=" << id
|
||||
<< "\n\ttime=" << time
|
||||
<< "\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)
|
||||
{
|
||||
qDebug().nospace() << "\n\t\t" << lib.get();
|
||||
}
|
||||
qDebug().nospace() << "\n)";
|
||||
}
|
||||
|
||||
bool OneSixVersion::canRemove(const int index) const
|
||||
{
|
||||
if (index < versionFiles.size())
|
||||
{
|
||||
return versionFiles.at(index).id != "org.multimc.version.json";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QString OneSixVersion::versionFileId(const int index) const
|
||||
{
|
||||
if (index < 0 || index >= versionFiles.size())
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
return versionFiles.at(index).id;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (lib->isActive() && !lib->isNative())
|
||||
{
|
||||
output.append(lib);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNativeLibs()
|
||||
{
|
||||
QList<std::shared_ptr<OneSixLibrary> > output;
|
||||
for (auto lib : libraries)
|
||||
{
|
||||
if (lib->isActive() && lib->isNative())
|
||||
{
|
||||
output.append(lib);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(const QJsonObject &obj)
|
||||
{
|
||||
std::shared_ptr<OneSixVersion> version(new OneSixVersion(0));
|
||||
if (OneSixVersionBuilder::read(version.get(), obj))
|
||||
{
|
||||
return version;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QVariant OneSixVersion::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
int row = index.row();
|
||||
int column = index.column();
|
||||
|
||||
if (row < 0 || row >= versionFiles.size())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
return versionFiles.at(row).name;
|
||||
case 1:
|
||||
return versionFiles.at(row).version;
|
||||
default:
|
||||
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
|
||||
{
|
||||
if (!index.isValid())
|
||||
return Qt::NoItemFlags;
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
int OneSixVersion::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return versionFiles.size();
|
||||
}
|
||||
|
||||
int OneSixVersion::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
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();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -17,39 +17,32 @@
|
||||
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include "VersionFile.h"
|
||||
|
||||
class OneSixVersion;
|
||||
class VersionFinal;
|
||||
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 onlyVanilla, const QStringList &external);
|
||||
static bool read(OneSixVersion *version, const QJsonObject &obj);
|
||||
static void build(VersionFinal *version, OneSixInstance *instance, const bool onlyVanilla,
|
||||
const QStringList &external);
|
||||
static void readJsonAndApplyToVersion(VersionFinal *version, const QJsonObject &obj);
|
||||
|
||||
static QMap<QString, int> readOverrideOrders(OneSixInstance *instance);
|
||||
static bool writeOverrideOrders(const QMap<QString, int> &order, OneSixInstance *instance);
|
||||
|
||||
enum ParseFlag
|
||||
{
|
||||
NoFlags = 0x0,
|
||||
IsFTBPackJson = 0x1
|
||||
};
|
||||
Q_DECLARE_FLAGS(ParseFlags, ParseFlag)
|
||||
|
||||
private:
|
||||
OneSixVersion *m_version;
|
||||
VersionFinal *m_version;
|
||||
OneSixInstance *m_instance;
|
||||
QWidget *m_widgetParent;
|
||||
|
||||
bool build(const bool onlyVanilla, const QStringList &external);
|
||||
bool read(const QJsonObject &obj);
|
||||
void buildInternal(const bool onlyVanilla, const QStringList &external);
|
||||
void readJsonAndApply(const QJsonObject &obj);
|
||||
void finalizeVersion();
|
||||
|
||||
bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out, const ParseFlags flags = NoFlags);
|
||||
VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,
|
||||
bool isFTB = false);
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(OneSixVersionBuilder::ParseFlags)
|
||||
|
535
logic/VersionFile.cpp
Normal file
535
logic/VersionFile.cpp
Normal file
@ -0,0 +1,535 @@
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <modutils.h>
|
||||
|
||||
#include "logger/QsLog.h"
|
||||
#include "logic/VersionFile.h"
|
||||
#include "logic/OneSixLibrary.h"
|
||||
#include "logic/VersionFinal.h"
|
||||
#include "MMCJson.h"
|
||||
|
||||
using namespace MMCJson;
|
||||
|
||||
#define CURRENT_MINIMUM_LAUNCHER_VERSION 14
|
||||
|
||||
RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &filename)
|
||||
{
|
||||
RawLibraryPtr out(new RawLibrary());
|
||||
if (!libObj.contains("name"))
|
||||
{
|
||||
throw JSONValidationError(filename +
|
||||
"contains a library that doesn't have a 'name' field");
|
||||
}
|
||||
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"))
|
||||
{
|
||||
out->applyExcludes = true;
|
||||
auto extractObj = ensureObject(libObj.value("extract"));
|
||||
for (auto excludeVal : ensureArray(extractObj.value("exclude")))
|
||||
{
|
||||
out->excludes.append(ensureString(excludeVal));
|
||||
}
|
||||
}
|
||||
if (libObj.contains("natives"))
|
||||
{
|
||||
out->applyNatives = true;
|
||||
QJsonObject nativesObj = ensureObject(libObj.value("natives"));
|
||||
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);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &filename,
|
||||
const bool requireOrder, const bool isFTB)
|
||||
{
|
||||
VersionFilePtr out(new VersionFile());
|
||||
if (doc.isEmpty() || doc.isNull())
|
||||
{
|
||||
throw JSONValidationError(filename + " is empty or null");
|
||||
}
|
||||
if (!doc.isObject())
|
||||
{
|
||||
throw JSONValidationError("The root of " + filename + " is not an object");
|
||||
}
|
||||
|
||||
QJsonObject root = doc.object();
|
||||
|
||||
if (requireOrder)
|
||||
{
|
||||
if (root.contains("order"))
|
||||
{
|
||||
out->order = ensureInteger(root.value("order"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: evaluate if we don't want to throw exceptions here instead
|
||||
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))
|
||||
{
|
||||
variable = ensureString(root.value(key));
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: This should be ignored when applying.
|
||||
if (!isFTB)
|
||||
{
|
||||
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"))
|
||||
{
|
||||
out->minimumLauncherVersion = ensureInteger(root.value("minimumLauncherVersion"));
|
||||
}
|
||||
|
||||
if (root.contains("tweakers"))
|
||||
{
|
||||
out->shouldOverwriteTweakers = true;
|
||||
for (auto tweakerVal : ensureArray(root.value("tweakers")))
|
||||
{
|
||||
out->overwriteTweakers.append(ensureString(tweakerVal));
|
||||
}
|
||||
}
|
||||
|
||||
if (root.contains("+tweakers"))
|
||||
{
|
||||
for (auto tweakerVal : ensureArray(root.value("+tweakers")))
|
||||
{
|
||||
out->addTweakers.append(ensureString(tweakerVal));
|
||||
}
|
||||
}
|
||||
|
||||
if (root.contains("-tweakers"))
|
||||
{
|
||||
for (auto tweakerVal : ensureArray(root.value("-tweakers")))
|
||||
{
|
||||
out->removeTweakers.append(ensureString(tweakerVal));
|
||||
}
|
||||
}
|
||||
|
||||
if (root.contains("libraries"))
|
||||
{
|
||||
// FIXME: This should be done when applying.
|
||||
out->shouldOverwriteLibs = !isFTB;
|
||||
for (auto libVal : ensureArray(root.value("libraries")))
|
||||
{
|
||||
auto libObj = ensureObject(libVal);
|
||||
|
||||
auto lib = RawLibrary::fromJson(libObj, filename);
|
||||
// FIXME: This should be done when applying.
|
||||
if (isFTB)
|
||||
{
|
||||
lib->hint = "local";
|
||||
lib->insertType = RawLibrary::Prepend;
|
||||
out->addLibs.prepend(lib);
|
||||
}
|
||||
else
|
||||
{
|
||||
out->overwriteLibs.append(lib);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (root.contains("+libraries"))
|
||||
{
|
||||
for (auto libVal : ensureArray(root.value("+libraries")))
|
||||
{
|
||||
QJsonObject libObj = ensureObject(libVal);
|
||||
QJsonValue insertVal = ensureExists(libObj.value("insert"));
|
||||
|
||||
// parse the library
|
||||
auto lib = RawLibrary::fromJson(libObj, filename);
|
||||
|
||||
// TODO: utility functions for handling this case. templates?
|
||||
QString insertString;
|
||||
{
|
||||
if (insertVal.isString())
|
||||
{
|
||||
insertString = insertVal.toString();
|
||||
}
|
||||
else if (insertVal.isObject())
|
||||
{
|
||||
QJsonObject insertObj = insertVal.toObject();
|
||||
if (insertObj.isEmpty())
|
||||
{
|
||||
throw JSONValidationError("One library has an empty insert object in " +
|
||||
filename);
|
||||
}
|
||||
insertString = insertObj.keys().first();
|
||||
lib->insertData = insertObj.value(insertString).toString();
|
||||
}
|
||||
}
|
||||
if (insertString == "apply")
|
||||
{
|
||||
lib->insertType = RawLibrary::Apply;
|
||||
}
|
||||
else if (insertString == "prepend")
|
||||
{
|
||||
lib->insertType = RawLibrary::Prepend;
|
||||
}
|
||||
else if (insertString == "append")
|
||||
{
|
||||
lib->insertType = RawLibrary::Prepend;
|
||||
}
|
||||
else if (insertString == "replace")
|
||||
{
|
||||
lib->insertType = RawLibrary::Replace;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JSONValidationError("A '+' library in " + filename +
|
||||
" contains an invalid insert type");
|
||||
}
|
||||
if (libObj.contains("MMC-depend"))
|
||||
{
|
||||
const QString dependString = ensureString(libObj.value("MMC-depend"));
|
||||
if (dependString == "hard")
|
||||
{
|
||||
lib->dependType = RawLibrary::Hard;
|
||||
}
|
||||
else if (dependString == "soft")
|
||||
{
|
||||
lib->dependType = RawLibrary::Soft;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JSONValidationError("A '+' library in " + filename +
|
||||
" contains an invalid depend type");
|
||||
}
|
||||
}
|
||||
out->addLibs.append(lib);
|
||||
}
|
||||
}
|
||||
if (root.contains("-libraries"))
|
||||
{
|
||||
for (auto libVal : ensureArray(root.value("-libraries")))
|
||||
{
|
||||
auto libObj = ensureObject(libVal);
|
||||
out->removeLibs.append(ensureString(libObj.value("name")));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
OneSixLibraryPtr VersionFile::createLibrary(RawLibraryPtr lib)
|
||||
{
|
||||
std::shared_ptr<OneSixLibrary> out(new OneSixLibrary(lib->name));
|
||||
if (!lib->url.isEmpty())
|
||||
{
|
||||
out->setBaseUrl(lib->url);
|
||||
}
|
||||
out->setHint(lib->hint);
|
||||
if (!lib->absoluteUrl.isEmpty())
|
||||
{
|
||||
out->setAbsoluteUrl(lib->absoluteUrl);
|
||||
}
|
||||
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 VersionFile::findLibrary(QList<OneSixLibraryPtr> 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 VersionFile::applyTo(VersionFinal *version)
|
||||
{
|
||||
if (minimumLauncherVersion != -1)
|
||||
{
|
||||
if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
|
||||
{
|
||||
throw LauncherVersionError(minimumLauncherVersion, CURRENT_MINIMUM_LAUNCHER_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
if (!version->id.isNull() && !mcVersion.isNull())
|
||||
{
|
||||
if (QRegExp(mcVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(version->id) ==
|
||||
-1)
|
||||
{
|
||||
throw MinecraftVersionMismatch(fileId, mcVersion, version->id);
|
||||
}
|
||||
}
|
||||
|
||||
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 RawLibrary::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->name << "(skipping)";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RawLibrary::Append:
|
||||
case RawLibrary::Prepend:
|
||||
{
|
||||
|
||||
const int startOfVersion = lib->name.lastIndexOf(':') + 1;
|
||||
const int index = findLibrary(
|
||||
version->libraries, QString(lib->name).replace(startOfVersion, INT_MAX, '*'));
|
||||
if (index < 0)
|
||||
{
|
||||
if (lib->insertType == RawLibrary::Append)
|
||||
{
|
||||
version->libraries.append(createLibrary(lib));
|
||||
}
|
||||
else
|
||||
{
|
||||
version->libraries.prepend(createLibrary(lib));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto otherLib = version->libraries.at(index);
|
||||
const Util::Version ourVersion = lib->name.mid(startOfVersion, INT_MAX);
|
||||
const Util::Version otherVersion = otherLib->version();
|
||||
// if the existing version is a hard dependency we can either use it or
|
||||
// fail, but we can't change it
|
||||
if (otherLib->dependType == OneSixLibrary::Hard)
|
||||
{
|
||||
// we need a higher version, or we're hard to and the versions aren't
|
||||
// equal
|
||||
if (ourVersion > otherVersion ||
|
||||
(lib->dependType == RawLibrary::Hard && ourVersion != otherVersion))
|
||||
{
|
||||
throw VersionBuildError(
|
||||
QObject::tr(
|
||||
"Error resolving library dependencies between %1 and %2 in %3.")
|
||||
.arg(otherLib->rawName(), lib->name, filename));
|
||||
}
|
||||
else
|
||||
{
|
||||
// the library is already existing, so we don't have to do anything
|
||||
}
|
||||
}
|
||||
else if (otherLib->dependType == OneSixLibrary::Soft)
|
||||
{
|
||||
// if we are higher it means we should update
|
||||
if (ourVersion > otherVersion)
|
||||
{
|
||||
auto library = createLibrary(lib);
|
||||
if (Util::Version(otherLib->minVersion) < ourVersion)
|
||||
{
|
||||
library->minVersion = ourVersion.toString();
|
||||
}
|
||||
version->libraries.replace(index, library);
|
||||
}
|
||||
else
|
||||
{
|
||||
// our version is smaller than the existing version, but we require
|
||||
// it: fail
|
||||
if (lib->dependType == RawLibrary::Hard)
|
||||
{
|
||||
throw VersionBuildError(QObject::tr(
|
||||
"Error resolving library dependencies between %1 and %2 in %3.")
|
||||
.arg(otherLib->rawName(), lib->name,
|
||||
filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RawLibrary::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)";
|
||||
}
|
||||
}
|
||||
}
|
127
logic/VersionFile.h
Normal file
127
logic/VersionFile.h
Normal file
@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <memory>
|
||||
#include "logic/OpSys.h"
|
||||
#include "logic/OneSixRule.h"
|
||||
#include "MMCError.h"
|
||||
|
||||
class VersionFinal;
|
||||
|
||||
class VersionBuildError : public MMCError
|
||||
{
|
||||
public:
|
||||
VersionBuildError(QString cause) : MMCError(cause) {};
|
||||
virtual ~VersionBuildError() noexcept {}
|
||||
};
|
||||
|
||||
/**
|
||||
* the base version file was meant for a newer version of the vanilla launcher than we support
|
||||
*/
|
||||
class LauncherVersionError : public VersionBuildError
|
||||
{
|
||||
public:
|
||||
LauncherVersionError(int actual, int supported)
|
||||
: VersionBuildError(QObject::tr(
|
||||
"The base version file of this instance was meant for a newer (%1) "
|
||||
"version of the vanilla launcher than this version of MultiMC supports (%2).")
|
||||
.arg(actual)
|
||||
.arg(supported)) {};
|
||||
virtual ~LauncherVersionError() noexcept {}
|
||||
};
|
||||
|
||||
/**
|
||||
* some patch was intended for a different version of minecraft
|
||||
*/
|
||||
class MinecraftVersionMismatch : public VersionBuildError
|
||||
{
|
||||
public:
|
||||
MinecraftVersionMismatch(QString fileId, QString mcVersion, QString parentMcVersion)
|
||||
: VersionBuildError(QObject::tr("The patch %1 is for a different version of Minecraft "
|
||||
"(%2) than that of the instance (%3).")
|
||||
.arg(fileId)
|
||||
.arg(mcVersion)
|
||||
.arg(parentMcVersion)) {};
|
||||
virtual ~MinecraftVersionMismatch() noexcept {}
|
||||
};
|
||||
|
||||
struct RawLibrary;
|
||||
typedef std::shared_ptr<RawLibrary> RawLibraryPtr;
|
||||
struct RawLibrary
|
||||
{
|
||||
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,
|
||||
Replace
|
||||
};
|
||||
InsertType insertType = Append;
|
||||
QString insertData;
|
||||
enum DependType
|
||||
{
|
||||
Soft,
|
||||
Hard
|
||||
};
|
||||
DependType dependType = Soft;
|
||||
|
||||
static RawLibraryPtr fromJson(const QJsonObject &libObj, const QString &filename);
|
||||
};
|
||||
|
||||
struct VersionFile;
|
||||
typedef std::shared_ptr<VersionFile> VersionFilePtr;
|
||||
struct VersionFile
|
||||
{
|
||||
public: /* methods */
|
||||
static VersionFilePtr fromJson(const QJsonDocument &doc, const QString &filename,
|
||||
const bool requireOrder, const bool isFTB = false);
|
||||
|
||||
static OneSixLibraryPtr createLibrary(RawLibraryPtr lib);
|
||||
int findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle);
|
||||
void applyTo(VersionFinal *version);
|
||||
|
||||
public: /* data */
|
||||
int order = 0;
|
||||
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;
|
||||
|
||||
bool shouldOverwriteLibs = false;
|
||||
QList<RawLibraryPtr> overwriteLibs;
|
||||
QList<RawLibraryPtr> addLibs;
|
||||
QList<QString> removeLibs;
|
||||
};
|
183
logic/VersionFinal.cpp
Normal file
183
logic/VersionFinal.cpp
Normal file
@ -0,0 +1,183 @@
|
||||
/* 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 "VersionFinal.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
#include "OneSixVersionBuilder.h"
|
||||
|
||||
VersionFinal::VersionFinal(OneSixInstance *instance, QObject *parent)
|
||||
: QAbstractListModel(parent), m_instance(instance)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
bool VersionFinal::reload(const bool onlyVanilla, const QStringList &external)
|
||||
{
|
||||
//FIXME: source of epic failure.
|
||||
beginResetModel();
|
||||
OneSixVersionBuilder::build(this, m_instance, onlyVanilla, external);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void VersionFinal::clear()
|
||||
{
|
||||
beginResetModel();
|
||||
id.clear();
|
||||
time.clear();
|
||||
releaseTime.clear();
|
||||
type.clear();
|
||||
assets.clear();
|
||||
processArguments.clear();
|
||||
minecraftArguments.clear();
|
||||
minimumLauncherVersion = 0xDEADBEAF;
|
||||
mainClass.clear();
|
||||
libraries.clear();
|
||||
tweakers.clear();
|
||||
versionFiles.clear();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
bool VersionFinal::canRemove(const int index) const
|
||||
{
|
||||
if (index < versionFiles.size())
|
||||
{
|
||||
return versionFiles.at(index)->fileId != "org.multimc.version.json";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QString VersionFinal::versionFileId(const int index) const
|
||||
{
|
||||
if (index < 0 || index >= versionFiles.size())
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
return versionFiles.at(index)->fileId;
|
||||
}
|
||||
|
||||
bool VersionFinal::remove(const int index)
|
||||
{
|
||||
if (canRemove(index))
|
||||
{
|
||||
return QFile::remove(versionFiles.at(index)->filename);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNormalLibs()
|
||||
{
|
||||
QList<std::shared_ptr<OneSixLibrary> > output;
|
||||
for (auto lib : libraries)
|
||||
{
|
||||
if (lib->isActive() && !lib->isNative())
|
||||
{
|
||||
output.append(lib);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNativeLibs()
|
||||
{
|
||||
QList<std::shared_ptr<OneSixLibrary> > output;
|
||||
for (auto lib : libraries)
|
||||
{
|
||||
if (lib->isActive() && lib->isNative())
|
||||
{
|
||||
output.append(lib);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
std::shared_ptr<VersionFinal> VersionFinal::fromJson(const QJsonObject &obj)
|
||||
{
|
||||
std::shared_ptr<VersionFinal> version(new VersionFinal(0));
|
||||
try
|
||||
{
|
||||
OneSixVersionBuilder::readJsonAndApplyToVersion(version.get(), obj);
|
||||
}
|
||||
catch(MMCError & err)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
QVariant VersionFinal::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
int row = index.row();
|
||||
int column = index.column();
|
||||
|
||||
if (row < 0 || row >= versionFiles.size())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
return versionFiles.at(row)->name;
|
||||
case 1:
|
||||
return versionFiles.at(row)->version;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant VersionFinal::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 VersionFinal::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return Qt::NoItemFlags;
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
int VersionFinal::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return versionFiles.size();
|
||||
}
|
||||
|
||||
int VersionFinal::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
return 2;
|
||||
}
|
@ -22,14 +22,15 @@
|
||||
#include <memory>
|
||||
|
||||
#include "OneSixLibrary.h"
|
||||
#include "VersionFile.h"
|
||||
|
||||
class OneSixInstance;
|
||||
|
||||
class OneSixVersion : public QAbstractListModel
|
||||
class VersionFinal : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OneSixVersion(OneSixInstance *instance, QObject *parent = 0);
|
||||
explicit VersionFinal(OneSixInstance *instance, QObject *parent = 0);
|
||||
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||
@ -37,7 +38,7 @@ public:
|
||||
virtual int columnCount(const QModelIndex &parent) const;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
|
||||
bool reload(QWidget *widgetParent, const bool onlyVanilla = false, const QStringList &external = QStringList());
|
||||
bool reload(const bool onlyVanilla = false, const QStringList &external = QStringList());
|
||||
void clear();
|
||||
|
||||
void dump() const;
|
||||
@ -54,7 +55,7 @@ public:
|
||||
QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
|
||||
QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
|
||||
|
||||
static std::shared_ptr<OneSixVersion> fromJson(const QJsonObject &obj);
|
||||
static std::shared_ptr<VersionFinal> fromJson(const QJsonObject &obj);
|
||||
|
||||
// data members
|
||||
public:
|
||||
@ -118,20 +119,8 @@ public:
|
||||
*/
|
||||
// QList<Rule> rules;
|
||||
|
||||
struct VersionFile
|
||||
{
|
||||
QString name;
|
||||
QString id;
|
||||
QString version;
|
||||
QString mcVersion;
|
||||
QString filename;
|
||||
int order;
|
||||
};
|
||||
QList<VersionFile> versionFiles;
|
||||
QList<VersionFilePtr> versionFiles;
|
||||
|
||||
private:
|
||||
OneSixInstance *m_instance;
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug &dbg, const OneSixVersion *version);
|
||||
QDebug operator<<(QDebug &dbg, const OneSixLibrary *library);
|
@ -92,7 +92,6 @@ void LiteLoaderVersionList::updateListData(QList<BaseVersionPtr> versions)
|
||||
LLListLoadTask::LLListLoadTask(LiteLoaderVersionList *vlist)
|
||||
{
|
||||
m_list = vlist;
|
||||
vlistReply = nullptr;
|
||||
}
|
||||
|
||||
LLListLoadTask::~LLListLoadTask()
|
||||
@ -102,23 +101,49 @@ LLListLoadTask::~LLListLoadTask()
|
||||
void LLListLoadTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Loading LiteLoader version list..."));
|
||||
auto worker = MMC->qnam();
|
||||
vlistReply = worker->get(QNetworkRequest(QUrl(URLConstants::LITELOADER_URL)));
|
||||
connect(vlistReply, SIGNAL(finished()), this, SLOT(listDownloaded()));
|
||||
auto job = new NetJob("Version index");
|
||||
// we do not care if the version is stale or not.
|
||||
auto liteloaderEntry = MMC->metacache()->resolveEntry("liteloader", "versions.json");
|
||||
|
||||
// verify by poking the server.
|
||||
liteloaderEntry->stale = true;
|
||||
|
||||
job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::LITELOADER_URL),
|
||||
liteloaderEntry));
|
||||
|
||||
connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed()));
|
||||
|
||||
listJob.reset(job);
|
||||
connect(listJob.get(), SIGNAL(succeeded()), SLOT(listDownloaded()));
|
||||
connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
|
||||
listJob->start();
|
||||
}
|
||||
|
||||
void LLListLoadTask::listFailed()
|
||||
{
|
||||
emitFailed("Failed to load LiteLoader version list.");
|
||||
return;
|
||||
}
|
||||
|
||||
void LLListLoadTask::listDownloaded()
|
||||
{
|
||||
if (vlistReply->error() != QNetworkReply::NoError)
|
||||
QByteArray data;
|
||||
{
|
||||
vlistReply->deleteLater();
|
||||
emitFailed("Failed to load LiteLoader version list" + vlistReply->errorString());
|
||||
auto dlJob = listDownload;
|
||||
auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->getTargetFilepath();
|
||||
QFile listFile(filename);
|
||||
if (!listFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
emitFailed("Failed to open the LiteLoader version list.");
|
||||
return;
|
||||
}
|
||||
data = listFile.readAll();
|
||||
listFile.close();
|
||||
dlJob.reset();
|
||||
}
|
||||
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError);
|
||||
vlistReply->deleteLater();
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
|
||||
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
@ -140,7 +165,12 @@ void LLListLoadTask::listDownloaded()
|
||||
emitFailed("Error parsing version list JSON: missing 'versions' object");
|
||||
return;
|
||||
}
|
||||
const QJsonObject versions = root.value("versions").toObject();
|
||||
|
||||
auto meta = root.value("meta").toObject();
|
||||
QString description = meta.value("description").toString(tr("This is a lightweight loader for mods that don't change game mechanics."));
|
||||
QString defaultUrl = meta.value("url").toString("http://dl.liteloader.com");
|
||||
QString authors = meta.value("authors").toString("Mumfrey");
|
||||
auto versions = root.value("versions").toObject();
|
||||
|
||||
QList<BaseVersionPtr> tempList;
|
||||
for (auto vIt = versions.begin(); vIt != versions.end(); ++vIt)
|
||||
@ -170,6 +200,9 @@ void LLListLoadTask::listDownloaded()
|
||||
version->md5 = artefact.value("md5").toString();
|
||||
version->timestamp = artefact.value("timestamp").toDouble();
|
||||
version->tweakClass = artefact.value("tweakClass").toString();
|
||||
version->authors = authors;
|
||||
version->description = description;
|
||||
version->defaultUrl = defaultUrl;
|
||||
const QJsonArray libs = artefact.value("libraries").toArray();
|
||||
for (auto lIt = libs.begin(); lIt != libs.end(); ++lIt)
|
||||
{
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "BaseVersionList.h"
|
||||
#include "logic/tasks/Task.h"
|
||||
#include "logic/BaseVersion.h"
|
||||
#include <logic/net/NetJob.h>
|
||||
|
||||
class LLListLoadTask;
|
||||
class QNetworkReply;
|
||||
@ -46,6 +47,7 @@ public:
|
||||
return version;
|
||||
}
|
||||
|
||||
// important info
|
||||
QString version;
|
||||
QString file;
|
||||
QString mcVersion;
|
||||
@ -54,6 +56,11 @@ public:
|
||||
bool isLatest;
|
||||
QString tweakClass;
|
||||
QStringList libraries;
|
||||
|
||||
// meta
|
||||
QString defaultUrl;
|
||||
QString description;
|
||||
QString authors;
|
||||
};
|
||||
typedef std::shared_ptr<LiteLoaderVersion> LiteLoaderVersionPtr;
|
||||
|
||||
@ -96,8 +103,10 @@ public:
|
||||
protected
|
||||
slots:
|
||||
void listDownloaded();
|
||||
void listFailed();
|
||||
|
||||
protected:
|
||||
QNetworkReply *vlistReply;
|
||||
NetJobPtr listJob;
|
||||
CacheDownloadPtr listDownload;
|
||||
LiteLoaderVersionList *m_list;
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ void PasteUpload::executeTask()
|
||||
|
||||
m_reply = std::shared_ptr<QNetworkReply>(rep);
|
||||
connect(rep, &QNetworkReply::downloadProgress, [&](qint64 value, qint64 max)
|
||||
{ setProgress(value / max * 100); });
|
||||
{ setProgress(value / qMax((qint64)1, max) * 100); });
|
||||
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this,
|
||||
SLOT(downloadError(QNetworkReply::NetworkError)));
|
||||
connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
|
||||
@ -52,10 +52,9 @@ void PasteUpload::downloadFinished()
|
||||
emitFailed(jsonError.errorString());
|
||||
return;
|
||||
}
|
||||
QString error;
|
||||
if (!parseResult(doc, &error))
|
||||
if (!parseResult(doc))
|
||||
{
|
||||
emitFailed(error);
|
||||
emitFailed(tr("paste.ee returned an error. Please consult the logs for more information"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -69,13 +68,13 @@ void PasteUpload::downloadFinished()
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
bool PasteUpload::parseResult(QJsonDocument doc, QString *parseError)
|
||||
bool PasteUpload::parseResult(QJsonDocument doc)
|
||||
{
|
||||
auto object = doc.object();
|
||||
auto status = object.value("status").toString("error");
|
||||
if (status == "error")
|
||||
{
|
||||
parseError = new QString(object.value("error").toString());
|
||||
QLOG_ERROR() << "paste.ee reported error:" << QString(object.value("error").toString());
|
||||
return false;
|
||||
}
|
||||
// FIXME: not the place for GUI things.
|
||||
|
@ -14,7 +14,7 @@ protected:
|
||||
virtual void executeTask();
|
||||
|
||||
private:
|
||||
bool parseResult(QJsonDocument doc, QString *parseError);
|
||||
bool parseResult(QJsonDocument doc);
|
||||
QString m_text;
|
||||
QString m_error;
|
||||
QWidget *m_window;
|
||||
|
20
translations/CMakeLists.txt
Normal file
20
translations/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM 1)
|
||||
|
||||
### translation stuff
|
||||
|
||||
file(GLOB TRANSLATION_FILES ${CMAKE_CURRENT_LIST_DIR}/*.ts)
|
||||
set(FILES_TO_TRANSLATE_ABSOLUTE)
|
||||
foreach(file ${FILES_TO_TRANSLATE})
|
||||
list(APPEND FILES_TO_TRANSLATE_ABSOLUTE "${CMAKE_SOURCE_DIR}/${file}")
|
||||
endforeach()
|
||||
|
||||
qt5_create_translation(TRANSLATION_MESSAGES ${FILES_TO_TRANSLATE_ABSOLUTE} ${TRANSLATION_FILES})
|
||||
qt5_add_translation(TRANSLATION_QM ${TRANSLATION_FILES})
|
||||
add_custom_target(translations_update DEPENDS ${TRANSLATION_MESSAGES})
|
||||
add_custom_target(translations DEPENDS ${TRANSLATION_QM})
|
||||
|
||||
IF(APPLE AND UNIX) ## OSX
|
||||
install(FILES ${TRANSLATION_QM} DESTINATION MultiMC.app/Contents/MacOS/translations)
|
||||
ELSE()
|
||||
install(FILES ${TRANSLATION_QM} DESTINATION translations)
|
||||
ENDIF()
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user