all worlds list will now join correct instance for each world

This commit is contained in:
Ice Yeti 2026-05-31 13:48:31 -04:00
parent 0ceb218180
commit 3a6ae78bc6
5 changed files with 66 additions and 56 deletions

View file

@ -46,7 +46,9 @@
#include <QUuid> #include <QUuid>
#include <Qt> #include <Qt>
MultiWorldList::MultiWorldList(const QList<QString>& dirs, QList<BaseInstance*> instances) : QAbstractListModel(), m_instances(instances) #include "MinecraftInstance.h"
MultiWorldList::MultiWorldList(const QList<QString>& dirs, const QList<BaseInstance*>& instances) : QAbstractListModel(), m_instances(instances)
{ {
for (QString dir : dirs) { for (QString dir : dirs) {
m_dirs.append(dir); m_dirs.append(dir);
@ -105,9 +107,10 @@ bool MultiWorldList::update()
if (!isValid()) if (!isValid())
return false; return false;
QList<World> newWorlds; QList<InstanceWorld> newWorlds;
for (QDir dir : m_dirs) { for (BaseInstance* inst : m_instances) {
QDir dir = dynamic_cast<MinecraftInstance*>(inst)->worldDir();
dir.refresh(); dir.refresh();
auto folderContents = dir.entryInfoList(); auto folderContents = dir.entryInfoList();
// if there are any untracked files... // if there are any untracked files...
@ -117,7 +120,7 @@ bool MultiWorldList::update()
World w(entry); World w(entry);
if (w.isValid()) { if (w.isValid()) {
newWorlds.append(w); newWorlds.append(InstanceWorld(w, inst));
} }
} }
} }
@ -134,16 +137,24 @@ void MultiWorldList::directoryChanged(QString)
update(); update();
} }
bool MultiWorldList::isValid() //account for all directories bool MultiWorldList::isValid()
{ {
return m_dirs[0].exists() && m_dirs[0].isReadable(); bool valid = true;
for (const QDir& dir : m_dirs) {
if (!(dir.exists() && dir.isReadable())) {
valid = false;
}
}
return valid;
} }
QList<QString> MultiWorldList::instDirPaths() const QList<QString> MultiWorldList::instDirPaths() const
{ {
QList<QString> dirList; QList<QString> dirList;
for (BaseInstance* instance : m_instances) { for (BaseInstance* instance : m_instances) { //use m_dirs??? iy
dirList.append(QFileInfo(instance->instanceRoot()).absoluteFilePath()); dirList.append(QFileInfo(instance->instanceRoot()).absoluteFilePath());
} }
@ -154,7 +165,7 @@ bool MultiWorldList::deleteWorld(int index)
{ {
if (index >= m_worlds.size() || index < 0) if (index >= m_worlds.size() || index < 0)
return false; return false;
World& m = m_worlds[index]; World& m = m_worlds[index].world;
if (m.destroy()) { if (m.destroy()) {
beginRemoveRows(QModelIndex(), index, index); beginRemoveRows(QModelIndex(), index, index);
m_worlds.removeAt(index); m_worlds.removeAt(index);
@ -168,7 +179,7 @@ bool MultiWorldList::deleteWorld(int index)
bool MultiWorldList::deleteWorlds(int first, int last) bool MultiWorldList::deleteWorlds(int first, int last)
{ {
for (int i = first; i <= last; i++) { for (int i = first; i <= last; i++) {
World& m = m_worlds[i]; World& m = m_worlds[i].world;
m.destroy(); m.destroy();
} }
beginRemoveRows(QModelIndex(), first, last); beginRemoveRows(QModelIndex(), first, last);
@ -182,7 +193,7 @@ bool MultiWorldList::resetIcon(int row)
{ {
if (row >= m_worlds.size() || row < 0) if (row >= m_worlds.size() || row < 0)
return false; return false;
World& m = m_worlds[row]; World& m = m_worlds[row].world;
if (m.resetIcon()) { if (m.resetIcon()) {
QModelIndex modelIndex = index(row, NameColumn); QModelIndex modelIndex = index(row, NameColumn);
emit dataChanged(modelIndex, modelIndex, { MultiWorldList::IconFileRole }); emit dataChanged(modelIndex, modelIndex, { MultiWorldList::IconFileRole });
@ -209,29 +220,29 @@ QVariant MultiWorldList::data(const QModelIndex& index, int role) const
QLocale locale; QLocale locale;
auto& world = m_worlds[row]; auto& instanceWorld = m_worlds[row];
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole:
switch (column) { switch (column) {
case NameColumn: case NameColumn:
return world.name(); return instanceWorld.world.name();
case GameModeColumn: case GameModeColumn:
return world.gameType().toTranslatedString(); return instanceWorld.world.gameType().toTranslatedString();
case LastPlayedColumn: case LastPlayedColumn:
return world.lastPlayed(); return instanceWorld.world.lastPlayed();
case SizeColumn: case SizeColumn:
return locale.formattedDataSize(world.bytes()); return locale.formattedDataSize(instanceWorld.world.bytes());
case InfoColumn: case InfoColumn:
for (QString path : instDirPaths()) { //use canonical paths instead of for loops? iy for (QString path : instDirPaths()) { //use canonical paths instead of for loops? iy
if (world.isSymLinkUnder(path)) { if (instanceWorld.world.isSymLinkUnder(path)) {
return tr("This world is symbolically linked from elsewhere."); return tr("This world is symbolically linked from elsewhere.");
} }
} }
if (world.isMoreThanOneHardLink()) { if (instanceWorld.world.isMoreThanOneHardLink()) {
return tr("\nThis world is hard linked elsewhere."); return tr("\nThis world is hard linked elsewhere.");
} }
return ""; return "";
@ -241,44 +252,44 @@ QVariant MultiWorldList::data(const QModelIndex& index, int role) const
case Qt::UserRole: case Qt::UserRole:
if (column == SizeColumn) if (column == SizeColumn)
return QVariant::fromValue<qlonglong>(world.bytes()); return QVariant::fromValue<qlonglong>(instanceWorld.world.bytes());
return data(index, Qt::DisplayRole); return data(index, Qt::DisplayRole);
case Qt::ToolTipRole: { case Qt::ToolTipRole: {
if (column == InfoColumn) { if (column == InfoColumn) {
for (QString path : instDirPaths()) { //use canonical paths instead of for loops? iy for (QString path : instDirPaths()) { //use canonical paths instead of for loops? iy
if (world.isSymLinkUnder(path)) { if (instanceWorld.world.isSymLinkUnder(path)) {
return tr("Warning: This world is symbolically linked from elsewhere. Editing it will also change the original." return tr("Warning: This world is symbolically linked from elsewhere. Editing it will also change the original."
"\nCanonical Path: %1") "\nCanonical Path: %1")
.arg(world.canonicalFilePath()); .arg(instanceWorld.world.canonicalFilePath());
} }
} }
if (world.isMoreThanOneHardLink()) { if (instanceWorld.world.isMoreThanOneHardLink()) {
return tr("Warning: This world is hard linked elsewhere. Editing it will also change the original."); return tr("Warning: This world is hard linked elsewhere. Editing it will also change the original.");
} }
} }
return world.folderName(); return instanceWorld.world.folderName();
} }
case ObjectRole: { case ObjectRole: {
return QVariant::fromValue<void*>((void*)&world); return QVariant::fromValue<void*>((void*)&instanceWorld);
} }
case FolderRole: { case FolderRole: {
return QDir::toNativeSeparators(QDir(world.canonicalFilePath()).absoluteFilePath(world.folderName())); //test if canonical file path works iy return QDir::toNativeSeparators(QDir(instanceWorld.world.canonicalFilePath()).absoluteFilePath(instanceWorld.world.folderName())); //test if canonical file path works iy
} }
case SeedRole: { case SeedRole: {
return QVariant::fromValue<qlonglong>(world.seed()); return QVariant::fromValue<qlonglong>(instanceWorld.world.seed());
} }
case NameRole: { case NameRole: {
return world.name(); return instanceWorld.world.name();
} }
case LastPlayedRole: { case LastPlayedRole: {
return world.lastPlayed(); return instanceWorld.world.lastPlayed();
} }
case SizeRole: { case SizeRole: {
return QVariant::fromValue<qlonglong>(world.bytes()); return QVariant::fromValue<qlonglong>(instanceWorld.world.bytes());
} }
case IconFileRole: { case IconFileRole: {
return world.iconFile(); return instanceWorld.world.iconFile();
} }
default: default:
return QVariant(); return QVariant();
@ -345,7 +356,7 @@ QMimeData* MultiWorldList::mimeData(const QModelIndexList& indexes) const
if (row < 0 || row >= this->m_worlds.size()) if (row < 0 || row >= this->m_worlds.size())
continue; continue;
const World& world = m_worlds[row]; const World& world = m_worlds[row].world;
if (!world.isValid() || !world.isOnFS()) if (!world.isValid() || !world.isOnFS())
continue; continue;
@ -446,7 +457,7 @@ int64_t MultiWorldList::calculateWorldSize(const QFileInfo& file)
void MultiWorldList::loadWorldsAsync() void MultiWorldList::loadWorldsAsync()
{ {
for (int i = 0; i < m_worlds.size(); ++i) { for (int i = 0; i < m_worlds.size(); ++i) {
auto file = m_worlds.at(i).container(); auto file = m_worlds.at(i).world.container();
int row = i; int row = i;
QThreadPool::globalInstance()->start([this, file, row]() mutable { QThreadPool::globalInstance()->start([this, file, row]() mutable {
auto size = calculateWorldSize(file); auto size = calculateWorldSize(file);
@ -454,8 +465,8 @@ void MultiWorldList::loadWorldsAsync()
QMetaObject::invokeMethod( QMetaObject::invokeMethod(
this, this,
[this, size, row, file]() { [this, size, row, file]() {
if (row < m_worlds.size() && m_worlds[row].container() == file) { if (row < m_worlds.size() && m_worlds[row].world.container() == file) {
m_worlds[row].setSize(size); m_worlds[row].world.setSize(size);
// Notify views // Notify views
QModelIndex modelIndex = index(row, SizeColumn); QModelIndex modelIndex = index(row, SizeColumn);

View file

@ -25,6 +25,11 @@
class QFileSystemWatcher; class QFileSystemWatcher;
struct InstanceWorld {
World world;
BaseInstance* instance;
};
class MultiWorldList : public QAbstractListModel { class MultiWorldList : public QAbstractListModel {
Q_OBJECT Q_OBJECT
public: public:
@ -32,7 +37,7 @@ class MultiWorldList : public QAbstractListModel {
enum Roles { ObjectRole = Qt::UserRole + 1, FolderRole, SeedRole, NameRole, GameModeRole, LastPlayedRole, SizeRole, IconFileRole }; enum Roles { ObjectRole = Qt::UserRole + 1, FolderRole, SeedRole, NameRole, GameModeRole, LastPlayedRole, SizeRole, IconFileRole };
MultiWorldList(const QList<QString>& dirs, QList<BaseInstance*> instances); MultiWorldList(const QList<QString>& dirs, const QList<BaseInstance*>& instances);
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
@ -42,7 +47,7 @@ class MultiWorldList : public QAbstractListModel {
size_t size() const { return m_worlds.size(); }; size_t size() const { return m_worlds.size(); };
bool empty() const { return size() == 0; } bool empty() const { return size() == 0; }
World& operator[](size_t index) { return m_worlds[index]; } World& operator[](size_t index) { return m_worlds[index].world; }
/// Reloads the mod list and returns true if the list changed. /// Reloads the mod list and returns true if the list changed.
virtual bool update(); virtual bool update();
@ -83,7 +88,7 @@ class MultiWorldList : public QAbstractListModel {
QList<QString> instDirPaths() const; QList<QString> instDirPaths() const;
const QList<World>& allWorlds() const { return m_worlds; } const QList<InstanceWorld>& allWorlds() const { return m_worlds; }
private slots: private slots:
void directoryChanged(QString path); void directoryChanged(QString path);
@ -97,5 +102,5 @@ class MultiWorldList : public QAbstractListModel {
QFileSystemWatcher* m_watcher; QFileSystemWatcher* m_watcher;
bool m_isWatching; bool m_isWatching;
QList<QDir> m_dirs; QList<QDir> m_dirs;
QList<World> m_worlds; QList<InstanceWorld> m_worlds;
}; };

View file

@ -339,7 +339,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
// Create the all worlds widget // Create the all worlds widget
{ {
QList<BaseInstance*> allInstances = APPLICATION->instances()->getAllInstances(); QList<BaseInstance*> allInstances = APPLICATION->instances()->getAllInstances();
qDebug() << "iy initially" << allInstances.length();
QList<QString> dirs; QList<QString> dirs;
for (BaseInstance* inst : allInstances) { for (BaseInstance* inst : allInstances) {
dirs.append(dynamic_cast<MinecraftInstance*>(inst)->worldDir()); dirs.append(dynamic_cast<MinecraftInstance*>(inst)->worldDir());
@ -347,7 +346,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
allWorlds = new MultiWorldList(dirs, allInstances); allWorlds = new MultiWorldList(dirs, allInstances);
allWorlds->update(); allWorlds->update();
allWorldsPage = new MultiWorldListPage(dynamic_cast<MinecraftInstance*>(allInstances[0]), allWorlds); //shouldnt be only one instance iy allWorldsPage = new MultiWorldListPage(allWorlds);
ui->horizontalLayout->addWidget(allWorldsPage); ui->horizontalLayout->addWidget(allWorldsPage);
} }
@ -945,10 +944,7 @@ void MainWindow::addInstance(const QString& url, const QMap<QString, QString>& e
instanceFromInstanceTask(creationTask); instanceFromInstanceTask(creationTask);
} }
//clean this up iy - fix ghosting issue and only joining one world despite which one you click
QList<BaseInstance*> allInstances = APPLICATION->instances()->getAllInstances(); QList<BaseInstance*> allInstances = APPLICATION->instances()->getAllInstances();
qDebug() << "iy finally " << allInstances.length();
QList<QString> dirs; QList<QString> dirs;
for (BaseInstance* inst : allInstances) { for (BaseInstance* inst : allInstances) {
dirs.append(dynamic_cast<MinecraftInstance*>(inst)->worldDir()); dirs.append(dynamic_cast<MinecraftInstance*>(inst)->worldDir());
@ -956,8 +952,9 @@ void MainWindow::addInstance(const QString& url, const QMap<QString, QString>& e
allWorlds = new MultiWorldList(dirs, allInstances); allWorlds = new MultiWorldList(dirs, allInstances);
allWorlds->update(); allWorlds->update();
auto newAllWorldsPage = new MultiWorldListPage(dynamic_cast<MinecraftInstance*>(allInstances[0]), allWorlds); //shouldnt be only one instance iy auto newAllWorldsPage = new MultiWorldListPage(allWorlds);
ui->horizontalLayout->replaceWidget(allWorldsPage, newAllWorldsPage); ui->horizontalLayout->replaceWidget(allWorldsPage, newAllWorldsPage);
delete allWorldsPage;
allWorldsPage = newAllWorldsPage; allWorldsPage = newAllWorldsPage;
} }

View file

@ -86,14 +86,14 @@ class MultiWorldListProxyModel : public QSortFilterProxyModel {
} }
}; };
MultiWorldListPage::MultiWorldListPage(MinecraftInstance* inst, MultiWorldList* worlds, QWidget* parent) //require all instances instead of just one iy MultiWorldListPage::MultiWorldListPage(MultiWorldList* worlds, QWidget* parent)
: QMainWindow(parent), m_inst(inst), ui(new Ui::MultiWorldListPage), m_worlds(worlds) : QMainWindow(parent), ui(new Ui::MultiWorldListPage), m_worlds(worlds)
{ {
ui->setupUi(this); ui->setupUi(this);
ui->toolBar->insertSpacer(ui->actionRefresh); ui->toolBar->insertSpacer(ui->actionRefresh);
MultiWorldListProxyModel* proxy = new MultiWorldListProxyModel(this); auto* proxy = new MultiWorldListProxyModel(this);
proxy->setSortCaseSensitivity(Qt::CaseInsensitive); proxy->setSortCaseSensitivity(Qt::CaseInsensitive);
proxy->setSourceModel(m_worlds); proxy->setSourceModel(m_worlds);
proxy->setSortRole(Qt::UserRole); proxy->setSortRole(Qt::UserRole);
@ -195,7 +195,7 @@ void MultiWorldListPage::on_actionRemove_triggered()
tr("You are about to delete \"%1\".\n" tr("You are about to delete \"%1\".\n"
"The world may be gone forever (A LONG TIME).\n\n" "The world may be gone forever (A LONG TIME).\n\n"
"Are you sure?") "Are you sure?")
.arg(m_worlds->allWorlds().at(proxiedIndex.row()).name()), .arg(m_worlds->allWorlds().at(proxiedIndex.row()).world.name()),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec(); ->exec();
@ -237,9 +237,9 @@ void MultiWorldListPage::on_actionData_Packs_triggered()
GenericPageProvider provider(dialog->windowTitle()); GenericPageProvider provider(dialog->windowTitle());
bool isIndexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); bool isIndexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_datapackModel.reset(new DataPackFolderModel(folder, m_inst, isIndexed, true)); m_datapackModel.reset(new DataPackFolderModel(folder, m_worlds->allWorlds()[0].instance, isIndexed, true)); //don't use instance 0 iy
provider.addPageCreator([this] { return new DataPackPage(m_inst, m_datapackModel.get(), this); }); provider.addPageCreator([this] { return new DataPackPage(m_worlds->allWorlds()[0].instance, m_datapackModel.get(), this); }); //no instance 0 iy
auto layout = new QVBoxLayout(dialog); auto layout = new QVBoxLayout(dialog);
@ -402,7 +402,7 @@ void MultiWorldListPage::on_actionAdd_triggered()
bool MultiWorldListPage::isWorldSafe(QModelIndex) bool MultiWorldListPage::isWorldSafe(QModelIndex)
{ {
return !m_inst->isRunning(); return !m_worlds->allWorlds()[0].instance->isRunning(); //don't use instance 0 iy
} }
bool MultiWorldListPage::worldSafetyNagQuestion(const QString& actionType) bool MultiWorldListPage::worldSafetyNagQuestion(const QString& actionType)
@ -471,8 +471,8 @@ void MultiWorldListPage::on_actionJoin_triggered()
return; return;
} }
auto worldVariant = m_worlds->data(index, MultiWorldList::ObjectRole); auto worldVariant = m_worlds->data(index, MultiWorldList::ObjectRole);
auto world = (World*)worldVariant.value<void*>(); auto world = (InstanceWorld*)worldVariant.value<void*>();
APPLICATION->launch(m_inst, LaunchMode::Normal, std::make_shared<MinecraftTarget>(MinecraftTarget::parse(world->folderName(), true))); APPLICATION->launch(world->instance, LaunchMode::Normal, std::make_shared<MinecraftTarget>(MinecraftTarget::parse(world->world.folderName(), true)));
} }
#include "MultiWorldListPage.moc" #include "MultiWorldListPage.moc"

View file

@ -52,7 +52,7 @@ class MultiWorldListPage : public QMainWindow, public BasePage {
Q_OBJECT Q_OBJECT
public: public:
explicit MultiWorldListPage(MinecraftInstance* inst, MultiWorldList* worlds, QWidget* parent = 0); explicit MultiWorldListPage(MultiWorldList* worlds, QWidget* parent = 0);
virtual ~MultiWorldListPage(); virtual ~MultiWorldListPage();
virtual QString displayName() const override { return tr("Worlds"); } virtual QString displayName() const override { return tr("Worlds"); }
@ -70,9 +70,6 @@ class MultiWorldListPage : public QMainWindow, public BasePage {
bool worldListFilter(QKeyEvent* ev); bool worldListFilter(QKeyEvent* ev);
QMenu* createPopupMenu() override; QMenu* createPopupMenu() override;
protected:
MinecraftInstance* m_inst;
private: private:
QModelIndex getSelectedWorld(); QModelIndex getSelectedWorld();
bool isWorldSafe(QModelIndex index); bool isWorldSafe(QModelIndex index);