Merge branch 'feature_profiling' into integration_json_and_tools

This commit is contained in:
Petr Mrázek 2014-02-24 00:29:13 +01:00
commit f7c97efcf3
24 changed files with 1058 additions and 62 deletions

View File

@ -500,6 +500,18 @@ logic/assets/AssetsMigrateTask.h
logic/assets/AssetsMigrateTask.cpp logic/assets/AssetsMigrateTask.cpp
logic/assets/AssetsUtils.h logic/assets/AssetsUtils.h
logic/assets/AssetsUtils.cpp logic/assets/AssetsUtils.cpp
# Tools
logic/tools/BaseExternalTool.h
logic/tools/BaseExternalTool.cpp
logic/tools/MCEditTool.h
logic/tools/MCEditTool.cpp
logic/tools/BaseProfiler.h
logic/tools/BaseProfiler.cpp
logic/tools/JProfiler.h
logic/tools/JProfiler.cpp
logic/tools/JVisualVM.h
logic/tools/JVisualVM.cpp
) )

View File

@ -32,6 +32,10 @@
#include "logic/updater/UpdateChecker.h" #include "logic/updater/UpdateChecker.h"
#include "logic/updater/NotificationChecker.h" #include "logic/updater/NotificationChecker.h"
#include "logic/tools/JProfiler.h"
#include "logic/tools/JVisualVM.h"
#include "logic/tools/MCEditTool.h"
#include "pathutils.h" #include "pathutils.h"
#include "cmdutils.h" #include "cmdutils.h"
#include <inisettingsobject.h> #include <inisettingsobject.h>
@ -42,8 +46,9 @@
using namespace Util::Commandline; using namespace Util::Commandline;
MultiMC::MultiMC(int &argc, char **argv, bool root_override) MultiMC::MultiMC(int &argc, char **argv, bool root_override)
: QApplication(argc, argv), m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_HOTFIX, : QApplication(argc, argv),
VERSION_BUILD, MultiMCVersion::VERSION_TYPE, VERSION_CHANNEL, BUILD_PLATFORM} m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_HOTFIX, VERSION_BUILD,
MultiMCVersion::VERSION_TYPE, VERSION_CHANNEL, BUILD_PLATFORM}
{ {
setOrganizationName("MultiMC"); setOrganizationName("MultiMC");
setApplicationName("MultiMC5"); setApplicationName("MultiMC5");
@ -212,6 +217,21 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
// init proxy settings // init proxy settings
updateProxySettings(); updateProxySettings();
m_profilers.insert("jprofiler",
std::shared_ptr<BaseProfilerFactory>(new JProfilerFactory()));
m_profilers.insert("jvisualvm",
std::shared_ptr<BaseProfilerFactory>(new JVisualVMFactory()));
for (auto profiler : m_profilers.values())
{
profiler->registerSettings(m_settings.get());
}
m_tools.insert("mcedit",
std::shared_ptr<BaseDetachedToolFactory>(new MCEditFactory()));
for (auto tool : m_tools.values())
{
tool->registerSettings(m_settings.get());
}
// launch instance, if that's what should be done // launch instance, if that's what should be done
// WARNING: disabled until further notice // WARNING: disabled until further notice
/* /*

View File

@ -23,6 +23,8 @@ class UpdateChecker;
class NotificationChecker; class NotificationChecker;
class NewsChecker; class NewsChecker;
class StatusChecker; class StatusChecker;
class BaseProfilerFactory;
class BaseDetachedToolFactory;
#if defined(MMC) #if defined(MMC)
#undef MMC #undef MMC
@ -130,6 +132,15 @@ public:
std::shared_ptr<JavaVersionList> javalist(); std::shared_ptr<JavaVersionList> javalist();
QMap<QString, std::shared_ptr<BaseProfilerFactory>> profilers()
{
return m_profilers;
}
QMap<QString, std::shared_ptr<BaseDetachedToolFactory>> tools()
{
return m_tools;
}
void installUpdates(const QString updateFilesDir, UpdateFlags flags = None); void installUpdates(const QString updateFilesDir, UpdateFlags flags = None);
/*! /*!
@ -202,6 +213,8 @@ private:
std::shared_ptr<LiteLoaderVersionList> m_liteloaderlist; std::shared_ptr<LiteLoaderVersionList> m_liteloaderlist;
std::shared_ptr<MinecraftVersionList> m_minecraftlist; std::shared_ptr<MinecraftVersionList> m_minecraftlist;
std::shared_ptr<JavaVersionList> m_javalist; std::shared_ptr<JavaVersionList> m_javalist;
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
QMap<QString, std::shared_ptr<BaseDetachedToolFactory>> m_tools;
QsLogging::DestinationPtr m_fileDestination; QsLogging::DestinationPtr m_fileDestination;
QsLogging::DestinationPtr m_debugDestination; QsLogging::DestinationPtr m_debugDestination;

View File

@ -29,7 +29,8 @@ public class EntryPoint
private enum Action private enum Action
{ {
Proceed, Proceed,
Launch Launch,
Abort
} }
public static void main(String[] args) public static void main(String[] args)
@ -61,27 +62,40 @@ public class EntryPoint
private Action parseLine(String inData) throws ParseException private Action parseLine(String inData) throws ParseException
{ {
String[] pair = inData.split(" ", 2); String[] pair = inData.split(" ", 2);
if(pair.length == 1)
{
String command = pair[0];
if (pair[0].equals("launch"))
return Action.Launch;
else if (pair[0].equals("abort"))
return Action.Abort;
else throw new ParseException();
}
if(pair.length != 2) if(pair.length != 2)
throw new ParseException(); throw new ParseException();
String command = pair[0]; String command = pair[0];
String param = pair[1]; String param = pair[1];
if(command.equals("launch")) if(command.equals("launcher"))
{ {
if(param.equals("legacy")) if(param.equals("legacy"))
{ {
m_launcher = new LegacyLauncher(); m_launcher = new LegacyLauncher();
Utils.log("Using legacy launcher."); Utils.log("Using legacy launcher.");
Utils.log(); Utils.log();
return Action.Launch; return Action.Proceed;
} }
if(param.equals("onesix")) if(param.equals("onesix"))
{ {
m_launcher = new OneSixLauncher(); m_launcher = new OneSixLauncher();
Utils.log("Using onesix launcher."); Utils.log("Using onesix launcher.");
Utils.log(); Utils.log();
return Action.Launch; return Action.Proceed;
} }
else else
throw new ParseException(); throw new ParseException();
@ -105,6 +119,7 @@ public class EntryPoint
return 1; return 1;
} }
boolean isListening = true; boolean isListening = true;
boolean isAborted = false;
// Main loop // Main loop
while (isListening) while (isListening)
{ {
@ -115,7 +130,13 @@ public class EntryPoint
inData = buffer.readLine(); inData = buffer.readLine();
if (inData != null) if (inData != null)
{ {
if(parseLine(inData) == Action.Launch) Action a = parseLine(inData);
if(a == Action.Abort)
{
isListening = false;
isAborted = true;
}
if(a == Action.Launch)
{ {
isListening = false; isListening = false;
} }
@ -134,6 +155,11 @@ public class EntryPoint
return 1; return 1;
} }
} }
if(isAborted)
{
System.err.println("Launch aborted by MultiMC.");
return 1;
}
if(m_launcher != null) if(m_launcher != null)
{ {
return m_launcher.launch(m_params); return m_launcher.launch(m_params);

View File

@ -33,6 +33,7 @@
#include <QLabel> #include <QLabel>
#include <QToolButton> #include <QToolButton>
#include <QWidgetAction> #include <QWidgetAction>
#include <QProgressDialog>
#include "osutils.h" #include "osutils.h"
#include "userutils.h" #include "userutils.h"
@ -97,6 +98,8 @@
#include <logic/updater/NotificationChecker.h> #include <logic/updater/NotificationChecker.h>
#include <logic/tasks/ThreadTask.h> #include <logic/tasks/ThreadTask.h>
#include "logic/tools/BaseProfiler.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{ {
MultiMCPlatform::fixWM_CLASS(this); MultiMCPlatform::fixWM_CLASS(this);
@ -357,6 +360,51 @@ void MainWindow::showInstanceContextMenu(const QPoint &pos)
myMenu.exec(view->mapToGlobal(pos)); myMenu.exec(view->mapToGlobal(pos));
} }
void MainWindow::updateToolsMenu()
{
if (ui->actionLaunchInstance->menu())
{
ui->actionLaunchInstance->menu()->deleteLater();
}
QMenu *launchMenu = new QMenu(this);
QAction *normalLaunch = launchMenu->addAction(tr("Launch"));
connect(normalLaunch, &QAction::triggered, [this](){doLaunch();});
launchMenu->addSeparator()->setText(tr("Profilers"));
for (auto profiler : MMC->profilers().values())
{
QAction *profilerAction = launchMenu->addAction(profiler->name());
QString error;
if (!profiler->check(&error))
{
profilerAction->setDisabled(true);
profilerAction->setToolTip(tr("Profiler not setup correctly. Go into settings, \"External Tools\"."));
}
else
{
connect(profilerAction, &QAction::triggered, [this, profiler](){doLaunch(true, profiler.get());});
}
}
launchMenu->addSeparator()->setText(tr("Tools"));
for (auto tool : MMC->tools().values())
{
QAction *toolAction = launchMenu->addAction(tool->name());
QString error;
if (!tool->check(&error))
{
toolAction->setDisabled(true);
toolAction->setToolTip(tr("Tool not setup correctly. Go into settings, \"External Tools\"."));
}
else
{
connect(toolAction, &QAction::triggered, [this, tool]()
{
tool->createDetachedTool(m_selectedInstance, this)->run();
});
}
}
ui->actionLaunchInstance->setMenu(launchMenu);
}
void MainWindow::repopulateAccountsMenu() void MainWindow::repopulateAccountsMenu()
{ {
accountMenu->clear(); accountMenu->clear();
@ -930,6 +978,7 @@ void MainWindow::on_actionSettings_triggered()
// FIXME: quick HACK to make this work. improve, optimize. // FIXME: quick HACK to make this work. improve, optimize.
proxymodel->invalidate(); proxymodel->invalidate();
proxymodel->sort(0); proxymodel->sort(0);
updateToolsMenu();
} }
void MainWindow::on_actionManageAccounts_triggered() void MainWindow::on_actionManageAccounts_triggered()
@ -1078,7 +1127,7 @@ void MainWindow::on_actionLaunchInstanceOffline_triggered()
} }
} }
void MainWindow::doLaunch(bool online) void MainWindow::doLaunch(bool online, BaseProfilerFactory *profiler)
{ {
if (!m_selectedInstance) if (!m_selectedInstance)
return; return;
@ -1194,11 +1243,11 @@ void MainWindow::doLaunch(bool online)
// update first if the server actually responded // update first if the server actually responded
if (session->auth_server_online) if (session->auth_server_online)
{ {
updateInstance(m_selectedInstance, session); updateInstance(m_selectedInstance, session, profiler);
} }
else else
{ {
launchInstance(m_selectedInstance, session); launchInstance(m_selectedInstance, session, profiler);
} }
tryagain = false; tryagain = false;
} }
@ -1206,22 +1255,22 @@ void MainWindow::doLaunch(bool online)
} }
} }
void MainWindow::updateInstance(BaseInstance *instance, AuthSessionPtr session) void MainWindow::updateInstance(BaseInstance *instance, AuthSessionPtr session, BaseProfilerFactory *profiler)
{ {
auto updateTask = instance->doUpdate(); auto updateTask = instance->doUpdate();
if (!updateTask) if (!updateTask)
{ {
launchInstance(instance, session); launchInstance(instance, session, profiler);
return; return;
} }
ProgressDialog tDialog(this); ProgressDialog tDialog(this);
connect(updateTask.get(), &Task::succeeded, [this, instance, session] connect(updateTask.get(), &Task::succeeded, [this, instance, session, profiler]
{ launchInstance(instance, session); }); { launchInstance(instance, session, profiler); });
connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
tDialog.exec(updateTask.get()); tDialog.exec(updateTask.get());
} }
void MainWindow::launchInstance(BaseInstance *instance, AuthSessionPtr session) void MainWindow::launchInstance(BaseInstance *instance, AuthSessionPtr session, BaseProfilerFactory *profiler)
{ {
Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL"); Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL");
Q_ASSERT_X(session.get() != nullptr, "launchInstance", "session is NULL"); Q_ASSERT_X(session.get() != nullptr, "launchInstance", "session is NULL");
@ -1236,7 +1285,56 @@ void MainWindow::launchInstance(BaseInstance *instance, AuthSessionPtr session)
connect(console, SIGNAL(isClosing()), this, SLOT(instanceEnded())); connect(console, SIGNAL(isClosing()), this, SLOT(instanceEnded()));
proc->setLogin(session); proc->setLogin(session);
proc->arm();
if (profiler)
{
QString error;
if (!profiler->check(&error))
{
QMessageBox::critical(this, tr("Error"), tr("Couldn't start profiler: %1").arg(error));
proc->abort();
return;
}
BaseProfiler *profilerInstance = profiler->createProfiler(instance, this);
QProgressDialog dialog;
dialog.setMinimum(0);
dialog.setMaximum(0);
dialog.setValue(0);
dialog.setLabelText(tr("Waiting for profiler..."));
connect(&dialog, &QProgressDialog::canceled, profilerInstance, &BaseProfiler::abortProfiling);
dialog.show();
connect(profilerInstance, &BaseProfiler::readyToLaunch, [&dialog, this](const QString &message)
{
dialog.accept();
QMessageBox msg;
msg.setText(tr("The launch of Minecraft itself is delayed until you press the "
"button. This is the right time to setup the profiler, as the "
"profiler server is running now.\n\n%1").arg(message));
msg.setWindowTitle(tr("Waiting"));
msg.setIcon(QMessageBox::Information);
msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
msg.exec();
proc->launch(); proc->launch();
});
connect(profilerInstance, &BaseProfiler::abortLaunch, [&dialog, this](const QString &message)
{
dialog.accept();
QMessageBox msg;
msg.setText(tr("Couldn't start the profiler: %1").arg(message));
msg.setWindowTitle(tr("Error"));
msg.setIcon(QMessageBox::Critical);
msg.addButton(QMessageBox::Ok);
msg.exec();
proc->abort();
});
profilerInstance->beginProfiling(proc);
dialog.exec();
}
else
{
proc->launch();
}
} }
void MainWindow::onGameUpdateError(QString error) void MainWindow::onGameUpdateError(QString error)
@ -1377,6 +1475,8 @@ void MainWindow::instanceChanged(const QModelIndex &current, const QModelIndex &
m_statusLeft->setText(m_selectedInstance->getStatusbarDescription()); m_statusLeft->setText(m_selectedInstance->getStatusbarDescription());
updateInstanceToolIcon(m_selectedInstance->iconKey()); updateInstanceToolIcon(m_selectedInstance->iconKey());
updateToolsMenu();
MMC->settings()->set("SelectedInstance", m_selectedInstance->id()); MMC->settings()->set("SelectedInstance", m_selectedInstance->id());
} }
else else

View File

@ -29,6 +29,7 @@ class LabeledToolButton;
class QLabel; class QLabel;
class MinecraftProcess; class MinecraftProcess;
class ConsoleWindow; class ConsoleWindow;
class BaseProfilerFactory;
namespace Ui namespace Ui
{ {
@ -111,18 +112,18 @@ slots:
* Launches the currently selected instance with the default account. * Launches the currently selected instance with the default account.
* If no default account is selected, prompts the user to pick an account. * If no default account is selected, prompts the user to pick an account.
*/ */
void doLaunch(bool online = true); void doLaunch(bool online = true, BaseProfilerFactory *profiler = 0);
/*! /*!
* Launches the given instance with the given account. * Launches the given instance with the given account.
* This function assumes that the given account has a valid, usable access token. * This function assumes that the given account has a valid, usable access token.
*/ */
void launchInstance(BaseInstance *instance, AuthSessionPtr session); void launchInstance(BaseInstance *instance, AuthSessionPtr session, BaseProfilerFactory *profiler = 0);
/*! /*!
* Prepares the given instance for launch with the given account. * Prepares the given instance for launch with the given account.
*/ */
void updateInstance(BaseInstance *instance, AuthSessionPtr account); void updateInstance(BaseInstance *instance, AuthSessionPtr account, BaseProfilerFactory *profiler = 0);
void onGameUpdateError(QString error); void onGameUpdateError(QString error);
@ -140,6 +141,8 @@ slots:
void showInstanceContextMenu(const QPoint&); void showInstanceContextMenu(const QPoint&);
void updateToolsMenu();
public public
slots: slots:
void instanceActivated(QModelIndex); void instanceActivated(QModelIndex);

View File

@ -531,9 +531,8 @@
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources> <resources>
<include location="../resources/instances/instances.qrc"/>
<include location="../resources/multimc/multimc.qrc"/> <include location="../resources/multimc/multimc.qrc"/>
<include location="../resources/backgrounds/backgrounds.qrc"/> <include location="../resources/instances/instances.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -29,6 +29,8 @@
#include "logic/updater/UpdateChecker.h" #include "logic/updater/UpdateChecker.h"
#include "logic/tools/BaseProfiler.h"
#include <settingsobject.h> #include <settingsobject.h>
#include <pathutils.h> #include <pathutils.h>
#include <QFileDialog> #include <QFileDialog>
@ -46,12 +48,14 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::Se
ui->jsonEditorTextBox->setClearButtonEnabled(true); ui->jsonEditorTextBox->setClearButtonEnabled(true);
#endif #endif
restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("SettingsGeometry").toByteArray())); restoreGeometry(
QByteArray::fromBase64(MMC->settings()->get("SettingsGeometry").toByteArray()));
loadSettings(MMC->settings().get()); loadSettings(MMC->settings().get());
updateCheckboxStuff(); updateCheckboxStuff();
QObject::connect(MMC->updateChecker().get(), &UpdateChecker::channelListLoaded, this, &SettingsDialog::refreshUpdateChannelList); QObject::connect(MMC->updateChecker().get(), &UpdateChecker::channelListLoaded, this,
&SettingsDialog::refreshUpdateChannelList);
if (MMC->updateChecker()->hasChannels()) if (MMC->updateChecker()->hasChannels())
{ {
@ -62,6 +66,9 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::Se
MMC->updateChecker()->updateChanList(); MMC->updateChecker()->updateChanList();
} }
connect(ui->proxyGroup, SIGNAL(buttonClicked(int)), SLOT(proxyChanged(int))); connect(ui->proxyGroup, SIGNAL(buttonClicked(int)), SLOT(proxyChanged(int)));
ui->mceditLink->setOpenExternalLinks(true);
ui->jvisualvmLink->setOpenExternalLinks(true);
ui->jprofilerLink->setOpenExternalLinks(true);
} }
SettingsDialog::~SettingsDialog() SettingsDialog::~SettingsDialog()
@ -84,8 +91,10 @@ void SettingsDialog::updateCheckboxStuff()
{ {
ui->windowWidthSpinBox->setEnabled(!ui->maximizedCheckBox->isChecked()); ui->windowWidthSpinBox->setEnabled(!ui->maximizedCheckBox->isChecked());
ui->windowHeightSpinBox->setEnabled(!ui->maximizedCheckBox->isChecked()); ui->windowHeightSpinBox->setEnabled(!ui->maximizedCheckBox->isChecked());
ui->proxyAddrBox->setEnabled(!ui->proxyNoneBtn->isChecked() && !ui->proxyDefaultBtn->isChecked()); ui->proxyAddrBox->setEnabled(!ui->proxyNoneBtn->isChecked() &&
ui->proxyAuthBox->setEnabled(!ui->proxyNoneBtn->isChecked() && !ui->proxyDefaultBtn->isChecked()); !ui->proxyDefaultBtn->isChecked());
ui->proxyAuthBox->setEnabled(!ui->proxyNoneBtn->isChecked() &&
!ui->proxyDefaultBtn->isChecked());
} }
void SettingsDialog::on_ftbLauncherBrowseBtn_clicked() void SettingsDialog::on_ftbLauncherBrowseBtn_clicked()
@ -103,8 +112,8 @@ void SettingsDialog::on_ftbLauncherBrowseBtn_clicked()
void SettingsDialog::on_ftbBrowseBtn_clicked() void SettingsDialog::on_ftbBrowseBtn_clicked()
{ {
QString raw_dir = QFileDialog::getExistingDirectory(this, tr("FTB Directory"), QString raw_dir =
ui->ftbBox->text()); QFileDialog::getExistingDirectory(this, tr("FTB Directory"), ui->ftbBox->text());
QString cooked_dir = NormalizePath(raw_dir); QString cooked_dir = NormalizePath(raw_dir);
// do not allow current dir - it's dirty. Do not allow dirs that don't exist // do not allow current dir - it's dirty. Do not allow dirs that don't exist
@ -184,14 +193,14 @@ void SettingsDialog::on_jsonEditorBrowseBtn_clicked()
} }
// it has to exist and be an executable // it has to exist and be an executable
if (QFileInfo(cooked_file).exists() && if (QFileInfo(cooked_file).exists() && QFileInfo(cooked_file).isExecutable())
QFileInfo(cooked_file).isExecutable())
{ {
ui->jsonEditorTextBox->setText(cooked_file); ui->jsonEditorTextBox->setText(cooked_file);
} }
else else
{ {
QMessageBox::warning(this, tr("Invalid"), tr("The file chosen does not seem to be an executable")); QMessageBox::warning(this, tr("Invalid"),
tr("The file chosen does not seem to be an executable"));
} }
} }
@ -223,9 +232,11 @@ void SettingsDialog::proxyChanged(int)
void SettingsDialog::refreshUpdateChannelList() void SettingsDialog::refreshUpdateChannelList()
{ {
// Stop listening for selection changes. It's going to change a lot while we update it and we don't need to update the // Stop listening for selection changes. It's going to change a lot while we update it and
// we don't need to update the
// description label constantly. // description label constantly.
QObject::disconnect(ui->updateChannelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateChannelSelectionChanged(int))); QObject::disconnect(ui->updateChannelComboBox, SIGNAL(currentIndexChanged(int)), this,
SLOT(updateChannelSelectionChanged(int)));
QList<UpdateChecker::ChannelListEntry> channelList = MMC->updateChecker()->getChannelList(); QList<UpdateChecker::ChannelListEntry> channelList = MMC->updateChecker()->getChannelList();
ui->updateChannelComboBox->clear(); ui->updateChannelComboBox->clear();
@ -234,13 +245,16 @@ void SettingsDialog::refreshUpdateChannelList()
{ {
UpdateChecker::ChannelListEntry entry = channelList.at(i); UpdateChecker::ChannelListEntry entry = channelList.at(i);
// When it comes to selection, we'll rely on the indexes of a channel entry being the same in the // When it comes to selection, we'll rely on the indexes of a channel entry being the
// same in the
// combo box as it is in the update checker's channel list. // combo box as it is in the update checker's channel list.
// This probably isn't very safe, but the channel list doesn't change often enough (or at all) for // This probably isn't very safe, but the channel list doesn't change often enough (or
// at all) for
// this to be a big deal. Hope it doesn't break... // this to be a big deal. Hope it doesn't break...
ui->updateChannelComboBox->addItem(entry.name); ui->updateChannelComboBox->addItem(entry.name);
// If the update channel we just added was the selected one, set the current index in the combo box to it. // If the update channel we just added was the selected one, set the current index in
// the combo box to it.
if (entry.id == m_currentUpdateChannel) if (entry.id == m_currentUpdateChannel)
{ {
QLOG_DEBUG() << "Selected index" << i << "channel id" << m_currentUpdateChannel; QLOG_DEBUG() << "Selected index" << i << "channel id" << m_currentUpdateChannel;
@ -251,11 +265,13 @@ void SettingsDialog::refreshUpdateChannelList()
ui->updateChannelComboBox->setCurrentIndex(selection); ui->updateChannelComboBox->setCurrentIndex(selection);
// Start listening for selection changes again and update the description label. // Start listening for selection changes again and update the description label.
QObject::connect(ui->updateChannelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateChannelSelectionChanged(int))); QObject::connect(ui->updateChannelComboBox, SIGNAL(currentIndexChanged(int)), this,
SLOT(updateChannelSelectionChanged(int)));
refreshUpdateChannelDesc(); refreshUpdateChannelDesc();
// Now that we've updated the channel list, we can enable the combo box. // Now that we've updated the channel list, we can enable the combo box.
// It starts off disabled so that if the channel list hasn't been loaded, it will be disabled. // It starts off disabled so that if the channel list hasn't been loaded, it will be
// disabled.
ui->updateChannelComboBox->setEnabled(true); ui->updateChannelComboBox->setEnabled(true);
} }
@ -289,7 +305,8 @@ void SettingsDialog::refreshUpdateChannelDesc()
void SettingsDialog::applySettings(SettingsObject *s) void SettingsDialog::applySettings(SettingsObject *s)
{ {
// Language // Language
s->set("Language", ui->languageBox->itemData(ui->languageBox->currentIndex()).toLocale().bcp47Name()); s->set("Language",
ui->languageBox->itemData(ui->languageBox->currentIndex()).toLocale().bcp47Name());
// Updates // Updates
s->set("AutoUpdate", ui->autoUpdateCheckBox->isChecked()); s->set("AutoUpdate", ui->autoUpdateCheckBox->isChecked());
@ -309,7 +326,8 @@ void SettingsDialog::applySettings(SettingsObject *s)
// Editors // Editors
QString jsonEditor = ui->jsonEditorTextBox->text(); QString jsonEditor = ui->jsonEditorTextBox->text();
if (!jsonEditor.isEmpty() && (!QFileInfo(jsonEditor).exists() || !QFileInfo(jsonEditor).isExecutable())) if (!jsonEditor.isEmpty() &&
(!QFileInfo(jsonEditor).exists() || !QFileInfo(jsonEditor).isExecutable()))
{ {
QString found = QStandardPaths::findExecutable(jsonEditor); QString found = QStandardPaths::findExecutable(jsonEditor);
if (!found.isEmpty()) if (!found.isEmpty())
@ -330,10 +348,14 @@ void SettingsDialog::applySettings(SettingsObject *s)
// Proxy // Proxy
QString proxyType = "None"; QString proxyType = "None";
if (ui->proxyDefaultBtn->isChecked()) proxyType = "Default"; if (ui->proxyDefaultBtn->isChecked())
else if (ui->proxyNoneBtn->isChecked()) proxyType = "None"; proxyType = "Default";
else if (ui->proxySOCKS5Btn->isChecked()) proxyType = "SOCKS5"; else if (ui->proxyNoneBtn->isChecked())
else if (ui->proxyHTTPBtn->isChecked()) proxyType = "HTTP"; proxyType = "None";
else if (ui->proxySOCKS5Btn->isChecked())
proxyType = "SOCKS5";
else if (ui->proxyHTTPBtn->isChecked())
proxyType = "HTTP";
s->set("ProxyType", proxyType); s->set("ProxyType", proxyType);
s->set("ProxyAddr", ui->proxyAddrEdit->text()); s->set("ProxyAddr", ui->proxyAddrEdit->text());
@ -368,6 +390,11 @@ void SettingsDialog::applySettings(SettingsObject *s)
} }
s->set("PostExitCommand", ui->postExitCmdTextBox->text()); s->set("PostExitCommand", ui->postExitCmdTextBox->text());
// Profilers
s->set("JProfilerPath", ui->jprofilerPathEdit->text());
s->set("JVisualVMPath", ui->jvisualvmPathEdit->text());
s->set("MCEditPath", ui->mceditPathEdit->text());
} }
void SettingsDialog::loadSettings(SettingsObject *s) void SettingsDialog::loadSettings(SettingsObject *s)
@ -379,11 +406,10 @@ void SettingsDialog::loadSettings(SettingsObject *s)
QDir(MMC->root() + "/translations").entryList(QStringList() << "*.qm", QDir::Files)) QDir(MMC->root() + "/translations").entryList(QStringList() << "*.qm", QDir::Files))
{ {
QLocale locale(lang.section(QRegExp("[_\.]"), 1)); QLocale locale(lang.section(QRegExp("[_\.]"), 1));
ui->languageBox->addItem( ui->languageBox->addItem(QLocale::languageToString(locale.language()), locale);
QLocale::languageToString(locale.language()),
locale);
} }
ui->languageBox->setCurrentIndex(ui->languageBox->findData(QLocale(s->get("Language").toString()))); ui->languageBox->setCurrentIndex(
ui->languageBox->findData(QLocale(s->get("Language").toString())));
// Updates // Updates
ui->autoUpdateCheckBox->setChecked(s->get("AutoUpdate").toBool()); ui->autoUpdateCheckBox->setChecked(s->get("AutoUpdate").toBool());
@ -430,10 +456,14 @@ void SettingsDialog::loadSettings(SettingsObject *s)
// Proxy // Proxy
QString proxyType = s->get("ProxyType").toString(); QString proxyType = s->get("ProxyType").toString();
if (proxyType == "Default") ui->proxyDefaultBtn->setChecked(true); if (proxyType == "Default")
else if (proxyType == "None") ui->proxyNoneBtn->setChecked(true); ui->proxyDefaultBtn->setChecked(true);
else if (proxyType == "SOCKS5") ui->proxySOCKS5Btn->setChecked(true); else if (proxyType == "None")
else if (proxyType == "HTTP") ui->proxyHTTPBtn->setChecked(true); ui->proxyNoneBtn->setChecked(true);
else if (proxyType == "SOCKS5")
ui->proxySOCKS5Btn->setChecked(true);
else if (proxyType == "HTTP")
ui->proxyHTTPBtn->setChecked(true);
ui->proxyAddrEdit->setText(s->get("ProxyAddr").toString()); ui->proxyAddrEdit->setText(s->get("ProxyAddr").toString());
ui->proxyPortEdit->setValue(s->get("ProxyPort").value<qint16>()); ui->proxyPortEdit->setValue(s->get("ProxyPort").value<qint16>());
@ -447,6 +477,11 @@ void SettingsDialog::loadSettings(SettingsObject *s)
// Custom Commands // Custom Commands
ui->preLaunchCmdTextBox->setText(s->get("PreLaunchCommand").toString()); ui->preLaunchCmdTextBox->setText(s->get("PreLaunchCommand").toString());
ui->postExitCmdTextBox->setText(s->get("PostExitCommand").toString()); ui->postExitCmdTextBox->setText(s->get("PostExitCommand").toString());
// Profilers
ui->jprofilerPathEdit->setText(s->get("JProfilerPath").toString());
ui->jvisualvmPathEdit->setText(s->get("JVisualVMPath").toString());
ui->mceditPathEdit->setText(s->get("MCEditPath").toString());
} }
void SettingsDialog::on_javaDetectBtn_clicked() void SettingsDialog::on_javaDetectBtn_clicked()
@ -503,3 +538,126 @@ void SettingsDialog::checkFinished(JavaCheckResult result)
"or set the path to the java executable.")); "or set the path to the java executable."));
} }
} }
void SettingsDialog::on_jprofilerPathBtn_clicked()
{
QString raw_dir = ui->jprofilerPathEdit->text();
QString error;
do
{
raw_dir = QFileDialog::getExistingDirectory(this, tr("JProfiler Directory"), raw_dir);
if (raw_dir.isEmpty())
{
break;
}
QString cooked_dir = NormalizePath(raw_dir);
if (!MMC->profilers()["jprofiler"]->check(cooked_dir, &error))
{
QMessageBox::critical(this, tr("Error"),
tr("Error while checking JProfiler install:\n%1").arg(error));
continue;
}
else
{
ui->jprofilerPathEdit->setText(cooked_dir);
break;
}
} while (1);
}
void SettingsDialog::on_jprofilerCheckBtn_clicked()
{
QString error;
if (!MMC->profilers()["jprofiler"]->check(ui->jprofilerPathEdit->text(), &error))
{
QMessageBox::critical(this, tr("Error"),
tr("Error while checking JProfiler install:\n%1").arg(error));
}
else
{
QMessageBox::information(this, tr("OK"), tr("JProfiler setup seems to be OK"));
}
}
void SettingsDialog::on_jvisualvmPathBtn_clicked()
{
QString raw_dir = ui->jvisualvmPathEdit->text();
QString error;
do
{
raw_dir = QFileDialog::getOpenFileName(this, tr("JVisualVM Executable"), raw_dir);
if (raw_dir.isEmpty())
{
break;
}
QString cooked_dir = NormalizePath(raw_dir);
if (!MMC->profilers()["jvisualvm"]->check(cooked_dir, &error))
{
QMessageBox::critical(this, tr("Error"),
tr("Error while checking JVisualVM install:\n%1").arg(error));
continue;
}
else
{
ui->jvisualvmPathEdit->setText(cooked_dir);
break;
}
} while (1);
}
void SettingsDialog::on_jvisualvmCheckBtn_clicked()
{
QString error;
if (!MMC->profilers()["jvisualvm"]->check(ui->jvisualvmPathEdit->text(), &error))
{
QMessageBox::critical(this, tr("Error"),
tr("Error while checking JVisualVM install:\n%1").arg(error));
}
else
{
QMessageBox::information(this, tr("OK"), tr("JVisualVM setup seems to be OK"));
}
}
void SettingsDialog::on_mceditPathBtn_clicked()
{
QString raw_dir = ui->mceditPathEdit->text();
QString error;
do
{
#ifdef Q_OS_OSX
#warning stuff
raw_dir = QFileDialog::getOpenFileName(this, tr("MCEdit Application"), raw_dir);
#else
raw_dir = QFileDialog::getExistingDirectory(this, tr("MCEdit Directory"), raw_dir);
#endif
if (raw_dir.isEmpty())
{
break;
}
QString cooked_dir = NormalizePath(raw_dir);
if (!MMC->tools()["mcedit"]->check(cooked_dir, &error))
{
QMessageBox::critical(this, tr("Error"),
tr("Error while checking MCEdit install:\n%1").arg(error));
continue;
}
else
{
ui->mceditPathEdit->setText(cooked_dir);
break;
}
} while (1);
}
void SettingsDialog::on_mceditCheckBtn_clicked()
{
QString error;
if (!MMC->tools()["mcedit"]->check(ui->mceditPathEdit->text(), &error))
{
QMessageBox::critical(this, tr("Error"),
tr("Error while checking MCEdit install:\n%1").arg(error));
}
else
{
QMessageBox::information(this, tr("OK"), tr("MCEdit setup seems to be OK"));
}
}

