From 2f9244c17c61b4ade210f3ba86133f166c2f1e17 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Dec 2023 22:03:59 +0300 Subject: [PATCH] New world and player files format --- src/coders/json.cpp | 36 +++---- src/content/ContentLoader.cpp | 10 +- src/files/WorldFiles.cpp | 174 +++++++++++++++++++++++----------- src/files/WorldFiles.h | 9 +- src/files/files.cpp | 13 ++- src/files/files.h | 5 + 6 files changed, 168 insertions(+), 79 deletions(-) diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 73c8a579..94d5d620 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -320,8 +320,12 @@ void JObject::num(std::string key, uint& dst) const { JObject* JObject::obj(std::string key) const { auto found = map.find(key); - if (found != map.end()) - return found->second->value.obj; + if (found != map.end()) { + auto& val = found->second; + if (val->type != valtype::object) + return nullptr; + return val->value.obj; + } return nullptr; } @@ -500,6 +504,19 @@ JArray* Parser::parseArray() { Value* Parser::parseValue() { char next = peek(); valvalue val; + if (next == '-' || next == '+') { + pos++; + number_u num; + valtype type; + if (parseNumber(next == '-' ? -1 : 1, num)) { + val.integer = num.ival; + type = valtype::integer; + } else { + val.decimal = num.fval; + type = valtype::number; + } + return new Value(type, val); + } if (is_identifier_start(next)) { string literal = parseName(); if (literal == "true") { @@ -515,7 +532,7 @@ Value* Parser::parseValue() { val.decimal = NAN; return new Value(valtype::number, val); } - throw error("invalid literal"); + throw error("invalid literal "); } if (next == '{') { val.obj = parseObject(); @@ -525,19 +542,6 @@ Value* Parser::parseValue() { val.arr = parseArray(); return new Value(valtype::array, val); } - if (next == '-' || next == '+') { - pos++; - number_u num; - valtype type; - if (parseNumber(next == '-' ? -1 : 1, num)) { - val.integer = num.ival; - type = valtype::integer; - } else { - val.decimal = num.fval; - type = valtype::number; - } - return new Value(type, val); - } if (is_digit(next)) { number_u num; valtype type; diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index d47ae8ae..ee91dfee 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -12,6 +12,7 @@ #include +// don't ask using glm::vec3; using std::cout; using std::cerr; @@ -24,14 +25,7 @@ ContentLoader::ContentLoader(path folder) : folder(folder) {} // TODO: add basic validation and logging Block* ContentLoader::loadBlock(string name, path file) { - string source = files::read_string(file); - unique_ptr root = nullptr; - try { - root.reset(json::parse(file.string(), source)); - } catch (const parsing_error& error) { - cerr << error.errorLog() << endl; - throw std::runtime_error("could not load block def"); - } + unique_ptr root(files::read_json(file)); unique_ptr def(new Block(name)); // block texturing diff --git a/src/files/WorldFiles.cpp b/src/files/WorldFiles.cpp index 77cd1a57..d7d2edc2 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -14,7 +14,11 @@ #include "../maths/voxmaths.h" #include "../world/World.h" +#include "../coders/json.h" +#include "../constants.h" + #include +#include #include #include #include @@ -33,8 +37,10 @@ using glm::ivec2; using glm::vec3; using std::ios; +using std::string; using std::unique_ptr; using std::filesystem::path; +namespace fs = std::filesystem; int bytes2Int(const ubyte* src, size_t offset){ return (src[offset] << 24) | (src[offset+1] << 16) | (src[offset+2] << 8) | (src[offset+3]); @@ -115,15 +121,23 @@ path WorldFiles::getRegionFile(int x, int y) const { } path WorldFiles::getPlayerFile() const { - return directory/path("player.bin"); + return directory/path("player.json"); } path WorldFiles::getWorldFile() const { - return directory/path("world.bin"); + return directory/path("world.json"); } -path WorldFiles::getBlockIndicesFile() const { - return directory/path("blocks.idx"); +path WorldFiles::getIndicesFile() const { + return directory/path("indices.json"); +} + +path WorldFiles::getOldPlayerFile() const { + return directory/path("player.bin"); +} + +path WorldFiles::getOldWorldFile() const { + return directory/path("world.bin"); } ubyte* WorldFiles::getChunk(int x, int y){ @@ -220,42 +234,38 @@ void WorldFiles::write(const World* world, const Content* content) { } void WorldFiles::writeIndices(const ContentIndices* indices) { - /* Blocks indices */ { - BinaryWriter out; - uint count = indices->countBlockDefs(); - out.putInt16(count); - for (uint i = 0; i < count; i++) { - const Block* def = indices->getBlockDef(i); - out.putShortStr(def->name); - } - files::write_bytes(getBlockIndicesFile(), - (const char*)out.data(), out.size()); + json::JObject root; + json::JArray& blocks = root.putArray("blocks"); + uint count = indices->countBlockDefs(); + for (uint i = 0; i < count; i++) { + const Block* def = indices->getBlockDef(i); + blocks.put(def->name); } + files::write_string(getIndicesFile(), json::stringify(&root, true, " ")); } void WorldFiles::writeWorldInfo(const World* world) { - BinaryWriter out; - out.putCStr(WORLD_FORMAT_MAGIC); - out.put(WORLD_FORMAT_VERSION); + json::JObject root; + + json::JObject& versionobj = root.putObj("version"); + versionobj.put("major", ENGINE_VERSION_MAJOR); + versionobj.put("minor", ENGINE_VERSION_MINOR); + + root.put("name", world->name); + root.put("seed", world->seed); - out.put(WORLD_SECTION_MAIN); - out.putInt64(world->seed); - out.put(world->name); + json::JObject& timeobj = root.putObj("time"); + timeobj.put("day-time", world->daytime); + timeobj.put("day-time-speed", world->daytimeSpeed); - out.put(WORLD_SECTION_DAYNIGHT); - out.putFloat32(world->daytime); - out.putFloat32(world->daytimeSpeed); - - files::write_bytes(getWorldFile(), (const char*)out.data(), out.size()); + files::write_string(getWorldFile(), json::stringify(&root, true, " ")); } -bool WorldFiles::readWorldInfo(World* world) { +// TODO: remove in v0.16 +bool WorldFiles::readOldWorldInfo(World* world) { size_t length = 0; ubyte* data = (ubyte*)files::read_bytes(getWorldFile(), length); - if (data == nullptr){ - std::cerr << "could not to read world.bin (ignored)" << std::endl; - return false; - } + assert(data != nullptr); BinaryReader inp(data, length); inp.checkMagic(WORLD_FORMAT_MAGIC, 8); /*ubyte version = */inp.get(); @@ -274,28 +284,7 @@ bool WorldFiles::readWorldInfo(World* world) { } return false; } - -void WorldFiles::writePlayer(Player* player){ - vec3 position = player->hitbox->position; - - BinaryWriter out; - out.put(SECTION_POSITION); - out.putFloat32(position.x); - out.putFloat32(position.y); - out.putFloat32(position.z); - - out.put(SECTION_ROTATION); - out.putFloat32(player->camX); - out.putFloat32(player->camY); - - out.put(SECTION_FLAGS); - out.put(player->flight * PLAYER_FLAG_FLIGHT | - player->noclip * PLAYER_FLAG_NOCLIP); - - files::write_bytes(getPlayerFile(), (const char*)out.data(), out.size()); -} - -bool WorldFiles::readPlayer(Player* player) { +bool WorldFiles::readOldPlayer(Player* player) { size_t length = 0; ubyte* data = (ubyte*)files::read_bytes(getPlayerFile(), length); if (data == nullptr){ @@ -330,6 +319,85 @@ bool WorldFiles::readPlayer(Player* player) { player->camera->position = position + vec3(0, 1, 0); return true; } +// ----- // ----- // + +bool WorldFiles::readWorldInfo(World* world) { + path file = getWorldFile(); + if (!fs::is_regular_file(file)) { + // TODO: remove in v0.16 + file = getOldWorldFile(); + if (fs::is_regular_file(file)) { + readOldWorldInfo(world); + } + std::cerr << "warning: world.json does not exists" << std::endl; + return false; + } + + unique_ptr root(files::read_json(file)); + root->num("seed", world->seed); + + json::JObject* verobj = root->obj("version"); + if (verobj) { + int major=0, minor=-1; + verobj->num("major", major); + verobj->num("minor", minor); + std::cout << "world version: " << major << "." << minor << std::endl; + } + + json::JObject* timeobj = root->obj("time"); + if (timeobj) { + timeobj->num("day-time", world->daytime); + timeobj->num("day-time-speed", world->daytimeSpeed); + } + + return true; +} + +void WorldFiles::writePlayer(Player* player){ + vec3 position = player->hitbox->position; + json::JObject root; + json::JArray& posarr = root.putArray("position"); + posarr.put(position.x); + posarr.put(position.y); + posarr.put(position.z); + + json::JArray& rotarr = root.putArray("rotation"); + rotarr.put(player->camX); + rotarr.put(player->camY); + + root.put("flight", player->flight); + root.put("noclip", player->noclip); + + files::write_string(getPlayerFile(), json::stringify(&root, true, " ")); +} + +bool WorldFiles::readPlayer(Player* player) { + path file = getPlayerFile(); + if (!fs::is_regular_file(file)) { + // TODO: remove in v0.16 + file = getOldPlayerFile(); + if (fs::is_regular_file(file)) { + readOldPlayer(player); + } + std::cerr << "warning: player.json does not exists" << std::endl; + return false; + } + + unique_ptr root(files::read_json(file)); + json::JArray* posarr = root->arr("position"); + vec3& position = player->hitbox->position; + position.x = posarr->num(0); + position.y = posarr->num(1); + position.z = posarr->num(2); + + json::JArray* rotarr = root->arr("rotation"); + player->camX = rotarr->num(0); + player->camY = rotarr->num(1); + + root->flag("flight", player->flight); + root->flag("noclip", player->noclip); + return true; +} void WorldFiles::writeRegion(int x, int y, WorldRegion& entry){ ubyte** region = entry.chunksData; @@ -371,4 +439,4 @@ void WorldFiles::writeRegion(int x, int y, WorldRegion& entry){ int2Bytes(offsets[i], (ubyte*)intbuf, 0); file.write(intbuf, 4); } -} \ No newline at end of file +} diff --git a/src/files/WorldFiles.h b/src/files/WorldFiles.h index 92716e6e..82628b39 100644 --- a/src/files/WorldFiles.h +++ b/src/files/WorldFiles.h @@ -39,7 +39,14 @@ class WorldFiles { std::filesystem::path getRegionFile(int x, int y) const; std::filesystem::path getPlayerFile() const; std::filesystem::path getWorldFile() const; - std::filesystem::path getBlockIndicesFile() const; + std::filesystem::path getIndicesFile() const; + + // TODO: remove in 0.16 + std::filesystem::path getOldPlayerFile() const; + std::filesystem::path getOldWorldFile() const; + bool readOldWorldInfo(World* world); + bool readOldPlayer(Player* player); + // -------------------- public: std::unordered_map regions; std::filesystem::path directory; diff --git a/src/files/files.cpp b/src/files/files.cpp index b631778e..a9b6fb88 100644 --- a/src/files/files.cpp +++ b/src/files/files.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "../coders/json.h" using std::ios; using std::string; @@ -71,4 +72,14 @@ bool files::write_string(path filename, const string content) { } file << content; return true; -} \ No newline at end of file +} + +json::JObject* files::read_json(path file) { + string text = files::read_string(file); + try { + return json::parse(file.string(), text); + } catch (const parsing_error& error) { + std::cerr << error.errorLog() << std::endl; + throw std::runtime_error("could not to parse "+file.string()); + } +} diff --git a/src/files/files.h b/src/files/files.h index 23d4427a..8442f78b 100644 --- a/src/files/files.h +++ b/src/files/files.h @@ -5,6 +5,10 @@ #include #include "../typedefs.h" +namespace json { + class JObject; +} + namespace files { extern bool write_bytes(std::filesystem::path, const char* data, size_t size); extern uint append_bytes(std::filesystem::path, const char* data, size_t size); @@ -12,6 +16,7 @@ namespace files { extern char* read_bytes(std::filesystem::path, size_t& length); extern std::string read_string(std::filesystem::path filename); extern bool write_string(std::filesystem::path filename, const std::string content); + extern json::JObject* read_json(std::filesystem::path file); } #endif /* FILES_FILES_H_ */ \ No newline at end of file