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
# Processes
LaunchController.h
LaunchController.cpp
LaunchInteraction.h
LaunchInteraction.cpp
# page provider for instances
InstancePageProvider.h

View File

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

View File

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

View File

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

View File

@ -135,13 +135,21 @@ public:
virtual SettingsObjectPtr settings() const;
/// 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.
virtual std::shared_ptr<BaseLauncher> prepareForLaunch(AuthSessionPtr account) = 0;
/// returns a valid launcher (task container)
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 QString getStatusbarDescription() = 0;

View File

@ -421,7 +421,7 @@ void BaseLauncher::on_pre_state(LoggedProcess::State state)
void BaseLauncher::updateInstance()
{
m_updateTask = m_instance->doUpdate();
m_updateTask = m_instance->createUpdateTask();
if(m_updateTask)
{
connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
@ -435,7 +435,7 @@ void BaseLauncher::updateFinished()
{
if(m_updateTask->successful())
{
makeReady();
doJarModding();
}
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()
{
QStringList args = javaArguments();

View File

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

View File

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

View File

@ -134,9 +134,9 @@ QString OneSixFTBInstance::getStatusbarDescription()
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"

View File

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

View File

@ -26,6 +26,7 @@
#include "icons/IconList.h"
#include "BaseLauncher.h"
#include "minecraft/ModList.h"
#include <MMCZip.h>
LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &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.
auto list = jarModList();
@ -95,7 +96,7 @@ std::shared_ptr<Task> LegacyInstance::doUpdate()
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;
QIcon icon = ENV.icons()->getIcon(iconKey());
@ -129,6 +130,87 @@ std::shared_ptr<BaseLauncher> LegacyInstance::prepareForLaunch(AuthSessionPtr ac
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()
{
// FIXME: delete the launcher and icons and whatnot.

View File

@ -109,9 +109,12 @@ public:
virtual bool shouldUpdate() const 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 QString getStatusbarDescription() override;

View File

@ -349,7 +349,7 @@ void LegacyUpdate::jarStart()
LegacyInstance *inst = (LegacyInstance *)m_inst;
if (!inst->shouldUpdate() || inst->shouldUseCustomBaseJar())
{
ModTheJar();
emitSucceeded();
return;
}
@ -384,7 +384,7 @@ void LegacyUpdate::jarStart()
void LegacyUpdate::jarFinished()
{
// process the jar
ModTheJar();
emitSucceeded();
}
void LegacyUpdate::jarFailed(QString reason)
@ -392,73 +392,3 @@ void LegacyUpdate::jarFailed(QString reason)
// bad, bad
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 ModTheJar();
private:
std::shared_ptr<QNetworkReply> m_reply;

View File

@ -56,7 +56,7 @@ QSet<QString> OneSixInstance::traits()
return version->traits;
}
std::shared_ptr<Task> OneSixInstance::doUpdate()
std::shared_ptr<Task> OneSixInstance::createUpdateTask()
{
return std::shared_ptr<Task>(new OneSixUpdate(this));
}
@ -123,7 +123,7 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
return parts;
}
std::shared_ptr<BaseLauncher> OneSixInstance::prepareForLaunch(AuthSessionPtr session)
std::shared_ptr<BaseLauncher> OneSixInstance::createLaunchTask(AuthSessionPtr session)
{
QString launchScript;
QIcon icon = ENV.icons()->getIcon(iconKey());
@ -237,6 +237,64 @@ std::shared_ptr<BaseLauncher> OneSixInstance::prepareForLaunch(AuthSessionPtr se
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()
{
QString target_dir = PathCombine(instanceRoot(), "natives/");

View File

@ -48,8 +48,9 @@ public:
QString libDir() const;
virtual QString instanceConfigFolder() const override;
virtual std::shared_ptr<Task> doUpdate() override;
virtual std::shared_ptr<BaseLauncher> prepareForLaunch(AuthSessionPtr account) 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 void cleanupAfterRun() override;

View File

@ -296,46 +296,6 @@ void OneSixUpdate::jarlibFinished()
OneSixInstance *inst = (OneSixInstance *)m_inst;
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"))
{
fmllibsStart();