View File

@ -75,6 +75,13 @@ slots:
void checkFinished(JavaCheckResult result); void checkFinished(JavaCheckResult result);
void on_jprofilerPathBtn_clicked();
void on_jprofilerCheckBtn_clicked();
void on_jvisualvmPathBtn_clicked();
void on_jvisualvmCheckBtn_clicked();
void on_mceditPathBtn_clicked();
void on_mceditCheckBtn_clicked();
/*! /*!
* Updates the list of update channels in the combo box. * Updates the list of update channels in the combo box.
*/ */

View File

@ -863,6 +863,137 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="externalToolsTab">
<attribute name="title">
<string>External Tools</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>JProfiler</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLineEdit" name="jprofilerPathEdit"/>
</item>
<item>
<widget class="QPushButton" name="jprofilerPathBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="jprofilerCheckBtn">
<property name="text">
<string>Check</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="jprofilerLink">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://www.ej-technologies.com/products/jprofiler/overview.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://www.ej-technologies.com/products/jprofiler/overview.html&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>JVisualVM</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="jvisualvmPathEdit"/>
</item>
<item>
<widget class="QPushButton" name="jvisualvmPathBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="jvisualvmCheckBtn">
<property name="text">
<string>Check</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="jvisualvmLink">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://visualvm.java.net/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://visualvm.java.net/&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>MCEdit</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="mceditPathEdit"/>
</item>
<item>
<widget class="QPushButton" name="mceditPathBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="mceditCheckBtn">
<property name="text">
<string>Check</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mceditLink">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://www.mcedit.net/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://www.mcedit.net/&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<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> </widget>
</item> </item>
<item> <item>
@ -938,7 +1069,7 @@
</connection> </connection>
</connections> </connections>
<buttongroups> <buttongroups>
<buttongroup name="proxyGroup"/>
<buttongroup name="sortingModeGroup"/> <buttongroup name="sortingModeGroup"/>
<buttongroup name="proxyGroup"/>
</buttongroups> </buttongroups>
</ui> </ui>

