Merge branch 'develop' of github.com:MultiMC/MultiMC5 into feature_news
Conflicts: CMakeLists.txt gui/MainWindow.h
This commit is contained in:
commit
17f1864a71
2
.gitignore
vendored
2
.gitignore
vendored
@ -18,3 +18,5 @@ tags
|
|||||||
# YouCompleteMe config stuff.
|
# YouCompleteMe config stuff.
|
||||||
.ycm_extra_conf.*
|
.ycm_extra_conf.*
|
||||||
|
|
||||||
|
#OSX Stuff
|
||||||
|
.DS_Store
|
||||||
|
@ -43,11 +43,20 @@ ENDIF()
|
|||||||
######## 3rd Party Libs ########
|
######## 3rd Party Libs ########
|
||||||
|
|
||||||
# Find the required Qt parts
|
# Find the required Qt parts
|
||||||
|
find_package(Qt5Core REQUIRED)
|
||||||
find_package(Qt5Widgets REQUIRED)
|
find_package(Qt5Widgets REQUIRED)
|
||||||
find_package(Qt5Network REQUIRED)
|
find_package(Qt5Network REQUIRED)
|
||||||
|
find_package(Qt5Test REQUIRED)
|
||||||
|
find_package(Qt5Concurrent REQUIRED)
|
||||||
find_package(Qt5LinguistTools REQUIRED)
|
find_package(Qt5LinguistTools REQUIRED)
|
||||||
|
|
||||||
include_directories(${Qt5Widgets_INCLUDE_DIRS})
|
include_directories(
|
||||||
|
${Qt5Core_INCLUDE_DIRS}
|
||||||
|
${Qt5Widgets_INCLUDE_DIRS}
|
||||||
|
${Qt5Concurrent_INCLUDE_DIRS}
|
||||||
|
${Qt5Network_INCLUDE_DIRS}
|
||||||
|
${Qt5Test_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
# The Qt5 cmake files don't provide its install paths, so ask qmake.
|
# The Qt5 cmake files don't provide its install paths, so ask qmake.
|
||||||
get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)
|
get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)
|
||||||
@ -360,16 +369,22 @@ logic/OpSys.h
|
|||||||
logic/OpSys.cpp
|
logic/OpSys.cpp
|
||||||
logic/ForgeInstaller.h
|
logic/ForgeInstaller.h
|
||||||
logic/ForgeInstaller.cpp
|
logic/ForgeInstaller.cpp
|
||||||
|
logic/LiteLoaderInstaller.h
|
||||||
|
logic/LiteLoaderInstaller.cpp
|
||||||
|
|
||||||
# Nostalgia
|
# Nostalgia
|
||||||
logic/NostalgiaInstance.h
|
logic/NostalgiaInstance.h
|
||||||
logic/NostalgiaInstance.cpp
|
logic/NostalgiaInstance.cpp
|
||||||
|
|
||||||
|
# FTB
|
||||||
|
logic/OneSixFTBInstance.h
|
||||||
|
logic/OneSixFTBInstance.cpp
|
||||||
|
logic/LegacyFTBInstance.h
|
||||||
|
logic/LegacyFTBInstance.cpp
|
||||||
|
|
||||||
# Lists
|
# Lists
|
||||||
logic/lists/InstanceList.h
|
logic/lists/InstanceList.h
|
||||||
logic/lists/InstanceList.cpp
|
logic/lists/InstanceList.cpp
|
||||||
logic/lists/IconList.h
|
|
||||||
logic/lists/IconList.cpp
|
|
||||||
logic/lists/BaseVersionList.h
|
logic/lists/BaseVersionList.h
|
||||||
logic/lists/BaseVersionList.cpp
|
logic/lists/BaseVersionList.cpp
|
||||||
logic/lists/MinecraftVersionList.h
|
logic/lists/MinecraftVersionList.h
|
||||||
@ -381,6 +396,13 @@ logic/lists/ForgeVersionList.cpp
|
|||||||
logic/lists/JavaVersionList.h
|
logic/lists/JavaVersionList.h
|
||||||
logic/lists/JavaVersionList.cpp
|
logic/lists/JavaVersionList.cpp
|
||||||
|
|
||||||
|
# Icons
|
||||||
|
logic/icons/MMCIcon.h
|
||||||
|
logic/icons/MMCIcon.cpp
|
||||||
|
logic/icons/IconList.h
|
||||||
|
logic/icons/IconList.cpp
|
||||||
|
|
||||||
|
|
||||||
# misc model/view
|
# misc model/view
|
||||||
logic/EnabledItemFilter.h
|
logic/EnabledItemFilter.h
|
||||||
logic/EnabledItemFilter.cpp
|
logic/EnabledItemFilter.cpp
|
||||||
@ -389,6 +411,10 @@ logic/EnabledItemFilter.cpp
|
|||||||
logic/tasks/ProgressProvider.h
|
logic/tasks/ProgressProvider.h
|
||||||
logic/tasks/Task.h
|
logic/tasks/Task.h
|
||||||
logic/tasks/Task.cpp
|
logic/tasks/Task.cpp
|
||||||
|
logic/tasks/ThreadTask.h
|
||||||
|
logic/tasks/ThreadTask.cpp
|
||||||
|
logic/tasks/SequentialTask.h
|
||||||
|
logic/tasks/SequentialTask.cpp
|
||||||
|
|
||||||
# Utilities
|
# Utilities
|
||||||
logic/JavaChecker.h
|
logic/JavaChecker.h
|
||||||
@ -403,6 +429,8 @@ logic/JavaCheckerJob.h
|
|||||||
logic/JavaCheckerJob.cpp
|
logic/JavaCheckerJob.cpp
|
||||||
|
|
||||||
# Assets
|
# Assets
|
||||||
|
logic/assets/AssetsMigrateTask.h
|
||||||
|
logic/assets/AssetsMigrateTask.cpp
|
||||||
logic/assets/AssetsUtils.h
|
logic/assets/AssetsUtils.h
|
||||||
logic/assets/AssetsUtils.cpp
|
logic/assets/AssetsUtils.cpp
|
||||||
)
|
)
|
||||||
@ -499,8 +527,8 @@ ADD_EXECUTABLE(MultiMC MACOSX_BUNDLE WIN32 main.cpp ${MULTIMC_RCS})
|
|||||||
# Link
|
# Link
|
||||||
TARGET_LINK_LIBRARIES(MultiMC MultiMC_common)
|
TARGET_LINK_LIBRARIES(MultiMC MultiMC_common)
|
||||||
TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings libGroupView ${MultiMC_LINK_ADDITIONAL_LIBS})
|
TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings libGroupView ${MultiMC_LINK_ADDITIONAL_LIBS})
|
||||||
QT5_USE_MODULES(MultiMC Core Widgets Network Xml WebKit ${MultiMC_QT_ADDITIONAL_MODULES})
|
QT5_USE_MODULES(MultiMC Core Widgets Network Xml WebKit Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
|
||||||
QT5_USE_MODULES(MultiMC_common Core Widgets Network Xml WebKit ${MultiMC_QT_ADDITIONAL_MODULES})
|
QT5_USE_MODULES(MultiMC_common Core Widgets Network Xml WebKit Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
|
||||||
ADD_DEPENDENCIES(MultiMC_common MultiMCLauncher JavaCheck)
|
ADD_DEPENDENCIES(MultiMC_common MultiMCLauncher JavaCheck)
|
||||||
|
|
||||||
################################ INSTALLATION AND PACKAGING ################################
|
################################ INSTALLATION AND PACKAGING ################################
|
||||||
@ -514,9 +542,12 @@ IF(UNIX AND APPLE)
|
|||||||
|
|
||||||
SET(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC")
|
SET(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC")
|
||||||
SET(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.")
|
SET(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.")
|
||||||
|
SET(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.MultiMC5")
|
||||||
SET(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
|
SET(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
|
||||||
#SET(MACOSX_BUNDLE_GUI_IDENTIFIER "")
|
SET(MACOSX_BUNDLE_SHORT_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
|
||||||
|
SET(MACOSX_BUNDLE_LONG_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
|
||||||
SET(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
|
SET(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
|
||||||
|
SET(MACOSX_BUNDLE_COPYRIGHT "Copyright 2013 MultiMC Contributors")
|
||||||
ELSEIF(UNIX)
|
ELSEIF(UNIX)
|
||||||
SET(PLUGIN_DEST_DIR plugins)
|
SET(PLUGIN_DEST_DIR plugins)
|
||||||
SET(QTCONF_DEST_DIR .)
|
SET(QTCONF_DEST_DIR .)
|
||||||
@ -592,6 +623,18 @@ INSTALL(
|
|||||||
REGEX "d\\." EXCLUDE
|
REGEX "d\\." EXCLUDE
|
||||||
REGEX "_debug\\." EXCLUDE
|
REGEX "_debug\\." EXCLUDE
|
||||||
)
|
)
|
||||||
|
IF(APPLE)
|
||||||
|
# Accessible plugin to make buttons look decent on osx
|
||||||
|
INSTALL(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/accessible"
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "quick" EXCLUDE
|
||||||
|
REGEX "d\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# qtconf
|
# qtconf
|
||||||
@ -658,8 +701,12 @@ ELSE()
|
|||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
add_custom_target (translations DEPENDS ${QM_FILES})
|
add_custom_target (translations DEPENDS ${QM_FILES})
|
||||||
|
IF(APPLE AND UNIX) ## OSX
|
||||||
|
install(FILES ${QM_FILES} DESTINATION MultiMC.app/Contents/MacOS/translations)
|
||||||
|
ELSE()
|
||||||
|
install(FILES ${QM_FILES} DESTINATION translations)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/translations)
|
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
323
MultiMC.cpp
323
MultiMC.cpp
@ -8,11 +8,12 @@
|
|||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
|
||||||
#include "gui/dialogs/VersionSelectDialog.h"
|
#include "gui/dialogs/VersionSelectDialog.h"
|
||||||
#include "logic/lists/InstanceList.h"
|
#include "logic/lists/InstanceList.h"
|
||||||
#include "logic/auth/MojangAccountList.h"
|
#include "logic/auth/MojangAccountList.h"
|
||||||
#include "logic/lists/IconList.h"
|
#include "logic/icons/IconList.h"
|
||||||
#include "logic/lists/LwjglVersionList.h"
|
#include "logic/lists/LwjglVersionList.h"
|
||||||
#include "logic/lists/MinecraftVersionList.h"
|
#include "logic/lists/MinecraftVersionList.h"
|
||||||
#include "logic/lists/ForgeVersionList.h"
|
#include "logic/lists/ForgeVersionList.h"
|
||||||
@ -34,16 +35,28 @@
|
|||||||
#include <logger/QsLogDest.h>
|
#include <logger/QsLogDest.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#ifdef WINDOWS
|
||||||
|
#define UPDATER_BIN "updater.exe"
|
||||||
|
#elif LINUX
|
||||||
|
#define UPDATER_BIN "updater"
|
||||||
|
#elif OSX
|
||||||
|
#define UPDATER_BIN "updater"
|
||||||
|
#else
|
||||||
|
#error Unsupported operating system.
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace Util::Commandline;
|
using namespace Util::Commandline;
|
||||||
|
|
||||||
MultiMC::MultiMC(int &argc, char **argv, const QString &root) : QApplication(argc, argv),
|
MultiMC::MultiMC(int &argc, char **argv, const QString &root)
|
||||||
m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_CHANNEL, VERSION_BUILD_TYPE}
|
: QApplication(argc, argv), m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD,
|
||||||
|
VERSION_CHANNEL, VERSION_BUILD_TYPE}
|
||||||
{
|
{
|
||||||
setOrganizationName("MultiMC");
|
setOrganizationName("MultiMC");
|
||||||
setApplicationName("MultiMC5");
|
setApplicationName("MultiMC5");
|
||||||
|
|
||||||
initTranslations();
|
initTranslations();
|
||||||
|
|
||||||
|
setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||||
// Don't quit on hiding the last window
|
// Don't quit on hiding the last window
|
||||||
this->setQuitOnLastWindowClosed(false);
|
this->setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
@ -137,9 +150,10 @@ MultiMC::MultiMC(int &argc, char **argv, const QString &root) : QApplication(arg
|
|||||||
}
|
}
|
||||||
|
|
||||||
// change directory
|
// change directory
|
||||||
QDir::setCurrent(args["dir"].toString().isEmpty() ?
|
QDir::setCurrent(
|
||||||
(root.isEmpty() ? QDir::currentPath() : QDir::current().absoluteFilePath(root))
|
args["dir"].toString().isEmpty()
|
||||||
: args["dir"].toString());
|
? (root.isEmpty() ? QDir::currentPath() : QDir::current().absoluteFilePath(root))
|
||||||
|
: args["dir"].toString());
|
||||||
|
|
||||||
// init the logger
|
// init the logger
|
||||||
initLogger();
|
initLogger();
|
||||||
@ -158,7 +172,7 @@ MultiMC::MultiMC(int &argc, char **argv, const QString &root) : QApplication(arg
|
|||||||
m_instances.reset(new InstanceList(InstDirSetting->get().toString(), this));
|
m_instances.reset(new InstanceList(InstDirSetting->get().toString(), this));
|
||||||
QLOG_INFO() << "Loading Instances...";
|
QLOG_INFO() << "Loading Instances...";
|
||||||
m_instances->loadList();
|
m_instances->loadList();
|
||||||
connect(InstDirSetting, SIGNAL(settingChanged(const Setting &, QVariant)),
|
connect(InstDirSetting.get(), SIGNAL(settingChanged(const Setting &, QVariant)),
|
||||||
m_instances.get(), SLOT(on_InstFolderChanged(const Setting &, QVariant)));
|
m_instances.get(), SLOT(on_InstFolderChanged(const Setting &, QVariant)));
|
||||||
|
|
||||||
// and accounts
|
// and accounts
|
||||||
@ -179,42 +193,43 @@ MultiMC::MultiMC(int &argc, char **argv, const QString &root) : QApplication(arg
|
|||||||
{
|
{
|
||||||
QLOG_INFO() << "No proxy found.";
|
QLOG_INFO() << "No proxy found.";
|
||||||
}
|
}
|
||||||
else for (auto proxy : proxies)
|
else
|
||||||
{
|
for (auto proxy : proxies)
|
||||||
QString proxyDesc;
|
|
||||||
if (proxy.type() == QNetworkProxy::NoProxy)
|
|
||||||
{
|
{
|
||||||
QLOG_INFO() << "Using no proxy is an option!";
|
QString proxyDesc;
|
||||||
continue;
|
if (proxy.type() == QNetworkProxy::NoProxy)
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Using no proxy is an option!";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (proxy.type())
|
||||||
|
{
|
||||||
|
case QNetworkProxy::DefaultProxy:
|
||||||
|
proxyDesc = "Default proxy: ";
|
||||||
|
break;
|
||||||
|
case QNetworkProxy::Socks5Proxy:
|
||||||
|
proxyDesc = "Socks5 proxy: ";
|
||||||
|
break;
|
||||||
|
case QNetworkProxy::HttpProxy:
|
||||||
|
proxyDesc = "HTTP proxy: ";
|
||||||
|
break;
|
||||||
|
case QNetworkProxy::HttpCachingProxy:
|
||||||
|
proxyDesc = "HTTP caching: ";
|
||||||
|
break;
|
||||||
|
case QNetworkProxy::FtpCachingProxy:
|
||||||
|
proxyDesc = "FTP caching: ";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
proxyDesc = "DERP proxy: ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
proxyDesc += QString("%3@%1:%2 pass %4")
|
||||||
|
.arg(proxy.hostName())
|
||||||
|
.arg(proxy.port())
|
||||||
|
.arg(proxy.user())
|
||||||
|
.arg(proxy.password());
|
||||||
|
QLOG_INFO() << proxyDesc;
|
||||||
}
|
}
|
||||||
switch (proxy.type())
|
|
||||||
{
|
|
||||||
case QNetworkProxy::DefaultProxy:
|
|
||||||
proxyDesc = "Default proxy: ";
|
|
||||||
break;
|
|
||||||
case QNetworkProxy::Socks5Proxy:
|
|
||||||
proxyDesc = "Socks5 proxy: ";
|
|
||||||
break;
|
|
||||||
case QNetworkProxy::HttpProxy:
|
|
||||||
proxyDesc = "HTTP proxy: ";
|
|
||||||
break;
|
|
||||||
case QNetworkProxy::HttpCachingProxy:
|
|
||||||
proxyDesc = "HTTP caching: ";
|
|
||||||
break;
|
|
||||||
case QNetworkProxy::FtpCachingProxy:
|
|
||||||
proxyDesc = "FTP caching: ";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
proxyDesc = "DERP proxy: ";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
proxyDesc += QString("%3@%1:%2 pass %4")
|
|
||||||
.arg(proxy.hostName())
|
|
||||||
.arg(proxy.port())
|
|
||||||
.arg(proxy.user())
|
|
||||||
.arg(proxy.password());
|
|
||||||
QLOG_INFO() << proxyDesc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the global network manager
|
// create the global network manager
|
||||||
m_qnam.reset(new QNetworkAccessManager(this));
|
m_qnam.reset(new QNetworkAccessManager(this));
|
||||||
@ -285,13 +300,26 @@ void MultiMC::initTranslations()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void moveFile(const QString &oldName, const QString &newName)
|
||||||
|
{
|
||||||
|
QFile::remove(newName);
|
||||||
|
QFile::copy(oldName, newName);
|
||||||
|
QFile::remove(oldName);
|
||||||
|
}
|
||||||
void MultiMC::initLogger()
|
void MultiMC::initLogger()
|
||||||
{
|
{
|
||||||
|
static const QString logBase = "MultiMC-%0.log";
|
||||||
|
|
||||||
|
moveFile(logBase.arg(3), logBase.arg(4));
|
||||||
|
moveFile(logBase.arg(2), logBase.arg(3));
|
||||||
|
moveFile(logBase.arg(1), logBase.arg(2));
|
||||||
|
moveFile(logBase.arg(0), logBase.arg(1));
|
||||||
|
|
||||||
// init the logging mechanism
|
// init the logging mechanism
|
||||||
QsLogging::Logger &logger = QsLogging::Logger::instance();
|
QsLogging::Logger &logger = QsLogging::Logger::instance();
|
||||||
logger.setLoggingLevel(QsLogging::TraceLevel);
|
logger.setLoggingLevel(QsLogging::TraceLevel);
|
||||||
m_fileDestination = QsLogging::DestinationFactory::MakeFileDestination("MultiMC.log");
|
m_fileDestination = QsLogging::DestinationFactory::MakeFileDestination(logBase.arg(0));
|
||||||
m_debugDestination = QsLogging::DestinationFactory::MakeDebugOutputDestination();
|
m_debugDestination = QsLogging::DestinationFactory::MakeQDebugDestination();
|
||||||
logger.addDestination(m_fileDestination.get());
|
logger.addDestination(m_fileDestination.get());
|
||||||
logger.addDestination(m_debugDestination.get());
|
logger.addDestination(m_debugDestination.get());
|
||||||
// log all the things
|
// log all the things
|
||||||
@ -302,67 +330,110 @@ void MultiMC::initGlobalSettings()
|
|||||||
{
|
{
|
||||||
m_settings.reset(new INISettingsObject("multimc.cfg", this));
|
m_settings.reset(new INISettingsObject("multimc.cfg", this));
|
||||||
// Updates
|
// Updates
|
||||||
m_settings->registerSetting(new Setting("UseDevBuilds", false));
|
m_settings->registerSetting("UseDevBuilds", false);
|
||||||
m_settings->registerSetting(new Setting("AutoUpdate", true));
|
m_settings->registerSetting("AutoUpdate", true);
|
||||||
|
|
||||||
// Folders
|
// FTB
|
||||||
m_settings->registerSetting(new Setting("InstanceDir", "instances"));
|
m_settings->registerSetting("TrackFTBInstances", false);
|
||||||
m_settings->registerSetting(new Setting("CentralModsDir", "mods"));
|
#ifdef Q_OS_LINUX
|
||||||
m_settings->registerSetting(new Setting("LWJGLDir", "lwjgl"));
|
QString ftbDefault = QDir::home().absoluteFilePath(".ftblauncher");
|
||||||
|
#elif defined(Q_OS_WIN32)
|
||||||
|
QString ftbDefault = PathCombine(QDir::homePath(), "AppData/Roaming/ftblauncher");
|
||||||
|
#elif defined(Q_OS_MAC)
|
||||||
|
QString ftbDefault =
|
||||||
|
PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher");
|
||||||
|
#endif
|
||||||
|
m_settings->registerSetting("FTBLauncherRoot", ftbDefault);
|
||||||
|
|
||||||
// Console
|
m_settings->registerSetting("FTBRoot");
|
||||||
m_settings->registerSetting(new Setting("ShowConsole", true));
|
if (m_settings->get("FTBRoot").isNull())
|
||||||
m_settings->registerSetting(new Setting("AutoCloseConsole", true));
|
|
||||||
|
|
||||||
// Console Colors
|
|
||||||
// m_settings->registerSetting(new Setting("SysMessageColor", QColor(Qt::blue)));
|
|
||||||
// m_settings->registerSetting(new Setting("StdOutColor", QColor(Qt::black)));
|
|
||||||
// m_settings->registerSetting(new Setting("StdErrColor", QColor(Qt::red)));
|
|
||||||
|
|
||||||
// Window Size
|
|
||||||
m_settings->registerSetting(new Setting("LaunchMaximized", false));
|
|
||||||
m_settings->registerSetting(new Setting("MinecraftWinWidth", 854));
|
|
||||||
m_settings->registerSetting(new Setting("MinecraftWinHeight", 480));
|
|
||||||
|
|
||||||
// Auto login
|
|
||||||
m_settings->registerSetting(new Setting("AutoLogin", false));
|
|
||||||
|
|
||||||
// Memory
|
|
||||||
m_settings->registerSetting(new Setting("MinMemAlloc", 512));
|
|
||||||
m_settings->registerSetting(new Setting("MaxMemAlloc", 1024));
|
|
||||||
m_settings->registerSetting(new Setting("PermGen", 64));
|
|
||||||
|
|
||||||
// Java Settings
|
|
||||||
m_settings->registerSetting(new Setting("JavaPath", ""));
|
|
||||||
m_settings->registerSetting(new Setting("LastHostname", ""));
|
|
||||||
m_settings->registerSetting(new Setting("JvmArgs", ""));
|
|
||||||
|
|
||||||
// Custom Commands
|
|
||||||
m_settings->registerSetting(new Setting("PreLaunchCommand", ""));
|
|
||||||
m_settings->registerSetting(new Setting("PostExitCommand", ""));
|
|
||||||
|
|
||||||
// The cat
|
|
||||||
m_settings->registerSetting(new Setting("TheCat", false));
|
|
||||||
|
|
||||||
|
|
||||||
m_settings->registerSetting(new Setting("InstSortMode", "Name"));
|
|
||||||
m_settings->registerSetting(new Setting("SelectedInstance", QString()));
|
|
||||||
|
|
||||||
// Persistent value for the client ID
|
|
||||||
m_settings->registerSetting(new Setting("YggdrasilClientToken", ""));
|
|
||||||
QString currentYggID = m_settings->get("YggdrasilClientToken").toString();
|
|
||||||
if (currentYggID.isEmpty())
|
|
||||||
{
|
{
|
||||||
QUuid uuid = QUuid::createUuid();
|
QString ftbRoot;
|
||||||
m_settings->set("YggdrasilClientToken", uuid.toString());
|
QFile f(QDir(m_settings->get("FTBLauncherRoot").toString())
|
||||||
|
.absoluteFilePath("ftblaunch.cfg"));
|
||||||
|
QLOG_INFO() << "Attempting to read" << f.fileName();
|
||||||
|
if (f.open(QFile::ReadOnly))
|
||||||
|
{
|
||||||
|
const QString data = QString::fromLatin1(f.readAll());
|
||||||
|
QRegularExpression exp("installPath=(.*)");
|
||||||
|
ftbRoot = QDir::cleanPath(exp.match(data).captured(1));
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
if (!ftbRoot.isEmpty())
|
||||||
|
{
|
||||||
|
if (ftbRoot.at(0).isLetter() && ftbRoot.size() > 1 && ftbRoot.at(1) == '/')
|
||||||
|
{
|
||||||
|
ftbRoot.remove(1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (ftbRoot.isEmpty())
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Failed to get FTB root path";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "FTB is installed at" << ftbRoot;
|
||||||
|
m_settings->set("FTBRoot", ftbRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QLOG_WARN() << "Couldn't open" << f.fileName() << ":" << f.errorString();
|
||||||
|
QLOG_WARN() << "This is perfectly normal if you don't have FTB installed";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Window state and geometry
|
// Folders
|
||||||
m_settings->registerSetting(new Setting("MainWindowState", ""));
|
m_settings->registerSetting("InstanceDir", "instances");
|
||||||
m_settings->registerSetting(new Setting("MainWindowGeometry", ""));
|
m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods");
|
||||||
|
m_settings->registerSetting({"LWJGLDir", "LwjglDir"}, "lwjgl");
|
||||||
|
m_settings->registerSetting("IconsDir", "icons");
|
||||||
|
|
||||||
m_settings->registerSetting(new Setting("ConsoleWindowState", ""));
|
// Editors
|
||||||
m_settings->registerSetting(new Setting("ConsoleWindowGeometry", ""));
|
m_settings->registerSetting("JsonEditor", QString());
|
||||||
|
|
||||||
|
// Console
|
||||||
|
m_settings->registerSetting("ShowConsole", true);
|
||||||
|
m_settings->registerSetting("AutoCloseConsole", true);
|
||||||
|
|
||||||
|
// Console Colors
|
||||||
|
// m_settings->registerSetting("SysMessageColor", QColor(Qt::blue));
|
||||||
|
// m_settings->registerSetting("StdOutColor", QColor(Qt::black));
|
||||||
|
// m_settings->registerSetting("StdErrColor", QColor(Qt::red));
|
||||||
|
|
||||||
|
// Window Size
|
||||||
|
m_settings->registerSetting({"LaunchMaximized", "MCWindowMaximize"}, false);
|
||||||
|
m_settings->registerSetting({"MinecraftWinWidth", "MCWindowWidth"}, 854);
|
||||||
|
m_settings->registerSetting({"MinecraftWinHeight", "MCWindowHeight"}, 480);
|
||||||
|
|
||||||
|
// Memory
|
||||||
|
m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
|
||||||
|
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 1024);
|
||||||
|
m_settings->registerSetting("PermGen", 64);
|
||||||
|
|
||||||
|
// Java Settings
|
||||||
|
m_settings->registerSetting("JavaPath", "");
|
||||||
|
m_settings->registerSetting("LastHostname", "");
|
||||||
|
m_settings->registerSetting("JvmArgs", "");
|
||||||
|
|
||||||
|
// Custom Commands
|
||||||
|
m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, "");
|
||||||
|
m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, "");
|
||||||
|
|
||||||
|
// The cat
|
||||||
|
m_settings->registerSetting("TheCat", false);
|
||||||
|
|
||||||
|
m_settings->registerSetting("InstSortMode", "Name");
|
||||||
|
m_settings->registerSetting("SelectedInstance", QString());
|
||||||
|
|
||||||
|
// Window state and geometry
|
||||||
|
m_settings->registerSetting("MainWindowState", "");
|
||||||
|
m_settings->registerSetting("MainWindowGeometry", "");
|
||||||
|
|
||||||
|
m_settings->registerSetting("ConsoleWindowState", "");
|
||||||
|
m_settings->registerSetting("ConsoleWindowGeometry", "");
|
||||||
|
|
||||||
|
m_settings->registerSetting("SettingsGeometry", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiMC::initHttpMetaCache()
|
void MultiMC::initHttpMetaCache()
|
||||||
@ -374,6 +445,7 @@ void MultiMC::initHttpMetaCache()
|
|||||||
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
|
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
|
||||||
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
|
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
|
||||||
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
|
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
|
||||||
|
m_metacache->addBase("root", QDir(".").absolutePath());
|
||||||
m_metacache->Load();
|
m_metacache->Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,27 +494,20 @@ std::shared_ptr<JavaVersionList> MultiMC::javalist()
|
|||||||
return m_javalist;
|
return m_javalist;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WINDOWS
|
void MultiMC::installUpdates(const QString &updateFilesDir, bool restartOnFinish)
|
||||||
#define UPDATER_BIN "updater.exe"
|
|
||||||
#elif LINUX
|
|
||||||
#define UPDATER_BIN "updater"
|
|
||||||
#elif OSX
|
|
||||||
#define UPDATER_BIN "updater"
|
|
||||||
#else
|
|
||||||
#error Unsupported operating system.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void MultiMC::installUpdates(const QString& updateFilesDir, bool restartOnFinish)
|
|
||||||
{
|
{
|
||||||
QLOG_INFO() << "Installing updates.";
|
QLOG_INFO() << "Installing updates.";
|
||||||
#if LINUX
|
#if LINUX
|
||||||
// On Linux, the MultiMC executable file is actually in the bin folder inside the installation directory.
|
// On Linux, the MultiMC executable file is actually in the bin folder inside the
|
||||||
|
// installation directory.
|
||||||
// This means that MultiMC's *actual* install path is the parent folder.
|
// This means that MultiMC's *actual* install path is the parent folder.
|
||||||
// We need to tell the updater to run with this directory as the install path, rather than the bin folder where the executable is.
|
// We need to tell the updater to run with this directory as the install path, rather than
|
||||||
|
// the bin folder where the executable is.
|
||||||
// On other operating systems, we'll just use the path to the executable.
|
// On other operating systems, we'll just use the path to the executable.
|
||||||
QString appDir = QFileInfo(MMC->applicationDirPath()).dir().path();
|
QString appDir = QFileInfo(MMC->applicationDirPath()).dir().path();
|
||||||
|
|
||||||
// On Linux, we also need to set the finish command to the launch script, rather than the binary.
|
// On Linux, we also need to set the finish command to the launch script, rather than the
|
||||||
|
// binary.
|
||||||
QString finishCmd = PathCombine(appDir, "MultiMC");
|
QString finishCmd = PathCombine(appDir, "MultiMC");
|
||||||
#else
|
#else
|
||||||
QString appDir = MMC->applicationDirPath();
|
QString appDir = MMC->applicationDirPath();
|
||||||
@ -450,28 +515,35 @@ void MultiMC::installUpdates(const QString& updateFilesDir, bool restartOnFinish
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Build the command we'll use to run the updater.
|
// Build the command we'll use to run the updater.
|
||||||
// Note, the above comment about the app dir path on Linux is irrelevant here because the updater binary is always in the
|
// Note, the above comment about the app dir path on Linux is irrelevant here because the
|
||||||
|
// updater binary is always in the
|
||||||
// same folder as the main binary.
|
// same folder as the main binary.
|
||||||
QString updaterBinary = PathCombine(MMC->applicationDirPath(), UPDATER_BIN);
|
QString updaterBinary = PathCombine(MMC->applicationDirPath(), UPDATER_BIN);
|
||||||
QStringList args;
|
QStringList args;
|
||||||
// ./updater --install-dir $INSTALL_DIR --package-dir $UPDATEFILES_DIR --script $UPDATEFILES_DIR/file_list.xml --wait $PID --mode main
|
// ./updater --install-dir $INSTALL_DIR --package-dir $UPDATEFILES_DIR --script
|
||||||
|
// $UPDATEFILES_DIR/file_list.xml --wait $PID --mode main
|
||||||
args << "--install-dir" << appDir;
|
args << "--install-dir" << appDir;
|
||||||
args << "--package-dir" << updateFilesDir;
|
args << "--package-dir" << updateFilesDir;
|
||||||
args << "--script" << PathCombine(updateFilesDir, "file_list.xml");
|
args << "--script" << PathCombine(updateFilesDir, "file_list.xml");
|
||||||
args << "--wait" << QString::number(MMC->applicationPid());
|
args << "--wait" << QString::number(MMC->applicationPid());
|
||||||
|
|
||||||
if (restartOnFinish)
|
if (restartOnFinish)
|
||||||
args << "--finish-cmd" << finishCmd;
|
args << "--finish-cmd" << finishCmd;
|
||||||
|
|
||||||
QLOG_INFO() << "Running updater with command" << updaterBinary << args.join(" ");
|
QLOG_INFO() << "Running updater with command" << updaterBinary << args.join(" ");
|
||||||
|
QFile::setPermissions(updaterBinary, (QFileDevice::Permission)0x7755);
|
||||||
|
|
||||||
QProcess::startDetached(updaterBinary, args);
|
if (!QProcess::startDetached(updaterBinary, args))
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Failed to start the updater process!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Now that we've started the updater, quit MultiMC.
|
// Now that we've started the updater, quit MultiMC.
|
||||||
MMC->quit();
|
MMC->quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiMC::setUpdateOnExit(const QString& updateFilesDir)
|
void MultiMC::setUpdateOnExit(const QString &updateFilesDir)
|
||||||
{
|
{
|
||||||
m_updateOnExitPath = updateFilesDir;
|
m_updateOnExitPath = updateFilesDir;
|
||||||
}
|
}
|
||||||
@ -481,5 +553,18 @@ QString MultiMC::getExitUpdatePath() const
|
|||||||
return m_updateOnExitPath;
|
return m_updateOnExitPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MultiMC::openJsonEditor(const QString &filename)
|
||||||
|
{
|
||||||
|
const QString file = QDir::current().absoluteFilePath(filename);
|
||||||
|
if (m_settings->get("JsonEditor").toString().isEmpty())
|
||||||
|
{
|
||||||
|
return QDesktopServices::openUrl(QUrl::fromLocalFile(file));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return QProcess::startDetached(m_settings->get("JsonEditor").toString(), QStringList()
|
||||||
|
<< file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#include "MultiMC.moc"
|
#include "MultiMC.moc"
|
||||||
|
14
MultiMC.h
14
MultiMC.h
@ -6,7 +6,6 @@
|
|||||||
#include "logger/QsLog.h"
|
#include "logger/QsLog.h"
|
||||||
#include "logger/QsLogDest.h"
|
#include "logger/QsLogDest.h"
|
||||||
|
|
||||||
|
|
||||||
class MinecraftVersionList;
|
class MinecraftVersionList;
|
||||||
class LWJGLVersionList;
|
class LWJGLVersionList;
|
||||||
class HttpMetaCache;
|
class HttpMetaCache;
|
||||||
@ -107,12 +106,12 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
* Installs update from the given update files directory.
|
* Installs update from the given update files directory.
|
||||||
*/
|
*/
|
||||||
void installUpdates(const QString& updateFilesDir, bool restartOnFinish=false);
|
void installUpdates(const QString &updateFilesDir, bool restartOnFinish = false);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Sets MultiMC to install updates from the given directory when it exits.
|
* Sets MultiMC to install updates from the given directory when it exits.
|
||||||
*/
|
*/
|
||||||
void setUpdateOnExit(const QString& updateFilesDir);
|
void setUpdateOnExit(const QString &updateFilesDir);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Gets the path to install updates from on exit.
|
* Gets the path to install updates from on exit.
|
||||||
@ -120,6 +119,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
QString getExitUpdatePath() const;
|
QString getExitUpdatePath() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Opens a json file using either a system default editor, or, if note empty, the editor
|
||||||
|
* specified in the settings
|
||||||
|
*/
|
||||||
|
bool openJsonEditor(const QString &filename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initLogger();
|
void initLogger();
|
||||||
|
|
||||||
@ -130,6 +135,9 @@ private:
|
|||||||
void initTranslations();
|
void initTranslations();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class UpdateCheckerTest;
|
||||||
|
friend class DownloadUpdateTaskTest;
|
||||||
|
|
||||||
std::shared_ptr<QTranslator> m_qt_translator;
|
std::shared_ptr<QTranslator> m_qt_translator;
|
||||||
std::shared_ptr<QTranslator> m_mmc_translator;
|
std::shared_ptr<QTranslator> m_mmc_translator;
|
||||||
std::shared_ptr<SettingsObject> m_settings;
|
std::shared_ptr<SettingsObject> m_settings;
|
||||||
|
27
MultiMC.manifest
Normal file
27
MultiMC.manifest
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<assemblyIdentity name="MultiMC.Application.5" type="win32" version="5.0.0.0" />
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges>
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
<description>Custom Minecraft launcher for managing multiple installs.</description>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!--The ID below indicates app support for Windows Vista -->
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||||
|
<!--The ID below indicates app support for Windows 7 -->
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||||
|
<!--The ID below indicates app support for Windows Developer Preview / Windows 8 -->
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</assembly>
|
10
README.md
10
README.md
@ -11,6 +11,16 @@ Check [BUILD.md](BUILD.md) for build instructions.
|
|||||||
## Contributing
|
## Contributing
|
||||||
The repository is currently managed by @peterix and @drayshak - we're the ones likely to review pull requests. If you'd like to contribute to the project please talk to us on IRC (Esper/#MultiMC) first! This helps us organise ideas and keep in contact with you, and we're unlikely to accept anything blindly.
|
The repository is currently managed by @peterix and @drayshak - we're the ones likely to review pull requests. If you'd like to contribute to the project please talk to us on IRC (Esper/#MultiMC) first! This helps us organise ideas and keep in contact with you, and we're unlikely to accept anything blindly.
|
||||||
|
|
||||||
|
We use [Clang Format](http://clang.llvm.org/docs/ClangFormat.html) to format the project. We highly recommend setting it up so the project stays well formatted, but there are issues with it on Windows. If you have trouble setting it up, check [.clang-format](.clang-format) manually. We don't accept pull requests with poor formatting. If you have questions, talk to us on IRC (Esper/#MultiMC) _before_ submitting a pull request.
|
||||||
|
|
||||||
|
## Forking/Redistributing
|
||||||
|
We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.
|
||||||
|
|
||||||
|
Part of the reason for using the Apache license is we don't want people using the "MultiMC" name when redistributing the project. This means people must take the time to go through the source code and remove all references to "MultiMC", including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).
|
||||||
|
|
||||||
|
Apache covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork *without* implying that you have our blessing.
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Copyright © 2013 MultiMC Contributors
|
Copyright © 2013 MultiMC Contributors
|
||||||
|
|
||||||
|
40
cmake/MacOSXBundleInfo.plist.in
Normal file
40
cmake/MacOSXBundleInfo.plist.in
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string>NSApplication</string>
|
||||||
|
<key>NSHighResolutionCapable</key>
|
||||||
|
<string>True</string>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>English</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleGetInfoString</key>
|
||||||
|
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleLongVersionString</key>
|
||||||
|
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||||
|
<key>CSResourcesFileMapped</key>
|
||||||
|
<true/>
|
||||||
|
<key>LSRequiresCarbon</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -8,21 +8,36 @@
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if (argc == 3)
|
if (argc != 3)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
unpack_200(argv[1], argv[2]);
|
|
||||||
}
|
|
||||||
catch (std::runtime_error &e)
|
|
||||||
{
|
|
||||||
std::cerr << "Bad things happened: " << e.what() << std::endl;
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
std::cerr << "Simple pack200 unpacker!" << std::endl << "Run like this:" << std::endl
|
std::cerr << "Simple pack200 unpacker!" << std::endl << "Run like this:" << std::endl
|
||||||
<< " " << argv[0] << " input.jar.lzma output.jar" << std::endl;
|
<< " " << argv[0] << " input.jar.lzma output.jar" << std::endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *input = fopen(argv[1], "rb");
|
||||||
|
FILE *output = fopen(argv[2], "wb");
|
||||||
|
if (!input)
|
||||||
|
{
|
||||||
|
std::cerr << "Can't open input file";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (!output)
|
||||||
|
{
|
||||||
|
fclose(output);
|
||||||
|
std::cerr << "Can't open output file";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
unpack_200(input, output);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error &e)
|
||||||
|
{
|
||||||
|
std::cerr << "Bad things happened: " << e.what() << std::endl;
|
||||||
|
fclose(input);
|
||||||
|
fclose(output);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -34,4 +34,4 @@
|
|||||||
* @return void
|
* @return void
|
||||||
* @throw std::runtime_error for any error encountered
|
* @throw std::runtime_error for any error encountered
|
||||||
*/
|
*/
|
||||||
void unpack_200(std::string input_path, std::string output_path);
|
void unpack_200(FILE * input, FILE * output);
|
||||||
|
@ -94,20 +94,9 @@ static int read_magic(unpacker *u, char peek[], int peeklen)
|
|||||||
return magic;
|
return magic;
|
||||||
}
|
}
|
||||||
|
|
||||||
void unpack_200(std::string input_path, std::string output_path)
|
void unpack_200(FILE *input, FILE *output)
|
||||||
{
|
{
|
||||||
unpacker u;
|
unpacker u;
|
||||||
FILE *input = fopen(input_path.c_str(), "rb");
|
|
||||||
if (!input)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Can't open input file" + input_path);
|
|
||||||
}
|
|
||||||
FILE *output = fopen(output_path.c_str(), "wb");
|
|
||||||
if (!output)
|
|
||||||
{
|
|
||||||
fclose(output);
|
|
||||||
throw std::runtime_error("Can't open output file" + output_path);
|
|
||||||
}
|
|
||||||
u.init(read_input_via_stdio);
|
u.init(read_input_via_stdio);
|
||||||
|
|
||||||
// initialize jar output
|
// initialize jar output
|
||||||
|
@ -5,44 +5,27 @@ find_package(Qt5Core REQUIRED)
|
|||||||
|
|
||||||
# Include Qt headers.
|
# Include Qt headers.
|
||||||
include_directories(${Qt5Base_INCLUDE_DIRS})
|
include_directories(${Qt5Base_INCLUDE_DIRS})
|
||||||
include_directories(${Qt5Network_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
SET(LIBSETTINGS_HEADERS
|
|
||||||
include/libsettings_config.h
|
|
||||||
|
|
||||||
include/inifile.h
|
|
||||||
|
|
||||||
include/settingsobject.h
|
|
||||||
include/setting.h
|
|
||||||
include/overridesetting.h
|
|
||||||
|
|
||||||
include/basicsettingsobject.h
|
|
||||||
include/inisettingsobject.h
|
|
||||||
|
|
||||||
include/keyring.h
|
|
||||||
)
|
|
||||||
|
|
||||||
SET(LIBSETTINGS_HEADERS_PRIVATE
|
|
||||||
src/stubkeyring.h
|
|
||||||
)
|
|
||||||
|
|
||||||
SET(LIBSETTINGS_SOURCES
|
SET(LIBSETTINGS_SOURCES
|
||||||
src/inifile.cpp
|
libsettings_config.h
|
||||||
|
|
||||||
src/settingsobject.cpp
|
inifile.h
|
||||||
src/setting.cpp
|
inifile.cpp
|
||||||
src/overridesetting.cpp
|
|
||||||
|
|
||||||
src/basicsettingsobject.cpp
|
settingsobject.h
|
||||||
src/inisettingsobject.cpp
|
settingsobject.cpp
|
||||||
|
inisettingsobject.h
|
||||||
|
inisettingsobject.cpp
|
||||||
|
|
||||||
src/keyring.cpp
|
setting.h
|
||||||
src/stubkeyring.cpp
|
setting.cpp
|
||||||
|
overridesetting.h
|
||||||
|
overridesetting.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set the include dir path.
|
# Set the include dir path.
|
||||||
SET(LIBSETTINGS_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
|
SET(LIBSETTINGS_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE)
|
||||||
include_directories(${LIBSETTINGS_INCLUDE_DIR})
|
|
||||||
|
|
||||||
# Static link!
|
# Static link!
|
||||||
ADD_DEFINITIONS(-DLIBSETTINGS_STATIC)
|
ADD_DEFINITIONS(-DLIBSETTINGS_STATIC)
|
||||||
@ -59,6 +42,6 @@ IF(MultiMC_CODE_COVERAGE)
|
|||||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0 --coverage")
|
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0 --coverage")
|
||||||
ENDIF(MultiMC_CODE_COVERAGE)
|
ENDIF(MultiMC_CODE_COVERAGE)
|
||||||
|
|
||||||
add_library(libSettings STATIC ${LIBSETTINGS_SOURCES} ${LIBSETTINGS_HEADERS} ${LIBSETTINGS_HEADERS_PRIVATE})
|
add_library(libSettings STATIC ${LIBSETTINGS_SOURCES})
|
||||||
qt5_use_modules(libSettings Core)
|
qt5_use_modules(libSettings Core)
|
||||||
target_link_libraries(libSettings)
|
target_link_libraries(libSettings)
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
/* Copyright 2013 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
#include "libsettings_config.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file libsettings/include/keyring.h
|
|
||||||
* Access to System Keyrings
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The Keyring class
|
|
||||||
* the System Keyring/Keychain/Wallet/Vault/etc
|
|
||||||
*/
|
|
||||||
class LIBSETTINGS_EXPORT Keyring
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief virtual dtor
|
|
||||||
*/
|
|
||||||
virtual ~Keyring() {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief the System Keyring instance
|
|
||||||
* @return the Keyring instance
|
|
||||||
*/
|
|
||||||
static Keyring *instance();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief store a password in the Keyring
|
|
||||||
* @param service the service name
|
|
||||||
* @param username the account name
|
|
||||||
* @param password the password to store
|
|
||||||
* @return success
|
|
||||||
*/
|
|
||||||
virtual bool storePassword(QString service, QString username, QString password) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief get a password from the Keyring
|
|
||||||
* @param service the service name
|
|
||||||
* @param username the account name
|
|
||||||
* @return the password (success=!isNull())
|
|
||||||
*/
|
|
||||||
virtual QString getPassword(QString service, QString username) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief lookup a password
|
|
||||||
* @param service the service name
|
|
||||||
* @param username the account name
|
|
||||||
* @return wether the password is available
|
|
||||||
*/
|
|
||||||
virtual bool hasPassword(QString service, QString username) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief get a list of all stored accounts.
|
|
||||||
* @param service the service name
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
virtual QStringList getStoredAccounts(QString service) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Remove the specified account from storage
|
|
||||||
* @param service the service name
|
|
||||||
* @param username the account name
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
virtual void removeStoredAccount(QString service, QString username) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/// fall back to StubKeyring if false
|
|
||||||
virtual bool isValid()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static Keyring *m_instance;
|
|
||||||
static void destroy();
|
|
||||||
};
|
|
@ -13,7 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "include/inifile.h"
|
#include "inifile.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
@ -13,8 +13,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "include/inisettingsobject.h"
|
#include "inisettingsobject.h"
|
||||||
#include "include/setting.h"
|
#include "setting.h"
|
||||||
|
|
||||||
INISettingsObject::INISettingsObject(const QString &path, QObject *parent)
|
INISettingsObject::INISettingsObject(const QString &path, QObject *parent)
|
||||||
: SettingsObject(parent)
|
: SettingsObject(parent)
|
||||||
@ -32,31 +32,45 @@ void INISettingsObject::changeSetting(const Setting &setting, QVariant value)
|
|||||||
{
|
{
|
||||||
if (contains(setting.id()))
|
if (contains(setting.id()))
|
||||||
{
|
{
|
||||||
|
// valid value -> set the main config, remove all the sysnonyms
|
||||||
if (value.isValid())
|
if (value.isValid())
|
||||||
m_ini.set(setting.configKey(), value);
|
{
|
||||||
|
auto list = setting.configKeys();
|
||||||
|
m_ini.set(list.takeFirst(), value);
|
||||||
|
for(auto iter: list)
|
||||||
|
m_ini.remove(iter);
|
||||||
|
}
|
||||||
|
// invalid -> remove all (just like resetSetting)
|
||||||
else
|
else
|
||||||
m_ini.remove(setting.configKey());
|
{
|
||||||
|
for(auto iter: setting.configKeys())
|
||||||
|
m_ini.remove(iter);
|
||||||
|
}
|
||||||
m_ini.saveFile(m_filePath);
|
m_ini.saveFile(m_filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void INISettingsObject::resetSetting(const Setting &setting)
|
void INISettingsObject::resetSetting(const Setting &setting)
|
||||||
{
|
{
|
||||||
|
// if we have the setting, remove all the synonyms. ALL OF THEM
|
||||||
if (contains(setting.id()))
|
if (contains(setting.id()))
|
||||||
{
|
{
|
||||||
m_ini.remove(setting.configKey());
|
for(auto iter: setting.configKeys())
|
||||||
|
m_ini.remove(iter);
|
||||||
m_ini.saveFile(m_filePath);
|
m_ini.saveFile(m_filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant INISettingsObject::retrieveValue(const Setting &setting)
|
QVariant INISettingsObject::retrieveValue(const Setting &setting)
|
||||||
{
|
{
|
||||||
|
// if we have the setting, return value of the first matching synonym
|
||||||
if (contains(setting.id()))
|
if (contains(setting.id()))
|
||||||
{
|
{
|
||||||
return m_ini.get(setting.configKey(), QVariant());
|
for(auto iter: setting.configKeys())
|
||||||
}
|
{
|
||||||
else
|
if(m_ini.contains(iter))
|
||||||
{
|
return m_ini[iter];
|
||||||
return QVariant();
|
}
|
||||||
}
|
}
|
||||||
|
return QVariant();
|
||||||
}
|
}
|
@ -26,3 +26,4 @@
|
|||||||
#define LIBSETTINGS_EXPORT Q_DECL_IMPORT
|
#define LIBSETTINGS_EXPORT Q_DECL_IMPORT
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -13,10 +13,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "include/overridesetting.h"
|
#include "overridesetting.h"
|
||||||
|
|
||||||
OverrideSetting::OverrideSetting(const QString &name, Setting *other, QObject *parent)
|
OverrideSetting::OverrideSetting(std::shared_ptr<Setting> other)
|
||||||
: Setting(name, QVariant(), parent)
|
: Setting(other->configKeys(), QVariant())
|
||||||
{
|
{
|
||||||
m_other = other;
|
m_other = other;
|
||||||
}
|
}
|
@ -16,6 +16,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "setting.h"
|
#include "setting.h"
|
||||||
|
|
||||||
@ -31,10 +32,10 @@ class LIBSETTINGS_EXPORT OverrideSetting : public Setting
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit OverrideSetting(const QString &name, Setting *other, QObject *parent = 0);
|
explicit OverrideSetting(std::shared_ptr<Setting> other);
|
||||||
|
|
||||||
virtual QVariant defValue() const;
|
virtual QVariant defValue() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Setting *m_other;
|
std::shared_ptr<Setting> m_other;
|
||||||
};
|
};
|
@ -13,17 +13,17 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "include/setting.h"
|
#include "setting.h"
|
||||||
#include "include/settingsobject.h"
|
#include "settingsobject.h"
|
||||||
|
|
||||||
Setting::Setting(QString id, QVariant defVal, QObject *parent)
|
Setting::Setting(QStringList synonyms, QVariant defVal)
|
||||||
: QObject(parent), m_id(id), m_defVal(defVal)
|
: QObject(), m_synonyms(synonyms), m_defVal(defVal)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant Setting::get() const
|
QVariant Setting::get() const
|
||||||
{
|
{
|
||||||
SettingsObject *sbase = qobject_cast<SettingsObject *>(parent());
|
SettingsObject *sbase = m_storage;
|
||||||
if (!sbase)
|
if (!sbase)
|
||||||
{
|
{
|
||||||
return defValue();
|
return defValue();
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "libsettings_config.h"
|
#include "libsettings_config.h"
|
||||||
|
|
||||||
@ -29,11 +31,16 @@ class LIBSETTINGS_EXPORT Setting : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
/*!
|
/**
|
||||||
* \brief Constructs a new Setting object with the given parent.
|
* Construct a Setting
|
||||||
* \param parent The Setting's parent object.
|
*
|
||||||
|
* Synonyms are all the possible names used in the settings object, in order of preference.
|
||||||
|
* First synonym is the ID, which identifies the setting in MultiMC.
|
||||||
|
*
|
||||||
|
* defVal is the default value that will be returned when the settings object
|
||||||
|
* doesn't have any value for this setting.
|
||||||
*/
|
*/
|
||||||
explicit Setting(QString id, QVariant defVal = QVariant(), QObject *parent = 0);
|
explicit Setting(QStringList synonyms, QVariant defVal = QVariant());
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Gets this setting's ID.
|
* \brief Gets this setting's ID.
|
||||||
@ -44,7 +51,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual QString id() const
|
virtual QString id() const
|
||||||
{
|
{
|
||||||
return m_id;
|
return m_synonyms.first();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -53,9 +60,9 @@ public:
|
|||||||
* the same as the setting's ID, but it can be different.
|
* the same as the setting's ID, but it can be different.
|
||||||
* \return The setting's config file key.
|
* \return The setting's config file key.
|
||||||
*/
|
*/
|
||||||
virtual QString configKey() const
|
virtual QStringList configKeys() const
|
||||||
{
|
{
|
||||||
return id();
|
return m_synonyms;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -67,16 +74,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual QVariant get() const;
|
virtual QVariant get() const;
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Gets this setting's actual value (I.E. not as a QVariant).
|
|
||||||
* This function is just shorthand for get().value<T>()
|
|
||||||
* \return The setting's actual value.
|
|
||||||
*/
|
|
||||||
template <typename T> inline T value() const
|
|
||||||
{
|
|
||||||
return get().value<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Gets this setting's default value.
|
* \brief Gets this setting's default value.
|
||||||
* \return The default value of this setting.
|
* \return The default value of this setting.
|
||||||
@ -111,11 +108,12 @@ slots:
|
|||||||
* \brief Reset the setting to default
|
* \brief Reset the setting to default
|
||||||
* This is done by emitting the settingReset() signal which will then be
|
* This is done by emitting the settingReset() signal which will then be
|
||||||
* handled by the SettingsObject object and cause the setting to change.
|
* handled by the SettingsObject object and cause the setting to change.
|
||||||
* \param value The new value.
|
|
||||||
*/
|
*/
|
||||||
virtual void reset();
|
virtual void reset();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString m_id;
|
friend class SettingsObject;
|
||||||
|
SettingsObject * m_storage;
|
||||||
|
QStringList m_synonyms;
|
||||||
QVariant m_defVal;
|
QVariant m_defVal;
|
||||||
};
|
};
|
@ -13,8 +13,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "include/settingsobject.h"
|
#include "settingsobject.h"
|
||||||
#include "include/setting.h"
|
#include "setting.h"
|
||||||
|
#include "overridesetting.h"
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
@ -22,17 +23,49 @@ SettingsObject::SettingsObject(QObject *parent) : QObject(parent)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsObject::~SettingsObject()
|
||||||
|
{
|
||||||
|
m_settings.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Setting> SettingsObject::registerOverride(std::shared_ptr<Setting> original)
|
||||||
|
{
|
||||||
|
if (contains(original->id()))
|
||||||
|
{
|
||||||
|
qDebug(QString("Failed to register setting %1. ID already exists.")
|
||||||
|
.arg(original->id())
|
||||||
|
.toUtf8());
|
||||||
|
return nullptr; // Fail
|
||||||
|
}
|
||||||
|
auto override = std::make_shared<OverrideSetting>(original);
|
||||||
|
override->m_storage = this;
|
||||||
|
connectSignals(*override);
|
||||||
|
m_settings.insert(override->id(), override);
|
||||||
|
return override;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Setting> SettingsObject::registerSetting(QStringList synonyms, QVariant defVal)
|
||||||
|
{
|
||||||
|
if (synonyms.empty())
|
||||||
|
return nullptr;
|
||||||
|
if (contains(synonyms.first()))
|
||||||
|
{
|
||||||
|
qDebug(QString("Failed to register setting %1. ID already exists.")
|
||||||
|
.arg(synonyms.first())
|
||||||
|
.toUtf8());
|
||||||
|
return nullptr; // Fail
|
||||||
|
}
|
||||||
|
auto setting = std::make_shared<Setting>(synonyms, defVal);
|
||||||
|
setting->m_storage = this;
|
||||||
|
connectSignals(*setting);
|
||||||
|
m_settings.insert(setting->id(), setting);
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
bool SettingsObject::registerSetting(Setting *setting)
|
bool SettingsObject::registerSetting(Setting *setting)
|
||||||
{
|
{
|
||||||
// Check if setting is null or we already have a setting with the same ID.
|
|
||||||
if (!setting)
|
|
||||||
{
|
|
||||||
qDebug(QString("Failed to register setting. Setting is null.")
|
|
||||||
.arg(setting->id())
|
|
||||||
.toUtf8());
|
|
||||||
return false; // Fail
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contains(setting->id()))
|
if (contains(setting->id()))
|
||||||
{
|
{
|
||||||
qDebug(QString("Failed to register setting %1. ID already exists.")
|
qDebug(QString("Failed to register setting %1. ID already exists.")
|
||||||
@ -50,21 +83,8 @@ bool SettingsObject::registerSetting(Setting *setting)
|
|||||||
// qDebug(QString("Registered setting %1.").arg(setting->id()).toUtf8());
|
// qDebug(QString("Registered setting %1.").arg(setting->id()).toUtf8());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
void SettingsObject::unregisterSetting(Setting *setting)
|
std::shared_ptr<Setting> SettingsObject::getSetting(const QString &id) const
|
||||||
{
|
|
||||||
if (!setting || !m_settings.contains(setting->id()))
|
|
||||||
return; // We can't unregister something that's not registered.
|
|
||||||
|
|
||||||
m_settings.remove(setting->id());
|
|
||||||
|
|
||||||
// Disconnect signals.
|
|
||||||
disconnectSignals(*setting);
|
|
||||||
|
|
||||||
setting->setParent(NULL); // Drop ownership.
|
|
||||||
}
|
|
||||||
|
|
||||||
Setting *SettingsObject::getSetting(const QString &id) const
|
|
||||||
{
|
{
|
||||||
// Make sure there is a setting with the given ID.
|
// Make sure there is a setting with the given ID.
|
||||||
if (!m_settings.contains(id))
|
if (!m_settings.contains(id))
|
||||||
@ -75,13 +95,13 @@ Setting *SettingsObject::getSetting(const QString &id) const
|
|||||||
|
|
||||||
QVariant SettingsObject::get(const QString &id) const
|
QVariant SettingsObject::get(const QString &id) const
|
||||||
{
|
{
|
||||||
Setting *setting = getSetting(id);
|
auto setting = getSetting(id);
|
||||||
return (setting ? setting->get() : QVariant());
|
return (setting ? setting->get() : QVariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SettingsObject::set(const QString &id, QVariant value)
|
bool SettingsObject::set(const QString &id, QVariant value)
|
||||||
{
|
{
|
||||||
Setting *setting = getSetting(id);
|
auto setting = getSetting(id);
|
||||||
if (!setting)
|
if (!setting)
|
||||||
{
|
{
|
||||||
qDebug(QString("Error changing setting %1. Setting doesn't exist.").arg(id).toUtf8());
|
qDebug(QString("Error changing setting %1. Setting doesn't exist.").arg(id).toUtf8());
|
||||||
@ -96,16 +116,11 @@ bool SettingsObject::set(const QString &id, QVariant value)
|
|||||||
|
|
||||||
void SettingsObject::reset(const QString &id) const
|
void SettingsObject::reset(const QString &id) const
|
||||||
{
|
{
|
||||||
Setting *setting = getSetting(id);
|
auto setting = getSetting(id);
|
||||||
if (setting)
|
if (setting)
|
||||||
setting->reset();
|
setting->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Setting *> SettingsObject::getSettings()
|
|
||||||
{
|
|
||||||
return m_settings.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SettingsObject::contains(const QString &id)
|
bool SettingsObject::contains(const QString &id)
|
||||||
{
|
{
|
||||||
return m_settings.contains(id);
|
return m_settings.contains(id);
|
||||||
@ -121,16 +136,3 @@ void SettingsObject::connectSignals(const Setting &setting)
|
|||||||
connect(&setting, SIGNAL(settingReset(Setting)), SLOT(resetSetting(const Setting &)));
|
connect(&setting, SIGNAL(settingReset(Setting)), SLOT(resetSetting(const Setting &)));
|
||||||
connect(&setting, SIGNAL(settingReset(Setting)), SIGNAL(settingReset(const Setting &)));
|
connect(&setting, SIGNAL(settingReset(Setting)), SIGNAL(settingReset(const Setting &)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsObject::disconnectSignals(const Setting &setting)
|
|
||||||
{
|
|
||||||
setting.disconnect(SIGNAL(settingChanged(const Setting &, QVariant)), this,
|
|
||||||
SLOT(changeSetting(const Setting &, QVariant)));
|
|
||||||
setting.disconnect(SIGNAL(settingChanged(const Setting &, QVariant)), this,
|
|
||||||
SIGNAL(settingChanged(const Setting &, QVariant)));
|
|
||||||
|
|
||||||
setting.disconnect(SIGNAL(settingReset(const Setting &, QVariant)), this,
|
|
||||||
SLOT(resetSetting(const Setting &, QVariant)));
|
|
||||||
setting.disconnect(SIGNAL(settingReset(const Setting &, QVariant)), this,
|
|
||||||
SIGNAL(settingReset(const Setting &, QVariant)));
|
|
||||||
}
|
|
@ -17,6 +17,9 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "libsettings_config.h"
|
#include "libsettings_config.h"
|
||||||
|
|
||||||
@ -39,32 +42,37 @@ class LIBSETTINGS_EXPORT SettingsObject : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit SettingsObject(QObject *parent = 0);
|
explicit SettingsObject(QObject *parent = 0);
|
||||||
|
virtual ~SettingsObject();
|
||||||
/*!
|
/*!
|
||||||
* \brief Registers the given setting with this SettingsObject and connects the necessary
|
* Registers an override setting for the given original setting in this settings object
|
||||||
* signals.
|
*
|
||||||
* This will fail if there is already a setting with the same ID as
|
* This will fail if there is already a setting with the same ID as
|
||||||
* the one that is being registered.
|
* the one that is being registered.
|
||||||
* \note Registering a setting object causes the SettingsObject to take ownership
|
* \return A valid Setting shared pointer if successful.
|
||||||
* of the object. This means that setting's parent will be set to the object
|
|
||||||
* it was registered with. Because the object it was registered with has taken
|
|
||||||
* ownership, it becomes responsible for managing that setting object's memory.
|
|
||||||
* \warning Do \b not delete the setting after registering it.
|
|
||||||
* \param setting A pointer to the setting that will be registered.
|
|
||||||
* \return True if successful. False if registry failed.
|
|
||||||
*/
|
*/
|
||||||
virtual bool registerSetting(Setting *setting);
|
std::shared_ptr<Setting> registerOverride(std::shared_ptr<Setting> original);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Unregisters the given setting from this SettingsObject and disconnects its
|
* Registers the given setting with this SettingsObject and connects the necessary signals.
|
||||||
* signals.
|
*
|
||||||
* \note This does not delete the setting. Furthermore, when the setting is
|
* This will fail if there is already a setting with the same ID as
|
||||||
* unregistered, the SettingsObject drops ownership of the setting. This means
|
* the one that is being registered.
|
||||||
* that if you unregister a setting, its parent is set to null and you become
|
* \return A valid Setting shared pointer if successful.
|
||||||
* responsible for freeing its memory.
|
|
||||||
* \param setting The setting to unregister.
|
|
||||||
*/
|
*/
|
||||||
virtual void unregisterSetting(Setting *setting);
|
std::shared_ptr<Setting> registerSetting(QStringList synonyms,
|
||||||
|
QVariant defVal = QVariant());
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Registers the given setting with this SettingsObject and connects the necessary signals.
|
||||||
|
*
|
||||||
|
* This will fail if there is already a setting with the same ID as
|
||||||
|
* the one that is being registered.
|
||||||
|
* \return A valid Setting shared pointer if successful.
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Setting> registerSetting(QString id, QVariant defVal = QVariant())
|
||||||
|
{
|
||||||
|
return registerSetting(QStringList(id), defVal);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Gets the setting with the given ID.
|
* \brief Gets the setting with the given ID.
|
||||||
@ -73,18 +81,7 @@ public:
|
|||||||
* Returns null if there is no setting with the given ID.
|
* Returns null if there is no setting with the given ID.
|
||||||
* \sa operator []()
|
* \sa operator []()
|
||||||
*/
|
*/
|
||||||
virtual Setting *getSetting(const QString &id) const;
|
std::shared_ptr<Setting> getSetting(const QString &id) const;
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Same as getSetting()
|
|
||||||
* \param id The ID of the setting to get.
|
|
||||||
* \return A pointer to the setting with the given ID.
|
|
||||||
* \sa getSetting()
|
|
||||||
*/
|
|
||||||
inline Setting *operator[](const QString &id)
|
|
||||||
{
|
|
||||||
return getSetting(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Gets the value of the setting with the given ID.
|
* \brief Gets the value of the setting with the given ID.
|
||||||
@ -92,7 +89,7 @@ public:
|
|||||||
* \return The setting's value as a QVariant.
|
* \return The setting's value as a QVariant.
|
||||||
* If no setting with the given ID exists, returns an invalid QVariant.
|
* If no setting with the given ID exists, returns an invalid QVariant.
|
||||||
*/
|
*/
|
||||||
virtual QVariant get(const QString &id) const;
|
QVariant get(const QString &id) const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Sets the value of the setting with the given ID.
|
* \brief Sets the value of the setting with the given ID.
|
||||||
@ -101,27 +98,20 @@ public:
|
|||||||
* \param value The new value of the setting.
|
* \param value The new value of the setting.
|
||||||
* \return True if successful, false if it failed.
|
* \return True if successful, false if it failed.
|
||||||
*/
|
*/
|
||||||
virtual bool set(const QString &id, QVariant value);
|
bool set(const QString &id, QVariant value);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Reverts the setting with the given ID to default.
|
* \brief Reverts the setting with the given ID to default.
|
||||||
* \param id The ID of the setting to reset.
|
* \param id The ID of the setting to reset.
|
||||||
*/
|
*/
|
||||||
virtual void reset(const QString &id) const;
|
void reset(const QString &id) const;
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Gets a QList with pointers to all of the registered settings.
|
|
||||||
* The order of the entries in the list is undefined.
|
|
||||||
* \return A QList with pointers to all registered settings.
|
|
||||||
*/
|
|
||||||
virtual QList<Setting *> getSettings();
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Checks if this SettingsObject contains a setting with the given ID.
|
* \brief Checks if this SettingsObject contains a setting with the given ID.
|
||||||
* \param id The ID to check for.
|
* \param id The ID to check for.
|
||||||
* \return True if the SettingsObject has a setting with the given ID.
|
* \return True if the SettingsObject has a setting with the given ID.
|
||||||
*/
|
*/
|
||||||
virtual bool contains(const QString &id);
|
bool contains(const QString &id);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/*!
|
/*!
|
||||||
@ -167,13 +157,7 @@ protected:
|
|||||||
* \brief Connects the necessary signals to the given Setting.
|
* \brief Connects the necessary signals to the given Setting.
|
||||||
* \param setting The setting to connect.
|
* \param setting The setting to connect.
|
||||||
*/
|
*/
|
||||||
virtual void connectSignals(const Setting &setting);
|
void connectSignals(const Setting &setting);
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Disconnects signals from the given Setting.
|
|
||||||
* \param setting The setting to disconnect.
|
|
||||||
*/
|
|
||||||
virtual void disconnectSignals(const Setting &setting);
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Function used by Setting objects to get their values from the SettingsObject.
|
* \brief Function used by Setting objects to get their values from the SettingsObject.
|
||||||
@ -185,5 +169,5 @@ protected:
|
|||||||
friend class Setting;
|
friend class Setting;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<QString, Setting *> m_settings;
|
QMap<QString, std::shared_ptr<Setting>> m_settings;
|
||||||
};
|
};
|
@ -1,63 +0,0 @@
|
|||||||
/* Copyright 2013 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "include/keyring.h"
|
|
||||||
|
|
||||||
#include "osutils.h"
|
|
||||||
|
|
||||||
#include "stubkeyring.h"
|
|
||||||
|
|
||||||
// system specific keyrings
|
|
||||||
/*#if defined(OSX)
|
|
||||||
class OSXKeychain;
|
|
||||||
#define KEYRING OSXKeychain
|
|
||||||
#elif defined(LINUX)
|
|
||||||
class XDGKeyring;
|
|
||||||
#define KEYRING XDGKeyring
|
|
||||||
#elif defined(WINDOWS)
|
|
||||||
class Win32Keystore;
|
|
||||||
#define KEYRING Win32Keystore
|
|
||||||
#else
|
|
||||||
#pragma message Keyrings are not supported on your os. Falling back to the insecure StubKeyring
|
|
||||||
#endif*/
|
|
||||||
|
|
||||||
Keyring *Keyring::instance()
|
|
||||||
{
|
|
||||||
if (m_instance == nullptr)
|
|
||||||
{
|
|
||||||
#ifdef KEYRING
|
|
||||||
m_instance = new KEYRING();
|
|
||||||
if (!m_instance->isValid())
|
|
||||||
{
|
|
||||||
qWarning("Could not create SystemKeyring! falling back to StubKeyring.");
|
|
||||||
m_instance = new StubKeyring();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
qWarning("Keyrings are not supported on your OS. Fallback StubKeyring is insecure!");
|
|
||||||
m_instance = new StubKeyring();
|
|
||||||
#endif
|
|
||||||
atexit(Keyring::destroy);
|
|
||||||
}
|
|
||||||
return m_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Keyring::destroy()
|
|
||||||
{
|
|
||||||
delete m_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
Keyring *Keyring::m_instance;
|
|
@ -1,105 +0,0 @@
|
|||||||
/* Copyright 2013 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "stubkeyring.h"
|
|
||||||
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
// Scrambling
|
|
||||||
// this is NOT SAFE, but it's not plain either.
|
|
||||||
int scrambler = 0x9586309;
|
|
||||||
|
|
||||||
QString scramble(QString in_)
|
|
||||||
{
|
|
||||||
QByteArray in = in_.toUtf8();
|
|
||||||
QByteArray out;
|
|
||||||
for (int i = 0; i < in.length(); i++)
|
|
||||||
out.append(in.at(i) ^ scrambler);
|
|
||||||
return QString::fromUtf8(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QString base64(QString in)
|
|
||||||
{
|
|
||||||
return QString(in.toUtf8().toBase64());
|
|
||||||
}
|
|
||||||
inline QString unbase64(QString in)
|
|
||||||
{
|
|
||||||
return QString::fromUtf8(QByteArray::fromBase64(in.toLatin1()));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QString scramble64(QString in)
|
|
||||||
{
|
|
||||||
return base64(scramble(in));
|
|
||||||
}
|
|
||||||
inline QString unscramble64(QString in)
|
|
||||||
{
|
|
||||||
return scramble(unbase64(in));
|
|
||||||
}
|
|
||||||
|
|
||||||
// StubKeyring implementation
|
|
||||||
inline QString generateKey(QString service, QString username)
|
|
||||||
{
|
|
||||||
return QString("%1/%2").arg(base64(service)).arg(scramble64(username));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool StubKeyring::storePassword(QString service, QString username, QString password)
|
|
||||||
{
|
|
||||||
m_settings.setValue(generateKey(service, username), scramble64(password));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString StubKeyring::getPassword(QString service, QString username)
|
|
||||||
{
|
|
||||||
QString key = generateKey(service, username);
|
|
||||||
if (!m_settings.contains(key))
|
|
||||||
return QString();
|
|
||||||
return unscramble64(m_settings.value(key).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool StubKeyring::hasPassword(QString service, QString username)
|
|
||||||
{
|
|
||||||
return m_settings.contains(generateKey(service, username));
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList StubKeyring::getStoredAccounts(QString service)
|
|
||||||
{
|
|
||||||
service = base64(service).append('/');
|
|
||||||
QStringList out;
|
|
||||||
QStringList in(m_settings.allKeys());
|
|
||||||
QStringListIterator it(in);
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
QString c = it.next();
|
|
||||||
if (c.startsWith(service))
|
|
||||||
out << unscramble64(c.mid(service.length()));
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StubKeyring::removeStoredAccount(QString service, QString username)
|
|
||||||
{
|
|
||||||
QString key = generateKey(service, username);
|
|
||||||
m_settings.remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: this needs tweaking/changes for user account level storage
|
|
||||||
StubKeyring::StubKeyring()
|
|
||||||
:
|
|
||||||
// m_settings(QSettings::UserScope, "Orochimarufan", "Keyring")
|
|
||||||
m_settings("keyring.cfg", QSettings::IniFormat)
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
/* Copyright 2013 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "include/keyring.h"
|
|
||||||
|
|
||||||
#include <QSettings>
|
|
||||||
|
|
||||||
class StubKeyring : public Keyring
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief virtual dtor
|
|
||||||
*/
|
|
||||||
virtual ~StubKeyring() {};
|
|
||||||
|
|
||||||
virtual bool storePassword(QString service, QString username, QString password);
|
|
||||||
virtual QString getPassword(QString service, QString username);
|
|
||||||
virtual bool hasPassword(QString service, QString username);
|
|
||||||
virtual QStringList getStoredAccounts(QString service);
|
|
||||||
virtual void removeStoredAccount(QString service, QString username);
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class Keyring;
|
|
||||||
explicit StubKeyring();
|
|
||||||
virtual bool isValid()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSettings m_settings;
|
|
||||||
};
|
|
@ -23,10 +23,7 @@
|
|||||||
|
|
||||||
QString PathCombine(QString path1, QString path2)
|
QString PathCombine(QString path1, QString path2)
|
||||||
{
|
{
|
||||||
if (!path1.endsWith('/'))
|
return QDir::cleanPath(path1 + QDir::separator() + path2);
|
||||||
return path1.append('/').append(path2);
|
|
||||||
else
|
|
||||||
return path1.append(path2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString PathCombine(QString path1, QString path2, QString path3)
|
QString PathCombine(QString path1, QString path2, QString path3)
|
||||||
|
@ -58,10 +58,17 @@ ConsoleWindow::~ConsoleWindow()
|
|||||||
void ConsoleWindow::writeColor(QString text, const char *color)
|
void ConsoleWindow::writeColor(QString text, const char *color)
|
||||||
{
|
{
|
||||||
// append a paragraph
|
// append a paragraph
|
||||||
if (color != nullptr)
|
QString newtext;
|
||||||
ui->text->appendHtml(QString("<font color=\"%1\">%2</font>").arg(color).arg(text));
|
newtext += "<span style=\"";
|
||||||
else
|
{
|
||||||
ui->text->appendPlainText(text);
|
if(color)
|
||||||
|
newtext += QString("color:") + color + ";";
|
||||||
|
newtext += "font-family: monospace;";
|
||||||
|
}
|
||||||
|
newtext += "\">";
|
||||||
|
newtext += text.toHtmlEscaped();
|
||||||
|
newtext += "</span>";
|
||||||
|
ui->text->appendHtml(newtext);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleWindow::write(QString data, MessageLevel::Enum mode)
|
void ConsoleWindow::write(QString data, MessageLevel::Enum mode)
|
||||||
|
@ -17,11 +17,6 @@
|
|||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPlainTextEdit" name="text">
|
<widget class="QPlainTextEdit" name="text">
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>10</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="undoRedoEnabled">
|
<property name="undoRedoEnabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
#include "logic/lists/InstanceList.h"
|
#include "logic/lists/InstanceList.h"
|
||||||
#include "logic/lists/MinecraftVersionList.h"
|
#include "logic/lists/MinecraftVersionList.h"
|
||||||
#include "logic/lists/LwjglVersionList.h"
|
#include "logic/lists/LwjglVersionList.h"
|
||||||
#include "logic/lists/IconList.h"
|
#include "logic/icons/IconList.h"
|
||||||
#include "logic/lists/JavaVersionList.h"
|
#include "logic/lists/JavaVersionList.h"
|
||||||
|
|
||||||
#include "logic/auth/flows/AuthenticateTask.h"
|
#include "logic/auth/flows/AuthenticateTask.h"
|
||||||
@ -90,7 +90,9 @@
|
|||||||
#include "logic/LegacyInstance.h"
|
#include "logic/LegacyInstance.h"
|
||||||
|
|
||||||
#include "logic/assets/AssetsUtils.h"
|
#include "logic/assets/AssetsUtils.h"
|
||||||
|
#include "logic/assets/AssetsMigrateTask.h"
|
||||||
#include <logic/updater/UpdateChecker.h>
|
#include <logic/updater/UpdateChecker.h>
|
||||||
|
#include <logic/tasks/ThreadTask.h>
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
|
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
|
||||||
{
|
{
|
||||||
@ -99,7 +101,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
setWindowTitle(QString("MultiMC %1").arg(MMC->version().toString()));
|
setWindowTitle(QString("MultiMC %1").arg(MMC->version().toString()));
|
||||||
|
|
||||||
// OSX magic.
|
// OSX magic.
|
||||||
setUnifiedTitleAndToolBarOnMac(true);
|
// setUnifiedTitleAndToolBarOnMac(true);
|
||||||
|
|
||||||
// The instance action toolbar customizations
|
// The instance action toolbar customizations
|
||||||
{
|
{
|
||||||
@ -178,6 +180,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
connect(view->selectionModel(),
|
connect(view->selectionModel(),
|
||||||
SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this,
|
SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this,
|
||||||
SLOT(instanceChanged(const QModelIndex &, const QModelIndex &)));
|
SLOT(instanceChanged(const QModelIndex &, const QModelIndex &)));
|
||||||
|
|
||||||
|
// track icon changes and update the toolbar!
|
||||||
|
connect(MMC->icons().get(), SIGNAL(iconUpdated(QString)), SLOT(iconUpdated(QString)));
|
||||||
|
|
||||||
// model reset -> selection is invalid. All the instance pointers are wrong.
|
// model reset -> selection is invalid. All the instance pointers are wrong.
|
||||||
// FIXME: stop using POINTERS everywhere
|
// FIXME: stop using POINTERS everywhere
|
||||||
connect(MMC->instances().get(), SIGNAL(dataIsInvalid()), SLOT(selectionBad()));
|
connect(MMC->instances().get(), SIGNAL(dataIsInvalid()), SLOT(selectionBad()));
|
||||||
@ -264,8 +270,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
|
|
||||||
// set up the updater object.
|
// set up the updater object.
|
||||||
auto updater = MMC->updateChecker();
|
auto updater = MMC->updateChecker();
|
||||||
QObject::connect(updater.get(), &UpdateChecker::updateAvailable, this,
|
connect(updater.get(), &UpdateChecker::updateAvailable, this,
|
||||||
&MainWindow::updateAvailable);
|
&MainWindow::updateAvailable);
|
||||||
|
connect(updater.get(), &UpdateChecker::noUpdateFound, [this]()
|
||||||
|
{
|
||||||
|
CustomMessageBox::selectable(
|
||||||
|
this, tr("No update found."),
|
||||||
|
tr("No MultiMC update was found!\nYou are using the latest version."))->exec();
|
||||||
|
});
|
||||||
// if automatic update checks are allowed, start one.
|
// if automatic update checks are allowed, start one.
|
||||||
if (MMC->settings()->get("AutoUpdate").toBool())
|
if (MMC->settings()->get("AutoUpdate").toBool())
|
||||||
on_actionCheckUpdate_triggered();
|
on_actionCheckUpdate_triggered();
|
||||||
@ -292,8 +304,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
|
|
||||||
// removing this looks stupid
|
// removing this looks stupid
|
||||||
view->setFocus();
|
view->setFocus();
|
||||||
|
|
||||||
AssetsUtils::migrateOldAssets();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
@ -502,7 +512,7 @@ void MainWindow::downloadUpdates(QString repo, int versionId, bool installOnExit
|
|||||||
if (installOnExit)
|
if (installOnExit)
|
||||||
MMC->setUpdateOnExit(updateTask.updateFilesDir());
|
MMC->setUpdateOnExit(updateTask.updateFilesDir());
|
||||||
else
|
else
|
||||||
MMC->installUpdates(updateTask.updateFilesDir());
|
MMC->installUpdates(updateTask.updateFilesDir(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -674,6 +684,20 @@ void MainWindow::on_actionChangeInstIcon_triggered()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::iconUpdated(QString icon)
|
||||||
|
{
|
||||||
|
if(icon == m_currentInstIcon)
|
||||||
|
{
|
||||||
|
ui->actionChangeInstIcon->setIcon(MMC->icons()->getIcon(m_currentInstIcon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateInstanceToolIcon(QString new_icon)
|
||||||
|
{
|
||||||
|
m_currentInstIcon = new_icon;
|
||||||
|
ui->actionChangeInstIcon->setIcon(MMC->icons()->getIcon(m_currentInstIcon));
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionChangeInstGroup_triggered()
|
void MainWindow::on_actionChangeInstGroup_triggered()
|
||||||
{
|
{
|
||||||
if (!m_selectedInstance)
|
if (!m_selectedInstance)
|
||||||
@ -721,7 +745,8 @@ void MainWindow::on_actionConfig_Folder_triggered()
|
|||||||
void MainWindow::on_actionCheckUpdate_triggered()
|
void MainWindow::on_actionCheckUpdate_triggered()
|
||||||
{
|
{
|
||||||
auto updater = MMC->updateChecker();
|
auto updater = MMC->updateChecker();
|
||||||
updater->checkForUpdate();
|
|
||||||
|
updater->checkForUpdate(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionSettings_triggered()
|
void MainWindow::on_actionSettings_triggered()
|
||||||
@ -905,6 +930,8 @@ void MainWindow::doLaunch()
|
|||||||
if (!account.get())
|
if (!account.get())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
QString failReason = tr("Your account is currently not logged in. Please enter "
|
||||||
|
"your password to log in again.");
|
||||||
// do the login. if the account has an access token, try to refresh it first.
|
// do the login. if the account has an access token, try to refresh it first.
|
||||||
if (account->accountStatus() != NotVerified)
|
if (account->accountStatus() != NotVerified)
|
||||||
{
|
{
|
||||||
@ -919,13 +946,28 @@ void MainWindow::doLaunch()
|
|||||||
{
|
{
|
||||||
updateInstance(m_selectedInstance, account);
|
updateInstance(m_selectedInstance, account);
|
||||||
}
|
}
|
||||||
// revert from online to verified.
|
else
|
||||||
|
{
|
||||||
|
if (!task->successful())
|
||||||
|
{
|
||||||
|
failReason = task->failReason();
|
||||||
|
}
|
||||||
|
if (loginWithPassword(account, failReason))
|
||||||
|
updateInstance(m_selectedInstance, account);
|
||||||
|
}
|
||||||
|
// in any case, revert from online to verified.
|
||||||
|
account->downgrade();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (loginWithPassword(account, failReason))
|
||||||
|
{
|
||||||
|
updateInstance(m_selectedInstance, account);
|
||||||
|
account->downgrade();
|
||||||
|
}
|
||||||
|
// in any case, revert from online to verified.
|
||||||
account->downgrade();
|
account->downgrade();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (loginWithPassword(account, tr("Your account is currently not logged in. Please enter "
|
|
||||||
"your password to log in again.")))
|
|
||||||
updateInstance(m_selectedInstance, account);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainWindow::loginWithPassword(MojangAccountPtr account, const QString &errorMsg)
|
bool MainWindow::loginWithPassword(MojangAccountPtr account, const QString &errorMsg)
|
||||||
@ -1042,22 +1084,9 @@ void MainWindow::on_actionChangeInstMCVersion_triggered()
|
|||||||
VersionSelectDialog vselect(m_selectedInstance->versionList().get(),
|
VersionSelectDialog vselect(m_selectedInstance->versionList().get(),
|
||||||
tr("Change Minecraft version"), this);
|
tr("Change Minecraft version"), this);
|
||||||
vselect.setFilter(1, "OneSix");
|
vselect.setFilter(1, "OneSix");
|
||||||
if (vselect.exec() && vselect.selectedVersion())
|
if(!vselect.exec() || !vselect.selectedVersion())
|
||||||
{
|
return;
|
||||||
if (m_selectedInstance->versionIsCustom())
|
|
||||||
{
|
|
||||||
auto result = CustomMessageBox::selectable(
|
|
||||||
this, tr("Are you sure?"),
|
|
||||||
tr("This will remove any library/version customization you did previously. "
|
|
||||||
"This includes things like Forge install and similar."),
|
|
||||||
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Abort,
|
|
||||||
QMessageBox::Abort)->exec();
|
|
||||||
|
|
||||||
if (result != QMessageBox::Ok)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor());
|
|
||||||
}
|
|
||||||
if (!MMC->accounts()->anyAccountIsValid())
|
if (!MMC->accounts()->anyAccountIsValid())
|
||||||
{
|
{
|
||||||
CustomMessageBox::selectable(
|
CustomMessageBox::selectable(
|
||||||
@ -1067,7 +1096,22 @@ void MainWindow::on_actionChangeInstMCVersion_triggered()
|
|||||||
QMessageBox::Warning)->show();
|
QMessageBox::Warning)->show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto updateTask = m_selectedInstance->doUpdate(false /*only_prepare*/);
|
|
||||||
|
if (m_selectedInstance->versionIsCustom())
|
||||||
|
{
|
||||||
|
auto result = CustomMessageBox::selectable(
|
||||||
|
this, tr("Are you sure?"),
|
||||||
|
tr("This will remove any library/version customization you did previously. "
|
||||||
|
"This includes things like Forge install and similar."),
|
||||||
|
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Abort,
|
||||||
|
QMessageBox::Abort)->exec();
|
||||||
|
|
||||||
|
if (result != QMessageBox::Ok)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor());
|
||||||
|
|
||||||
|
auto updateTask = m_selectedInstance->doUpdate(false);
|
||||||
if (!updateTask)
|
if (!updateTask)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -1109,7 +1153,6 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex &
|
|||||||
.value<void *>()))
|
.value<void *>()))
|
||||||
{
|
{
|
||||||
ui->instanceToolBar->setEnabled(true);
|
ui->instanceToolBar->setEnabled(true);
|
||||||
QString iconKey = m_selectedInstance->iconKey();
|
|
||||||
renameButton->setText(m_selectedInstance->name());
|
renameButton->setText(m_selectedInstance->name());
|
||||||
ui->actionChangeInstLWJGLVersion->setEnabled(
|
ui->actionChangeInstLWJGLVersion->setEnabled(
|
||||||
m_selectedInstance->menuActionEnabled("actionChangeInstLWJGLVersion"));
|
m_selectedInstance->menuActionEnabled("actionChangeInstLWJGLVersion"));
|
||||||
@ -1118,8 +1161,7 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex &
|
|||||||
ui->actionChangeInstMCVersion->setEnabled(
|
ui->actionChangeInstMCVersion->setEnabled(
|
||||||
m_selectedInstance->menuActionEnabled("actionChangeInstMCVersion"));
|
m_selectedInstance->menuActionEnabled("actionChangeInstMCVersion"));
|
||||||
m_statusLeft->setText(m_selectedInstance->getStatusbarDescription());
|
m_statusLeft->setText(m_selectedInstance->getStatusbarDescription());
|
||||||
auto ico = MMC->icons()->getIcon(iconKey);
|
updateInstanceToolIcon(m_selectedInstance->iconKey());
|
||||||
ui->actionChangeInstIcon->setIcon(ico);
|
|
||||||
|
|
||||||
MMC->settings()->set("SelectedInstance", m_selectedInstance->id());
|
MMC->settings()->set("SelectedInstance", m_selectedInstance->id());
|
||||||
}
|
}
|
||||||
@ -1134,12 +1176,11 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex &
|
|||||||
void MainWindow::selectionBad()
|
void MainWindow::selectionBad()
|
||||||
{
|
{
|
||||||
m_selectedInstance = nullptr;
|
m_selectedInstance = nullptr;
|
||||||
QString iconKey = "infinity";
|
|
||||||
statusBar()->clearMessage();
|
statusBar()->clearMessage();
|
||||||
ui->instanceToolBar->setEnabled(false);
|
ui->instanceToolBar->setEnabled(false);
|
||||||
renameButton->setText(tr("Rename Instance"));
|
renameButton->setText(tr("Rename Instance"));
|
||||||
auto ico = MMC->icons()->getIcon(iconKey);
|
updateInstanceToolIcon("infinity");
|
||||||
ui->actionChangeInstIcon->setIcon(ico);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionEditInstNotes_triggered()
|
void MainWindow::on_actionEditInstNotes_triggered()
|
||||||
@ -1162,6 +1203,32 @@ void MainWindow::instanceEnded()
|
|||||||
this->show();
|
this->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::checkMigrateLegacyAssets()
|
||||||
|
{
|
||||||
|
int legacyAssets = AssetsUtils::findLegacyAssets();
|
||||||
|
if(legacyAssets > 0)
|
||||||
|
{
|
||||||
|
ProgressDialog migrateDlg(this);
|
||||||
|
AssetsMigrateTask migrateTask(legacyAssets, &migrateDlg);
|
||||||
|
{
|
||||||
|
ThreadTask threadTask(&migrateTask);
|
||||||
|
|
||||||
|
if (migrateDlg.exec(&threadTask))
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Assets migration task completed successfully";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Assets migration task reported failure";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Didn't find any legacy assets to migrate";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::checkSetDefaultJava()
|
void MainWindow::checkSetDefaultJava()
|
||||||
{
|
{
|
||||||
bool askForJava = false;
|
bool askForJava = false;
|
||||||
|
@ -51,6 +51,7 @@ public:
|
|||||||
void openWebPage(QUrl url);
|
void openWebPage(QUrl url);
|
||||||
|
|
||||||
void checkSetDefaultJava();
|
void checkSetDefaultJava();
|
||||||
|
void checkMigrateLegacyAssets();
|
||||||
|
|
||||||
private
|
private
|
||||||
slots:
|
slots:
|
||||||
@ -145,6 +146,9 @@ slots:
|
|||||||
void assetsFailed();
|
void assetsFailed();
|
||||||
void assetsFinished();
|
void assetsFinished();
|
||||||
|
|
||||||
|
// called when an icon is changed in the icon model.
|
||||||
|
void iconUpdated(QString);
|
||||||
|
|
||||||
public
|
public
|
||||||
slots:
|
slots:
|
||||||
void instanceActivated(QModelIndex);
|
void instanceActivated(QModelIndex);
|
||||||
@ -173,6 +177,7 @@ slots:
|
|||||||
protected:
|
protected:
|
||||||
bool eventFilter(QObject *obj, QEvent *ev);
|
bool eventFilter(QObject *obj, QEvent *ev);
|
||||||
void setCatBackground(bool enabled);
|
void setCatBackground(bool enabled);
|
||||||
|
void updateInstanceToolIcon(QString new_icon);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
@ -186,6 +191,7 @@ private:
|
|||||||
QToolButton* newsLabel;
|
QToolButton* newsLabel;
|
||||||
|
|
||||||
BaseInstance *m_selectedInstance;
|
BaseInstance *m_selectedInstance;
|
||||||
|
QString m_currentInstIcon;
|
||||||
|
|
||||||
Task *m_versionLoadTask;
|
Task *m_versionLoadTask;
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>706</width>
|
<width>707</width>
|
||||||
<height>579</height>
|
<height>593</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
@ -103,8 +103,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>688</width>
|
<width>685</width>
|
||||||
<height>313</height>
|
<height>304</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<attribute name="label">
|
<attribute name="label">
|
||||||
@ -113,6 +113,9 @@
|
|||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="aboutLabel">
|
<widget class="QLabel" name="aboutLabel">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><html><head/><body><p>MultiMC is a custom launcher that makes managing Minecraft easier by allowing you to have multiple instances of Minecraft at once.</p></body></html></string>
|
<string><html><head/><body><p>MultiMC is a custom launcher that makes managing Minecraft easier by allowing you to have multiple instances of Minecraft at once.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
@ -162,8 +165,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>688</width>
|
<width>685</width>
|
||||||
<height>313</height>
|
<height>304</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<attribute name="label">
|
<attribute name="label">
|
||||||
@ -179,13 +182,22 @@
|
|||||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
p, li { white-space: pre-wrap; }
|
p, li { white-space: pre-wrap; }
|
||||||
</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;">
|
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:7.8pt; font-weight:400; font-style:normal;">
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Andrew Okin &lt;</span><a href="mailto:forkk@forkk.net"><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">forkk@forkk.net</span></a><span style=" font-family:'Ubuntu';">&gt;</span></p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">MultiMC</span></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Petr Mrázek &lt;</span><a href="mailto:peterix@gmail.com"><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">peterix@gmail.com</span></a><span style=" font-family:'Ubuntu';">&gt;</span></p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Andrew Okin &lt;</span><a href="mailto:forkk@forkk.net"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">forkk@forkk.net</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Orochimarufan &lt;</span><a href="mailto:orochimarufan.x3@gmail.com"><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">orochimarufan.x3@gmail.com</span></a><span style=" font-family:'Ubuntu';">&gt;</span></p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Petr Mrázek &lt;</span><a href="mailto:peterix@gmail.com"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">peterix@gmail.com</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">TakSuyu &lt;</span><a href="mailto:taksuyu@gmail.com"><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">taksuyu@gmail.com</span></a><span style=" font-family:'Ubuntu';">&gt;</span></p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Sky &lt;</span><a href="https://www.twitter.com/drayshak"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">@drayshak</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Sky (Drayshak) &lt;</span><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">multimc@bunnies.cc</span><span style=" font-family:'Ubuntu';">&gt;</span></p>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-weight:600;"><br /></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Kilobyte &lt;</span><a href="mailto:stiepen22@gmx.de"><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">stiepen22@gmx.de</span></a><span style=" font-family:'Ubuntu';">&gt;</span></p></body></html></string>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt; font-weight:600;">With thanks to</span></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Orochimarufan &lt;</span><a href="mailto:orochimarufan.x3@gmail.com"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">orochimarufan.x3@gmail.com</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">TakSuyu &lt;</span><a href="mailto:taksuyu@gmail.com"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">taksuyu@gmail.com</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Kilobyte &lt;</span><a href="mailto:stiepen22@gmx.de"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">stiepen22@gmx.de</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Jan (02JanDal) &lt;</span><a href="mailto:02jandal@gmail.com"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">02jandal@gmail.com</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Robotbrain &lt;</span><a href="https://twitter.com/skylordelros"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">@skylordelros</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Rootbear75 &lt;</span><a href="https://twitter.com/rootbear75"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">@rootbear75</span></a><span style=" font-size:10pt;">&gt; (build server)</span></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -206,8 +218,8 @@ p, li { white-space: pre-wrap; }
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>688</width>
|
<width>684</width>
|
||||||
<height>313</height>
|
<height>290</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<attribute name="label">
|
<attribute name="label">
|
||||||
@ -234,9 +246,9 @@ p, li { white-space: pre-wrap; }
|
|||||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
p, li { white-space: pre-wrap; }
|
p, li { white-space: pre-wrap; }
|
||||||
</style></head><body style=" font-family:'DejaVu Sans Mono'; font-size:11pt; font-weight:400; font-style:normal;">
|
</style></head><body style=" font-family:'DejaVu Sans Mono'; font-size:7.8pt; font-weight:400; font-style:normal;">
|
||||||
<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;">MultiMC</span></p>
|
<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;">MultiMC</span></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Copyright 2012 MultiMC Contributors</span></p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Copyright 2012-2014 MultiMC Contributors</span></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</span></p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</span></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">you may not use this file except in compliance with the License.</span></p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">you may not use this file except in compliance with the License.</span></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">You may obtain a copy of the License at</span></p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">You may obtain a copy of the License at</span></p>
|
||||||
@ -361,6 +373,39 @@ p, li { white-space: pre-wrap; }
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="forkPage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>684</width>
|
||||||
|
<height>290</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<attribute name="label">
|
||||||
|
<string>Forking/Redistribution</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_33">
|
||||||
|
<item>
|
||||||
|
<widget class="QTextEdit" name="textEdit">
|
||||||
|
<property name="html">
|
||||||
|
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
|
p, li { white-space: pre-wrap; }
|
||||||
|
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:7.8pt; font-weight:400; font-style:normal;">
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:11pt;">We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.</span></p>
|
||||||
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Bitstream Vera Sans'; font-size:11pt;"><br /></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:11pt;">Part of the reason for using the Apache license is we don't want people using the &quot;MultiMC&quot; name when redistributing the project. This means people must take the time to go through the source code and remove all references to &quot;MultiMC&quot;, including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).</span></p>
|
||||||
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Bitstream Vera Sans'; font-size:11pt;"><br /></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:11pt;">The Apache license covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork </span><span style=" font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:600;">without</span><span style=" font-family:'Bitstream Vera Sans'; font-size:11pt;"> implying that you have our blessing.</span></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -26,7 +26,9 @@
|
|||||||
#include <gui/dialogs/EditAccountDialog.h>
|
#include <gui/dialogs/EditAccountDialog.h>
|
||||||
#include <gui/dialogs/ProgressDialog.h>
|
#include <gui/dialogs/ProgressDialog.h>
|
||||||
#include <gui/dialogs/AccountSelectDialog.h>
|
#include <gui/dialogs/AccountSelectDialog.h>
|
||||||
|
#include "CustomMessageBox.h"
|
||||||
#include <logic/tasks/Task.h>
|
#include <logic/tasks/Task.h>
|
||||||
|
#include <logic/auth/YggdrasilTask.h>
|
||||||
|
|
||||||
#include <MultiMC.h>
|
#include <MultiMC.h>
|
||||||
|
|
||||||
@ -147,5 +149,12 @@ void AccountListDialog::addAccount(const QString& errMsg)
|
|||||||
|
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto reason = task->failReason();
|
||||||
|
auto dlg = CustomMessageBox::selectable(this, tr("Login error."), reason, QMessageBox::Critical);
|
||||||
|
dlg->exec();
|
||||||
|
delete dlg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
#include "logic/InstanceFactory.h"
|
#include "logic/InstanceFactory.h"
|
||||||
#include "logic/BaseVersion.h"
|
#include "logic/BaseVersion.h"
|
||||||
#include "logic/lists/IconList.h"
|
#include "logic/icons/IconList.h"
|
||||||
#include "logic/lists/MinecraftVersionList.h"
|
#include "logic/lists/MinecraftVersionList.h"
|
||||||
#include "logic/tasks/Task.h"
|
#include "logic/tasks/Task.h"
|
||||||
#include "logic/BaseInstance.h"
|
#include "logic/BaseInstance.h"
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
#include "EditAccountDialog.h"
|
#include "EditAccountDialog.h"
|
||||||
#include "ui_EditAccountDialog.h"
|
#include "ui_EditAccountDialog.h"
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
EditAccountDialog::EditAccountDialog(const QString &text, QWidget *parent, int flags)
|
EditAccountDialog::EditAccountDialog(const QString &text, QWidget *parent, int flags)
|
||||||
: QDialog(parent), ui(new Ui::EditAccountDialog)
|
: QDialog(parent), ui(new Ui::EditAccountDialog)
|
||||||
@ -33,6 +35,11 @@ EditAccountDialog::~EditAccountDialog()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditAccountDialog::on_label_linkActivated(const QString &link)
|
||||||
|
{
|
||||||
|
QDesktopServices::openUrl(QUrl(link));
|
||||||
|
}
|
||||||
|
|
||||||
QString EditAccountDialog::username() const
|
QString EditAccountDialog::username() const
|
||||||
{
|
{
|
||||||
return ui->userTextBox->text();
|
return ui->userTextBox->text();
|
||||||
|
@ -52,6 +52,9 @@ public:
|
|||||||
PasswordField,
|
PasswordField,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_label_linkActivated(const QString &link);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::EditAccountDialog *ui;
|
Ui::EditAccountDialog *ui;
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,12 @@
|
|||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Message label placeholder.</string>
|
<string>Message label placeholder.</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::RichText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
#include "gui/Platform.h"
|
#include "gui/Platform.h"
|
||||||
#include "gui/widgets/InstanceDelegate.h"
|
#include "gui/widgets/InstanceDelegate.h"
|
||||||
|
|
||||||
#include "logic/lists/IconList.h"
|
#include "logic/icons/IconList.h"
|
||||||
|
|
||||||
IconPickerDialog::IconPickerDialog(QWidget *parent)
|
IconPickerDialog::IconPickerDialog(QWidget *parent)
|
||||||
: QDialog(parent), ui(new Ui::IconPickerDialog)
|
: QDialog(parent), ui(new Ui::IconPickerDialog)
|
||||||
@ -103,7 +103,7 @@ void IconPickerDialog::addNewIcon()
|
|||||||
QString selectIcons = tr("Select Icons");
|
QString selectIcons = tr("Select Icons");
|
||||||
//: The type of icon files
|
//: The type of icon files
|
||||||
QStringList fileNames = QFileDialog::getOpenFileNames(this, selectIcons, QString(),
|
QStringList fileNames = QFileDialog::getOpenFileNames(this, selectIcons, QString(),
|
||||||
tr("Icons") + "(*.png *.jpg *.jpeg)");
|
tr("Icons") + "(*.png *.jpg *.jpeg *.ico)");
|
||||||
MMC->icons()->installIcons(fileNames);
|
MMC->icons()->installIcons(fileNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,9 @@ InstanceSettings::InstanceSettings(SettingsObject *obj, QWidget *parent)
|
|||||||
{
|
{
|
||||||
MultiMCPlatform::fixWM_CLASS(this);
|
MultiMCPlatform::fixWM_CLASS(this);
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("SettingsGeometry").toByteArray()));
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +50,13 @@ InstanceSettings::~InstanceSettings()
|
|||||||
void InstanceSettings::showEvent(QShowEvent *ev)
|
void InstanceSettings::showEvent(QShowEvent *ev)
|
||||||
{
|
{
|
||||||
QDialog::showEvent(ev);
|
QDialog::showEvent(ev);
|
||||||
adjustSize();
|
}
|
||||||
|
|
||||||
|
void InstanceSettings::closeEvent(QCloseEvent *ev)
|
||||||
|
{
|
||||||
|
MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
|
||||||
|
|
||||||
|
QDialog::closeEvent(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceSettings::on_customCommandsGroupBox_toggled(bool state)
|
void InstanceSettings::on_customCommandsGroupBox_toggled(bool state)
|
||||||
@ -57,12 +66,16 @@ void InstanceSettings::on_customCommandsGroupBox_toggled(bool state)
|
|||||||
|
|
||||||
void InstanceSettings::on_buttonBox_accepted()
|
void InstanceSettings::on_buttonBox_accepted()
|
||||||
{
|
{
|
||||||
|
MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
|
||||||
|
|
||||||
applySettings();
|
applySettings();
|
||||||
accept();
|
accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceSettings::on_buttonBox_rejected()
|
void InstanceSettings::on_buttonBox_rejected()
|
||||||
{
|
{
|
||||||
|
MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
|
||||||
|
|
||||||
reject();
|
reject();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,18 +111,6 @@ void InstanceSettings::applySettings()
|
|||||||
m_obj->reset("MinecraftWinHeight");
|
m_obj->reset("MinecraftWinHeight");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto Login
|
|
||||||
bool login = ui->accountSettingsBox->isChecked();
|
|
||||||
m_obj->set("OverrideLogin", login);
|
|
||||||
if (login)
|
|
||||||
{
|
|
||||||
m_obj->set("AutoLogin", ui->autoLoginCheckBox->isChecked());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_obj->reset("AutoLogin");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
bool memory = ui->memoryGroupBox->isChecked();
|
bool memory = ui->memoryGroupBox->isChecked();
|
||||||
m_obj->set("OverrideMemory", memory);
|
m_obj->set("OverrideMemory", memory);
|
||||||
@ -170,10 +171,6 @@ void InstanceSettings::loadSettings()
|
|||||||
ui->windowWidthSpinBox->setValue(m_obj->get("MinecraftWinWidth").toInt());
|
ui->windowWidthSpinBox->setValue(m_obj->get("MinecraftWinWidth").toInt());
|
||||||
ui->windowHeightSpinBox->setValue(m_obj->get("MinecraftWinHeight").toInt());
|
ui->windowHeightSpinBox->setValue(m_obj->get("MinecraftWinHeight").toInt());
|
||||||
|
|
||||||
// Auto Login
|
|
||||||
ui->accountSettingsBox->setChecked(m_obj->get("OverrideLogin").toBool());
|
|
||||||
ui->autoLoginCheckBox->setChecked(m_obj->get("AutoLogin").toBool());
|
|
||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
ui->memoryGroupBox->setChecked(m_obj->get("OverrideMemory").toBool());
|
ui->memoryGroupBox->setChecked(m_obj->get("OverrideMemory").toBool());
|
||||||
ui->minMemSpinBox->setValue(m_obj->get("MinMemAlloc").toInt());
|
ui->minMemSpinBox->setValue(m_obj->get("MinMemAlloc").toInt());
|
||||||
|
@ -39,6 +39,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void showEvent(QShowEvent *);
|
virtual void showEvent(QShowEvent *);
|
||||||
|
virtual void closeEvent(QCloseEvent *);
|
||||||
private
|
private
|
||||||
slots:
|
slots:
|
||||||
void on_customCommandsGroupBox_toggled(bool arg1);
|
void on_customCommandsGroupBox_toggled(bool arg1);
|
||||||
|
@ -131,31 +131,6 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="accountSettingsBox">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>Account Settings</string>
|
|
||||||
</property>
|
|
||||||
<property name="checkable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="autoLoginCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Login automatically when an instance icon is double clicked?</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacerMinecraft">
|
<spacer name="verticalSpacerMinecraft">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
@ -411,7 +386,6 @@
|
|||||||
<tabstop>consoleSettingsBox</tabstop>
|
<tabstop>consoleSettingsBox</tabstop>
|
||||||
<tabstop>showConsoleCheck</tabstop>
|
<tabstop>showConsoleCheck</tabstop>
|
||||||
<tabstop>autoCloseConsoleCheck</tabstop>
|
<tabstop>autoCloseConsoleCheck</tabstop>
|
||||||
<tabstop>accountSettingsBox</tabstop>
|
|
||||||
<tabstop>memoryGroupBox</tabstop>
|
<tabstop>memoryGroupBox</tabstop>
|
||||||
<tabstop>minMemSpinBox</tabstop>
|
<tabstop>minMemSpinBox</tabstop>
|
||||||
<tabstop>maxMemSpinBox</tabstop>
|
<tabstop>maxMemSpinBox</tabstop>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include "logic/InstanceFactory.h"
|
#include "logic/InstanceFactory.h"
|
||||||
#include "logic/BaseVersion.h"
|
#include "logic/BaseVersion.h"
|
||||||
#include "logic/lists/IconList.h"
|
#include "logic/icons/IconList.h"
|
||||||
#include "logic/lists/MinecraftVersionList.h"
|
#include "logic/lists/MinecraftVersionList.h"
|
||||||
#include "logic/tasks/Task.h"
|
#include "logic/tasks/Task.h"
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "logic/EnabledItemFilter.h"
|
#include "logic/EnabledItemFilter.h"
|
||||||
#include "logic/lists/ForgeVersionList.h"
|
#include "logic/lists/ForgeVersionList.h"
|
||||||
#include "logic/ForgeInstaller.h"
|
#include "logic/ForgeInstaller.h"
|
||||||
|
#include "logic/LiteLoaderInstaller.h"
|
||||||
|
|
||||||
OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
|
OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
|
||||||
: QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst)
|
: QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst)
|
||||||
@ -95,6 +96,8 @@ void OneSixModEditDialog::updateVersionControls()
|
|||||||
ui->customizeBtn->setEnabled(!customVersion);
|
ui->customizeBtn->setEnabled(!customVersion);
|
||||||
ui->revertBtn->setEnabled(customVersion);
|
ui->revertBtn->setEnabled(customVersion);
|
||||||
ui->forgeBtn->setEnabled(true);
|
ui->forgeBtn->setEnabled(true);
|
||||||
|
ui->liteloaderBtn->setEnabled(LiteLoaderInstaller(m_inst->intendedVersionId()).canApply());
|
||||||
|
ui->customEditorBtn->setEnabled(customVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixModEditDialog::disableVersionControls()
|
void OneSixModEditDialog::disableVersionControls()
|
||||||
@ -102,6 +105,8 @@ void OneSixModEditDialog::disableVersionControls()
|
|||||||
ui->customizeBtn->setEnabled(false);
|
ui->customizeBtn->setEnabled(false);
|
||||||
ui->revertBtn->setEnabled(false);
|
ui->revertBtn->setEnabled(false);
|
||||||
ui->forgeBtn->setEnabled(false);
|
ui->forgeBtn->setEnabled(false);
|
||||||
|
ui->liteloaderBtn->setEnabled(false);
|
||||||
|
ui->customEditorBtn->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixModEditDialog::on_customizeBtn_clicked()
|
void OneSixModEditDialog::on_customizeBtn_clicked()
|
||||||
@ -131,6 +136,17 @@ void OneSixModEditDialog::on_revertBtn_clicked()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OneSixModEditDialog::on_customEditorBtn_clicked()
|
||||||
|
{
|
||||||
|
if (m_inst->versionIsCustom())
|
||||||
|
{
|
||||||
|
if (!MMC->openJsonEditor(m_inst->instanceRoot() + "/custom.json"))
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Error"), tr("Unable to open custom.json, check the settings"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OneSixModEditDialog::on_forgeBtn_clicked()
|
void OneSixModEditDialog::on_forgeBtn_clicked()
|
||||||
{
|
{
|
||||||
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
|
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
|
||||||
@ -204,6 +220,32 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OneSixModEditDialog::on_liteloaderBtn_clicked()
|
||||||
|
{
|
||||||
|
LiteLoaderInstaller liteloader(m_inst->intendedVersionId());
|
||||||
|
if (!liteloader.canApply())
|
||||||
|
{
|
||||||
|
QMessageBox::critical(
|
||||||
|
this, tr("LiteLoader"),
|
||||||
|
tr("There is no information available on how to install LiteLoader "
|
||||||
|
"into this version of Minecraft"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_inst->versionIsCustom())
|
||||||
|
{
|
||||||
|
m_inst->customizeVersion();
|
||||||
|
m_version = m_inst->getFullVersion();
|
||||||
|
main_model->setSourceModel(m_version.get());
|
||||||
|
updateVersionControls();
|
||||||
|
}
|
||||||
|
if (!liteloader.apply(m_version))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(
|
||||||
|
this, tr("LiteLoader"),
|
||||||
|
tr("For reasons unknown, the LiteLoader installation failed. Check your MultiMC log files for details."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent)
|
bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent)
|
||||||
{
|
{
|
||||||
switch (keyEvent->key())
|
switch (keyEvent->key())
|
||||||
|
@ -44,8 +44,10 @@ slots:
|
|||||||
// Questionable: SettingsDialog doesn't need this for some reason?
|
// Questionable: SettingsDialog doesn't need this for some reason?
|
||||||
void on_buttonBox_rejected();
|
void on_buttonBox_rejected();
|
||||||
void on_forgeBtn_clicked();
|
void on_forgeBtn_clicked();
|
||||||
|
void on_liteloaderBtn_clicked();
|
||||||
void on_customizeBtn_clicked();
|
void on_customizeBtn_clicked();
|
||||||
void on_revertBtn_clicked();
|
void on_revertBtn_clicked();
|
||||||
|
void on_customEditorBtn_clicked();
|
||||||
void updateVersionControls();
|
void updateVersionControls();
|
||||||
void disableVersionControls();
|
void disableVersionControls();
|
||||||
|
|
||||||
|
@ -77,6 +77,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="liteloaderBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>Install LiteLoader</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="customizeBtn">
|
<widget class="QPushButton" name="customizeBtn">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
@ -136,6 +143,20 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="customEditorBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>Open custom.json</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer_7">
|
<spacer name="verticalSpacer_7">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -40,6 +40,12 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::Se
|
|||||||
ui->sortingModeGroup->setId(ui->sortByNameBtn, Sort_Name);
|
ui->sortingModeGroup->setId(ui->sortByNameBtn, Sort_Name);
|
||||||
ui->sortingModeGroup->setId(ui->sortLastLaunchedBtn, Sort_LastLaunch);
|
ui->sortingModeGroup->setId(ui->sortLastLaunchedBtn, Sort_LastLaunch);
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
|
||||||
|
ui->jsonEditorTextBox->setClearButtonEnabled(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("SettingsGeometry").toByteArray()));
|
||||||
|
|
||||||
loadSettings(MMC->settings().get());
|
loadSettings(MMC->settings().get());
|
||||||
updateCheckboxStuff();
|
updateCheckboxStuff();
|
||||||
}
|
}
|
||||||
@ -51,7 +57,13 @@ SettingsDialog::~SettingsDialog()
|
|||||||
void SettingsDialog::showEvent(QShowEvent *ev)
|
void SettingsDialog::showEvent(QShowEvent *ev)
|
||||||
{
|
{
|
||||||
QDialog::showEvent(ev);
|
QDialog::showEvent(ev);
|
||||||
adjustSize();
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::closeEvent(QCloseEvent *ev)
|
||||||
|
{
|
||||||
|
MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
|
||||||
|
|
||||||
|
QDialog::closeEvent(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::updateCheckboxStuff()
|
void SettingsDialog::updateCheckboxStuff()
|
||||||
@ -60,6 +72,32 @@ void SettingsDialog::updateCheckboxStuff()
|
|||||||
ui->windowHeightSpinBox->setEnabled(!ui->maximizedCheckBox->isChecked());
|
ui->windowHeightSpinBox->setEnabled(!ui->maximizedCheckBox->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::on_ftbLauncherBrowseBtn_clicked()
|
||||||
|
{
|
||||||
|
QString raw_dir = QFileDialog::getExistingDirectory(this, tr("FTB Launcher Directory"),
|
||||||
|
ui->ftbLauncherBox->text());
|
||||||
|
QString cooked_dir = NormalizePath(raw_dir);
|
||||||
|
|
||||||
|
// do not allow current dir - it's dirty. Do not allow dirs that don't exist
|
||||||
|
if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists())
|
||||||
|
{
|
||||||
|
ui->ftbLauncherBox->setText(cooked_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::on_ftbBrowseBtn_clicked()
|
||||||
|
{
|
||||||
|
QString raw_dir = QFileDialog::getExistingDirectory(this, tr("FTB Directory"),
|
||||||
|
ui->ftbBox->text());
|
||||||
|
QString cooked_dir = NormalizePath(raw_dir);
|
||||||
|
|
||||||
|
// do not allow current dir - it's dirty. Do not allow dirs that don't exist
|
||||||
|
if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists())
|
||||||
|
{
|
||||||
|
ui->ftbBox->setText(cooked_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsDialog::on_instDirBrowseBtn_clicked()
|
void SettingsDialog::on_instDirBrowseBtn_clicked()
|
||||||
{
|
{
|
||||||
QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Instance Directory"),
|
QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Instance Directory"),
|
||||||
@ -72,6 +110,18 @@ void SettingsDialog::on_instDirBrowseBtn_clicked()
|
|||||||
ui->instDirTextBox->setText(cooked_dir);
|
ui->instDirTextBox->setText(cooked_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void SettingsDialog::on_iconsDirBrowseBtn_clicked()
|
||||||
|
{
|
||||||
|
QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Icons Directory"),
|
||||||
|
ui->iconsDirTextBox->text());
|
||||||
|
QString cooked_dir = NormalizePath(raw_dir);
|
||||||
|
|
||||||
|
// do not allow current dir - it's dirty. Do not allow dirs that don't exist
|
||||||
|
if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists())
|
||||||
|
{
|
||||||
|
ui->iconsDirTextBox->setText(cooked_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsDialog::on_modsDirBrowseBtn_clicked()
|
void SettingsDialog::on_modsDirBrowseBtn_clicked()
|
||||||
{
|
{
|
||||||
@ -99,6 +149,36 @@ void SettingsDialog::on_lwjglDirBrowseBtn_clicked()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::on_jsonEditorBrowseBtn_clicked()
|
||||||
|
{
|
||||||
|
QString raw_file = QFileDialog::getOpenFileName(
|
||||||
|
this, tr("JSON Editor"),
|
||||||
|
ui->jsonEditorTextBox->text().isEmpty()
|
||||||
|
#if defined(Q_OS_LINUX)
|
||||||
|
? QString("/usr/bin")
|
||||||
|
#else
|
||||||
|
? QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).first()
|
||||||
|
#endif
|
||||||
|
: ui->jsonEditorTextBox->text());
|
||||||
|
QString cooked_file = NormalizePath(raw_file);
|
||||||
|
|
||||||
|
if (cooked_file.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it has to exist and be an executable
|
||||||
|
if (QFileInfo(cooked_file).exists() &&
|
||||||
|
QFileInfo(cooked_file).isExecutable())
|
||||||
|
{
|
||||||
|
ui->jsonEditorTextBox->setText(cooked_file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Invalid"), tr("The file chosen does not seem to be an executable"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsDialog::on_maximizedCheckBox_clicked(bool checked)
|
void SettingsDialog::on_maximizedCheckBox_clicked(bool checked)
|
||||||
{
|
{
|
||||||
Q_UNUSED(checked);
|
Q_UNUSED(checked);
|
||||||
@ -108,6 +188,13 @@ void SettingsDialog::on_maximizedCheckBox_clicked(bool checked)
|
|||||||
void SettingsDialog::on_buttonBox_accepted()
|
void SettingsDialog::on_buttonBox_accepted()
|
||||||
{
|
{
|
||||||
applySettings(MMC->settings().get());
|
applySettings(MMC->settings().get());
|
||||||
|
|
||||||
|
MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::on_buttonBox_rejected()
|
||||||
|
{
|
||||||
|
MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::applySettings(SettingsObject *s)
|
void SettingsDialog::applySettings(SettingsObject *s)
|
||||||
@ -135,11 +222,29 @@ void SettingsDialog::applySettings(SettingsObject *s)
|
|||||||
// Updates
|
// Updates
|
||||||
s->set("AutoUpdate", ui->autoUpdateCheckBox->isChecked());
|
s->set("AutoUpdate", ui->autoUpdateCheckBox->isChecked());
|
||||||
|
|
||||||
|
// FTB
|
||||||
|
s->set("TrackFTBInstances", ui->trackFtbBox->isChecked());
|
||||||
|
s->set("FTBLauncherRoot", ui->ftbLauncherBox->text());
|
||||||
|
s->set("FTBRoot", ui->ftbBox->text());
|
||||||
|
|
||||||
// Folders
|
// Folders
|
||||||
// TODO: Offer to move instances to new instance folder.
|
// TODO: Offer to move instances to new instance folder.
|
||||||
s->set("InstanceDir", ui->instDirTextBox->text());
|
s->set("InstanceDir", ui->instDirTextBox->text());
|
||||||
s->set("CentralModsDir", ui->modsDirTextBox->text());
|
s->set("CentralModsDir", ui->modsDirTextBox->text());
|
||||||
s->set("LWJGLDir", ui->lwjglDirTextBox->text());
|
s->set("LWJGLDir", ui->lwjglDirTextBox->text());
|
||||||
|
s->set("IconsDir", ui->iconsDirTextBox->text());
|
||||||
|
|
||||||
|
// Editors
|
||||||
|
QString jsonEditor = ui->jsonEditorTextBox->text();
|
||||||
|
if (!jsonEditor.isEmpty() && (!QFileInfo(jsonEditor).exists() || !QFileInfo(jsonEditor).isExecutable()))
|
||||||
|
{
|
||||||
|
QString found = QStandardPaths::findExecutable(jsonEditor);
|
||||||
|
if (!found.isEmpty())
|
||||||
|
{
|
||||||
|
jsonEditor = found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s->set("JsonEditor", jsonEditor);
|
||||||
|
|
||||||
// Console
|
// Console
|
||||||
s->set("ShowConsole", ui->showConsoleCheck->isChecked());
|
s->set("ShowConsole", ui->showConsoleCheck->isChecked());
|
||||||
@ -150,9 +255,6 @@ void SettingsDialog::applySettings(SettingsObject *s)
|
|||||||
s->set("MinecraftWinWidth", ui->windowWidthSpinBox->value());
|
s->set("MinecraftWinWidth", ui->windowWidthSpinBox->value());
|
||||||
s->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
|
s->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
|
||||||
|
|
||||||
// Auto Login
|
|
||||||
s->set("AutoLogin", ui->autoLoginCheckBox->isChecked());
|
|
||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
s->set("MinMemAlloc", ui->minMemSpinBox->value());
|
s->set("MinMemAlloc", ui->minMemSpinBox->value());
|
||||||
s->set("MaxMemAlloc", ui->maxMemSpinBox->value());
|
s->set("MaxMemAlloc", ui->maxMemSpinBox->value());
|
||||||
@ -188,10 +290,19 @@ void SettingsDialog::loadSettings(SettingsObject *s)
|
|||||||
ui->autoUpdateCheckBox->setChecked(s->get("AutoUpdate").toBool());
|
ui->autoUpdateCheckBox->setChecked(s->get("AutoUpdate").toBool());
|
||||||
ui->devBuildsCheckBox->setChecked(s->get("UseDevBuilds").toBool());
|
ui->devBuildsCheckBox->setChecked(s->get("UseDevBuilds").toBool());
|
||||||
|
|
||||||
|
// FTB
|
||||||
|
ui->trackFtbBox->setChecked(s->get("TrackFTBInstances").toBool());
|
||||||
|
ui->ftbLauncherBox->setText(s->get("FTBLauncherRoot").toString());
|
||||||
|
ui->ftbBox->setText(s->get("FTBRoot").toString());
|
||||||
|
|
||||||
// Folders
|
// Folders
|
||||||
ui->instDirTextBox->setText(s->get("InstanceDir").toString());
|
ui->instDirTextBox->setText(s->get("InstanceDir").toString());
|
||||||
ui->modsDirTextBox->setText(s->get("CentralModsDir").toString());
|
ui->modsDirTextBox->setText(s->get("CentralModsDir").toString());
|
||||||
ui->lwjglDirTextBox->setText(s->get("LWJGLDir").toString());
|
ui->lwjglDirTextBox->setText(s->get("LWJGLDir").toString());
|
||||||
|
ui->iconsDirTextBox->setText(s->get("IconsDir").toString());
|
||||||
|
|
||||||
|
// Editors
|
||||||
|
ui->jsonEditorTextBox->setText(s->get("JsonEditor").toString());
|
||||||
|
|
||||||
// Console
|
// Console
|
||||||
ui->showConsoleCheck->setChecked(s->get("ShowConsole").toBool());
|
ui->showConsoleCheck->setChecked(s->get("ShowConsole").toBool());
|
||||||
@ -202,9 +313,6 @@ void SettingsDialog::loadSettings(SettingsObject *s)
|
|||||||
ui->windowWidthSpinBox->setValue(s->get("MinecraftWinWidth").toInt());
|
ui->windowWidthSpinBox->setValue(s->get("MinecraftWinWidth").toInt());
|
||||||
ui->windowHeightSpinBox->setValue(s->get("MinecraftWinHeight").toInt());
|
ui->windowHeightSpinBox->setValue(s->get("MinecraftWinHeight").toInt());
|
||||||
|
|
||||||
// Auto Login
|
|
||||||
ui->autoLoginCheckBox->setChecked(s->get("AutoLogin").toBool());
|
|
||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
ui->minMemSpinBox->setValue(s->get("MinMemAlloc").toInt());
|
ui->minMemSpinBox->setValue(s->get("MinMemAlloc").toInt());
|
||||||
ui->maxMemSpinBox->setValue(s->get("MaxMemAlloc").toInt());
|
ui->maxMemSpinBox->setValue(s->get("MaxMemAlloc").toInt());
|
||||||
|
@ -41,20 +41,32 @@ public:
|
|||||||
void loadSettings(SettingsObject *s);
|
void loadSettings(SettingsObject *s);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void showEvent(QShowEvent *);
|
virtual void showEvent(QShowEvent *ev);
|
||||||
|
virtual void closeEvent(QCloseEvent *ev);
|
||||||
|
|
||||||
private
|
private
|
||||||
slots:
|
slots:
|
||||||
|
void on_ftbLauncherBrowseBtn_clicked();
|
||||||
|
|
||||||
|
void on_ftbBrowseBtn_clicked();
|
||||||
|
|
||||||
void on_instDirBrowseBtn_clicked();
|
void on_instDirBrowseBtn_clicked();
|
||||||
|
|
||||||
void on_modsDirBrowseBtn_clicked();
|
void on_modsDirBrowseBtn_clicked();
|
||||||
|
|
||||||
void on_lwjglDirBrowseBtn_clicked();
|
void on_lwjglDirBrowseBtn_clicked();
|
||||||
|
|
||||||
|
|
||||||
|
void on_jsonEditorBrowseBtn_clicked();
|
||||||
|
|
||||||
|
void on_iconsDirBrowseBtn_clicked();
|
||||||
|
|
||||||
void on_maximizedCheckBox_clicked(bool checked);
|
void on_maximizedCheckBox_clicked(bool checked);
|
||||||
|
|
||||||
void on_buttonBox_accepted();
|
void on_buttonBox_accepted();
|
||||||
|
|
||||||
|
void on_buttonBox_rejected();
|
||||||
|
|
||||||
void on_javaDetectBtn_clicked();
|
void on_javaDetectBtn_clicked();
|
||||||
|
|
||||||
void on_javaTestBtn_clicked();
|
void on_javaTestBtn_clicked();
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>526</width>
|
<width>526</width>
|
||||||
<height>599</height>
|
<height>628</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -39,7 +39,7 @@
|
|||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>General</string>
|
<string>General</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="generalTabLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="sortingModeBox">
|
<widget class="QGroupBox" name="sortingModeBox">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
@ -95,6 +95,93 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>FTB</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QPushButton" name="ftbLauncherBrowseBtn">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>28</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="focusPolicy">
|
||||||
|
<enum>Qt::TabFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Launcher:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="ftbLauncherBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="trackFtbBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Track FTB instances</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="ftbBox"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="QPushButton" name="ftbBrowseBtn">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>28</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Files:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="foldersBox">
|
<widget class="QGroupBox" name="foldersBox">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@ -128,6 +215,9 @@
|
|||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="modsDirTextBox"/>
|
<widget class="QLineEdit" name="modsDirTextBox"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="lwjglDirTextBox"/>
|
||||||
|
</item>
|
||||||
<item row="1" column="2">
|
<item row="1" column="2">
|
||||||
<widget class="QToolButton" name="modsDirBrowseBtn">
|
<widget class="QToolButton" name="modsDirBrowseBtn">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -142,9 +232,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLineEdit" name="lwjglDirTextBox"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="2">
|
<item row="2" column="2">
|
||||||
<widget class="QToolButton" name="lwjglDirBrowseBtn">
|
<widget class="QToolButton" name="lwjglDirBrowseBtn">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -152,6 +239,49 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="iconsDirTextBox"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="labelIconsDir">
|
||||||
|
<property name="text">
|
||||||
|
<string>Icons:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="QToolButton" name="iconsDirBrowseBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="editorsBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>External Editors (leave empty for system default)</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="foldersBoxLayout_2">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="jsonEditorTextBox"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="labelJsonEditor">
|
||||||
|
<property name="text">
|
||||||
|
<string>JSON Editor:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QToolButton" name="jsonEditorBrowseBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -261,22 +391,6 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="accountSettingsBox">
|
|
||||||
<property name="title">
|
|
||||||
<string>Account Settings</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="autoLoginCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Login automatically when an instance icon is double clicked?</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacerMinecraft">
|
<spacer name="verticalSpacerMinecraft">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
@ -394,25 +508,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="labelJVMArgs">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>JVM arguments:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1" colspan="5">
|
|
||||||
<widget class="QLineEdit" name="javaPathTextBox"/>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1" colspan="5">
|
|
||||||
<widget class="QLineEdit" name="jvmArgsTextBox"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QPushButton" name="javaDetectBtn">
|
<widget class="QPushButton" name="javaDetectBtn">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -426,7 +521,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="4" colspan="2">
|
<item row="1" column="2">
|
||||||
<widget class="QPushButton" name="javaTestBtn">
|
<widget class="QPushButton" name="javaTestBtn">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
@ -439,19 +534,48 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="2" colspan="2">
|
<item row="2" column="0">
|
||||||
<widget class="QPushButton" name="javaBrowseBtn">
|
<widget class="QLabel" name="labelJVMArgs">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Browse...</string>
|
<string>JVM arguments:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="1" colspan="2">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="javaPathTextBox"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="javaBrowseBtn">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>28</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1" colspan="2">
|
||||||
|
<widget class="QLineEdit" name="jvmArgsTextBox"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -520,7 +644,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>settingsTabs</tabstop>
|
<tabstop>settingsTab</tabstop>
|
||||||
<tabstop>buttonBox</tabstop>
|
<tabstop>buttonBox</tabstop>
|
||||||
<tabstop>sortLastLaunchedBtn</tabstop>
|
<tabstop>sortLastLaunchedBtn</tabstop>
|
||||||
<tabstop>sortByNameBtn</tabstop>
|
<tabstop>sortByNameBtn</tabstop>
|
||||||
|
@ -30,7 +30,7 @@ void MCModInfoFrame::updateWithMod(Mod &m)
|
|||||||
|
|
||||||
QString text = "";
|
QString text = "";
|
||||||
QString name = "";
|
QString name = "";
|
||||||
if(m.name().isEmpty()) name = m.id();
|
if(m.name().isEmpty()) name = m.mmc_id();
|
||||||
else name = m.name();
|
else name = m.name();
|
||||||
|
|
||||||
if(m.homeurl().isEmpty()) text = name;
|
if(m.homeurl().isEmpty()) text = name;
|
||||||
|
@ -44,8 +44,9 @@ void ModListView::setModel ( QAbstractItemModel* model )
|
|||||||
QTreeView::setModel ( model );
|
QTreeView::setModel ( model );
|
||||||
auto head = header();
|
auto head = header();
|
||||||
head->setStretchLastSection(false);
|
head->setStretchLastSection(false);
|
||||||
head->setSectionResizeMode(0, QHeaderView::Stretch);
|
head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||||
for(int i = 1; i < head->count(); i++)
|
head->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||||
|
for(int i = 2; i < head->count(); i++)
|
||||||
head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
|
head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
|
||||||
dropIndicatorPosition();
|
dropIndicatorPosition();
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,15 @@ void DebugOutputDestination::write(const QString &message)
|
|||||||
QsDebugOutput::output(message);
|
QsDebugOutput::output(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class QDebugDestination : public Destination
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void write(const QString &message)
|
||||||
|
{
|
||||||
|
qDebug() << message;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
DestinationPtr DestinationFactory::MakeFileDestination(const QString &filePath)
|
DestinationPtr DestinationFactory::MakeFileDestination(const QString &filePath)
|
||||||
{
|
{
|
||||||
return DestinationPtr(new FileDestination(filePath));
|
return DestinationPtr(new FileDestination(filePath));
|
||||||
@ -87,4 +96,9 @@ DestinationPtr DestinationFactory::MakeDebugOutputDestination()
|
|||||||
return DestinationPtr(new DebugOutputDestination);
|
return DestinationPtr(new DebugOutputDestination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DestinationPtr DestinationFactory::MakeQDebugDestination()
|
||||||
|
{
|
||||||
|
return DestinationPtr(new QDebugDestination);
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace
|
} // end namespace
|
||||||
|
@ -47,6 +47,7 @@ class DestinationFactory
|
|||||||
public:
|
public:
|
||||||
static DestinationPtr MakeFileDestination(const QString &filePath);
|
static DestinationPtr MakeFileDestination(const QString &filePath);
|
||||||
static DestinationPtr MakeDebugOutputDestination();
|
static DestinationPtr MakeDebugOutputDestination();
|
||||||
|
static DestinationPtr MakeQDebugDestination();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace
|
} // end namespace
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "pathutils.h"
|
#include "pathutils.h"
|
||||||
#include "lists/MinecraftVersionList.h"
|
#include "lists/MinecraftVersionList.h"
|
||||||
|
#include "logic/icons/IconList.h"
|
||||||
|
|
||||||
BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
|
BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
|
||||||
SettingsObject *settings_obj, QObject *parent)
|
SettingsObject *settings_obj, QObject *parent)
|
||||||
@ -36,10 +37,11 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
|
|||||||
d->m_settings = settings_obj;
|
d->m_settings = settings_obj;
|
||||||
d->m_rootDir = rootDir;
|
d->m_rootDir = rootDir;
|
||||||
|
|
||||||
settings().registerSetting(new Setting("name", "Unnamed Instance"));
|
settings().registerSetting("name", "Unnamed Instance");
|
||||||
settings().registerSetting(new Setting("iconKey", "default"));
|
settings().registerSetting("iconKey", "default");
|
||||||
settings().registerSetting(new Setting("notes", ""));
|
connect(MMC->icons().get(), SIGNAL(iconUpdated(QString)), SLOT(iconUpdated(QString)));
|
||||||
settings().registerSetting(new Setting("lastLaunchTime", 0));
|
settings().registerSetting("notes", "");
|
||||||
|
settings().registerSetting("lastLaunchTime", 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* custom base jar has no default. it is determined in code... see the accessor methods for
|
* custom base jar has no default. it is determined in code... see the accessor methods for
|
||||||
@ -48,54 +50,45 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
|
|||||||
* for instances that DO NOT have the CustomBaseJar setting (legacy instances),
|
* for instances that DO NOT have the CustomBaseJar setting (legacy instances),
|
||||||
* [.]minecraft/bin/mcbackup.jar is the default base jar
|
* [.]minecraft/bin/mcbackup.jar is the default base jar
|
||||||
*/
|
*/
|
||||||
settings().registerSetting(new Setting("UseCustomBaseJar", true));
|
settings().registerSetting("UseCustomBaseJar", true);
|
||||||
settings().registerSetting(new Setting("CustomBaseJar", ""));
|
settings().registerSetting("CustomBaseJar", "");
|
||||||
|
|
||||||
auto globalSettings = MMC->settings();
|
auto globalSettings = MMC->settings();
|
||||||
|
|
||||||
// Java Settings
|
// Java Settings
|
||||||
settings().registerSetting(new Setting("OverrideJava", false));
|
settings().registerSetting("OverrideJava", false);
|
||||||
settings().registerSetting(
|
settings().registerOverride(globalSettings->getSetting("JavaPath"));
|
||||||
new OverrideSetting("JavaPath", globalSettings->getSetting("JavaPath")));
|
settings().registerOverride(globalSettings->getSetting("JvmArgs"));
|
||||||
settings().registerSetting(
|
|
||||||
new OverrideSetting("JvmArgs", globalSettings->getSetting("JvmArgs")));
|
|
||||||
|
|
||||||
// Custom Commands
|
// Custom Commands
|
||||||
settings().registerSetting(new Setting("OverrideCommands", false));
|
settings().registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
|
||||||
settings().registerSetting(new OverrideSetting(
|
settings().registerOverride(globalSettings->getSetting("PreLaunchCommand"));
|
||||||
"PreLaunchCommand", globalSettings->getSetting("PreLaunchCommand")));
|
settings().registerOverride(globalSettings->getSetting("PostExitCommand"));
|
||||||
settings().registerSetting(
|
|
||||||
new OverrideSetting("PostExitCommand", globalSettings->getSetting("PostExitCommand")));
|
|
||||||
|
|
||||||
// Window Size
|
// Window Size
|
||||||
settings().registerSetting(new Setting("OverrideWindow", false));
|
settings().registerSetting("OverrideWindow", false);
|
||||||
settings().registerSetting(
|
settings().registerOverride(globalSettings->getSetting("LaunchMaximized"));
|
||||||
new OverrideSetting("LaunchMaximized", globalSettings->getSetting("LaunchMaximized")));
|
settings().registerOverride(globalSettings->getSetting("MinecraftWinWidth"));
|
||||||
settings().registerSetting(new OverrideSetting(
|
settings().registerOverride(globalSettings->getSetting("MinecraftWinHeight"));
|
||||||
"MinecraftWinWidth", globalSettings->getSetting("MinecraftWinWidth")));
|
|
||||||
settings().registerSetting(new OverrideSetting(
|
|
||||||
"MinecraftWinHeight", globalSettings->getSetting("MinecraftWinHeight")));
|
|
||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
settings().registerSetting(new Setting("OverrideMemory", false));
|
settings().registerSetting("OverrideMemory", false);
|
||||||
settings().registerSetting(
|
settings().registerOverride(globalSettings->getSetting("MinMemAlloc"));
|
||||||
new OverrideSetting("MinMemAlloc", globalSettings->getSetting("MinMemAlloc")));
|
settings().registerOverride(globalSettings->getSetting("MaxMemAlloc"));
|
||||||
settings().registerSetting(
|
settings().registerOverride(globalSettings->getSetting("PermGen"));
|
||||||
new OverrideSetting("MaxMemAlloc", globalSettings->getSetting("MaxMemAlloc")));
|
|
||||||
settings().registerSetting(
|
|
||||||
new OverrideSetting("PermGen", globalSettings->getSetting("PermGen")));
|
|
||||||
|
|
||||||
// Auto login
|
|
||||||
settings().registerSetting(new Setting("OverrideLogin", false));
|
|
||||||
settings().registerSetting(
|
|
||||||
new OverrideSetting("AutoLogin", globalSettings->getSetting("AutoLogin")));
|
|
||||||
|
|
||||||
// Console
|
// Console
|
||||||
settings().registerSetting(new Setting("OverrideConsole", false));
|
settings().registerSetting("OverrideConsole", false);
|
||||||
settings().registerSetting(
|
settings().registerOverride(globalSettings->getSetting("ShowConsole"));
|
||||||
new OverrideSetting("ShowConsole", globalSettings->getSetting("ShowConsole")));
|
settings().registerOverride(globalSettings->getSetting("AutoCloseConsole"));
|
||||||
settings().registerSetting(new OverrideSetting(
|
}
|
||||||
"AutoCloseConsole", globalSettings->getSetting("AutoCloseConsole")));
|
|
||||||
|
void BaseInstance::iconUpdated(QString key)
|
||||||
|
{
|
||||||
|
if(iconKey() == key)
|
||||||
|
{
|
||||||
|
emit propertiesChanged(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseInstance::nuke()
|
void BaseInstance::nuke()
|
||||||
|
@ -184,6 +184,9 @@ signals:
|
|||||||
*/
|
*/
|
||||||
void nuked(BaseInstance *inst);
|
void nuked(BaseInstance *inst);
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void iconUpdated(QString key);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<BaseInstancePrivate> inst_d;
|
std::shared_ptr<BaseInstancePrivate> inst_d;
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,9 @@
|
|||||||
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "LegacyInstance.h"
|
#include "LegacyInstance.h"
|
||||||
|
#include "LegacyFTBInstance.h"
|
||||||
#include "OneSixInstance.h"
|
#include "OneSixInstance.h"
|
||||||
|
#include "OneSixFTBInstance.h"
|
||||||
#include "NostalgiaInstance.h"
|
#include "NostalgiaInstance.h"
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "MinecraftVersion.h"
|
#include "MinecraftVersion.h"
|
||||||
@ -43,7 +45,7 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst
|
|||||||
{
|
{
|
||||||
auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg"));
|
auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg"));
|
||||||
|
|
||||||
m_settings->registerSetting(new Setting("InstanceType", "Legacy"));
|
m_settings->registerSetting("InstanceType", "Legacy");
|
||||||
|
|
||||||
QString inst_type = m_settings->get("InstanceType").toString();
|
QString inst_type = m_settings->get("InstanceType").toString();
|
||||||
|
|
||||||
@ -60,6 +62,14 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst
|
|||||||
{
|
{
|
||||||
inst = new NostalgiaInstance(instDir, m_settings, this);
|
inst = new NostalgiaInstance(instDir, m_settings, this);
|
||||||
}
|
}
|
||||||
|
else if (inst_type == "LegacyFTB")
|
||||||
|
{
|
||||||
|
inst = new LegacyFTBInstance(instDir, m_settings, this);
|
||||||
|
}
|
||||||
|
else if (inst_type == "OneSixFTB")
|
||||||
|
{
|
||||||
|
inst = new OneSixFTBInstance(instDir, m_settings, this);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return InstanceFactory::UnknownLoadError;
|
return InstanceFactory::UnknownLoadError;
|
||||||
@ -69,7 +79,8 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst
|
|||||||
|
|
||||||
InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *&inst,
|
InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *&inst,
|
||||||
BaseVersionPtr version,
|
BaseVersionPtr version,
|
||||||
const QString &instDir)
|
const QString &instDir,
|
||||||
|
const InstType type)
|
||||||
{
|
{
|
||||||
QDir rootDir(instDir);
|
QDir rootDir(instDir);
|
||||||
|
|
||||||
@ -83,34 +94,65 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *&
|
|||||||
return InstanceFactory::NoSuchVersion;
|
return InstanceFactory::NoSuchVersion;
|
||||||
|
|
||||||
auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg"));
|
auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg"));
|
||||||
m_settings->registerSetting(new Setting("InstanceType", "Legacy"));
|
m_settings->registerSetting("InstanceType", "Legacy");
|
||||||
|
|
||||||
switch (mcVer->type)
|
if (type == NormalInst)
|
||||||
{
|
{
|
||||||
case MinecraftVersion::Legacy:
|
switch (mcVer->type)
|
||||||
m_settings->set("InstanceType", "Legacy");
|
{
|
||||||
inst = new LegacyInstance(instDir, m_settings, this);
|
case MinecraftVersion::Legacy:
|
||||||
inst->setIntendedVersionId(version->descriptor());
|
m_settings->set("InstanceType", "Legacy");
|
||||||
inst->setShouldUseCustomBaseJar(false);
|
inst = new LegacyInstance(instDir, m_settings, this);
|
||||||
break;
|
inst->setIntendedVersionId(version->descriptor());
|
||||||
case MinecraftVersion::OneSix:
|
inst->setShouldUseCustomBaseJar(false);
|
||||||
m_settings->set("InstanceType", "OneSix");
|
break;
|
||||||
inst = new OneSixInstance(instDir, m_settings, this);
|
case MinecraftVersion::OneSix:
|
||||||
inst->setIntendedVersionId(version->descriptor());
|
m_settings->set("InstanceType", "OneSix");
|
||||||
inst->setShouldUseCustomBaseJar(false);
|
inst = new OneSixInstance(instDir, m_settings, this);
|
||||||
break;
|
inst->setIntendedVersionId(version->descriptor());
|
||||||
case MinecraftVersion::Nostalgia:
|
inst->setShouldUseCustomBaseJar(false);
|
||||||
m_settings->set("InstanceType", "Nostalgia");
|
break;
|
||||||
inst = new NostalgiaInstance(instDir, m_settings, this);
|
case MinecraftVersion::Nostalgia:
|
||||||
inst->setIntendedVersionId(version->descriptor());
|
m_settings->set("InstanceType", "Nostalgia");
|
||||||
inst->setShouldUseCustomBaseJar(false);
|
inst = new NostalgiaInstance(instDir, m_settings, this);
|
||||||
break;
|
inst->setIntendedVersionId(version->descriptor());
|
||||||
default:
|
inst->setShouldUseCustomBaseJar(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
delete m_settings;
|
||||||
|
return InstanceFactory::NoSuchVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == FTBInstance)
|
||||||
|
{
|
||||||
|
switch (mcVer->type)
|
||||||
|
{
|
||||||
|
case MinecraftVersion::Legacy:
|
||||||
|
m_settings->set("InstanceType", "LegacyFTB");
|
||||||
|
inst = new LegacyFTBInstance(instDir, m_settings, this);
|
||||||
|
inst->setIntendedVersionId(version->descriptor());
|
||||||
|
inst->setShouldUseCustomBaseJar(false);
|
||||||
|
break;
|
||||||
|
case MinecraftVersion::OneSix:
|
||||||
|
m_settings->set("InstanceType", "OneSixFTB");
|
||||||
|
inst = new OneSixFTBInstance(instDir, m_settings, this);
|
||||||
|
inst->setIntendedVersionId(version->descriptor());
|
||||||
|
inst->setShouldUseCustomBaseJar(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
delete m_settings;
|
||||||
|
return InstanceFactory::NoSuchVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
delete m_settings;
|
delete m_settings;
|
||||||
return InstanceFactory::NoSuchVersion;
|
return InstanceFactory::NoSuchVersion;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: really, how do you even know?
|
// FIXME: really, how do you even know?
|
||||||
return InstanceFactory::NoCreateError;
|
return InstanceFactory::NoCreateError;
|
||||||
@ -128,7 +170,17 @@ InstanceFactory::InstCreateError InstanceFactory::copyInstance(BaseInstance *&ne
|
|||||||
rootDir.removeRecursively();
|
rootDir.removeRecursively();
|
||||||
return InstanceFactory::CantCreateDir;
|
return InstanceFactory::CantCreateDir;
|
||||||
}
|
}
|
||||||
|
auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg"));
|
||||||
|
m_settings->registerSetting("InstanceType", "Legacy");
|
||||||
|
QString inst_type = m_settings->get("InstanceType").toString();
|
||||||
|
|
||||||
|
if(inst_type == "OneSixFTB")
|
||||||
|
m_settings->set("InstanceType", "OneSix");
|
||||||
|
if(inst_type == "LegacyFTB")
|
||||||
|
m_settings->set("InstanceType", "Legacy");
|
||||||
|
|
||||||
auto error = loadInstance(newInstance, instDir);
|
auto error = loadInstance(newInstance, instDir);
|
||||||
|
|
||||||
switch (error)
|
switch (error)
|
||||||
{
|
{
|
||||||
case NoLoadError:
|
case NoLoadError:
|
||||||
|
@ -55,18 +55,25 @@ public:
|
|||||||
CantCreateDir
|
CantCreateDir
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum InstType
|
||||||
|
{
|
||||||
|
NormalInst,
|
||||||
|
FTBInstance
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Creates a stub instance
|
* \brief Creates a stub instance
|
||||||
*
|
*
|
||||||
* \param inst Pointer to store the created instance in.
|
* \param inst Pointer to store the created instance in.
|
||||||
* \param inst Game version to use for the instance
|
* \param version Game version to use for the instance
|
||||||
* \param instDir The new instance's directory.
|
* \param instDir The new instance's directory.
|
||||||
|
* \param type The type of instance to create
|
||||||
* \return An InstCreateError error code.
|
* \return An InstCreateError error code.
|
||||||
* - InstExists if the given instance directory is already an instance.
|
* - InstExists if the given instance directory is already an instance.
|
||||||
* - CantCreateDir if the given instance directory cannot be created.
|
* - CantCreateDir if the given instance directory cannot be created.
|
||||||
*/
|
*/
|
||||||
InstCreateError createInstance(BaseInstance *&inst, BaseVersionPtr version,
|
InstCreateError createInstance(BaseInstance *&inst, BaseVersionPtr version,
|
||||||
const QString &instDir);
|
const QString &instDir, const InstType type = NormalInst);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Creates a copy of an existing instance with a new name
|
* \brief Creates a copy of an existing instance with a new name
|
||||||
|
@ -99,6 +99,7 @@ void JavaChecker::error(QProcess::ProcessError err)
|
|||||||
if(err == QProcess::FailedToStart)
|
if(err == QProcess::FailedToStart)
|
||||||
{
|
{
|
||||||
killTimer.stop();
|
killTimer.stop();
|
||||||
|
checkerJar.remove();
|
||||||
|
|
||||||
JavaCheckResult result;
|
JavaCheckResult result;
|
||||||
{
|
{
|
||||||
@ -116,6 +117,5 @@ void JavaChecker::timeout()
|
|||||||
if(process)
|
if(process)
|
||||||
{
|
{
|
||||||
process->kill();
|
process->kill();
|
||||||
process.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,10 +177,10 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||||||
#elif OSX
|
#elif OSX
|
||||||
QList<QString> JavaUtils::FindJavaPaths()
|
QList<QString> JavaUtils::FindJavaPaths()
|
||||||
{
|
{
|
||||||
QLOG_INFO() << "OS X Java detection incomplete - defaulting to \"java\"";
|
|
||||||
|
|
||||||
QList<QString> javas;
|
QList<QString> javas;
|
||||||
javas.append(this->GetDefaultJava()->path);
|
javas.append(this->GetDefaultJava()->path);
|
||||||
|
javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java");
|
||||||
|
javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java");
|
||||||
|
|
||||||
return javas;
|
return javas;
|
||||||
}
|
}
|
||||||
|
16
logic/LegacyFTBInstance.cpp
Normal file
16
logic/LegacyFTBInstance.cpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "LegacyFTBInstance.h"
|
||||||
|
|
||||||
|
LegacyFTBInstance::LegacyFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) :
|
||||||
|
LegacyInstance(rootDir, settings, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LegacyFTBInstance::getStatusbarDescription()
|
||||||
|
{
|
||||||
|
return "Legacy FTB: " + intendedVersionId();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LegacyFTBInstance::menuActionEnabled(QString action_name) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
13
logic/LegacyFTBInstance.h
Normal file
13
logic/LegacyFTBInstance.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "LegacyInstance.h"
|
||||||
|
|
||||||
|
class LegacyFTBInstance : public LegacyInstance
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit LegacyFTBInstance(const QString &rootDir, SettingsObject *settings,
|
||||||
|
QObject *parent = 0);
|
||||||
|
virtual QString getStatusbarDescription();
|
||||||
|
virtual bool menuActionEnabled(QString action_name) const;
|
||||||
|
};
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
#include "logic/MinecraftProcess.h"
|
#include "logic/MinecraftProcess.h"
|
||||||
#include "logic/LegacyUpdate.h"
|
#include "logic/LegacyUpdate.h"
|
||||||
#include "logic/lists/IconList.h"
|
#include "logic/icons/IconList.h"
|
||||||
|
|
||||||
#include "gui/dialogs/LegacyModEditDialog.h"
|
#include "gui/dialogs/LegacyModEditDialog.h"
|
||||||
|
|
||||||
@ -37,11 +37,11 @@ LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings,
|
|||||||
QObject *parent)
|
QObject *parent)
|
||||||
: BaseInstance(new LegacyInstancePrivate(), rootDir, settings, parent)
|
: BaseInstance(new LegacyInstancePrivate(), rootDir, settings, parent)
|
||||||
{
|
{
|
||||||
settings->registerSetting(new Setting("NeedsRebuild", true));
|
settings->registerSetting("NeedsRebuild", true);
|
||||||
settings->registerSetting(new Setting("ShouldUpdate", false));
|
settings->registerSetting("ShouldUpdate", false);
|
||||||
settings->registerSetting(new Setting("JarVersion", "Unknown"));
|
settings->registerSetting("JarVersion", "Unknown");
|
||||||
settings->registerSetting(new Setting("LwjglVersion", "2.9.0"));
|
settings->registerSetting("LwjglVersion", "2.9.0");
|
||||||
settings->registerSetting(new Setting("IntendedJarVersion", ""));
|
settings->registerSetting("IntendedJarVersion", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Task> LegacyInstance::doUpdate(bool only_prepare)
|
std::shared_ptr<Task> LegacyInstance::doUpdate(bool only_prepare)
|
||||||
@ -150,6 +150,7 @@ std::shared_ptr<ModList> LegacyInstance::jarModList()
|
|||||||
|
|
||||||
void LegacyInstance::jarModsChanged()
|
void LegacyInstance::jarModsChanged()
|
||||||
{
|
{
|
||||||
|
QLOG_INFO() << "Jar mods of instance " << name() << " have changed. Jar will be rebuilt.";
|
||||||
setShouldRebuild(true);
|
setShouldRebuild(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ void LegacyUpdate::lwjglStart()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus("Downloading new LWJGL.");
|
setStatus(tr("Downloading new LWJGL..."));
|
||||||
auto version = list->getVersion(lwjglVersion);
|
auto version = list->getVersion(lwjglVersion);
|
||||||
if (!version)
|
if (!version)
|
||||||
{
|
{
|
||||||
@ -144,7 +144,7 @@ void LegacyUpdate::lwjglFinished(QNetworkReply *reply)
|
|||||||
saveMe.open(QIODevice::WriteOnly);
|
saveMe.open(QIODevice::WriteOnly);
|
||||||
saveMe.write(m_reply->readAll());
|
saveMe.write(m_reply->readAll());
|
||||||
saveMe.close();
|
saveMe.close();
|
||||||
setStatus("Installing new LWJGL...");
|
setStatus(tr("Installing new LWJGL..."));
|
||||||
extractLwjgl();
|
extractLwjgl();
|
||||||
jarStart();
|
jarStart();
|
||||||
}
|
}
|
||||||
@ -220,7 +220,7 @@ void LegacyUpdate::extractLwjgl()
|
|||||||
// Now if destFileName is still empty, go to the next file.
|
// Now if destFileName is still empty, go to the next file.
|
||||||
if (!destFileName.isEmpty())
|
if (!destFileName.isEmpty())
|
||||||
{
|
{
|
||||||
setStatus("Installing new LWJGL - Extracting " + name);
|
setStatus(tr("Installing new LWJGL - extracting ") + name + "...");
|
||||||
QFile output(destFileName);
|
QFile output(destFileName);
|
||||||
output.open(QIODevice::WriteOnly);
|
output.open(QIODevice::WriteOnly);
|
||||||
output.write(file.readAll()); // FIXME: wste of memory!?
|
output.write(file.readAll()); // FIXME: wste of memory!?
|
||||||
@ -250,7 +250,7 @@ void LegacyUpdate::jarStart()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus("Checking for jar updates...");
|
setStatus(tr("Checking for jar updates..."));
|
||||||
// Make directories
|
// Make directories
|
||||||
QDir binDir(inst->binDir());
|
QDir binDir(inst->binDir());
|
||||||
if (!binDir.exists() && !binDir.mkpath("."))
|
if (!binDir.exists() && !binDir.mkpath("."))
|
||||||
@ -260,7 +260,7 @@ void LegacyUpdate::jarStart()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build a list of URLs that will need to be downloaded.
|
// Build a list of URLs that will need to be downloaded.
|
||||||
setStatus("Downloading new minecraft.jar");
|
setStatus(tr("Downloading new minecraft.jar ..."));
|
||||||
|
|
||||||
QString version_id = inst->intendedVersionId();
|
QString version_id = inst->intendedVersionId();
|
||||||
QString localPath = version_id + "/" + version_id + ".jar";
|
QString localPath = version_id + "/" + version_id + ".jar";
|
||||||
@ -294,7 +294,7 @@ void LegacyUpdate::jarFailed()
|
|||||||
bool LegacyUpdate::MergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
|
bool LegacyUpdate::MergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
|
||||||
MetainfAction metainf)
|
MetainfAction metainf)
|
||||||
{
|
{
|
||||||
setStatus("Installing mods - Adding " + from.fileName());
|
setStatus(tr("Installing mods: Adding ") + from.fileName() + " ...");
|
||||||
|
|
||||||
QuaZip modZip(from.filePath());
|
QuaZip modZip(from.filePath());
|
||||||
modZip.open(QuaZip::mdUnzip);
|
modZip.open(QuaZip::mdUnzip);
|
||||||
@ -380,7 +380,7 @@ void LegacyUpdate::ModTheJar()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus("Installing mods - backing up minecraft.jar...");
|
setStatus(tr("Installing mods: Backing up minecraft.jar ..."));
|
||||||
if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath()))
|
if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath()))
|
||||||
{
|
{
|
||||||
emitFailed("It seems both the active and base jar are gone. A fresh base jar will "
|
emitFailed("It seems both the active and base jar are gone. A fresh base jar will "
|
||||||
@ -405,7 +405,7 @@ void LegacyUpdate::ModTheJar()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TaskStep(); // STEP 1
|
// TaskStep(); // STEP 1
|
||||||
setStatus("Installing mods - Opening minecraft.jar");
|
setStatus(tr("Installing mods: Opening minecraft.jar ..."));
|
||||||
|
|
||||||
QuaZip zipOut(runnableJar.filePath());
|
QuaZip zipOut(runnableJar.filePath());
|
||||||
if (!zipOut.open(QuaZip::mdCreate))
|
if (!zipOut.open(QuaZip::mdCreate))
|
||||||
@ -419,10 +419,15 @@ void LegacyUpdate::ModTheJar()
|
|||||||
QSet<QString> addedFiles;
|
QSet<QString> addedFiles;
|
||||||
|
|
||||||
// Modify the jar
|
// Modify the jar
|
||||||
setStatus("Installing mods - Adding mod files...");
|
setStatus(tr("Installing mods: Adding mod files..."));
|
||||||
for (int i = modList->size() - 1; i >= 0; i--)
|
for (int i = modList->size() - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
auto &mod = modList->operator[](i);
|
auto &mod = modList->operator[](i);
|
||||||
|
|
||||||
|
// do not merge disabled mods.
|
||||||
|
if(!mod.enabled())
|
||||||
|
continue;
|
||||||
|
|
||||||
if (mod.type() == Mod::MOD_ZIPFILE)
|
if (mod.type() == Mod::MOD_ZIPFILE)
|
||||||
{
|
{
|
||||||
if (!MergeZipFiles(&zipOut, mod.filename(), addedFiles, LegacyUpdate::KeepMetainf))
|
if (!MergeZipFiles(&zipOut, mod.filename(), addedFiles, LegacyUpdate::KeepMetainf))
|
||||||
|
102
logic/LiteLoaderInstaller.cpp
Normal file
102
logic/LiteLoaderInstaller.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/* Copyright 2013 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* 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 "LiteLoaderInstaller.h"
|
||||||
|
|
||||||
|
#include "OneSixVersion.h"
|
||||||
|
#include "OneSixLibrary.h"
|
||||||
|
|
||||||
|
QMap<QString, QString> LiteLoaderInstaller::m_launcherWrapperVersionMapping;
|
||||||
|
|
||||||
|
LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion(mcVersion)
|
||||||
|
{
|
||||||
|
if (m_launcherWrapperVersionMapping.isEmpty())
|
||||||
|
{
|
||||||
|
m_launcherWrapperVersionMapping["1.6.2"] = "1.3";
|
||||||
|
m_launcherWrapperVersionMapping["1.6.4"] = "1.8";
|
||||||
|
//m_launcherWrapperVersionMapping["1.7.2"] = "1.8";
|
||||||
|
//m_launcherWrapperVersionMapping["1.7.4"] = "1.8";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LiteLoaderInstaller::canApply() const
|
||||||
|
{
|
||||||
|
return m_launcherWrapperVersionMapping.contains(m_mcVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LiteLoaderInstaller::apply(std::shared_ptr<OneSixVersion> to)
|
||||||
|
{
|
||||||
|
to->externalUpdateStart();
|
||||||
|
|
||||||
|
applyLaunchwrapper(to);
|
||||||
|
applyLiteLoader(to);
|
||||||
|
|
||||||
|
to->mainClass = "net.minecraft.launchwrapper.Launch";
|
||||||
|
if (!to->minecraftArguments.contains(
|
||||||
|
" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"))
|
||||||
|
{
|
||||||
|
to->minecraftArguments.append(
|
||||||
|
" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker");
|
||||||
|
}
|
||||||
|
|
||||||
|
to->externalUpdateFinish();
|
||||||
|
return to->toOriginalFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr<OneSixVersion> to)
|
||||||
|
{
|
||||||
|
const QString intendedVersion = m_launcherWrapperVersionMapping[m_mcVersion];
|
||||||
|
|
||||||
|
QMutableListIterator<std::shared_ptr<OneSixLibrary>> it(to->libraries);
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
it.next();
|
||||||
|
if (it.value()->rawName().startsWith("net.minecraft:launchwrapper:"))
|
||||||
|
{
|
||||||
|
if (it.value()->version() >= intendedVersion)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<OneSixLibrary> lib(new OneSixLibrary(
|
||||||
|
"net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[m_mcVersion]));
|
||||||
|
lib->finalize();
|
||||||
|
to->libraries.prepend(lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr<OneSixVersion> to)
|
||||||
|
{
|
||||||
|
QMutableListIterator<std::shared_ptr<OneSixLibrary>> it(to->libraries);
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
it.next();
|
||||||
|
if (it.value()->rawName().startsWith("com.mumfrey:liteloader:"))
|
||||||
|
{
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<OneSixLibrary> lib(
|
||||||
|
new OneSixLibrary("com.mumfrey:liteloader:" + m_mcVersion));
|
||||||
|
lib->setBaseUrl("http://dl.liteloader.com/versions/");
|
||||||
|
lib->finalize();
|
||||||
|
to->libraries.prepend(lib);
|
||||||
|
}
|
@ -14,29 +14,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <QString>
|
||||||
|
#include <QMap>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <QObject>
|
class OneSixVersion;
|
||||||
#include <QSettings>
|
|
||||||
|
|
||||||
#include "settingsobject.h"
|
class LiteLoaderInstaller
|
||||||
|
|
||||||
#include "libsettings_config.h"
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief A settings object that stores its settings in a QSettings object.
|
|
||||||
*/
|
|
||||||
class LIBSETTINGS_EXPORT BasicSettingsObject : public SettingsObject
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit BasicSettingsObject(QObject *parent = 0);
|
LiteLoaderInstaller(const QString &mcVersion);
|
||||||
|
|
||||||
protected
|
bool canApply() const;
|
||||||
slots:
|
|
||||||
virtual void changeSetting(const Setting &setting, QVariant value);
|
|
||||||
|
|
||||||
protected:
|
bool apply(std::shared_ptr<OneSixVersion> to);
|
||||||
virtual QVariant retrieveValue(const Setting &setting);
|
|
||||||
|
|
||||||
QSettings config;
|
private:
|
||||||
|
QString m_mcVersion;
|
||||||
|
|
||||||
|
void applyLaunchwrapper(std::shared_ptr<OneSixVersion> to);
|
||||||
|
void applyLiteLoader(std::shared_ptr<OneSixVersion> to);
|
||||||
|
|
||||||
|
static QMap<QString, QString> m_launcherWrapperVersionMapping;
|
||||||
};
|
};
|
143
logic/Mod.cpp
143
logic/Mod.cpp
@ -35,20 +35,45 @@ Mod::Mod(const QFileInfo &file)
|
|||||||
void Mod::repath(const QFileInfo &file)
|
void Mod::repath(const QFileInfo &file)
|
||||||
{
|
{
|
||||||
m_file = file;
|
m_file = file;
|
||||||
m_name = file.completeBaseName();
|
QString name_base = file.fileName();
|
||||||
m_id = file.fileName();
|
|
||||||
|
|
||||||
m_type = Mod::MOD_UNKNOWN;
|
m_type = Mod::MOD_UNKNOWN;
|
||||||
|
|
||||||
if (m_file.isDir())
|
if (m_file.isDir())
|
||||||
|
{
|
||||||
m_type = MOD_FOLDER;
|
m_type = MOD_FOLDER;
|
||||||
|
m_name = name_base;
|
||||||
|
m_mmc_id = name_base;
|
||||||
|
}
|
||||||
else if (m_file.isFile())
|
else if (m_file.isFile())
|
||||||
{
|
{
|
||||||
QString ext = m_file.suffix().toLower();
|
if (name_base.endsWith(".disabled"))
|
||||||
if (ext == "zip" || ext == "jar")
|
{
|
||||||
m_type = MOD_ZIPFILE;
|
m_enabled = false;
|
||||||
|
name_base.chop(9);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
m_enabled = true;
|
||||||
|
}
|
||||||
|
m_mmc_id = name_base;
|
||||||
|
if (name_base.endsWith(".zip") || name_base.endsWith(".jar"))
|
||||||
|
{
|
||||||
|
m_type = MOD_ZIPFILE;
|
||||||
|
name_base.chop(4);
|
||||||
|
}
|
||||||
|
else if (name_base.endsWith(".litemod"))
|
||||||
|
{
|
||||||
|
m_type = MOD_LITEMOD;
|
||||||
|
name_base.chop(8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
m_type = MOD_SINGLEFILE;
|
m_type = MOD_SINGLEFILE;
|
||||||
|
}
|
||||||
|
m_name = name_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_type == MOD_ZIPFILE)
|
if (m_type == MOD_ZIPFILE)
|
||||||
{
|
{
|
||||||
QuaZip zip(m_file.filePath());
|
QuaZip zip(m_file.filePath());
|
||||||
@ -59,7 +84,7 @@ void Mod::repath(const QFileInfo &file)
|
|||||||
|
|
||||||
if (zip.setCurrentFile("mcmod.info"))
|
if (zip.setCurrentFile("mcmod.info"))
|
||||||
{
|
{
|
||||||
if(!file.open(QIODevice::ReadOnly))
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
{
|
{
|
||||||
zip.close();
|
zip.close();
|
||||||
return;
|
return;
|
||||||
@ -100,6 +125,27 @@ void Mod::repath(const QFileInfo &file)
|
|||||||
ReadMCModInfo(data);
|
ReadMCModInfo(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (m_type == MOD_LITEMOD)
|
||||||
|
{
|
||||||
|
QuaZip zip(m_file.filePath());
|
||||||
|
if (!zip.open(QuaZip::mdUnzip))
|
||||||
|
return;
|
||||||
|
|
||||||
|
QuaZipFile file(&zip);
|
||||||
|
|
||||||
|
if (zip.setCurrentFile("litemod.json"))
|
||||||
|
{
|
||||||
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
zip.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadLiteModInfo(file.readAll());
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
zip.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW format
|
// NEW format
|
||||||
@ -114,7 +160,7 @@ void Mod::ReadMCModInfo(QByteArray contents)
|
|||||||
if (!arr.at(0).isObject())
|
if (!arr.at(0).isObject())
|
||||||
return;
|
return;
|
||||||
auto firstObj = arr.at(0).toObject();
|
auto firstObj = arr.at(0).toObject();
|
||||||
m_id = firstObj.value("modid").toString();
|
m_mod_id = firstObj.value("modid").toString();
|
||||||
m_name = firstObj.value("name").toString();
|
m_name = firstObj.value("name").toString();
|
||||||
m_version = firstObj.value("version").toString();
|
m_version = firstObj.value("version").toString();
|
||||||
m_homeurl = firstObj.value("url").toString();
|
m_homeurl = firstObj.value("url").toString();
|
||||||
@ -132,8 +178,7 @@ void Mod::ReadMCModInfo(QByteArray contents)
|
|||||||
}
|
}
|
||||||
m_credits = firstObj.value("credits").toString();
|
m_credits = firstObj.value("credits").toString();
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
;
|
|
||||||
QJsonParseError jsonError;
|
QJsonParseError jsonError;
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
|
||||||
// this is the very old format that had just the array
|
// this is the very old format that had just the array
|
||||||
@ -163,7 +208,7 @@ void Mod::ReadForgeInfo(QByteArray contents)
|
|||||||
{
|
{
|
||||||
// Read the data
|
// Read the data
|
||||||
m_name = "Minecraft Forge";
|
m_name = "Minecraft Forge";
|
||||||
m_id = "Forge";
|
m_mod_id = "Forge";
|
||||||
m_homeurl = "http://www.minecraftforge.net/forum/";
|
m_homeurl = "http://www.minecraftforge.net/forum/";
|
||||||
INIFile ini;
|
INIFile ini;
|
||||||
if (!ini.loadFile(contents))
|
if (!ini.loadFile(contents))
|
||||||
@ -177,15 +222,40 @@ void Mod::ReadForgeInfo(QByteArray contents)
|
|||||||
m_version = major + "." + minor + "." + revision + "." + build;
|
m_version = major + "." + minor + "." + revision + "." + build;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mod::ReadLiteModInfo(QByteArray contents)
|
||||||
|
{
|
||||||
|
QJsonParseError jsonError;
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
|
||||||
|
auto object = jsonDoc.object();
|
||||||
|
if(object.contains("name"))
|
||||||
|
{
|
||||||
|
m_mod_id = m_name = object.value("name").toString();
|
||||||
|
}
|
||||||
|
if(object.contains("version"))
|
||||||
|
{
|
||||||
|
m_version=object.value("version").toString("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_version=object.value("revision").toString("");
|
||||||
|
}
|
||||||
|
m_mcversion = object.value("mcversion").toString();
|
||||||
|
m_authors = object.value("author").toString();
|
||||||
|
m_description = object.value("description").toString();
|
||||||
|
m_homeurl = object.value("url").toString();
|
||||||
|
}
|
||||||
|
|
||||||
bool Mod::replace(Mod &with)
|
bool Mod::replace(Mod &with)
|
||||||
{
|
{
|
||||||
if (!destroy())
|
if (!destroy())
|
||||||
return false;
|
return false;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
auto t = with.type();
|
auto t = with.type();
|
||||||
|
|
||||||
if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE)
|
if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE)
|
||||||
{
|
{
|
||||||
success = QFile::copy(with.m_file.filePath(), m_file.path());
|
QLOG_DEBUG() << "Copy: " << with.m_file.filePath() << " to " << m_file.filePath();
|
||||||
|
success = QFile::copy(with.m_file.filePath(), m_file.filePath());
|
||||||
}
|
}
|
||||||
if (t == MOD_FOLDER)
|
if (t == MOD_FOLDER)
|
||||||
{
|
{
|
||||||
@ -193,11 +263,17 @@ bool Mod::replace(Mod &with)
|
|||||||
}
|
}
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
m_id = with.m_id;
|
|
||||||
m_mcversion = with.m_mcversion;
|
|
||||||
m_type = with.m_type;
|
|
||||||
m_name = with.m_name;
|
m_name = with.m_name;
|
||||||
|
m_mmc_id = with.m_mmc_id;
|
||||||
|
m_mod_id = with.m_mod_id;
|
||||||
m_version = with.m_version;
|
m_version = with.m_version;
|
||||||
|
m_mcversion = with.m_mcversion;
|
||||||
|
m_description = with.m_description;
|
||||||
|
m_authors = with.m_authors;
|
||||||
|
m_credits = with.m_credits;
|
||||||
|
m_homeurl = with.m_homeurl;
|
||||||
|
m_type = with.m_type;
|
||||||
|
m_file.refresh();
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@ -232,6 +308,7 @@ QString Mod::version() const
|
|||||||
switch (type())
|
switch (type())
|
||||||
{
|
{
|
||||||
case MOD_ZIPFILE:
|
case MOD_ZIPFILE:
|
||||||
|
case MOD_LITEMOD:
|
||||||
return m_version;
|
return m_version;
|
||||||
case MOD_FOLDER:
|
case MOD_FOLDER:
|
||||||
return "Folder";
|
return "Folder";
|
||||||
@ -241,3 +318,41 @@ QString Mod::version() const
|
|||||||
return "VOID";
|
return "VOID";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Mod::enable(bool value)
|
||||||
|
{
|
||||||
|
if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_enabled == value)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QString path = m_file.absoluteFilePath();
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
QFile foo(path);
|
||||||
|
if (!path.endsWith(".disabled"))
|
||||||
|
return false;
|
||||||
|
path.chop(9);
|
||||||
|
if (!foo.rename(path))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QFile foo(path);
|
||||||
|
path += ".disabled";
|
||||||
|
if (!foo.rename(path))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_file = QFileInfo(path);
|
||||||
|
m_enabled = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool Mod::operator==(const Mod &other) const
|
||||||
|
{
|
||||||
|
return mmc_id() == other.mmc_id();
|
||||||
|
}
|
||||||
|
bool Mod::strongCompare(const Mod &other) const
|
||||||
|
{
|
||||||
|
return mmc_id() == other.mmc_id() && version() == other.version() && type() == other.type();
|
||||||
|
}
|
||||||
|
32
logic/Mod.h
32
logic/Mod.h
@ -25,6 +25,7 @@ public:
|
|||||||
MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files.
|
MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files.
|
||||||
MOD_SINGLEFILE, //!< The mod is a single file (not a zip file).
|
MOD_SINGLEFILE, //!< The mod is a single file (not a zip file).
|
||||||
MOD_FOLDER, //!< The mod is in a folder on the filesystem.
|
MOD_FOLDER, //!< The mod is in a folder on the filesystem.
|
||||||
|
MOD_LITEMOD, //!< The mod is a litemod
|
||||||
};
|
};
|
||||||
|
|
||||||
Mod(const QFileInfo &file);
|
Mod(const QFileInfo &file);
|
||||||
@ -33,9 +34,13 @@ public:
|
|||||||
{
|
{
|
||||||
return m_file;
|
return m_file;
|
||||||
}
|
}
|
||||||
QString id() const
|
QString mmc_id() const
|
||||||
{
|
{
|
||||||
return m_id;
|
return m_mmc_id;
|
||||||
|
}
|
||||||
|
QString mod_id() const
|
||||||
|
{
|
||||||
|
return m_mod_id;
|
||||||
}
|
}
|
||||||
ModType type() const
|
ModType type() const
|
||||||
{
|
{
|
||||||
@ -77,6 +82,13 @@ public:
|
|||||||
return m_credits;
|
return m_credits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool enabled() const
|
||||||
|
{
|
||||||
|
return m_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enable(bool value);
|
||||||
|
|
||||||
// delete all the files of this mod
|
// delete all the files of this mod
|
||||||
bool destroy();
|
bool destroy();
|
||||||
// replace this mod with a copy of the other
|
// replace this mod with a copy of the other
|
||||||
@ -85,19 +97,13 @@ public:
|
|||||||
void repath(const QFileInfo &file);
|
void repath(const QFileInfo &file);
|
||||||
|
|
||||||
// WEAK compare operator - used for replacing mods
|
// WEAK compare operator - used for replacing mods
|
||||||
bool operator==(const Mod &other) const
|
bool operator==(const Mod &other) const;
|
||||||
{
|
bool strongCompare(const Mod &other) const;
|
||||||
return filename() == other.filename();
|
|
||||||
}
|
|
||||||
bool strongCompare(const Mod &other) const
|
|
||||||
{
|
|
||||||
return filename() == other.filename() && id() == other.id() &&
|
|
||||||
version() == other.version() && type() == other.type();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ReadMCModInfo(QByteArray contents);
|
void ReadMCModInfo(QByteArray contents);
|
||||||
void ReadForgeInfo(QByteArray contents);
|
void ReadForgeInfo(QByteArray contents);
|
||||||
|
void ReadLiteModInfo(QByteArray contents);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
@ -108,7 +114,9 @@ protected:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
QFileInfo m_file;
|
QFileInfo m_file;
|
||||||
QString m_id;
|
QString m_mmc_id;
|
||||||
|
QString m_mod_id;
|
||||||
|
bool m_enabled = true;
|
||||||
QString m_name;
|
QString m_name;
|
||||||
QString m_version;
|
QString m_version;
|
||||||
QString m_mcversion;
|
QString m_mcversion;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
#include <QString>
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
#include "logger/QsLog.h"
|
#include "logger/QsLog.h"
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ ModList::ModList(const QString &dir, const QString &list_file)
|
|||||||
{
|
{
|
||||||
m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs |
|
m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs |
|
||||||
QDir::NoSymLinks);
|
QDir::NoSymLinks);
|
||||||
m_dir.setSorting(QDir::Name);
|
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
|
||||||
m_list_id = QUuid::createUuid().toString();
|
m_list_id = QUuid::createUuid().toString();
|
||||||
m_watcher = new QFileSystemWatcher(this);
|
m_watcher = new QFileSystemWatcher(this);
|
||||||
is_watching = false;
|
is_watching = false;
|
||||||
@ -66,52 +67,89 @@ bool ModList::update()
|
|||||||
if (!isValid())
|
if (!isValid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
QList<Mod> orderedMods;
|
||||||
QList<Mod> newMods;
|
QList<Mod> newMods;
|
||||||
m_dir.refresh();
|
m_dir.refresh();
|
||||||
auto folderContents = m_dir.entryInfoList();
|
auto folderContents = m_dir.entryInfoList();
|
||||||
bool orderWasInvalid = false;
|
bool orderOrStateChanged = false;
|
||||||
|
|
||||||
// first, process the ordered items (if any)
|
// first, process the ordered items (if any)
|
||||||
QStringList listOrder = readListFile();
|
OrderList listOrder = readListFile();
|
||||||
for (auto item : listOrder)
|
for (auto item : listOrder)
|
||||||
{
|
{
|
||||||
QFileInfo info(m_dir.filePath(item));
|
QFileInfo infoEnabled(m_dir.filePath(item.id));
|
||||||
int idx = folderContents.indexOf(info);
|
QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled"));
|
||||||
|
int idxEnabled = folderContents.indexOf(infoEnabled);
|
||||||
|
int idxDisabled = folderContents.indexOf(infoDisabled);
|
||||||
|
bool isEnabled;
|
||||||
|
// if both enabled and disabled versions are present, it's a special case...
|
||||||
|
if (idxEnabled >= 0 && idxDisabled >= 0)
|
||||||
|
{
|
||||||
|
// we only process the one we actually have in the order file.
|
||||||
|
// and exactly as we have it.
|
||||||
|
// THIS IS A CORNER CASE
|
||||||
|
isEnabled = item.enabled;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// only one is present.
|
||||||
|
// we pick the one that we found.
|
||||||
|
// we assume the mod was enabled/disabled by external means
|
||||||
|
isEnabled = idxEnabled >= 0;
|
||||||
|
}
|
||||||
|
int idx = isEnabled ? idxEnabled : idxDisabled;
|
||||||
|
QFileInfo & info = isEnabled ? infoEnabled : infoDisabled;
|
||||||
// if the file from the index file exists
|
// if the file from the index file exists
|
||||||
if (idx != -1)
|
if (idx != -1)
|
||||||
{
|
{
|
||||||
// remove from the actual folder contents list
|
// remove from the actual folder contents list
|
||||||
folderContents.takeAt(idx);
|
folderContents.takeAt(idx);
|
||||||
// append the new mod
|
// append the new mod
|
||||||
newMods.append(Mod(info));
|
orderedMods.append(Mod(info));
|
||||||
|
if (isEnabled != item.enabled)
|
||||||
|
orderOrStateChanged = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
orderWasInvalid = true;
|
orderOrStateChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto entry : folderContents)
|
// if there are any untracked files...
|
||||||
|
if (folderContents.size())
|
||||||
{
|
{
|
||||||
newMods.append(Mod(entry));
|
// the order surely changed!
|
||||||
}
|
for (auto entry : folderContents)
|
||||||
if (mods.size() != newMods.size())
|
|
||||||
{
|
|
||||||
orderWasInvalid = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
for (int i = 0; i < mods.size(); i++)
|
|
||||||
{
|
{
|
||||||
if (!mods[i].strongCompare(newMods[i]))
|
newMods.append(Mod(entry));
|
||||||
{
|
|
||||||
orderWasInvalid = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
beginResetModel();
|
std::sort(newMods.begin(), newMods.end(), [](const Mod & left, const Mod & right)
|
||||||
mods.swap(newMods);
|
{ return left.name().localeAwareCompare(right.name()) <= 0; });
|
||||||
endResetModel();
|
orderedMods.append(newMods);
|
||||||
if (orderWasInvalid)
|
orderOrStateChanged = true;
|
||||||
|
}
|
||||||
|
// otherwise, if we were already tracking some mods
|
||||||
|
else if (mods.size())
|
||||||
{
|
{
|
||||||
|
// if the number doesn't match, order changed.
|
||||||
|
if (mods.size() != orderedMods.size())
|
||||||
|
orderOrStateChanged = true;
|
||||||
|
// if it does match, compare the mods themselves
|
||||||
|
else
|
||||||
|
for (int i = 0; i < mods.size(); i++)
|
||||||
|
{
|
||||||
|
if (!mods[i].strongCompare(orderedMods[i]))
|
||||||
|
{
|
||||||
|
orderOrStateChanged = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
beginResetModel();
|
||||||
|
mods.swap(orderedMods);
|
||||||
|
endResetModel();
|
||||||
|
if (orderOrStateChanged && !m_list_file.isEmpty())
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Mod list " << m_list_file << " changed!";
|
||||||
saveListFile();
|
saveListFile();
|
||||||
emit changed();
|
emit changed();
|
||||||
}
|
}
|
||||||
@ -123,17 +161,19 @@ void ModList::directoryChanged(QString path)
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList ModList::readListFile()
|
ModList::OrderList ModList::readListFile()
|
||||||
{
|
{
|
||||||
QStringList stringList;
|
OrderList itemList;
|
||||||
if (m_list_file.isNull() || m_list_file.isEmpty())
|
if (m_list_file.isNull() || m_list_file.isEmpty())
|
||||||
return stringList;
|
return itemList;
|
||||||
|
|
||||||
QFile textFile(m_list_file);
|
QFile textFile(m_list_file);
|
||||||
if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||||
return QStringList();
|
return OrderList();
|
||||||
|
|
||||||
QTextStream textStream(&textFile);
|
QTextStream textStream;
|
||||||
|
textStream.setAutoDetectUnicode(true);
|
||||||
|
textStream.setDevice(&textFile);
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
QString line = textStream.readLine();
|
QString line = textStream.readLine();
|
||||||
@ -141,11 +181,18 @@ QStringList ModList::readListFile()
|
|||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
stringList.append(line);
|
OrderItem it;
|
||||||
|
it.enabled = !line.endsWith(".disabled");
|
||||||
|
if (!it.enabled)
|
||||||
|
{
|
||||||
|
line.chop(9);
|
||||||
|
}
|
||||||
|
it.id = line;
|
||||||
|
itemList.append(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
textFile.close();
|
textFile.close();
|
||||||
return stringList;
|
return itemList;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ModList::saveListFile()
|
bool ModList::saveListFile()
|
||||||
@ -155,12 +202,16 @@ bool ModList::saveListFile()
|
|||||||
QFile textFile(m_list_file);
|
QFile textFile(m_list_file);
|
||||||
if (!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
|
if (!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
|
||||||
return false;
|
return false;
|
||||||
QTextStream textStream(&textFile);
|
QTextStream textStream;
|
||||||
|
textStream.setGenerateByteOrderMark(true);
|
||||||
|
textStream.setCodec("UTF-8");
|
||||||
|
textStream.setDevice(&textFile);
|
||||||
for (auto mod : mods)
|
for (auto mod : mods)
|
||||||
{
|
{
|
||||||
auto pathname = mod.filename();
|
textStream << mod.mmc_id();
|
||||||
QString filename = pathname.fileName();
|
if (!mod.enabled())
|
||||||
textStream << filename << endl;
|
textStream << ".disabled";
|
||||||
|
textStream << endl;
|
||||||
}
|
}
|
||||||
textFile.close();
|
textFile.close();
|
||||||
return false;
|
return false;
|
||||||
@ -185,6 +236,9 @@ bool ModList::installMod(const QFileInfo &filename, int index)
|
|||||||
int idx = mods.indexOf(m);
|
int idx = mods.indexOf(m);
|
||||||
if (idx != -1)
|
if (idx != -1)
|
||||||
{
|
{
|
||||||
|
int idx2 = mods.indexOf(m,idx+1);
|
||||||
|
if(idx2 != -1)
|
||||||
|
return false;
|
||||||
if (mods[idx].replace(m))
|
if (mods[idx].replace(m))
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -201,7 +255,7 @@ bool ModList::installMod(const QFileInfo &filename, int index)
|
|||||||
auto type = m.type();
|
auto type = m.type();
|
||||||
if (type == Mod::MOD_UNKNOWN)
|
if (type == Mod::MOD_UNKNOWN)
|
||||||
return false;
|
return false;
|
||||||
if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE)
|
if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE || type == Mod::MOD_LITEMOD)
|
||||||
{
|
{
|
||||||
QString newpath = PathCombine(m_dir.path(), filename.fileName());
|
QString newpath = PathCombine(m_dir.path(), filename.fileName());
|
||||||
if (!QFile::copy(filename.filePath(), newpath))
|
if (!QFile::copy(filename.filePath(), newpath))
|
||||||
@ -327,7 +381,7 @@ bool ModList::moveModsDown(int first, int last)
|
|||||||
|
|
||||||
int ModList::columnCount(const QModelIndex &parent) const
|
int ModList::columnCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
return 2;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ModList::data(const QModelIndex &index, int role) const
|
QVariant ModList::data(const QModelIndex &index, int role) const
|
||||||
@ -341,43 +395,96 @@ QVariant ModList::data(const QModelIndex &index, int role) const
|
|||||||
if (row < 0 || row >= mods.size())
|
if (row < 0 || row >= mods.size())
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
if (role != Qt::DisplayRole)
|
switch (role)
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
switch (column)
|
|
||||||
{
|
{
|
||||||
case 0:
|
case Qt::DisplayRole:
|
||||||
return mods[row].name();
|
switch (index.column())
|
||||||
case 1:
|
{
|
||||||
return mods[row].version();
|
case NameColumn:
|
||||||
case 2:
|
return mods[row].name();
|
||||||
return mods[row].mcversion();
|
case VersionColumn:
|
||||||
|
return mods[row].version();
|
||||||
|
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::ToolTipRole:
|
||||||
|
return mods[row].mmc_id();
|
||||||
|
|
||||||
|
case Qt::CheckStateRole:
|
||||||
|
switch (index.column())
|
||||||
|
{
|
||||||
|
case ActiveColumn:
|
||||||
|
return mods[row].enabled();
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == Qt::CheckStateRole)
|
||||||
|
{
|
||||||
|
auto &mod = mods[index.row()];
|
||||||
|
if (mod.enable(!mod.enabled()))
|
||||||
|
{
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QVariant ModList::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant ModList::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
|
switch (role)
|
||||||
return QVariant();
|
|
||||||
switch (section)
|
|
||||||
{
|
{
|
||||||
case 0:
|
case Qt::DisplayRole:
|
||||||
return QString("Name");
|
switch (section)
|
||||||
case 1:
|
{
|
||||||
return QString("Version");
|
case ActiveColumn:
|
||||||
case 2:
|
return QString();
|
||||||
return QString("Minecraft");
|
case NameColumn:
|
||||||
|
return QString("Name");
|
||||||
|
case VersionColumn:
|
||||||
|
return QString("Version");
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::ToolTipRole:
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case ActiveColumn:
|
||||||
|
return "Is the mod enabled?";
|
||||||
|
case NameColumn:
|
||||||
|
return "The name of the mod.";
|
||||||
|
case VersionColumn:
|
||||||
|
return "The version of the mod.";
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
}
|
}
|
||||||
return QString();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::ItemFlags ModList::flags(const QModelIndex &index) const
|
Qt::ItemFlags ModList::flags(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
|
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
|
||||||
if (index.isValid())
|
if (index.isValid())
|
||||||
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
|
return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled |
|
||||||
|
defaultFlags;
|
||||||
else
|
else
|
||||||
return Qt::ItemIsDropEnabled | defaultFlags;
|
return Qt::ItemIsDropEnabled | defaultFlags;
|
||||||
}
|
}
|
||||||
@ -456,6 +563,14 @@ bool ModList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row
|
|||||||
QString filename = url.toLocalFile();
|
QString filename = url.toLocalFile();
|
||||||
installMod(filename, row);
|
installMod(filename, row);
|
||||||
QLOG_INFO() << "installing: " << filename;
|
QLOG_INFO() << "installing: " << filename;
|
||||||
|
// if there is no ordering, re-sort the list
|
||||||
|
if (m_list_file.isEmpty())
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
std::sort(mods.begin(), mods.end(), [](const Mod & left, const Mod & right)
|
||||||
|
{ return left.name().localeAwareCompare(right.name()) <= 0; });
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (was_watching)
|
if (was_watching)
|
||||||
startWatching();
|
startWatching();
|
||||||
|
@ -34,9 +34,18 @@ class ModList : public QAbstractListModel
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
enum Columns
|
||||||
|
{
|
||||||
|
ActiveColumn = 0,
|
||||||
|
NameColumn,
|
||||||
|
VersionColumn
|
||||||
|
};
|
||||||
ModList(const QString &dir, const QString &list_file = QString());
|
ModList(const QString &dir, const QString &list_file = QString());
|
||||||
|
|
||||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||||
|
virtual bool setData(const QModelIndex &index, const QVariant &value,
|
||||||
|
int role = Qt::EditRole);
|
||||||
|
|
||||||
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const
|
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const
|
||||||
{
|
{
|
||||||
return size();
|
return size();
|
||||||
@ -59,7 +68,6 @@ public:
|
|||||||
{
|
{
|
||||||
return mods[index];
|
return mods[index];
|
||||||
}
|
}
|
||||||
;
|
|
||||||
|
|
||||||
/// Reloads the mod list and returns true if the list changed.
|
/// Reloads the mod list and returns true if the list changed.
|
||||||
virtual bool update();
|
virtual bool update();
|
||||||
@ -119,7 +127,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStringList readListFile();
|
struct OrderItem
|
||||||
|
{
|
||||||
|
QString id;
|
||||||
|
bool enabled = false;
|
||||||
|
};
|
||||||
|
typedef QList<OrderItem> OrderList;
|
||||||
|
OrderList readListFile();
|
||||||
bool saveListFile();
|
bool saveListFile();
|
||||||
private
|
private
|
||||||
slots:
|
slots:
|
||||||
|
120
logic/OneSixFTBInstance.cpp
Normal file
120
logic/OneSixFTBInstance.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#include "OneSixFTBInstance.h"
|
||||||
|
|
||||||
|
#include "OneSixVersion.h"
|
||||||
|
#include "OneSixLibrary.h"
|
||||||
|
#include "tasks/SequentialTask.h"
|
||||||
|
#include "ForgeInstaller.h"
|
||||||
|
#include "lists/ForgeVersionList.h"
|
||||||
|
#include "MultiMC.h"
|
||||||
|
|
||||||
|
class OneSixFTBInstanceForge : public Task
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit OneSixFTBInstanceForge(const QString &version, OneSixFTBInstance *inst, QObject *parent = 0) :
|
||||||
|
Task(parent), instance(inst), version("Forge " + version)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void executeTask()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < MMC->forgelist()->count(); ++i)
|
||||||
|
{
|
||||||
|
if (MMC->forgelist()->at(i)->name() == version)
|
||||||
|
{
|
||||||
|
forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(MMC->forgelist()->at(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!forgeVersion)
|
||||||
|
{
|
||||||
|
emitFailed(QString("Couldn't find forge version ") + version );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
|
||||||
|
if (entry->stale)
|
||||||
|
{
|
||||||
|
setStatus(tr("Downloading Forge..."));
|
||||||
|
fjob = new NetJob("Forge download");
|
||||||
|
fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
|
||||||
|
connect(fjob, &NetJob::failed, [this](){emitFailed(m_failReason);});
|
||||||
|
connect(fjob, &NetJob::succeeded, this, &OneSixFTBInstanceForge::installForge);
|
||||||
|
connect(fjob, &NetJob::progress, [this](qint64 c, qint64 total){ setProgress(100 * c / total); });
|
||||||
|
fjob->start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
installForge();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private
|
||||||
|
slots:
|
||||||
|
void installForge()
|
||||||
|
{
|
||||||
|
setStatus(tr("Installing Forge..."));
|
||||||
|
QString forgePath = entry->getFullPath();
|
||||||
|
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
|
||||||
|
if (!instance->reloadFullVersion())
|
||||||
|
{
|
||||||
|
emitFailed(tr("Couldn't load the version config"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
instance->revertCustomVersion();
|
||||||
|
instance->customizeVersion();
|
||||||
|
auto version = instance->getFullVersion();
|
||||||
|
if (!forge.apply(version))
|
||||||
|
{
|
||||||
|
emitFailed(tr("Couldn't install Forge"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
OneSixFTBInstance *instance;
|
||||||
|
QString version;
|
||||||
|
ForgeVersionPtr forgeVersion;
|
||||||
|
MetaEntryPtr entry;
|
||||||
|
NetJob *fjob;
|
||||||
|
};
|
||||||
|
|
||||||
|
OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) :
|
||||||
|
OneSixInstance(rootDir, settings, parent)
|
||||||
|
{
|
||||||
|
QFile f(QDir(minecraftRoot()).absoluteFilePath("pack.json"));
|
||||||
|
if (f.open(QFile::ReadOnly))
|
||||||
|
{
|
||||||
|
QString data = QString::fromUtf8(f.readAll());
|
||||||
|
QRegularExpressionMatch match = QRegularExpression("net.minecraftforge:minecraftforge:[\\.\\d]*").match(data);
|
||||||
|
m_forge.reset(new OneSixLibrary(match.captured()));
|
||||||
|
m_forge->finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString OneSixFTBInstance::getStatusbarDescription()
|
||||||
|
{
|
||||||
|
return "OneSix FTB: " + intendedVersionId();
|
||||||
|
}
|
||||||
|
bool OneSixFTBInstance::menuActionEnabled(QString action_name) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Task> OneSixFTBInstance::doUpdate(bool only_prepare)
|
||||||
|
{
|
||||||
|
std::shared_ptr<SequentialTask> task;
|
||||||
|
task.reset(new SequentialTask(this));
|
||||||
|
if (!MMC->forgelist()->isLoaded())
|
||||||
|
{
|
||||||
|
task->addTask(std::shared_ptr<Task>(MMC->forgelist()->getLoadTask()));
|
||||||
|
}
|
||||||
|
task->addTask(OneSixInstance::doUpdate(only_prepare));
|
||||||
|
task->addTask(std::shared_ptr<Task>(new OneSixFTBInstanceForge(m_forge->version(), this, this)));
|
||||||
|
//FIXME: yes. this may appear dumb. but the previous step can change the list, so we do it all again.
|
||||||
|
//TODO: Add a graph task. Construct graphs of tasks so we may capture the logic properly.
|
||||||
|
task->addTask(OneSixInstance::doUpdate(only_prepare));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "OneSixFTBInstance.moc"
|
20
logic/OneSixFTBInstance.h
Normal file
20
logic/OneSixFTBInstance.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "OneSixInstance.h"
|
||||||
|
|
||||||
|
class OneSixLibrary;
|
||||||
|
|
||||||
|
class OneSixFTBInstance : public OneSixInstance
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit OneSixFTBInstance(const QString &rootDir, SettingsObject *settings,
|
||||||
|
QObject *parent = 0);
|
||||||
|
virtual QString getStatusbarDescription();
|
||||||
|
virtual bool menuActionEnabled(QString action_name) const;
|
||||||
|
|
||||||
|
virtual std::shared_ptr<Task> doUpdate(bool only_prepare) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<OneSixLibrary> m_forge;
|
||||||
|
};
|
@ -33,8 +33,8 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_o
|
|||||||
: BaseInstance(new OneSixInstancePrivate(), rootDir, setting_obj, parent)
|
: BaseInstance(new OneSixInstancePrivate(), rootDir, setting_obj, parent)
|
||||||
{
|
{
|
||||||
I_D(OneSixInstance);
|
I_D(OneSixInstance);
|
||||||
d->m_settings->registerSetting(new Setting("IntendedVersion", ""));
|
d->m_settings->registerSetting("IntendedVersion", "");
|
||||||
d->m_settings->registerSetting(new Setting("ShouldUpdate", false));
|
d->m_settings->registerSetting("ShouldUpdate", false);
|
||||||
reloadFullVersion();
|
reloadFullVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +68,12 @@ public:
|
|||||||
m_name = name;
|
m_name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the raw name field
|
||||||
|
QString rawName() const
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
QJsonObject toJson();
|
QJsonObject toJson();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,7 +57,7 @@ void OneSixUpdate::executeTask()
|
|||||||
/*
|
/*
|
||||||
* FIXME: in offline mode, do not proceed!
|
* FIXME: in offline mode, do not proceed!
|
||||||
*/
|
*/
|
||||||
setStatus("Testing the Java installation.");
|
setStatus(tr("Testing the Java installation..."));
|
||||||
QString java_path = m_inst->settings().get("JavaPath").toString();
|
QString java_path = m_inst->settings().get("JavaPath").toString();
|
||||||
|
|
||||||
checker.reset(new JavaChecker());
|
checker.reset(new JavaChecker());
|
||||||
@ -89,7 +89,7 @@ void OneSixUpdate::executeTask()
|
|||||||
|
|
||||||
void OneSixUpdate::checkJavaOnline()
|
void OneSixUpdate::checkJavaOnline()
|
||||||
{
|
{
|
||||||
setStatus("Testing the Java installation.");
|
setStatus(tr("Testing the Java installation..."));
|
||||||
QString java_path = m_inst->settings().get("JavaPath").toString();
|
QString java_path = m_inst->settings().get("JavaPath").toString();
|
||||||
|
|
||||||
checker.reset(new JavaChecker());
|
checker.reset(new JavaChecker());
|
||||||
@ -128,7 +128,7 @@ void OneSixUpdate::checkFinishedOffline(JavaCheckResult result)
|
|||||||
void OneSixUpdate::versionFileStart()
|
void OneSixUpdate::versionFileStart()
|
||||||
{
|
{
|
||||||
QLOG_INFO() << m_inst->name() << ": getting version file.";
|
QLOG_INFO() << m_inst->name() << ": getting version file.";
|
||||||
setStatus("Getting the version files from Mojang.");
|
setStatus(tr("Getting the version files from Mojang..."));
|
||||||
|
|
||||||
QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json";
|
QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json";
|
||||||
auto job = new NetJob("Version index");
|
auto job = new NetJob("Version index");
|
||||||
@ -196,7 +196,7 @@ void OneSixUpdate::versionFileFailed()
|
|||||||
|
|
||||||
void OneSixUpdate::assetIndexStart()
|
void OneSixUpdate::assetIndexStart()
|
||||||
{
|
{
|
||||||
setStatus("Updating asset index.");
|
setStatus(tr("Updating assets index..."));
|
||||||
OneSixInstance *inst = (OneSixInstance *)m_inst;
|
OneSixInstance *inst = (OneSixInstance *)m_inst;
|
||||||
std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
|
std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
|
||||||
QString assetName = version->assets;
|
QString assetName = version->assets;
|
||||||
@ -247,7 +247,7 @@ void OneSixUpdate::assetIndexFinished()
|
|||||||
}
|
}
|
||||||
if(dls.size())
|
if(dls.size())
|
||||||
{
|
{
|
||||||
setStatus("Getting the assets files from Mojang...");
|
setStatus(tr("Getting the assets files from Mojang..."));
|
||||||
auto job = new NetJob("Assets for " + inst->name());
|
auto job = new NetJob("Assets for " + inst->name());
|
||||||
for(auto dl: dls)
|
for(auto dl: dls)
|
||||||
job->addNetAction(dl);
|
job->addNetAction(dl);
|
||||||
@ -281,7 +281,7 @@ void OneSixUpdate::assetsFailed()
|
|||||||
|
|
||||||
void OneSixUpdate::jarlibStart()
|
void OneSixUpdate::jarlibStart()
|
||||||
{
|
{
|
||||||
setStatus("Getting the library files from Mojang.");
|
setStatus(tr("Getting the library files from Mojang..."));
|
||||||
QLOG_INFO() << m_inst->name() << ": downloading libraries";
|
QLOG_INFO() << m_inst->name() << ": downloading libraries";
|
||||||
OneSixInstance *inst = (OneSixInstance *)m_inst;
|
OneSixInstance *inst = (OneSixInstance *)m_inst;
|
||||||
bool successful = inst->reloadFullVersion();
|
bool successful = inst->reloadFullVersion();
|
||||||
@ -369,7 +369,7 @@ void OneSixUpdate::jarlibFailed()
|
|||||||
|
|
||||||
void OneSixUpdate::prepareForLaunch()
|
void OneSixUpdate::prepareForLaunch()
|
||||||
{
|
{
|
||||||
setStatus("Preparing for launch.");
|
setStatus(tr("Preparing for launch..."));
|
||||||
QLOG_INFO() << m_inst->name() << ": preparing for launch";
|
QLOG_INFO() << m_inst->name() << ": preparing for launch";
|
||||||
auto onesix_inst = (OneSixInstance *)m_inst;
|
auto onesix_inst = (OneSixInstance *)m_inst;
|
||||||
|
|
||||||
|
143
logic/assets/AssetsMigrateTask.cpp
Normal file
143
logic/assets/AssetsMigrateTask.cpp
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
#include "AssetsMigrateTask.h"
|
||||||
|
#include "MultiMC.h"
|
||||||
|
#include "logger/QsLog.h"
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include "gui/dialogs/CustomMessageBox.h"
|
||||||
|
#include <QDesktopServices>
|
||||||
|
|
||||||
|
AssetsMigrateTask::AssetsMigrateTask(int expected, QObject *parent)
|
||||||
|
: Task(parent)
|
||||||
|
{
|
||||||
|
this->m_expected = expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetsMigrateTask::executeTask()
|
||||||
|
{
|
||||||
|
this->setStatus(tr("Migrating legacy assets..."));
|
||||||
|
this->setProgress(0);
|
||||||
|
|
||||||
|
QDir assets_dir("assets");
|
||||||
|
if (!assets_dir.exists())
|
||||||
|
{
|
||||||
|
emitFailed("Assets directory didn't exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assets_dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
|
||||||
|
int base_length = assets_dir.path().length();
|
||||||
|
|
||||||
|
QList<QString> blacklist = {"indexes", "objects", "virtual"};
|
||||||
|
|
||||||
|
if (!assets_dir.exists("objects"))
|
||||||
|
assets_dir.mkdir("objects");
|
||||||
|
QDir objects_dir("assets/objects");
|
||||||
|
|
||||||
|
QDirIterator iterator(assets_dir, QDirIterator::Subdirectories);
|
||||||
|
int successes = 0;
|
||||||
|
int failures = 0;
|
||||||
|
while (iterator.hasNext())
|
||||||
|
{
|
||||||
|
QString currentDir = iterator.next();
|
||||||
|
currentDir = currentDir.remove(0, base_length + 1);
|
||||||
|
|
||||||
|
bool ignore = false;
|
||||||
|
for (QString blacklisted : blacklist)
|
||||||
|
{
|
||||||
|
if (currentDir.startsWith(blacklisted))
|
||||||
|
ignore = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iterator.fileInfo().isDir() && !ignore)
|
||||||
|
{
|
||||||
|
QString filename = iterator.filePath();
|
||||||
|
|
||||||
|
QFile input(filename);
|
||||||
|
input.open(QIODevice::ReadOnly | QIODevice::WriteOnly);
|
||||||
|
QString sha1sum =
|
||||||
|
QCryptographicHash::hash(input.readAll(), QCryptographicHash::Sha1)
|
||||||
|
.toHex()
|
||||||
|
.constData();
|
||||||
|
|
||||||
|
QString object_name = filename.remove(0, base_length + 1);
|
||||||
|
QLOG_DEBUG() << "Processing" << object_name << ":" << sha1sum << input.size();
|
||||||
|
|
||||||
|
QString object_tlk = sha1sum.left(2);
|
||||||
|
QString object_tlk_dir = objects_dir.path() + "/" + object_tlk;
|
||||||
|
|
||||||
|
QDir tlk_dir(object_tlk_dir);
|
||||||
|
if (!tlk_dir.exists())
|
||||||
|
objects_dir.mkdir(object_tlk);
|
||||||
|
|
||||||
|
QString new_filename = tlk_dir.path() + "/" + sha1sum;
|
||||||
|
QFile new_object(new_filename);
|
||||||
|
if (!new_object.exists())
|
||||||
|
{
|
||||||
|
bool rename_success = input.rename(new_filename);
|
||||||
|
QLOG_DEBUG() << " Doesn't exist, copying to" << new_filename << ":"
|
||||||
|
<< QString::number(rename_success);
|
||||||
|
if (rename_success)
|
||||||
|
successes++;
|
||||||
|
else
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
input.remove();
|
||||||
|
QLOG_DEBUG() << " Already exists, deleting original and not copying.";
|
||||||
|
}
|
||||||
|
|
||||||
|
this->setProgress(100 * ((successes + failures) / (float) this->m_expected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (successes + failures == 0)
|
||||||
|
{
|
||||||
|
this->setProgress(100);
|
||||||
|
QLOG_DEBUG() << "No legacy assets needed importing.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QLOG_DEBUG() << "Finished copying legacy assets:" << successes << "successes and"
|
||||||
|
<< failures << "failures.";
|
||||||
|
|
||||||
|
this->setStatus("Cleaning up legacy assets...");
|
||||||
|
this->setProgress(100);
|
||||||
|
|
||||||
|
QDirIterator cleanup_iterator(assets_dir);
|
||||||
|
|
||||||
|
while (cleanup_iterator.hasNext())
|
||||||
|
{
|
||||||
|
QString currentDir = cleanup_iterator.next();
|
||||||
|
currentDir = currentDir.remove(0, base_length + 1);
|
||||||
|
|
||||||
|
bool ignore = false;
|
||||||
|
for (QString blacklisted : blacklist)
|
||||||
|
{
|
||||||
|
if (currentDir.startsWith(blacklisted))
|
||||||
|
ignore = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleanup_iterator.fileInfo().isDir() && !ignore)
|
||||||
|
{
|
||||||
|
QString path = cleanup_iterator.filePath();
|
||||||
|
QDir folder(path);
|
||||||
|
|
||||||
|
QLOG_DEBUG() << "Cleaning up legacy assets folder:" << path;
|
||||||
|
|
||||||
|
folder.removeRecursively();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(failures > 0)
|
||||||
|
{
|
||||||
|
emitFailed(QString("Failed to migrate %1 legacy assets").arg(failures));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
18
logic/assets/AssetsMigrateTask.h
Normal file
18
logic/assets/AssetsMigrateTask.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "logic/tasks/Task.h"
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class AssetsMigrateTask : public Task
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit AssetsMigrateTask(int expected, QObject* parent=0);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void executeTask();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_expected;
|
||||||
|
};
|
@ -25,23 +25,18 @@
|
|||||||
|
|
||||||
namespace AssetsUtils
|
namespace AssetsUtils
|
||||||
{
|
{
|
||||||
void migrateOldAssets()
|
int findLegacyAssets()
|
||||||
{
|
{
|
||||||
QDir assets_dir("assets");
|
QDir assets_dir("assets");
|
||||||
if (!assets_dir.exists())
|
if (!assets_dir.exists())
|
||||||
return;
|
return 0;
|
||||||
assets_dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
|
assets_dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
|
||||||
int base_length = assets_dir.path().length();
|
int base_length = assets_dir.path().length();
|
||||||
|
|
||||||
QList<QString> blacklist = {"indexes", "objects", "virtual"};
|
QList<QString> blacklist = {"indexes", "objects", "virtual"};
|
||||||
|
|
||||||
if (!assets_dir.exists("objects"))
|
|
||||||
assets_dir.mkdir("objects");
|
|
||||||
QDir objects_dir("assets/objects");
|
|
||||||
|
|
||||||
QDirIterator iterator(assets_dir, QDirIterator::Subdirectories);
|
QDirIterator iterator(assets_dir, QDirIterator::Subdirectories);
|
||||||
int successes = 0;
|
int found = 0;
|
||||||
int failures = 0;
|
|
||||||
while (iterator.hasNext())
|
while (iterator.hasNext())
|
||||||
{
|
{
|
||||||
QString currentDir = iterator.next();
|
QString currentDir = iterator.next();
|
||||||
@ -56,79 +51,11 @@ void migrateOldAssets()
|
|||||||
|
|
||||||
if (!iterator.fileInfo().isDir() && !ignore)
|
if (!iterator.fileInfo().isDir() && !ignore)
|
||||||
{
|
{
|
||||||
QString filename = iterator.filePath();
|
found++;
|
||||||
|
|
||||||
QFile input(filename);
|
|
||||||
input.open(QIODevice::ReadOnly | QIODevice::WriteOnly);
|
|
||||||
QString sha1sum =
|
|
||||||
QCryptographicHash::hash(input.readAll(), QCryptographicHash::Sha1)
|
|
||||||
.toHex()
|
|
||||||
.constData();
|
|
||||||
|
|
||||||
QString object_name = filename.remove(0, base_length + 1);
|
|
||||||
QLOG_DEBUG() << "Processing" << object_name << ":" << sha1sum << input.size();
|
|
||||||
|
|
||||||
QString object_tlk = sha1sum.left(2);
|
|
||||||
QString object_tlk_dir = objects_dir.path() + "/" + object_tlk;
|
|
||||||
|
|
||||||
QDir tlk_dir(object_tlk_dir);
|
|
||||||
if (!tlk_dir.exists())
|
|
||||||
objects_dir.mkdir(object_tlk);
|
|
||||||
|
|
||||||
QString new_filename = tlk_dir.path() + "/" + sha1sum;
|
|
||||||
QFile new_object(new_filename);
|
|
||||||
if (!new_object.exists())
|
|
||||||
{
|
|
||||||
bool rename_success = input.rename(new_filename);
|
|
||||||
QLOG_DEBUG() << " Doesn't exist, copying to" << new_filename << ":"
|
|
||||||
<< QString::number(rename_success);
|
|
||||||
if (rename_success)
|
|
||||||
successes++;
|
|
||||||
else
|
|
||||||
failures++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
input.remove();
|
|
||||||
QLOG_DEBUG() << " Already exists, deleting original and not copying.";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (successes + failures == 0)
|
return found;
|
||||||
{
|
|
||||||
QLOG_DEBUG() << "No legacy assets needed importing.";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QLOG_DEBUG() << "Finished copying legacy assets:" << successes << "successes and"
|
|
||||||
<< failures << "failures.";
|
|
||||||
|
|
||||||
QDirIterator cleanup_iterator(assets_dir);
|
|
||||||
|
|
||||||
while (cleanup_iterator.hasNext())
|
|
||||||
{
|
|
||||||
QString currentDir = cleanup_iterator.next();
|
|
||||||
currentDir = currentDir.remove(0, base_length + 1);
|
|
||||||
|
|
||||||
bool ignore = false;
|
|
||||||
for (QString blacklisted : blacklist)
|
|
||||||
{
|
|
||||||
if (currentDir.startsWith(blacklisted))
|
|
||||||
ignore = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cleanup_iterator.fileInfo().isDir() && !ignore)
|
|
||||||
{
|
|
||||||
QString path = cleanup_iterator.filePath();
|
|
||||||
QDir folder(path);
|
|
||||||
|
|
||||||
QLOG_DEBUG() << "Cleaning up legacy assets folder:" << path;
|
|
||||||
|
|
||||||
folder.removeRecursively();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -34,6 +34,6 @@ struct AssetsIndex
|
|||||||
|
|
||||||
namespace AssetsUtils
|
namespace AssetsUtils
|
||||||
{
|
{
|
||||||
void migrateOldAssets();
|
|
||||||
bool loadAssetsIndexJson(QString file, AssetsIndex* index);
|
bool loadAssetsIndexJson(QString file, AssetsIndex* index);
|
||||||
|
int findLegacyAssets();
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,8 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
|
|||||||
// The JSON object must at least have a username for it to be valid.
|
// The JSON object must at least have a username for it to be valid.
|
||||||
if (!object.value("username").isString())
|
if (!object.value("username").isString())
|
||||||
{
|
{
|
||||||
QLOG_ERROR() << "Can't load Mojang account info from JSON object. Username field is missing or of the wrong type.";
|
QLOG_ERROR() << "Can't load Mojang account info from JSON object. Username field is "
|
||||||
|
"missing or of the wrong type.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +44,8 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
|
|||||||
QJsonArray profileArray = object.value("profiles").toArray();
|
QJsonArray profileArray = object.value("profiles").toArray();
|
||||||
if (profileArray.size() < 1)
|
if (profileArray.size() < 1)
|
||||||
{
|
{
|
||||||
QLOG_ERROR() << "Can't load Mojang account with username \"" << username << "\". No profiles found.";
|
QLOG_ERROR() << "Can't load Mojang account with username \"" << username
|
||||||
|
<< "\". No profiles found.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +65,7 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MojangAccountPtr account(new MojangAccount());
|
MojangAccountPtr account(new MojangAccount());
|
||||||
if(object.value("user").isObject())
|
if (object.value("user").isObject())
|
||||||
{
|
{
|
||||||
User u;
|
User u;
|
||||||
QJsonObject userStructure = object.value("user").toObject();
|
QJsonObject userStructure = object.value("user").toObject();
|
||||||
@ -92,7 +94,7 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
|
|||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
|
|
||||||
MojangAccountPtr MojangAccount::createFromUsername(const QString& username)
|
MojangAccountPtr MojangAccount::createFromUsername(const QString &username)
|
||||||
{
|
{
|
||||||
MojangAccountPtr account(new MojangAccount());
|
MojangAccountPtr account(new MojangAccount());
|
||||||
account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
|
account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
|
||||||
@ -152,27 +154,27 @@ bool MojangAccount::setCurrentProfile(const QString &profileId)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AccountProfile* MojangAccount::currentProfile() const
|
const AccountProfile *MojangAccount::currentProfile() const
|
||||||
{
|
{
|
||||||
if(m_currentProfile == -1)
|
if (m_currentProfile == -1)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return &m_profiles[m_currentProfile];
|
return &m_profiles[m_currentProfile];
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountStatus MojangAccount::accountStatus() const
|
AccountStatus MojangAccount::accountStatus() const
|
||||||
{
|
{
|
||||||
if(m_accessToken.isEmpty())
|
if (m_accessToken.isEmpty())
|
||||||
return NotVerified;
|
return NotVerified;
|
||||||
if(!m_online)
|
if (!m_online)
|
||||||
return Verified;
|
return Verified;
|
||||||
return Online;
|
return Online;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Task> MojangAccount::login(QString password)
|
std::shared_ptr<YggdrasilTask> MojangAccount::login(QString password)
|
||||||
{
|
{
|
||||||
if(m_currentTask)
|
if (m_currentTask)
|
||||||
return m_currentTask;
|
return m_currentTask;
|
||||||
if(password.isEmpty())
|
if (password.isEmpty())
|
||||||
{
|
{
|
||||||
m_currentTask.reset(new RefreshTask(this));
|
m_currentTask.reset(new RefreshTask(this));
|
||||||
}
|
}
|
||||||
@ -196,7 +198,7 @@ void MojangAccount::authFailed(QString reason)
|
|||||||
{
|
{
|
||||||
// This is emitted when the yggdrasil tasks time out or are cancelled.
|
// This is emitted when the yggdrasil tasks time out or are cancelled.
|
||||||
// -> we treat the error as no-op
|
// -> we treat the error as no-op
|
||||||
if(reason != "Yggdrasil task cancelled.")
|
if (reason != "Yggdrasil task cancelled.")
|
||||||
{
|
{
|
||||||
m_online = false;
|
m_online = false;
|
||||||
m_accessToken = QString();
|
m_accessToken = QString();
|
||||||
|
@ -95,7 +95,7 @@ public: /* manipulation */
|
|||||||
* Attempt to login. Empty password means we use the token.
|
* Attempt to login. Empty password means we use the token.
|
||||||
* If the attempt fails because we already are performing some task, it returns false.
|
* If the attempt fails because we already are performing some task, it returns false.
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<Task> login(QString password = QString());
|
std::shared_ptr<YggdrasilTask> login(QString password = QString());
|
||||||
|
|
||||||
void downgrade()
|
void downgrade()
|
||||||
{
|
{
|
||||||
|
@ -48,6 +48,7 @@ void YggdrasilTask::executeTask()
|
|||||||
connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply);
|
connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply);
|
||||||
connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers);
|
connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers);
|
||||||
connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers);
|
connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers);
|
||||||
|
connect(m_netReply, &QNetworkReply::sslErrors, this, &YggdrasilTask::sslErrors);
|
||||||
timeout_keeper.setSingleShot(true);
|
timeout_keeper.setSingleShot(true);
|
||||||
timeout_keeper.start(timeout_max);
|
timeout_keeper.start(timeout_max);
|
||||||
counter.setSingleShot(false);
|
counter.setSingleShot(false);
|
||||||
@ -75,16 +76,45 @@ void YggdrasilTask::abort()
|
|||||||
m_netReply->abort();
|
m_netReply->abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void YggdrasilTask::sslErrors(QList<QSslError> errors)
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
for (auto error : errors)
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "LOGIN SSL Error #" << i << " : " << error.errorString();
|
||||||
|
auto cert = error.certificate();
|
||||||
|
QLOG_ERROR() << "Certificate in question:\n" << cert.toText();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void YggdrasilTask::processReply()
|
void YggdrasilTask::processReply()
|
||||||
{
|
{
|
||||||
setStatus(getStateMessage(STATE_PROCESSING_RESPONSE));
|
setStatus(getStateMessage(STATE_PROCESSING_RESPONSE));
|
||||||
|
|
||||||
|
if (m_netReply->error() == QNetworkReply::SslHandshakeFailedError)
|
||||||
|
{
|
||||||
|
emitFailed(
|
||||||
|
tr("<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>"
|
||||||
|
"<ul>"
|
||||||
|
"<li>You use Windows XP and need to <a "
|
||||||
|
"href=\"http://www.microsoft.com/en-us/download/details.aspx?id=38918\">update "
|
||||||
|
"your root certificates</a></li>"
|
||||||
|
"<li>Some device on your network is interfering with SSL traffic. In that case, "
|
||||||
|
"you have bigger worries than Minecraft not starting.</li>"
|
||||||
|
"<li>Possibly something else. Check the MultiMC log file for details</li>"
|
||||||
|
"</ul>"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// any network errors lead to offline mode right now
|
// any network errors lead to offline mode right now
|
||||||
if (m_netReply->error() >= QNetworkReply::ConnectionRefusedError &&
|
if (m_netReply->error() >= QNetworkReply::ConnectionRefusedError &&
|
||||||
m_netReply->error() <= QNetworkReply::UnknownNetworkError)
|
m_netReply->error() <= QNetworkReply::UnknownNetworkError)
|
||||||
{
|
{
|
||||||
// WARNING/FIXME: the value here is used in MojangAccount to detect the cancel/timeout
|
// WARNING/FIXME: the value here is used in MojangAccount to detect the cancel/timeout
|
||||||
emitFailed("Yggdrasil task cancelled.");
|
emitFailed("Yggdrasil task cancelled.");
|
||||||
|
QLOG_ERROR() << "Yggdrasil task cancelled because of: " << m_netReply->error() << " : "
|
||||||
|
<< m_netReply->errorString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,10 +202,10 @@ QString YggdrasilTask::getStateMessage(const YggdrasilTask::State state) const
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case STATE_SENDING_REQUEST:
|
case STATE_SENDING_REQUEST:
|
||||||
return tr("Sending request to auth servers.");
|
return tr("Sending request to auth servers...");
|
||||||
case STATE_PROCESSING_RESPONSE:
|
case STATE_PROCESSING_RESPONSE:
|
||||||
return tr("Processing response from servers.");
|
return tr("Processing response from servers...");
|
||||||
default:
|
default:
|
||||||
return tr("Processing. Please wait.");
|
return tr("Processing. Please wait...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <qsslerror.h>
|
||||||
|
|
||||||
#include "logic/auth/MojangAccount.h"
|
#include "logic/auth/MojangAccount.h"
|
||||||
|
|
||||||
@ -99,6 +100,7 @@ slots:
|
|||||||
void processReply();
|
void processReply();
|
||||||
void refreshTimers(qint64, qint64);
|
void refreshTimers(qint64, qint64);
|
||||||
void heartbeat();
|
void heartbeat();
|
||||||
|
void sslErrors(QList<QSslError>);
|
||||||
|
|
||||||
public
|
public
|
||||||
slots:
|
slots:
|
||||||
|
@ -194,9 +194,9 @@ QString AuthenticateTask::getStateMessage(const YggdrasilTask::State state) cons
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case STATE_SENDING_REQUEST:
|
case STATE_SENDING_REQUEST:
|
||||||
return tr("Authenticating: Sending request.");
|
return tr("Authenticating: Sending request...");
|
||||||
case STATE_PROCESSING_RESPONSE:
|
case STATE_PROCESSING_RESPONSE:
|
||||||
return tr("Authenticating: Processing response.");
|
return tr("Authenticating: Processing response...");
|
||||||
default:
|
default:
|
||||||
return YggdrasilTask::getStateMessage(state);
|
return YggdrasilTask::getStateMessage(state);
|
||||||
}
|
}
|
||||||
|
@ -145,9 +145,9 @@ QString RefreshTask::getStateMessage(const YggdrasilTask::State state) const
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case STATE_SENDING_REQUEST:
|
case STATE_SENDING_REQUEST:
|
||||||
return tr("Refreshing login token.");
|
return tr("Refreshing login token...");
|
||||||
case STATE_PROCESSING_RESPONSE:
|
case STATE_PROCESSING_RESPONSE:
|
||||||
return tr("Refreshing login token: Processing response.");
|
return tr("Refreshing login token: Processing response...");
|
||||||
default:
|
default:
|
||||||
return YggdrasilTask::getStateMessage(state);
|
return YggdrasilTask::getStateMessage(state);
|
||||||
}
|
}
|
||||||
|
@ -55,9 +55,9 @@ QString ValidateTask::getStateMessage(const YggdrasilTask::State state) const
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case STATE_SENDING_REQUEST:
|
case STATE_SENDING_REQUEST:
|
||||||
return tr("Validating Access Token: Sending request.");
|
return tr("Validating access token: Sending request...");
|
||||||
case STATE_PROCESSING_RESPONSE:
|
case STATE_PROCESSING_RESPONSE:
|
||||||
return tr("Validating Access Token: Processing response.");
|
return tr("Validating access token: Processing response...");
|
||||||
default:
|
default:
|
||||||
return YggdrasilTask::getStateMessage(state);
|
return YggdrasilTask::getStateMessage(state);
|
||||||
}
|
}
|
||||||
|
351
logic/icons/IconList.cpp
Normal file
351
logic/icons/IconList.cpp
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
/* Copyright 2013 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* 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 "IconList.h"
|
||||||
|
#include <pathutils.h>
|
||||||
|
#include <settingsobject.h>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
#include <MultiMC.h>
|
||||||
|
#include <setting.h>
|
||||||
|
|
||||||
|
#define MAX_SIZE 1024
|
||||||
|
|
||||||
|
IconList::IconList(QObject *parent) : QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
// add builtin icons
|
||||||
|
QDir instance_icons(":/icons/instances/");
|
||||||
|
auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name);
|
||||||
|
for (auto file_info : file_info_list)
|
||||||
|
{
|
||||||
|
QString key = file_info.baseName();
|
||||||
|
addIcon(key, key, file_info.absoluteFilePath(), MMCIcon::Builtin);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_watcher.reset(new QFileSystemWatcher());
|
||||||
|
is_watching = false;
|
||||||
|
connect(m_watcher.get(), SIGNAL(directoryChanged(QString)),
|
||||||
|
SLOT(directoryChanged(QString)));
|
||||||
|
connect(m_watcher.get(), SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString)));
|
||||||
|
|
||||||
|
auto setting = MMC->settings()->getSetting("IconsDir");
|
||||||
|
QString path = setting->get().toString();
|
||||||
|
connect(setting.get(), SIGNAL(settingChanged(const Setting &, QVariant)),
|
||||||
|
SLOT(settingChanged(const Setting &, QVariant)));
|
||||||
|
directoryChanged(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IconList::directoryChanged(const QString &path)
|
||||||
|
{
|
||||||
|
QDir new_dir (path);
|
||||||
|
if(m_dir.absolutePath() != new_dir.absolutePath())
|
||||||
|
{
|
||||||
|
m_dir.setPath(path);
|
||||||
|
m_dir.refresh();
|
||||||
|
if(is_watching)
|
||||||
|
stopWatching();
|
||||||
|
startWatching();
|
||||||
|
}
|
||||||
|
if(!m_dir.exists())
|
||||||
|
if(!ensureFolderPathExists(m_dir.absolutePath()))
|
||||||
|
return;
|
||||||
|
m_dir.refresh();
|
||||||
|
auto new_list = m_dir.entryList(QDir::Files, QDir::Name);
|
||||||
|
for (auto it = new_list.begin(); it != new_list.end(); it++)
|
||||||
|
{
|
||||||
|
QString &foo = (*it);
|
||||||
|
foo = m_dir.filePath(foo);
|
||||||
|
}
|
||||||
|
auto new_set = new_list.toSet();
|
||||||
|
QList<QString> current_list;
|
||||||
|
for (auto &it : icons)
|
||||||
|
{
|
||||||
|
if (!it.has(MMCIcon::FileBased))
|
||||||
|
continue;
|
||||||
|
current_list.push_back(it.m_images[MMCIcon::FileBased].filename);
|
||||||
|
}
|
||||||
|
QSet<QString> current_set = current_list.toSet();
|
||||||
|
|
||||||
|
QSet<QString> to_remove = current_set;
|
||||||
|
to_remove -= new_set;
|
||||||
|
|
||||||
|
QSet<QString> to_add = new_set;
|
||||||
|
to_add -= current_set;
|
||||||
|
|
||||||
|
for (auto remove : to_remove)
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Removing " << remove;
|
||||||
|
QFileInfo rmfile(remove);
|
||||||
|
QString key = rmfile.baseName();
|
||||||
|
int idx = getIconIndex(key);
|
||||||
|
if (idx == -1)
|
||||||
|
continue;
|
||||||
|
icons[idx].remove(MMCIcon::FileBased);
|
||||||
|
if (icons[idx].type() == MMCIcon::ToBeDeleted)
|
||||||
|
{
|
||||||
|
beginRemoveRows(QModelIndex(), idx, idx);
|
||||||
|
icons.remove(idx);
|
||||||
|
reindex();
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dataChanged(index(idx), index(idx));
|
||||||
|
}
|
||||||
|
m_watcher->removePath(remove);
|
||||||
|
emit iconUpdated(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto add : to_add)
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Adding " << add;
|
||||||
|
QFileInfo addfile(add);
|
||||||
|
QString key = addfile.baseName();
|
||||||
|
if (addIcon(key, QString(), addfile.filePath(), MMCIcon::FileBased))
|
||||||
|
{
|
||||||
|
m_watcher->addPath(add);
|
||||||
|
emit iconUpdated(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IconList::fileChanged(const QString &path)
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Checking " << path;
|
||||||
|
QFileInfo checkfile(path);
|
||||||
|
if (!checkfile.exists())
|
||||||
|
return;
|
||||||
|
QString key = checkfile.baseName();
|
||||||
|
int idx = getIconIndex(key);
|
||||||
|
if (idx == -1)
|
||||||
|
return;
|
||||||
|
QIcon icon(path);
|
||||||
|
if (!icon.availableSizes().size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
icons[idx].m_images[MMCIcon::FileBased].icon = icon;
|
||||||
|
dataChanged(index(idx), index(idx));
|
||||||
|
emit iconUpdated(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IconList::settingChanged(const Setting &setting, QVariant value)
|
||||||
|
{
|
||||||
|
if(setting.id() != "IconsDir")
|
||||||
|
return;
|
||||||
|
|
||||||
|
directoryChanged(value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void IconList::startWatching()
|
||||||
|
{
|
||||||
|
auto abs_path = m_dir.absolutePath();
|
||||||
|
ensureFolderPathExists(abs_path);
|
||||||
|
is_watching = m_watcher->addPath(abs_path);
|
||||||
|
if (is_watching)
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Started watching " << abs_path;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Failed to start watching " << abs_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IconList::stopWatching()
|
||||||
|
{
|
||||||
|
m_watcher->removePaths(m_watcher->files());
|
||||||
|
m_watcher->removePaths(m_watcher->directories());
|
||||||
|
is_watching = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList IconList::mimeTypes() const
|
||||||
|
{
|
||||||
|
QStringList types;
|
||||||
|
types << "text/uri-list";
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
Qt::DropActions IconList::supportedDropActions() const
|
||||||
|
{
|
||||||
|
return Qt::CopyAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
|
||||||
|
const QModelIndex &parent)
|
||||||
|
{
|
||||||
|
if (action == Qt::IgnoreAction)
|
||||||
|
return true;
|
||||||
|
// check if the action is supported
|
||||||
|
if (!data || !(action & supportedDropActions()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// files dropped from outside?
|
||||||
|
if (data->hasUrls())
|
||||||
|
{
|
||||||
|
auto urls = data->urls();
|
||||||
|
QStringList iconFiles;
|
||||||
|
for (auto url : urls)
|
||||||
|
{
|
||||||
|
// only local files may be dropped...
|
||||||
|
if (!url.isLocalFile())
|
||||||
|
continue;
|
||||||
|
iconFiles += url.toLocalFile();
|
||||||
|
}
|
||||||
|
installIcons(iconFiles);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags IconList::flags(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
|
||||||
|
if (index.isValid())
|
||||||
|
return Qt::ItemIsDropEnabled | defaultFlags;
|
||||||
|
else
|
||||||
|
return Qt::ItemIsDropEnabled | defaultFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant IconList::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
int row = index.row();
|
||||||
|
|
||||||
|
if (row < 0 || row >= icons.size())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
switch (role)
|
||||||
|
{
|
||||||
|
case Qt::DecorationRole:
|
||||||
|
return icons[row].icon();
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
return icons[row].name();
|
||||||
|
case Qt::UserRole:
|
||||||
|
return icons[row].m_key;
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int IconList::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return icons.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IconList::installIcons(QStringList iconFiles)
|
||||||
|
{
|
||||||
|
for (QString file : iconFiles)
|
||||||
|
{
|
||||||
|
QFileInfo fileinfo(file);
|
||||||
|
if (!fileinfo.isReadable() || !fileinfo.isFile())
|
||||||
|
continue;
|
||||||
|
QString target = PathCombine("icons", fileinfo.fileName());
|
||||||
|
|
||||||
|
QString suffix = fileinfo.suffix();
|
||||||
|
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!QFile::copy(file, target))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IconList::deleteIcon(QString key)
|
||||||
|
{
|
||||||
|
int iconIdx = getIconIndex(key);
|
||||||
|
if (iconIdx == -1)
|
||||||
|
return false;
|
||||||
|
auto &iconEntry = icons[iconIdx];
|
||||||
|
if (iconEntry.has(MMCIcon::FileBased))
|
||||||
|
{
|
||||||
|
return QFile::remove(iconEntry.m_images[MMCIcon::FileBased].filename);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IconList::addIcon(QString key, QString name, QString path, MMCIcon::Type type)
|
||||||
|
{
|
||||||
|
// replace the icon even? is the input valid?
|
||||||
|
QIcon icon(path);
|
||||||
|
if (!icon.availableSizes().size())
|
||||||
|
return false;
|
||||||
|
auto iter = name_index.find(key);
|
||||||
|
if (iter != name_index.end())
|
||||||
|
{
|
||||||
|
auto &oldOne = icons[*iter];
|
||||||
|
oldOne.replace(type, icon, path);
|
||||||
|
dataChanged(index(*iter), index(*iter));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// add a new icon
|
||||||
|
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
||||||
|
{
|
||||||
|
MMCIcon mmc_icon;
|
||||||
|
mmc_icon.m_name = name;
|
||||||
|
mmc_icon.m_key = key;
|
||||||
|
mmc_icon.replace(type, icon, path);
|
||||||
|
icons.push_back(mmc_icon);
|
||||||
|
name_index[key] = icons.size() - 1;
|
||||||
|
}
|
||||||
|
endInsertRows();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IconList::reindex()
|
||||||
|
{
|
||||||
|
name_index.clear();
|
||||||
|
int i = 0;
|
||||||
|
for (auto &iter : icons)
|
||||||
|
{
|
||||||
|
name_index[iter.m_key] = i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon IconList::getIcon(QString key)
|
||||||
|
{
|
||||||
|
int icon_index = getIconIndex(key);
|
||||||
|
|
||||||
|
if (icon_index != -1)
|
||||||
|
return icons[icon_index].icon();
|
||||||
|
|
||||||
|
// Fallback for icons that don't exist.
|
||||||
|
icon_index = getIconIndex("infinity");
|
||||||
|
|
||||||
|
if (icon_index != -1)
|
||||||
|
return icons[icon_index].icon();
|
||||||
|
return QIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
int IconList::getIconIndex(QString key)
|
||||||
|
{
|
||||||
|
if (key == "default")
|
||||||
|
key = "infinity";
|
||||||
|
|
||||||
|
auto iter = name_index.find(key);
|
||||||
|
if (iter != name_index.end())
|
||||||
|
return *iter;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#include "IconList.moc"
|
@ -17,15 +17,21 @@
|
|||||||
|
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDir>
|
||||||
#include <QtGui/QIcon>
|
#include <QtGui/QIcon>
|
||||||
|
#include <memory>
|
||||||
|
#include "MMCIcon.h"
|
||||||
|
#include "setting.h"
|
||||||
|
|
||||||
class Private;
|
class QFileSystemWatcher;
|
||||||
|
|
||||||
class IconList : public QAbstractListModel
|
class IconList : public QAbstractListModel
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
IconList();
|
explicit IconList(QObject *parent = 0);
|
||||||
virtual ~IconList();
|
virtual ~IconList() {};
|
||||||
|
|
||||||
QIcon getIcon(QString key);
|
QIcon getIcon(QString key);
|
||||||
int getIconIndex(QString key);
|
int getIconIndex(QString key);
|
||||||
@ -33,7 +39,7 @@ public:
|
|||||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||||
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
|
||||||
bool addIcon(QString key, QString name, QString path, bool is_builtin = false);
|
bool addIcon(QString key, QString name, QString path, MMCIcon::Type type);
|
||||||
bool deleteIcon(QString key);
|
bool deleteIcon(QString key);
|
||||||
|
|
||||||
virtual QStringList mimeTypes() const;
|
virtual QStringList mimeTypes() const;
|
||||||
@ -44,11 +50,28 @@ public:
|
|||||||
|
|
||||||
void installIcons(QStringList iconFiles);
|
void installIcons(QStringList iconFiles);
|
||||||
|
|
||||||
|
void startWatching();
|
||||||
|
void stopWatching();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void iconUpdated(QString key);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// hide copy constructor
|
// hide copy constructor
|
||||||
IconList(const IconList &) = delete;
|
IconList(const IconList &) = delete;
|
||||||
// hide assign op
|
// hide assign op
|
||||||
IconList &operator=(const IconList &) = delete;
|
IconList &operator=(const IconList &) = delete;
|
||||||
void reindex();
|
void reindex();
|
||||||
Private *d;
|
|
||||||
|
protected
|
||||||
|
slots:
|
||||||
|
void directoryChanged(const QString &path);
|
||||||
|
void fileChanged(const QString &path);
|
||||||
|
void settingChanged(const Setting & setting, QVariant value);
|
||||||
|
private:
|
||||||
|
std::shared_ptr<QFileSystemWatcher> m_watcher;
|
||||||
|
bool is_watching;
|
||||||
|
QMap<QString, int> name_index;
|
||||||
|
QVector<MMCIcon> icons;
|
||||||
|
QDir m_dir;
|
||||||
};
|
};
|
89
logic/icons/MMCIcon.cpp
Normal file
89
logic/icons/MMCIcon.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/* Copyright 2013 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* 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 "MMCIcon.h"
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
MMCIcon::Type operator--(MMCIcon::Type &t, int)
|
||||||
|
{
|
||||||
|
MMCIcon::Type temp = t;
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
case MMCIcon::Type::Builtin:
|
||||||
|
t = MMCIcon::Type::ToBeDeleted;
|
||||||
|
break;
|
||||||
|
case MMCIcon::Type::Transient:
|
||||||
|
t = MMCIcon::Type::Builtin;
|
||||||
|
break;
|
||||||
|
case MMCIcon::Type::FileBased:
|
||||||
|
t = MMCIcon::Type::Transient;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
MMCIcon::Type MMCIcon::type() const
|
||||||
|
{
|
||||||
|
return m_current_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MMCIcon::name() const
|
||||||
|
{
|
||||||
|
if (m_name.size())
|
||||||
|
return m_name;
|
||||||
|
return m_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMCIcon::has(MMCIcon::Type _type) const
|
||||||
|
{
|
||||||
|
return m_images[_type].present();
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon MMCIcon::icon() const
|
||||||
|
{
|
||||||
|
if (m_current_type == Type::ToBeDeleted)
|
||||||
|
return QIcon();
|
||||||
|
return m_images[m_current_type].icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MMCIcon::remove(Type rm_type)
|
||||||
|
{
|
||||||
|
m_images[rm_type].filename = QString();
|
||||||
|
m_images[rm_type].icon = QIcon();
|
||||||
|
for (auto iter = rm_type; iter != Type::ToBeDeleted; iter--)
|
||||||
|
{
|
||||||
|
if (m_images[iter].present())
|
||||||
|
{
|
||||||
|
m_current_type = iter;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_current_type = Type::ToBeDeleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MMCIcon::replace(MMCIcon::Type new_type, QIcon icon, QString path)
|
||||||
|
{
|
||||||
|
QFileInfo foo(path);
|
||||||
|
if (new_type > m_current_type || m_current_type == MMCIcon::ToBeDeleted)
|
||||||
|
{
|
||||||
|
m_current_type = new_type;
|
||||||
|
}
|
||||||
|
m_images[new_type].icon = icon;
|
||||||
|
m_images[new_type].changed = foo.lastModified();
|
||||||
|
m_images[new_type].filename = path;
|
||||||
|
}
|
@ -13,32 +13,40 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "include/basicsettingsobject.h"
|
#pragma once
|
||||||
#include "include/setting.h"
|
#include <QString>
|
||||||
|
#include <QDateTime>
|
||||||
BasicSettingsObject::BasicSettingsObject(QObject *parent) : SettingsObject(parent)
|
#include <QIcon>
|
||||||
|
struct MMCImage
|
||||||
{
|
{
|
||||||
}
|
QIcon icon;
|
||||||
|
QString filename;
|
||||||
|
QDateTime changed;
|
||||||
|
bool present() const
|
||||||
|
{
|
||||||
|
return !icon.isNull();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void BasicSettingsObject::changeSetting(const Setting &setting, QVariant value)
|
struct MMCIcon
|
||||||
{
|
{
|
||||||
if (contains(setting.id()))
|
enum Type : unsigned
|
||||||
{
|
{
|
||||||
if (value.isValid())
|
Builtin,
|
||||||
config.setValue(setting.configKey(), value);
|
Transient,
|
||||||
else
|
FileBased,
|
||||||
config.remove(setting.configKey());
|
ICONS_TOTAL,
|
||||||
}
|
ToBeDeleted
|
||||||
}
|
};
|
||||||
|
QString m_key;
|
||||||
|
QString m_name;
|
||||||
|
MMCImage m_images[ICONS_TOTAL];
|
||||||
|
Type m_current_type = ToBeDeleted;
|
||||||
|
|
||||||
QVariant BasicSettingsObject::retrieveValue(const Setting &setting)
|
Type type() const;
|
||||||
{
|
QString name() const;
|
||||||
if (contains(setting.id()))
|
bool has(Type _type) const;
|
||||||
{
|
QIcon icon() const;
|
||||||
return config.value(setting.configKey());
|
void remove(Type rm_type);
|
||||||
}
|
void replace(Type new_type, QIcon icon, QString path = QString());
|
||||||
else
|
};
|
||||||
{
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "ForgeVersionList.h"
|
#include "ForgeVersionList.h"
|
||||||
#include <logic/net/NetJob.h>
|
#include <logic/net/NetJob.h>
|
||||||
|
#include <logic/net/URLConstants.h>
|
||||||
#include "MultiMC.h"
|
#include "MultiMC.h"
|
||||||
|
|
||||||
#include <QtNetwork>
|
#include <QtNetwork>
|
||||||
@ -23,8 +24,6 @@
|
|||||||
|
|
||||||
#include "logger/QsLog.h"
|
#include "logger/QsLog.h"
|
||||||
|
|
||||||
#define JSON_URL "http://files.minecraftforge.net/minecraftforge/json"
|
|
||||||
|
|
||||||
ForgeVersionList::ForgeVersionList(QObject *parent) : BaseVersionList(parent)
|
ForgeVersionList::ForgeVersionList(QObject *parent) : BaseVersionList(parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -159,44 +158,43 @@ ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList *vlist) : Task()
|
|||||||
|
|
||||||
void ForgeListLoadTask::executeTask()
|
void ForgeListLoadTask::executeTask()
|
||||||
{
|
{
|
||||||
|
setStatus(tr("Fetching Forge version lists..."));
|
||||||
auto job = new NetJob("Version index");
|
auto job = new NetJob("Version index");
|
||||||
// we do not care if the version is stale or not.
|
// we do not care if the version is stale or not.
|
||||||
auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json");
|
auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json");
|
||||||
|
auto gradleForgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "json");
|
||||||
|
|
||||||
// verify by poking the server.
|
// verify by poking the server.
|
||||||
forgeListEntry->stale = true;
|
forgeListEntry->stale = true;
|
||||||
|
gradleForgeListEntry->stale = true;
|
||||||
|
|
||||||
|
job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::FORGE_LEGACY_URL),
|
||||||
|
forgeListEntry));
|
||||||
|
job->addNetAction(gradleListDownload = CacheDownload::make(
|
||||||
|
QUrl(URLConstants::FORGE_GRADLE_URL), gradleForgeListEntry));
|
||||||
|
|
||||||
|
connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed()));
|
||||||
|
connect(gradleListDownload.get(), SIGNAL(failed(int)), SLOT(gradleListFailed()));
|
||||||
|
|
||||||
job->addNetAction(CacheDownload::make(QUrl(JSON_URL), forgeListEntry));
|
|
||||||
listJob.reset(job);
|
listJob.reset(job);
|
||||||
connect(listJob.get(), SIGNAL(succeeded()), SLOT(list_downloaded()));
|
connect(listJob.get(), SIGNAL(succeeded()), SLOT(listDownloaded()));
|
||||||
connect(listJob.get(), SIGNAL(failed()), SLOT(list_failed()));
|
|
||||||
connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
|
connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
|
||||||
listJob->start();
|
listJob->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForgeListLoadTask::list_failed()
|
bool ForgeListLoadTask::parseForgeList(QList<BaseVersionPtr> &out)
|
||||||
{
|
|
||||||
auto DlJob = listJob->first();
|
|
||||||
auto reply = DlJob->m_reply;
|
|
||||||
if (reply)
|
|
||||||
{
|
|
||||||
QLOG_ERROR() << "Getting forge version list failed: " << reply->errorString();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
QLOG_ERROR() << "Getting forge version list failed for reasons unknown.";
|
|
||||||
}
|
|
||||||
|
|
||||||
void ForgeListLoadTask::list_downloaded()
|
|
||||||
{
|
{
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
{
|
{
|
||||||
auto DlJob = listJob->first();
|
auto dlJob = listDownload;
|
||||||
auto filename = std::dynamic_pointer_cast<CacheDownload>(DlJob)->m_target_path;
|
auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->m_target_path;
|
||||||
QFile listFile(filename);
|
QFile listFile(filename);
|
||||||
if (!listFile.open(QIODevice::ReadOnly))
|
if (!listFile.open(QIODevice::ReadOnly))
|
||||||
return;
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
data = listFile.readAll();
|
data = listFile.readAll();
|
||||||
DlJob.reset();
|
dlJob.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonParseError jsonError;
|
QJsonParseError jsonError;
|
||||||
@ -205,13 +203,13 @@ void ForgeListLoadTask::list_downloaded()
|
|||||||
if (jsonError.error != QJsonParseError::NoError)
|
if (jsonError.error != QJsonParseError::NoError)
|
||||||
{
|
{
|
||||||
emitFailed("Error parsing version list JSON:" + jsonError.errorString());
|
emitFailed("Error parsing version list JSON:" + jsonError.errorString());
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!jsonDoc.isObject())
|
if (!jsonDoc.isObject())
|
||||||
{
|
{
|
||||||
emitFailed("Error parsing version list JSON: jsonDoc is not an object");
|
emitFailed("Error parsing version list JSON: JSON root is not an object");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject root = jsonDoc.object();
|
QJsonObject root = jsonDoc.object();
|
||||||
@ -221,11 +219,10 @@ void ForgeListLoadTask::list_downloaded()
|
|||||||
{
|
{
|
||||||
emitFailed(
|
emitFailed(
|
||||||
"Error parsing version list JSON: version list object is missing 'builds' array");
|
"Error parsing version list JSON: version list object is missing 'builds' array");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
QJsonArray builds = root.value("builds").toArray();
|
QJsonArray builds = root.value("builds").toArray();
|
||||||
|
|
||||||
QList<BaseVersionPtr> tempList;
|
|
||||||
for (int i = 0; i < builds.count(); i++)
|
for (int i = 0; i < builds.count(); i++)
|
||||||
{
|
{
|
||||||
// Load the version info.
|
// Load the version info.
|
||||||
@ -246,7 +243,9 @@ void ForgeListLoadTask::list_downloaded()
|
|||||||
for (int j = 0; j < files.count(); j++)
|
for (int j = 0; j < files.count(); j++)
|
||||||
{
|
{
|
||||||
if (!files[j].isObject())
|
if (!files[j].isObject())
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
QJsonObject file = files[j].toObject();
|
QJsonObject file = files[j].toObject();
|
||||||
buildtype = file.value("buildtype").toString();
|
buildtype = file.value("buildtype").toString();
|
||||||
if ((buildtype == "client" || buildtype == "universal") && !valid)
|
if ((buildtype == "client" || buildtype == "universal") && !valid)
|
||||||
@ -262,7 +261,9 @@ void ForgeListLoadTask::list_downloaded()
|
|||||||
{
|
{
|
||||||
QString ext = file.value("ext").toString();
|
QString ext = file.value("ext").toString();
|
||||||
if (ext.isEmpty())
|
if (ext.isEmpty())
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
changelog_url = file.value("url").toString();
|
changelog_url = file.value("url").toString();
|
||||||
}
|
}
|
||||||
else if (buildtype == "installer")
|
else if (buildtype == "installer")
|
||||||
@ -282,15 +283,161 @@ void ForgeListLoadTask::list_downloaded()
|
|||||||
fVersion->jobbuildver = jobbuildver;
|
fVersion->jobbuildver = jobbuildver;
|
||||||
fVersion->mcver = mcver;
|
fVersion->mcver = mcver;
|
||||||
if (installer_filename.isEmpty())
|
if (installer_filename.isEmpty())
|
||||||
|
{
|
||||||
fVersion->filename = filename;
|
fVersion->filename = filename;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
fVersion->filename = installer_filename;
|
fVersion->filename = installer_filename;
|
||||||
|
}
|
||||||
fVersion->m_buildnr = build_nr;
|
fVersion->m_buildnr = build_nr;
|
||||||
tempList.append(fVersion);
|
out.append(fVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_list->updateListData(tempList);
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ForgeListLoadTask::parseForgeGradleList(QList<BaseVersionPtr> &out)
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
{
|
||||||
|
auto dlJob = gradleListDownload;
|
||||||
|
auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->m_target_path;
|
||||||
|
QFile listFile(filename);
|
||||||
|
if (!listFile.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
data = listFile.readAll();
|
||||||
|
dlJob.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonParseError jsonError;
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
|
||||||
|
|
||||||
|
if (jsonError.error != QJsonParseError::NoError)
|
||||||
|
{
|
||||||
|
emitFailed("Error parsing gradle version list JSON:" + jsonError.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jsonDoc.isObject())
|
||||||
|
{
|
||||||
|
emitFailed("Error parsing gradle version list JSON: JSON root is not an object");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject root = jsonDoc.object();
|
||||||
|
|
||||||
|
// we probably could hard code these, but it might still be worth doing it this way
|
||||||
|
const QString webpath = root.value("webpath").toString();
|
||||||
|
const QString artifact = root.value("artifact").toString();
|
||||||
|
|
||||||
|
QJsonObject numbers = root.value("number").toObject();
|
||||||
|
for (auto it = numbers.begin(); it != numbers.end(); ++it)
|
||||||
|
{
|
||||||
|
QJsonObject number = it.value().toObject();
|
||||||
|
std::shared_ptr<ForgeVersion> fVersion(new ForgeVersion());
|
||||||
|
fVersion->m_buildnr = number.value("build").toDouble();
|
||||||
|
fVersion->jobbuildver = number.value("version").toString();
|
||||||
|
fVersion->mcver = number.value("mcversion").toString();
|
||||||
|
fVersion->filename = "";
|
||||||
|
QString filename, installer_filename;
|
||||||
|
QJsonArray files = number.value("files").toArray();
|
||||||
|
for (auto fIt = files.begin(); fIt != files.end(); ++fIt)
|
||||||
|
{
|
||||||
|
// TODO with gradle we also get checksums, use them
|
||||||
|
QJsonArray file = (*fIt).toArray();
|
||||||
|
if (file.size() < 3)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (file.at(1).toString() == "installer")
|
||||||
|
{
|
||||||
|
fVersion->installer_url = QString("%1/%2-%3/%4-%2-%3-installer.%5").arg(
|
||||||
|
webpath, fVersion->mcver, fVersion->jobbuildver, artifact,
|
||||||
|
file.at(0).toString());
|
||||||
|
installer_filename = QString("%1-%2-%3-installer.%4").arg(
|
||||||
|
artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString());
|
||||||
|
}
|
||||||
|
else if (file.at(1).toString() == "universal")
|
||||||
|
{
|
||||||
|
fVersion->universal_url = QString("%1/%2-%3/%4-%2-%3-universal.%5").arg(
|
||||||
|
webpath, fVersion->mcver, fVersion->jobbuildver, artifact,
|
||||||
|
file.at(0).toString());
|
||||||
|
filename = QString("%1-%2-%3-universal.%4").arg(
|
||||||
|
artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString());
|
||||||
|
}
|
||||||
|
else if (file.at(1).toString() == "changelog")
|
||||||
|
{
|
||||||
|
fVersion->changelog_url = QString("%1/%2-%3/%4-%2-%3-changelog.%5").arg(
|
||||||
|
webpath, fVersion->mcver, fVersion->jobbuildver, artifact,
|
||||||
|
file.at(0).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fVersion->installer_url.isEmpty() && fVersion->universal_url.isEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fVersion->filename = fVersion->installer_url.isEmpty() ? filename : installer_filename;
|
||||||
|
out.append(fVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForgeListLoadTask::listDownloaded()
|
||||||
|
{
|
||||||
|
QList<BaseVersionPtr> list;
|
||||||
|
bool ret = true;
|
||||||
|
if (!parseForgeList(list))
|
||||||
|
{
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
if (!parseForgeGradleList(list))
|
||||||
|
{
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qSort(list.begin(), list.end(), [](const BaseVersionPtr & p1, const BaseVersionPtr & p2)
|
||||||
|
{
|
||||||
|
// TODO better comparison (takes major/minor/build number into account)
|
||||||
|
return p1->name() > p2->name();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_list->updateListData(list);
|
||||||
|
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ForgeListLoadTask::listFailed()
|
||||||
|
{
|
||||||
|
auto reply = listDownload->m_reply;
|
||||||
|
if (reply)
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Getting forge version list failed: " << reply->errorString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Getting forge version list failed for reasons unknown.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ForgeListLoadTask::gradleListFailed()
|
||||||
|
{
|
||||||
|
auto reply = gradleListDownload->m_reply;
|
||||||
|
if (reply)
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Getting forge version list failed: " << reply->errorString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Getting forge version list failed for reasons unknown.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -80,7 +80,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
QList<BaseVersionPtr> m_vlist;
|
QList<BaseVersionPtr> m_vlist;
|
||||||
|
|
||||||
bool m_loaded;
|
bool m_loaded = false;
|
||||||
|
|
||||||
protected
|
protected
|
||||||
slots:
|
slots:
|
||||||
@ -98,10 +98,18 @@ public:
|
|||||||
|
|
||||||
protected
|
protected
|
||||||
slots:
|
slots:
|
||||||
void list_downloaded();
|
void listDownloaded();
|
||||||
void list_failed();
|
void listFailed();
|
||||||
|
void gradleListFailed();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
NetJobPtr listJob;
|
NetJobPtr listJob;
|
||||||
ForgeVersionList *m_list;
|
ForgeVersionList *m_list;
|
||||||
|
|
||||||
|
CacheDownloadPtr listDownload;
|
||||||
|
CacheDownloadPtr gradleListDownload;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool parseForgeList(QList<BaseVersionPtr> &out);
|
||||||
|
bool parseForgeGradleList(QList<BaseVersionPtr> &out);
|
||||||
};
|
};
|
||||||
|
@ -1,271 +0,0 @@
|
|||||||
/* Copyright 2013 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* 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 "IconList.h"
|
|
||||||
#include <pathutils.h>
|
|
||||||
#include <QMap>
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QMimeData>
|
|
||||||
#include <QUrl>
|
|
||||||
#define MAX_SIZE 1024
|
|
||||||
|
|
||||||
struct entry
|
|
||||||
{
|
|
||||||
QString key;
|
|
||||||
QString name;
|
|
||||||
QIcon icon;
|
|
||||||
bool is_builtin;
|
|
||||||
QString filename;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Private : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
QMap<QString, int> index;
|
|
||||||
QVector<entry> icons;
|
|
||||||
Private()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
IconList::IconList() : QAbstractListModel(), d(new Private())
|
|
||||||
{
|
|
||||||
QDir instance_icons(":/icons/instances/");
|
|
||||||
auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name);
|
|
||||||
for (auto file_info : file_info_list)
|
|
||||||
{
|
|
||||||
QString key = file_info.baseName();
|
|
||||||
addIcon(key, key, file_info.absoluteFilePath(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: get from settings
|
|
||||||
ensureFolderPathExists("icons");
|
|
||||||
QDir user_icons("icons");
|
|
||||||
file_info_list = user_icons.entryInfoList(QDir::Files, QDir::Name);
|
|
||||||
for (auto file_info : file_info_list)
|
|
||||||
{
|
|
||||||
QString filename = file_info.absoluteFilePath();
|
|
||||||
QString key = file_info.baseName();
|
|
||||||
addIcon(key, key, filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IconList::~IconList()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
d = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList IconList::mimeTypes() const
|
|
||||||
{
|
|
||||||
QStringList types;
|
|
||||||
types << "text/uri-list";
|
|
||||||
return types;
|
|
||||||
}
|
|
||||||
Qt::DropActions IconList::supportedDropActions() const
|
|
||||||
{
|
|
||||||
return Qt::CopyAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
|
|
||||||
const QModelIndex &parent)
|
|
||||||
{
|
|
||||||
if (action == Qt::IgnoreAction)
|
|
||||||
return true;
|
|
||||||
// check if the action is supported
|
|
||||||
if (!data || !(action & supportedDropActions()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// files dropped from outside?
|
|
||||||
if (data->hasUrls())
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
bool was_watching = is_watching;
|
|
||||||
if(was_watching)
|
|
||||||
stopWatching();
|
|
||||||
*/
|
|
||||||
auto urls = data->urls();
|
|
||||||
QStringList iconFiles;
|
|
||||||
for (auto url : urls)
|
|
||||||
{
|
|
||||||
// only local files may be dropped...
|
|
||||||
if (!url.isLocalFile())
|
|
||||||
continue;
|
|
||||||
iconFiles += url.toLocalFile();
|
|
||||||
}
|
|
||||||
installIcons(iconFiles);
|
|
||||||
/*
|
|
||||||
if(was_watching)
|
|
||||||
startWatching();
|
|
||||||
*/
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::ItemFlags IconList::flags(const QModelIndex &index) const
|
|
||||||
{
|
|
||||||
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
|
|
||||||
if (index.isValid())
|
|
||||||
return Qt::ItemIsDropEnabled | defaultFlags;
|
|
||||||
else
|
|
||||||
return Qt::ItemIsDropEnabled | defaultFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant IconList::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
int row = index.row();
|
|
||||||
|
|
||||||
if (row < 0 || row >= d->icons.size())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
switch (role)
|
|
||||||
{
|
|
||||||
case Qt::DecorationRole:
|
|
||||||
return d->icons[row].icon;
|
|
||||||
case Qt::DisplayRole:
|
|
||||||
return d->icons[row].name;
|
|
||||||
case Qt::UserRole:
|
|
||||||
return d->icons[row].key;
|
|
||||||
default:
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int IconList::rowCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
return d->icons.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IconList::installIcons(QStringList iconFiles)
|
|
||||||
{
|
|
||||||
for (QString file : iconFiles)
|
|
||||||
{
|
|
||||||
QFileInfo fileinfo(file);
|
|
||||||
if (!fileinfo.isReadable() || !fileinfo.isFile())
|
|
||||||
continue;
|
|
||||||
QString target = PathCombine("icons", fileinfo.fileName());
|
|
||||||
|
|
||||||
QString suffix = fileinfo.suffix();
|
|
||||||
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!QFile::copy(file, target))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
QString key = fileinfo.baseName();
|
|
||||||
addIcon(key, key, target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IconList::deleteIcon(QString key)
|
|
||||||
{
|
|
||||||
int iconIdx = getIconIndex(key);
|
|
||||||
if (iconIdx == -1)
|
|
||||||
return false;
|
|
||||||
auto &iconEntry = d->icons[iconIdx];
|
|
||||||
if (iconEntry.is_builtin)
|
|
||||||
return false;
|
|
||||||
if (QFile::remove(iconEntry.filename))
|
|
||||||
{
|
|
||||||
beginRemoveRows(QModelIndex(), iconIdx, iconIdx);
|
|
||||||
d->icons.remove(iconIdx);
|
|
||||||
reindex();
|
|
||||||
endRemoveRows();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IconList::addIcon(QString key, QString name, QString path, bool is_builtin)
|
|
||||||
{
|
|
||||||
auto iter = d->index.find(key);
|
|
||||||
if (iter != d->index.end())
|
|
||||||
{
|
|
||||||
if (d->icons[*iter].is_builtin)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
QIcon icon(path);
|
|
||||||
if (icon.isNull())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto &oldOne = d->icons[*iter];
|
|
||||||
|
|
||||||
if (!QFile::remove(oldOne.filename))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// replace the icon
|
|
||||||
oldOne = {key, name, icon, is_builtin, path};
|
|
||||||
dataChanged(index(*iter), index(*iter));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QIcon icon(path);
|
|
||||||
if (icon.isNull())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// add a new icon
|
|
||||||
beginInsertRows(QModelIndex(), d->icons.size(), d->icons.size());
|
|
||||||
d->icons.push_back({key, name, icon, is_builtin, path});
|
|
||||||
d->index[key] = d->icons.size() - 1;
|
|
||||||
endInsertRows();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IconList::reindex()
|
|
||||||
{
|
|
||||||
d->index.clear();
|
|
||||||
int i = 0;
|
|
||||||
for (auto &iter : d->icons)
|
|
||||||
{
|
|
||||||
d->index[iter.key] = i;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon IconList::getIcon(QString key)
|
|
||||||
{
|
|
||||||
int icon_index = getIconIndex(key);
|
|
||||||
|
|
||||||
if (icon_index != -1)
|
|
||||||
return d->icons[icon_index].icon;
|
|
||||||
|
|
||||||
// Fallback for icons that don't exist.
|
|
||||||
icon_index = getIconIndex("infinity");
|
|
||||||
|
|
||||||
if (icon_index != -1)
|
|
||||||
return d->icons[icon_index].icon;
|
|
||||||
return QIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
int IconList::getIconIndex(QString key)
|
|
||||||
{
|
|
||||||
if (key == "default")
|
|
||||||
key = "infinity";
|
|
||||||
|
|
||||||
auto iter = d->index.find(key);
|
|
||||||
if (iter != d->index.end())
|
|
||||||
return *iter;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "IconList.moc"
|
|
@ -22,11 +22,14 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <pathutils.h>
|
#include <pathutils.h>
|
||||||
|
|
||||||
#include "MultiMC.h"
|
#include "MultiMC.h"
|
||||||
#include "logic/lists/InstanceList.h"
|
#include "logic/lists/InstanceList.h"
|
||||||
#include "logic/lists/IconList.h"
|
#include "logic/icons/IconList.h"
|
||||||
|
#include "logic/lists/MinecraftVersionList.h"
|
||||||
#include "logic/BaseInstance.h"
|
#include "logic/BaseInstance.h"
|
||||||
#include "logic/InstanceFactory.h"
|
#include "logic/InstanceFactory.h"
|
||||||
#include "logger/QsLog.h"
|
#include "logger/QsLog.h"
|
||||||
@ -42,6 +45,9 @@ InstanceList::InstanceList(const QString &instDir, QObject *parent)
|
|||||||
{
|
{
|
||||||
QDir::current().mkpath(m_instDir);
|
QDir::current().mkpath(m_instDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this,
|
||||||
|
&InstanceList::loadList);
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceList::~InstanceList()
|
InstanceList::~InstanceList()
|
||||||
@ -276,6 +282,125 @@ void InstanceList::loadGroupList(QMap<QString, QString> &groupMap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FTBRecord
|
||||||
|
{
|
||||||
|
QString dir;
|
||||||
|
QString name;
|
||||||
|
QString logo;
|
||||||
|
QString mcVersion;
|
||||||
|
QString description;
|
||||||
|
};
|
||||||
|
|
||||||
|
void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
|
||||||
|
{
|
||||||
|
QList<FTBRecord> records;
|
||||||
|
QDir dir = QDir(MMC->settings()->get("FTBLauncherRoot").toString());
|
||||||
|
QDir dataDir = QDir(MMC->settings()->get("FTBRoot").toString());
|
||||||
|
if (!dir.exists())
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "The FTB launcher directory specified does not exist. Please check your "
|
||||||
|
"settings.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (!dataDir.exists())
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir.cd("ModPacks");
|
||||||
|
QFile f(dir.absoluteFilePath("modpacks.xml"));
|
||||||
|
if (!f.open(QFile::ReadOnly))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// read the FTB packs XML.
|
||||||
|
QXmlStreamReader reader(&f);
|
||||||
|
while (!reader.atEnd())
|
||||||
|
{
|
||||||
|
switch (reader.readNext())
|
||||||
|
{
|
||||||
|
case QXmlStreamReader::StartElement:
|
||||||
|
{
|
||||||
|
if (reader.name() == "modpack")
|
||||||
|
{
|
||||||
|
QXmlStreamAttributes attrs = reader.attributes();
|
||||||
|
FTBRecord record;
|
||||||
|
record.dir = attrs.value("dir").toString();
|
||||||
|
record.name = attrs.value("name").toString();
|
||||||
|
record.logo = attrs.value("logo").toString();
|
||||||
|
record.mcVersion = attrs.value("mcVersion").toString();
|
||||||
|
record.description = attrs.value("description").toString();
|
||||||
|
records.append(record);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QXmlStreamReader::EndElement:
|
||||||
|
break;
|
||||||
|
case QXmlStreamReader::Characters:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
// process the records we acquired.
|
||||||
|
for (auto record : records)
|
||||||
|
{
|
||||||
|
auto instanceDir = dataDir.absoluteFilePath(record.dir);
|
||||||
|
auto templateDir = dir.absoluteFilePath(record.dir);
|
||||||
|
if (!QFileInfo(instanceDir).exists())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString iconKey = record.logo;
|
||||||
|
iconKey.remove(QRegularExpression("\\..*"));
|
||||||
|
MMC->icons()->addIcon(iconKey, iconKey, PathCombine(templateDir, record.logo),
|
||||||
|
MMCIcon::Transient);
|
||||||
|
|
||||||
|
if (!QFileInfo(PathCombine(instanceDir, "instance.cfg")).exists())
|
||||||
|
{
|
||||||
|
BaseInstance *instPtr = NULL;
|
||||||
|
auto &factory = InstanceFactory::get();
|
||||||
|
auto version = MMC->minecraftlist()->findVersion(record.mcVersion);
|
||||||
|
if (!version)
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Can't load instance " << instanceDir
|
||||||
|
<< " because minecraft version " << record.mcVersion
|
||||||
|
<< " can't be resolved.";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto error = factory.createInstance(instPtr, version, instanceDir,
|
||||||
|
InstanceFactory::FTBInstance);
|
||||||
|
|
||||||
|
if (!instPtr || error != InstanceFactory::NoCreateError)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
instPtr->setGroupInitial("FTB");
|
||||||
|
instPtr->setName(record.name);
|
||||||
|
instPtr->setIconKey(iconKey);
|
||||||
|
instPtr->setIntendedVersionId(record.mcVersion);
|
||||||
|
instPtr->setNotes(record.description);
|
||||||
|
continueProcessInstance(instPtr, error, instanceDir, groupMap);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BaseInstance *instPtr = NULL;
|
||||||
|
auto error = InstanceFactory::get().loadInstance(instPtr, instanceDir);
|
||||||
|
if (!instPtr || error != InstanceFactory::NoCreateError)
|
||||||
|
continue;
|
||||||
|
instPtr->setGroupInitial("FTB");
|
||||||
|
instPtr->setName(record.name);
|
||||||
|
instPtr->setIconKey(iconKey);
|
||||||
|
if (instPtr->intendedVersionId() != record.mcVersion)
|
||||||
|
instPtr->setIntendedVersionId(record.mcVersion);
|
||||||
|
instPtr->setNotes(record.description);
|
||||||
|
continueProcessInstance(instPtr, error, instanceDir, groupMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
InstanceList::InstListError InstanceList::loadList()
|
InstanceList::InstListError InstanceList::loadList()
|
||||||
{
|
{
|
||||||
// load the instance groups
|
// load the instance groups
|
||||||
@ -285,57 +410,27 @@ InstanceList::InstListError InstanceList::loadList()
|
|||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
|
||||||
m_instances.clear();
|
m_instances.clear();
|
||||||
QDir dir(m_instDir);
|
|
||||||
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable,
|
|
||||||
QDirIterator::FollowSymlinks);
|
|
||||||
while (iter.hasNext())
|
|
||||||
{
|
{
|
||||||
QString subDir = iter.next();
|
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable,
|
||||||
if (!QFileInfo(PathCombine(subDir, "instance.cfg")).exists())
|
QDirIterator::FollowSymlinks);
|
||||||
continue;
|
while (iter.hasNext())
|
||||||
|
|
||||||
BaseInstance *instPtr = NULL;
|
|
||||||
auto &loader = InstanceFactory::get();
|
|
||||||
auto error = loader.loadInstance(instPtr, subDir);
|
|
||||||
|
|
||||||
if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance)
|
|
||||||
{
|
{
|
||||||
QString errorMsg = QString("Failed to load instance %1: ")
|
QString subDir = iter.next();
|
||||||
.arg(QFileInfo(subDir).baseName())
|
if (!QFileInfo(PathCombine(subDir, "instance.cfg")).exists())
|
||||||
.toUtf8();
|
continue;
|
||||||
|
|
||||||
switch (error)
|
BaseInstance *instPtr = NULL;
|
||||||
{
|
auto error = InstanceFactory::get().loadInstance(instPtr, subDir);
|
||||||
default:
|
continueProcessInstance(instPtr, error, subDir, groupMap);
|
||||||
errorMsg += QString("Unknown instance loader error %1").arg(error);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
QLOG_ERROR() << errorMsg.toUtf8();
|
|
||||||
}
|
|
||||||
else if (!instPtr)
|
|
||||||
{
|
|
||||||
QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.")
|
|
||||||
.arg(QFileInfo(subDir).baseName())
|
|
||||||
.toUtf8();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::shared_ptr<BaseInstance> inst(instPtr);
|
|
||||||
auto iter = groupMap.find(inst->id());
|
|
||||||
if (iter != groupMap.end())
|
|
||||||
{
|
|
||||||
inst->setGroupInitial((*iter));
|
|
||||||
}
|
|
||||||
QLOG_INFO() << "Loaded instance " << inst->name();
|
|
||||||
inst->setParent(this);
|
|
||||||
m_instances.append(inst);
|
|
||||||
connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this,
|
|
||||||
SLOT(propertiesChanged(BaseInstance *)));
|
|
||||||
connect(instPtr, SIGNAL(groupChanged()), this, SLOT(groupChanged()));
|
|
||||||
connect(instPtr, SIGNAL(nuked(BaseInstance *)), this,
|
|
||||||
SLOT(instanceNuked(BaseInstance *)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (MMC->settings()->get("TrackFTBInstances").toBool())
|
||||||
|
{
|
||||||
|
loadForgeInstances(groupMap);
|
||||||
|
}
|
||||||
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
emit dataIsInvalid();
|
emit dataIsInvalid();
|
||||||
return NoError;
|
return NoError;
|
||||||
@ -409,6 +504,47 @@ int InstanceList::getInstIndex(BaseInstance *inst) const
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int error,
|
||||||
|
const QDir &dir, QMap<QString, QString> &groupMap)
|
||||||
|
{
|
||||||
|
if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance)
|
||||||
|
{
|
||||||
|
QString errorMsg = QString("Failed to load instance %1: ")
|
||||||
|
.arg(QFileInfo(dir.absolutePath()).baseName())
|
||||||
|
.toUtf8();
|
||||||
|
|
||||||
|
switch (error)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
errorMsg += QString("Unknown instance loader error %1").arg(error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QLOG_ERROR() << errorMsg.toUtf8();
|
||||||
|
}
|
||||||
|
else if (!instPtr)
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.")
|
||||||
|
.arg(QFileInfo(dir.absolutePath()).baseName())
|
||||||
|
.toUtf8();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto iter = groupMap.find(instPtr->id());
|
||||||
|
if (iter != groupMap.end())
|
||||||
|
{
|
||||||
|
instPtr->setGroupInitial((*iter));
|
||||||
|
}
|
||||||
|
QLOG_INFO() << "Loaded instance " << instPtr->name();
|
||||||
|
instPtr->setParent(this);
|
||||||
|
m_instances.append(std::shared_ptr<BaseInstance>(instPtr));
|
||||||
|
connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this,
|
||||||
|
SLOT(propertiesChanged(BaseInstance *)));
|
||||||
|
connect(instPtr, SIGNAL(groupChanged()), this, SLOT(groupChanged()));
|
||||||
|
connect(instPtr, SIGNAL(nuked(BaseInstance *)), this,
|
||||||
|
SLOT(instanceNuked(BaseInstance *)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceList::instanceNuked(BaseInstance *inst)
|
void InstanceList::instanceNuked(BaseInstance *inst)
|
||||||
{
|
{
|
||||||
int i = getInstIndex(inst);
|
int i = getInstIndex(inst);
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
class BaseInstance;
|
class BaseInstance;
|
||||||
|
|
||||||
|
class QDir;
|
||||||
|
|
||||||
class InstanceList : public QAbstractListModel
|
class InstanceList : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -65,11 +67,6 @@ public:
|
|||||||
return m_instDir;
|
return m_instDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Loads the instance list. Triggers notifications.
|
|
||||||
*/
|
|
||||||
InstListError loadList();
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Get the instance at index
|
* \brief Get the instance at index
|
||||||
*/
|
*/
|
||||||
@ -108,6 +105,12 @@ public
|
|||||||
slots:
|
slots:
|
||||||
void on_InstFolderChanged(const Setting &setting, QVariant value);
|
void on_InstFolderChanged(const Setting &setting, QVariant value);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Loads the instance list. Triggers notifications.
|
||||||
|
*/
|
||||||
|
InstListError loadList();
|
||||||
|
void loadForgeInstances(QMap<QString, QString> groupMap);
|
||||||
|
|
||||||
private
|
private
|
||||||
slots:
|
slots:
|
||||||
void propertiesChanged(BaseInstance *inst);
|
void propertiesChanged(BaseInstance *inst);
|
||||||
@ -117,6 +120,9 @@ slots:
|
|||||||
private:
|
private:
|
||||||
int getInstIndex(BaseInstance *inst) const;
|
int getInstIndex(BaseInstance *inst) const;
|
||||||
|
|
||||||
|
void continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir,
|
||||||
|
QMap<QString, QString> &groupMap);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString m_instDir;
|
QString m_instDir;
|
||||||
QList<InstancePtr> m_instances;
|
QList<InstancePtr> m_instances;
|
||||||
|
@ -172,14 +172,14 @@ JavaListLoadTask::~JavaListLoadTask()
|
|||||||
|
|
||||||
void JavaListLoadTask::executeTask()
|
void JavaListLoadTask::executeTask()
|
||||||
{
|
{
|
||||||
setStatus("Detecting Java installations...");
|
setStatus(tr("Detecting Java installations..."));
|
||||||
|
|
||||||
JavaUtils ju;
|
JavaUtils ju;
|
||||||
QList<QString> candidate_paths = ju.FindJavaPaths();
|
QList<QString> candidate_paths = ju.FindJavaPaths();
|
||||||
|
|
||||||
auto job = new JavaCheckerJob("Java detection");
|
m_job = std::shared_ptr<JavaCheckerJob>(new JavaCheckerJob("Java detection"));
|
||||||
connect(job, SIGNAL(finished(QList<JavaCheckResult>)), this, SLOT(javaCheckerFinished(QList<JavaCheckResult>)));
|
connect(m_job.get(), SIGNAL(finished(QList<JavaCheckResult>)), this, SLOT(javaCheckerFinished(QList<JavaCheckResult>)));
|
||||||
connect(job, SIGNAL(progress(int, int)), this, SLOT(checkerProgress(int, int)));
|
connect(m_job.get(), SIGNAL(progress(int, int)), this, SLOT(checkerProgress(int, int)));
|
||||||
|
|
||||||
QLOG_DEBUG() << "Probing the following Java paths: ";
|
QLOG_DEBUG() << "Probing the following Java paths: ";
|
||||||
for(QString candidate : candidate_paths)
|
for(QString candidate : candidate_paths)
|
||||||
@ -188,10 +188,10 @@ void JavaListLoadTask::executeTask()
|
|||||||
|
|
||||||
auto candidate_checker = new JavaChecker();
|
auto candidate_checker = new JavaChecker();
|
||||||
candidate_checker->path = candidate;
|
candidate_checker->path = candidate;
|
||||||
job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
|
m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
|
||||||
}
|
}
|
||||||
|
|
||||||
job->start();
|
m_job->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaListLoadTask::checkerProgress(int current, int total)
|
void JavaListLoadTask::checkerProgress(int current, int total)
|
||||||
@ -203,6 +203,7 @@ void JavaListLoadTask::checkerProgress(int current, int total)
|
|||||||
void JavaListLoadTask::javaCheckerFinished(QList<JavaCheckResult> results)
|
void JavaListLoadTask::javaCheckerFinished(QList<JavaCheckResult> results)
|
||||||
{
|
{
|
||||||
QList<JavaVersionPtr> candidates;
|
QList<JavaVersionPtr> candidates;
|
||||||
|
m_job.reset();
|
||||||
|
|
||||||
QLOG_DEBUG() << "Found the following valid Java installations:";
|
QLOG_DEBUG() << "Found the following valid Java installations:";
|
||||||
for(JavaCheckResult result : results)
|
for(JavaCheckResult result : results)
|
||||||
|
@ -90,6 +90,7 @@ public slots:
|
|||||||
void checkerProgress(int current, int total);
|
void checkerProgress(int current, int total);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
std::shared_ptr<JavaCheckerJob> m_job;
|
||||||
JavaVersionList *m_list;
|
JavaVersionList *m_list;
|
||||||
JavaVersion *m_currentRecommended;
|
JavaVersion *m_currentRecommended;
|
||||||
};
|
};
|
||||||
|
@ -139,7 +139,7 @@ MCVListLoadTask::~MCVListLoadTask()
|
|||||||
|
|
||||||
void MCVListLoadTask::executeTask()
|
void MCVListLoadTask::executeTask()
|
||||||
{
|
{
|
||||||
setStatus("Loading instance version list...");
|
setStatus(tr("Loading instance version list..."));
|
||||||
auto worker = MMC->qnam();
|
auto worker = MMC->qnam();
|
||||||
vlistReply = worker->get(QNetworkRequest(QUrl("http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + "versions.json")));
|
vlistReply = worker->get(QNetworkRequest(QUrl("http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + "versions.json")));
|
||||||
connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded()));
|
connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded()));
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QDir>
|
||||||
#include "logger/QsLog.h"
|
#include "logger/QsLog.h"
|
||||||
|
|
||||||
ForgeXzDownload::ForgeXzDownload(QString relative_path, MetaEntryPtr entry) : NetAction()
|
ForgeXzDownload::ForgeXzDownload(QString relative_path, MetaEntryPtr entry) : NetAction()
|
||||||
@ -310,16 +311,51 @@ void ForgeXzDownload::decompressAndInstall()
|
|||||||
m_pack200_xz_file.remove();
|
m_pack200_xz_file.remove();
|
||||||
|
|
||||||
// revert pack200
|
// revert pack200
|
||||||
pack200_file.close();
|
pack200_file.seek(0);
|
||||||
QString pack_name = pack200_file.fileName();
|
int handle_in = pack200_file.handle();
|
||||||
|
// FIXME: dispose of file handles, pointers and the like. Ideally wrap in objects.
|
||||||
|
if(handle_in == -1)
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Error reopening " << pack200_file.fileName();
|
||||||
|
failAndTryNextMirror();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FILE * file_in = fdopen(handle_in,"r");
|
||||||
|
if(!file_in)
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Error reopening " << pack200_file.fileName();
|
||||||
|
failAndTryNextMirror();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QFile qfile_out(m_target_path);
|
||||||
|
if(!qfile_out.open(QIODevice::WriteOnly))
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Error opening " << qfile_out.fileName();
|
||||||
|
failAndTryNextMirror();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int handle_out = qfile_out.handle();
|
||||||
|
if(handle_out == -1)
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Error opening " << qfile_out.fileName();
|
||||||
|
failAndTryNextMirror();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FILE * file_out = fdopen(handle_out,"w");
|
||||||
|
if(!file_out)
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Error opening " << qfile_out.fileName();
|
||||||
|
failAndTryNextMirror();
|
||||||
|
return;
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
unpack_200(pack_name.toStdString(), m_target_path.toStdString());
|
unpack_200(file_in, file_out);
|
||||||
}
|
}
|
||||||
catch (std::runtime_error &err)
|
catch (std::runtime_error &err)
|
||||||
{
|
{
|
||||||
m_status = Job_Failed;
|
m_status = Job_Failed;
|
||||||
QLOG_ERROR() << "Error unpacking " << pack_name.toUtf8() << " : " << err.what();
|
QLOG_ERROR() << "Error unpacking " << pack200_file.fileName() << " : " << err.what();
|
||||||
QFile f(m_target_path);
|
QFile f(m_target_path);
|
||||||
if (f.exists())
|
if (f.exists())
|
||||||
f.remove();
|
f.remove();
|
||||||
|
@ -23,7 +23,6 @@ MD5EtagDownload::MD5EtagDownload(QUrl url, QString target_path) : NetAction()
|
|||||||
{
|
{
|
||||||
m_url = url;
|
m_url = url;
|
||||||
m_target_path = target_path;
|
m_target_path = target_path;
|
||||||
m_check_md5 = false;
|
|
||||||
m_status = Job_NotStarted;
|
m_status = Job_NotStarted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,22 +33,26 @@ void MD5EtagDownload::start()
|
|||||||
// if there already is a file and md5 checking is in effect and it can be opened
|
// if there already is a file and md5 checking is in effect and it can be opened
|
||||||
if (m_output_file.exists() && m_output_file.open(QIODevice::ReadOnly))
|
if (m_output_file.exists() && m_output_file.open(QIODevice::ReadOnly))
|
||||||
{
|
{
|
||||||
// check the md5 against the expected one
|
// get the md5 of the local file.
|
||||||
QString hash =
|
m_local_md5 =
|
||||||
QCryptographicHash::hash(m_output_file.readAll(), QCryptographicHash::Md5)
|
QCryptographicHash::hash(m_output_file.readAll(), QCryptographicHash::Md5)
|
||||||
.toHex()
|
.toHex()
|
||||||
.constData();
|
.constData();
|
||||||
m_output_file.close();
|
m_output_file.close();
|
||||||
// skip this file if they match
|
// if we are expecting some md5sum, compare it with the local one
|
||||||
if (m_check_md5 && hash == m_expected_md5)
|
if (!m_expected_md5.isEmpty())
|
||||||
{
|
{
|
||||||
QLOG_INFO() << "Skipping " << m_url.toString() << ": md5 match.";
|
// skip if they match
|
||||||
emit succeeded(m_index_within_job);
|
if(m_local_md5 == m_expected_md5)
|
||||||
return;
|
{
|
||||||
|
QLOG_INFO() << "Skipping " << m_url.toString() << ": md5 match.";
|
||||||
|
emit succeeded(m_index_within_job);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_expected_md5 = hash;
|
// no expected md5. we use the local md5sum as an ETag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ensureFilePathExists(filename))
|
if (!ensureFilePathExists(filename))
|
||||||
@ -58,9 +61,18 @@ void MD5EtagDownload::start()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QLOG_INFO() << "Downloading " << m_url.toString() << " expecting " << m_expected_md5;
|
|
||||||
QNetworkRequest request(m_url);
|
QNetworkRequest request(m_url);
|
||||||
request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1());
|
|
||||||
|
QLOG_INFO() << "Downloading " << m_url.toString() << " got " << m_local_md5;
|
||||||
|
|
||||||
|
if(!m_local_md5.isEmpty())
|
||||||
|
{
|
||||||
|
QLOG_INFO() << "Got " << m_local_md5;
|
||||||
|
request.setRawHeader(QString("If-None-Match").toLatin1(), m_local_md5.toLatin1());
|
||||||
|
}
|
||||||
|
if(!m_expected_md5.isEmpty())
|
||||||
|
QLOG_INFO() << "Expecting " << m_expected_md5;
|
||||||
|
|
||||||
request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
|
request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
|
||||||
|
|
||||||
// Go ahead and try to open the file.
|
// Go ahead and try to open the file.
|
||||||
@ -107,7 +119,10 @@ void MD5EtagDownload::downloadFinished()
|
|||||||
m_status = Job_Finished;
|
m_status = Job_Finished;
|
||||||
m_output_file.close();
|
m_output_file.close();
|
||||||
|
|
||||||
|
// FIXME: compare with the real written data md5sum
|
||||||
|
// this is just an ETag
|
||||||
QLOG_INFO() << "Finished " << m_url.toString() << " got " << m_reply->rawHeader("ETag").constData();
|
QLOG_INFO() << "Finished " << m_url.toString() << " got " << m_reply->rawHeader("ETag").constData();
|
||||||
|
|
||||||
m_reply.reset();
|
m_reply.reset();
|
||||||
emit succeeded(m_index_within_job);
|
emit succeeded(m_index_within_job);
|
||||||
return;
|
return;
|
||||||
@ -116,6 +131,7 @@ void MD5EtagDownload::downloadFinished()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_output_file.close();
|
m_output_file.close();
|
||||||
|
m_output_file.remove();
|
||||||
m_reply.reset();
|
m_reply.reset();
|
||||||
emit failed(m_index_within_job);
|
emit failed(m_index_within_job);
|
||||||
return;
|
return;
|
||||||
|
@ -23,12 +23,10 @@ class MD5EtagDownload : public NetAction
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
/// if true, check the md5sum against a provided md5sum
|
/// the expected md5 checksum. Only set from outside
|
||||||
/// also, if a file exists, perform an md5sum first and don't download only if they don't
|
|
||||||
/// match
|
|
||||||
bool m_check_md5;
|
|
||||||
/// the expected md5 checksum
|
|
||||||
QString m_expected_md5;
|
QString m_expected_md5;
|
||||||
|
/// the md5 checksum of a file that already exists.
|
||||||
|
QString m_local_md5;
|
||||||
/// if saving to file, use the one specified in this string
|
/// if saving to file, use the one specified in this string
|
||||||
QString m_target_path;
|
QString m_target_path;
|
||||||
/// this is the output file, if any
|
/// this is the output file, if any
|
||||||
|
@ -29,4 +29,6 @@ const QString RESOURCE_BASE("resources.download.minecraft.net/");
|
|||||||
const QString LIBRARY_BASE("libraries.minecraft.net/");
|
const QString LIBRARY_BASE("libraries.minecraft.net/");
|
||||||
const QString SKINS_BASE("skins.minecraft.net/MinecraftSkins/");
|
const QString SKINS_BASE("skins.minecraft.net/MinecraftSkins/");
|
||||||
const QString AUTH_BASE("authserver.mojang.com/");
|
const QString AUTH_BASE("authserver.mojang.com/");
|
||||||
|
const QString FORGE_LEGACY_URL("http://files.minecraftforge.net/minecraftforge/json");
|
||||||
|
const QString FORGE_GRADLE_URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/json");
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user