aboutsummaryrefslogtreecommitdiff
path: root/api/logic/minecraft/World.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'api/logic/minecraft/World.cpp')
-rw-r--r--api/logic/minecraft/World.cpp239
1 files changed, 187 insertions, 52 deletions
diff --git a/api/logic/minecraft/World.cpp b/api/logic/minecraft/World.cpp
index b39f940e..a2b4dac7 100644
--- a/api/logic/minecraft/World.cpp
+++ b/api/logic/minecraft/World.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,79 @@
#include <quazipfile.h>
#include <quazipdir.h>
+#include <QCoreApplication>
+
+#include <nonstd/optional>
+
+using nonstd::optional;
+using nonstd::nullopt;
+
+GameType::GameType(nonstd::optional<int> original):
+ original(original)
+{
+ if(!original) {
+ return;
+ }
+ switch(*original) {
+ case 0:
+ type = GameType::Survival;
+ break;
+ case 1:
+ type = GameType::Creative;
+ break;
+ case 2:
+ type = GameType::Adventure;
+ break;
+ case 3:
+ type = GameType::Spectator;
+ break;
+ default:
+ break;
+ }
+}
+
+QString GameType::toTranslatedString() const
+{
+ switch (type)
+ {
+ case GameType::Survival:
+ return QCoreApplication::translate("GameType", "Survival");
+ case GameType::Creative:
+ return QCoreApplication::translate("GameType", "Creative");
+ case GameType::Adventure:
+ return QCoreApplication::translate("GameType", "Adventure");
+ case GameType::Spectator:
+ return QCoreApplication::translate("GameType", "Spectator");
+ default:
+ break;
+ }
+ if(original) {
+ return QCoreApplication::translate("GameType", "Unknown (%1)").arg(*original);
+ }
+ return QCoreApplication::translate("GameType", "Undefined");
+}
+
+QString GameType::toLogString() const
+{
+ switch (type)
+ {
+ case GameType::Survival:
+ return "Survival";
+ case GameType::Creative:
+ return "Creative";
+ case GameType::Adventure:
+ return "Adventure";
+ case GameType::Spectator:
+ return "Spectator";
+ default:
+ break;
+ }
+ if(original) {
+ return QString("Unknown (%1)").arg(*original);
+ }
+ return "Undefined";
+}
+
std::unique_ptr <nbt::tag_compound> parseLevelDat(QByteArray data)
{
QByteArray output;
@@ -38,15 +111,22 @@ std::unique_ptr <nbt::tag_compound> parseLevelDat(QByteArray data)
return nullptr;
}
std::istringstream foo(std::string(output.constData(), output.size()));
- auto pair = nbt::io::read_compound(foo);
+ try {
+ auto pair = nbt::io::read_compound(foo);
- if(pair.first != "")
- return nullptr;
+ if(pair.first != "")
+ return nullptr;
- if(pair.second == nullptr)
- return nullptr;
+ if(pair.second == nullptr)
+ return nullptr;
- return std::move(pair.second);
+ return std::move(pair.second);
+ }
+ catch (const nbt::io::input_error &e)
+ {
+ qWarning() << "Unable to parse level.dat:" << e.what();
+ return nullptr;
+ }
}
QByteArray serializeLevelDat(nbt::tag_compound * levelInfo)
@@ -118,14 +198,31 @@ void World::repath(const QFileInfo &file)
m_folderName = file.fileName();
if(file.isFile() && file.suffix() == "zip")
{
+ m_iconFile = QString();
readFromZip(file);
}
else if(file.isDir())
{
+ QFileInfo assumedIconPath(file.absoluteFilePath() + "/icon.png");
+ if(assumedIconPath.exists()) {
+ m_iconFile = assumedIconPath.absoluteFilePath();
+ }
readFromFS(file);
}
}
+bool World::resetIcon()
+{
+ if(m_iconFile.isNull()) {
+ return false;
+ }
+ if(QFile(m_iconFile).remove()) {
+ m_iconFile = QString();
+ return true;
+ }
+ return false;
+}
+
void World::readFromFS(const QFileInfo &file)
{
auto bytes = getLevelDatDataFromFS(file);
@@ -192,7 +289,7 @@ bool World::install(const QString &to, const QString &name)
{
return false;
}
- ok = !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath).isEmpty();
+ ok = !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath);
}
else if(m_containerFile.isDir())
{
@@ -251,14 +348,16 @@ bool World::rename(const QString &newName)
return true;
}
-static QString read_string (nbt::value& parent, const char * name, const QString & fallback = QString())
+namespace {
+
+optional<QString> read_string (nbt::value& parent, const char * name)
{
try
{
auto &namedValue = parent.at(name);
if(namedValue.get_type() != nbt::tag_type::String)
{
- return fallback;
+ return nullopt;
}
auto & tag_str = namedValue.as<nbt::tag_string>();
return QString::fromStdString(tag_str.get());
@@ -266,25 +365,25 @@ static QString read_string (nbt::value& parent, const char * name, const QString
catch (const std::out_of_range &e)
{
// fallback for old world formats
- qWarning() << "String NBT tag" << name << "could not be found. Defaulting to" << fallback;
- return fallback;
+ qWarning() << "String NBT tag" << name << "could not be found.";
+ return nullopt;
}
catch (const std::bad_cast &e)
{
// type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to string. Defaulting to" << fallback;
- return fallback;
+ qWarning() << "NBT tag" << name << "could not be converted to string.";
+ return nullopt;
}
}
-static int64_t read_long (nbt::value& parent, const char * name, const int64_t & fallback = 0)
+optional<int64_t> read_long (nbt::value& parent, const char * name)
{
try
{
auto &namedValue = parent.at(name);
if(namedValue.get_type() != nbt::tag_type::Long)
{
- return fallback;
+ return nullopt;
}
auto & tag_str = namedValue.as<nbt::tag_long>();
return tag_str.get();
@@ -292,58 +391,98 @@ static int64_t read_long (nbt::value& parent, const char * name, const int64_t &
catch (const std::out_of_range &e)
{
// fallback for old world formats
- qWarning() << "Long NBT tag" << name << "could not be found. Defaulting to" << fallback;
- return fallback;
+ qWarning() << "Long NBT tag" << name << "could not be found.";
+ return nullopt;
}
catch (const std::bad_cast &e)
{
// type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to long. Defaulting to" << fallback;
- return fallback;
+ qWarning() << "NBT tag" << name << "could not be converted to long.";
+ return nullopt;
}
}
-void World::loadFromLevelDat(QByteArray data)
+optional<int> read_int (nbt::value& parent, const char * name)
{
try
{
- auto levelData = parseLevelDat(data);
- if(!levelData)
+ auto &namedValue = parent.at(name);
+ if(namedValue.get_type() != nbt::tag_type::Int)
{
- is_valid = false;
- return;
+ return nullopt;
}
+ auto & tag_str = namedValue.as<nbt::tag_int>();
+ return tag_str.get();
+ }
+ catch (const std::out_of_range &e)
+ {
+ // fallback for old world formats
+ qWarning() << "Int NBT tag" << name << "could not be found.";
+ return nullopt;
+ }
+ catch (const std::bad_cast &e)
+ {
+ // type mismatch
+ qWarning() << "NBT tag" << name << "could not be converted to int.";
+ return nullopt;
+ }
+}
- auto &val = levelData->at("Data");
- is_valid = val.get_type() == nbt::tag_type::Compound;
- if(!is_valid)
- return;
-
- m_actualName = read_string(val, "LevelName", m_folderName);
-
+GameType read_gametype(nbt::value& parent, const char * name) {
+ return GameType(read_int(parent, name));
+}
- int64_t temp = read_long(val, "LastPlayed", 0);
- if(temp == 0)
- {
- m_lastPlayed = levelDatTime;
- }
- else
- {
- m_lastPlayed = QDateTime::fromMSecsSinceEpoch(temp);
- }
+}
- m_randomSeed = read_long(val, "RandomSeed", 0);
+void World::loadFromLevelDat(QByteArray data)
+{
+ auto levelData = parseLevelDat(data);
+ if(!levelData)
+ {
+ is_valid = false;
+ return;
+ }
- qDebug() << "World Name:" << m_actualName;
- qDebug() << "Last Played:" << m_lastPlayed.toString();
- qDebug() << "Seed:" << m_randomSeed;
+ nbt::value * valPtr = nullptr;
+ try {
+ valPtr = &levelData->at("Data");
}
- catch (const nbt::io::input_error &e)
- {
- qWarning() << "Unable to load" << m_folderName << ":" << e.what();
+ catch (const std::out_of_range &e) {
+ qWarning() << "Unable to read NBT tags from " << m_folderName << ":" << e.what();
is_valid = false;
return;
}
+ nbt::value &val = *valPtr;
+
+ is_valid = val.get_type() == nbt::tag_type::Compound;
+ if(!is_valid)
+ return;
+
+ auto name = read_string(val, "LevelName");
+ m_actualName = name ? *name : m_folderName;
+
+ auto timestamp = read_long(val, "LastPlayed");
+ m_lastPlayed = timestamp ? QDateTime::fromMSecsSinceEpoch(*timestamp) : levelDatTime;
+
+ m_gameType = read_gametype(val, "GameType");
+
+ optional<int64_t> randomSeed;
+ try {
+ auto &WorldGen_val = val.at("WorldGenSettings");
+ randomSeed = read_long(WorldGen_val, "seed");
+ }
+ catch (const std::out_of_range &) {}
+ if(!randomSeed) {
+ randomSeed = read_long(val, "RandomSeed");
+ }
+ m_randomSeed = randomSeed ? *randomSeed : 0;
+
+ qDebug() << "World Name:" << m_actualName;
+ qDebug() << "Last Played:" << m_lastPlayed.toString();
+ if(randomSeed) {
+ qDebug() << "Seed:" << *randomSeed;
+ }
+ qDebug() << "GameType:" << m_gameType.toLogString();
}
bool World::replace(World &with)
@@ -379,7 +518,3 @@ bool World::operator==(const World &other) const
{
return is_valid == other.is_valid && folderName() == other.folderName();
}
-bool World::strongCompare(const World &other) const
-{
- return is_valid == other.is_valid && folderName() == other.folderName();
-}