summaryrefslogtreecommitdiffstats
path: root/logic
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2015-09-06 23:35:58 +0200
committerPetr Mrázek <peterix@gmail.com>2015-09-06 23:35:58 +0200
commit38693e1d6ca7f05d9488348ddf298488d1cc0995 (patch)
treed99551fc3ebbef931d10ee45cb34f2ee791cc1fe /logic
parent40b233448c7a3977d45831b8aa6cf31019c03940 (diff)
downloadMultiMC-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.txt2
-rw-r--r--logic/minecraft/World.cpp126
-rw-r--r--logic/minecraft/World.h24
-rw-r--r--logic/minecraft/WorldList.cpp34
-rw-r--r--logic/minecraft/WorldList.h144
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;
};
-