View File

@ -77,7 +77,7 @@ MinecraftProcess *LegacyInstance::prepareForLaunch(AuthSessionPtr account)
launchScript += "windowTitle " + windowTitle() + "\n"; launchScript += "windowTitle " + windowTitle() + "\n";
launchScript += "windowParams " + windowParams + "\n"; launchScript += "windowParams " + windowParams + "\n";
launchScript += "lwjgl " + lwjgl + "\n"; launchScript += "lwjgl " + lwjgl + "\n";
launchScript += "launch legacy\n"; launchScript += "launcher legacy\n";
} }
proc->setLaunchScript(launchScript); proc->setLaunchScript(launchScript);

View File

@ -288,7 +288,7 @@ void MinecraftProcess::killMinecraft()
kill(); kill();
} }
void MinecraftProcess::launch() void MinecraftProcess::arm()
{ {
emit log("MultiMC version: " + MMC->version().toString() + "\n\n"); emit log("MultiMC version: " + MMC->version().toString() + "\n\n");
emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n"); emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n");
@ -374,3 +374,17 @@ void MinecraftProcess::launch()
QByteArray bytes = launchScript.toUtf8(); QByteArray bytes = launchScript.toUtf8();
writeData(bytes.constData(), bytes.length()); writeData(bytes.constData(), bytes.length());
} }
void MinecraftProcess::launch()
{
QString launchString("launch\n");
QByteArray bytes = launchString.toUtf8();
writeData(bytes.constData(), bytes.length());
}
void MinecraftProcess::abort()
{
QString launchString("abort\n");
QByteArray bytes = launchString.toUtf8();
writeData(bytes.constData(), bytes.length());
}

View File

@ -55,10 +55,20 @@ public:
MinecraftProcess(BaseInstance *inst); MinecraftProcess(BaseInstance *inst);
/** /**
* @brief launch minecraft * @brief start the launcher part with the provided launch script
*/
void arm();
/**
* @brief launch the armed instance!
*/ */
void launch(); void launch();
/**
* @brief abort launch!
*/
void abort();
BaseInstance *instance() BaseInstance *instance()
{ {
return m_instance; return m_instance;

View File

@ -230,7 +230,7 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(AuthSessionPtr session)
launchScript += "ext " + finfo.absoluteFilePath() + "\n"; launchScript += "ext " + finfo.absoluteFilePath() + "\n";
} }
launchScript += "natives " + natives_dir.absolutePath() + "\n"; launchScript += "natives " + natives_dir.absolutePath() + "\n";
launchScript += "launch onesix\n"; launchScript += "launcher onesix\n";
// create the process and set its parameters // create the process and set its parameters
MinecraftProcess *proc = new MinecraftProcess(this); MinecraftProcess *proc = new MinecraftProcess(this);

View File

@ -0,0 +1,77 @@
#include "BaseExternalTool.h"
#include <QProcess>
#include <QDir>
#include <QInputDialog>
#ifdef Q_OS_WIN
#include <windows.h>
#endif
#include "logic/BaseInstance.h"
#include "MultiMC.h"
BaseExternalTool::BaseExternalTool(BaseInstance *instance, QObject *parent)
: QObject(parent), m_instance(instance)
{
}
BaseExternalTool::~BaseExternalTool()
{
}
qint64 BaseExternalTool::pid(QProcess *process)
{
#ifdef Q_OS_WIN
struct _PROCESS_INFORMATION *procinfo = process->pid();
return procinfo->dwProcessId;
#else
return process->pid();
#endif
}
QString BaseExternalTool::getSave() const
{
QDir saves(m_instance->minecraftRoot() + "/saves");
QStringList worlds = saves.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QMutableListIterator<QString> it(worlds);
while (it.hasNext())
{
it.next();
if (!QDir(saves.absoluteFilePath(it.value())).exists("level.dat"))
{
it.remove();
}
}
bool ok = true;
const QString save = QInputDialog::getItem(
MMC->activeWindow(), tr("MCEdit"), tr("Choose which world to open:"),
worlds, 0, false, &ok);
if (ok)
{
return saves.absoluteFilePath(save);
}
return QString();
}
BaseDetachedTool::BaseDetachedTool(BaseInstance *instance, QObject *parent)
: BaseExternalTool(instance, parent)
{
}
void BaseDetachedTool::run()
{
runImpl();
}
BaseExternalToolFactory::~BaseExternalToolFactory()
{
}
BaseDetachedTool *BaseDetachedToolFactory::createDetachedTool(BaseInstance *instance, QObject *parent)
{
return qobject_cast<BaseDetachedTool *>(createTool(instance, parent));
}

View File

@ -0,0 +1,57 @@
#pragma once
#include <QObject>
class BaseInstance;
class SettingsObject;
class MinecraftProcess;
class QProcess;
class BaseExternalTool : public QObject
{
Q_OBJECT
public:
explicit BaseExternalTool(BaseInstance *instance, QObject *parent = 0);
virtual ~BaseExternalTool();
protected:
BaseInstance *m_instance;
qint64 pid(QProcess *process);
QString getSave() const;
};
class BaseDetachedTool : public BaseExternalTool
{
Q_OBJECT
public:
explicit BaseDetachedTool(BaseInstance *instance, QObject *parent = 0);
public
slots:
void run();
protected:
virtual void runImpl() = 0;
};
class BaseExternalToolFactory
{
public:
virtual ~BaseExternalToolFactory();
virtual QString name() const = 0;
virtual void registerSettings(SettingsObject *settings) = 0;
virtual BaseExternalTool *createTool(BaseInstance *instance, QObject *parent = 0) = 0;
virtual bool check(QString *error) = 0;
virtual bool check(const QString &path, QString *error) = 0;
};
class BaseDetachedToolFactory : public BaseExternalToolFactory
{
public:
virtual BaseDetachedTool *createDetachedTool(BaseInstance *instance, QObject *parent = 0);
};

