From 636cb0c44ccf507470a526af69bc843235d00f35 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 7 Dec 2023 15:16:37 +0300 Subject: [PATCH] World indices check --- src/content/Content.cpp | 10 ++++- src/content/Content.h | 3 +- src/content/ContentIndexLUT.cpp | 69 +++++++++++++++++++++++++++++++++ src/content/ContentIndexLUT.h | 33 ++++++++++++++++ src/files/WorldFiles.cpp | 1 + src/frontend/menu.cpp | 9 ++--- src/frontend/screens.cpp | 2 +- src/logic/ChunksController.cpp | 12 ------ src/voxels/ChunksStorage.cpp | 13 +++++++ src/voxels/WorldGenerator.cpp | 20 +++++----- src/world/World.cpp | 44 +++++++++++++++++---- src/world/World.h | 21 ++++++++-- 12 files changed, 197 insertions(+), 40 deletions(-) create mode 100644 src/content/ContentIndexLUT.cpp create mode 100644 src/content/ContentIndexLUT.h diff --git a/src/content/Content.cpp b/src/content/Content.cpp index 5322a98d..c7ddf25b 100644 --- a/src/content/Content.cpp +++ b/src/content/Content.cpp @@ -64,7 +64,15 @@ Content::~Content() { delete indices; } -Block* Content::require(std::string id) const { +Block* Content::findBlock(string id) const { + auto found = blockDefs.find(id); + if (found == blockDefs.end()) { + return nullptr; + } + return found->second; +} + +Block* Content::requireBlock(string id) const { auto found = blockDefs.find(id); if (found == blockDefs.end()) { throw std::runtime_error("missing block "+id); diff --git a/src/content/Content.h b/src/content/Content.h index 70c68cc6..f54f7fe5 100644 --- a/src/content/Content.h +++ b/src/content/Content.h @@ -55,7 +55,8 @@ public: std::unordered_map blockDefs); ~Content(); - Block* require(std::string id) const; + Block* findBlock(std::string id) const; + Block* requireBlock(std::string id) const; }; #endif // CONTENT_CONTENT_H_ \ No newline at end of file diff --git a/src/content/ContentIndexLUT.cpp b/src/content/ContentIndexLUT.cpp new file mode 100644 index 00000000..8d9df840 --- /dev/null +++ b/src/content/ContentIndexLUT.cpp @@ -0,0 +1,69 @@ +#include "ContentIndexLUT.h" + +#include +#include + +#include "Content.h" +#include "../constants.h" +#include "../files/files.h" +#include "../coders/json.h" +#include "../voxels/Block.h" + +using std::string; +using std::unique_ptr; +using std::filesystem::path; + +ContentIndexLUT::ContentIndexLUT(size_t blocksCount, const Content* content) { + blocks = new blockid_t[blocksCount]; + blockNames = new string[blocksCount]; + for (size_t i = 0; i < blocksCount; i++) { + blocks[i] = i; + } + + ContentIndices* indices = content->indices; + for (size_t i = 0; i < indices->countBlockDefs(); i++) { + blockNames[i] = indices->getBlockDef(i)->name; + } +} + +ContentIndexLUT::~ContentIndexLUT() { + delete[] blockNames; + delete[] blocks; +} + +ContentIndexLUT* ContentIndexLUT::create(const path& filename, const Content* content) { + auto& indices = content->indices; + unique_ptr root(files::read_json(filename)); + json::JArray* blocksarr = root->arr("blocks"); + + size_t blocks_c = blocksarr + ? std::max(blocksarr->size(), indices->countBlockDefs()) + : indices->countBlockDefs(); + + unique_ptr lut(new ContentIndexLUT(blocks_c, content)); + + bool conflicts = false; + + // TODO: implement world files convert feature using ContentIndexLUT report + for (size_t i = 0; i < blocksarr->size(); i++) { + string name = blocksarr->str(i); + Block* def = content->findBlock(name); + if (def) { + if (i != def->rt.id) { + std::cerr << "block id has changed from "; + std::cerr << def->rt.id << " to " << i << std::endl; + conflicts = true; + } + lut->setBlock(i, name, def->rt.id); + } else { + std::cerr << "unknown block: " << name << std::endl; + lut->setBlock(i, name, i); + conflicts = true; + } + } + if (conflicts) { + return lut.release(); + } else { + return nullptr; + } +} diff --git a/src/content/ContentIndexLUT.h b/src/content/ContentIndexLUT.h new file mode 100644 index 00000000..37bb7df8 --- /dev/null +++ b/src/content/ContentIndexLUT.h @@ -0,0 +1,33 @@ +#ifndef CONTENT_CONTENT_INDEX_LUT_H_ +#define CONTENT_CONTENT_INDEX_LUT_H_ + +#include "../typedefs.h" +#include +#include + +class Content; + +/* Content indices lookup table or report + used to convert world with different indices + Building with indices.json */ +class ContentIndexLUT { + blockid_t* blocks; + std::string* blockNames; +public: + ContentIndexLUT(size_t blocks, const Content* content); + ~ContentIndexLUT(); + + inline blockid_t getBlockId(blockid_t index) { + return blocks[index]; + } + + inline void setBlock(blockid_t index, std::string name, blockid_t id) { + blocks[index] = id; + blockNames[index] = name; + } + + static ContentIndexLUT* create(const std::filesystem::path& filename, + const Content* content); +}; + +#endif // CONTENT_CONTENT_INDEX_LUT_H_ diff --git a/src/files/WorldFiles.cpp b/src/files/WorldFiles.cpp index 1ea0e007..87e670af 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -447,6 +447,7 @@ bool WorldFiles::readWorldInfo(World* world) { } unique_ptr root(files::read_json(file)); + root->str("name", world->name); root->num("seed", world->seed); json::JObject* verobj = root->obj("version"); diff --git a/src/frontend/menu.cpp b/src/frontend/menu.cpp index 65a02595..a1de88c9 100644 --- a/src/frontend/menu.cpp +++ b/src/frontend/menu.cpp @@ -56,9 +56,8 @@ Panel* create_main_menu_panel(Engine* engine, PagesControl* menu) { EngineSettings& settings = engine->getSettings(); auto folder = paths->getWorldsFolder()/u8path(name); - World* world = new World(name, folder, 42, settings); - auto screen = new LevelScreen(engine, - world->load(settings, engine->getContent())); + Level* level = World::load(folder, settings, engine->getContent()); + auto screen = new LevelScreen(engine, level); engine->setScreen(shared_ptr(screen)); }); worldsPanel->add(button); @@ -146,8 +145,8 @@ Panel* create_new_world_panel(Engine* engine, PagesControl* menu) { auto folder = paths->getWorldsFolder()/u8path(nameutf8); std::filesystem::create_directories(folder); - World* world = new World(nameutf8, folder, seed, settings); - auto screen = new LevelScreen(engine, world->create(settings, engine->getContent())); + Level* level = World::create(nameutf8, folder, seed, settings, engine->getContent()); + auto screen = new LevelScreen(engine, level); engine->setScreen(shared_ptr(screen)); }); panel->add(button); diff --git a/src/frontend/screens.cpp b/src/frontend/screens.cpp index eec335e2..bdbf193c 100644 --- a/src/frontend/screens.cpp +++ b/src/frontend/screens.cpp @@ -134,7 +134,7 @@ void LevelScreen::updateHotkeys() { // TODO: remove in v0.16 if (Events::jpressed(keycode::F9)) { - blockid_t woodid = level->content->require("base:wood")->rt.id; + blockid_t woodid = level->content->requireBlock("base:wood")->rt.id; for (size_t i = 0; i < level->chunks->volume; i++){ Chunk* chunk = level->chunks->chunks[i].get(); if (chunk) { diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 81d16e0e..1d354608 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -1,6 +1,5 @@ #include "ChunksController.h" -#include #include #include @@ -57,7 +56,6 @@ void ChunksController::update(int64_t maxDuration) { } bool ChunksController::loadVisible(){ - const Content* content = level->content; const int w = chunks->w; const int d = chunks->d; const int ox = chunks->ox; @@ -115,16 +113,6 @@ bool ChunksController::loadVisible(){ chunk->updateHeights(); - ContentIndices* indices = content->indices; - for (size_t i = 0; i < CHUNK_VOL; i++) { - blockid_t id = chunk->voxels[i].id; - if (indices->getBlockDef(id) == nullptr) { - std::cout << "corruped block detected at " << i << " of chunk "; - std::cout << chunk->x << "x" << chunk->z; - std::cout << " -> " << (int)id << std::endl; - chunk->voxels[i].id = 11; - } - } if (!chunk->isLoadedLights()) { lighting->prebuildSkyLight(chunk->x, chunk->z); } diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 8c8c7231..abdc65d1 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -1,6 +1,7 @@ #include "ChunksStorage.h" #include +#include #include "VoxelsVolume.h" #include "Chunk.h" @@ -53,6 +54,18 @@ std::shared_ptr ChunksStorage::create(int x, int z) { chunk->setLoaded(true); } + // Verifying and converting data + ContentIndices* indices = level->content->indices; + for (size_t i = 0; i < CHUNK_VOL; i++) { + blockid_t id = chunk->voxels[i].id; + if (indices->getBlockDef(id) == nullptr) { + std::cout << "corruped block detected at " << i << " of chunk "; + std::cout << chunk->x << "x" << chunk->z; + std::cout << " -> " << (int)id << std::endl; + chunk->voxels[i].id = 11; + } + } + light_t* lights = world->wfile->getLights(chunk->x, chunk->z); if (lights) { chunk->lightmap->set(lights); diff --git a/src/voxels/WorldGenerator.cpp b/src/voxels/WorldGenerator.cpp index 1cd45ea6..7fdb3ec8 100644 --- a/src/voxels/WorldGenerator.cpp +++ b/src/voxels/WorldGenerator.cpp @@ -97,16 +97,16 @@ float calc_height(fnl_state *noise, int real_x, int real_z){ } WorldGenerator::WorldGenerator(const Content* content) - : idStone(content->require("base:stone")->rt.id), - idDirt(content->require("base:dirt")->rt.id), - idGrassBlock(content->require("base:grass_block")->rt.id), - idSand(content->require("base:sand")->rt.id), - idWater(content->require("base:water")->rt.id), - idWood(content->require("base:wood")->rt.id), - idLeaves(content->require("base:leaves")->rt.id), - idGrass(content->require("base:grass")->rt.id), - idFlower(content->require("base:flower")->rt.id), - idBazalt(content->require("base:bazalt")->rt.id) {; + : idStone(content->requireBlock("base:stone")->rt.id), + idDirt(content->requireBlock("base:dirt")->rt.id), + idGrassBlock(content->requireBlock("base:grass_block")->rt.id), + idSand(content->requireBlock("base:sand")->rt.id), + idWater(content->requireBlock("base:water")->rt.id), + idWood(content->requireBlock("base:wood")->rt.id), + idLeaves(content->requireBlock("base:leaves")->rt.id), + idGrass(content->requireBlock("base:grass")->rt.id), + idFlower(content->requireBlock("base:flower")->rt.id), + idBazalt(content->requireBlock("base:bazalt")->rt.id) {; } int generate_tree(fnl_state *noise, diff --git a/src/world/World.cpp b/src/world/World.cpp index 2e4f98b3..042b2418 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -6,6 +6,7 @@ #include "Level.h" #include "../files/WorldFiles.h" #include "../content/Content.h" +#include "../content/ContentIndexLUT.h" #include "../voxels/Chunk.h" #include "../voxels/Chunks.h" #include "../voxels/ChunksStorage.h" @@ -13,15 +14,22 @@ #include "../window/Camera.h" using glm::vec3; +using std::unique_ptr; using std::shared_ptr; using std::string; using std::filesystem::path; +namespace fs = std::filesystem; + +world_load_error::world_load_error(string message) : std::runtime_error(message) { +} World::World(string name, path directory, uint64_t seed, - EngineSettings& settings) + EngineSettings& settings, + const Content* content) : settings(settings), + content(content), name(name), seed(seed) { wfile = new WorldFiles(directory, settings.debug); @@ -59,17 +67,39 @@ void World::write(Level* level) { const float DEF_PLAYER_Y = 100.0f; const float DEF_PLAYER_SPEED = 4.0f; -Level* World::create(EngineSettings& settings, const Content* content) { +Level* World::create(string name, + path directory, + uint64_t seed, + EngineSettings& settings, + const Content* content) { + World* world = new World(name, directory, seed, settings, content); Player* player = new Player(vec3(0, DEF_PLAYER_Y, 0), DEF_PLAYER_SPEED); - return new Level(this, content, player, settings); + return new Level(world, content, player, settings); } -Level* World::load(EngineSettings& settings, const Content* content) { - wfile->readWorldInfo(this); +Level* World::load(path directory, + EngineSettings& settings, + const Content* content) { + + path indicesFile = directory/path("indices.json"); + if (fs::is_regular_file(indicesFile)) { + auto lut = ContentIndexLUT::create(indicesFile, content); + if (lut) { + throw world_load_error("world indices conflict"); + } + } + + unique_ptr world (new World(".", directory, 0, settings, content)); + auto& wfile = world->wfile; + + if (!wfile->readWorldInfo(world.get())) { + throw world_load_error("could not to find world.json"); + } Player* player = new Player(vec3(0, DEF_PLAYER_Y, 0), DEF_PLAYER_SPEED); - Level* level = new Level(this, content, player, settings); - + Level* level = new Level(world.get(), content, player, settings); wfile->readPlayer(player); + + world.release(); return level; } diff --git a/src/world/World.h b/src/world/World.h index 26c3b863..fee5ff36 100644 --- a/src/world/World.h +++ b/src/world/World.h @@ -3,6 +3,7 @@ #include #include +#include #include "../typedefs.h" #include "../settings.h" #include "../util/timeutil.h" @@ -13,8 +14,14 @@ class Chunks; class Level; class Player; +class world_load_error : public std::runtime_error { +public: + world_load_error(std::string message); +}; + class World { EngineSettings& settings; + const Content* const content; public: std::string name; WorldFiles* wfile; @@ -30,13 +37,21 @@ public: World(std::string name, std::filesystem::path directory, uint64_t seed, - EngineSettings& settings); + EngineSettings& settings, + const Content* content); ~World(); void updateTimers(float delta); void write(Level* level); - Level* create(EngineSettings& settings, const Content* content); - Level* load(EngineSettings& settings, const Content* content); + + static Level* create(std::string name, + std::filesystem::path directory, + uint64_t seed, + EngineSettings& settings, + const Content* content); + static Level* load(std::filesystem::path directory, + EngineSettings& settings, + const Content* content); }; #endif /* WORLD_WORLD_H_ */ \ No newline at end of file