summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--data/minecraftprocess.cpp214
-rw-r--r--data/minecraftprocess.h94
-rw-r--r--libinstance/include/instance.h3
-rw-r--r--libinstance/src/instance.cpp8
-rw-r--r--libutil/include/siglist.h2
-rw-r--r--main.cpp119
-rw-r--r--multimc.qrc2
8 files changed, 425 insertions, 20 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 272250ad..dceb4615 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -123,6 +123,7 @@ IF(DEFINED MultiMC_BUILD_TAG)
MESSAGE(STATUS "Build tag: ${MultiMC_BUILD_TAG}")
ELSE()
MESSAGE(STATUS "No build tag specified.")
+ SET(MultiMC_BUILD_TAG custom)
ENDIF()
# Architecture detection
@@ -168,6 +169,7 @@ gui/aboutdialog.h
data/version.h
data/userinfo.h
data/loginresponse.h
+data/minecraftprocess.h
data/plugin/pluginmanager.h
@@ -195,6 +197,7 @@ data/userinfo.cpp
data/loginresponse.cpp
data/plugin/pluginmanager.cpp
+data/minecraftprocess.cpp
gui/mainwindow.cpp
gui/modeditwindow.cpp
diff --git a/data/minecraftprocess.cpp b/data/minecraftprocess.cpp
new file mode 100644
index 00000000..4cbe7208
--- /dev/null
+++ b/data/minecraftprocess.cpp
@@ -0,0 +1,214 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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 "minecraftprocess.h"
+
+#include <QDataStream>
+#include <QFile>
+#include <QDir>
+#include <QImage>
+#include <QProcessEnvironment>
+
+#include "instance.h"
+
+#include "osutils.h"
+#include "pathutils.h"
+
+#define LAUNCHER_FILE "MultiMCLauncher.jar"
+#define IBUS "@im=ibus"
+
+// commandline splitter
+QStringList MinecraftProcess::splitArgs(QString args)
+{
+ QStringList argv;
+ QString current;
+ bool escape = false;
+ QChar inquotes;
+ for (int i=0; i<args.length(); i++)
+ {
+ QChar cchar = args.at(i);
+
+ // \ escaped
+ if (escape) {
+ current += cchar;
+ escape = false;
+ // in "quotes"
+ } else if (!inquotes.isNull()) {
+ if (cchar == 0x5C)
+ escape = true;
+ else if (cchar == inquotes)
+ inquotes = 0;
+ else
+ current += cchar;
+ // otherwise
+ } else {
+ if (cchar == 0x20) {
+ if (!current.isEmpty()) {
+ argv << current;
+ current.clear();
+ }
+ } else if (cchar == 0x22 || cchar == 0x27)
+ inquotes = cchar;
+ else
+ current += cchar;
+ }
+ }
+ if (!current.isEmpty())
+ argv << current;
+ return argv;
+}
+
+// prepare tools
+inline void MinecraftProcess::extractIcon(InstancePtr inst, QString destination)
+{
+ QImage(":/icons/instances/" + inst->iconKey()).save(destination);
+}
+
+inline void MinecraftProcess::extractLauncher(QString destination)
+{
+ QFile(":/launcher/launcher.jar").copy(destination);
+}
+
+void MinecraftProcess::prepare(InstancePtr inst)
+{
+ extractLauncher(PathCombine(inst->minecraftDir(), LAUNCHER_FILE));
+ extractIcon(inst, PathCombine(inst->minecraftDir(), "icon.png"));
+}
+
+// constructor
+MinecraftProcess::MinecraftProcess(InstancePtr inst, QString user, QString session, ConsoleWindow *console) :
+ m_instance(inst), m_user(user), m_session(session), m_console(console)
+{
+ connect(this, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(finish(int, QProcess::ExitStatus)));
+
+ // prepare the process environment
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+
+#ifdef LINUX
+ // Strip IBus
+ if (env.value("XMODIFIERS").contains(IBUS))
+ env.insert("XMODIFIERS", env.value("XMODIFIERS").replace(IBUS, ""));
+#endif
+
+ // export some infos
+ env.insert("INST_NAME", inst->name());
+ env.insert("INST_ID", inst->id());
+ env.insert("INST_DIR", QDir(inst->rootDir()).absolutePath());
+
+ this->setProcessEnvironment(env);
+ m_prepostlaunchprocess.setProcessEnvironment(env);
+
+ // set the cwd
+ QDir mcDir(inst->minecraftDir());
+ this->setWorkingDirectory(mcDir.absolutePath());
+ m_prepostlaunchprocess.setWorkingDirectory(mcDir.absolutePath());
+
+ //TODO: do console redirection
+}
+
+void MinecraftProcess::finish(int code, ExitStatus status)
+{
+ if (status != NormalExit)
+ {
+ //TODO: error handling
+ }
+
+ m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code));
+
+ // run post-exit
+ if (!m_instance->getPostExitCommand().isEmpty())
+ {
+ m_prepostlaunchprocess.start(m_instance->getPostExitCommand());
+ m_prepostlaunchprocess.waitForFinished();
+ if (m_prepostlaunchprocess.exitStatus() != NormalExit)
+ {
+ //TODO: error handling
+ }
+ }
+
+ emit ended();
+}
+
+void MinecraftProcess::launch()
+{
+ if (!m_instance->getPreLaunchCommand().isEmpty())
+ {
+ m_prepostlaunchprocess.start(m_instance->getPreLaunchCommand());
+ m_prepostlaunchprocess.waitForFinished();
+ if (m_prepostlaunchprocess.exitStatus() != NormalExit)
+ {
+ //TODO: error handling
+ return;
+ }
+ }
+
+ m_instance->setLastLaunch();
+
+ prepare(m_instance);
+
+ genArgs();
+
+ qDebug("Minecraft folder is: '%s'", qPrintable(workingDirectory()));
+ qDebug("Instance launched with arguments: '%s'", qPrintable(m_arguments.join("' '")));
+
+ start(m_instance->getJavaPath(), m_arguments);
+ if (!waitForStarted())
+ {
+ //TODO: error handling
+ }
+}
+
+void MinecraftProcess::genArgs()
+{
+ // start fresh
+ m_arguments.clear();
+
+ // window size
+ QString windowSize;
+ if (m_instance->getLaunchMaximized())
+ windowSize = "max";
+ else
+ windowSize = QString("%1x%2").arg(m_instance->getMinecraftWinWidth()).arg(m_instance->getMinecraftWinHeight());
+
+ // window title
+ QString windowTitle;
+ windowTitle.append("MultiMC: ").append(m_instance->name());
+
+ // Java arguments
+ m_arguments.append(splitArgs(m_instance->getJvmArgs()));
+
+#ifdef OSX
+ // OSX dock icon and name
+ m_arguments << "-Xdock:icon=icon.png";
+ m_arguments << QString("-Xdock:name=\"%1\"").arg(windowTitle);
+#endif
+
+ // lwjgl
+ QString lwjgl = m_instance->lwjglVersion();
+ if (lwjgl != "Mojang")
+ lwjgl = QDir(settings->getLWJGLDir() + "/" + lwjgl).absolutePath();
+
+ // launcher arguments
+ m_arguments << QString("-Xms%1m").arg(m_instance->getMinMemAlloc());
+ m_arguments << QString("-Xmx%1m").arg(m_instance->getMaxMemAlloc());
+ m_arguments << "-jar" << LAUNCHER_FILE;
+ m_arguments << m_user;
+ m_arguments << m_session;
+ m_arguments << windowTitle;
+ m_arguments << windowSize;
+ m_arguments << lwjgl;
+}
diff --git a/data/minecraftprocess.h b/data/minecraftprocess.h
new file mode 100644
index 00000000..99a3bed6
--- /dev/null
+++ b/data/minecraftprocess.h
@@ -0,0 +1,94 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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.
+ */
+#ifndef MINECRAFTPROCESS_H
+#define MINECRAFTPROCESS_H
+
+#include <QProcess>
+
+class ConsoleWindow;
+
+#include "instance.h"
+
+/**
+ * @file data/minecraftprocess.h
+ * @brief The MinecraftProcess class
+ */
+class MinecraftProcess : public QProcess
+{
+ Q_OBJECT
+public:
+ /**
+ * @brief MinecraftProcess constructor
+ * @param inst the Instance pointer to launch
+ * @param user the minecraft username
+ * @param session the minecraft session id
+ * @param console the instance console window
+ */
+ MinecraftProcess(InstancePtr inst, QString user, QString session, ConsoleWindow *console);
+
+ /**
+ * @brief launch minecraft
+ */
+ void launch();
+
+ /**
+ * @brief extract the instance icon
+ * @param inst the instance
+ * @param destination the destination path
+ */
+ static inline void extractIcon(InstancePtr inst, QString destination);
+
+ /**
+ * @brief extract the MultiMC launcher.jar
+ * @param destination the destination path
+ */
+ static inline void extractLauncher(QString destination);
+
+ /**
+ * @brief prepare the launch by extracting icon and launcher
+ * @param inst the instance
+ */
+ static void prepare(InstancePtr inst);
+
+ /**
+ * @brief split a string into argv items like a shell would do
+ * @param args the argument string
+ * @return a QStringList containing all arguments
+ */
+ static QStringList splitArgs(QString args);
+
+signals:
+ /**
+ * @brief emitted when mc has finished and the PostLaunchCommand was run
+ */
+ void ended();
+
+protected:
+ ConsoleWindow *m_console;
+ InstancePtr m_instance;
+ QString m_user;
+ QString m_session;
+ QProcess m_prepostlaunchprocess;
+ QStringList m_arguments;
+
+ void genArgs();
+
+protected slots:
+ void finish(int, QProcess::ExitStatus status);
+};
+
+#endif // MINECRAFTPROCESS_H
diff --git a/libinstance/include/instance.h b/libinstance/include/instance.h
index 7b3c001d..995b88d8 100644
--- a/libinstance/include/instance.h
+++ b/libinstance/include/instance.h
@@ -339,4 +339,7 @@ private:
QString m_rootDir;
};
+// pointer for lazy people
+typedef QSharedPointer<Instance> InstancePtr;
+
#endif // INSTANCE_H
diff --git a/libinstance/src/instance.cpp b/libinstance/src/instance.cpp
index c79c0213..c10286b6 100644
--- a/libinstance/src/instance.cpp
+++ b/libinstance/src/instance.cpp
@@ -50,13 +50,9 @@ QString Instance::minecraftDir() const
QFileInfo dotMCDir(PathCombine(rootDir(), ".minecraft"));
if (dotMCDir.exists() && !mcDir.exists())
- {
- return dotMCDir.path();
- }
+ return dotMCDir.filePath();
else
- {
- return mcDir.path();
- }
+ return mcDir.filePath();
}
QString Instance::binDir() const
diff --git a/libutil/include/siglist.h b/libutil/include/siglist.h
index dcae7c04..24b1a889 100644
--- a/libutil/include/siglist.h
+++ b/libutil/include/siglist.h
@@ -71,7 +71,7 @@ public:
virtual QList<T> &operator =(const QList<T> &other);
-
+protected:
// Signal emitted after an item is added to the list.
// Contains a reference to item and the item's new index.
virtual void onItemAdded(const T &item, int index) = 0;
diff --git a/main.cpp b/main.cpp
index 4a4d8e7e..4f072b38 100644
--- a/main.cpp
+++ b/main.cpp
@@ -22,9 +22,14 @@
#include <QDir>
#include "gui/mainwindow.h"
+#include "gui/logindialog.h"
+#include "gui/taskdialog.h"
+#include "instancelist.h"
#include "appsettings.h"
#include "data/loginresponse.h"
+#include "tasks/logintask.h"
+#include "data/minecraftprocess.h"
#include "data/plugin/pluginmanager.h"
@@ -35,6 +40,98 @@
using namespace Util::Commandline;
+// Commandline instance launcher
+class InstanceLauncher : public QObject
+{
+ Q_OBJECT
+private:
+ InstanceList instances;
+ QString instId;
+ InstancePtr instance;
+ MinecraftProcess *proc;
+public:
+ InstanceLauncher(QString instId) : QObject(), instances(settings->getInstanceDir())
+ {
+ this->instId = instId;
+ }
+
+private:
+ InstancePtr findInstance(QString instId)
+ {
+ QListIterator<InstancePtr> iter(instances);
+ InstancePtr inst;
+ while(iter.hasNext())
+ {
+ inst = iter.next();
+ if (inst->id() == instId)
+ break;
+ }
+ if (inst->id() != instId)
+ return InstancePtr();
+ else
+ return iter.peekPrevious();
+ }
+
+private slots:
+ void onTerminated()
+ {
+ std::cout << "Minecraft exited" << std::endl;
+ QApplication::instance()->quit();
+ }
+
+ void onLoginComplete(LoginResponse response)
+ {
+ // TODO: console
+ proc = new MinecraftProcess(instance, response.getUsername(), response.getSessionID(), nullptr);
+ connect(proc, SIGNAL(ended()), SLOT(onTerminated()));
+ proc->launch();
+ /*if (proc->pid() == 0)
+ {
+ std::cout << "Could not start instance." << std::endl;
+ QApplication::instance()->quit();
+ return;
+ }*/
+ }
+
+ void doLogin(const QString &errorMsg)
+ {
+ LoginDialog* loginDlg = new LoginDialog(nullptr, errorMsg);
+ if (loginDlg->exec())
+ {
+ UserInfo uInfo(loginDlg->getUsername(), loginDlg->getPassword());
+
+ TaskDialog* tDialog = new TaskDialog(nullptr);
+ LoginTask* loginTask = new LoginTask(uInfo, tDialog);
+ connect(loginTask, SIGNAL(loginComplete(LoginResponse)),
+ SLOT(onLoginComplete(LoginResponse)), Qt::QueuedConnection);
+ connect(loginTask, SIGNAL(loginFailed(QString)),
+ SLOT(doLogin(QString)), Qt::QueuedConnection);
+ tDialog->exec(loginTask);
+ }
+ //onLoginComplete(LoginResponse("Offline","Offline", 1));
+ }
+
+public:
+ int launch()
+ {
+ std::cout << "Loading Instances..." << std::endl;
+ instances.loadList();
+
+ std::cout << "Launching Instance '" << qPrintable(instId) << "'" << std::endl;
+ instance = findInstance(instId);
+ if (instance.isNull())
+ {
+ std::cout << "Could not find instance requested. note that you have to specify the ID, not the NAME" << std::endl;
+ return 1;
+ }
+
+ std::cout << "Logging in..." << std::endl;
+ doLogin("");
+
+ return QApplication::instance()->exec();
+ }
+};
+
int main(int argc, char *argv[])
{
// initialize Qt
@@ -44,7 +141,7 @@ int main(int argc, char *argv[])
// Print app header
std::cout << "MultiMC 5" << std::endl;
- std::cout << "(c) 2013 MultiMC contributors" << std::endl << std::endl;
+ std::cout << "(c) 2013 MultiMC Contributors" << std::endl << std::endl;
// Commandline parsing
Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
@@ -92,7 +189,9 @@ int main(int argc, char *argv[])
// display version and exit
if (args["version"].toBool()) {
- std::cout << VERSION_STR << " " << JENKINS_BUILD_TAG << " " << (ARCH==x64?"x86_64":"x86") << std::endl;
+ std::cout << "Version " << VERSION_STR << std::endl;
+ std::cout << "Git " << GIT_COMMIT << std::endl;
+ std::cout << "Tag: " << JENKINS_BUILD_TAG << " " << (ARCH==x64?"x86_64":"x86") << std::endl;
return 0;
}
@@ -113,16 +212,6 @@ int main(int argc, char *argv[])
// change directory
QDir::setCurrent(args["dir"].toString());
- // launch instance.
- if (!args["launch"].isNull())
- {
- std::cout << "Launching instance: " << qPrintable(args["launch"].toString()) << std::endl;
- // TODO: make it launch the an instance.
- // needs the new instance model to be complete
- std::cerr << "Launching Instances is not implemented yet!" << std::endl;
- return 255;
- }
-
// load settings
settings = new AppSettings(&app);
@@ -133,6 +222,10 @@ int main(int argc, char *argv[])
PluginManager::get().loadPlugins(PathCombine(qApp->applicationDirPath(), "plugins"));
PluginManager::get().initInstanceTypes();
+ // launch instance.
+ if (!args["launch"].isNull())
+ return InstanceLauncher(args["launch"].toString()).launch();
+
// show main window
MainWindow mainWin;
mainWin.show();
@@ -140,3 +233,5 @@ int main(int argc, char *argv[])
// loop
return app.exec();
}
+
+#include "main.moc"
diff --git a/multimc.qrc b/multimc.qrc
index 7008206b..cfcc9829 100644
--- a/multimc.qrc
+++ b/multimc.qrc
@@ -28,7 +28,7 @@
<file alias="infinity">resources/icons/multimc.svg</file>
</qresource>
<qresource prefix="/launcher">
- <file alias="launcherjar">resources/MultiMCLauncher.jar</file>
+ <file alias="launcher.jar">resources/MultiMCLauncher.jar</file>
</qresource>
<qresource prefix="/icons/multimc">
<file alias="scalable/apps/multimc.svg">resources/icons/multimc.svg</file>