GH-1034 do jar modding separate from update

This commit is contained in:
Petr Mrázek 2015-07-10 00:06:05 +02:00
parent 5133b0f34f
commit 5dd48e89f5
18 changed files with 214 additions and 141 deletions

View File

@ -141,8 +141,8 @@ SET(MULTIMC_SOURCES
SettingsUI.cpp SettingsUI.cpp
# Processes # Processes
LaunchController.h LaunchInteraction.h
LaunchController.cpp LaunchInteraction.cpp
# page provider for instances # page provider for instances
InstancePageProvider.h InstancePageProvider.h

View File

@ -1,4 +1,4 @@
#include "LaunchController.h" #include "LaunchInteraction.h"
#include <auth/MojangAccountList.h> #include <auth/MojangAccountList.h>
#include "MultiMC.h" #include "MultiMC.h"
#include "dialogs/CustomMessageBox.h" #include "dialogs/CustomMessageBox.h"
@ -23,6 +23,7 @@ void LaunchController::launch()
login(); login();
} }
// FIXME: minecraft specific
void LaunchController::login() void LaunchController::login()
{ {
if (!m_instance) if (!m_instance)
@ -156,7 +157,7 @@ void LaunchController::launchInstance()
return; return;
} }
m_launcher = m_instance->prepareForLaunch(m_session); m_launcher = m_instance->createLaunchTask(m_session);
if (!m_launcher) if (!m_launcher)
{ {
return; return;

View File

@ -372,7 +372,7 @@ namespace Ui {
#include "java/JavaUtils.h" #include "java/JavaUtils.h"
#include "JavaCommon.h" #include "JavaCommon.h"
#include "InstancePageProvider.h" #include "InstancePageProvider.h"
#include "LaunchController.h" #include "LaunchInteraction.h"
#include "SettingsUI.h" #include "SettingsUI.h"
#include "minecraft/SkinUtils.h" #include "minecraft/SkinUtils.h"
#include "resources/Resource.h" #include "resources/Resource.h"
@ -1184,7 +1184,7 @@ void MainWindow::finalizeInstance(InstancePtr inst)
if (MMC->accounts()->anyAccountIsValid()) if (MMC->accounts()->anyAccountIsValid())
{ {
ProgressDialog loadDialog(this); ProgressDialog loadDialog(this);
auto update = inst->doUpdate(); auto update = inst->createUpdateTask();
connect(update.get(), &Task::failed, [this](QString reason) connect(update.get(), &Task::failed, [this](QString reason)
{ {
QString error = QString("Instance load failed: %1").arg(reason); QString error = QString("Instance load failed: %1").arg(reason);

View File

@ -269,7 +269,7 @@ void VersionPage::on_changeVersionBtn_clicked()
int VersionPage::doUpdate() int VersionPage::doUpdate()
{ {
auto updateTask = m_inst->doUpdate(); auto updateTask = m_inst->createUpdateTask();
if (!updateTask) if (!updateTask)
{ {
return 1; return 1;

View File

@ -135,13 +135,21 @@ public:
virtual SettingsObjectPtr settings() const; virtual SettingsObjectPtr settings() const;
/// returns a valid update task /// returns a valid update task
virtual std::shared_ptr<Task> doUpdate() = 0; virtual std::shared_ptr<Task> createUpdateTask() = 0;
/// returns a valid process, ready for launch with the given account. /// returns a valid launcher (task container)
virtual std::shared_ptr<BaseLauncher> prepareForLaunch(AuthSessionPtr account) = 0; virtual std::shared_ptr<BaseLauncher> createLaunchTask(AuthSessionPtr account) = 0;
/// do any necessary cleanups after the instance finishes. also runs before /*!
/// 'prepareForLaunch' * Returns a task that should be done right before launch
* This task should do any extra preparations needed
*/
virtual std::shared_ptr<Task> createJarModdingTask() = 0;
/*!
* does any necessary cleanups after the instance finishes. also runs before\
* TODO: turn into a task that can run asynchronously
*/
virtual void cleanupAfterRun() = 0; virtual void cleanupAfterRun() = 0;
virtual QString getStatusbarDescription() = 0; virtual QString getStatusbarDescription() = 0;

View File

@ -421,7 +421,7 @@ void BaseLauncher::on_pre_state(LoggedProcess::State state)
void BaseLauncher::updateInstance() void BaseLauncher::updateInstance()
{ {
m_updateTask = m_instance->doUpdate(); m_updateTask = m_instance->createUpdateTask();
if(m_updateTask) if(m_updateTask)
{ {
connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished())); connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
@ -435,7 +435,7 @@ void BaseLauncher::updateFinished()
{ {
if(m_updateTask->successful()) if(m_updateTask->successful())
{ {
makeReady(); doJarModding();
} }
else else
{ {
@ -445,6 +445,28 @@ void BaseLauncher::updateFinished()
} }
} }
void BaseLauncher::doJarModding()
{
m_jarModTask = m_instance->createJarModdingTask();
if(!m_jarModTask)
{
jarModdingSucceeded();
}
connect(m_jarModTask.get(), SIGNAL(succeeded()), this, SLOT(jarModdingSucceeded()));
connect(m_jarModTask.get(), SIGNAL(failed(QString)), this, SLOT(jarModdingFailed(QString)));
m_jarModTask->start();
}
void BaseLauncher::jarModdingSucceeded()
{
makeReady();
}
void BaseLauncher::jarModdingFailed(QString reason)
{
emitFailed(reason);
}
void BaseLauncher::makeReady() void BaseLauncher::makeReady()
{ {
QStringList args = javaArguments(); QStringList args = javaArguments();

View File

@ -95,6 +95,7 @@ public: /* HACK: MINECRAFT: split! */
protected: /* methods */ protected: /* methods */
void preLaunch(); void preLaunch();
void updateInstance(); void updateInstance();
void doJarModding();
void makeReady(); void makeReady();
void postLaunch(); void postLaunch();
virtual void emitFailed(QString reason); virtual void emitFailed(QString reason);
@ -108,6 +109,10 @@ protected: /* methods */
virtual QString censorPrivateInfo(QString in); virtual QString censorPrivateInfo(QString in);
virtual MessageLevel::Enum guessLevel(const QString &message, MessageLevel::Enum defaultLevel); virtual MessageLevel::Enum guessLevel(const QString &message, MessageLevel::Enum defaultLevel);
protected slots:
void jarModdingSucceeded();
void jarModdingFailed(QString reason);
signals: signals:
/** /**
* @brief emitted when the launch preparations are done * @brief emitted when the launch preparations are done
@ -161,6 +166,7 @@ protected: /* HACK: MINECRAFT: split! */
QString launchScript; QString launchScript;
QString m_nativeFolder; QString m_nativeFolder;
std::shared_ptr<Task> m_updateTask; std::shared_ptr<Task> m_updateTask;
std::shared_ptr<Task> m_jarModTask;
protected: /* HACK: MINECRAFT: split! */ protected: /* HACK: MINECRAFT: split! */
void checkJava(); void checkJava();

View File

@ -43,11 +43,15 @@ public:
{ {
return instanceRoot(); return instanceRoot();
}; };
virtual std::shared_ptr<BaseLauncher> prepareForLaunch(AuthSessionPtr) virtual std::shared_ptr<BaseLauncher> createLaunchTask(AuthSessionPtr)
{ {
return nullptr; return nullptr;
} }
virtual std::shared_ptr< Task > doUpdate() virtual std::shared_ptr< Task > createUpdateTask()
{
return nullptr;
}
virtual std::shared_ptr<Task> createJarModdingTask()
{ {
return nullptr; return nullptr;
} }

View File

@ -134,9 +134,9 @@ QString OneSixFTBInstance::getStatusbarDescription()
return "OneSix FTB: " + intendedVersionId(); return "OneSix FTB: " + intendedVersionId();
} }
std::shared_ptr<Task> OneSixFTBInstance::doUpdate() std::shared_ptr<Task> OneSixFTBInstance::createUpdateTask()
{ {
return OneSixInstance::doUpdate(); return OneSixInstance::createUpdateTask();
} }
#include "OneSixFTBInstance.moc" #include "OneSixFTBInstance.moc"

View File

@ -17,7 +17,7 @@ public:
virtual QString getStatusbarDescription(); virtual QString getStatusbarDescription();
virtual std::shared_ptr<Task> doUpdate() override; virtual std::shared_ptr<Task> createUpdateTask() override;
virtual QString id() const; virtual QString id() const;

View File

@ -26,6 +26,7 @@
#include "icons/IconList.h" #include "icons/IconList.h"
#include "BaseLauncher.h" #include "BaseLauncher.h"
#include "minecraft/ModList.h" #include "minecraft/ModList.h"
#include <MMCZip.h>
LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
: MinecraftInstance(globalSettings, settings, rootDir) : MinecraftInstance(globalSettings, settings, rootDir)
@ -87,7 +88,7 @@ bool LegacyInstance::shouldUseCustomBaseJar() const
} }
std::shared_ptr<Task> LegacyInstance::doUpdate() std::shared_ptr<Task> LegacyInstance::createUpdateTask()
{ {
// make sure the jar mods list is initialized by asking for it. // make sure the jar mods list is initialized by asking for it.
auto list = jarModList(); auto list = jarModList();
@ -95,7 +96,7 @@ std::shared_ptr<Task> LegacyInstance::doUpdate()
return std::shared_ptr<Task>(new LegacyUpdate(this, this)); return std::shared_ptr<Task>(new LegacyUpdate(this, this));
} }
std::shared_ptr<BaseLauncher> LegacyInstance::prepareForLaunch(AuthSessionPtr account) std::shared_ptr<BaseLauncher> LegacyInstance::createLaunchTask(AuthSessionPtr account)
{ {
QString launchScript; QString launchScript;
QIcon icon = ENV.icons()->getIcon(iconKey()); QIcon icon = ENV.icons()->getIcon(iconKey());
@ -129,6 +130,87 @@ std::shared_ptr<BaseLauncher> LegacyInstance::prepareForLaunch(AuthSessionPtr ac
return process; return process;
} }
std::shared_ptr<Task> LegacyInstance::createJarModdingTask()
{
class JarModTask : public Task
{
public:
explicit JarModTask(std::shared_ptr<LegacyInstance> inst) : m_inst(inst), Task(nullptr)
{
}
virtual void executeTask()
{
if (!m_inst->shouldRebuild())
{
emitSucceeded();
return;
}
// Get the mod list
auto modList = m_inst->getJarMods();
QFileInfo runnableJar(m_inst->runnableJar());
QFileInfo baseJar(m_inst->baseJar());
bool base_is_custom = m_inst->shouldUseCustomBaseJar();
// Nothing to do if there are no jar mods to install, no backup and just the mc jar
if (base_is_custom)
{
// yes, this can happen if the instance only has the runnable jar and not the base jar
// it *could* be assumed that such an instance is vanilla, but that wouldn't be safe
// because that's not something mmc4 guarantees
if (runnableJar.isFile() && !baseJar.exists() && modList.empty())
{
m_inst->setShouldRebuild(false);
emitSucceeded();
return;
}
setStatus(tr("Installing mods: Backing up minecraft.jar ..."));
if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath()))
{
emitFailed("It seems both the active and base jar are gone. A fresh base jar will "
"be used on next run.");
m_inst->setShouldRebuild(true);
m_inst->setShouldUpdate(true);
m_inst->setShouldUseCustomBaseJar(false);
return;
}
}
if (!baseJar.exists())
{
emitFailed("The base jar " + baseJar.filePath() + " does not exist");
return;
}
if (runnableJar.exists() && !QFile::remove(runnableJar.filePath()))
{
emitFailed("Failed to delete old minecraft.jar");
return;
}
setStatus(tr("Installing mods: Opening minecraft.jar ..."));
QString outputJarPath = runnableJar.filePath();
QString inputJarPath = baseJar.filePath();
if(!MMCZip::createModdedJar(inputJarPath, outputJarPath, modList))
{
emitFailed(tr("Failed to create the custom Minecraft jar file."));
return;
}
m_inst->setShouldRebuild(false);
// inst->UpdateVersion(true);
emitSucceeded();
return;
}
std::shared_ptr<LegacyInstance> m_inst;
};
return std::make_shared<JarModTask>(std::dynamic_pointer_cast<LegacyInstance>(shared_from_this()));
}
void LegacyInstance::cleanupAfterRun() void LegacyInstance::cleanupAfterRun()
{ {
// FIXME: delete the launcher and icons and whatnot. // FIXME: delete the launcher and icons and whatnot.

View File

@ -109,9 +109,12 @@ public:
virtual bool shouldUpdate() const override; virtual bool shouldUpdate() const override;
virtual void setShouldUpdate(bool val) override; virtual void setShouldUpdate(bool val) override;
virtual std::shared_ptr<Task> doUpdate() override; virtual std::shared_ptr<Task> createUpdateTask() override;
virtual std::shared_ptr<BaseLauncher> createLaunchTask(AuthSessionPtr account) override;
virtual std::shared_ptr<Task> createJarModdingTask() override;
virtual std::shared_ptr<BaseLauncher> prepareForLaunch(AuthSessionPtr account) override;
virtual void cleanupAfterRun() override; virtual void cleanupAfterRun() override;
virtual QString getStatusbarDescription() override; virtual QString getStatusbarDescription() override;

View File

@ -349,7 +349,7 @@ void LegacyUpdate::jarStart()
LegacyInstance *inst = (LegacyInstance *)m_inst; LegacyInstance *inst = (LegacyInstance *)m_inst;
if (!inst->shouldUpdate() || inst->shouldUseCustomBaseJar()) if (!inst->shouldUpdate() || inst->shouldUseCustomBaseJar())
{ {
ModTheJar(); emitSucceeded();
return; return;
} }
@ -384,7 +384,7 @@ void LegacyUpdate::jarStart()
void LegacyUpdate::jarFinished() void LegacyUpdate::jarFinished()
{ {
// process the jar // process the jar
ModTheJar(); emitSucceeded();
} }
void LegacyUpdate::jarFailed(QString reason) void LegacyUpdate::jarFailed(QString reason)
@ -392,73 +392,3 @@ void LegacyUpdate::jarFailed(QString reason)
// bad, bad // bad, bad
emitFailed(tr("Failed to download the minecraft jar: %1.").arg(reason)); emitFailed(tr("Failed to download the minecraft jar: %1.").arg(reason));
} }
void LegacyUpdate::ModTheJar()
{
LegacyInstance *inst = (LegacyInstance *)m_inst;
if (!inst->shouldRebuild())
{
emitSucceeded();
return;
}
// Get the mod list
auto modList = inst->getJarMods();
QFileInfo runnableJar(inst->runnableJar());
QFileInfo baseJar(inst->baseJar());
bool base_is_custom = inst->shouldUseCustomBaseJar();
// Nothing to do if there are no jar mods to install, no backup and just the mc jar
if (base_is_custom)
{
// yes, this can happen if the instance only has the runnable jar and not the base jar
// it *could* be assumed that such an instance is vanilla, but that wouldn't be safe
// because that's not something mmc4 guarantees
if (runnableJar.isFile() && !baseJar.exists() && modList.empty())
{
inst->setShouldRebuild(false);
emitSucceeded();
return;
}
setStatus(tr("Installing mods: Backing up minecraft.jar ..."));
if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath()))
{
emitFailed("It seems both the active and base jar are gone. A fresh base jar will "
"be used on next run.");
inst->setShouldRebuild(true);
inst->setShouldUpdate(true);
inst->setShouldUseCustomBaseJar(false);
return;
}
}
if (!baseJar.exists())
{
emitFailed("The base jar " + baseJar.filePath() + " does not exist");
return;
}
if (runnableJar.exists() && !QFile::remove(runnableJar.filePath()))
{
emitFailed("Failed to delete old minecraft.jar");
return;
}
setStatus(tr("Installing mods: Opening minecraft.jar ..."));
QString outputJarPath = runnableJar.filePath();
QString inputJarPath = baseJar.filePath();
if(!MMCZip::createModdedJar(inputJarPath, outputJarPath, modList))
{
emitFailed(tr("Failed to create the custom Minecraft jar file."));
return;
}
inst->setShouldRebuild(false);
// inst->UpdateVersion(true);
emitSucceeded();
return;
}

View File

@ -51,8 +51,6 @@ slots:
void extractLwjgl(); void extractLwjgl();
void ModTheJar();
private: private:
std::shared_ptr<QNetworkReply> m_reply; std::shared_ptr<QNetworkReply> m_reply;

View File

@ -56,7 +56,7 @@ QSet<QString> OneSixInstance::traits()
return version->traits; return version->traits;
} }
std::shared_ptr<Task> OneSixInstance::doUpdate() std::shared_ptr<Task> OneSixInstance::createUpdateTask()
{ {
return std::shared_ptr<Task>(new OneSixUpdate(this)); return std::shared_ptr<Task>(new OneSixUpdate(this));
} }
@ -123,7 +123,7 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
return parts; return parts;
} }
std::shared_ptr<BaseLauncher> OneSixInstance::prepareForLaunch(AuthSessionPtr session) std::shared_ptr<BaseLauncher> OneSixInstance::createLaunchTask(AuthSessionPtr session)
{ {
QString launchScript; QString launchScript;
QIcon icon = ENV.icons()->getIcon(iconKey()); QIcon icon = ENV.icons()->getIcon(iconKey());
@ -237,6 +237,64 @@ std::shared_ptr<BaseLauncher> OneSixInstance::prepareForLaunch(AuthSessionPtr se
return process; return process;
} }
std::shared_ptr<Task> OneSixInstance::createJarModdingTask()
{
class JarModTask : public Task
{
public:
explicit JarModTask(std::shared_ptr<OneSixInstance> inst) : m_inst(inst), Task(nullptr)
{
}
virtual void executeTask()
{
std::shared_ptr<MinecraftProfile> version = m_inst->getMinecraftProfile();
// nuke obsolete stripped jar(s) if needed
QString version_id = version->id;
QString strippedPath = version_id + "/" + version_id + "-stripped.jar";
QFile strippedJar(strippedPath);
if(strippedJar.exists())
{
strippedJar.remove();
}
auto tempJarPath = QDir(m_inst->instanceRoot()).absoluteFilePath("temp.jar");
QFile tempJar(tempJarPath);
if(tempJar.exists())
{
tempJar.remove();
}
auto finalJarPath = QDir(m_inst->instanceRoot()).absoluteFilePath("minecraft.jar");
QFile finalJar(finalJarPath);
if(finalJar.exists())
{
if(!finalJar.remove())
{
emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath));
return;
}
}
// create temporary modded jar, if needed
auto jarMods = m_inst->getJarMods();
if(jarMods.size())
{
auto sourceJarPath = m_inst->versionsPath().absoluteFilePath(version->id + "/" + version->id + ".jar");
QString localPath = version_id + "/" + version_id + ".jar";
auto metacache = ENV.metacache();
auto entry = metacache->resolveEntry("versions", localPath);
QString fullJarPath = entry->getFullPath();
if(!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods))
{
emitFailed(tr("Failed to create the custom Minecraft jar file."));
return;
}
}
emitSucceeded();
}
std::shared_ptr<OneSixInstance> m_inst;
};
return std::make_shared<JarModTask>(std::dynamic_pointer_cast<OneSixInstance>(shared_from_this()));
}
void OneSixInstance::cleanupAfterRun() void OneSixInstance::cleanupAfterRun()
{ {
QString target_dir = PathCombine(instanceRoot(), "natives/"); QString target_dir = PathCombine(instanceRoot(), "natives/");

View File

@ -48,8 +48,9 @@ public:
QString libDir() const; QString libDir() const;
virtual QString instanceConfigFolder() const override; virtual QString instanceConfigFolder() const override;
virtual std::shared_ptr<Task> doUpdate() override; virtual std::shared_ptr<Task> createUpdateTask() override;
virtual std::shared_ptr<BaseLauncher> prepareForLaunch(AuthSessionPtr account) override; virtual std::shared_ptr<BaseLauncher> createLaunchTask(AuthSessionPtr account) override;
virtual std::shared_ptr<Task> createJarModdingTask() override;
virtual void cleanupAfterRun() override; virtual void cleanupAfterRun() override;

View File

@ -296,46 +296,6 @@ void OneSixUpdate::jarlibFinished()
OneSixInstance *inst = (OneSixInstance *)m_inst; OneSixInstance *inst = (OneSixInstance *)m_inst;
std::shared_ptr<MinecraftProfile> version = inst->getMinecraftProfile(); std::shared_ptr<MinecraftProfile> version = inst->getMinecraftProfile();
// nuke obsolete stripped jar(s) if needed
QString version_id = version->id;
QString strippedPath = version_id + "/" + version_id + "-stripped.jar";
QFile strippedJar(strippedPath);
if(strippedJar.exists())
{
strippedJar.remove();
}
auto tempJarPath = QDir(m_inst->instanceRoot()).absoluteFilePath("temp.jar");
QFile tempJar(tempJarPath);
if(tempJar.exists())
{
tempJar.remove();
}
auto finalJarPath = QDir(m_inst->instanceRoot()).absoluteFilePath("minecraft.jar");
QFile finalJar(finalJarPath);
if(finalJar.exists())
{
if(!finalJar.remove())
{
emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath));
return;
}
}
// create temporary modded jar, if needed
auto jarMods = inst->getJarMods();
if(jarMods.size())
{
auto sourceJarPath = m_inst->versionsPath().absoluteFilePath(version->id + "/" + version->id + ".jar");
QString localPath = version_id + "/" + version_id + ".jar";
auto metacache = ENV.metacache();
auto entry = metacache->resolveEntry("versions", localPath);
QString fullJarPath = entry->getFullPath();
if(!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods))
{
emitFailed(tr("Failed to create the custom Minecraft jar file."));
return;
}
}
if (version->traits.contains("legacyFML")) if (version->traits.contains("legacyFML"))
{ {
fmllibsStart(); fmllibsStart();