summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--gui/mainwindow.cpp8
-rw-r--r--gui/newinstancedialog.cpp81
-rw-r--r--gui/newinstancedialog.h5
-rw-r--r--gui/newinstancedialog.ui36
-rw-r--r--libmultimc/CMakeLists.txt14
-rw-r--r--libmultimc/include/instance.h26
-rw-r--r--libmultimc/include/instanceloader.h66
-rw-r--r--libmultimc/include/instancetypeinterface.h4
-rw-r--r--libmultimc/include/instversion.h86
-rw-r--r--libmultimc/include/instversionlist.h17
-rw-r--r--libmultimc/include/minecraftversion.h95
-rw-r--r--libmultimc/include/minecraftversionlist.h105
-rw-r--r--libmultimc/src/instance.cpp34
-rw-r--r--libmultimc/src/instancelist.cpp10
-rw-r--r--libmultimc/src/instanceloader.cpp78
-rw-r--r--libmultimc/src/instversion.cpp37
-rw-r--r--libmultimc/src/minecraftversion.cpp142
-rw-r--r--libmultimc/src/minecraftversionlist.cpp489
-rw-r--r--main.cpp6
20 files changed, 1064 insertions, 279 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 032c9265..523d55c9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -75,10 +75,6 @@ include_directories(${LIBMULTIMC_INCLUDE_DIR})
add_subdirectory(libgroupview)
include_directories(${LIBGROUPVIEW_INCLUDE_DIR})
-# Add the stdinstance plugin.
-add_subdirectory(plugins/stdinstance)
-
-
################################ SET UP BUILD OPTIONS ################################
diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp
index 7f05c745..6f99c18b 100644
--- a/gui/mainwindow.cpp
+++ b/gui/mainwindow.cpp
@@ -155,8 +155,8 @@ void MainWindow::on_actionAddInstance_triggered()
QString instDir = PathCombine(globalSettings->get("InstanceDir").toString(),
instDirName);
- InstanceLoader::InstTypeError error = InstanceLoader::get().
- createInstance(newInstance, newInstDlg->selectedType(), instDir);
+ InstanceLoader::InstLoaderError error = InstanceLoader::get().
+ createInstance(newInstance, instDir);
if (error == InstanceLoader::NoError)
{
@@ -170,10 +170,6 @@ void MainWindow::on_actionAddInstance_triggered()
switch (error)
{
- case InstanceLoader::TypeNotRegistered:
- errorMsg += "Instance type not found.";
- break;
-
case InstanceLoader::InstExists:
errorMsg += "An instance with the given directory name already exists.";
break;
diff --git a/gui/newinstancedialog.cpp b/gui/newinstancedialog.cpp
index 3cbfabb0..2034664d 100644
--- a/gui/newinstancedialog.cpp
+++ b/gui/newinstancedialog.cpp
@@ -30,6 +30,8 @@
#include <QLayout>
#include <QPushButton>
+#include <minecraftversionlist.h>
+
NewInstanceDialog::NewInstanceDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::NewInstanceDialog)
@@ -41,48 +43,25 @@ NewInstanceDialog::NewInstanceDialog(QWidget *parent) :
resize(minimumSizeHint());
layout()->setSizeConstraint(QLayout::SetFixedSize);
- loadTypeList();
-}
-
-NewInstanceDialog::~NewInstanceDialog()
-{
- delete ui;
-}
-
-void NewInstanceDialog::loadTypeList()
-{
- InstTypeList typeList = InstanceLoader::get().typeList();
-
- for (int i = 0; i < typeList.length(); i++)
+ if (!MinecraftVersionList::getMainList().isLoaded())
{
- ui->instTypeComboBox->addItem(typeList.at(i)->displayName(), typeList.at(i)->typeID());
+ TaskDialog *taskDlg = new TaskDialog(this);
+ Task *loadTask = MinecraftVersionList::getMainList().getLoadTask();
+ loadTask->setParent(taskDlg);
+ taskDlg->exec(loadTask);
}
-
- updateSelectedType();
+ setSelectedVersion(MinecraftVersionList::getMainList().getLatestStable());
}
-void NewInstanceDialog::updateSelectedType()
+NewInstanceDialog::~NewInstanceDialog()
{
- QString typeID = ui->instTypeComboBox->itemData(ui->instTypeComboBox->currentIndex()).toString();
-
- const InstanceTypeInterface *type = InstanceLoader::get().findType(typeID);
- m_selectedType = type;
-
- updateDialogState();
-
- if (m_selectedType)
- {
- if (!m_selectedType->versionList()->isLoaded())
- loadVersionList();
-
- setSelectedVersion(m_selectedType->versionList()->getLatestStable());
- }
+ delete ui;
}
void NewInstanceDialog::updateDialogState()
{
- ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_selectedType && m_selectedVersion);
- ui->btnChangeVersion->setEnabled(m_selectedType && m_selectedVersion);
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(
+ !instName().isEmpty() && m_selectedVersion);
}
void NewInstanceDialog::setSelectedVersion(const InstVersion *version)
@@ -101,19 +80,6 @@ void NewInstanceDialog::setSelectedVersion(const InstVersion *version)
updateDialogState();
}
-void NewInstanceDialog::loadVersionList()
-{
- if (!m_selectedType)
- return;
-
- TaskDialog *taskDlg = new TaskDialog(this);
- Task *loadTask = m_selectedType->versionList()->getLoadTask();
- loadTask->setParent(taskDlg);
- taskDlg->exec(loadTask);
-
- setSelectedVersion(m_selectedType->versionList()->getLatestStable());
-}
-
QString NewInstanceDialog::instName() const
{
return ui->instNameTextBox->text();
@@ -125,11 +91,6 @@ QString NewInstanceDialog::iconKey() const
return "default";
}
-const InstanceTypeInterface *NewInstanceDialog::selectedType() const
-{
- return m_selectedType;
-}
-
const InstVersion *NewInstanceDialog::selectedVersion() const
{
return m_selectedVersion;
@@ -137,19 +98,11 @@ const InstVersion *NewInstanceDialog::selectedVersion() const
void NewInstanceDialog::on_btnChangeVersion_clicked()
{
- if (m_selectedType)
+ VersionSelectDialog *vselect = new VersionSelectDialog(&MinecraftVersionList::getMainList(), this);
+ if (vselect->exec())
{
- VersionSelectDialog *vselect = new VersionSelectDialog(m_selectedType->versionList(), this);
- if (vselect->exec())
- {
- const InstVersion *version = vselect->selectedVersion();
- if (version)
- setSelectedVersion(version);
- }
+ const InstVersion *version = vselect->selectedVersion();
+ if (version)
+ setSelectedVersion(version);
}
}
-
-void NewInstanceDialog::on_instTypeComboBox_activated(int index)
-{
- updateSelectedType();
-}
diff --git a/gui/newinstancedialog.h b/gui/newinstancedialog.h
index 93d94575..ea730b3c 100644
--- a/gui/newinstancedialog.h
+++ b/gui/newinstancedialog.h
@@ -33,8 +33,6 @@ public:
explicit NewInstanceDialog(QWidget *parent = 0);
~NewInstanceDialog();
- void loadTypeList();
- void updateSelectedType();
void updateDialogState();
void setSelectedVersion(const InstVersion *version);
@@ -43,14 +41,11 @@ public:
QString instName() const;
QString iconKey() const;
- const InstanceTypeInterface *selectedType() const;
const InstVersion *selectedVersion() const;
private slots:
void on_btnChangeVersion_clicked();
- void on_instTypeComboBox_activated(int index);
-
private:
Ui::NewInstanceDialog *ui;
diff --git a/gui/newinstancedialog.ui b/gui/newinstancedialog.ui
index 6f96f167..540176d5 100644
--- a/gui/newinstancedialog.ui
+++ b/gui/newinstancedialog.ui
@@ -10,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>220</width>
- <height>230</height>
+ <height>234</height>
</rect>
</property>
<property name="windowTitle">
@@ -76,27 +76,6 @@
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <widget class="QLabel" name="typeLabel">
- <property name="text">
- <string>Type:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="instTypeComboBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -129,6 +108,19 @@
</layout>
</item>
<item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
diff --git a/libmultimc/CMakeLists.txt b/libmultimc/CMakeLists.txt
index 06102d9f..4ffa3173 100644
--- a/libmultimc/CMakeLists.txt
+++ b/libmultimc/CMakeLists.txt
@@ -5,6 +5,7 @@ set(CMAKE_AUTOMOC ON)
# Find Qt
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
+find_package(Qt5Xml REQUIRED)
# Include Qt headers.
include_directories(${Qt5Base_INCLUDE_DIRS})
@@ -28,10 +29,8 @@ include/instanceloader.h
include/instversion.h
include/instversionlist.h
-
-# Plugin Stuff
-include/pluginmanager.h
-include/instancetypeinterface.h
+include/minecraftversion.h
+include/minecraftversionlist.h
# Tasks
@@ -60,9 +59,8 @@ src/instanceloader.cpp
src/instversion.cpp
src/instversionlist.cpp
-
-# Plugin Stuff
-src/pluginmanager.cpp
+src/minecraftversion.cpp
+src/minecraftversionlist.cpp
# Tasks
@@ -92,5 +90,5 @@ include_directories(${CMAKE_BINARY_DIR}/include)
add_definitions(-DLIBMULTIMC_LIBRARY)
add_library(libMultiMC SHARED ${LIBINST_SOURCES} ${LIBINST_HEADERS})
-qt5_use_modules(libMultiMC Core Network)
+qt5_use_modules(libMultiMC Core Network Xml)
target_link_libraries(libMultiMC libUtil libSettings)
diff --git a/libmultimc/include/instance.h b/libmultimc/include/instance.h
index 258a0dab..e72a0be3 100644
--- a/libmultimc/include/instance.h
+++ b/libmultimc/include/instance.h
@@ -103,6 +103,13 @@ class LIBMULTIMC_EXPORT Instance : public QObject
*/
Q_PROPERTY(qint64 lastLaunch READ lastLaunch WRITE setLastLaunch)
+ /*!
+ * Gets the last time that the current version was checked.
+ * This is checked against the last modified time on the jar file to see if
+ * the current version needs to be checked again.
+ */
+ Q_PROPERTY(qint64 lastCurrentVersionUpdate READ lastCurrentVersionUpdate WRITE setLastCurrentVersionUpdate)
+
// Dirs
@@ -225,6 +232,9 @@ public:
emit propertiesChanged(this);
}
+ virtual qint64 lastCurrentVersionUpdate() { return settings().get("lastVersionUpdate").value<qint64>(); }
+ virtual void setLastCurrentVersionUpdate(qint64 val) { settings().set("lastVersionUpdate", val); }
+
////// Directories //////
QString minecraftDir() const;
@@ -250,17 +260,7 @@ public:
* \brief Gets a pointer to this instance's version list.
* \return A pointer to the available version list for this instance.
*/
- virtual InstVersionList *versionList() const = 0;
-
-
-
- //////// INSTANCE TYPE STUFF ////////
-
- /*!
- * \brief Returns a pointer to this instance's type.
- * \return A pointer to this instance's type interface.
- */
- virtual const InstanceTypeInterface *instanceType() const = 0;
+ virtual InstVersionList *versionList() const;
//////// OTHER FUNCTIONS ////////
@@ -274,7 +274,7 @@ public:
* stored in the instance config file against the last modified time of Minecraft.jar.
* \return True if updateCurrentVersion() should be called.
*/
- virtual bool shouldUpdateCurrentVersion() = 0;
+ virtual bool shouldUpdateCurrentVersion();
/*!
* \brief Updates the current version.
@@ -286,7 +286,7 @@ public:
* instance is loaded if shouldUpdateCurrentVersion returns true.
* \param keepCurrent If true, only the version timestamp will be updated.
*/
- virtual void updateCurrentVersion(bool keepCurrent = false) = 0;
+ virtual void updateCurrentVersion(bool keepCurrent = false);
//// Settings System ////
diff --git a/libmultimc/include/instanceloader.h b/libmultimc/include/instanceloader.h
index 3326d7d0..fd6d04d6 100644
--- a/libmultimc/include/instanceloader.h
+++ b/libmultimc/include/instanceloader.h
@@ -22,15 +22,10 @@
#include "libmmc_config.h"
-class InstanceTypeInterface;
class Instance;
-typedef QList<const InstanceTypeInterface *> InstTypeList;
-
/*!
- * \brief The InstanceLoader is a singleton that manages all of the instance types and handles loading and creating instances.
- * Instance types are registered with the instance loader through its registerInstType() function.
- * Creating instances is done through the InstanceLoader's createInstance() function. This function takes
+ * The InstanceLoader is a singleton that manages loading and creating instances.
*/
class LIBMULTIMC_EXPORT InstanceLoader : public QObject
{
@@ -46,94 +41,45 @@ public:
*
* - NoError indicates that no error occurred.
* - OtherError indicates that an unspecified error occurred.
- * - TypeIDExists is returned by registerInstanceType() if the ID of the type being registered already exists.
- * - TypeNotRegistered is returned by createInstance() and loadInstance() when the given type is not registered.
* - InstExists is returned by createInstance() if the given instance directory is already an instance.
* - NotAnInstance is returned by loadInstance() if the given instance directory is not a valid instance.
- * - WrongInstType is returned by loadInstance() if the given instance directory's type doesn't match the given type.
* - CantCreateDir is returned by createInstance( if the given instance directory can't be created.)
*/
- enum InstTypeError
+ enum InstLoaderError
{
NoError = 0,
OtherError,
- TypeIDExists,
-
- TypeNotRegistered,
InstExists,
NotAnInstance,
- WrongInstType,
CantCreateDir
};
/*!
- * \brief Registers the given InstanceType with the instance loader.
- *
- * \param type The InstanceType to register.
- * \return An InstTypeError error code.
- * - TypeIDExists if the given type's is already registered to another instance type.
- */
- InstTypeError registerInstanceType(InstanceTypeInterface *type);
-
- /*!
* \brief Creates an instance with the given type and stores it in inst.
*
* \param inst Pointer to store the created instance in.
* \param type The type of instance to create.
* \param instDir The instance's directory.
- * \return An InstTypeError error code.
- * - TypeNotRegistered if the given type is not registered with the InstanceLoader.
+ * \return An InstLoaderError error code.
* - InstExists if the given instance directory is already an instance.
* - CantCreateDir if the given instance directory cannot be created.
*/
- InstTypeError createInstance(Instance *&inst, const InstanceTypeInterface *type, const QString &instDir);
-
- /*!
- * \brief Loads an instance from the given directory.
- *
- * \param inst Pointer to store the loaded instance in.
- * \param type The type of instance to load.
- * \param instDir The instance's directory.
- * \return An InstTypeError error code.
- * - TypeNotRegistered if the given type is not registered with the InstanceLoader.
- * - NotAnInstance if the given instance directory isn't a valid instance.
- * - WrongInstType if the given instance directory's type isn't the same as the given type.
- */
- InstTypeError loadInstance(Instance *&inst, const InstanceTypeInterface *type, const QString &instDir);
+ InstLoaderError createInstance(Instance *&inst, const QString &instDir);
/*!
* \brief Loads an instance from the given directory.
* Checks the instance's INI file to figure out what the instance's type is first.
* \param inst Pointer to store the loaded instance in.
* \param instDir The instance's directory.
- * \return An InstTypeError error code.
- * - TypeNotRegistered if the instance's type is not registered with the InstanceLoader.
+ * \return An InstLoaderError error code.
* - NotAnInstance if the given instance directory isn't a valid instance.
*/
- InstTypeError loadInstance(Instance *&inst, const QString &instDir);
-
- /*!
- * \brief Finds an instance type with the given ID.
- * If one cannot be found, returns NULL.
- *
- * \param id The ID of the type to find.
- * \return The type with the given ID. NULL if none were found.
- */
- const InstanceTypeInterface *findType(const QString &id);
-
- /*!
- * \brief Gets a list of the registered instance types.
- *
- * \return A list of instance types.
- */
- InstTypeList typeList();
+ InstLoaderError loadInstance(Instance *&inst, const QString &instDir);
private:
InstanceLoader();
- QMap<QString, InstanceTypeInterface *> m_typeMap;
-
static InstanceLoader loader;
};
diff --git a/libmultimc/include/instancetypeinterface.h b/libmultimc/include/instancetypeinterface.h
index ba13f820..4fbc2593 100644
--- a/libmultimc/include/instancetypeinterface.h
+++ b/libmultimc/include/instancetypeinterface.h
@@ -75,7 +75,7 @@ protected:
* TypeNotRegistered if the given type is not registered with the InstanceLoader.
* InstExists if the given instance directory is already an instance.
*/
- virtual InstanceLoader::InstTypeError createInstance(Instance *&inst, const QString &instDir) const = 0;
+ virtual InstanceLoader::InstLoaderError createInstance(Instance *&inst, const QString &instDir) const = 0;
/*!
* \brief Loads an instance from the given directory.
@@ -86,7 +86,7 @@ protected:
* NotAnInstance if the given instance directory isn't a valid instance.
* WrongInstType if the given instance directory's type isn't an instance of this type.
*/
- virtual InstanceLoader::InstTypeError loadInstance(Instance *&inst, const QString &instDir) const = 0;
+ virtual InstanceLoader::InstLoaderError loadInstance(Instance *&inst, const QString &instDir) const = 0;
};
Q_DECLARE_INTERFACE(InstanceTypeInterface, InstanceTypeInterface_IID)
diff --git a/libmultimc/include/instversion.h b/libmultimc/include/instversion.h
index 9d13dbe4..e91e68ba 100644
--- a/libmultimc/include/instversion.h
+++ b/libmultimc/include/instversion.h
@@ -22,36 +22,92 @@
class InstVersionList;
+/*!
+ * An abstract base class for instance versions.
+ * InstVersions hold information about versions such as their names, identifiers,
+ * types, etc.
+ */
class LIBMULTIMC_EXPORT InstVersion : public QObject
{
Q_OBJECT
-public:
+
/*!
- * \brief Constructs a new InstVersion with the given parent.
- * The parent *must* be the InstVersionList that contains this InstVersion.
- * The InstVersion should be added to the list immediately after being created.
+ * A string used to identify this version in config files.
+ * This should be unique within the version list or shenanigans will occur.
+ */
+ Q_PROPERTY(QString descriptor READ descriptor CONSTANT)
+
+ /*!
+ * The name of this version as it is displayed to the user.
+ * For example: "1.5.1"
+ */
+ Q_PROPERTY(QString name READ name)
+
+ /*!
+ * The name of this version's type as it is displayed to the user.
+ * For example: "Latest Version", "Snapshot", or "MCNostalgia"
+ */
+ Q_PROPERTY(QString typeName READ typeName)
+
+ /*!
+ * Whether or not this is a meta version.
+ * Meta versions are not real versions, merely versions that act as aliases
+ * for other versions.
+ * For example: There could be a meta version called "Latest" that always
+ * points to the latest version. The user would pick this version and when
+ * a new version came out, it would point to the new one and update the instance
+ * automatically.
*/
- explicit InstVersion(InstVersionList *parent = 0);
+ Q_PROPERTY(bool isMeta READ isMeta)
- //! Gets the string used to identify this version in config files.
- virtual QString descriptor() const = 0;
/*!
- * \breif Returns this InstVersion's name.
- * This is displayed to the user in the GUI and is usually just the version number ("1.4.7"), for example.
+ * Gets the version's timestamp.
+ * This is primarily used for sorting versions in a list.
*/
- virtual QString name() const = 0;
+ Q_PROPERTY(qint64 timestamp READ timestamp)
+
+public:
/*!
- * \brief Returns this InstVersion's type name.
- * This is usually displayed to the user in the GUI and specifies what
- * kind of version this is. For example: it could be "Snapshot",
- * "Latest Version", "MCNostalgia", etc.
+ * \brief Constructs a new InstVersion with the given parent.
+ * The parent *must* be the InstVersionList that contains this InstVersion.
+ * The InstVersion will be added to the list immediately after being created.
*/
+ explicit InstVersion(const QString &descriptor,
+ const QString &name,
+ qint64 timestamp,
+ InstVersionList *parent = 0);
+
+ /*!
+ * Copy constructor.
+ * If the 'parent' parameter is not NULL, sets this version's parent to the
+ * specified object, rather than setting it to the same parent as the version
+ * we're copying from.
+ * \param other The version to copy.
+ * \param parent If not NULL, will be set as the new version object's parent.
+ */
+ InstVersion(const InstVersion &other, QObject *parent = 0);
+
+ virtual QString descriptor() const;
+ virtual QString name() const;
virtual QString typeName() const = 0;
+ virtual qint64 timestamp() const;
+ virtual bool isMeta() const;
- //! Returns the version list that this InstVersion is a part of.
virtual InstVersionList *versionList() const;
+
+ /*!
+ * Creates a copy of this version with a different parent.
+ * \param newParent The parent QObject of the copy.
+ * \return A new, identical copy of this version with the given parent set.
+ */
+ virtual InstVersion *copyVersion(InstVersionList *newParent) const = 0;
+
+protected:
+ QString m_descriptor;
+ QString m_name;
+ qint64 m_timestamp;
};
#endif // INSTVERSION_H
diff --git a/libmultimc/include/instversionlist.h b/libmultimc/include/instversionlist.h
index b5a9f254..1aabc5cf 100644
--- a/libmultimc/include/instversionlist.h
+++ b/libmultimc/include/instversionlist.h
@@ -49,7 +49,7 @@ public:
explicit InstVersionList(QObject *parent = 0);
/*!
- * \brief Gets a task that will reload the version list.
+ * \brief Gets a task that will reload the version islt.
* Simply execute the task to load the list.
* The task returned by this function should reset the model when it's done.
* \return A pointer to a task that reloads the version list.
@@ -87,6 +87,21 @@ public:
* By default, this is simply the first version in the list.
*/
virtual const InstVersion *getLatestStable();
+
+protected slots:
+ /*!
+ * Updates this list with the given list of versions.
+ * This is done by copying each version in the given list and inserting it
+ * into this one.
+ * We need to do this so that we can set the parents of the versions are set to this
+ * version list. This can't be done in the load task, because the versions the load
+ * task creates are on the load task's thread and Qt won't allow their parents
+ * to be set to something created on another thread.
+ * To get around that problem, we invoke this method on the GUI thread, which
+ * then copies the versions and sets their parents correctly.
+ * \param versions List of versions whose parents should be set.
+ */
+ virtual void updateListData(QList<InstVersion *> versions) = 0;
};
#endif // INSTVERSIONLIST_H
diff --git a/libmultimc/include/minecraftversion.h b/libmultimc/include/minecraftversion.h
new file mode 100644
index 00000000..e30582ac
--- /dev/null
+++ b/libmultimc/include/minecraftversion.h
@@ -0,0 +1,95 @@
+/* Copyright 2013 Andrew Okin
+ *
+ * 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 MINECRAFTVERSION_H
+#define MINECRAFTVERSION_H
+
+#include "libmmc_config.h"
+
+#include "instversion.h"
+
+class LIBMULTIMC_EXPORT MinecraftVersion : public InstVersion
+{
+ Q_OBJECT
+
+ /*!
+ * This version's type. Used internally to identify what kind of version this is.
+ */
+ Q_PROPERTY(VersionType versionType READ versionType WRITE setVersionType)
+
+ /*!
+ * The URL that this version will be downloaded from.
+ */
+ Q_PROPERTY(QString downloadURL READ downloadURL)
+
+ /*!
+ * ETag/MD5 Used to verify the integrity of the downloaded minecraft.jar.
+ */
+ Q_PROPERTY(QString etag READ etag)
+
+public:
+ explicit MinecraftVersion(QString descriptor,
+ QString name,
+ qint64 timestamp,
+ QString dlUrl,
+ QString etag,
+ InstVersionList *parent = 0);
+
+ /*!
+ * Creates a meta version that links to the given version.
+ * This is *NOT* a copy constructor.
+ * \param linkedVersion the version that the meta version will link to.
+ */
+ explicit MinecraftVersion(const MinecraftVersion *linkedVersion);
+
+ MinecraftVersion(const MinecraftVersion &other, QObject *parent);
+
+ static InstVersion *mcnVersion(QString rawName, QString niceName);
+
+ enum VersionType
+ {
+ OldSnapshot,
+ Stable,
+ CurrentStable,
+ Snapshot,
+ MCNostalgia,
+ MetaCustom,
+ MetaLatestSnapshot,
+ MetaLatestStable
+ };
+
+ virtual QString descriptor() const;
+ virtual QString name() const;
+ virtual QString typeName() const;
+ virtual qint64 timestamp() const;
+
+ virtual VersionType versionType() const;
+ virtual void setVersionType(VersionType typeName);
+
+ virtual QString downloadURL() const;
+ virtual QString etag() const;
+ virtual bool isMeta() const;
+
+ virtual InstVersion *copyVersion(InstVersionList *newParent) const;
+
+private:
+ InstVersion *m_linkedVersion;
+
+ QString m_dlUrl;
+ QString m_etag;
+ VersionType m_type;
+};
+
+#endif // MINECRAFTVERSION_H
diff --git a/libmultimc/include/minecraftversionlist.h b/libmultimc/include/minecraftversionlist.h
new file mode 100644
index 00000000..18eb4ea6
--- /dev/null
+++ b/libmultimc/include/minecraftversionlist.h
@@ -0,0 +1,105 @@
+/* Copyright 2013 Andrew Okin
+ *
+ * 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 MINECRAFTVERSIONLIST_H
+#define MINECRAFTVERSIONLIST_H
+
+#include <QObject>
+
+#include <QNetworkAccessManager>
+
+#include <QList>
+
+#include "instversionlist.h"
+
+#include "task.h"
+
+#include "minecraftversion.h"
+
+#include "libmmc_config.h"
+
+class MCVListLoadTask;
+
+class LIBMULTIMC_EXPORT MinecraftVersionList : public InstVersionList
+{
+ Q_OBJECT
+public:
+ friend class MCVListLoadTask;
+
+ explicit MinecraftVersionList(QObject *parent = 0);
+
+ virtual Task *getLoadTask();
+ virtual bool isLoaded();
+ virtual const InstVersion *at(int i) const;
+ virtual int count() const;
+ virtual void printToStdOut() const;
+
+ /*!
+ * Gets the main version list instance.
+ */
+ static MinecraftVersionList &getMainList();
+
+protected:
+ QList<InstVersion *>m_vlist;
+
+ bool m_loaded;
+
+protected slots:
+ virtual void updateListData(QList<InstVersion *> versions);
+};
+
+class MCVListLoadTask : public Task
+{
+ Q_OBJECT
+
+public:
+ explicit MCVListLoadTask(MinecraftVersionList *vlist);
+ ~MCVListLoadTask();
+
+ virtual void executeTask();
+
+protected:
+ void setSubStatus(const QString msg = "");
+
+ //! Loads versions from Mojang's official version list.
+ bool loadFromVList();
+
+ //! Loads versions from assets.minecraft.net. Any duplicates are ignored.
+ bool loadFromAssets();
+
+ //! Loads versions from MCNostalgia.
+ bool loadMCNostalgia();
+
+ //! Finalizes loading by updating the version list.
+ bool finalize();
+
+ void updateStuff();
+
+ QNetworkAccessManager *netMgr;
+
+ MinecraftVersionList *m_list;
+ QList<InstVersion *> tempList; //! < List of loaded versions
+ QList<InstVersion *> assetsList; //! < List of versions loaded from assets.minecraft.net
+ QList<InstVersion *> mcnList; //! < List of loaded MCNostalgia versions
+
+ MinecraftVersion *m_currentStable;
+
+ bool processedMCVListReply;
+ bool processedAssetsReply;
+ bool processedMCNReply;
+};
+
+
+#endif // MINECRAFTVERSIONLIST_H
diff --git a/libmultimc/src/instance.cpp b/libmultimc/src/instance.cpp
index f9e105c7..f4a6a3c9 100644
--- a/libmultimc/src/instance.cpp
+++ b/libmultimc/src/instance.cpp
@@ -22,6 +22,7 @@
#include "overridesetting.h"
#include "pathutils.h"
+#include <minecraftversionlist.h>
Instance::Instance(const QString &rootDir, QObject *parent) :
QObject(parent)
@@ -151,6 +152,39 @@ QString Instance::modListFile() const
return PathCombine(rootDir(), "modlist");
}
+InstVersionList *Instance::versionList() const
+{
+ return &MinecraftVersionList::getMainList();
+}
+
+bool Instance::shouldUpdateCurrentVersion()
+{
+ QFileInfo jar(mcJar());
+ return jar.lastModified().toUTC().toMSecsSinceEpoch() != lastCurrentVersionUpdate();
+}
+
+void Instance::updateCurrentVersion(bool keepCurrent)
+{
+ QFileInfo jar(mcJar());
+
+ if(!jar.exists())
+ {
+ setLastCurrentVersionUpdate(0);
+ setCurrentVersion("Unknown");
+ return;
+ }
+
+ qint64 time = jar.lastModified().toUTC().toMSecsSinceEpoch();
+
+ setLastCurrentVersionUpdate(time);
+ if (!keepCurrent)
+ {
+ // TODO: Implement GetMinecraftJarVersion function.
+ QString newVersion = "Unknown";//javautils::GetMinecraftJarVersion(jar.absoluteFilePath());
+ setCurrentVersion(newVersion);
+ }
+}
+
SettingsObject &Instance::settings() const
{
return *m_settings;
diff --git a/libmultimc/src/instancelist.cpp b/libmultimc/src/instancelist.cpp
index f9c525d0..6f5a9f99 100644
--- a/libmultimc/src/instancelist.cpp
+++ b/libmultimc/src/instancelist.cpp
@@ -147,21 +147,17 @@ InstanceList::InstListError InstanceList::loadList()
{
Instance *instPtr = NULL;
- InstanceLoader::InstTypeError error = InstanceLoader::get().
+ InstanceLoader::InstLoaderError error = InstanceLoader::get().
loadInstance(instPtr, subDir);
if (error != InstanceLoader::NoError &&
- error != InstanceLoader::NotAnInstance)
+ error != InstanceLoader::NotAnInstance)
{
QString errorMsg = QString("Failed to load instance %1: ").
arg(QFileInfo(subDir).baseName()).toUtf8();
switch (error)
{
- case InstanceLoader::TypeNotRegistered:
- errorMsg += "Instance type not found.";
- break;
-
default:
errorMsg += QString("Unknown instance loader error %1").
arg(error);
@@ -234,4 +230,4 @@ void InstanceList::propertiesChanged(Instance * inst)
break;
}
}
-} \ No newline at end of file
+}
diff --git a/libmultimc/src/instanceloader.cpp b/libmultimc/src/instanceloader.cpp
index 9d98230f..e9924af4 100644
--- a/libmultimc/src/instanceloader.cpp
+++ b/libmultimc/src/instanceloader.cpp
@@ -15,9 +15,10 @@
#include "include/instanceloader.h"
+#include <QDir>
#include <QFileInfo>
-#include "include/instancetypeinterface.h"
+#include "include/instance.h"
#include "inifile.h"
@@ -31,79 +32,30 @@ InstanceLoader::InstanceLoader() :
}
-
-InstanceLoader::InstTypeError InstanceLoader::registerInstanceType(InstanceTypeInterface *type)
+InstanceLoader::InstLoaderError InstanceLoader::loadInstance(
+ Instance *&inst, const QString &instDir)
{
- // Check to see if the type ID exists.
- if (m_typeMap.contains(type->typeID()))
- return TypeIDExists;
+ Instance *loadedInst = new Instance(instDir, this);
- // Set the parent to this.
- // ((QObject *)type)->setParent(this);
+ // TODO: Sanity checks to verify that the instance is valid.
- // Add it to the map.
- m_typeMap.insert(type->typeID(), type);
+ inst = loadedInst;
- qDebug(QString("Registered instance type %1.").
- arg(type->typeID()).toUtf8());
return NoError;
}
-InstanceLoader::InstTypeError InstanceLoader::createInstance(Instance *&inst,
- const InstanceTypeInterface *type,
- const QString &instDir)
-{
- // Check if the type is registered.
- if (!type || findType(type->typeID()) != type)
- return TypeNotRegistered;
-
- // Create the instance.
- return type->createInstance(inst, instDir);
-}
-
-InstanceLoader::InstTypeError InstanceLoader::loadInstance(Instance *&inst,
- const InstanceTypeInterface *type,
- const QString &instDir)
-{
- // Check if the type is registered.
- if (!type || findType(type->typeID()) != type)
- return TypeNotRegistered;
-
- return type->loadInstance(inst, instDir);
-}
-
-InstanceLoader::InstTypeError InstanceLoader::loadInstance(Instance *&inst,
- const QString &instDir)
-{
- QFileInfo instConfig(PathCombine(instDir, "instance.cfg"));
-
- if (!instConfig.exists())
- return NotAnInstance;
-
- INIFile ini;
- ini.loadFile(instConfig.path());
- QString typeName = ini.get("type", "net.forkk.MultiMC.StdInstance").toString();
- const InstanceTypeInterface *type = findType(typeName);
-
- return loadInstance(inst, type, instDir);
-}
-const InstanceTypeInterface *InstanceLoader::findType(const QString &id)
+InstanceLoader::InstLoaderError InstanceLoader::createInstance(Instance *&inst, const QString &instDir)
{
- if (!m_typeMap.contains(id))
- return NULL;
- else
- return m_typeMap[id];
-}
-
-InstTypeList InstanceLoader::typeList()
-{
- InstTypeList typeList;
+ QDir rootDir(instDir);
- for (QMap<QString, InstanceTypeInterface *>::iterator iter = m_typeMap.begin(); iter != m_typeMap.end(); iter++)
+ qDebug(instDir.toUtf8());
+ if (!rootDir.exists() && !rootDir.mkpath("."))
{
- typeList.append(*iter);
+ return InstanceLoader::CantCreateDir;
}
- return typeList;
+ inst = new Instance(instDir, this);
+
+ return InstanceLoader::NoError;
}
diff --git a/libmultimc/src/instversion.cpp b/libmultimc/src/instversion.cpp
index cedb61df..d3d078a9 100644
--- a/libmultimc/src/instversion.cpp
+++ b/libmultimc/src/instversion.cpp
@@ -16,17 +16,48 @@
#include "include/instversion.h"
#include "include/instversionlist.h"
-InstVersion::InstVersion(InstVersionList *parent) :
- QObject(parent)
+InstVersion::InstVersion(const QString &descriptor,
+ const QString &name,
+ qint64 timestamp,
+ InstVersionList *parent) :
+ QObject(parent), m_descriptor(descriptor), m_name(name), m_timestamp(timestamp)
+{
+
+}
+
+InstVersion::InstVersion(const InstVersion &other, QObject *parent) :
+ QObject(parent ? parent : other.parent()),
+ m_descriptor(other.descriptor()), m_name(other.name()), m_timestamp(other.timestamp())
{
}
InstVersionList *InstVersion::versionList() const
{
- // Parent should *always* be an InstVersionList
+ // Parent should *always* be either an InstVersionList or NULL.
if (!parent() || !parent()->inherits("InstVersionList"))
return NULL;
else
return (InstVersionList *)parent();
}
+
+bool InstVersion::isMeta() const
+{
+ return false;
+}
+
+
+QString InstVersion::descriptor() const
+{
+ return m_descriptor;
+}
+
+QString InstVersion::name() const
+{
+ return m_name;
+}
+
+qint64 InstVersion::timestamp() const
+{
+ return m_timestamp;
+}
diff --git a/libmultimc/src/minecraftversion.cpp b/libmultimc/src/minecraftversion.cpp
new file mode 100644
index 00000000..896c2e18
--- /dev/null
+++ b/libmultimc/src/minecraftversion.cpp
@@ -0,0 +1,142 @@
+/* Copyright 2013 Andrew Okin
+ *
+ * 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 "minecraftversion.h"
+
+MinecraftVersion::MinecraftVersion(QString descriptor,
+ QString name,
+ qint64 timestamp,
+ QString dlUrl,
+ QString etag,
+ InstVersionList *parent) :
+ InstVersion(descriptor, name, timestamp, parent), m_dlUrl(dlUrl), m_etag(etag)
+{
+ m_linkedVersion = NULL;
+}
+
+MinecraftVersion::MinecraftVersion(const MinecraftVersion *linkedVersion) :
+ InstVersion(linkedVersion->descriptor(), linkedVersion->name(), linkedVersion->timestamp(),
+ linkedVersion->versionList())
+{
+ m_linkedVersion = (MinecraftVersion *)linkedVersion;
+}
+
+MinecraftVersion::MinecraftVersion(const MinecraftVersion &other, QObject *parent) :
+ InstVersion(other, parent)
+{
+ if (other.m_linkedVersion)
+ m_linkedVersion = other.m_linkedVersion;
+ else
+ {
+ m_dlUrl = other.downloadURL();
+ m_etag = other.etag();
+ }
+}
+
+QString MinecraftVersion::descriptor() const
+{
+ return m_descriptor;
+}
+
+QString MinecraftVersion::name() const
+{
+ return m_name;
+}
+
+QString MinecraftVersion::typeName() const
+{
+ if (m_linkedVersion)
+ return m_linkedVersion->typeName();
+
+ switch (versionType())
+ {
+ case OldSnapshot:
+ return "Old Snapshot";
+
+ case Stable:
+ return "Stable";
+
+ case CurrentStable:
+ return "Current Stable";
+
+ case Snapshot:
+ return "Snapshot";
+
+ case MCNostalgia:
+ return "MCNostalgia";
+
+ case MetaCustom:
+ // Not really sure what this does, but it was in the code for v4,
+ // so it must be important... Right?
+ return "Custom Meta Version";
+
+ case MetaLatestSnapshot:
+ return "Latest Snapshot";
+
+ case MetaLatestStable:
+ return "Latest Stable";
+
+ default:
+ return QString("Unknown Type %1").arg(versionType());
+ }
+}
+
+qint64 MinecraftVersion::timestamp() const
+{
+ return m_timestamp;
+}
+
+MinecraftVersion::VersionType MinecraftVersion::versionType() const
+{
+ return m_type;
+}
+
+void MinecraftVersion::setVersionType(MinecraftVersion::VersionType typeName)
+{
+ m_type = typeName;
+}
+
+QString MinecraftVersion::downloadURL() const
+{
+ return m_dlUrl;
+}
+
+QString MinecraftVersion::etag() const
+{
+ return m_etag;
+}
+
+bool MinecraftVersion::isMeta() const
+{
+ return versionType() == MetaCustom ||
+ versionType() == MetaLatestSnapshot ||
+ versionType() == MetaLatestStable;
+}
+
+InstVersion *MinecraftVersion::copyVersion(InstVersionList *newParent) const
+{
+ if (isMeta())
+ {
+ MinecraftVersion *version = new MinecraftVersion((MinecraftVersion *)m_linkedVersion);
+ return version;
+ }
+ else
+ {
+ MinecraftVersion *version = new MinecraftVersion(
+ descriptor(), name(), timestamp(), downloadURL(), etag(), newParent);
+ version->setVersionType(versionType());
+ return version;
+ }
+}
diff --git a/libmultimc/src/minecraftversionlist.cpp b/libmultimc/src/minecraftversionlist.cpp
new file mode 100644
index 00000000..ce02a769
--- /dev/null
+++ b/libmultimc/src/minecraftversionlist.cpp
@@ -0,0 +1,489 @@
+/* Copyright 2013 Andrew Okin
+ *
+ * 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 "include/minecraftversionlist.h"
+
+#include <QDebug>
+
+#include <QtXml>
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonValue>
+#include <QJsonParseError>
+
+#include <QtNetwork>
+
+#define MCVLIST_URLBASE "http://s3.amazonaws.com/Minecraft.Download/versions/"
+#define ASSETS_URLBASE "http://assets.minecraft.net/"
+#define MCN_URLBASE "http://sonicrules.org/mcnweb.py"
+
+MinecraftVersionList mcVList;
+
+MinecraftVersionList::MinecraftVersionList(QObject *parent) :
+ InstVersionList(parent)
+{
+
+}
+
+Task *MinecraftVersionList::getLoadTask()
+{
+ return new MCVListLoadTask(this);
+}
+
+bool MinecraftVersionList::isLoaded()
+{
+ return m_loaded;
+}
+
+const InstVersion *MinecraftVersionList::at(int i) const
+{
+ return m_vlist.at(i);
+}
+
+int MinecraftVersionList::count() const
+{
+ return m_vlist.count();
+}
+
+void MinecraftVersionList::printToStdOut() const
+{
+ qDebug() << "---------------- Version List ----------------";
+
+ for (int i = 0; i < m_vlist.count(); i++)
+ {
+ MinecraftVersion *version = qobject_cast<MinecraftVersion *>(m_vlist.at(i));
+
+ if (!version)
+ continue;
+
+ qDebug() << "Version " << version->name();
+ qDebug() << "\tDownload: " << version->downloadURL();
+ qDebug() << "\tTimestamp: " << version->timestamp();
+ qDebug() << "\tType: " << version->typeName();
+ qDebug() << "----------------------------------------------";
+ }
+}
+
+MinecraftVersionList &MinecraftVersionList::getMainList()
+{
+ return mcVList;
+}
+
+void MinecraftVersionList::updateListData(QList<InstVersion *> versions)
+{
+ // First, we populate a temporary list with the copies of the versions.
+ QList<InstVersion *> tempList;
+ for (int i = 0; i < versions.length(); i++)
+ {
+ InstVersion *version = versions[i]->copyVersion(this);
+ Q_ASSERT(version != NULL);
+ tempList.append(version);
+ }
+
+ // Now we swap the temporary list into the actual version list.
+ // This applies our changes to the version list immediately and still gives us
+ // access to the old version list so that we can delete the objects in it and
+ // free their memory. By doing this, we cause the version list to update as
+ // quickly as possible.
+ beginResetModel();
+ m_vlist.swap(tempList);
+ m_loaded = true;
+ endResetModel();
+
+ // We called swap, so all the data that was in the version list previously is now in
+ // tempList (and vice-versa). Now we just free the memory.
+ while (!tempList.isEmpty())
+ delete tempList.takeFirst();
+}
+
+inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
+{
+ QDomNodeList elementList = parent.elementsByTagName(tagname);
+ if (elementList.count())
+ return elementList.at(0).toElement();
+ else
+ return QDomElement();
+}
+
+inline QDateTime timeFromS3Time(QString str)
+{
+ const QString fmt("yyyy-MM-dd'T'HH:mm:ss'.000Z'");
+ return QDateTime::fromString(str, fmt);
+}
+
+inline void waitForNetRequest(QNetworkReply *netReply)
+{
+ QEventLoop loop;
+ loop.connect(netReply, SIGNAL(finished()), SLOT(quit()));
+ loop.exec();
+}
+
+
+MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist)
+{
+ m_list = vlist;
+ m_currentStable = NULL;
+}
+
+MCVListLoadTask::~MCVListLoadTask()
+{
+// delete netMgr;
+}
+
+void MCVListLoadTask::executeTask()
+{
+ setSubStatus();
+
+ QNetworkAccessManager networkMgr;
+ netMgr = &networkMgr;
+
+ if (!loadFromVList())
+ {
+ qDebug() << "Failed to load from Mojang version list.";
+ }
+ if (!loadFromAssets())
+ {
+ qDebug() << "Failed to load assets version list.";
+ }
+ if (!loadMCNostalgia())
+ {
+ qDebug() << "Failed to load MCNostalgia version list.";
+ }
+ finalize();
+}
+
+void MCVListLoadTask::setSubStatus(const QString msg)
+{
+ if (msg.isEmpty())
+ setStatus("Loading instance version list...");
+ else
+ setStatus("Loading instance version list: " + msg);
+}
+
+bool MCVListLoadTask::loadFromVList()
+{
+ QNetworkReply *vlistReply = netMgr->get(QNetworkRequest(QUrl(QString(MCVLIST_URLBASE) +
+ "versions.json")));
+ waitForNetRequest(vlistReply);
+
+ switch (vlistReply->error())
+ {
+ case QNetworkReply::NoError:
+ {
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError);
+
+ if (jsonError.error == QJsonParseError::NoError)
+ {
+ Q_ASSERT_X(jsonDoc.isObject(), "loadFromVList", "jsonDoc is not an object");
+
+ QJsonObject root = jsonDoc.object();
+
+ // Get the ID of the latest release and the latest snapshot.
+ Q_ASSERT_X(root.value("latest").isObject(), "loadFromVList",
+ "version list is missing 'latest' object");
+ QJsonObject latest = root.value("latest").toObject();
+
+ QString latestReleaseID = latest.value("release").toString("");
+ QString latestSnapshotID = latest.value("snapshot").toString("");
+ Q_ASSERT_X(!latestReleaseID.isEmpty(), "loadFromVList", "latest release field is missing");
+ Q_ASSERT_X(!latestSnapshotID.isEmpty(), "loadFromVList", "latest snapshot field is missing");
+
+ // Now, get the array of versions.
+ Q_ASSERT_X(root.value("versions").isArray(), "loadFromVList",
+ "version list object is missing 'versions' array");
+ QJsonArray versions = root.value("versions").toArray();
+
+ for (int i = 0; i < versions.count(); i++)
+ {
+ // Load the version info.
+ Q_ASSERT_X(versions[i].isObject(), "loadFromVList",
+ QString("in versions array, index %1 is not an object").
+ arg(i).toUtf8());
+ QJsonObject version = versions[i].toObject();
+
+ QString versionID = version.value("id").toString("");
+ QString versionTimeStr = version.value("time").toString("");
+ QString versionTypeStr = version.value("type").toString("");
+
+ Q_ASSERT_X(!versionID.isEmpty(), "loadFromVList",
+ QString("in versions array, index %1's \"id\" field is not a valid string").
+ arg(i).toUtf8());
+ Q_ASSERT_X(!versionTimeStr.isEmpty(), "loadFromVList",
+ QString("in versions array, index %1's \"time\" field is not a valid string").
+ arg(i).toUtf8());
+ Q_ASSERT_X(!versionTypeStr.isEmpty(), "loadFromVList",
+ QString("in versions array, index %1's \"type\" field is not a valid string").
+ arg(i).toUtf8());
+
+
+ // Now, process that info and add the version to the list.
+
+ // Parse the timestamp.
+ QDateTime versionTime = timeFromS3Time(versionTimeStr);
+
+ // Parse the type.
+ MinecraftVersion::VersionType versionType;
+ if (versionTypeStr == "release")
+ {
+ // Check if this version is the current stable version.
+ if (versionID == latestReleaseID)
+ versionType = MinecraftVersion::CurrentStable;
+ else
+ versionType = MinecraftVersion::Stable;
+ }
+ else
+ {
+ versionType = MinecraftVersion::Snapshot;
+ }
+
+ // Get the download URL.
+ QString dlUrl = QString(MCVLIST_URLBASE) + versionID + "/";
+
+
+ // Now, we construct the version object and add it to the list.
+ MinecraftVersion *mcVersion = new MinecraftVersion(
+ versionID, versionID, versionTime.toMSecsSinceEpoch(),
+ dlUrl, "");
+ mcVersion->setVersionType(versionType);
+ tempList.append(mcVersion);
+ }
+ }
+ else
+ {
+ qDebug() << "Error parsing version list JSON:" << jsonError.errorString();
+ }
+
+ break;
+ }
+
+ default:
+ // TODO: Network error handling.
+ qDebug() << "Failed to load Minecraft main version list" << vlistReply->errorString();
+ break;
+ }
+
+ return true;
+}
+
+bool MCVListLoadTask::loadFromAssets()
+{
+ setSubStatus("Loading versions from assets.minecraft.net...");
+
+ bool succeeded = false;
+
+ QNetworkReply *assetsReply = netMgr->get(QNetworkRequest(QUrl(ASSETS_URLBASE)));
+ waitForNetRequest(assetsReply);
+
+ switch (assetsReply->error())
+ {
+ case QNetworkReply::NoError:
+ {
+ // Get the XML string.
+ QString xmlString = assetsReply->readAll();
+
+ QString xmlErrorMsg;
+
+ QDomDocument doc;
+ if (!doc.setContent(xmlString, false, &xmlErrorMsg))
+ {
+ // TODO: Display error message to the user.
+ qDebug() << "Failed to process assets.minecraft.net. XML error:" <<
+ xmlErrorMsg << xmlString;
+ }
+
+ QDomNodeList contents = doc.elementsByTagName("Contents");
+
+ QRegExp mcRegex("/minecraft.jar$");
+ QRegExp snapshotRegex("[0-9][0-9]w[0-9][0-9][a-z]|pre|rc");
+
+ for (int i = 0; i < contents.length(); i++)
+ {
+ QDomElement element = contents.at(i).toElement();
+
+ if (element.isNull())
+ continue;
+
+ QDomElement keyElement = getDomElementByTagName(element, "Key");
+ QDomElement lastmodElement = getDomElementByTagName(element, "LastModified");
+ QDomElement etagElement = getDomElementByTagName(element, "ETag");
+
+ if (keyElement.isNull() || lastmodElement.isNull() || etagElement.isNull())
+ continue;
+
+ QString key = keyElement.text();
+ QString lastModStr = lastmodElement.text();
+ QString etagStr = etagElement.text();
+
+ if (!key.contains(mcRegex))
+ continue;
+
+ QString versionDirName = key.left(key.length() - 14);
+ QString dlUrl = QString("http://assets.minecraft.net/%1/").arg(versionDirName);
+
+ QString versionName = versionDirName.replace("_", ".");
+
+ QDateTime versionTimestamp = timeFromS3Time(lastModStr);
+ if (!versionTimestamp.isValid())
+ {
+ qDebug(QString("Failed to parse timestamp for version %1 %2").
+ arg(versionName, lastModStr).toUtf8());
+ versionTimestamp = QDateTime::currentDateTime();
+ }
+
+ if (m_currentStable)
+ {
+ {
+ bool older = versionTimestamp.toMSecsSinceEpoch() < m_currentStable->timestamp();
+ bool newer = versionTimestamp.toMSecsSinceEpoch() > m_currentStable->timestamp();
+ bool isSnapshot = versionName.contains(snapshotRegex);
+
+ MinecraftVersion *version = new MinecraftVersion(
+ versionName, versionName,
+ versionTimestamp.toMSecsSinceEpoch(),
+ dlUrl, etagStr);
+
+ if (newer)
+ {
+ version->setVersionType(MinecraftVersion::Snapshot);
+ }
+ else if (older && isSnapshot)
+ {
+ version->setVersionType(MinecraftVersion::OldSnapshot);
+ }
+ else if (older)
+ {
+ version->setVersionType(MinecraftVersion::Stable);
+ }
+ else
+ {
+ // Shouldn't happen, but just in case...
+ version->setVersionType(MinecraftVersion::CurrentStable);
+ }
+
+ assetsList.push_back(version);
+ }
+ }
+ else // If there isn't a current stable version.
+ {
+ bool isSnapshot = versionName.contains(snapshotRegex);
+
+ MinecraftVersion *version = new MinecraftVersion(
+ versionName, versionName,
+ versionTimestamp.toMSecsSinceEpoch(),
+ dlUrl, etagStr);
+ version->setVersionType(isSnapshot? MinecraftVersion::Snapshot :
+ MinecraftVersion::Stable);
+ assetsList.push_back(version);
+ }
+ }
+
+ setSubStatus("Loaded assets.minecraft.net");
+ succeeded = true;
+ break;
+ }
+
+ default:
+ // TODO: Network error handling.
+ qDebug() << "Failed to load assets.minecraft.net" << assetsReply->errorString();
+ break;
+ }
+
+ processedAssetsReply = true;
+ updateStuff();
+ return succeeded;
+}
+
+bool MCVListLoadTask::loadMCNostalgia()
+{
+ QNetworkReply *mcnReply = netMgr->get(QNetworkRequest(QUrl(QString(MCN_URLBASE) + "?pversion=1&list=True")));
+ waitForNetRequest(mcnReply);
+ return true;
+}
+
+bool MCVListLoadTask::finalize()
+{
+ // First, we need to do some cleanup. We loaded assets versions into assetsList,
+ // MCNostalgia versions into mcnList and all the others into tempList. MCNostalgia
+ // provides some versions that are on assets.minecraft.net and we want to ignore
+ // those, so we remove and delete them from mcnList. assets.minecraft.net also provides
+ // versions that are on Mojang's version list and we want to ignore those as well.
+
+ // To start, we get a list of the descriptors in tmpList.
+ QStringList tlistDescriptors;
+ for (int i = 0; i < tempList.count(); i++)
+ tlistDescriptors.append(tempList.at(i)->descriptor());
+
+ // Now, we go through our assets version list and remove anything with
+ // a descriptor that matches one we already have in tempList.
+ for (int i = 0; i < assetsList.count(); i++)
+ if (tlistDescriptors.contains(assetsList.at(i)->descriptor()))
+ delete assetsList.takeAt(i--); // We need to decrement here because we're removing an item.
+
+ // We also need to rebuild the list of descriptors.
+ tlistDescriptors.clear();
+ for (int i = 0; i < tempList.count(); i++)
+ tlistDescriptors.append(tempList.at(i)->descriptor());
+
+ // Next, we go through our MCNostalgia version list and do the same thing.
+ for (int i = 0; i < mcnList.count(); i++)
+ if (tlistDescriptors.contains(mcnList.at(i)->descriptor()))
+ delete mcnList.takeAt(i--); // We need to decrement here because we're removing an item.
+
+ // Now that the duplicates are gone, we need to merge the lists. This is
+ // simple enough.
+ tempList.append(assetsList);
+ tempList.append(mcnList);
+
+ // We're done with these lists now, but the items have been moved over to
+ // tempList, so we don't need to delete them yet.
+
+ // Now, we invoke the updateListData slot on the GUI thread. This will copy all
+ // the versions we loaded and set their parents to the version list.
+ // Then, it will swap the new list with the old one and free the old list's memory.
+ QMetaObject::invokeMethod(m_list, "updateListData", Qt::BlockingQueuedConnection,
+ Q_ARG(QList<InstVersion*>, tempList));
+
+ // Once that's finished, we can delete the versions in our temp list.
+ while (!tempList.isEmpty())
+ delete tempList.takeFirst();
+
+#ifdef PRINT_VERSIONS
+ m_list->printToStdOut();
+#endif
+ return true;
+}
+
+void MCVListLoadTask::updateStuff()
+{
+ const int totalReqs = 3;
+ int reqsComplete = 0;
+
+ if (processedMCVListReply)
+ reqsComplete++;
+ if (processedAssetsReply)
+ reqsComplete++;
+ if (processedMCNReply)
+ reqsComplete++;
+
+ calcProgress(reqsComplete, totalReqs);
+
+ if (reqsComplete >= totalReqs)
+ {
+ quit();
+ }
+}
diff --git a/main.cpp b/main.cpp
index 4239d70f..f54e8723 100644
--- a/main.cpp
+++ b/main.cpp
@@ -32,8 +32,6 @@
#include "logintask.h"
#include "minecraftprocess.h"
-#include "pluginmanager.h"
-
#include "pathutils.h"
#include "cmdutils.h"
@@ -206,10 +204,6 @@ int main(int argc, char *argv[])
// Register meta types.
qRegisterMetaType<LoginResponse>("LoginResponse");
- // Initialize plugins.
- PluginManager::get().loadPlugins(PathCombine(qApp->applicationDirPath(), "plugins"));
- PluginManager::get().initInstanceTypes();
-
// launch instance.
if (!args["launch"].isNull())
return InstanceLauncher(args["launch"].toString()).launch();