GH-1047 parse world files and integrate MCEdit with world page

This commit is contained in:
Petr Mrázek
2015-09-06 23:35:58 +02:00
parent 40b233448c
commit 38693e1d6c
58 changed files with 6079 additions and 161 deletions

View File

@ -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})

View File

@ -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();
}

View File

@ -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;
};

View File

@ -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();
}

View File

@ -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
};
enum Columns
{
NameColumn,
LastPlayedColumn
};
WorldList ( const QString &dir );
enum Roles
{
FolderRole = Qt::UserRole + 1,
SeedRole,
NameRole,
LastPlayedRole
};
virtual QVariant data ( const QModelIndex &index, int role = Qt::DisplayRole ) const;
WorldList(const QString &dir);
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;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
size_t size() const {
return worlds.size();
}
;
bool empty() const {
return size() == 0;
}
World &operator[] ( size_t index ) {
return worlds[index];
}
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;
/// Reloads the mod list and returns true if the list changed.
virtual bool update();
size_t size() const
{
return worlds.size();
};
bool empty() const
{
return size() == 0;
}
World &operator[](size_t index)
{
return worlds[index];
}
/// Deletes the mod at the given index.
virtual bool deleteWorld ( int index );
/// Reloads the mod list and returns true if the list changed.
virtual bool update();
/// Deletes all the selected mods
virtual bool deleteWorlds ( int first, int last );
/// Deletes the mod at the given index.
virtual bool deleteWorld(int index);
/// get data for drag action
virtual QMimeData *mimeData ( const QModelIndexList &indexes ) const;
/// get the supported mime types
virtual QStringList mimeTypes() const;
/// Deletes all the selected mods
virtual bool deleteWorlds(int first, int last);
void startWatching();
void stopWatching();
/// get data for drag action
virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
/// get the supported mime types
virtual QStringList mimeTypes() const;
virtual bool isValid();
void startWatching();
void stopWatching();
QDir dir() {
return m_dir;
}
virtual bool isValid();
const QList<World> & allWorlds() {
return worlds;
}
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;
};