Removed old plugin system and implemented some version list stuff.

This commit is contained in:
Andrew 2013-05-03 14:41:37 -05:00
parent 1626fa013c
commit 055198303c
20 changed files with 1064 additions and 279 deletions

View File

@ -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 ################################

View File

@ -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;

View File

@ -30,6 +30,8 @@
#include <QLayout>
#include <QPushButton>
#include <minecraftversionlist.h>
NewInstanceDialog::NewInstanceDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::NewInstanceDialog)
@ -41,7 +43,14 @@ NewInstanceDialog::NewInstanceDialog(QWidget *parent) :
resize(minimumSizeHint());
layout()->setSizeConstraint(QLayout::SetFixedSize);
loadTypeList();
if (!MinecraftVersionList::getMainList().isLoaded())
{
TaskDialog *taskDlg = new TaskDialog(this);
Task *loadTask = MinecraftVersionList::getMainList().getLoadTask();
loadTask->setParent(taskDlg);
taskDlg->exec(loadTask);
}
setSelectedVersion(MinecraftVersionList::getMainList().getLatestStable());
}
NewInstanceDialog::~NewInstanceDialog()
@ -49,40 +58,10 @@ NewInstanceDialog::~NewInstanceDialog()
delete ui;
}
void NewInstanceDialog::loadTypeList()
{
InstTypeList typeList = InstanceLoader::get().typeList();
for (int i = 0; i < typeList.length(); i++)
{
ui->instTypeComboBox->addItem(typeList.at(i)->displayName(), typeList.at(i)->typeID());
}
updateSelectedType();
}
void NewInstanceDialog::updateSelectedType()
{
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());
}
}
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();
}

View File

@ -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;

View File

@ -10,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>220</width>
<height>230</height>
<height>234</height>
</rect>
</property>
<property name="windowTitle">
@ -75,27 +75,6 @@
</property>
</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">
@ -128,6 +107,19 @@
</item>
</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">

View File

@ -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)

View File

@ -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 ////

View File

@ -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;
};

View File

@ -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)

View File

@ -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
/*!
* 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.
*/
Q_PROPERTY(bool isMeta READ isMeta)
/*!
* Gets the version's timestamp.
* This is primarily used for sorting versions in a list.
*/
Q_PROPERTY(qint64 timestamp READ timestamp)
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.
* The InstVersion will be added to the list immediately after being created.
*/
explicit InstVersion(InstVersionList *parent = 0);
//! Gets the string used to identify this version in config files.
virtual QString descriptor() const = 0;
explicit InstVersion(const QString &descriptor,
const QString &name,
qint64 timestamp,
InstVersionList *parent = 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.
* 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.
*/
virtual QString name() const = 0;
InstVersion(const InstVersion &other, QObject *parent = 0);
/*!
* \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.
*/
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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)
InstanceLoader::InstLoaderError InstanceLoader::createInstance(Instance *&inst, const QString &instDir)
{
// Check if the type is registered.
if (!type || findType(type->typeID()) != type)
return TypeNotRegistered;
QDir rootDir(instDir);
// 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)
{
if (!m_typeMap.contains(id))
return NULL;
else
return m_typeMap[id];
}
InstTypeList InstanceLoader::typeList()
{
InstTypeList typeList;
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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();