View File

@ -0,0 +1,35 @@
#include "BaseProfiler.h"
#include <QProcess>
BaseProfiler::BaseProfiler(BaseInstance *instance, QObject *parent)
: BaseExternalTool(instance, parent)
{
}
void BaseProfiler::beginProfiling(MinecraftProcess *process)
{
beginProfilingImpl(process);
}
void BaseProfiler::abortProfiling()
{
abortProfilingImpl();
}
void BaseProfiler::abortProfilingImpl()
{
if (!m_profilerProcess)
{
return;
}
m_profilerProcess->terminate();
m_profilerProcess->deleteLater();
m_profilerProcess = 0;
emit abortLaunch(tr("Profiler aborted"));
}
BaseProfiler *BaseProfilerFactory::createProfiler(BaseInstance *instance, QObject *parent)
{
return qobject_cast<BaseProfiler *>(createTool(instance, parent));
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "BaseExternalTool.h"
class BaseInstance;
class SettingsObject;
class MinecraftProcess;
class QProcess;
class BaseProfiler : public BaseExternalTool
{
Q_OBJECT
public:
explicit BaseProfiler(BaseInstance *instance, QObject *parent = 0);
public
slots:
void beginProfiling(MinecraftProcess *process);
void abortProfiling();
protected:
QProcess *m_profilerProcess;
virtual void beginProfilingImpl(MinecraftProcess *process) = 0;
virtual void abortProfilingImpl();
signals:
void readyToLaunch(const QString &message);
void abortLaunch(const QString &message);
};
class BaseProfilerFactory : public BaseExternalToolFactory
{
public:
virtual BaseProfiler *createProfiler(BaseInstance *instance, QObject *parent = 0);
};

78
logic/tools/JProfiler.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "JProfiler.h"
#include <QDir>
#include <QMessageBox>
#include "settingsobject.h"
#include "logic/MinecraftProcess.h"
#include "logic/BaseInstance.h"
#include "MultiMC.h"
JProfiler::JProfiler(BaseInstance *instance, QObject *parent) : BaseProfiler(instance, parent)
{
}
void JProfiler::beginProfilingImpl(MinecraftProcess *process)
{
int port = MMC->settings()->get("JProfilerPort").toInt();
QProcess *profiler = new QProcess(this);
profiler->setArguments(QStringList() << "-d" << QString::number(pid(process)) << "--gui"
<< "-p" << QString::number(port));
profiler->setProgram(QDir(MMC->settings()->get("JProfilerPath").toString())
.absoluteFilePath("bin/jpenable"));
connect(profiler, &QProcess::started, [this, port]()
{ emit readyToLaunch(tr("Listening on port: %1").arg(port)); });
connect(profiler,
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[this](int exit, QProcess::ExitStatus status)
{
if (status == QProcess::CrashExit)
{
emit abortLaunch(tr("Profiler aborted"));
}
if (m_profilerProcess)
{
m_profilerProcess->deleteLater();
m_profilerProcess = 0;
}
});
profiler->start();
m_profilerProcess = profiler;
}
void JProfilerFactory::registerSettings(SettingsObject *settings)
{
settings->registerSetting("JProfilerPath");
settings->registerSetting("JProfilerPort", 42042);
}
BaseExternalTool *JProfilerFactory::createTool(BaseInstance *instance, QObject *parent)
{
return new JProfiler(instance, parent);
}
bool JProfilerFactory::check(QString *error)
{
return check(MMC->settings()->get("JProfilerPath").toString(), error);
}
bool JProfilerFactory::check(const QString &path, QString *error)
{
if (path.isEmpty())
{
*error = QObject::tr("Empty path");
return false;
}
QDir dir(path);
if (!dir.exists())
{
*error = QObject::tr("Path does not exist");
return false;
}
if (!dir.exists("bin") || !dir.exists("bin/jprofiler") || !dir.exists("bin/agent.jar"))
{
*error = QObject::tr("Invalid JProfiler install");
return false;
}
return true;
}

23
logic/tools/JProfiler.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include "BaseProfiler.h"
class JProfiler : public BaseProfiler
{
Q_OBJECT
public:
JProfiler(BaseInstance *instance, QObject *parent = 0);
protected:
void beginProfilingImpl(MinecraftProcess *process);
};
class JProfilerFactory : public BaseProfilerFactory
{
public:
QString name() const override { return "JProfiler"; }
void registerSettings(SettingsObject *settings) override;
BaseExternalTool *createTool(BaseInstance *instance, QObject *parent = 0) override;
bool check(QString *error) override;
bool check(const QString &path, QString *error) override;
};

74
logic/tools/JVisualVM.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "JVisualVM.h"
#include <QDir>
#include <QStandardPaths>
#include "settingsobject.h"
#include "logic/MinecraftProcess.h"
#include "logic/BaseInstance.h"
#include "MultiMC.h"
JVisualVM::JVisualVM(BaseInstance *instance, QObject *parent) : BaseProfiler(instance, parent)
{
}
void JVisualVM::beginProfilingImpl(MinecraftProcess *process)
{
QProcess *profiler = new QProcess(this);
profiler->setArguments(QStringList() << "--openpid" << QString::number(pid(process)));
profiler->setProgram(MMC->settings()->get("JVisualVMPath").toString());
connect(profiler, &QProcess::started, [this]()
{ emit readyToLaunch(tr("JVisualVM started")); });
connect(profiler,
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[this](int exit, QProcess::ExitStatus status)
{
if (exit != 0 || status == QProcess::CrashExit)
{
emit abortLaunch(tr("Profiler aborted"));
}
if (m_profilerProcess)
{
m_profilerProcess->deleteLater();
m_profilerProcess = 0;
}
});
profiler->start();
m_profilerProcess = profiler;
}
void JVisualVMFactory::registerSettings(SettingsObject *settings)
{
QString defaultValue = QStandardPaths::findExecutable("jvisualvm");
if (defaultValue.isNull())
{
defaultValue = QStandardPaths::findExecutable("visualvm");
}
settings->registerSetting("JVisualVMPath", defaultValue);
}
BaseExternalTool *JVisualVMFactory::createTool(BaseInstance *instance, QObject *parent)
{
return new JVisualVM(instance, parent);
}
bool JVisualVMFactory::check(QString *error)
{
return check(MMC->settings()->get("JVisualVMPath").toString(), error);
}
bool JVisualVMFactory::check(const QString &path, QString *error)
{
if (path.isEmpty())
{
*error = QObject::tr("Empty path");
return false;
}
QString resolved = QStandardPaths::findExecutable(path);
if (resolved.isEmpty() && !QDir::isAbsolutePath(path))
{
*error = QObject::tr("Invalid path to JVisualVM");
return false;
}
return true;
}

23
logic/tools/JVisualVM.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include "BaseProfiler.h"
class JVisualVM : public BaseProfiler
{
Q_OBJECT
public:
JVisualVM(BaseInstance *instance, QObject *parent = 0);
protected:
void beginProfilingImpl(MinecraftProcess *process);
};
class JVisualVMFactory : public BaseProfilerFactory
{
public:
QString name() const override { return "JVisualVM"; }
void registerSettings(SettingsObject *settings) override;
BaseExternalTool *createTool(BaseInstance *instance, QObject *parent = 0) override;
bool check(QString *error) override;
bool check(const QString &path, QString *error) override;
};

View File

@ -0,0 +1,77 @@
#include "MCEditTool.h"
#include <QDir>
#include <QProcess>
#include <QDesktopServices>
#include <QUrl>
#include "settingsobject.h"
#include "logic/BaseInstance.h"
#include "MultiMC.h"
MCEditTool::MCEditTool(BaseInstance *instance, QObject *parent)
: BaseDetachedTool(instance, parent)
{
}
void MCEditTool::runImpl()
{
const QString mceditPath = MMC->settings()->get("MCEditPath").toString();
const QString save = getSave();
if (save.isNull())
{
return;
}
#ifdef Q_OS_OSX
QProcess *process = new QProcess();
connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), process, SLOT(deleteLater()));
process->setProgram(mceditPath);
process->setArguments(QStringList() << save);
process->start();
#else
QDir mceditDir(mceditPath);
QString program;
if (mceditDir.exists("mcedit.py"))
{
program = mceditDir.absoluteFilePath("mcedit.py");
}
else if (mceditDir.exists("mcedit.exe"))
{
program = mceditDir.absoluteFilePath("mcedit.exe");
}
QProcess::startDetached(program, QStringList() << save, mceditPath);
#endif
}
void MCEditFactory::registerSettings(SettingsObject *settings)
{
settings->registerSetting("MCEditPath");
}
BaseExternalTool *MCEditFactory::createTool(BaseInstance *instance, QObject *parent)
{
return new MCEditTool(instance, parent);
}
bool MCEditFactory::check(QString *error)
{
return check(MMC->settings()->get("MCEditPath").toString(), error);
}
bool MCEditFactory::check(const QString &path, QString *error)
{
if (path.isEmpty())
{
*error = QObject::tr("Path is empty");
return false;
}
const QDir dir(path);
if (!dir.exists())
{
*error = QObject::tr("Path does not exist");
return false;
}
if (!dir.exists("mcedit.py") && !dir.exists("mcedit.exe") && !dir.exists("Contents"))
{
*error = QObject::tr("Path does not seem to be a MCEdit path");
return false;
}
return true;
}

23
logic/tools/MCEditTool.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include "BaseExternalTool.h"
class MCEditTool : public BaseDetachedTool
{
Q_OBJECT
public:
explicit MCEditTool(BaseInstance *instance, QObject *parent = 0);
protected:
void runImpl() override;
};
class MCEditFactory : public BaseDetachedToolFactory
{
public:
QString name() const override { return "MCEdit"; }
void registerSettings(SettingsObject *settings) override;
BaseExternalTool *createTool(BaseInstance *instance, QObject *parent = 0) override;
bool check(QString *error) override;
bool check(const QString &path, QString *error) override;
};