diff options
author | Petr Mrázek <peterix@gmail.com> | 2015-09-06 23:35:58 +0200 |
---|---|---|
committer | Petr Mrázek <peterix@gmail.com> | 2015-09-06 23:35:58 +0200 |
commit | 38693e1d6ca7f05d9488348ddf298488d1cc0995 (patch) | |
tree | d99551fc3ebbef931d10ee45cb34f2ee791cc1fe /logic | |
parent | 40b233448c7a3977d45831b8aa6cf31019c03940 (diff) | |
download | MultiMC-38693e1d6ca7f05d9488348ddf298488d1cc0995.tar MultiMC-38693e1d6ca7f05d9488348ddf298488d1cc0995.tar.gz MultiMC-38693e1d6ca7f05d9488348ddf298488d1cc0995.tar.lz MultiMC-38693e1d6ca7f05d9488348ddf298488d1cc0995.tar.xz MultiMC-38693e1d6ca7f05d9488348ddf298488d1cc0995.zip |
GH-1047 parse world files and integrate MCEdit with world page
Diffstat (limited to 'logic')
-rw-r--r-- | logic/CMakeLists.txt | 2 | ||||
-rw-r--r-- | logic/minecraft/World.cpp | 126 | ||||
-rw-r--r-- | logic/minecraft/World.h | 24 | ||||
-rw-r--r-- | logic/minecraft/WorldList.cpp | 34 | ||||
-rw-r--r-- | logic/minecraft/WorldList.h | 144 |
5 files changed, 251 insertions, 79 deletions
diff --git a/logic/CMakeLists.txt b/logic/CMakeLists.txt index df2aea28..183c8269 100644 --- a/logic/CMakeLists.txt +++ b/logic/CMakeLists.txt @@ -329,7 +329,7 @@ else(UNIX) endif(UNIX) # Link -target_link_libraries(MultiMC_logic xz-embedded unpack200 iconfix libUtil LogicalGui ${QUAZIP_LIBRARIES} +target_link_libraries(MultiMC_logic xz-embedded unpack200 iconfix libUtil LogicalGui ${QUAZIP_LIBRARIES} nbt++ Qt5::Core Qt5::Xml Qt5::Widgets Qt5::Network Qt5::Concurrent ${ZLIB_LIBRARIES} ${MultiMC_LINK_ADDITIONAL_LIBS}) diff --git a/logic/minecraft/World.cpp b/logic/minecraft/World.cpp index 3443fb45..4ef7845f 100644 --- a/logic/minecraft/World.cpp +++ b/logic/minecraft/World.cpp @@ -15,9 +15,16 @@ #include <QDir> #include <QString> +#include <QDebug> #include "World.h" #include <pathutils.h> +#include "GZip.h" +#include <sstream> +#include <io/stream_reader.h> +#include <tag_string.h> +#include <tag_primitive.h> + World::World(const QFileInfo &file) { repath(file); @@ -26,8 +33,117 @@ World::World(const QFileInfo &file) void World::repath(const QFileInfo &file) { m_file = file; - m_name = file.fileName(); - is_valid = file.isDir() && QDir(file.filePath()).exists("level.dat"); + m_folderName = file.fileName(); + QDir worldDir(file.filePath()); + is_valid = file.isDir() && worldDir.exists("level.dat"); + if(!is_valid) + { + return; + } + + auto fullFilePath = worldDir.absoluteFilePath("level.dat"); + QFile f(fullFilePath); + is_valid = f.open(QIODevice::ReadOnly); + if(!is_valid) + { + return; + } + + QByteArray output; + is_valid = GZip::inflate(f.readAll(), output); + if(!is_valid) + { + return; + } + f.close(); + + auto read_string = [](nbt::value& parent, const char * name, const QString & fallback = QString()) -> QString + { + try + { + auto &namedValue = parent.at(name); + if(namedValue.get_type() != nbt::tag_type::String) + { + return fallback; + } + auto & tag_str = namedValue.as<nbt::tag_string>(); + return QString::fromStdString(tag_str.get()); + } + catch(std::out_of_range e) + { + // fallback for old world formats + return fallback; + } + }; + + auto read_long = [](nbt::value& parent, const char * name, const int64_t & fallback = 0) -> int64_t + { + try + { + auto &namedValue = parent.at(name); + if(namedValue.get_type() != nbt::tag_type::Long) + { + return fallback; + } + auto & tag_str = namedValue.as<nbt::tag_long>(); + return tag_str.get(); + } + catch(std::out_of_range e) + { + // fallback for old world formats + return fallback; + } + }; + + try + { + std::istringstream foo(std::string(output.constData(), output.size())); + auto pair = nbt::io::read_compound(foo); + is_valid = pair.first == ""; + + if(!is_valid) + { + return; + } + std::ostringstream ostr; + is_valid = pair.second != nullptr; + if(!is_valid) + { + qDebug() << "FAIL~!!!"; + return; + } + + auto &val = pair.second->at("Data"); + is_valid = val.get_type() == nbt::tag_type::Compound; + if(!is_valid) + return; + + m_actualName = read_string(val, "LevelName", m_folderName); + + + int64_t temp = read_long(val, "LastPlayed", 0); + if(temp == 0) + { + QFileInfo finfo(fullFilePath); + m_lastPlayed = finfo.lastModified(); + } + else + { + m_lastPlayed = QDateTime::fromMSecsSinceEpoch(temp); + } + + m_randomSeed = read_long(val, "RandomSeed", 0); + + qDebug() << "World Name:" << m_actualName; + qDebug() << "Last Played:" << m_lastPlayed.toString(); + qDebug() << "Seed:" << m_randomSeed; + } + catch (nbt::io::input_error e) + { + qWarning() << "Unable to load" << m_folderName << ":" << e.what(); + is_valid = false; + return; + } } bool World::replace(World &with) @@ -37,7 +153,7 @@ bool World::replace(World &with) bool success = copyPath(with.m_file.filePath(), m_file.path()); if (success) { - m_name = with.m_name; + m_folderName = with.m_folderName; m_file.refresh(); } return success; @@ -64,9 +180,9 @@ bool World::destroy() bool World::operator==(const World &other) const { - return is_valid == other.is_valid && name() == other.name(); + return is_valid == other.is_valid && folderName() == other.folderName(); } bool World::strongCompare(const World &other) const { - return is_valid == other.is_valid && name() == other.name(); + return is_valid == other.is_valid && folderName() == other.folderName(); } diff --git a/logic/minecraft/World.h b/logic/minecraft/World.h index 60151dd6..91cb2a83 100644 --- a/logic/minecraft/World.h +++ b/logic/minecraft/World.h @@ -15,20 +15,33 @@ #pragma once #include <QFileInfo> +#include <QDateTime> class World { public: World(const QFileInfo &file); + QString folderName() const + { + return m_folderName; + } QString name() const { - return m_name; + return m_actualName; + } + QDateTime lastPlayed() const + { + return m_lastPlayed; + } + int64_t seed() const + { + return m_randomSeed; } bool isValid() const { return is_valid; } -// // delete all the files of this world + // delete all the files of this world bool destroy(); // replace this world with a copy of the other bool replace(World &with); @@ -42,6 +55,9 @@ public: protected: QFileInfo m_file; - QString m_name; - bool is_valid; + QString m_folderName; + QString m_actualName; + QDateTime m_lastPlayed; + int64_t m_randomSeed = 0; + bool is_valid = false; }; diff --git a/logic/minecraft/WorldList.cpp b/logic/minecraft/WorldList.cpp index 7d54c0ba..7066093c 100644 --- a/logic/minecraft/WorldList.cpp +++ b/logic/minecraft/WorldList.cpp @@ -66,7 +66,7 @@ void WorldList::internalSort(QList<World> &what) { auto predicate = [](const World &left, const World &right) { - return left.name().localeAwareCompare(right.name()) < 0; + return left.folderName().localeAwareCompare(right.folderName()) < 0; }; std::sort(what.begin(), what.end(), predicate); } @@ -142,7 +142,7 @@ bool WorldList::deleteWorlds(int first, int last) int WorldList::columnCount(const QModelIndex &parent) const { - return 1; + return 2; } QVariant WorldList::data(const QModelIndex &index, int role) const @@ -156,20 +156,42 @@ QVariant WorldList::data(const QModelIndex &index, int role) const if (row < 0 || row >= worlds.size()) return QVariant(); + auto & world = worlds[row]; switch (role) { case Qt::DisplayRole: switch (column) { case NameColumn: - return worlds[row].name(); + return world.name(); + + case LastPlayedColumn: + return world.lastPlayed(); default: return QVariant(); } case Qt::ToolTipRole: - return worlds[row].name(); + { + return world.folderName(); + } + case FolderRole: + { + return QDir::toNativeSeparators(dir().absoluteFilePath(world.folderName())); + } + case SeedRole: + { + return qVariantFromValue<qlonglong>(world.seed()); + } + case NameRole: + { + return world.name(); + } + case LastPlayedRole: + { + return world.lastPlayed(); + } default: return QVariant(); } @@ -184,6 +206,8 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol { case NameColumn: return tr("Name"); + case LastPlayedColumn: + return tr("Last Played"); default: return QVariant(); } @@ -193,6 +217,8 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol { case NameColumn: return tr("The name of the world."); + case LastPlayedColumn: + return tr("Date and time the world was last played."); default: return QVariant(); } diff --git a/logic/minecraft/WorldList.h b/logic/minecraft/WorldList.h index 8d3d937c..7f119e81 100644 --- a/logic/minecraft/WorldList.h +++ b/logic/minecraft/WorldList.h @@ -21,79 +21,93 @@ #include <QAbstractListModel> #include "minecraft/World.h" +#include "multimc_logic_export.h" + class QFileSystemWatcher; -class WorldList : public QAbstractListModel +class MULTIMC_LOGIC_EXPORT WorldList : public QAbstractListModel { - Q_OBJECT + Q_OBJECT public: - enum Columns { - NameColumn - }; - - WorldList ( const QString &dir ); - - virtual QVariant data ( const QModelIndex &index, int role = Qt::DisplayRole ) const; - - virtual int rowCount ( const QModelIndex &parent = QModelIndex() ) const { - return size(); - } - ; - virtual QVariant headerData ( int section, Qt::Orientation orientation, - int role = Qt::DisplayRole ) const; - virtual int columnCount ( const QModelIndex &parent ) const; - - size_t size() const { - return worlds.size(); - } - ; - bool empty() const { - return size() == 0; - } - World &operator[] ( size_t index ) { - return worlds[index]; - } - - /// Reloads the mod list and returns true if the list changed. - virtual bool update(); - - /// Deletes the mod at the given index. - virtual bool deleteWorld ( int index ); - - /// Deletes all the selected mods - virtual bool deleteWorlds ( int first, int last ); - - /// get data for drag action - virtual QMimeData *mimeData ( const QModelIndexList &indexes ) const; - /// get the supported mime types - virtual QStringList mimeTypes() const; - - void startWatching(); - void stopWatching(); - - virtual bool isValid(); - - QDir dir() { - return m_dir; - } - - const QList<World> & allWorlds() { - return worlds; - } + enum Columns + { + NameColumn, + LastPlayedColumn + }; + + enum Roles + { + FolderRole = Qt::UserRole + 1, + SeedRole, + NameRole, + LastPlayedRole + }; + + WorldList(const QString &dir); + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const + { + return size(); + }; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + virtual int columnCount(const QModelIndex &parent) const; + + size_t size() const + { + return worlds.size(); + }; + bool empty() const + { + return size() == 0; + } + World &operator[](size_t index) + { + return worlds[index]; + } + + /// Reloads the mod list and returns true if the list changed. + virtual bool update(); + + /// Deletes the mod at the given index. + virtual bool deleteWorld(int index); + + /// Deletes all the selected mods + virtual bool deleteWorlds(int first, int last); + + /// get data for drag action + virtual QMimeData *mimeData(const QModelIndexList &indexes) const; + /// get the supported mime types + virtual QStringList mimeTypes() const; + + void startWatching(); + void stopWatching(); + + virtual bool isValid(); + + QDir dir() const + { + return m_dir; + } + + const QList<World> &allWorlds() const + { + return worlds; + } private: - void internalSort ( QList<World> & what ); - private -slots: - void directoryChanged ( QString path ); + void internalSort(QList<World> &what); +private slots: + void directoryChanged(QString path); signals: - void changed(); + void changed(); protected: - QFileSystemWatcher *m_watcher; - bool is_watching; - QDir m_dir; - QList<World> worlds; + QFileSystemWatcher *m_watcher; + bool is_watching; + QDir m_dir; + QList<World> worlds; }; - |