Merge branch 'release-0.2'
14
.gitignore
vendored
@ -1,17 +1,23 @@
|
||||
Thumbs.db
|
||||
.kdev4
|
||||
MultiMC5.kdev4
|
||||
MultiMC.pro.user
|
||||
CMakeLists.txt.user
|
||||
.user
|
||||
.directory
|
||||
build
|
||||
resources/CMakeFiles
|
||||
resources/MultiMCLauncher.jar
|
||||
*~
|
||||
*.swp
|
||||
html/
|
||||
|
||||
# Project Files
|
||||
MultiMC5.kdev4
|
||||
MultiMC.pro.user
|
||||
CMakeLists.txt.user
|
||||
CMakeLists.txt.user.*
|
||||
|
||||
# Build dirs
|
||||
build
|
||||
/build-*
|
||||
|
||||
# Ctags File
|
||||
tags
|
||||
|
||||
|
@ -25,6 +25,8 @@ IF(UNIX)
|
||||
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||
ENDIF()
|
||||
|
||||
set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
|
||||
|
||||
######## Set compiler flags ########
|
||||
IF(APPLE)
|
||||
message(STATUS "Using APPLE CMAKE_CXX_FLAGS")
|
||||
@ -90,8 +92,8 @@ SET(MultiMC_NEWS_RSS_URL "http://multimc.org/rss.xml" CACHE STRING "URL to fetch
|
||||
|
||||
######## Set version numbers ########
|
||||
SET(MultiMC_VERSION_MAJOR 0)
|
||||
SET(MultiMC_VERSION_MINOR 1)
|
||||
SET(MultiMC_VERSION_HOTFIX 1)
|
||||
SET(MultiMC_VERSION_MINOR 2)
|
||||
SET(MultiMC_VERSION_HOTFIX 0)
|
||||
|
||||
# Build number
|
||||
SET(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
||||
@ -280,7 +282,6 @@ gui/dialogs/SettingsDialog.h
|
||||
gui/dialogs/SettingsDialog.cpp
|
||||
gui/dialogs/CopyInstanceDialog.h
|
||||
gui/dialogs/CopyInstanceDialog.cpp
|
||||
gui/dialogs/dialogs/
|
||||
gui/dialogs/NewInstanceDialog.cpp
|
||||
gui/dialogs/ProgressDialog.h
|
||||
gui/dialogs/ProgressDialog.cpp
|
||||
@ -391,6 +392,10 @@ logic/news/NewsChecker.cpp
|
||||
logic/news/NewsEntry.h
|
||||
logic/news/NewsEntry.cpp
|
||||
|
||||
# Status system
|
||||
logic/status/StatusChecker.h
|
||||
logic/status/StatusChecker.cpp
|
||||
|
||||
# legacy instances
|
||||
logic/LegacyInstance.h
|
||||
logic/LegacyInstance.cpp
|
||||
@ -514,10 +519,16 @@ gui/widgets/MCModInfoFrame.ui
|
||||
|
||||
set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${MULTIMC_SOURCES} ${MULTIMC_UIS})
|
||||
|
||||
SET(MULTIMC_QRCS
|
||||
resources/backgrounds/backgrounds.qrc
|
||||
resources/multimc/multimc.qrc
|
||||
resources/instances/instances.qrc
|
||||
)
|
||||
|
||||
|
||||
######## Windows resource files ########
|
||||
IF(WIN32)
|
||||
SET(MULTIMC_RCS multimc.rc)
|
||||
SET(MULTIMC_RCS resources/multimc.rc)
|
||||
ENDIF()
|
||||
|
||||
####### X11 Stuff #######
|
||||
@ -532,13 +543,6 @@ ENDIF()
|
||||
|
||||
################################ COMPILE ################################
|
||||
|
||||
# ICNS file for OS X
|
||||
IF(APPLE)
|
||||
SET(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
|
||||
SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/MultiMC.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
SET(MULTIMC_SOURCES ${MULTIMC_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/MultiMC.icns)
|
||||
ENDIF(APPLE)
|
||||
|
||||
# Link additional libraries
|
||||
IF(WIN32)
|
||||
SET(MultiMC_LINK_ADDITIONAL_LIBS ${MultiMC_LINK_ADDITIONAL_LIBS}
|
||||
@ -564,12 +568,10 @@ ENDIF(MultiMC_CODE_COVERAGE)
|
||||
|
||||
# Qt 5 stuff
|
||||
QT5_WRAP_UI(MULTIMC_UI ${MULTIMC_UIS})
|
||||
CONFIGURE_FILE(generated.qrc.in generated.qrc)
|
||||
QT5_ADD_RESOURCES(GENERATED_QRC ${CMAKE_CURRENT_BINARY_DIR}/generated.qrc)
|
||||
QT5_ADD_RESOURCES(GRAPHICS_QRC graphics.qrc)
|
||||
QT5_ADD_RESOURCES(MULTIMC_RESOURCES ${MULTIMC_QRCS})
|
||||
|
||||
# Add common library
|
||||
ADD_LIBRARY(MultiMC_common STATIC ${MULTIMC_SOURCES} ${MULTIMC_UI} ${GENERATED_QRC} ${GRAPHICS_QRC})
|
||||
ADD_LIBRARY(MultiMC_common STATIC ${MULTIMC_SOURCES} ${MULTIMC_UI} ${MULTIMC_RESOURCES})
|
||||
|
||||
# Add executable
|
||||
ADD_EXECUTABLE(MultiMC MACOSX_BUNDLE WIN32 main.cpp ${MULTIMC_RCS})
|
||||
@ -579,7 +581,6 @@ TARGET_LINK_LIBRARIES(MultiMC MultiMC_common)
|
||||
TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings libGroupView ${MultiMC_LINK_ADDITIONAL_LIBS})
|
||||
QT5_USE_MODULES(MultiMC Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
|
||||
QT5_USE_MODULES(MultiMC_common Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
|
||||
ADD_DEPENDENCIES(MultiMC_common MultiMCLauncher JavaCheck)
|
||||
|
||||
################################ INSTALLATION AND PACKAGING ################################
|
||||
|
||||
@ -667,6 +668,11 @@ FILE(WRITE \"\${CMAKE_INSTALL_PREFIX}/${QTCONF_DEST_DIR}/qt.conf\" \"\")
|
||||
COMPONENT Runtime
|
||||
)
|
||||
|
||||
# ICNS file for OS X
|
||||
IF(APPLE)
|
||||
INSTALL(FILES resources/MultiMC.icns DESTINATION MultiMC.app/Contents/Resources)
|
||||
ENDIF()
|
||||
|
||||
CONFIGURE_FILE(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
|
||||
|
37
MultiMC.cpp
@ -20,8 +20,11 @@
|
||||
|
||||
#include "logic/news/NewsChecker.h"
|
||||
|
||||
#include "logic/status/StatusChecker.h"
|
||||
|
||||
#include "logic/InstanceLauncher.h"
|
||||
#include "logic/net/HttpMetaCache.h"
|
||||
#include "logic/net/URLConstants.h"
|
||||
|
||||
#include "logic/JavaUtils.h"
|
||||
|
||||
@ -44,8 +47,6 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
|
||||
setOrganizationName("MultiMC");
|
||||
setApplicationName("MultiMC5");
|
||||
|
||||
initTranslations();
|
||||
|
||||
setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
// Don't quit on hiding the last window
|
||||
this->setQuitOnLastWindowClosed(false);
|
||||
@ -172,6 +173,9 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
|
||||
// load settings
|
||||
initGlobalSettings();
|
||||
|
||||
// load translations
|
||||
initTranslations();
|
||||
|
||||
// initialize the updater
|
||||
m_updateChecker.reset(new UpdateChecker());
|
||||
|
||||
@ -181,6 +185,9 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
|
||||
// initialize the news checker
|
||||
m_newsChecker.reset(new NewsChecker(NEWS_RSS_URL));
|
||||
|
||||
// initialize the status checker
|
||||
m_statusChecker.reset(new StatusChecker());
|
||||
|
||||
// and instances
|
||||
auto InstDirSetting = m_settings->getSetting("InstanceDir");
|
||||
m_instances.reset(new InstanceList(InstDirSetting->get().toString(), this));
|
||||
@ -234,18 +241,20 @@ MultiMC::~MultiMC()
|
||||
|
||||
void MultiMC::initTranslations()
|
||||
{
|
||||
QLocale locale(m_settings->get("Language").toString());
|
||||
QLocale::setDefault(locale);
|
||||
QLOG_INFO() << "Your language is" << locale.bcp47Name();
|
||||
m_qt_translator.reset(new QTranslator());
|
||||
if (m_qt_translator->load("qt_" + QLocale::system().name(),
|
||||
if (m_qt_translator->load("qt_" + locale.bcp47Name(),
|
||||
QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
|
||||
{
|
||||
std::cout << "Loading Qt Language File for "
|
||||
<< QLocale::system().name().toLocal8Bit().constData() << "...";
|
||||
QLOG_DEBUG() << "Loading Qt Language File for"
|
||||
<< locale.bcp47Name().toLocal8Bit().constData() << "...";
|
||||
if (!installTranslator(m_qt_translator.get()))
|
||||
{
|
||||
std::cout << " failed.";
|
||||
QLOG_ERROR() << "Loading Qt Language File failed.";
|
||||
m_qt_translator.reset();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -253,17 +262,15 @@ void MultiMC::initTranslations()
|
||||
}
|
||||
|
||||
m_mmc_translator.reset(new QTranslator());
|
||||
if (m_mmc_translator->load("mmc_" + QLocale::system().name(),
|
||||
QDir("translations").absolutePath()))
|
||||
if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), MMC->root() + "/translations"))
|
||||
{
|
||||
std::cout << "Loading MMC Language File for "
|
||||
<< QLocale::system().name().toLocal8Bit().constData() << "...";
|
||||
QLOG_DEBUG() << "Loading MMC Language File for"
|
||||
<< locale.bcp47Name().toLocal8Bit().constData() << "...";
|
||||
if (!installTranslator(m_mmc_translator.get()))
|
||||
{
|
||||
std::cout << " failed.";
|
||||
QLOG_ERROR() << "Loading MMC Language File failed.";
|
||||
m_mmc_translator.reset();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -366,9 +373,13 @@ void MultiMC::initGlobalSettings()
|
||||
// Editors
|
||||
m_settings->registerSetting("JsonEditor", QString());
|
||||
|
||||
// Language
|
||||
m_settings->registerSetting("Language", QLocale(QLocale::system().language()).bcp47Name());
|
||||
|
||||
// Console
|
||||
m_settings->registerSetting("ShowConsole", true);
|
||||
m_settings->registerSetting("AutoCloseConsole", true);
|
||||
m_settings->registerSetting("LogPrePostOutput", true);
|
||||
|
||||
// Console Colors
|
||||
// m_settings->registerSetting("SysMessageColor", QColor(Qt::blue));
|
||||
|
@ -21,6 +21,7 @@ class JavaVersionList;
|
||||
class UpdateChecker;
|
||||
class NotificationChecker;
|
||||
class NewsChecker;
|
||||
class StatusChecker;
|
||||
|
||||
#if defined(MMC)
|
||||
#undef MMC
|
||||
@ -113,6 +114,11 @@ public:
|
||||
return m_newsChecker;
|
||||
}
|
||||
|
||||
std::shared_ptr<StatusChecker> statusChecker()
|
||||
{
|
||||
return m_statusChecker;
|
||||
}
|
||||
|
||||
std::shared_ptr<LWJGLVersionList> lwjgllist();
|
||||
|
||||
std::shared_ptr<ForgeVersionList> forgelist();
|
||||
@ -183,6 +189,7 @@ private:
|
||||
std::shared_ptr<UpdateChecker> m_updateChecker;
|
||||
std::shared_ptr<NotificationChecker> m_notificationChecker;
|
||||
std::shared_ptr<NewsChecker> m_newsChecker;
|
||||
std::shared_ptr<StatusChecker> m_statusChecker;
|
||||
std::shared_ptr<MojangAccountList> m_accounts;
|
||||
std::shared_ptr<IconList> m_icons;
|
||||
std::shared_ptr<QNetworkAccessManager> m_qnam;
|
||||
|
@ -16,4 +16,16 @@
|
||||
- Added additional information to the about dialog.
|
||||
0.1.1:
|
||||
- Hotfix - Changed the issue tracker URL to [GitHub issues](https://github.com/MultiMC/MultiMC5/issues).
|
||||
|
||||
0.2:
|
||||
- Java memory settings have MB added to the number to make the units obvious.
|
||||
- Complete rework of the launcher part. No more sensitive information in the process arguments.
|
||||
- Cached downloads now do not destroy files on failure.
|
||||
- Mojang service status is now on the MultiMC status bar.
|
||||
- Java checker is no longer needed/used on instance launch.
|
||||
- Support for private FTB packs.
|
||||
- Fixed instance ID issues related to copying FTB packs without changing the instance name.
|
||||
- Forge versions are better sorted (build numbers above 999 were sorted wrong).
|
||||
- Fixed crash related to the MultiMC update channel picker in offline mode.
|
||||
- Started using icon themes for the application icons, fixing many OSX graphical glitches.
|
||||
- Icon sources have been located, along with icon licenses.
|
||||
- Update to the German translation.
|
||||
|
6
depends/javacheck/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
.idea
|
||||
*.iml
|
||||
out
|
||||
.classpath
|
||||
.idea
|
||||
.project
|
6
depends/launcher/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
.idea
|
||||
*.iml
|
||||
out
|
||||
.classpath
|
||||
.idea
|
||||
.project
|
@ -3,20 +3,33 @@ project(launcher Java)
|
||||
find_package(Java 1.6 REQUIRED COMPONENTS Development)
|
||||
|
||||
include(UseJava)
|
||||
set(CMAKE_JAVA_JAR_ENTRY_POINT MultiMCLauncher)
|
||||
set(CMAKE_JAVA_JAR_ENTRY_POINT org.multimc.EntryPoint)
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS -target 1.6 -source 1.6 -Xlint:deprecation -Xlint:unchecked)
|
||||
|
||||
set(SRC
|
||||
MultiMCLauncher.java
|
||||
# OSX things
|
||||
org/simplericity/macify/eawt/Application.java
|
||||
org/simplericity/macify/eawt/ApplicationAdapter.java
|
||||
org/simplericity/macify/eawt/ApplicationEvent.java
|
||||
org/simplericity/macify/eawt/ApplicationListener.java
|
||||
org/simplericity/macify/eawt/DefaultApplication.java
|
||||
|
||||
# legacy applet wrapper thing.
|
||||
# The launcher has to be there for silly FML/Forge relauncher.
|
||||
net/minecraft/Launcher.java
|
||||
MCFrame.java
|
||||
org/multimc/legacy/LegacyLauncher.java
|
||||
org/multimc/legacy/LegacyFrame.java
|
||||
|
||||
# onesix launcher
|
||||
org/multimc/onesix/OneSixLauncher.java
|
||||
|
||||
# generic launcher
|
||||
org/multimc/EntryPoint.java
|
||||
org/multimc/Launcher.java
|
||||
org/multimc/ParseException.java
|
||||
org/multimc/Utils.java
|
||||
org/multimc/IconLoader.java
|
||||
)
|
||||
add_jar(NewLaunch ${SRC})
|
||||
|
||||
add_jar(MultiMCLauncher ${SRC})
|
||||
|
||||
INSTALL_JAR(MultiMCLauncher "${BINARY_DEST_DIR}/jars")
|
||||
INSTALL_JAR(NewLaunch "${BINARY_DEST_DIR}/jars")
|
||||
|
@ -1,331 +0,0 @@
|
||||
//
|
||||
// Copyright 2012 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.
|
||||
//
|
||||
|
||||
import java.applet.Applet;
|
||||
import java.awt.Dimension;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import org.simplericity.macify.eawt.Application;
|
||||
import org.simplericity.macify.eawt.DefaultApplication;
|
||||
|
||||
public class MultiMCLauncher
|
||||
{
|
||||
/**
|
||||
* @param args
|
||||
* The arguments you want to launch Minecraft with. New path,
|
||||
* Username, Session ID.
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
if (args.length < 3)
|
||||
{
|
||||
System.out.println("Not enough arguments.");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
// Set the OSX application icon first, if we are on OSX.
|
||||
Application application = new DefaultApplication();
|
||||
if(application.isMac())
|
||||
{
|
||||
try
|
||||
{
|
||||
BufferedImage image = ImageIO.read(new File("icon.png"));
|
||||
application.setApplicationIconImage(image);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
String userName = args[0];
|
||||
String sessionId = args[1];
|
||||
String windowtitle = args[2];
|
||||
String windowParams = args[3];
|
||||
String lwjgl = args[4];
|
||||
String cwd = System.getProperty("user.dir");
|
||||
|
||||
Dimension winSize = new Dimension(854, 480);
|
||||
boolean maximize = false;
|
||||
boolean compatMode = false;
|
||||
|
||||
|
||||
String[] dimStrings = windowParams.split("x");
|
||||
|
||||
if (windowParams.equalsIgnoreCase("compatmode"))
|
||||
{
|
||||
compatMode = true;
|
||||
}
|
||||
else if (windowParams.equalsIgnoreCase("max"))
|
||||
{
|
||||
maximize = true;
|
||||
}
|
||||
else if (dimStrings.length == 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
winSize = new Dimension(Integer.parseInt(dimStrings[0]),
|
||||
Integer.parseInt(dimStrings[1]));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
System.out.println("Invalid Window size argument, " +
|
||||
"using default.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Invalid Window size argument, " +
|
||||
"using default.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File binDir = new File(cwd, "bin");
|
||||
File lwjglDir;
|
||||
if(lwjgl.equalsIgnoreCase("Mojang"))
|
||||
lwjglDir = binDir;
|
||||
else
|
||||
lwjglDir = new File(lwjgl);
|
||||
|
||||
System.out.println("Loading jars...");
|
||||
String[] lwjglJars = new String[] {
|
||||
"lwjgl.jar", "lwjgl_util.jar", "jinput.jar"
|
||||
};
|
||||
|
||||
URL[] urls = new URL[4];
|
||||
try
|
||||
{
|
||||
File f = new File(binDir, "minecraft.jar");
|
||||
urls[0] = f.toURI().toURL();
|
||||
System.out.println("Loading URL: " + urls[0].toString());
|
||||
|
||||
for (int i = 1; i < urls.length; i++)
|
||||
{
|
||||
File jar = new File(lwjglDir, lwjglJars[i-1]);
|
||||
urls[i] = jar.toURI().toURL();
|
||||
System.out.println("Loading URL: " + urls[i].toString());
|
||||
}
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
System.err.println("MalformedURLException, " + e.toString());
|
||||
System.exit(5);
|
||||
}
|
||||
|
||||
System.out.println("Loading natives...");
|
||||
String nativesDir = new File(lwjglDir, "natives").toString();
|
||||
|
||||
System.setProperty("org.lwjgl.librarypath", nativesDir);
|
||||
System.setProperty("net.java.games.input.librarypath", nativesDir);
|
||||
|
||||
URLClassLoader cl =
|
||||
new URLClassLoader(urls, MultiMCLauncher.class.getClassLoader());
|
||||
|
||||
// Get the Minecraft Class.
|
||||
Class<?> mc = null;
|
||||
try
|
||||
{
|
||||
mc = cl.loadClass("net.minecraft.client.Minecraft");
|
||||
|
||||
Field f = getMCPathField(mc);
|
||||
|
||||
if (f == null)
|
||||
{
|
||||
System.err.println("Could not find Minecraft path field. Launch failed.");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
f.setAccessible(true);
|
||||
f.set(null, new File(cwd));
|
||||
// And set it.
|
||||
System.out.println("Fixed Minecraft Path: Field was " + f.toString());
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
System.err.println("Can't find main class. Searching...");
|
||||
|
||||
// Look for any class that looks like the main class.
|
||||
File mcJar = new File(new File(cwd, "bin"), "minecraft.jar");
|
||||
ZipFile zip = null;
|
||||
try
|
||||
{
|
||||
zip = new ZipFile(mcJar);
|
||||
} catch (ZipException e1)
|
||||
{
|
||||
e1.printStackTrace();
|
||||
System.err.println("Search failed.");
|
||||
System.exit(-1);
|
||||
} catch (IOException e1)
|
||||
{
|
||||
e1.printStackTrace();
|
||||
System.err.println("Search failed.");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
Enumeration<? extends ZipEntry> entries = zip.entries();
|
||||
ArrayList<String> classes = new ArrayList<String>();
|
||||
|
||||
while (entries.hasMoreElements())
|
||||
{
|
||||
ZipEntry entry = entries.nextElement();
|
||||
if (entry.getName().endsWith(".class"))
|
||||
{
|
||||
String entryName = entry.getName().substring(0, entry.getName().lastIndexOf('.'));
|
||||
entryName = entryName.replace('/', '.');
|
||||
System.out.println("Found class: " + entryName);
|
||||
classes.add(entryName);
|
||||
}
|
||||
}
|
||||
|
||||
for (String clsName : classes)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class<?> cls = cl.loadClass(clsName);
|
||||
if (!Runnable.class.isAssignableFrom(cls))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Found class implementing runnable: " +
|
||||
cls.getName());
|
||||
}
|
||||
|
||||
if (getMCPathField(cls) == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Found class implementing runnable " +
|
||||
"with mcpath field: " + cls.getName());
|
||||
}
|
||||
|
||||
mc = cls;
|
||||
break;
|
||||
}
|
||||
catch (ClassNotFoundException e1)
|
||||
{
|
||||
// Ignore
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (mc == null)
|
||||
{
|
||||
System.err.println("Failed to find Minecraft main class.");
|
||||
System.exit(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Found main class: " + mc.getName());
|
||||
}
|
||||
}
|
||||
|
||||
System.setProperty("minecraft.applet.TargetDirectory", cwd);
|
||||
|
||||
String[] mcArgs = new String[2];
|
||||
mcArgs[0] = userName;
|
||||
mcArgs[1] = sessionId;
|
||||
|
||||
if (compatMode)
|
||||
{
|
||||
System.out.println("Launching in compatibility mode...");
|
||||
mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Launching with applet wrapper...");
|
||||
try
|
||||
{
|
||||
Class<?> MCAppletClass = cl.loadClass(
|
||||
"net.minecraft.client.MinecraftApplet");
|
||||
Applet mcappl = (Applet) MCAppletClass.newInstance();
|
||||
MCFrame mcWindow = new MCFrame(windowtitle);
|
||||
mcWindow.start(mcappl, userName, sessionId, winSize, maximize);
|
||||
} catch (InstantiationException e)
|
||||
{
|
||||
System.out.println("Applet wrapper failed! Falling back " +
|
||||
"to compatibility mode.");
|
||||
mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs);
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
} catch (IllegalArgumentException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(2);
|
||||
} catch (IllegalAccessException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(2);
|
||||
} catch (InvocationTargetException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(3);
|
||||
} catch (NoSuchMethodException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(3);
|
||||
} catch (SecurityException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(4);
|
||||
}
|
||||
}
|
||||
|
||||
public static Field getMCPathField(Class<?> mc)
|
||||
{
|
||||
Field[] fields = mc.getDeclaredFields();
|
||||
|
||||
for (int i = 0; i < fields.length; i++)
|
||||
{
|
||||
Field f = fields[i];
|
||||
if (f.getType() != File.class)
|
||||
{
|
||||
// Has to be File
|
||||
continue;
|
||||
}
|
||||
if (f.getModifiers() != (Modifier.PRIVATE + Modifier.STATIC))
|
||||
{
|
||||
// And Private Static.
|
||||
continue;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
//
|
||||
// Copyright 2012 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.
|
||||
//
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package net.minecraft;
|
||||
|
||||
|
135
depends/launcher/org/multimc/EntryPoint.java
Normal file
@ -0,0 +1,135 @@
|
||||
package org.multimc;/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
import org.multimc.legacy.LegacyLauncher;
|
||||
import org.multimc.onesix.OneSixLauncher;
|
||||
import org.simplericity.macify.eawt.Application;
|
||||
import org.simplericity.macify.eawt.DefaultApplication;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
|
||||
public class EntryPoint
|
||||
{
|
||||
private enum Action
|
||||
{
|
||||
Proceed,
|
||||
Launch
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
// Set the OSX application icon first, if we are on OSX.
|
||||
Application application = new DefaultApplication();
|
||||
if(application.isMac())
|
||||
{
|
||||
try
|
||||
{
|
||||
BufferedImage image = ImageIO.read(new File("icon.png"));
|
||||
application.setApplicationIconImage(image);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
EntryPoint listener = new EntryPoint();
|
||||
int retCode = listener.listen();
|
||||
if (retCode != 0)
|
||||
{
|
||||
System.out.println("Exiting with " + retCode);
|
||||
System.exit(retCode);
|
||||
}
|
||||
}
|
||||
|
||||
private Action parseLine(String inData) throws ParseException
|
||||
{
|
||||
String[] pair = inData.split(" ", 2);
|
||||
if(pair.length != 2)
|
||||
throw new ParseException();
|
||||
|
||||
String command = pair[0];
|
||||
String param = pair[1];
|
||||
|
||||
if(command.equals("launch"))
|
||||
{
|
||||
if(param.equals("legacy"))
|
||||
{
|
||||
m_launcher = new LegacyLauncher();
|
||||
Utils.log("Using legacy launcher.");
|
||||
Utils.log();
|
||||
return Action.Launch;
|
||||
}
|
||||
if(param.equals("onesix"))
|
||||
{
|
||||
m_launcher = new OneSixLauncher();
|
||||
Utils.log("Using onesix launcher.");
|
||||
Utils.log();
|
||||
return Action.Launch;
|
||||
}
|
||||
else
|
||||
throw new ParseException();
|
||||
}
|
||||
|
||||
m_params.add(command, param);
|
||||
//System.out.println(command + " : " + param);
|
||||
return Action.Proceed;
|
||||
}
|
||||
|
||||
public int listen()
|
||||
{
|
||||
BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));
|
||||
boolean isListening = true;
|
||||
// Main loop
|
||||
while (isListening)
|
||||
{
|
||||
String inData="";
|
||||
try
|
||||
{
|
||||
// Read from the pipe one line at a time
|
||||
inData = buffer.readLine();
|
||||
if (inData != null)
|
||||
{
|
||||
if(parseLine(inData) == Action.Launch)
|
||||
{
|
||||
isListening = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return 1;
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if(m_launcher != null)
|
||||
{
|
||||
return m_launcher.launch(m_params);
|
||||
}
|
||||
System.err.println("No valid launcher implementation specified.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
private ParamBucket m_params = new ParamBucket();
|
||||
private org.multimc.Launcher m_launcher;
|
||||
}
|
132
depends/launcher/org/multimc/IconLoader.java
Normal file
@ -0,0 +1,132 @@
|
||||
package org.multimc;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/*****************************************************************************
|
||||
* A convenience class for loading icons from images.
|
||||
*
|
||||
* Icons loaded from this class are formatted to fit within the required
|
||||
* dimension (16x16, 32x32, or 128x128). If the source image is larger than the
|
||||
* target dimension, it is shrunk down to the minimum size that will fit. If it
|
||||
* is smaller, then it is only scaled up if the new scale can be a per-pixel
|
||||
* linear scale (i.e., x2, x3, x4, etc). In both cases, the image's width/height
|
||||
* ratio is kept the same as the source image.
|
||||
*
|
||||
* @author Chris Molini
|
||||
*****************************************************************************/
|
||||
public class IconLoader
|
||||
{
|
||||
/*************************************************************************
|
||||
* Loads an icon in ByteBuffer form.
|
||||
*
|
||||
* @param filepath
|
||||
* The location of the Image to use as an icon.
|
||||
*
|
||||
* @return An array of ByteBuffers containing the pixel data for the icon in
|
||||
* various sizes (as recommended by the OS).
|
||||
*************************************************************************/
|
||||
public static ByteBuffer[] load(String filepath)
|
||||
{
|
||||
BufferedImage image;
|
||||
try {
|
||||
image = ImageIO.read ( new File( filepath ) );
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
return new ByteBuffer[0];
|
||||
}
|
||||
ByteBuffer[] buffers;
|
||||
buffers = new ByteBuffer[1];
|
||||
buffers[0] = loadInstance(image, 128);
|
||||
return buffers;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Copies the supplied image into a square icon at the indicated size.
|
||||
*
|
||||
* @param image
|
||||
* The image to place onto the icon.
|
||||
* @param dimension
|
||||
* The desired size of the icon.
|
||||
*
|
||||
* @return A ByteBuffer of pixel data at the indicated size.
|
||||
*************************************************************************/
|
||||
private static ByteBuffer loadInstance(BufferedImage image, int dimension)
|
||||
{
|
||||
BufferedImage scaledIcon = new BufferedImage(dimension, dimension,
|
||||
BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
Graphics2D g = scaledIcon.createGraphics();
|
||||
double ratio = getIconRatio(image, scaledIcon);
|
||||
double width = image.getWidth() * ratio;
|
||||
double height = image.getHeight() * ratio;
|
||||
g.drawImage(image, (int) ((scaledIcon.getWidth() - width) / 2),
|
||||
(int) ((scaledIcon.getHeight() - height) / 2), (int) (width),
|
||||
(int) (height), null);
|
||||
g.dispose();
|
||||
|
||||
return convertToByteBuffer(scaledIcon);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Gets the width/height ratio of the icon. This is meant to simplify
|
||||
* scaling the icon to a new dimension.
|
||||
*
|
||||
* @param src
|
||||
* The base image that will be placed onto the icon.
|
||||
* @param icon
|
||||
* The icon that will have the image placed on it.
|
||||
*
|
||||
* @return The amount to scale the source image to fit it onto the icon
|
||||
* appropriately.
|
||||
*************************************************************************/
|
||||
private static double getIconRatio(BufferedImage src, BufferedImage icon)
|
||||
{
|
||||
double ratio = 1;
|
||||
if (src.getWidth() > icon.getWidth())
|
||||
ratio = (double) (icon.getWidth()) / src.getWidth();
|
||||
else
|
||||
ratio = (int) (icon.getWidth() / src.getWidth());
|
||||
if (src.getHeight() > icon.getHeight())
|
||||
{
|
||||
double r2 = (double) (icon.getHeight()) / src.getHeight();
|
||||
if (r2 < ratio)
|
||||
ratio = r2;
|
||||
}
|
||||
else
|
||||
{
|
||||
double r2 = (int) (icon.getHeight() / src.getHeight());
|
||||
if (r2 < ratio)
|
||||
ratio = r2;
|
||||
}
|
||||
return ratio;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Converts a BufferedImage into a ByteBuffer of pixel data.
|
||||
*
|
||||
* @param image
|
||||
* The image to convert.
|
||||
*
|
||||
* @return A ByteBuffer that contains the pixel data of the supplied image.
|
||||
*************************************************************************/
|
||||
public static ByteBuffer convertToByteBuffer(BufferedImage image)
|
||||
{
|
||||
byte[] buffer = new byte[image.getWidth() * image.getHeight() * 4];
|
||||
int counter = 0;
|
||||
for (int i = 0; i < image.getHeight(); i++)
|
||||
for (int j = 0; j < image.getWidth(); j++)
|
||||
{
|
||||
int colorSpace = image.getRGB(j, i);
|
||||
buffer[counter + 0] = (byte) ((colorSpace << 8) >> 24);
|
||||
buffer[counter + 1] = (byte) ((colorSpace << 16) >> 24);
|
||||
buffer[counter + 2] = (byte) ((colorSpace << 24) >> 24);
|
||||
buffer[counter + 3] = (byte) (colorSpace >> 24);
|
||||
counter += 4;
|
||||
}
|
||||
return ByteBuffer.wrap(buffer);
|
||||
}
|
||||
}
|
22
depends/launcher/org/multimc/Launcher.java
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.multimc;
|
||||
|
||||
public interface Launcher
|
||||
{
|
||||
abstract int launch(ParamBucket params);
|
||||
}
|
21
depends/launcher/org/multimc/NotFoundException.java
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.multimc;
|
||||
|
||||
public class NotFoundException extends Exception
|
||||
{
|
||||
}
|
86
depends/launcher/org/multimc/ParamBucket.java
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.multimc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ParamBucket
|
||||
{
|
||||
public void add(String key, String value)
|
||||
{
|
||||
List<String> coll = null;
|
||||
if(!m_params.containsKey(key))
|
||||
{
|
||||
coll = new ArrayList<String>();
|
||||
m_params.put(key, coll);
|
||||
}
|
||||
else
|
||||
{
|
||||
coll = m_params.get(key);
|
||||
}
|
||||
coll.add(value);
|
||||
}
|
||||
|
||||
public List<String> all(String key) throws NotFoundException
|
||||
{
|
||||
if(!m_params.containsKey(key))
|
||||
throw new NotFoundException();
|
||||
return m_params.get(key);
|
||||
}
|
||||
|
||||
public List<String> allSafe(String key, List<String> def)
|
||||
{
|
||||
if(!m_params.containsKey(key) || m_params.get(key).size() < 1)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
return m_params.get(key);
|
||||
}
|
||||
|
||||
public List<String> allSafe(String key)
|
||||
{
|
||||
return allSafe(key, new ArrayList<String>());
|
||||
}
|
||||
|
||||
public String first(String key) throws NotFoundException
|
||||
{
|
||||
List<String> list = all(key);
|
||||
if(list.size() < 1)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
public String firstSafe(String key, String def)
|
||||
{
|
||||
if(!m_params.containsKey(key) || m_params.get(key).size() < 1)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
return m_params.get(key).get(0);
|
||||
}
|
||||
|
||||
public String firstSafe(String key)
|
||||
{
|
||||
return firstSafe(key, "");
|
||||
}
|
||||
|
||||
private HashMap<String, List<String>> m_params = new HashMap<String, List<String>>();
|
||||
}
|
22
depends/launcher/org/multimc/ParseException.java
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.multimc;
|
||||
|
||||
public class ParseException extends java.lang.Exception
|
||||
{
|
||||
|
||||
}
|
179
depends/launcher/org/multimc/Utils.java
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.multimc;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Utils
|
||||
{
|
||||
/**
|
||||
* Combine two parts of a path.
|
||||
* @param path1
|
||||
* @param path2
|
||||
* @return the paths, combined
|
||||
*/
|
||||
public static String combine (String path1, String path2)
|
||||
{
|
||||
File file1 = new File(path1);
|
||||
File file2 = new File(file1, path2);
|
||||
return file2.getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Join a list of strings into a string using a separator!
|
||||
* @param strings the string list to join
|
||||
* @param separator the glue
|
||||
* @return the result.
|
||||
*/
|
||||
public static String join (List<String> strings, String separator)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String sep = "";
|
||||
for(String s: strings)
|
||||
{
|
||||
sb.append(sep).append(s);
|
||||
sep = separator;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified library to the classpath
|
||||
*
|
||||
* @param s the path to add
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void addToClassPath(String s) throws Exception
|
||||
{
|
||||
File f = new File(s);
|
||||
URL u = f.toURI().toURL();
|
||||
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
|
||||
Class urlClass = URLClassLoader.class;
|
||||
Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
|
||||
method.setAccessible(true);
|
||||
method.invoke(urlClassLoader, new Object[]{u});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds many libraries to the classpath
|
||||
*
|
||||
* @param jars the paths to add
|
||||
*/
|
||||
public static boolean addToClassPath(List<String> jars)
|
||||
{
|
||||
boolean pure = true;
|
||||
// initialize the class path
|
||||
for (String jar : jars)
|
||||
{
|
||||
try
|
||||
{
|
||||
Utils.addToClassPath(jar);
|
||||
} catch (Exception e)
|
||||
{
|
||||
System.err.println("Unable to load: " + jar);
|
||||
e.printStackTrace(System.err);
|
||||
pure = false;
|
||||
}
|
||||
}
|
||||
return pure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified path to the java library path
|
||||
*
|
||||
* @param pathToAdd the path to add
|
||||
* @throws Exception
|
||||
*/
|
||||
@Deprecated public static void addLibraryPath(String pathToAdd) throws Exception
|
||||
{
|
||||
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
|
||||
usrPathsField.setAccessible(true);
|
||||
|
||||
//get array of paths
|
||||
final String[] paths = (String[]) usrPathsField.get(null);
|
||||
|
||||
//check if the path to add is already present
|
||||
for (String path : paths)
|
||||
{
|
||||
if (path.equals(pathToAdd))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//add the new path
|
||||
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
|
||||
newPaths[newPaths.length - 1] = pathToAdd;
|
||||
usrPathsField.set(null, newPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a field that looks like a Minecraft base folder in a supplied class
|
||||
*
|
||||
* @param mc the class to scan
|
||||
*/
|
||||
public static Field getMCPathField(Class<?> mc)
|
||||
{
|
||||
Field[] fields = mc.getDeclaredFields();
|
||||
|
||||
for (Field f : fields)
|
||||
{
|
||||
if (f.getType() != File.class)
|
||||
{
|
||||
// Has to be File
|
||||
continue;
|
||||
}
|
||||
if (f.getModifiers() != (Modifier.PRIVATE + Modifier.STATIC))
|
||||
{
|
||||
// And Private Static.
|
||||
continue;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log to the MultiMC console
|
||||
*
|
||||
* @param message A String containing the message
|
||||
* @param level A String containing the level name. See MinecraftProcess::getLevel()
|
||||
*/
|
||||
public static void log(String message, String level)
|
||||
{
|
||||
// Kinda dirty
|
||||
String tag = "!![" + level + "]!";
|
||||
System.out.println(tag + message.replace("\n", "\n" + tag));
|
||||
}
|
||||
|
||||
public static void log(String message)
|
||||
{
|
||||
log(message, "MultiMC");
|
||||
}
|
||||
|
||||
public static void log()
|
||||
{
|
||||
System.out.println();
|
||||
}
|
||||
}
|
@ -1,40 +1,39 @@
|
||||
//
|
||||
// Copyright 2012 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.
|
||||
//
|
||||
package org.multimc.legacy;/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
import net.minecraft.Launcher;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.applet.Applet;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowListener;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public class MCFrame extends Frame implements WindowListener
|
||||
public class LegacyFrame extends Frame implements WindowListener
|
||||
{
|
||||
private Launcher appletWrap = null;
|
||||
public MCFrame ( String title )
|
||||
public LegacyFrame(String title)
|
||||
{
|
||||
super ( title );
|
||||
BufferedImage image = null;
|
||||
BufferedImage image;
|
||||
try {
|
||||
image = ImageIO.read ( new File ( "icon.png" ) );
|
||||
setIconImage ( image );
|
||||
@ -47,14 +46,14 @@ public class MCFrame extends Frame implements WindowListener
|
||||
public void start ( Applet mcApplet, String user, String session, Dimension winSize, boolean maximize )
|
||||
{
|
||||
try {
|
||||
appletWrap = new Launcher ( mcApplet, new URL ( "http://www.minecraft.net/game" ) );
|
||||
appletWrap = new Launcher( mcApplet, new URL ( "http://www.minecraft.net/game" ) );
|
||||
} catch ( MalformedURLException ignored ) {}
|
||||
|
||||
appletWrap.setParameter ( "username", user );
|
||||
appletWrap.setParameter ( "sessionid", session );
|
||||
appletWrap.setParameter ( "stand-alone", "true" ); // Show the quit button.
|
||||
mcApplet.setStub ( appletWrap );
|
||||
|
||||
appletWrap.setParameter ( "demo", "false" );
|
||||
appletWrap.setParameter("fullscreen", "false");
|
||||
mcApplet.setStub(appletWrap);
|
||||
this.add ( appletWrap );
|
||||
appletWrap.setPreferredSize ( winSize );
|
||||
this.pack();
|
||||
@ -63,7 +62,6 @@ public class MCFrame extends Frame implements WindowListener
|
||||
if ( maximize ) {
|
||||
this.setExtendedState ( MAXIMIZED_BOTH );
|
||||
}
|
||||
|
||||
validate();
|
||||
appletWrap.init();
|
||||
appletWrap.start();
|
178
depends/launcher/org/multimc/legacy/LegacyLauncher.java
Normal file
@ -0,0 +1,178 @@
|
||||
package org.multimc.legacy;/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
import org.multimc.Launcher;
|
||||
import org.multimc.NotFoundException;
|
||||
import org.multimc.ParamBucket;
|
||||
import org.multimc.Utils;
|
||||
|
||||
import java.applet.Applet;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
public class LegacyLauncher implements Launcher
|
||||
{
|
||||
@Override
|
||||
public int launch(ParamBucket params)
|
||||
{
|
||||
String userName, sessionId, windowTitle, windowParams, lwjgl;
|
||||
String mainClass = "net.minecraft.client.Minecraft";
|
||||
try
|
||||
{
|
||||
userName = params.first("userName");
|
||||
sessionId = params.first("sessionId");
|
||||
windowTitle = params.first("windowTitle");
|
||||
windowParams = params.first("windowParams");
|
||||
lwjgl = params.first("lwjgl");
|
||||
} catch (NotFoundException e)
|
||||
{
|
||||
System.err.println("Not enough arguments.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
String cwd = System.getProperty("user.dir");
|
||||
Dimension winSize = new Dimension(854, 480);
|
||||
boolean maximize = false;
|
||||
|
||||
String[] dimStrings = windowParams.split("x");
|
||||
|
||||
if (windowParams.equalsIgnoreCase("max"))
|
||||
{
|
||||
maximize = true;
|
||||
}
|
||||
else if (dimStrings.length == 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
winSize = new Dimension(Integer.parseInt(dimStrings[0]), Integer.parseInt(dimStrings[1]));
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
File binDir = new File(cwd, "bin");
|
||||
File lwjglDir;
|
||||
if (lwjgl.equalsIgnoreCase("Mojang"))
|
||||
{
|
||||
lwjglDir = binDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
lwjglDir = new File(lwjgl);
|
||||
}
|
||||
|
||||
URL[] classpath;
|
||||
{
|
||||
try
|
||||
{
|
||||
classpath = new URL[]
|
||||
{
|
||||
new File(binDir, "minecraft.jar").toURI().toURL(),
|
||||
new File(lwjglDir, "lwjgl.jar").toURI().toURL(),
|
||||
new File(lwjglDir, "lwjgl_util.jar").toURI().toURL(),
|
||||
new File(lwjglDir, "jinput.jar").toURI().toURL(),
|
||||
};
|
||||
} catch (MalformedURLException e)
|
||||
{
|
||||
System.err.println("Class path entry is badly formed:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
String nativesDir = new File(lwjglDir, "natives").toString();
|
||||
|
||||
System.setProperty("org.lwjgl.librarypath", nativesDir);
|
||||
System.setProperty("net.java.games.input.librarypath", nativesDir);
|
||||
|
||||
// print the pretty things
|
||||
{
|
||||
Utils.log("Main Class:");
|
||||
Utils.log(" " + mainClass);
|
||||
Utils.log();
|
||||
|
||||
Utils.log("Class Path:");
|
||||
for (URL s : classpath)
|
||||
{
|
||||
Utils.log(" " + s);
|
||||
}
|
||||
Utils.log();
|
||||
|
||||
Utils.log("Native Path:");
|
||||
Utils.log(" " + nativesDir);
|
||||
Utils.log();
|
||||
}
|
||||
|
||||
URLClassLoader cl = new URLClassLoader(classpath, LegacyLauncher.class.getClassLoader());
|
||||
|
||||
// Get the Minecraft Class and set the base folder
|
||||
Class<?> mc;
|
||||
try
|
||||
{
|
||||
mc = cl.loadClass(mainClass);
|
||||
|
||||
Field f = Utils.getMCPathField(mc);
|
||||
|
||||
if (f == null)
|
||||
{
|
||||
System.err.println("Could not find Minecraft path field. Launch failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
f.setAccessible(true);
|
||||
f.set(null, new File(cwd));
|
||||
} catch (Exception e)
|
||||
{
|
||||
System.err.println("Could not set base folder. Failed to find/access Minecraft main class:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
System.setProperty("minecraft.applet.TargetDirectory", cwd);
|
||||
|
||||
String[] mcArgs = new String[2];
|
||||
mcArgs[0] = userName;
|
||||
mcArgs[1] = sessionId;
|
||||
|
||||
Utils.log("Launching with applet wrapper...");
|
||||
try
|
||||
{
|
||||
Class<?> MCAppletClass = cl.loadClass("net.minecraft.client.MinecraftApplet");
|
||||
Applet mcappl = (Applet) MCAppletClass.newInstance();
|
||||
LegacyFrame mcWindow = new LegacyFrame(windowTitle);
|
||||
mcWindow.start(mcappl, userName, sessionId, winSize, maximize);
|
||||
} catch (Exception e)
|
||||
{
|
||||
Utils.log("Applet wrapper failed:", "Error");
|
||||
e.printStackTrace(System.err);
|
||||
Utils.log();
|
||||
Utils.log("Falling back to compatibility mode.");
|
||||
try
|
||||
{
|
||||
mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs);
|
||||
} catch (Exception e1)
|
||||
{
|
||||
Utils.log("Failed to invoke the Minecraft main class:", "Fatal");
|
||||
e1.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
210
depends/launcher/org/multimc/onesix/OneSixLauncher.java
Normal file
@ -0,0 +1,210 @@
|
||||
/* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.multimc.onesix;
|
||||
|
||||
import org.multimc.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OneSixLauncher implements Launcher
|
||||
{
|
||||
@Override
|
||||
public int launch(ParamBucket params)
|
||||
{
|
||||
// get and process the launch script params
|
||||
List<String> libraries;
|
||||
List<String> mcparams;
|
||||
List<String> mods;
|
||||
String mainClass;
|
||||
String natives;
|
||||
final String windowTitle;
|
||||
String windowParams;
|
||||
try
|
||||
{
|
||||
libraries = params.all("cp");
|
||||
mcparams = params.all("param");
|
||||
mainClass = params.first("mainClass");
|
||||
mods = params.allSafe("mods", new ArrayList<String>());
|
||||
natives = params.first("natives");
|
||||
windowTitle = params.first("windowTitle");
|
||||
// windowParams = params.first("windowParams");
|
||||
} catch (NotFoundException e)
|
||||
{
|
||||
System.err.println("Not enough arguments.");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
List<String> allJars = new ArrayList<String>();
|
||||
allJars.addAll(mods);
|
||||
allJars.addAll(libraries);
|
||||
|
||||
if(!Utils.addToClassPath(allJars))
|
||||
{
|
||||
System.err.println("Halting launch due to previous errors.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
String property = System.getProperty("os.arch");
|
||||
List<String> allNativePaths = new ArrayList<String>();
|
||||
boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64");
|
||||
allNativePaths.add(natives);
|
||||
allNativePaths.add(natives + "/" + (is_64 ? "64" : "32"));
|
||||
|
||||
// print the pretty things
|
||||
{
|
||||
Utils.log("Main Class:");
|
||||
Utils.log(" " + mainClass);
|
||||
Utils.log();
|
||||
|
||||
Utils.log("Native paths:");
|
||||
for (String s : allNativePaths)
|
||||
{
|
||||
Utils.log(" " + s);
|
||||
}
|
||||
Utils.log();
|
||||
|
||||
Utils.log("Libraries:");
|
||||
for (String s : libraries)
|
||||
{
|
||||
Utils.log(" " + s);
|
||||
}
|
||||
Utils.log();
|
||||
|
||||
if(mods.size() > 0)
|
||||
{
|
||||
Utils.log("Class Path Mods:");
|
||||
for (String s : mods)
|
||||
{
|
||||
Utils.log(" " + s);
|
||||
}
|
||||
Utils.log();
|
||||
}
|
||||
|
||||
Utils.log("Params:");
|
||||
Utils.log(" " + mcparams.toString());
|
||||
Utils.log();
|
||||
}
|
||||
|
||||
final ClassLoader cl = ClassLoader.getSystemClassLoader();
|
||||
|
||||
// set up the natives path(s).
|
||||
String libpath = Utils.join(allNativePaths, String.valueOf(File.pathSeparatorChar));
|
||||
System.setProperty("java.library.path", libpath);
|
||||
Field fieldSysPath;
|
||||
try
|
||||
{
|
||||
fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
|
||||
fieldSysPath.setAccessible( true );
|
||||
fieldSysPath.set( null, null );
|
||||
} catch (Exception e)
|
||||
{
|
||||
System.err.println("Failed to set the native library path:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the Minecraft Class.
|
||||
Class<?> mc;
|
||||
try
|
||||
{
|
||||
mc = cl.loadClass(mainClass);
|
||||
} catch (ClassNotFoundException e)
|
||||
{
|
||||
System.err.println("Failed to find Minecraft main class:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get the main method.
|
||||
Method meth;
|
||||
try
|
||||
{
|
||||
meth = mc.getMethod("main", String[].class);
|
||||
} catch (NoSuchMethodException e)
|
||||
{
|
||||
System.err.println("Failed to acquire the main method:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// FIXME: works only on linux, we need a better solution
|
||||
/*
|
||||
final java.nio.ByteBuffer[] icons = IconLoader.load("icon.png");
|
||||
new Thread() {
|
||||
public void run() {
|
||||
ClassLoader cl = ClassLoader.getSystemClassLoader();
|
||||
try
|
||||
{
|
||||
Class<?> Display;
|
||||
Method isCreated;
|
||||
Method setTitle;
|
||||
Method setIcon;
|
||||
|
||||
Display = cl.loadClass("org.lwjgl.opengl.Display");
|
||||
isCreated = Display.getMethod("isCreated");
|
||||
setTitle = Display.getMethod("setTitle", String.class);
|
||||
setIcon = Display.getMethod("setIcon", java.nio.ByteBuffer[].class);
|
||||
|
||||
// set the window title? Maybe?
|
||||
while(!(Boolean) isCreated.invoke(null))
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(150);
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
// Give it a bit more time ;)
|
||||
Thread.sleep(150);
|
||||
// set the title
|
||||
setTitle.invoke(null,windowTitle);
|
||||
// only set icon when there's actually something to set...
|
||||
if(icons.length > 0)
|
||||
{
|
||||
setIcon.invoke(null,(Object)icons);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.err.println("Couldn't set window icon or title.");
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
.start();
|
||||
*/
|
||||
// start Minecraft
|
||||
String[] paramsArray = mcparams.toArray(new String[mcparams.size()]); // init params accordingly
|
||||
try
|
||||
{
|
||||
meth.invoke(null, (Object) paramsArray); // static method doesn't have an instance
|
||||
} catch (Exception e)
|
||||
{
|
||||
System.err.println("Failed to start Minecraft:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
<RCC>
|
||||
<!--
|
||||
<qresource prefix="/java">
|
||||
<file alias="launcher.jar">@MMC_BIN@/depends/launcher/MultiMCLauncher.jar</file>
|
||||
<file alias="checker.jar">@MMC_BIN@/depends/javacheck/JavaCheck.jar</file>
|
||||
</qresource>
|
||||
-->
|
||||
</RCC>
|
51
graphics.qrc
@ -1,51 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="/icons/toolbar">
|
||||
<file alias="about">resources/icons/toolbar/about.png</file>
|
||||
<file alias="bug">resources/icons/toolbar/ReportBug.png</file>
|
||||
<file alias="centralmods">resources/icons/toolbar/centralmods.png</file>
|
||||
<file alias="checkupdate">resources/icons/toolbar/checkupdate.png</file>
|
||||
<file alias="help">resources/icons/toolbar/help.png</file>
|
||||
<file alias="new">resources/icons/toolbar/new.png</file>
|
||||
<file alias="copy">resources/icons/toolbar/InstCopy.png</file>
|
||||
<file alias="news">resources/icons/toolbar/NewsIcon.png</file>
|
||||
<file alias="refresh">resources/icons/toolbar/refresh.png</file>
|
||||
<file alias="settings">resources/icons/toolbar/settings.png</file>
|
||||
<file alias="viewfolder">resources/icons/toolbar/viewfolder.png</file>
|
||||
<file alias="cat">resources/icons/toolbar/Cat.png</file>
|
||||
<file alias="noaccount">resources/icons/toolbar/NoAccount.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/icons/instances">
|
||||
<file alias="brick">resources/icons/instances/brick.png</file>
|
||||
<file alias="chicken">resources/icons/instances/chicken128.png</file>
|
||||
<file alias="creeper">resources/icons/instances/creeper128.png</file>
|
||||
<file alias="derp">resources/icons/instances/derp.png</file>
|
||||
<file alias="diamond">resources/icons/instances/diamond.png</file>
|
||||
<file alias="dirt">resources/icons/instances/dirt.png</file>
|
||||
<file alias="enderman">resources/icons/instances/enderman.png</file>
|
||||
<file alias="enderpearl">resources/icons/instances/enderpearl128.png</file>
|
||||
<file alias="ftb-glow">resources/icons/instances/ftb_glow128.png</file>
|
||||
<file alias="ftb-logo">resources/icons/instances/ftb_logo128.png</file>
|
||||
<file alias="gear">resources/icons/instances/gear128.png</file>
|
||||
<file alias="gold">resources/icons/instances/gold.png</file>
|
||||
<file alias="grass">resources/icons/instances/grass.png</file>
|
||||
<file alias="herobrine">resources/icons/instances/herobrine128.png</file>
|
||||
<file alias="infinity">resources/icons/instances/infinity128.png</file>
|
||||
<file alias="iron">resources/icons/instances/iron.png</file>
|
||||
<file alias="magitech">resources/icons/instances/magitech128.png</file>
|
||||
<file alias="meat">resources/icons/instances/meat128.png</file>
|
||||
<file alias="netherstar">resources/icons/instances/netherstar128.png</file>
|
||||
<file alias="planks">resources/icons/instances/planks.png</file>
|
||||
<file alias="skeleton">resources/icons/instances/skeleton128.png</file>
|
||||
<file alias="squarecreeper">resources/icons/instances/squarecreeper128.png</file>
|
||||
<file alias="steve">resources/icons/instances/steve128.png</file>
|
||||
<file alias="stone">resources/icons/instances/stone.png</file>
|
||||
<file alias="tnt">resources/icons/instances/tnt.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/icons/multimc">
|
||||
<file alias="scalable/apps/multimc.svg">resources/icons/multimc.svg</file>
|
||||
<file alias="index.theme">resources/XdgIcon.theme</file>
|
||||
</qresource>
|
||||
<qresource prefix="/backgrounds">
|
||||
<file alias="kitteh">resources/catbgrnd2.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -140,6 +140,9 @@ void ConsoleWindow::write(QString data, MessageLevel::Enum mode)
|
||||
else if (mode == MessageLevel::Debug)
|
||||
while (iter.hasNext())
|
||||
writeColor(iter.next(), "green");
|
||||
else if (mode == MessageLevel::PrePost)
|
||||
while (iter.hasNext())
|
||||
writeColor(iter.next(), "grey");
|
||||
// TODO: implement other MessageLevels
|
||||
else
|
||||
while (iter.hasNext())
|
||||
@ -166,6 +169,10 @@ void ConsoleWindow::on_closeButton_clicked()
|
||||
|
||||
void ConsoleWindow::setMayClose(bool mayclose)
|
||||
{
|
||||
if(mayclose)
|
||||
ui->closeButton->setText(tr("Close"));
|
||||
else
|
||||
ui->closeButton->setText(tr("Hide"));
|
||||
m_mayclose = mayclose;
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,8 @@
|
||||
|
||||
#include "logic/news/NewsChecker.h"
|
||||
|
||||
#include "logic/status/StatusChecker.h"
|
||||
|
||||
#include "logic/net/URLConstants.h"
|
||||
|
||||
#include "logic/BaseInstance.h"
|
||||
@ -126,7 +128,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
// Add the news label to the news toolbar.
|
||||
{
|
||||
newsLabel = new QToolButton();
|
||||
newsLabel->setIcon(QIcon(":/icons/toolbar/news"));
|
||||
newsLabel->setIcon(QIcon::fromTheme("news"));
|
||||
newsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
newsLabel->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
ui->newsToolBar->insertWidget(ui->actionMoreNews, newsLabel);
|
||||
@ -199,7 +201,27 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
connect(MMC->instances().get(), SIGNAL(dataIsInvalid()), SLOT(selectionBad()));
|
||||
|
||||
m_statusLeft = new QLabel(tr("No instance selected"), this);
|
||||
m_statusRight = new QLabel(tr("No status available"), this);
|
||||
m_statusRefresh = new QToolButton(this);
|
||||
m_statusRefresh->setCheckable(true);
|
||||
m_statusRefresh->setToolButtonStyle(Qt::ToolButtonIconOnly);
|
||||
m_statusRefresh->setIcon(QIcon::fromTheme("refresh"));
|
||||
|
||||
statusBar()->addPermanentWidget(m_statusLeft, 1);
|
||||
statusBar()->addPermanentWidget(m_statusRight, 0);
|
||||
statusBar()->addPermanentWidget(m_statusRefresh, 0);
|
||||
|
||||
// Start status checker
|
||||
{
|
||||
connect(MMC->statusChecker().get(), &StatusChecker::statusLoaded, this, &MainWindow::updateStatusUI);
|
||||
connect(MMC->statusChecker().get(), &StatusChecker::statusLoadingFailed, this, &MainWindow::updateStatusFailedUI);
|
||||
|
||||
connect(m_statusRefresh, &QAbstractButton::clicked, this, &MainWindow::reloadStatus);
|
||||
connect(&statusTimer, &QTimer::timeout, this, &MainWindow::reloadStatus);
|
||||
statusTimer.setSingleShot(true);
|
||||
|
||||
reloadStatus();
|
||||
}
|
||||
|
||||
// Add "manage accounts" button, right align
|
||||
QWidget *spacer = new QWidget();
|
||||
@ -219,8 +241,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
accountMenuButton->setMenu(accountMenu);
|
||||
accountMenuButton->setPopupMode(QToolButton::InstantPopup);
|
||||
accountMenuButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
accountMenuButton->setIcon(
|
||||
QPixmap(":/icons/toolbar/noaccount").scaled(48, 48, Qt::KeepAspectRatio));
|
||||
accountMenuButton->setIcon(QIcon::fromTheme("noaccount"));
|
||||
|
||||
QWidgetAction *accountMenuButtonAction = new QWidgetAction(this);
|
||||
accountMenuButtonAction->setDefaultWidget(accountMenuButton);
|
||||
@ -235,17 +256,20 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
connect(MMC->accounts().get(), &MojangAccountList::listChanged, [this]
|
||||
{ repopulateAccountsMenu(); });
|
||||
|
||||
std::shared_ptr<MojangAccountList> accounts = MMC->accounts();
|
||||
// Show initial account
|
||||
activeAccountChanged();
|
||||
|
||||
auto accounts = MMC->accounts();
|
||||
|
||||
// TODO: Nicer way to iterate?
|
||||
for (int i = 0; i < accounts->count(); i++)
|
||||
{
|
||||
MojangAccountPtr account = accounts->at(i);
|
||||
auto account = accounts->at(i);
|
||||
if (account != nullptr)
|
||||
{
|
||||
auto job = new NetJob("Startup player skins: " + account->username());
|
||||
|
||||
for (AccountProfile profile : account->profiles())
|
||||
for (auto profile : account->profiles())
|
||||
{
|
||||
auto meta = MMC->metacache()->resolveEntry("skins", profile.name + ".png");
|
||||
auto action = CacheDownload::make(
|
||||
@ -383,7 +407,7 @@ void MainWindow::repopulateAccountsMenu()
|
||||
|
||||
QAction *action = new QAction(tr("No Default Account"), this);
|
||||
action->setCheckable(true);
|
||||
action->setIcon(QPixmap(":/icons/toolbar/noaccount").scaled(48, 48, Qt::KeepAspectRatio));
|
||||
action->setIcon(QIcon::fromTheme("noaccount"));
|
||||
action->setData("");
|
||||
if (active_username.isEmpty())
|
||||
{
|
||||
@ -437,8 +461,7 @@ void MainWindow::activeAccountChanged()
|
||||
}
|
||||
|
||||
// Set the icon to the "no account" icon.
|
||||
accountMenuButton->setIcon(
|
||||
QPixmap(":/icons/toolbar/noaccount").scaled(48, 48, Qt::KeepAspectRatio));
|
||||
accountMenuButton->setIcon(QIcon::fromTheme("noaccount"));
|
||||
}
|
||||
|
||||
bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
|
||||
@ -495,6 +518,57 @@ void MainWindow::updateNewsLabel()
|
||||
}
|
||||
}
|
||||
|
||||
static QString convertStatus(const QString &status)
|
||||
{
|
||||
QString ret = "?";
|
||||
|
||||
if(status == "green") ret = "↑";
|
||||
else if(status == "yellow") ret = "-";
|
||||
else if(status == "red") ret="↓";
|
||||
|
||||
return "<span style=\"font-size:11pt; font-weight:600;\">" + ret + "</span>";
|
||||
}
|
||||
|
||||
void MainWindow::reloadStatus()
|
||||
{
|
||||
m_statusRefresh->setChecked(true);
|
||||
MMC->statusChecker()->reloadStatus();
|
||||
//updateStatusUI();
|
||||
}
|
||||
|
||||
static QString makeStatusString(const QMap<QString, QString> statuses)
|
||||
{
|
||||
QString status = "";
|
||||
status += "Web: " + convertStatus(statuses["minecraft.net"]);
|
||||
status += " Account: " + convertStatus(statuses["account.mojang.com"]);
|
||||
status += " Skins: " + convertStatus(statuses["skins.minecraft.net"]);
|
||||
status += " Auth: " + convertStatus(statuses["authserver.mojang.com"]);
|
||||
status += " Session: " + convertStatus(statuses["sessionserver.mojang.com"]);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void MainWindow::updateStatusUI()
|
||||
{
|
||||
auto statusChecker = MMC->statusChecker();
|
||||
auto statuses = statusChecker->getStatusEntries();
|
||||
|
||||
QString status = makeStatusString(statuses);
|
||||
m_statusRefresh->setChecked(false);
|
||||
|
||||
m_statusRight->setText(status);
|
||||
|
||||
statusTimer.start(60 * 1000);
|
||||
}
|
||||
|
||||
void MainWindow::updateStatusFailedUI()
|
||||
{
|
||||
m_statusRight->setText(makeStatusString(QMap<QString, QString>()));
|
||||
m_statusRefresh->setChecked(false);
|
||||
|
||||
statusTimer.start(60 * 1000);
|
||||
}
|
||||
|
||||
void MainWindow::updateAvailable(QString repo, QString versionName, int versionId)
|
||||
{
|
||||
UpdateDialog dlg;
|
||||
@ -756,7 +830,7 @@ void MainWindow::on_actionChangeInstIcon_triggered()
|
||||
if (dlg.result() == QDialog::Accepted)
|
||||
{
|
||||
m_selectedInstance->setIconKey(dlg.selectedIconKey);
|
||||
auto ico = MMC->icons()->getIcon(dlg.selectedIconKey);
|
||||
auto ico = MMC->icons()->getBigIcon(dlg.selectedIconKey);
|
||||
ui->actionChangeInstIcon->setIcon(ico);
|
||||
}
|
||||
}
|
||||
@ -765,14 +839,14 @@ void MainWindow::iconUpdated(QString icon)
|
||||
{
|
||||
if(icon == m_currentInstIcon)
|
||||
{
|
||||
ui->actionChangeInstIcon->setIcon(MMC->icons()->getIcon(m_currentInstIcon));
|
||||
ui->actionChangeInstIcon->setIcon(MMC->icons()->getBigIcon(m_currentInstIcon));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updateInstanceToolIcon(QString new_icon)
|
||||
{
|
||||
m_currentInstIcon = new_icon;
|
||||
ui->actionChangeInstIcon->setIcon(MMC->icons()->getIcon(m_currentInstIcon));
|
||||
ui->actionChangeInstIcon->setIcon(MMC->icons()->getBigIcon(m_currentInstIcon));
|
||||
}
|
||||
|
||||
void MainWindow::setSelectedInstanceById(const QString &id)
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QProcess>
|
||||
#include <QTimer>
|
||||
|
||||
#include "logic/lists/InstanceList.h"
|
||||
#include "logic/BaseInstance.h"
|
||||
@ -169,6 +170,12 @@ slots:
|
||||
|
||||
void updateNewsLabel();
|
||||
|
||||
void updateStatusUI();
|
||||
|
||||
void updateStatusFailedUI();
|
||||
|
||||
void reloadStatus();
|
||||
|
||||
/*!
|
||||
* Runs the DownloadUpdateTask and installs updates.
|
||||
*/
|
||||
@ -198,8 +205,12 @@ private:
|
||||
Task *m_versionLoadTask;
|
||||
|
||||
QLabel *m_statusLeft;
|
||||
QLabel *m_statusRight;
|
||||
QToolButton *m_statusRefresh;
|
||||
|
||||
QMenu *accountMenu;
|
||||
QToolButton *accountMenuButton;
|
||||
QAction *manageAccountsAction;
|
||||
|
||||
QTimer statusTimer;
|
||||
};
|
||||
|
@ -14,7 +14,7 @@
|
||||
<string>MultiMC 5</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<iconset resource="../resources/multimc/multimc.qrc">
|
||||
<normaloff>:/icons/multimc/scalable/apps/multimc.svg</normaloff>:/icons/multimc/scalable/apps/multimc.svg</iconset>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
@ -152,8 +152,7 @@
|
||||
</widget>
|
||||
<action name="actionAddInstance">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/new</normaloff>:/icons/toolbar/new</iconset>
|
||||
<iconset theme="new"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add Instance</string>
|
||||
@ -167,8 +166,7 @@
|
||||
</action>
|
||||
<action name="actionViewInstanceFolder">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/viewfolder</normaloff>:/icons/toolbar/viewfolder</iconset>
|
||||
<iconset theme="viewfolder"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Instance Folder</string>
|
||||
@ -182,8 +180,7 @@
|
||||
</action>
|
||||
<action name="actionRefresh">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/refresh</normaloff>:/icons/toolbar/refresh</iconset>
|
||||
<iconset theme="refresh"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
@ -197,8 +194,7 @@
|
||||
</action>
|
||||
<action name="actionViewCentralModsFolder">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/centralmods</normaloff>:/icons/toolbar/centralmods</iconset>
|
||||
<iconset theme="centralmods"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Central Mods Folder</string>
|
||||
@ -212,8 +208,7 @@
|
||||
</action>
|
||||
<action name="actionCheckUpdate">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/checkupdate</normaloff>:/icons/toolbar/checkupdate</iconset>
|
||||
<iconset theme="checkupdate"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Check for Updates</string>
|
||||
@ -227,8 +222,7 @@
|
||||
</action>
|
||||
<action name="actionSettings">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/settings</normaloff>:/icons/toolbar/settings</iconset>
|
||||
<iconset theme="settings"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Settings</string>
|
||||
@ -245,8 +239,7 @@
|
||||
</action>
|
||||
<action name="actionReportBug">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/bug</normaloff>:/icons/toolbar/bug</iconset>
|
||||
<iconset theme="bug"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Report a Bug</string>
|
||||
@ -260,8 +253,7 @@
|
||||
</action>
|
||||
<action name="actionMoreNews">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/news</normaloff>:/icons/toolbar/news</iconset>
|
||||
<iconset theme="news"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>More News</string>
|
||||
@ -278,8 +270,7 @@
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/about</normaloff>:/icons/toolbar/about</iconset>
|
||||
<iconset theme="about"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>About MultiMC</string>
|
||||
@ -332,7 +323,7 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<iconset resource="../resources/instances/instances.qrc">
|
||||
<normaloff>:/icons/instances/infinity</normaloff>:/icons/instances/infinity</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
@ -472,20 +463,18 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/cat</normaloff>:/icons/toolbar/cat</iconset>
|
||||
<iconset theme="cat"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Meow</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p align="center"><span style=" font-weight:600; color:#ff0004;">Catnarok!</span></p><p align="center">Or just a cat with a ball of yarn?</p><p align="center"><span style=" font-style:italic;">WHO KNOWS?!</span></p><p align="center"><img src=":/icons/instances/tnt"/></p></body></html></string>
|
||||
<string><html><head/><body><p align="center">It's a fluffy kitty :3</p></body></html></string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCopyInstance">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/copy</normaloff>:/icons/toolbar/copy</iconset>
|
||||
<iconset theme="copy"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy Instance</string>
|
||||
@ -508,7 +497,9 @@
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
<include location="../graphics.qrc"/>
|
||||
<include location="../resources/instances/instances.qrc"/>
|
||||
<include location="../resources/multimc/multimc.qrc"/>
|
||||
<include location="../resources/backgrounds/backgrounds.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -24,6 +24,8 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
|
||||
MultiMCPlatform::fixWM_CLASS(this);
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->urlLabel->setOpenExternalLinks(true);
|
||||
|
||||
ui->icon->setPixmap(QIcon(":/icons/multimc/scalable/apps/multimc.svg").pixmap(64));
|
||||
ui->title->setText("MultiMC 5 " + MMC->version().toString());
|
||||
|
||||
|
@ -104,7 +104,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>689</width>
|
||||
<height>331</height>
|
||||
<height>311</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@ -229,7 +229,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>689</width>
|
||||
<height>331</height>
|
||||
<height>311</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@ -245,7 +245,7 @@
|
||||
<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:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
</style></head><body style=" font-family:'Bitstream Vera Sans'; font-size:11pt; 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:'MS Shell Dlg 2'; 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:'MS Shell Dlg 2'; font-size:10pt;">Andrew Okin &lt;</span><a href="mailto:forkk@forkk.net"><span style=" font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;">forkk@forkk.net</span></a><span style=" font-family:'MS Shell Dlg 2'; 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:'MS Shell Dlg 2'; font-size:10pt;">Petr Mrázek &lt;</span><a href="mailto:peterix@gmail.com"><span style=" font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;">peterix@gmail.com</span></a><span style=" font-family:'MS Shell Dlg 2'; font-size:10pt;">&gt;</span></p>
|
||||
@ -282,7 +282,7 @@ p, li { white-space: pre-wrap; }
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>689</width>
|
||||
<height>331</height>
|
||||
<height>311</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@ -309,7 +309,7 @@ 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">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'DejaVu Sans Mono'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
</style></head><body style=" font-family:'DejaVu Sans Mono'; font-size:11pt; 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 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>
|
||||
@ -430,7 +430,36 @@ p, li { white-space: pre-wrap; }
|
||||
<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:8pt;"> *</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:8pt;"> * This file has been put into the public domain.</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:8pt;"> * You can do whatever you want with this file.</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:8pt;"> */</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-size:8pt;"> */</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:8pt;"><br /></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;">Java IconLoader class</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;"><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-size:8pt;">Copyright (c) 2011, Chris Molini</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:8pt;">All rights reserved.</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:8pt;"><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-size:8pt;">Redistribution and use in source and binary forms, with or without</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:8pt;">modification, are permitted provided that the following conditions are met:</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:8pt;"> * Redistributions of source code must retain the above copyright</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:8pt;"> notice, this list of conditions and the following disclaimer.</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:8pt;"> * Redistributions in binary form must reproduce the above copyright</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:8pt;"> notice, this list of conditions and the following disclaimer in the</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:8pt;"> documentation and/or other materials provided with the distribution.</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:8pt;"> * Neither the name of the &lt;organization&gt; nor the</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:8pt;"> names of its contributors may be used to endorse or promote products</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:8pt;"> derived from this software without specific prior written permission.</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:8pt;"><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-size:8pt;">THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND</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:8pt;">ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED</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:8pt;">WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE</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:8pt;">DISCLAIMED. IN NO EVENT SHALL &lt;COPYRIGHT HOLDER&gt; BE LIABLE FOR ANY</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:8pt;">DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES</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:8pt;">(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;</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:8pt;">LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND</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:8pt;">ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT</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:8pt;">(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS</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:8pt;">SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</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:8pt;"><br /></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:8pt;"><br /></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -442,7 +471,7 @@ p, li { white-space: pre-wrap; }
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>689</width>
|
||||
<height>331</height>
|
||||
<height>311</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@ -455,12 +484,12 @@ 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">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; 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>
|
||||
</style></head><body style=" font-family:'Bitstream Vera Sans'; font-size:11pt; 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;">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.</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;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">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).</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;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">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 style=" font-weight:600;">without</span> implying that you have our blessing.</p></body></html></string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
|
@ -168,6 +168,12 @@
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="maxMemSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The maximum amount of memory Minecraft is allowed to use.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>512</number>
|
||||
</property>
|
||||
@ -198,6 +204,12 @@
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="minMemSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The amount of memory Minecraft is started with.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>256</number>
|
||||
</property>
|
||||
@ -214,6 +226,12 @@
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="permGenSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The amount of memory available to store loaded Java classes.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>64</number>
|
||||
</property>
|
||||
|
@ -50,8 +50,8 @@ void showWebsiteForMod(QWidget *parentDlg, Mod &m)
|
||||
else
|
||||
{
|
||||
CustomMessageBox::selectable(
|
||||
parentDlg, parentDlg->tr("How sad!"),
|
||||
parentDlg->tr("The mod author didn't provide a website link for this mod."),
|
||||
parentDlg, QObject::tr("How sad!"),
|
||||
QObject::tr("The mod author didn't provide a website link for this mod."),
|
||||
QMessageBox::Warning);
|
||||
}
|
||||
}
|
||||
|
@ -269,6 +269,10 @@ void SettingsDialog::refreshUpdateChannelDesc()
|
||||
// Get the channel list.
|
||||
QList<UpdateChecker::ChannelListEntry> channelList = MMC->updateChecker()->getChannelList();
|
||||
int selectedIndex = ui->updateChannelComboBox->currentIndex();
|
||||
if(selectedIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (selectedIndex < channelList.count())
|
||||
{
|
||||
// Find the channel list entry with the given index.
|
||||
@ -284,6 +288,9 @@ void SettingsDialog::refreshUpdateChannelDesc()
|
||||
|
||||
void SettingsDialog::applySettings(SettingsObject *s)
|
||||
{
|
||||
// Language
|
||||
s->set("Language", ui->languageBox->itemData(ui->languageBox->currentIndex()).toLocale().bcp47Name());
|
||||
|
||||
// Updates
|
||||
s->set("AutoUpdate", ui->autoUpdateCheckBox->isChecked());
|
||||
s->set("UpdateChannel", m_currentUpdateChannel);
|
||||
@ -365,6 +372,19 @@ void SettingsDialog::applySettings(SettingsObject *s)
|
||||
|
||||
void SettingsDialog::loadSettings(SettingsObject *s)
|
||||
{
|
||||
// Language
|
||||
ui->languageBox->clear();
|
||||
ui->languageBox->addItem(tr("English"), QLocale(QLocale::English));
|
||||
foreach(const QString & lang,
|
||||
QDir(MMC->root() + "/translations").entryList(QStringList() << "*.qm", QDir::Files))
|
||||
{
|
||||
QLocale locale(lang.section(QRegExp("[_\.]"), 1));
|
||||
ui->languageBox->addItem(
|
||||
QLocale::languageToString(locale.language()),
|
||||
locale);
|
||||
}
|
||||
ui->languageBox->setCurrentIndex(ui->languageBox->findData(QLocale(s->get("Language").toString())));
|
||||
|
||||
// Updates
|
||||
ui->autoUpdateCheckBox->setChecked(s->get("AutoUpdate").toBool());
|
||||
m_currentUpdateChannel = s->get("UpdateChannel").toString();
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>526</width>
|
||||
<height>723</height>
|
||||
<width>545</width>
|
||||
<height>609</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -35,43 +35,11 @@
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="generalTab">
|
||||
<widget class="QWidget" name="featuresTab">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
<string>Features</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="sortingModeBox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Sorting Mode</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="sortingModeBoxLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sortLastLaunchedBtn">
|
||||
<property name="text">
|
||||
<string>By last launched</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">sortingModeGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sortByNameBtn">
|
||||
<property name="text">
|
||||
<string>By name</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">sortingModeGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="updateSettingsBox">
|
||||
<property name="title">
|
||||
@ -86,32 +54,28 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="channelVerticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="updateChannelLabel">
|
||||
<property name="text">
|
||||
<string>Update Channel:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="updateChannelComboBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="updateChannelDescLabel">
|
||||
<property name="text">
|
||||
<string>No channel selected.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QLabel" name="updateChannelLabel">
|
||||
<property name="text">
|
||||
<string>Update Channel:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="updateChannelComboBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="updateChannelDescLabel">
|
||||
<property name="text">
|
||||
<string>No channel selected.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -280,6 +244,78 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="generalTab">
|
||||
<attribute name="title">
|
||||
<string>User Interface</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Language (needs restart):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="languageBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="sortingModeBox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Sorting Mode</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="sortingModeBoxLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sortLastLaunchedBtn">
|
||||
<property name="text">
|
||||
<string>By last launched</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">sortingModeGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sortByNameBtn">
|
||||
<property name="text">
|
||||
<string>By name</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">sortingModeGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="editorsBox">
|
||||
<property name="title">
|
||||
@ -427,6 +463,247 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="javaTab">
|
||||
<attribute name="title">
|
||||
<string>Java</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="memoryGroupBox">
|
||||
<property name="title">
|
||||
<string>Memory</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="maxMemSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The maximum amount of memory Minecraft is allowed to use.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>512</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65536</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>128</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1024</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelMinMem">
|
||||
<property name="text">
|
||||
<string>Minimum memory allocation:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelMaxMem">
|
||||
<property name="text">
|
||||
<string>Maximum memory allocation:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="minMemSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The amount of memory Minecraft is started with.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>256</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65536</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>128</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>256</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelPermGen">
|
||||
<property name="text">
|
||||
<string>PermGen:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="permGenSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The amount of memory available to store loaded Java classes.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>64</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>999999999</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>64</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="javaSettingsGroupBox">
|
||||
<property name="title">
|
||||
<string>Java Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelJavaPath">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Java path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="javaDetectBtn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto-detect...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="javaTestBtn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Test</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" 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="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>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="customCommandsGroupBox">
|
||||
<property name="title">
|
||||
<string>Custom Commands</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelPostExitCmd">
|
||||
<property name="text">
|
||||
<string>Post-exit command:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelPreLaunchCmd">
|
||||
<property name="text">
|
||||
<string>Pre-launch command:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="preLaunchCmdTextBox"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="postExitCmdTextBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelCustomCmdsDescription">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC's working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="networkTab">
|
||||
<property name="toolTip">
|
||||
<string>Network settings.</string>
|
||||
@ -586,229 +863,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="javaTab">
|
||||
<attribute name="title">
|
||||
<string>Java</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="memoryGroupBox">
|
||||
<property name="title">
|
||||
<string>Memory</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="maxMemSpinBox">
|
||||
<property name="minimum">
|
||||
<number>512</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65536</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>128</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1024</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelMinMem">
|
||||
<property name="text">
|
||||
<string>Minimum memory allocation:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelMaxMem">
|
||||
<property name="text">
|
||||
<string>Maximum memory allocation:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="minMemSpinBox">
|
||||
<property name="minimum">
|
||||
<number>256</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65536</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>128</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>256</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelPermGen">
|
||||
<property name="text">
|
||||
<string>PermGen:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="permGenSpinBox">
|
||||
<property name="minimum">
|
||||
<number>64</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>999999999</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>64</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="javaSettingsGroupBox">
|
||||
<property name="title">
|
||||
<string>Java Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelJavaPath">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Java path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="javaDetectBtn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto-detect...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="javaTestBtn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Test</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" 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="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>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="customCommandsGroupBox">
|
||||
<property name="title">
|
||||
<string>Custom Commands</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelPostExitCmd">
|
||||
<property name="text">
|
||||
<string>Post-exit command:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelPreLaunchCmd">
|
||||
<property name="text">
|
||||
<string>Pre-launch command:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="preLaunchCmdTextBox"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="postExitCmdTextBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelCustomCmdsDescription">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC's working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -827,20 +881,6 @@
|
||||
<tabstop>buttonBox</tabstop>
|
||||
<tabstop>sortLastLaunchedBtn</tabstop>
|
||||
<tabstop>sortByNameBtn</tabstop>
|
||||
<tabstop>autoUpdateCheckBox</tabstop>
|
||||
<tabstop>trackFtbBox</tabstop>
|
||||
<tabstop>ftbLauncherBox</tabstop>
|
||||
<tabstop>ftbLauncherBrowseBtn</tabstop>
|
||||
<tabstop>ftbBox</tabstop>
|
||||
<tabstop>ftbBrowseBtn</tabstop>
|
||||
<tabstop>instDirTextBox</tabstop>
|
||||
<tabstop>instDirBrowseBtn</tabstop>
|
||||
<tabstop>modsDirTextBox</tabstop>
|
||||
<tabstop>modsDirBrowseBtn</tabstop>
|
||||
<tabstop>lwjglDirTextBox</tabstop>
|
||||
<tabstop>lwjglDirBrowseBtn</tabstop>
|
||||
<tabstop>iconsDirTextBox</tabstop>
|
||||
<tabstop>iconsDirBrowseBtn</tabstop>
|
||||
<tabstop>jsonEditorTextBox</tabstop>
|
||||
<tabstop>jsonEditorBrowseBtn</tabstop>
|
||||
<tabstop>maximizedCheckBox</tabstop>
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "overridesetting.h"
|
||||
|
||||
#include "pathutils.h"
|
||||
#include <cmdutils.h>
|
||||
#include "lists/MinecraftVersionList.h"
|
||||
#include "logic/icons/IconList.h"
|
||||
|
||||
@ -81,6 +82,7 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
|
||||
settings().registerSetting("OverrideConsole", false);
|
||||
settings().registerOverride(globalSettings->getSetting("ShowConsole"));
|
||||
settings().registerOverride(globalSettings->getSetting("AutoCloseConsole"));
|
||||
settings().registerOverride(globalSettings->getSetting("LogPrePostOutput"));
|
||||
}
|
||||
|
||||
void BaseInstance::iconUpdated(QString key)
|
||||
@ -248,8 +250,19 @@ void BaseInstance::setName(QString val)
|
||||
d->m_settings->set("name", val);
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
|
||||
QString BaseInstance::name() const
|
||||
{
|
||||
I_D(BaseInstance);
|
||||
return d->m_settings->get("name").toString();
|
||||
}
|
||||
|
||||
QString BaseInstance::windowTitle() const
|
||||
{
|
||||
return "MultiMC: " + name();
|
||||
}
|
||||
|
||||
QStringList BaseInstance::extraArguments() const
|
||||
{
|
||||
return Util::Commandline::splitArgs(settings().get("JvmArgs").toString());
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public:
|
||||
|
||||
/// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to
|
||||
/// be unique.
|
||||
QString id() const;
|
||||
virtual QString id() const;
|
||||
|
||||
/// get the type of this instance
|
||||
QString instanceType() const;
|
||||
@ -71,6 +71,9 @@ public:
|
||||
QString name() const;
|
||||
void setName(QString val);
|
||||
|
||||
/// Value used for instance window titles
|
||||
QString windowTitle() const;
|
||||
|
||||
QString iconKey() const;
|
||||
void setIconKey(QString val);
|
||||
|
||||
@ -81,6 +84,8 @@ public:
|
||||
void setGroupInitial(QString val);
|
||||
void setGroupPost(QString val);
|
||||
|
||||
QStringList extraArguments() const;
|
||||
|
||||
virtual QString intendedVersionId() const = 0;
|
||||
virtual bool setIntendedVersionId(QString version) = 0;
|
||||
|
||||
|
@ -39,6 +39,15 @@ struct BaseVersion
|
||||
* the kind of version this is (Stable, Beta, Snapshot, whatever)
|
||||
*/
|
||||
virtual QString typeString() const = 0;
|
||||
|
||||
virtual bool operator<(BaseVersion &a)
|
||||
{
|
||||
return name() < a.name();
|
||||
};
|
||||
virtual bool operator>(BaseVersion &a)
|
||||
{
|
||||
return name() > a.name();
|
||||
};
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
||||
|
@ -20,6 +20,9 @@ void JavaChecker::performCheck()
|
||||
process->setArguments(args);
|
||||
process->setProgram(path);
|
||||
process->setProcessChannelMode(QProcess::SeparateChannels);
|
||||
QLOG_DEBUG() << "Running java checker!";
|
||||
QLOG_DEBUG() << "Java: " + path;
|
||||
QLOG_DEBUG() << "Args: {" + args.join("|") + "}";
|
||||
|
||||
connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this,
|
||||
SLOT(finished(int, QProcess::ExitStatus)));
|
||||
@ -42,15 +45,19 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
result.path = path;
|
||||
result.id = id;
|
||||
}
|
||||
QLOG_DEBUG() << "Java checker finished with status " << status << " exit code " << exitcode;
|
||||
|
||||
if (status == QProcess::CrashExit || exitcode == 1)
|
||||
{
|
||||
QLOG_DEBUG() << "Java checker failed!";
|
||||
emit checkFinished(result);
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
QString p_stdout = _process->readAllStandardOutput();
|
||||
QLOG_DEBUG() << p_stdout;
|
||||
|
||||
QMap<QString, QString> results;
|
||||
QStringList lines = p_stdout.split("\n", QString::SkipEmptyParts);
|
||||
for(QString line : lines)
|
||||
@ -70,6 +77,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
|
||||
if(!results.contains("os.arch") || !results.contains("java.version") || !success)
|
||||
{
|
||||
QLOG_DEBUG() << "Java checker failed - couldn't extract required information.";
|
||||
emit checkFinished(result);
|
||||
return;
|
||||
}
|
||||
@ -84,7 +92,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
result.mojangPlatform = is_64 ? "64" : "32";
|
||||
result.realPlatform = os_arch;
|
||||
result.javaVersion = java_version;
|
||||
|
||||
QLOG_DEBUG() << "Java checker succeeded.";
|
||||
emit checkFinished(result);
|
||||
}
|
||||
|
||||
@ -93,7 +101,7 @@ void JavaChecker::error(QProcess::ProcessError err)
|
||||
if(err == QProcess::FailedToStart)
|
||||
{
|
||||
killTimer.stop();
|
||||
|
||||
QLOG_DEBUG() << "Java checker has failed to start.";
|
||||
JavaCheckResult result;
|
||||
{
|
||||
result.path = path;
|
||||
@ -110,6 +118,7 @@ void JavaChecker::timeout()
|
||||
// NO MERCY. NO ABUSE.
|
||||
if(process)
|
||||
{
|
||||
QLOG_DEBUG() << "Java checker has been killed by timeout.";
|
||||
process->kill();
|
||||
}
|
||||
}
|
||||
|
@ -14,3 +14,8 @@ bool LegacyFTBInstance::menuActionEnabled(QString action_name) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QString LegacyFTBInstance::id() const
|
||||
{
|
||||
return "FTB/" + BaseInstance::id();
|
||||
}
|
||||
|
@ -10,4 +10,5 @@ public:
|
||||
QObject *parent = 0);
|
||||
virtual QString getStatusbarDescription();
|
||||
virtual bool menuActionEnabled(QString action_name) const;
|
||||
virtual QString id() const;
|
||||
};
|
||||
|
@ -47,7 +47,7 @@ std::shared_ptr<Task> LegacyInstance::doUpdate(bool only_prepare)
|
||||
// make sure the jar mods list is initialized by asking for it.
|
||||
auto list = jarModList();
|
||||
// create an update task
|
||||
return std::shared_ptr<Task> (new LegacyUpdate(this, only_prepare , this));
|
||||
return std::shared_ptr<Task>(new LegacyUpdate(this, only_prepare, this));
|
||||
}
|
||||
|
||||
MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account)
|
||||
@ -58,58 +58,27 @@ MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account)
|
||||
auto pixmap = icon.pixmap(128, 128);
|
||||
pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG");
|
||||
|
||||
// extract the legacy launcher
|
||||
QString launcherJar = PathCombine(MMC->bin(), "jars", "MultiMCLauncher.jar");
|
||||
|
||||
// set the process arguments
|
||||
// create the launch script
|
||||
QString launchScript;
|
||||
{
|
||||
QStringList args;
|
||||
|
||||
// window size
|
||||
QString windowSize;
|
||||
QString windowParams;
|
||||
if (settings().get("LaunchMaximized").toBool())
|
||||
windowSize = "max";
|
||||
windowParams = "max";
|
||||
else
|
||||
windowSize = QString("%1x%2").arg(settings().get("MinecraftWinWidth").toInt()).arg(
|
||||
windowParams = QString("%1x%2").arg(settings().get("MinecraftWinWidth").toInt()).arg(
|
||||
settings().get("MinecraftWinHeight").toInt());
|
||||
|
||||
// window title
|
||||
QString windowTitle;
|
||||
windowTitle.append("MultiMC: ").append(name());
|
||||
|
||||
// Java arguments
|
||||
args.append(Util::Commandline::splitArgs(settings().get("JvmArgs").toString()));
|
||||
|
||||
#ifdef OSX
|
||||
// OSX dock icon and name
|
||||
args << "-Xdock:icon=icon.png";
|
||||
args << QString("-Xdock:name=\"%1\"").arg(windowTitle);
|
||||
#endif
|
||||
|
||||
QString lwjgl = QDir(MMC->settings()->get("LWJGLDir").toString() + "/" + lwjglVersion())
|
||||
.absolutePath();
|
||||
|
||||
// launcher arguments
|
||||
args << QString("-Xms%1m").arg(settings().get("MinMemAlloc").toInt());
|
||||
args << QString("-Xmx%1m").arg(settings().get("MaxMemAlloc").toInt());
|
||||
args << QString("-XX:PermSize=%1m").arg(settings().get("PermGen").toInt());
|
||||
/**
|
||||
* HACK: Stupid hack for Intel drivers.
|
||||
* See: https://mojang.atlassian.net/browse/MCL-767
|
||||
*/
|
||||
#ifdef Q_OS_WIN32
|
||||
args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_"
|
||||
"minecraft.exe.heapdump");
|
||||
#endif
|
||||
|
||||
args << "-jar" << launcherJar;
|
||||
args << account->currentProfile()->name;
|
||||
args << account->sessionId();
|
||||
args << windowTitle;
|
||||
args << windowSize;
|
||||
args << lwjgl;
|
||||
proc->setArguments(args);
|
||||
launchScript += "userName " + account->currentProfile()->name + "\n";
|
||||
launchScript += "sessionId " + account->sessionId() + "\n";
|
||||
launchScript += "windowTitle " + windowTitle() + "\n";
|
||||
launchScript += "windowParams " + windowParams + "\n";
|
||||
launchScript += "lwjgl " + lwjgl + "\n";
|
||||
launchScript += "launch legacy\n";
|
||||
}
|
||||
proc->setLaunchScript(launchScript);
|
||||
|
||||
// set the process work path
|
||||
proc->setWorkdir(minecraftRoot());
|
||||
|
@ -14,14 +14,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "MultiMC.h"
|
||||
|
||||
#include "MinecraftProcess.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
//#include <QImage>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
|
||||
@ -42,6 +43,7 @@ MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst)
|
||||
|
||||
#ifdef LINUX
|
||||
// Strip IBus
|
||||
// IBus is a Linux IME framework. For some reason, it breaks MC?
|
||||
if (env.value("XMODIFIERS").contains(IBUS))
|
||||
env.insert("XMODIFIERS", env.value("XMODIFIERS").replace(IBUS, ""));
|
||||
#endif
|
||||
@ -57,11 +59,15 @@ MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst)
|
||||
// std channels
|
||||
connect(this, SIGNAL(readyReadStandardError()), SLOT(on_stdErr()));
|
||||
connect(this, SIGNAL(readyReadStandardOutput()), SLOT(on_stdOut()));
|
||||
}
|
||||
|
||||
void MinecraftProcess::setArguments(QStringList args)
|
||||
{
|
||||
m_args = args;
|
||||
// Log prepost launch command output (can be disabled.)
|
||||
if (m_instance->settings().get("LogPrePostOutput").toBool())
|
||||
{
|
||||
connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError,
|
||||
this, &MinecraftProcess::on_prepost_stdErr);
|
||||
connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput,
|
||||
this, &MinecraftProcess::on_prepost_stdOut);
|
||||
}
|
||||
}
|
||||
|
||||
void MinecraftProcess::setWorkdir(QString path)
|
||||
@ -90,47 +96,145 @@ QString MinecraftProcess::censorPrivateInfo(QString in)
|
||||
in.replace(profileId, "<PROFILE ID>");
|
||||
in.replace(profileName, "<PROFILE NAME>");
|
||||
}
|
||||
|
||||
auto i = m_account->user().properties.begin();
|
||||
while (i != m_account->user().properties.end())
|
||||
{
|
||||
in.replace(i.value(), "<" + i.key().toUpper() + ">");
|
||||
++i;
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
// console window
|
||||
MessageLevel::Enum MinecraftProcess::guessLevel(const QString &line, MessageLevel::Enum level)
|
||||
{
|
||||
if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") ||
|
||||
line.contains("[FINER]") || line.contains("[FINEST]"))
|
||||
level = MessageLevel::Message;
|
||||
if (line.contains("[SEVERE]") || line.contains("[STDERR]"))
|
||||
level = MessageLevel::Error;
|
||||
if (line.contains("[WARNING]"))
|
||||
level = MessageLevel::Warning;
|
||||
if (line.contains("Exception in thread") || line.contains(" at "))
|
||||
level = MessageLevel::Fatal;
|
||||
if (line.contains("[DEBUG]"))
|
||||
level = MessageLevel::Debug;
|
||||
return level;
|
||||
}
|
||||
|
||||
MessageLevel::Enum MinecraftProcess::getLevel(const QString &levelName)
|
||||
{
|
||||
if (levelName == "MultiMC")
|
||||
return MessageLevel::MultiMC;
|
||||
else if (levelName == "Debug")
|
||||
return MessageLevel::Debug;
|
||||
else if (levelName == "Info")
|
||||
return MessageLevel::Info;
|
||||
else if (levelName == "Message")
|
||||
return MessageLevel::Message;
|
||||
else if (levelName == "Warning")
|
||||
return MessageLevel::Warning;
|
||||
else if (levelName == "Error")
|
||||
return MessageLevel::Error;
|
||||
else if (levelName == "Fatal")
|
||||
return MessageLevel::Fatal;
|
||||
// Skip PrePost, it's not exposed to !![]!
|
||||
else
|
||||
return MessageLevel::Message;
|
||||
}
|
||||
|
||||
void MinecraftProcess::logOutput(const QStringList &lines,
|
||||
MessageLevel::Enum defaultLevel,
|
||||
bool guessLevel, bool censor)
|
||||
{
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
logOutput(lines[i], defaultLevel, guessLevel, censor);
|
||||
}
|
||||
|
||||
void MinecraftProcess::logOutput(QString line,
|
||||
MessageLevel::Enum defaultLevel,
|
||||
bool guessLevel, bool censor)
|
||||
{
|
||||
MessageLevel::Enum level = defaultLevel;
|
||||
|
||||
// Level prefix
|
||||
int endmark = line.indexOf("]!");
|
||||
if (line.startsWith("!![") && endmark != -1)
|
||||
{
|
||||
level = getLevel(line.left(endmark).mid(3));
|
||||
line = line.mid(endmark + 2);
|
||||
}
|
||||
// Guess level
|
||||
else if (guessLevel)
|
||||
level = this->guessLevel(line, defaultLevel);
|
||||
|
||||
if (censor)
|
||||
line = censorPrivateInfo(line);
|
||||
|
||||
emit log(line, level);
|
||||
}
|
||||
|
||||
void MinecraftProcess::on_stdErr()
|
||||
{
|
||||
QByteArray data = readAllStandardError();
|
||||
QString str = m_err_leftover + QString::fromLocal8Bit(data);
|
||||
m_err_leftover.clear();
|
||||
QStringList lines = str.split("\n");
|
||||
bool complete = str.endsWith("\n");
|
||||
|
||||
for (int i = 0; i < lines.size() - 1; i++)
|
||||
{
|
||||
QString &line = lines[i];
|
||||
emit log(censorPrivateInfo(line), getLevel(line, MessageLevel::Error));
|
||||
}
|
||||
if (!complete)
|
||||
m_err_leftover = lines.last();
|
||||
QStringList lines = str.split("\n");
|
||||
m_err_leftover = lines.takeLast();
|
||||
|
||||
logOutput(lines, MessageLevel::Error);
|
||||
}
|
||||
|
||||
void MinecraftProcess::on_stdOut()
|
||||
{
|
||||
QByteArray data = readAllStandardOutput();
|
||||
QString str = m_out_leftover + QString::fromLocal8Bit(data);
|
||||
m_out_leftover.clear();
|
||||
QStringList lines = str.split("\n");
|
||||
bool complete = str.endsWith("\n");
|
||||
|
||||
for (int i = 0; i < lines.size() - 1; i++)
|
||||
{
|
||||
QString &line = lines[i];
|
||||
emit log(censorPrivateInfo(line), getLevel(line, MessageLevel::Message));
|
||||
}
|
||||
if (!complete)
|
||||
m_out_leftover = lines.last();
|
||||
QStringList lines = str.split("\n");
|
||||
m_out_leftover = lines.takeLast();
|
||||
|
||||
logOutput(lines);
|
||||
}
|
||||
|
||||
void MinecraftProcess::on_prepost_stdErr()
|
||||
{
|
||||
QByteArray data = m_prepostlaunchprocess.readAllStandardError();
|
||||
QString str = m_err_leftover + QString::fromLocal8Bit(data);
|
||||
|
||||
QStringList lines = str.split("\n");
|
||||
m_err_leftover = lines.takeLast();
|
||||
|
||||
logOutput(lines, MessageLevel::PrePost, false, false);
|
||||
}
|
||||
|
||||
void MinecraftProcess::on_prepost_stdOut()
|
||||
{
|
||||
QByteArray data = m_prepostlaunchprocess.readAllStandardOutput();
|
||||
QString str = m_out_leftover + QString::fromLocal8Bit(data);
|
||||
|
||||
QStringList lines = str.split("\n");
|
||||
m_out_leftover = lines.takeLast();
|
||||
|
||||
logOutput(lines, MessageLevel::PrePost, false, false);
|
||||
}
|
||||
|
||||
// exit handler
|
||||
void MinecraftProcess::finish(int code, ExitStatus status)
|
||||
{
|
||||
// Flush console window
|
||||
if (!m_err_leftover.isEmpty())
|
||||
{
|
||||
logOutput(m_err_leftover, MessageLevel::Error);
|
||||
m_err_leftover.clear();
|
||||
}
|
||||
if (!m_out_leftover.isEmpty())
|
||||
{
|
||||
logOutput(m_out_leftover);
|
||||
m_out_leftover.clear();
|
||||
}
|
||||
|
||||
if (!killed)
|
||||
{
|
||||
if (status == NormalExit)
|
||||
@ -153,15 +257,32 @@ void MinecraftProcess::finish(int code, ExitStatus status)
|
||||
m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code));
|
||||
|
||||
// run post-exit
|
||||
if (!m_instance->settings().get("PostExitCommand").toString().isEmpty())
|
||||
QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString();
|
||||
if (!postlaunch_cmd.isEmpty())
|
||||
{
|
||||
m_prepostlaunchprocess.start(m_instance->settings().get("PostExitCommand").toString());
|
||||
emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd));
|
||||
m_prepostlaunchprocess.start(postlaunch_cmd);
|
||||
m_prepostlaunchprocess.waitForFinished();
|
||||
// Flush console window
|
||||
if (!m_err_leftover.isEmpty())
|
||||
{
|
||||
logOutput(m_err_leftover, MessageLevel::PrePost);
|
||||
m_err_leftover.clear();
|
||||
}
|
||||
if (!m_out_leftover.isEmpty())
|
||||
{
|
||||
logOutput(m_out_leftover, MessageLevel::PrePost);
|
||||
m_out_leftover.clear();
|
||||
}
|
||||
if (m_prepostlaunchprocess.exitStatus() != NormalExit)
|
||||
{
|
||||
emit log(tr("Post-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()),
|
||||
MessageLevel::Error);
|
||||
emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
|
||||
m_prepostlaunchprocess.exitStatus());
|
||||
}
|
||||
else
|
||||
emit log(tr("Post-Launch command ran successfully.\n\n"));
|
||||
}
|
||||
m_instance->cleanupAfterRun();
|
||||
emit ended(m_instance, code, status);
|
||||
@ -175,27 +296,78 @@ void MinecraftProcess::killMinecraft()
|
||||
|
||||
void MinecraftProcess::launch()
|
||||
{
|
||||
if (!m_instance->settings().get("PreLaunchCommand").toString().isEmpty())
|
||||
emit log("MultiMC version: " + MMC->version().toString() + "\n\n");
|
||||
emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n");
|
||||
|
||||
QString prelaunch_cmd = m_instance->settings().get("PreLaunchCommand").toString();
|
||||
if (!prelaunch_cmd.isEmpty())
|
||||
{
|
||||
m_prepostlaunchprocess.start(m_instance->settings().get("PreLaunchCommand").toString());
|
||||
// Launch
|
||||
emit log(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd));
|
||||
m_prepostlaunchprocess.start(prelaunch_cmd);
|
||||
// Wait
|
||||
m_prepostlaunchprocess.waitForFinished();
|
||||
// Flush console window
|
||||
if (!m_err_leftover.isEmpty())
|
||||
{
|
||||
logOutput(m_err_leftover, MessageLevel::PrePost);
|
||||
m_err_leftover.clear();
|
||||
}
|
||||
if (!m_out_leftover.isEmpty())
|
||||
{
|
||||
logOutput(m_out_leftover, MessageLevel::PrePost);
|
||||
m_out_leftover.clear();
|
||||
}
|
||||
// Process return values
|
||||
if (m_prepostlaunchprocess.exitStatus() != NormalExit)
|
||||
{
|
||||
emit log(tr("Pre-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()),
|
||||
MessageLevel::Fatal);
|
||||
m_instance->cleanupAfterRun();
|
||||
emit prelaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
|
||||
m_prepostlaunchprocess.exitStatus());
|
||||
return;
|
||||
}
|
||||
else
|
||||
emit log(tr("Pre-Launch command ran successfully.\n\n"));
|
||||
}
|
||||
|
||||
m_instance->setLastLaunch();
|
||||
auto &settings = m_instance->settings();
|
||||
|
||||
//////////// java arguments ////////////
|
||||
QStringList args;
|
||||
{
|
||||
// custom args go first. we want to override them if we have our own here.
|
||||
args.append(m_instance->extraArguments());
|
||||
|
||||
// OSX dock icon and name
|
||||
#ifdef OSX
|
||||
args << "-Xdock:icon=icon.png";
|
||||
args << QString("-Xdock:name=\"%1\"").arg(m_instance->windowTitle());
|
||||
#endif
|
||||
|
||||
// HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767
|
||||
#ifdef Q_OS_WIN32
|
||||
args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_"
|
||||
"minecraft.exe.heapdump");
|
||||
#endif
|
||||
|
||||
args << QString("-Xms%1m").arg(settings.get("MinMemAlloc").toInt());
|
||||
args << QString("-Xmx%1m").arg(settings.get("MaxMemAlloc").toInt());
|
||||
args << QString("-XX:PermSize=%1m").arg(settings.get("PermGen").toInt());
|
||||
if(!m_nativeFolder.isEmpty())
|
||||
args << QString("-Djava.library.path=%1").arg(m_nativeFolder);
|
||||
args << "-jar" << PathCombine(MMC->bin(), "jars", "NewLaunch.jar");
|
||||
}
|
||||
|
||||
emit log(QString("Minecraft folder is: '%1'").arg(workingDirectory()));
|
||||
QString JavaPath = m_instance->settings().get("JavaPath").toString();
|
||||
emit log(QString("Java path: '%1'").arg(JavaPath));
|
||||
QString allArgs = m_args.join("' '");
|
||||
emit log(QString("Arguments: '%1'").arg(censorPrivateInfo(allArgs)));
|
||||
start(JavaPath, m_args);
|
||||
emit log("Java path is:\n" + JavaPath + "\n\n");
|
||||
QString allArgs = args.join(", ");
|
||||
emit log("Java Arguments:\n[" + censorPrivateInfo(allArgs) + "]\n\n");
|
||||
|
||||
// instantiate the launcher part
|
||||
start(JavaPath, args);
|
||||
if (!waitForStarted())
|
||||
{
|
||||
//: Error message displayed if instace can't start
|
||||
@ -204,21 +376,7 @@ void MinecraftProcess::launch()
|
||||
emit launch_failed(m_instance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MessageLevel::Enum MinecraftProcess::getLevel(const QString &line, MessageLevel::Enum level)
|
||||
{
|
||||
|
||||
if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") ||
|
||||
line.contains("[FINER]") || line.contains("[FINEST]"))
|
||||
level = MessageLevel::Message;
|
||||
if (line.contains("[SEVERE]") || line.contains("[STDERR]"))
|
||||
level = MessageLevel::Error;
|
||||
if (line.contains("[WARNING]"))
|
||||
level = MessageLevel::Warning;
|
||||
if (line.contains("Exception in thread") || line.contains(" at "))
|
||||
level = MessageLevel::Fatal;
|
||||
if (line.contains("[DEBUG]"))
|
||||
level = MessageLevel::Debug;
|
||||
return level;
|
||||
// send the launch script to the launcher part
|
||||
QByteArray bytes = launchScript.toUtf8();
|
||||
writeData(bytes.constData(), bytes.length());
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
#include <QString>
|
||||
#include "BaseInstance.h"
|
||||
|
||||
/**
|
||||
@ -31,11 +31,12 @@ enum Enum
|
||||
{
|
||||
MultiMC, /**< MultiMC Messages */
|
||||
Debug, /**< Debug Messages */
|
||||
Info, /**< Info Messages */
|
||||
Info, /**< Info Messages */
|
||||
Message, /**< Standard Messages */
|
||||
Warning, /**< Warnings */
|
||||
Error, /**< Errors */
|
||||
Fatal /**< Fatal Errors */
|
||||
Fatal, /**< Fatal Errors */
|
||||
PrePost, /**< Pre/Post Launch command output */
|
||||
};
|
||||
}
|
||||
|
||||
@ -65,7 +66,15 @@ public:
|
||||
|
||||
void setWorkdir(QString path);
|
||||
|
||||
void setArguments(QStringList args);
|
||||
void setLaunchScript(QString script)
|
||||
{
|
||||
launchScript = script;
|
||||
}
|
||||
|
||||
void setNativeFolder(QString natives)
|
||||
{
|
||||
m_nativeFolder = natives;
|
||||
}
|
||||
|
||||
void killMinecraft();
|
||||
|
||||
@ -104,21 +113,30 @@ signals:
|
||||
|
||||
protected:
|
||||
BaseInstance *m_instance = nullptr;
|
||||
QStringList m_args;
|
||||
QString m_err_leftover;
|
||||
QString m_out_leftover;
|
||||
QProcess m_prepostlaunchprocess;
|
||||
bool killed = false;
|
||||
MojangAccountPtr m_account;
|
||||
QString launchScript;
|
||||
QString m_nativeFolder;
|
||||
|
||||
protected
|
||||
slots:
|
||||
void finish(int, QProcess::ExitStatus status);
|
||||
void on_stdErr();
|
||||
void on_stdOut();
|
||||
void on_prepost_stdOut();
|
||||
void on_prepost_stdErr();
|
||||
void logOutput(const QStringList &lines,
|
||||
MessageLevel::Enum defaultLevel = MessageLevel::Message,
|
||||
bool guessLevel = true, bool censor = true);
|
||||
void logOutput(QString line,
|
||||
MessageLevel::Enum defaultLevel = MessageLevel::Message,
|
||||
bool guessLevel = true, bool censor = true);
|
||||
|
||||
private:
|
||||
QString censorPrivateInfo(QString in);
|
||||
MessageLevel::Enum getLevel(const QString &message, MessageLevel::Enum defaultLevel);
|
||||
|
||||
MessageLevel::Enum guessLevel(const QString &message, MessageLevel::Enum defaultLevel);
|
||||
MessageLevel::Enum getLevel(const QString &levelName);
|
||||
};
|
||||
|
@ -23,15 +23,15 @@ void checkJVMArgs(QString jvmargs, QWidget *parent)
|
||||
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegExp("-Xm[sx]")))
|
||||
{
|
||||
CustomMessageBox::selectable(
|
||||
parent, parent->tr("JVM arguments warning"),
|
||||
parent->tr("You tried to manually set a JVM memory option (using "
|
||||
" \"-XX:PermSize\", \"-Xmx\" or \"-Xms\") - there"
|
||||
" are dedicated boxes for these in the settings (Java"
|
||||
" tab, in the Memory group at the top).\n"
|
||||
"Your manual settings will be overridden by the"
|
||||
" dedicated options.\n"
|
||||
"This message will be displayed until you remove them"
|
||||
" from the JVM arguments."),
|
||||
parent, QObject::tr("JVM arguments warning"),
|
||||
QObject::tr("You tried to manually set a JVM memory option (using "
|
||||
" \"-XX:PermSize\", \"-Xmx\" or \"-Xms\") - there"
|
||||
" are dedicated boxes for these in the settings (Java"
|
||||
" tab, in the Memory group at the top).\n"
|
||||
"Your manual settings will be overridden by the"
|
||||
" dedicated options.\n"
|
||||
"This message will be displayed until you remove them"
|
||||
" from the JVM arguments."),
|
||||
QMessageBox::Warning)->exec();
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +92,11 @@ OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *set
|
||||
}
|
||||
}
|
||||
|
||||
QString OneSixFTBInstance::id() const
|
||||
{
|
||||
return "FTB/" + BaseInstance::id();
|
||||
}
|
||||
|
||||
QString OneSixFTBInstance::getStatusbarDescription()
|
||||
{
|
||||
return "OneSix FTB: " + intendedVersionId();
|
||||
|
@ -15,6 +15,8 @@ public:
|
||||
|
||||
virtual std::shared_ptr<Task> doUpdate(bool only_prepare) override;
|
||||
|
||||
virtual QString id() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<OneSixLibrary> m_forge;
|
||||
};
|
||||
|
@ -13,12 +13,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "MultiMC.h"
|
||||
#include "OneSixInstance.h"
|
||||
#include "OneSixInstance_p.h"
|
||||
#include "OneSixUpdate.h"
|
||||
#include "MinecraftProcess.h"
|
||||
#include "OneSixVersion.h"
|
||||
#include "JavaChecker.h"
|
||||
#include "logic/icons/IconList.h"
|
||||
|
||||
#include <setting.h>
|
||||
#include <pathutils.h>
|
||||
@ -27,6 +29,7 @@
|
||||
#include "gui/dialogs/OneSixModEditDialog.h"
|
||||
#include "logger/QsLog.h"
|
||||
#include "logic/assets/AssetsUtils.h"
|
||||
#include <QIcon>
|
||||
|
||||
OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj,
|
||||
QObject *parent)
|
||||
@ -40,7 +43,7 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_o
|
||||
|
||||
std::shared_ptr<Task> OneSixInstance::doUpdate(bool only_prepare)
|
||||
{
|
||||
return std::shared_ptr<Task> (new OneSixUpdate(this, only_prepare));
|
||||
return std::shared_ptr<Task>(new OneSixUpdate(this, only_prepare));
|
||||
}
|
||||
|
||||
QString replaceTokensIn(QString text, QMap<QString, QString> with)
|
||||
@ -78,47 +81,50 @@ QDir OneSixInstance::reconstructAssets(std::shared_ptr<OneSixVersion> version)
|
||||
QFile indexFile(indexPath);
|
||||
QDir virtualRoot(PathCombine(virtualDir.path(), version->assets));
|
||||
|
||||
if(!indexFile.exists())
|
||||
if (!indexFile.exists())
|
||||
{
|
||||
QLOG_ERROR() << "No assets index file" << indexPath << "; can't reconstruct assets";
|
||||
return virtualRoot;
|
||||
}
|
||||
|
||||
QLOG_DEBUG() << "reconstructAssets" << assetsDir.path() << indexDir.path() << objectDir.path() << virtualDir.path() << virtualRoot.path();
|
||||
QLOG_DEBUG() << "reconstructAssets" << assetsDir.path() << indexDir.path()
|
||||
<< objectDir.path() << virtualDir.path() << virtualRoot.path();
|
||||
|
||||
AssetsIndex index;
|
||||
bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(indexPath, &index);
|
||||
|
||||
if(loadAssetsIndex)
|
||||
if (loadAssetsIndex && index.isVirtual)
|
||||
{
|
||||
if(index.isVirtual)
|
||||
QLOG_INFO() << "Reconstructing virtual assets folder at" << virtualRoot.path();
|
||||
|
||||
for (QString map : index.objects.keys())
|
||||
{
|
||||
QLOG_INFO() << "Reconstructing virtual assets folder at" << virtualRoot.path();
|
||||
AssetObject asset_object = index.objects.value(map);
|
||||
QString target_path = PathCombine(virtualRoot.path(), map);
|
||||
QFile target(target_path);
|
||||
|
||||
for(QString map : index.objects.keys())
|
||||
QString tlk = asset_object.hash.left(2);
|
||||
|
||||
QString original_path =
|
||||
PathCombine(PathCombine(objectDir.path(), tlk), asset_object.hash);
|
||||
QFile original(original_path);
|
||||
if(!original.exists())
|
||||
continue;
|
||||
if (!target.exists())
|
||||
{
|
||||
AssetObject asset_object = index.objects.value(map);
|
||||
QString target_path = PathCombine(virtualRoot.path(), map);
|
||||
QFile target(target_path);
|
||||
QFileInfo info(target_path);
|
||||
QDir target_dir = info.dir();
|
||||
// QLOG_DEBUG() << target_dir;
|
||||
if (!target_dir.exists())
|
||||
QDir("").mkpath(target_dir.path());
|
||||
|
||||
QString tlk = asset_object.hash.left(2);
|
||||
|
||||
QString original_path = PathCombine(PathCombine(objectDir.path(), tlk), asset_object.hash);
|
||||
QFile original(original_path);
|
||||
if(!target.exists())
|
||||
{
|
||||
QFileInfo info(target_path);
|
||||
QDir target_dir = info.dir();
|
||||
//QLOG_DEBUG() << target_dir;
|
||||
if(!target_dir.exists()) QDir("").mkpath(target_dir.path());
|
||||
|
||||
bool couldCopy = original.copy(target_path);
|
||||
QLOG_DEBUG() << " Copying" << original_path << "to" << target_path << QString::number(couldCopy);// << original.errorString();
|
||||
}
|
||||
bool couldCopy = original.copy(target_path);
|
||||
QLOG_DEBUG() << " Copying" << original_path << "to" << target_path
|
||||
<< QString::number(couldCopy); // << original.errorString();
|
||||
}
|
||||
|
||||
// TODO: Write last used time to virtualRoot/.lastused
|
||||
}
|
||||
|
||||
// TODO: Write last used time to virtualRoot/.lastused
|
||||
}
|
||||
|
||||
return virtualRoot;
|
||||
@ -155,7 +161,7 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
|
||||
|
||||
auto user = account->user();
|
||||
QJsonObject userAttrs;
|
||||
for(auto key: user.properties.keys())
|
||||
for (auto key : user.properties.keys())
|
||||
{
|
||||
auto array = QJsonArray::fromStringList(user.properties.values(key));
|
||||
userAttrs.insert(key, array);
|
||||
@ -180,71 +186,56 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account)
|
||||
{
|
||||
I_D(OneSixInstance);
|
||||
|
||||
QString natives_dir_raw = PathCombine(instanceRoot(), "natives/");
|
||||
QIcon icon = MMC->icons()->getIcon(iconKey());
|
||||
auto pixmap = icon.pixmap(128, 128);
|
||||
pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG");
|
||||
|
||||
auto version = d->version;
|
||||
if (!version)
|
||||
return nullptr;
|
||||
|
||||
QStringList args;
|
||||
args.append(Util::Commandline::splitArgs(settings().get("JvmArgs").toString()));
|
||||
args << QString("-Xms%1m").arg(settings().get("MinMemAlloc").toInt());
|
||||
args << QString("-Xmx%1m").arg(settings().get("MaxMemAlloc").toInt());
|
||||
args << QString("-XX:PermSize=%1m").arg(settings().get("PermGen").toInt());
|
||||
|
||||
/**
|
||||
* HACK: Stupid hack for Intel drivers.
|
||||
* See: https://mojang.atlassian.net/browse/MCL-767
|
||||
*/
|
||||
#ifdef Q_OS_WIN32
|
||||
args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_"
|
||||
"minecraft.exe.heapdump");
|
||||
#endif
|
||||
|
||||
QDir natives_dir(natives_dir_raw);
|
||||
args << QString("-Djava.library.path=%1").arg(natives_dir.absolutePath());
|
||||
QString classPath;
|
||||
QString launchScript;
|
||||
{
|
||||
auto libs = version->getActiveNormalLibs();
|
||||
for (auto lib : libs)
|
||||
{
|
||||
QFileInfo fi(QString("libraries/") + lib->storagePath());
|
||||
classPath.append(fi.absoluteFilePath());
|
||||
#ifdef Q_OS_WIN32
|
||||
classPath.append(';');
|
||||
#else
|
||||
classPath.append(':');
|
||||
#endif
|
||||
launchScript += "cp " + fi.absoluteFilePath() + "\n";
|
||||
}
|
||||
QString targetstr = "versions/" + version->id + "/" + version->id + ".jar";
|
||||
QFileInfo fi(targetstr);
|
||||
classPath.append(fi.absoluteFilePath());
|
||||
launchScript += "cp " + fi.absoluteFilePath() + "\n";
|
||||
}
|
||||
if (classPath.size())
|
||||
launchScript += "mainClass " + version->mainClass + "\n";
|
||||
|
||||
for (auto param : processMinecraftArgs(account))
|
||||
{
|
||||
args << "-cp";
|
||||
args << classPath;
|
||||
launchScript += "param " + param + "\n";
|
||||
}
|
||||
args << version->mainClass;
|
||||
args.append(processMinecraftArgs(account));
|
||||
|
||||
// Set the width and height for 1.6 instances
|
||||
bool maximize = settings().get("LaunchMaximized").toBool();
|
||||
if (maximize)
|
||||
{
|
||||
// this is probably a BAD idea
|
||||
// args << QString("--fullscreen");
|
||||
// launchScript += "param --fullscreen\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
args << QString("--width") << settings().get("MinecraftWinWidth").toString();
|
||||
args << QString("--height") << settings().get("MinecraftWinHeight").toString();
|
||||
launchScript +=
|
||||
"param --width\nparam " + settings().get("MinecraftWinWidth").toString() + "\n";
|
||||
launchScript +=
|
||||
"param --height\nparam " + settings().get("MinecraftWinHeight").toString() + "\n";
|
||||
}
|
||||
QDir natives_dir(PathCombine(instanceRoot(), "natives/"));
|
||||
launchScript += "windowTitle " + windowTitle() + "\n";
|
||||
launchScript += "natives " + natives_dir.absolutePath() + "\n";
|
||||
launchScript += "launch onesix\n";
|
||||
|
||||
// create the process and set its parameters
|
||||
MinecraftProcess *proc = new MinecraftProcess(this);
|
||||
proc->setArguments(args);
|
||||
proc->setWorkdir(minecraftRoot());
|
||||
proc->setLaunchScript(launchScript);
|
||||
// proc->setNativeFolder(natives_dir.absolutePath());
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,9 @@
|
||||
#include "OneSixRule.h"
|
||||
#include "OpSys.h"
|
||||
#include "logic/net/URLConstants.h"
|
||||
#include <pathutils.h>
|
||||
#include <JlCompress.h>
|
||||
#include "logger/QsLog.h"
|
||||
|
||||
void OneSixLibrary::finalize()
|
||||
{
|
||||
@ -133,6 +136,90 @@ QString OneSixLibrary::hint()
|
||||
return m_hint;
|
||||
}
|
||||
|
||||
bool OneSixLibrary::filesExist()
|
||||
{
|
||||
QString storage = storagePath();
|
||||
if (storage.contains("${arch}"))
|
||||
{
|
||||
QString cooked_storage = storage;
|
||||
cooked_storage.replace("${arch}", "32");
|
||||
QFileInfo info32(PathCombine("libraries", cooked_storage));
|
||||
if (!info32.exists())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
cooked_storage = storage;
|
||||
cooked_storage.replace("${arch}", "64");
|
||||
QFileInfo info64(PathCombine("libraries", cooked_storage));
|
||||
if (!info64.exists())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QFileInfo info(PathCombine("libraries", storage));
|
||||
if (!info.exists())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OneSixLibrary::extractTo(QString target_dir)
|
||||
{
|
||||
QString storage = storagePath();
|
||||
if (storage.contains("${arch}"))
|
||||
{
|
||||
QString cooked_storage = storage;
|
||||
cooked_storage.replace("${arch}", "32");
|
||||
QString origin = PathCombine("libraries", cooked_storage);
|
||||
QString target_dir_cooked = PathCombine(target_dir, "32");
|
||||
if(!ensureFolderPathExists(target_dir_cooked))
|
||||
{
|
||||
QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked;
|
||||
return false;
|
||||
}
|
||||
if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes)
|
||||
.isEmpty())
|
||||
{
|
||||
QLOG_ERROR() << "Couldn't extract " + origin;
|
||||
return false;
|
||||
}
|
||||
cooked_storage = storage;
|
||||
cooked_storage.replace("${arch}", "64");
|
||||
origin = PathCombine("libraries", cooked_storage);
|
||||
target_dir_cooked = PathCombine(target_dir, "32");
|
||||
if(!ensureFolderPathExists(target_dir_cooked))
|
||||
{
|
||||
QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked;
|
||||
return false;
|
||||
}
|
||||
if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes)
|
||||
.isEmpty())
|
||||
{
|
||||
QLOG_ERROR() << "Couldn't extract " + origin;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!ensureFolderPathExists(target_dir))
|
||||
{
|
||||
QLOG_ERROR() << "Couldn't create folder " + target_dir;
|
||||
return false;
|
||||
}
|
||||
QString path = PathCombine("libraries", storage);
|
||||
if (JlCompress::extractWithExceptions(path, target_dir, extract_excludes).isEmpty())
|
||||
{
|
||||
QLOG_ERROR() << "Couldn't extract " + path;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonObject OneSixLibrary::toJson()
|
||||
{
|
||||
QJsonObject libRoot;
|
||||
|
@ -126,4 +126,7 @@ public:
|
||||
/// set a hint about how to treat the library. This is an MMC extension.
|
||||
void setHint(QString hint);
|
||||
QString hint();
|
||||
|
||||
bool extractTo(QString target_dir);
|
||||
bool filesExist();
|
||||
};
|
||||
|
@ -54,17 +54,7 @@ void OneSixUpdate::executeTask()
|
||||
|
||||
if (m_only_prepare)
|
||||
{
|
||||
/*
|
||||
* FIXME: in offline mode, do not proceed!
|
||||
*/
|
||||
setStatus(tr("Testing the Java installation..."));
|
||||
QString java_path = m_inst->settings().get("JavaPath").toString();
|
||||
|
||||
checker.reset(new JavaChecker());
|
||||
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
|
||||
SLOT(checkFinishedOffline(JavaCheckResult)));
|
||||
checker->path = java_path;
|
||||
checker->performCheck();
|
||||
prepareForLaunch();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -83,46 +73,8 @@ void OneSixUpdate::executeTask()
|
||||
}
|
||||
else
|
||||
{
|
||||
checkJavaOnline();
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixUpdate::checkJavaOnline()
|
||||
{
|
||||
setStatus(tr("Testing the Java installation..."));
|
||||
QString java_path = m_inst->settings().get("JavaPath").toString();
|
||||
|
||||
checker.reset(new JavaChecker());
|
||||
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
|
||||
SLOT(checkFinishedOnline(JavaCheckResult)));
|
||||
checker->path = java_path;
|
||||
checker->performCheck();
|
||||
}
|
||||
|
||||
void OneSixUpdate::checkFinishedOnline(JavaCheckResult result)
|
||||
{
|
||||
if (result.valid)
|
||||
{
|
||||
java_is_64bit = result.is_64bit;
|
||||
jarlibStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
emitFailed("The java binary doesn't work. Check the settings and correct the problem");
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixUpdate::checkFinishedOffline(JavaCheckResult result)
|
||||
{
|
||||
if (result.valid)
|
||||
{
|
||||
java_is_64bit = result.is_64bit;
|
||||
prepareForLaunch();
|
||||
}
|
||||
else
|
||||
{
|
||||
emitFailed("The java binary doesn't work. Check the settings and correct the problem");
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixUpdate::versionFileStart()
|
||||
@ -130,7 +82,8 @@ void OneSixUpdate::versionFileStart()
|
||||
QLOG_INFO() << m_inst->name() << ": getting version file.";
|
||||
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");
|
||||
job->addNetAction(ByteArrayDownload::make(QUrl(urlstr)));
|
||||
specificVersionDownloadJob.reset(job);
|
||||
@ -186,7 +139,7 @@ void OneSixUpdate::versionFileFinished()
|
||||
}
|
||||
inst->reloadFullVersion();
|
||||
|
||||
checkJavaOnline();
|
||||
jarlibStart();
|
||||
}
|
||||
|
||||
void OneSixUpdate::versionFileFailed()
|
||||
@ -245,17 +198,17 @@ void OneSixUpdate::assetIndexFinished()
|
||||
dls.append(objectDL);
|
||||
}
|
||||
}
|
||||
if(dls.size())
|
||||
if (dls.size())
|
||||
{
|
||||
setStatus(tr("Getting the assets files from Mojang..."));
|
||||
auto job = new NetJob("Assets for " + inst->name());
|
||||
for(auto dl: dls)
|
||||
for (auto dl : dls)
|
||||
job->addNetAction(dl);
|
||||
jarlibDownloadJob.reset(job);
|
||||
connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetsFinished()));
|
||||
connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(assetsFailed()));
|
||||
connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
|
||||
SIGNAL(progress(qint64, qint64)));
|
||||
SIGNAL(progress(qint64, qint64)));
|
||||
jarlibDownloadJob->start();
|
||||
return;
|
||||
}
|
||||
@ -277,8 +230,6 @@ void OneSixUpdate::assetsFailed()
|
||||
emitFailed("Failed to download assets!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void OneSixUpdate::jarlibStart()
|
||||
{
|
||||
setStatus(tr("Getting the library files from Mojang..."));
|
||||
@ -318,24 +269,37 @@ void OneSixUpdate::jarlibStart()
|
||||
{
|
||||
if (lib->hint() == "local")
|
||||
continue;
|
||||
QString subst = java_is_64bit ? "64" : "32";
|
||||
QString storage = lib->storagePath();
|
||||
QString dl = lib->downloadUrl();
|
||||
|
||||
storage.replace("${arch}", subst);
|
||||
dl.replace("${arch}", subst);
|
||||
QString raw_storage = lib->storagePath();
|
||||
QString raw_dl = lib->downloadUrl();
|
||||
|
||||
auto entry = metacache->resolveEntry("libraries", storage);
|
||||
if (entry->stale)
|
||||
auto f = [&](QString storage, QString dl)
|
||||
{
|
||||
if (lib->hint() == "forge-pack-xz")
|
||||
auto entry = metacache->resolveEntry("libraries", storage);
|
||||
if (entry->stale)
|
||||
{
|
||||
ForgeLibs.append(ForgeXzDownload::make(storage, entry));
|
||||
}
|
||||
else
|
||||
{
|
||||
jarlibDownloadJob->addNetAction(CacheDownload::make(dl, entry));
|
||||
if (lib->hint() == "forge-pack-xz")
|
||||
{
|
||||
ForgeLibs.append(ForgeXzDownload::make(storage, entry));
|
||||
}
|
||||
else
|
||||
{
|
||||
jarlibDownloadJob->addNetAction(CacheDownload::make(dl, entry));
|
||||
}
|
||||
}
|
||||
};
|
||||
if (raw_storage.contains("${arch}"))
|
||||
{
|
||||
QString cooked_storage = raw_storage;
|
||||
QString cooked_dl = raw_dl;
|
||||
f(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32"));
|
||||
cooked_storage = raw_storage;
|
||||
cooked_dl = raw_dl;
|
||||
f(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64"));
|
||||
}
|
||||
else
|
||||
{
|
||||
f(raw_storage, raw_dl);
|
||||
}
|
||||
}
|
||||
// TODO: think about how to propagate this from the original json file... or IF AT ALL
|
||||
@ -376,7 +340,6 @@ void OneSixUpdate::prepareForLaunch()
|
||||
// delete any leftovers, if they are present.
|
||||
onesix_inst->cleanupAfterRun();
|
||||
|
||||
// Acquire swag
|
||||
QString natives_dir_raw = PathCombine(onesix_inst->instanceRoot(), "natives/");
|
||||
auto version = onesix_inst->getFullVersion();
|
||||
if (!version)
|
||||
@ -385,38 +348,30 @@ void OneSixUpdate::prepareForLaunch()
|
||||
"it or changing the version.");
|
||||
return;
|
||||
}
|
||||
auto libs_to_extract = version->getActiveNativeLibs();
|
||||
|
||||
// Acquire bag
|
||||
bool success = ensureFolderPathExists(natives_dir_raw);
|
||||
if (!success)
|
||||
/*
|
||||
* emitFailed("Could not create the native library folder:\n" + natives_dir_raw +
|
||||
"\nMake sure MultiMC has appropriate permissions and there is enough
|
||||
space "
|
||||
"on the storage device.");
|
||||
*/
|
||||
for (auto lib : version->getActiveNativeLibs())
|
||||
{
|
||||
emitFailed("Could not create the native library folder:\n" + natives_dir_raw +
|
||||
"\nMake sure MultiMC has appropriate permissions and there is enough space "
|
||||
"on the storage device.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Put swag in the bag
|
||||
QString subst = java_is_64bit ? "64" : "32";
|
||||
for (auto lib : libs_to_extract)
|
||||
{
|
||||
QString storage = lib->storagePath();
|
||||
storage.replace("${arch}", subst);
|
||||
|
||||
QString path = "libraries/" + storage;
|
||||
QLOG_INFO() << "Will extract " << path.toLocal8Bit();
|
||||
if (JlCompress::extractWithExceptions(path, natives_dir_raw, lib->extract_excludes)
|
||||
.isEmpty())
|
||||
if (!lib->filesExist())
|
||||
{
|
||||
emitFailed(
|
||||
"Could not extract the native library:\n" + path +
|
||||
"\nMake sure MultiMC has appropriate permissions and there is enough space "
|
||||
"on the storage device.");
|
||||
emitFailed("Native library is missing some files:\n" + lib->storagePath() +
|
||||
"\n\nRun the instance at least once in online mode to get all the "
|
||||
"required files.");
|
||||
return;
|
||||
}
|
||||
if (!lib->extractTo(natives_dir_raw))
|
||||
{
|
||||
emitFailed("Could not extract the native library:\n" + lib->storagePath() + " to " +
|
||||
natives_dir_raw +
|
||||
"\n\nMake sure MultiMC has appropriate permissions and there is enough "
|
||||
"space on the storage device.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Show them your war face!
|
||||
emitSucceeded();
|
||||
}
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include "logic/net/NetJob.h"
|
||||
#include "logic/tasks/Task.h"
|
||||
#include "logic/JavaChecker.h"
|
||||
|
||||
class MinecraftVersion;
|
||||
class BaseInstance;
|
||||
@ -50,10 +49,6 @@ slots:
|
||||
void assetsFinished();
|
||||
void assetsFailed();
|
||||
|
||||
void checkJavaOnline();
|
||||
void checkFinishedOnline(JavaCheckResult result);
|
||||
void checkFinishedOffline(JavaCheckResult result);
|
||||
|
||||
// extract the appropriate libraries
|
||||
void prepareForLaunch();
|
||||
|
||||
@ -65,7 +60,4 @@ private:
|
||||
std::shared_ptr<MinecraftVersion> targetVersion;
|
||||
BaseInstance *m_inst = nullptr;
|
||||
bool m_only_prepare = false;
|
||||
std::shared_ptr<JavaChecker> checker;
|
||||
|
||||
bool java_is_64bit = false;
|
||||
};
|
||||
|
@ -19,5 +19,5 @@
|
||||
|
||||
namespace SkinUtils
|
||||
{
|
||||
QPixmap getFaceFromCache(QString username, int height = 48, int width = 48);
|
||||
QPixmap getFaceFromCache(QString username, int height = 64, int width = 64);
|
||||
}
|
||||
|
@ -198,7 +198,11 @@ void MojangAccount::authFailed(QString reason)
|
||||
{
|
||||
// This is emitted when the yggdrasil tasks time out or are cancelled.
|
||||
// -> we treat the error as no-op
|
||||
if (reason != "Yggdrasil task cancelled.")
|
||||
if (reason == "Yggdrasil task cancelled.")
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
m_online = false;
|
||||
m_accessToken = QString();
|
||||
|
@ -336,6 +336,23 @@ QIcon IconList::getIcon(QString key)
|
||||
return QIcon();
|
||||
}
|
||||
|
||||
QIcon IconList::getBigIcon(QString key)
|
||||
{
|
||||
int icon_index = getIconIndex(key);
|
||||
|
||||
if (icon_index == -1)
|
||||
key = "infinity";
|
||||
|
||||
// Fallback for icons that don't exist.
|
||||
icon_index = getIconIndex(key);
|
||||
|
||||
if (icon_index == -1)
|
||||
return QIcon();
|
||||
|
||||
QPixmap bigone = icons[icon_index].icon().pixmap(256,256).scaled(256,256);
|
||||
return QIcon(bigone);
|
||||
}
|
||||
|
||||
int IconList::getIconIndex(QString key)
|
||||
{
|
||||
if (key == "default")
|
||||
|
@ -34,6 +34,7 @@ public:
|
||||
virtual ~IconList() {};
|
||||
|
||||
QIcon getIcon(QString key);
|
||||
QIcon getBigIcon(QString key);
|
||||
int getIconIndex(QString key);
|
||||
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||
|
@ -187,7 +187,7 @@ bool ForgeListLoadTask::parseForgeList(QList<BaseVersionPtr> &out)
|
||||
QByteArray data;
|
||||
{
|
||||
auto dlJob = listDownload;
|
||||
auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->m_target_path;
|
||||
auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->getTargetFilepath();
|
||||
QFile listFile(filename);
|
||||
if (!listFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
@ -303,7 +303,7 @@ bool ForgeListLoadTask::parseForgeGradleList(QList<BaseVersionPtr> &out)
|
||||
QByteArray data;
|
||||
{
|
||||
auto dlJob = gradleListDownload;
|
||||
auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->m_target_path;
|
||||
auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->getTargetFilepath();
|
||||
QFile listFile(filename);
|
||||
if (!listFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
@ -404,12 +404,8 @@ void ForgeListLoadTask::listDownloaded()
|
||||
{
|
||||
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();
|
||||
});
|
||||
std::sort(list.begin(), list.end(), [](const BaseVersionPtr & l, const BaseVersionPtr & r)
|
||||
{ return (*l > *r); });
|
||||
|
||||
m_list->updateListData(list);
|
||||
|
||||
|
@ -29,25 +29,38 @@ typedef std::shared_ptr<ForgeVersion> ForgeVersionPtr;
|
||||
|
||||
struct ForgeVersion : public BaseVersion
|
||||
{
|
||||
virtual QString descriptor()
|
||||
virtual QString descriptor() override
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
;
|
||||
virtual QString name()
|
||||
virtual QString name() override
|
||||
{
|
||||
return "Forge " + jobbuildver;
|
||||
}
|
||||
;
|
||||
virtual QString typeString() const
|
||||
virtual QString typeString() const override
|
||||
{
|
||||
if (installer_url.isEmpty())
|
||||
return "Universal";
|
||||
else
|
||||
return "Installer";
|
||||
}
|
||||
;
|
||||
|
||||
virtual bool operator<(BaseVersion &a) override
|
||||
{
|
||||
ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
|
||||
if(!pa)
|
||||
return true;
|
||||
return m_buildnr < pa->m_buildnr;
|
||||
}
|
||||
virtual bool operator>(BaseVersion &a) override
|
||||
{
|
||||
ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
|
||||
if(!pa)
|
||||
return false;
|
||||
return m_buildnr > pa->m_buildnr;
|
||||
}
|
||||
int m_buildnr = 0;
|
||||
QString universal_url;
|
||||
QString changelog_url;
|
||||
|
@ -308,45 +308,52 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
|
||||
return;
|
||||
}
|
||||
dir.cd("ModPacks");
|
||||
auto fpath = dir.absoluteFilePath("modpacks.xml");
|
||||
QFile f(fpath);
|
||||
QLOG_INFO() << "Discovering FTB instances -- " << fpath;
|
||||
if (!f.open(QFile::ReadOnly))
|
||||
return;
|
||||
|
||||
// read the FTB packs XML.
|
||||
QXmlStreamReader reader(&f);
|
||||
while (!reader.atEnd())
|
||||
auto allFiles = dir.entryList(QDir::Readable | QDir::Files, QDir::Name);
|
||||
for(auto filename: allFiles)
|
||||
{
|
||||
switch (reader.readNext())
|
||||
if(!filename.endsWith(".xml"))
|
||||
continue;
|
||||
auto fpath = dir.absoluteFilePath(filename);
|
||||
QFile f(fpath);
|
||||
QLOG_INFO() << "Discovering FTB instances -- " << fpath;
|
||||
if (!f.open(QFile::ReadOnly))
|
||||
continue;
|
||||
|
||||
// read the FTB packs XML.
|
||||
QXmlStreamReader reader(&f);
|
||||
while (!reader.atEnd())
|
||||
{
|
||||
case QXmlStreamReader::StartElement:
|
||||
{
|
||||
if (reader.name() == "modpack")
|
||||
switch (reader.readNext())
|
||||
{
|
||||
QXmlStreamAttributes attrs = reader.attributes();
|
||||
FTBRecord record;
|
||||
record.dir = attrs.value("dir").toString();
|
||||
QDir test(dataDir.absoluteFilePath(record.dir));
|
||||
if(!test.exists())
|
||||
continue;
|
||||
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);
|
||||
case QXmlStreamReader::StartElement:
|
||||
{
|
||||
if (reader.name() == "modpack")
|
||||
{
|
||||
QXmlStreamAttributes attrs = reader.attributes();
|
||||
FTBRecord record;
|
||||
record.dir = attrs.value("dir").toString();
|
||||
QDir test(dataDir.absoluteFilePath(record.dir));
|
||||
if(!test.exists())
|
||||
continue;
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QXmlStreamReader::EndElement:
|
||||
break;
|
||||
case QXmlStreamReader::Characters:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
f.close();
|
||||
|
||||
if(!records.size())
|
||||
{
|
||||
QLOG_INFO() << "No FTB instances to load.";
|
||||
|
@ -33,25 +33,44 @@ CacheDownload::CacheDownload(QUrl url, MetaEntryPtr entry)
|
||||
|
||||
void CacheDownload::start()
|
||||
{
|
||||
m_status = Job_InProgress;
|
||||
if (!m_entry->stale)
|
||||
{
|
||||
m_status = Job_Finished;
|
||||
emit succeeded(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
m_output_file.setFileName(m_target_path);
|
||||
// create a new save file
|
||||
m_output_file.reset(new QSaveFile(m_target_path));
|
||||
|
||||
// if there already is a file and md5 checking is in effect and it can be opened
|
||||
if (!ensureFilePathExists(m_target_path))
|
||||
{
|
||||
QLOG_ERROR() << "Could not create folder for " + m_target_path;
|
||||
m_status = Job_Failed;
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
if (!m_output_file->open(QIODevice::WriteOnly))
|
||||
{
|
||||
QLOG_ERROR() << "Could not open " + m_target_path + " for writing";
|
||||
m_status = Job_Failed;
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
QLOG_INFO() << "Downloading " << m_url.toString();
|
||||
QNetworkRequest request(m_url);
|
||||
if (m_entry->remote_changed_timestamp.size())
|
||||
request.setRawHeader(QString("If-Modified-Since").toLatin1(),
|
||||
m_entry->remote_changed_timestamp.toLatin1());
|
||||
if (m_entry->etag.size())
|
||||
request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1());
|
||||
|
||||
// check file consistency first.
|
||||
QFile current(m_target_path);
|
||||
if(current.exists() && current.size() != 0)
|
||||
{
|
||||
if (m_entry->remote_changed_timestamp.size())
|
||||
request.setRawHeader(QString("If-Modified-Since").toLatin1(),
|
||||
m_entry->remote_changed_timestamp.toLatin1());
|
||||
if (m_entry->etag.size())
|
||||
request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1());
|
||||
}
|
||||
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
|
||||
|
||||
@ -77,76 +96,74 @@ void CacheDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||
void CacheDownload::downloadError(QNetworkReply::NetworkError error)
|
||||
{
|
||||
// error happened during download.
|
||||
QLOG_ERROR() << "Failed" << m_url.toString() << "with reason" << error;
|
||||
QLOG_ERROR() << "Failed " << m_url.toString() << " with reason " << error;
|
||||
m_status = Job_Failed;
|
||||
}
|
||||
void CacheDownload::downloadFinished()
|
||||
{
|
||||
// if the download succeeded
|
||||
if (m_status != Job_Failed)
|
||||
if (m_status == Job_Failed)
|
||||
{
|
||||
|
||||
// nothing went wrong...
|
||||
m_status = Job_Finished;
|
||||
if (m_output_file.isOpen())
|
||||
{
|
||||
// save the data to the downloadable if we aren't saving to file
|
||||
m_output_file.close();
|
||||
m_entry->md5sum = md5sum.result().toHex().constData();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_output_file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
m_entry->md5sum =
|
||||
QCryptographicHash::hash(m_output_file.readAll(), QCryptographicHash::Md5)
|
||||
.toHex()
|
||||
.constData();
|
||||
m_output_file.close();
|
||||
}
|
||||
}
|
||||
QFileInfo output_file_info(m_target_path);
|
||||
|
||||
m_entry->etag = m_reply->rawHeader("ETag").constData();
|
||||
if (m_reply->hasRawHeader("Last-Modified"))
|
||||
{
|
||||
m_entry->remote_changed_timestamp = m_reply->rawHeader("Last-Modified").constData();
|
||||
}
|
||||
m_entry->local_changed_timestamp =
|
||||
output_file_info.lastModified().toUTC().toMSecsSinceEpoch();
|
||||
m_entry->stale = false;
|
||||
MMC->metacache()->updateEntry(m_entry);
|
||||
|
||||
m_reply.reset();
|
||||
emit succeeded(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
// else the download failed
|
||||
else
|
||||
{
|
||||
m_output_file.close();
|
||||
m_output_file.remove();
|
||||
m_output_file->cancelWriting();
|
||||
m_reply.reset();
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CacheDownload::downloadReadyRead()
|
||||
{
|
||||
if (!m_output_file.isOpen())
|
||||
// if we wrote any data to the save file, we try to commit the data to the real file.
|
||||
if (wroteAnyData)
|
||||
{
|
||||
if (!m_output_file.open(QIODevice::WriteOnly))
|
||||
// nothing went wrong...
|
||||
if (m_output_file->commit())
|
||||
{
|
||||
/*
|
||||
* Can't open the file... the job failed
|
||||
*/
|
||||
m_reply->abort();
|
||||
m_status = Job_Finished;
|
||||
m_entry->md5sum = md5sum.result().toHex().constData();
|
||||
}
|
||||
else
|
||||
{
|
||||
QLOG_ERROR() << "Failed to commit changes to " << m_target_path;
|
||||
m_output_file->cancelWriting();
|
||||
m_reply.reset();
|
||||
m_status = Job_Failed;
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status = Job_Finished;
|
||||
}
|
||||
|
||||
// then get rid of the save file
|
||||
m_output_file.reset();
|
||||
|
||||
QFileInfo output_file_info(m_target_path);
|
||||
|
||||
m_entry->etag = m_reply->rawHeader("ETag").constData();
|
||||
if (m_reply->hasRawHeader("Last-Modified"))
|
||||
{
|
||||
m_entry->remote_changed_timestamp = m_reply->rawHeader("Last-Modified").constData();
|
||||
}
|
||||
m_entry->local_changed_timestamp =
|
||||
output_file_info.lastModified().toUTC().toMSecsSinceEpoch();
|
||||
m_entry->stale = false;
|
||||
MMC->metacache()->updateEntry(m_entry);
|
||||
|
||||
m_reply.reset();
|
||||
emit succeeded(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
|
||||
void CacheDownload::downloadReadyRead()
|
||||
{
|
||||
QByteArray ba = m_reply->readAll();
|
||||
md5sum.addData(ba);
|
||||
m_output_file.write(ba);
|
||||
if (m_output_file->write(ba) != ba.size())
|
||||
{
|
||||
QLOG_ERROR() << "Failed writing into " + m_target_path;
|
||||
m_status = Job_Failed;
|
||||
m_reply->abort();
|
||||
emit failed(m_index_within_job);
|
||||
}
|
||||
wroteAnyData = true;
|
||||
}
|
||||
|
@ -17,29 +17,34 @@
|
||||
|
||||
#include "NetAction.h"
|
||||
#include "HttpMetaCache.h"
|
||||
#include <QFile>
|
||||
#include <qcryptographichash.h>
|
||||
#include <QCryptographicHash>
|
||||
#include <QSaveFile>
|
||||
|
||||
typedef std::shared_ptr<class CacheDownload> CacheDownloadPtr;
|
||||
class CacheDownload : public NetAction
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
private:
|
||||
MetaEntryPtr m_entry;
|
||||
/// if saving to file, use the one specified in this string
|
||||
QString m_target_path;
|
||||
/// this is the output file, if any
|
||||
QFile m_output_file;
|
||||
std::shared_ptr<QSaveFile> m_output_file;
|
||||
/// the hash-as-you-download
|
||||
QCryptographicHash md5sum;
|
||||
|
||||
bool wroteAnyData = false;
|
||||
|
||||
public:
|
||||
explicit CacheDownload(QUrl url, MetaEntryPtr entry);
|
||||
static CacheDownloadPtr make(QUrl url, MetaEntryPtr entry)
|
||||
{
|
||||
return CacheDownloadPtr(new CacheDownload(url, entry));
|
||||
}
|
||||
|
||||
QString getTargetFilepath()
|
||||
{
|
||||
return m_target_path;
|
||||
}
|
||||
protected
|
||||
slots:
|
||||
virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
|
@ -31,4 +31,6 @@ const QString SKINS_BASE("skins.minecraft.net/MinecraftSkins/");
|
||||
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");
|
||||
const QString MOJANG_STATUS_URL("http://status.mojang.com/check");
|
||||
const QString MOJANG_STATUS_NEWS_URL("http://status.mojang.com/news");
|
||||
}
|
||||
|
137
logic/status/StatusChecker.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
/* 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 "StatusChecker.h"
|
||||
|
||||
#include <logic/net/URLConstants.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDomDocument>
|
||||
|
||||
#include <logger/QsLog.h>
|
||||
|
||||
StatusChecker::StatusChecker()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void StatusChecker::reloadStatus()
|
||||
{
|
||||
if (isLoadingStatus())
|
||||
{
|
||||
// QLOG_INFO() << "Ignored request to reload status. Currently reloading already.";
|
||||
return;
|
||||
}
|
||||
|
||||
// QLOG_INFO() << "Reloading status.";
|
||||
|
||||
NetJob* job = new NetJob("Status JSON");
|
||||
job->addNetAction(ByteArrayDownload::make(URLConstants::MOJANG_STATUS_URL));
|
||||
QObject::connect(job, &NetJob::succeeded, this, &StatusChecker::statusDownloadFinished);
|
||||
QObject::connect(job, &NetJob::failed, this, &StatusChecker::statusDownloadFailed);
|
||||
m_statusNetJob.reset(job);
|
||||
job->start();
|
||||
}
|
||||
|
||||
void StatusChecker::statusDownloadFinished()
|
||||
{
|
||||
QLOG_DEBUG() << "Finished loading status JSON.";
|
||||
|
||||
QByteArray data;
|
||||
{
|
||||
ByteArrayDownloadPtr dl = std::dynamic_pointer_cast<ByteArrayDownload>(m_statusNetJob->first());
|
||||
data = dl->m_data;
|
||||
m_statusNetJob.reset();
|
||||
}
|
||||
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
|
||||
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
fail("Error parsing status JSON:" + jsonError.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jsonDoc.isArray())
|
||||
{
|
||||
fail("Error parsing status JSON: JSON root is not an array");
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonArray root = jsonDoc.array();
|
||||
|
||||
for(auto status = root.begin(); status != root.end(); ++status)
|
||||
{
|
||||
QVariantMap map = (*status).toObject().toVariantMap();
|
||||
|
||||
for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
|
||||
{
|
||||
QString key = iter.key();
|
||||
QVariant value = iter.value();
|
||||
|
||||
if(value.type() == QVariant::Type::String)
|
||||
{
|
||||
m_statusEntries.insert(key, value.toString());
|
||||
//QLOG_DEBUG() << "Status JSON object: " << key << m_statusEntries[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
fail("Malformed status JSON: expected status type to be a string.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
succeed();
|
||||
}
|
||||
|
||||
void StatusChecker::statusDownloadFailed()
|
||||
{
|
||||
fail("Failed to load status JSON.");
|
||||
}
|
||||
|
||||
|
||||
QMap<QString, QString> StatusChecker::getStatusEntries() const
|
||||
{
|
||||
return m_statusEntries;
|
||||
}
|
||||
|
||||
bool StatusChecker::isLoadingStatus() const
|
||||
{
|
||||
return m_statusNetJob.get() != nullptr;
|
||||
}
|
||||
|
||||
QString StatusChecker::getLastLoadErrorMsg() const
|
||||
{
|
||||
return m_lastLoadError;
|
||||
}
|
||||
|
||||
void StatusChecker::succeed()
|
||||
{
|
||||
m_lastLoadError = "";
|
||||
QLOG_DEBUG() << "Status loading succeeded.";
|
||||
m_statusNetJob.reset();
|
||||
emit statusLoaded();
|
||||
}
|
||||
|
||||
void StatusChecker::fail(const QString& errorMsg)
|
||||
{
|
||||
m_lastLoadError = errorMsg;
|
||||
QLOG_DEBUG() << "Failed to load status:" << errorMsg;
|
||||
m_statusNetJob.reset();
|
||||
emit statusLoadingFailed(errorMsg);
|
||||
}
|
||||
|
57
logic/status/StatusChecker.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
#include <logic/net/NetJob.h>
|
||||
|
||||
class StatusChecker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
StatusChecker();
|
||||
|
||||
QString getLastLoadErrorMsg() const;
|
||||
|
||||
bool isStatusLoaded() const;
|
||||
|
||||
bool isLoadingStatus() const;
|
||||
|
||||
QMap<QString, QString> getStatusEntries() const;
|
||||
|
||||
void Q_SLOT reloadStatus();
|
||||
|
||||
signals:
|
||||
void statusLoaded();
|
||||
void statusLoadingFailed(QString errorMsg);
|
||||
|
||||
protected slots:
|
||||
void statusDownloadFinished();
|
||||
void statusDownloadFailed();
|
||||
|
||||
protected:
|
||||
QMap<QString, QString> m_statusEntries;
|
||||
NetJobPtr m_statusNetJob;
|
||||
bool m_loadedStatus;
|
||||
QString m_lastLoadError;
|
||||
|
||||
void Q_SLOT succeed();
|
||||
void Q_SLOT fail(const QString& errorMsg);
|
||||
};
|
||||
|
@ -55,7 +55,7 @@ void NotificationChecker::downloadSucceeded(int)
|
||||
{
|
||||
m_entries.clear();
|
||||
|
||||
QFile file(m_download->m_output_file.fileName());
|
||||
QFile file(m_download->getTargetFilepath());
|
||||
if (file.open(QFile::ReadOnly))
|
||||
{
|
||||
QJsonArray root = QJsonDocument::fromJson(file.readAll()).array();
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
UpdateChecker::UpdateChecker()
|
||||
{
|
||||
m_currentChannel = VERSION_CHANNEL;
|
||||
m_channelListUrl = CHANLIST_URL;
|
||||
m_updateChecking = false;
|
||||
m_chanListLoading = false;
|
||||
|
@ -27,7 +27,6 @@ public:
|
||||
UpdateChecker();
|
||||
void checkForUpdate(bool notifyNoUpdate);
|
||||
|
||||
void setCurrentChannel(const QString &channel) { m_currentChannel = channel; }
|
||||
void setChannelListUrl(const QString &url) { m_channelListUrl = url; }
|
||||
|
||||
/*!
|
||||
@ -83,7 +82,6 @@ private:
|
||||
QString m_repoUrl;
|
||||
|
||||
QString m_channelListUrl;
|
||||
QString m_currentChannel;
|
||||
|
||||
QList<ChannelListEntry> m_channels;
|
||||
|
||||
|
6
main.cpp
@ -4,6 +4,7 @@
|
||||
int main_gui(MultiMC &app)
|
||||
{
|
||||
// show main window
|
||||
QIcon::setThemeName("multimc");
|
||||
MainWindow mainWin;
|
||||
mainWin.restoreState(QByteArray::fromBase64(MMC->settings()->get("MainWindowState").toByteArray()));
|
||||
mainWin.restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray()));
|
||||
@ -18,8 +19,9 @@ int main(int argc, char *argv[])
|
||||
// initialize Qt
|
||||
MultiMC app(argc, argv);
|
||||
|
||||
Q_INIT_RESOURCE(graphics);
|
||||
Q_INIT_RESOURCE(generated);
|
||||
Q_INIT_RESOURCE(instances);
|
||||
Q_INIT_RESOURCE(multimc);
|
||||
Q_INIT_RESOURCE(backgrounds);
|
||||
|
||||
switch (app.status())
|
||||
{
|
||||
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
@ -1,12 +0,0 @@
|
||||
[Icon Theme]
|
||||
Name=MultiMC
|
||||
Comment=MultiMC Default Icons
|
||||
Inherits=default
|
||||
Directories=scalable/apps
|
||||
|
||||
[scalable/apps]
|
||||
Size=48
|
||||
Type=scalable
|
||||
MinSize=1
|
||||
MaxSize=512
|
||||
Context=Applications
|
6
resources/backgrounds/backgrounds.qrc
Normal file
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0">
|
||||
<qresource prefix="/backgrounds">
|
||||
<file alias="kitteh">catbgrnd2.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 811 B |
Before Width: | Height: | Size: 297 B |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 713 B After Width: | Height: | Size: 713 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 708 B After Width: | Height: | Size: 708 B |
Before Width: | Height: | Size: 482 B After Width: | Height: | Size: 482 B |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 978 B After Width: | Height: | Size: 978 B |
Before Width: | Height: | Size: 618 B After Width: | Height: | Size: 618 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
35
resources/instances/instances.qrc
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0">
|
||||
<qresource prefix="/icons/instances">
|
||||
<!-- Source: Mojang -->
|
||||
<file alias="brick">brick.png</file>
|
||||
<file alias="diamond">diamond.png</file>
|
||||
<file alias="dirt">dirt.png</file>
|
||||
<file alias="gold">gold.png</file>
|
||||
<file alias="grass">grass.png</file>
|
||||
<file alias="stone">stone.png</file>
|
||||
<file alias="tnt">tnt.png</file>
|
||||
<file alias="iron">iron.png</file>
|
||||
<file alias="planks">planks.png</file>
|
||||
|
||||
<!-- Source: Unknown -->
|
||||
<file alias="derp">derp.png</file>
|
||||
<file alias="enderman">enderman.png</file>
|
||||
|
||||
<!-- Our own. -->
|
||||
<file alias="chicken">chicken128.png</file>
|
||||
<file alias="creeper">creeper128.png</file>
|
||||
<file alias="enderpearl">enderpearl128.png</file>
|
||||
<file alias="ftb-glow">ftb_glow128.png</file>
|
||||
<file alias="ftb-logo">ftb_logo128.png</file>
|
||||
<file alias="gear">gear128.png</file>
|
||||
<file alias="herobrine">herobrine128.png</file>
|
||||
<file alias="infinity">infinity128.png</file>
|
||||
<file alias="magitech">magitech128.png</file>
|
||||
<file alias="meat">meat128.png</file>
|
||||
<file alias="netherstar">netherstar128.png</file>
|
||||
<file alias="skeleton">skeleton128.png</file>
|
||||
<file alias="squarecreeper">squarecreeper128.png</file>
|
||||
<file alias="steve">steve128.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
Before Width: | Height: | Size: 532 B After Width: | Height: | Size: 532 B |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |