aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml2
-rw-r--r--launcher/Application.cpp5
-rw-r--r--launcher/Application.h1
-rw-r--r--launcher/FileSystem.cpp222
-rw-r--r--launcher/LaunchController.cpp11
-rw-r--r--launcher/LaunchController.h5
-rw-r--r--launcher/minecraft/MinecraftInstance.cpp8
-rw-r--r--launcher/minecraft/MinecraftInstance.h2
-rw-r--r--launcher/ui/InstanceWindow.cpp24
-rw-r--r--launcher/ui/InstanceWindow.h2
-rw-r--r--launcher/ui/MainWindow.cpp48
-rw-r--r--launcher/ui/MainWindow.h2
-rw-r--r--launcher/ui/pages/instance/ServersPage.cpp2
-rw-r--r--libraries/launcher/org/polymc/applet/LegacyFrame.java5
-rw-r--r--libraries/launcher/org/polymc/impl/OneSixLauncher.java9
-rw-r--r--tests/FileSystem_test.cpp36
16 files changed, 212 insertions, 172 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 434c5775..ae8947ab 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -39,7 +39,7 @@ jobs:
qt_ver: 6
- os: macos-12
- macosx_deployment_target: 10.14
+ macosx_deployment_target: 10.15
qt_ver: 6
qt_host: mac
qt_version: '6.3.0'
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index c4179b49..0d1db11c 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -1041,7 +1041,7 @@ void Application::performMainStartupAction()
qDebug() << " Launching with account" << m_profileToUse;
}
- launch(inst, true, nullptr, serverToJoin, accountToUse);
+ launch(inst, true, false, nullptr, serverToJoin, accountToUse);
return;
}
}
@@ -1145,6 +1145,7 @@ void Application::messageReceived(const QByteArray& message)
launch(
instance,
true,
+ false,
nullptr,
serverObject,
accountObject
@@ -1245,6 +1246,7 @@ bool Application::openJsonEditor(const QString &filename)
bool Application::launch(
InstancePtr instance,
bool online,
+ bool demo,
BaseProfilerFactory *profiler,
MinecraftServerTargetPtr serverToJoin,
MinecraftAccountPtr accountToUse
@@ -1268,6 +1270,7 @@ bool Application::launch(
controller.reset(new LaunchController());
controller->setInstance(instance);
controller->setOnline(online);
+ controller->setDemo(demo);
controller->setProfiler(profiler);
controller->setServerToJoin(serverToJoin);
controller->setAccountToUse(accountToUse);
diff --git a/launcher/Application.h b/launcher/Application.h
index 41fd4c47..34ad8c15 100644
--- a/launcher/Application.h
+++ b/launcher/Application.h
@@ -213,6 +213,7 @@ public slots:
bool launch(
InstancePtr instance,
bool online = true,
+ bool demo = false,
BaseProfilerFactory *profiler = nullptr,
MinecraftServerTargetPtr serverToJoin = nullptr,
MinecraftAccountPtr accountToUse = nullptr
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index 8eeb2885..5d179641 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -37,6 +37,7 @@
#include <QDebug>
#include <QDir>
+#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QSaveFile>
@@ -58,6 +59,24 @@
#include <utime.h>
#endif
+#include <filesystem>
+
+#if defined Q_OS_WIN32
+
+std::wstring toStdString(QString s)
+{
+ return s.toStdWString();
+}
+
+#else
+
+std::string toStdString(QString s)
+{
+ return s.toStdString();
+}
+
+#endif
+
namespace FS {
void ensureExists(const QDir& dir)
@@ -128,6 +147,8 @@ bool ensureFolderPathExists(QString foldernamepath)
bool copy::operator()(const QString& offset)
{
+ using copy_opts = std::filesystem::copy_options;
+
// NOTE always deep copy on windows. the alternatives are too messy.
#if defined Q_OS_WIN32
m_followSymlinks = true;
@@ -136,94 +157,53 @@ bool copy::operator()(const QString& offset)
auto src = PathCombine(m_src.absolutePath(), offset);
auto dst = PathCombine(m_dst.absolutePath(), offset);
- QFileInfo currentSrc(src);
- if (!currentSrc.exists())
- return false;
+ std::error_code err;
- if (!m_followSymlinks && currentSrc.isSymLink()) {
- qDebug() << "creating symlink" << src << " - " << dst;
- if (!ensureFilePathExists(dst)) {
- qWarning() << "Cannot create path!";
- return false;
- }
- return QFile::link(currentSrc.symLinkTarget(), dst);
- } else if (currentSrc.isFile()) {
- qDebug() << "copying file" << src << " - " << dst;
- if (!ensureFilePathExists(dst)) {
- qWarning() << "Cannot create path!";
- return false;
- }
- return QFile::copy(src, dst);
- } else if (currentSrc.isDir()) {
- qDebug() << "recursing" << offset;
- if (!ensureFolderPathExists(dst)) {
- qWarning() << "Cannot create path!";
- return false;
- }
- QDir currentDir(src);
- for (auto& f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) {
- auto inner_offset = PathCombine(offset, f);
- // ignore and skip stuff that matches the blacklist.
- if (m_blacklist && m_blacklist->matches(inner_offset)) {
- continue;
- }
- if (!operator()(inner_offset)) {
- qWarning() << "Failed to copy" << inner_offset;
- return false;
- }
+ std::filesystem::copy_options opt = copy_opts::none;
+
+ // The default behavior is to follow symlinks
+ if (!m_followSymlinks)
+ opt |= copy_opts::copy_symlinks;
+
+
+ // We can't use copy_opts::recursive because we need to take into account the
+ // blacklisted paths, so we iterate over the source directory, and if there's no blacklist
+ // match, we copy the file.
+ QDir src_dir(src);
+ QDirIterator source_it(src, QDir::Filter::Files, QDirIterator::Subdirectories);
+
+ while (source_it.hasNext()) {
+ auto src_path = source_it.next();
+ auto relative_path = src_dir.relativeFilePath(src_path);
+
+ if (m_blacklist && m_blacklist->matches(relative_path))
+ continue;
+
+ auto dst_path = PathCombine(dst, relative_path);
+ ensureFilePathExists(dst_path);
+
+ std::filesystem::copy(toStdString(src_path), toStdString(dst_path), opt, err);
+ if (err) {
+ qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
+ qDebug() << "Source file:" << src_path;
+ qDebug() << "Destination file:" << dst_path;
}
- } else {
- qCritical() << "Copy ERROR: Unknown filesystem object:" << src;
- return false;
}
- return true;
+
+ return err.value() == 0;
}
bool deletePath(QString path)
{
- bool OK = true;
- QFileInfo finfo(path);
- if (finfo.isFile()) {
- return QFile::remove(path);
- }
+ std::error_code err;
- QDir dir(path);
+ std::filesystem::remove_all(toStdString(path), err);
- if (!dir.exists()) {
- return OK;
+ if (err) {
+ qWarning() << "Failed to remove files:" << QString::fromStdString(err.message());
}
- auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst);
- for (auto& info : allEntries) {
-#if defined Q_OS_WIN32
- QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath());
- auto wString = nativePath.toStdWString();
- DWORD dwAttrs = GetFileAttributesW(wString.c_str());
- // Windows: check for junctions, reparse points and other nasty things of that sort
- if (dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT) {
- if (info.isFile()) {
- OK &= QFile::remove(info.absoluteFilePath());
- } else if (info.isDir()) {
- OK &= dir.rmdir(info.absoluteFilePath());
- }
- }
-#else
- // We do not trust Qt with reparse points, but do trust it with unix symlinks.
- if (info.isSymLink()) {
- OK &= QFile::remove(info.absoluteFilePath());
- }
-#endif
- else if (info.isDir()) {
- OK &= deletePath(info.absoluteFilePath());
- } else if (info.isFile()) {
- OK &= QFile::remove(info.absoluteFilePath());
- } else {
- OK = false;
- qCritical() << "Delete ERROR: Unknown filesystem object:" << info.absoluteFilePath();
- }
- }
- OK &= dir.rmdir(dir.absolutePath());
- return OK;
+ return err.value() == 0;
}
bool trash(QString path, QString *pathInTrash = nullptr)
@@ -316,8 +296,7 @@ QString DirNameFromString(QString string, QString inDir)
if (num == 0) {
dirName = baseName;
} else {
- dirName = baseName + QString::number(num);
- ;
+ dirName = baseName + "(" + QString::number(num) + ")";
}
// If it's over 9000
@@ -336,50 +315,6 @@ bool checkProblemticPathJava(QDir folder)
return pathfoldername.contains("!", Qt::CaseInsensitive);
}
-// Win32 crap
-#ifdef Q_OS_WIN
-
-bool called_coinit = false;
-
-HRESULT CreateLink(LPCCH linkPath, LPCWSTR targetPath, LPCWSTR args)
-{
- HRESULT hres;
-
- if (!called_coinit) {
- hres = CoInitialize(NULL);
- called_coinit = true;
-
- if (!SUCCEEDED(hres)) {
- qWarning("Failed to initialize COM. Error 0x%08lX", hres);
- return hres;
- }
- }
-
- IShellLink* link;
- hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&link);
-
- if (SUCCEEDED(hres)) {
- IPersistFile* persistFile;
-
- link->SetPath(targetPath);
- link->SetArguments(args);
-
- hres = link->QueryInterface(IID_IPersistFile, (LPVOID*)&persistFile);
- if (SUCCEEDED(hres)) {
- WCHAR wstr[MAX_PATH];
-
- MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH);
-
- hres = persistFile->Save(wstr, TRUE);
- persistFile->Release();
- }
- link->Release();
- }
- return hres;
-}
-
-#endif
-
QString getDesktopDir()
{
return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
@@ -439,47 +374,24 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na
#endif
}
-QStringList listFolderPaths(QDir root)
-{
- auto createAbsPath = [](QFileInfo const& entry) { return FS::PathCombine(entry.path(), entry.fileName()); };
-
- QStringList entries;
-
- root.refresh();
- for (auto entry : root.entryInfoList(QDir::Filter::Files)) {
- entries.append(createAbsPath(entry));
- }
-
- for (auto entry : root.entryInfoList(QDir::Filter::AllDirs | QDir::Filter::NoDotAndDotDot)) {
- entries.append(listFolderPaths(createAbsPath(entry)));
- }
-
- return entries;
-}
-
bool overrideFolder(QString overwritten_path, QString override_path)
{
+ using copy_opts = std::filesystem::copy_options;
+
if (!FS::ensureFolderPathExists(overwritten_path))
return false;
- QStringList paths_to_override;
- QDir root_override (override_path);
- for (auto file : listFolderPaths(root_override)) {
- QString destination = file;
- destination.replace(override_path, overwritten_path);
- ensureFilePathExists(destination);
+ std::error_code err;
+ std::filesystem::copy_options opt = copy_opts::recursive | copy_opts::overwrite_existing;
- qDebug() << QString("Applying override %1 in %2").arg(file, destination);
+ std::filesystem::copy(toStdString(override_path), toStdString(overwritten_path), opt, err);
- if (QFile::exists(destination))
- QFile::remove(destination);
- if (!QFile::rename(file, destination)) {
- qCritical() << QString("Failed to apply override from %1 to %2").arg(file, destination);
- return false;
- }
+ if (err) {
+ qCritical() << QString("Failed to apply override from %1 to %2").arg(override_path, overwritten_path);
+ qCritical() << "Reason:" << QString::fromStdString(err.message());
}
- return true;
+ return err.value() == 0;
}
}
diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp
index 11f9b2bb..830fcf82 100644
--- a/launcher/LaunchController.cpp
+++ b/launcher/LaunchController.cpp
@@ -167,6 +167,7 @@ void LaunchController::login() {
tries++;
m_session = std::make_shared<AuthSession>();
m_session->wants_online = m_online;
+ m_session->demo = m_demo;
m_accountToUse->fillSession(m_session);
// Launch immediately in true offline mode
@@ -184,12 +185,18 @@ void LaunchController::login() {
if(!m_session->wants_online) {
// we ask the user for a player name
bool ok = false;
+
+ QString message = tr("Choose your offline mode player name.");
+ if(m_session->demo) {
+ message = tr("Choose your demo mode player name.");
+ }
+
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
QString name = QInputDialog::getText(
m_parentWidget,
tr("Player name"),
- tr("Choose your offline mode player name."),
+ message,
QLineEdit::Normal,
usedname,
&ok
@@ -369,7 +376,7 @@ void LaunchController::launchInstance()
}
m_launcher->prependStep(new TextPrint(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
} else {
- online_mode = "offline";
+ online_mode = m_demo ? "demo" : "offline";
}
m_launcher->prependStep(new TextPrint(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
diff --git a/launcher/LaunchController.h b/launcher/LaunchController.h
index 2171ad5e..af6c98d1 100644
--- a/launcher/LaunchController.h
+++ b/launcher/LaunchController.h
@@ -63,6 +63,10 @@ public:
m_online = online;
}
+ void setDemo(bool demo) {
+ m_demo = demo;
+ }
+
void setProfiler(BaseProfilerFactory *profiler) {
m_profiler = profiler;
}
@@ -101,6 +105,7 @@ private slots:
private:
BaseProfilerFactory *m_profiler = nullptr;
bool m_online = true;
+ bool m_demo = false;
InstancePtr m_instance;
QWidget * m_parentWidget = nullptr;
InstanceWindow *m_console = nullptr;
diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp
index 47f53948..62540c75 100644
--- a/launcher/minecraft/MinecraftInstance.cpp
+++ b/launcher/minecraft/MinecraftInstance.cpp
@@ -245,6 +245,14 @@ QString MinecraftInstance::getLocalLibraryPath() const
return libraries_dir.absolutePath();
}
+bool MinecraftInstance::supportsDemo() const
+{
+ Version instance_ver { getPackProfile()->getComponentVersion("net.minecraft") };
+ // Demo mode was introduced in 1.3.1: https://minecraft.fandom.com/wiki/Demo_mode#History
+ // FIXME: Due to Version constraints atm, this can't handle well non-release versions
+ return instance_ver >= Version("1.3.1");
+}
+
QString MinecraftInstance::jarModsDir() const
{
QDir jarmods_dir(FS::PathCombine(instanceRoot(), "jarmods/"));
diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h
index d62ac655..fe39674a 100644
--- a/launcher/minecraft/MinecraftInstance.h
+++ b/launcher/minecraft/MinecraftInstance.h
@@ -69,6 +69,8 @@ public:
// where the instance-local libraries should be
QString getLocalLibraryPath() const;
+ /** Returns whether the instance, with its version, has support for demo mode. */
+ [[nodiscard]] bool supportsDemo() const;
////// Profile management //////
std::shared_ptr<PackProfile> getPackProfile() const;
diff --git a/launcher/ui/InstanceWindow.cpp b/launcher/ui/InstanceWindow.cpp
index 0ad8c594..09ce0d67 100644
--- a/launcher/ui/InstanceWindow.cpp
+++ b/launcher/ui/InstanceWindow.cpp
@@ -95,8 +95,14 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent)
m_launchOfflineButton = new QPushButton();
horizontalLayout->addWidget(m_launchOfflineButton);
m_launchOfflineButton->setText(tr("Launch Offline"));
+
+ m_launchDemoButton = new QPushButton();
+ horizontalLayout->addWidget(m_launchDemoButton);
+ m_launchDemoButton->setText(tr("Launch Demo"));
+
updateLaunchButtons();
connect(m_launchOfflineButton, SIGNAL(clicked(bool)), SLOT(on_btnLaunchMinecraftOffline_clicked()));
+ connect(m_launchDemoButton, SIGNAL(clicked(bool)), SLOT(on_btnLaunchMinecraftDemo_clicked()));
m_closeButton = new QPushButton();
m_closeButton->setText(tr("Close"));
@@ -143,6 +149,7 @@ void InstanceWindow::updateLaunchButtons()
if(m_instance->isRunning())
{
m_launchOfflineButton->setEnabled(false);
+ m_launchDemoButton->setEnabled(false);
m_killButton->setText(tr("Kill"));
m_killButton->setObjectName("killButton");
m_killButton->setToolTip(tr("Kill the running instance"));
@@ -150,6 +157,7 @@ void InstanceWindow::updateLaunchButtons()
else if(!m_instance->canLaunch())
{
m_launchOfflineButton->setEnabled(false);
+ m_launchDemoButton->setEnabled(false);
m_killButton->setText(tr("Launch"));
m_killButton->setObjectName("launchButton");
m_killButton->setToolTip(tr("Launch the instance"));
@@ -158,6 +166,13 @@ void InstanceWindow::updateLaunchButtons()
else
{
m_launchOfflineButton->setEnabled(true);
+
+ // Disable demo-mode if not available.
+ auto instance = dynamic_cast<MinecraftInstance*>(m_instance.get());
+ if (instance) {
+ m_launchDemoButton->setEnabled(instance->supportsDemo());
+ }
+
m_killButton->setText(tr("Launch"));
m_killButton->setObjectName("launchButton");
m_killButton->setToolTip(tr("Launch the instance"));
@@ -169,7 +184,12 @@ void InstanceWindow::updateLaunchButtons()
void InstanceWindow::on_btnLaunchMinecraftOffline_clicked()
{
- APPLICATION->launch(m_instance, false, nullptr);
+ APPLICATION->launch(m_instance, false, false, nullptr);
+}
+
+void InstanceWindow::on_btnLaunchMinecraftDemo_clicked()
+{
+ APPLICATION->launch(m_instance, false, true, nullptr);
}
void InstanceWindow::instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc)
@@ -223,7 +243,7 @@ void InstanceWindow::on_btnKillMinecraft_clicked()
}
else
{
- APPLICATION->launch(m_instance, true, nullptr);
+ APPLICATION->launch(m_instance, true, false, nullptr);
}
}
diff --git a/launcher/ui/InstanceWindow.h b/launcher/ui/InstanceWindow.h
index aec07868..554c4c74 100644
--- a/launcher/ui/InstanceWindow.h
+++ b/launcher/ui/InstanceWindow.h
@@ -74,6 +74,7 @@ slots:
void on_closeButton_clicked();
void on_btnKillMinecraft_clicked();
void on_btnLaunchMinecraftOffline_clicked();
+ void on_btnLaunchMinecraftDemo_clicked();
void instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc);
void runningStateChanged(bool running);
@@ -93,4 +94,5 @@ private:
QPushButton *m_closeButton = nullptr;
QPushButton *m_killButton = nullptr;
QPushButton *m_launchOfflineButton = nullptr;
+ QPushButton *m_launchDemoButton = nullptr;
};
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index 299401f5..58b1ae80 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -240,6 +240,7 @@ public:
TranslatedAction actionCAT;
TranslatedAction actionCopyInstance;
TranslatedAction actionLaunchInstanceOffline;
+ TranslatedAction actionLaunchInstanceDemo;
TranslatedAction actionScreenshots;
TranslatedAction actionExportInstance;
QVector<TranslatedAction *> all_actions;
@@ -499,6 +500,7 @@ public:
fileMenu->addAction(actionAddInstance);
fileMenu->addAction(actionLaunchInstance);
fileMenu->addAction(actionLaunchInstanceOffline);
+ fileMenu->addAction(actionLaunchInstanceDemo);
fileMenu->addAction(actionKillInstance);
fileMenu->addAction(actionCloseWindow);
fileMenu->addSeparator();
@@ -669,6 +671,12 @@ public:
actionLaunchInstanceOffline.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Launch the selected instance in offline mode."));
all_actions.append(&actionLaunchInstanceOffline);
+ actionLaunchInstanceDemo = TranslatedAction(MainWindow);
+ actionLaunchInstanceDemo->setObjectName(QStringLiteral("actionLaunchInstanceDemo"));
+ actionLaunchInstanceDemo.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Launch &Demo"));
+ actionLaunchInstanceDemo.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Launch the selected instance in demo mode."));
+ all_actions.append(&actionLaunchInstanceDemo);
+
actionKillInstance = TranslatedAction(MainWindow);
actionKillInstance->setObjectName(QStringLiteral("actionKillInstance"));
actionKillInstance->setDisabled(true);
@@ -1195,6 +1203,7 @@ void MainWindow::updateToolsMenu()
ui->actionLaunchInstance->setDisabled(!m_selectedInstance || currentInstanceRunning);
ui->actionLaunchInstanceOffline->setDisabled(!m_selectedInstance || currentInstanceRunning);
+ ui->actionLaunchInstanceDemo->setDisabled(!m_selectedInstance || currentInstanceRunning);
QMenu *launchMenu = ui->actionLaunchInstance->menu();
QMenu *launchOfflineMenu = ui->actionLaunchInstanceOffline->menu();
@@ -1220,23 +1229,37 @@ void MainWindow::updateToolsMenu()
normalLaunch->setShortcut(QKeySequence::Open);
QAction *normalLaunchOffline = launchOfflineMenu->addAction(tr("Launch Offline"));
normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O")));
+ QAction *normalLaunchDemo = launchOfflineMenu->addAction(tr("Launch Demo"));
+ normalLaunchDemo->setShortcut(QKeySequence(tr("Ctrl+Alt+O")));
if (m_selectedInstance)
{
normalLaunch->setEnabled(m_selectedInstance->canLaunch());
normalLaunchOffline->setEnabled(m_selectedInstance->canLaunch());
+ normalLaunchDemo->setEnabled(m_selectedInstance->canLaunch());
connect(normalLaunch, &QAction::triggered, [this]() {
- APPLICATION->launch(m_selectedInstance, true);
+ APPLICATION->launch(m_selectedInstance, true, false);
});
connect(normalLaunchOffline, &QAction::triggered, [this]() {
- APPLICATION->launch(m_selectedInstance, false);
+ APPLICATION->launch(m_selectedInstance, false, false);
+ });
+ connect(normalLaunchDemo, &QAction::triggered, [this]() {
+ APPLICATION->launch(m_selectedInstance, false, true);
});
}
else
{
normalLaunch->setDisabled(true);
normalLaunchOffline->setDisabled(true);
+ normalLaunchDemo->setDisabled(true);
+ }
+
+ // Disable demo-mode if not available.
+ auto instance = dynamic_cast<MinecraftInstance*>(m_selectedInstance.get());
+ if (instance) {
+ normalLaunchDemo->setEnabled(instance->supportsDemo());
}
+
QString profilersTitle = tr("Profilers");
launchMenu->addSeparator()->setText(profilersTitle);
launchOfflineMenu->addSeparator()->setText(profilersTitle);
@@ -1260,11 +1283,11 @@ void MainWindow::updateToolsMenu()
connect(profilerAction, &QAction::triggered, [this, profiler]()
{
- APPLICATION->launch(m_selectedInstance, true, profiler.get());
+ APPLICATION->launch(m_selectedInstance, true, false, profiler.get());
});
connect(profilerOfflineAction, &QAction::triggered, [this, profiler]()
{
- APPLICATION->launch(m_selectedInstance, false, profiler.get());
+ APPLICATION->launch(m_selectedInstance, false, false, profiler.get());
});
}
else
@@ -2096,6 +2119,14 @@ void MainWindow::on_actionLaunchInstanceOffline_triggered()
}
}
+void MainWindow::on_actionLaunchInstanceDemo_triggered()
+{
+ if (m_selectedInstance)
+ {
+ APPLICATION->launch(m_selectedInstance, false, true);
+ }
+}
+
void MainWindow::on_actionKillInstance_triggered()
{
if(m_selectedInstance && m_selectedInstance->isRunning())
@@ -2139,6 +2170,14 @@ void MainWindow::instanceChanged(const QModelIndex &current, const QModelIndex &
ui->setInstanceActionsEnabled(true);
ui->actionLaunchInstance->setEnabled(m_selectedInstance->canLaunch());
ui->actionLaunchInstanceOffline->setEnabled(m_selectedInstance->canLaunch());
+ ui->actionLaunchInstanceDemo->setEnabled(m_selectedInstance->canLaunch());
+
+ // Disable demo-mode if not available.
+ auto instance = dynamic_cast<MinecraftInstance*>(m_selectedInstance.get());
+ if (instance) {
+ ui->actionLaunchInstanceDemo->setEnabled(instance->supportsDemo());
+ }
+
ui->actionKillInstance->setEnabled(m_selectedInstance->isRunning());
ui->actionExportInstance->setEnabled(m_selectedInstance->canExport());
ui->renameButton->setText(m_selectedInstance->name());
@@ -2158,6 +2197,7 @@ void MainWindow::instanceChanged(const QModelIndex &current, const QModelIndex &
ui->setInstanceActionsEnabled(false);
ui->actionLaunchInstance->setEnabled(false);
ui->actionLaunchInstanceOffline->setEnabled(false);
+ ui->actionLaunchInstanceDemo->setEnabled(false);
ui->actionKillInstance->setEnabled(false);
APPLICATION->settings()->set("SelectedInstance", QString());
selectionBad();
diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h
index dde3d02c..8b41bf94 100644
--- a/launcher/ui/MainWindow.h
+++ b/launcher/ui/MainWindow.h
@@ -140,6 +140,8 @@ private slots:
void on_actionLaunchInstanceOffline_triggered();
+ void on_actionLaunchInstanceDemo_triggered();
+
void on_actionKillInstance_triggered();
void on_actionDeleteInstance_triggered();
diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp
index e5cce96c..5e8bd7cc 100644
--- a/launcher/ui/pages/instance/ServersPage.cpp
+++ b/launcher/ui/pages/instance/ServersPage.cpp
@@ -811,7 +811,7 @@ void ServersPage::on_actionMove_Down_triggered()
void ServersPage::on_actionJoin_triggered()
{
const auto &address = m_model->at(currentServer)->m_address;
- APPLICATION->launch(m_inst, true, nullptr, std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(address)));
+ APPLICATION->launch(m_inst, true, false, nullptr, std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(address)));
}
#include "ServersPage.moc"
diff --git a/libraries/launcher/org/polymc/applet/LegacyFrame.java b/libraries/launcher/org/polymc/applet/LegacyFrame.java
index 2cdd17d7..7ae56e60 100644
--- a/libraries/launcher/org/polymc/applet/LegacyFrame.java
+++ b/libraries/launcher/org/polymc/applet/LegacyFrame.java
@@ -63,7 +63,8 @@ public final class LegacyFrame extends Frame {
int winSizeH,
boolean maximize,
String serverAddress,
- String serverPort
+ String serverPort,
+ boolean isDemo
) {
// Implements support for launching in to multiplayer on classic servers using a mpticket
// file generated by an external program and stored in the instance's root folder.
@@ -106,7 +107,7 @@ public final class LegacyFrame extends Frame {
appletWrap.setParameter("sessionid", session);
appletWrap.setParameter("stand-alone", "true"); // Show the quit button.
appletWrap.setParameter("haspaid", "true"); // Some old versions need this for world saves to work.
- appletWrap.setParameter("demo", "false");
+ appletWrap.setParameter("demo", isDemo ? "true" : "false");
appletWrap.setParameter("fullscreen", "false");
add(appletWrap);
diff --git a/libraries/launcher/org/polymc/impl/OneSixLauncher.java b/libraries/launcher/org/polymc/impl/OneSixLauncher.java
index 362ff8d6..d43101eb 100644
--- a/libraries/launcher/org/polymc/impl/OneSixLauncher.java
+++ b/libraries/launcher/org/polymc/impl/OneSixLauncher.java
@@ -24,8 +24,8 @@ import java.applet.Applet;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.util.Collections;
import java.util.List;
+import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -58,10 +58,10 @@ public final class OneSixLauncher implements Launcher {
public OneSixLauncher(Parameters params) {
classLoader = ClassLoader.getSystemClassLoader();
- mcParams = params.allSafe("param", Collections.<String>emptyList());
+ mcParams = params.allSafe("param", new ArrayList<String>());
mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft");
appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet");
- traits = params.allSafe("traits", Collections.<String>emptyList());
+ traits = params.allSafe("traits", new ArrayList<String>());
userName = params.first("userName");
sessionId = params.first("sessionId");
@@ -137,7 +137,8 @@ public final class OneSixLauncher implements Launcher {
winSizeH,
maximize,
serverAddress,
- serverPort
+ serverPort,
+ mcParams.contains("--demo")
);
return;
diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp
index 6df13e80..4efb90ac 100644
--- a/tests/FileSystem_test.cpp
+++ b/tests/FileSystem_test.cpp
@@ -4,6 +4,8 @@
#include <FileSystem.h>
+#include <pathmatcher/RegexpMatcher.h>
+
class FileSystemTest : public QObject
{
Q_OBJECT
@@ -111,6 +113,40 @@ slots:
f();
}
+ void test_copy_with_blacklist()
+ {
+ QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder");
+ auto f = [&folder]()
+ {
+ QTemporaryDir tempDir;
+ tempDir.setAutoRemove(true);
+ qDebug() << "From:" << folder << "To:" << tempDir.path();
+
+ QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
+ qDebug() << tempDir.path();
+ qDebug() << target_dir.path();
+ FS::copy c(folder, target_dir.path());
+ c.blacklist(new RegexpMatcher("[.]?mcmeta"));
+ c();
+
+ for(auto entry: target_dir.entryList())
+ {
+ qDebug() << entry;
+ }
+ QVERIFY(!target_dir.entryList().contains("pack.mcmeta"));
+ QVERIFY(target_dir.entryList().contains("assets"));
+ };
+
+ // first try variant without trailing /
+ QVERIFY(!folder.endsWith('/'));
+ f();
+
+ // then variant with trailing /
+ folder.append('/');
+ QVERIFY(folder.endsWith('/'));
+ f();
+ }
+
void test_getDesktop()
{
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));