#include "Common.h" #include "Types.h" #include "World.h" #include "nbt/NBT.h" #include "RegionFile.h" #include "data/Registry.h" #include #include #include #include namespace fs = std::filesystem; using std::string; using std::stringstream; namespace Feather { // TODO: move this to ChunkSection const GlobalPalette World::GLOBAL_PALETTE = GlobalPalette( Registry::BLOCK_STATES, new BlockState("minecraft:air") // temp ); static bool CheckPath(fs::path path, bool dir = false) { if (!fs::exists(path) || (dir && !fs::is_directory(path))) { if (dir) Log::Error("Cannot find folder \"{}\"", path.string().c_str()); else Log::Error("Cannot find file \"{}\"", path.string().c_str()); return false; } return true; } World::World(string name) : m_name(name) { fs::path worldPath = fs::path(name); if (!CheckPath(worldPath)) return; fs::path levelDatPath = worldPath/"level.dat"; if (!CheckPath(levelDatPath)) return; Log::Info("Loading world \"{}\"", name.c_str()); NBT::CompoundTag levelDat = NBT::CompoundTag(levelDatPath.string().c_str()); //std::cout << levelDat << "\n"; m_levelData.spawnX = levelDat.Get("SpawnX"); m_levelData.spawnY = levelDat.Get("SpawnY"); m_levelData.spawnZ = levelDat.Get("SpawnZ"); } void World::PrepareSpawn() { Log::Info("Loading spawn area..."); BlockPos spawnPos(m_levelData.spawnX, m_levelData.spawnY, m_levelData.spawnZ); ChunkPos spawnChunk(spawnPos); for (int x = -10; x <= 10; x++) { for (int z = -10; z <= 10; z++) { GetChunk(ChunkPos(spawnChunk.x + x, spawnChunk.z + z)); } } Log::Info("Finished loading spawn area."); } Chunk* World::GetChunk(ChunkPos pos) { auto [chunks, lock] = m_chunks.borrow(); if (chunks.find(pos) == chunks.end()) { if (auto chunk = LoadChunk(pos)) chunks[pos] = std::move(chunk); } return chunks[pos].get(); } bool World::SetBlock(const BlockPos& pos, const BlockState& state) { if (IsOutsideBuildHeight(pos)) return false; Chunk* chunk = GetChunk(ChunkPos(pos)); Log::Debug("World::SetBlock at BlockPos({}, {}, {}) -> ChunkPos({}, {})", pos.x, pos.y, pos.z, chunk->pos.x, chunk->pos.z ); return chunk->SetBlock(pos, state); } std::unique_ptr World::LoadChunk(ChunkPos pos) { fs::path worldPath = fs::path(m_name); if (!CheckPath(worldPath)) return nullptr; fs::path regionsPath = worldPath / "region"; if (!CheckPath(regionsPath)) return nullptr; RegionPos regionPos(pos); RegionLocalPos regionChunkPos(pos); stringstream regionFile; regionFile << "r." << regionPos.x << "." << regionPos.z << ".mca"; fs::path mcaFile = regionsPath / regionFile.str(); if (!CheckPath(mcaFile)) return nullptr; //Log_Info("Spawn Chunk Region File: %s", mcaFile.string().c_str()); RegionFile region(mcaFile.string()); //int regionOffset = region.GetOffset(regionChunkPos); //int sectorNum = RegionFile::GetSectorNumber(regionOffset); //Log::Trace("Loading chunk from offset: [{}] = {}", regionChunkPos.x + regionChunkPos.z * 32, sectorNum); std::ifstream* chunkStream = region.GetChunkStream(regionChunkPos); if (!chunkStream->good()) { Log::Error("Failed to find chunk ({}, {}) on disk.", pos.x, pos.z); return nullptr; } char lengthBytes[5]; chunkStream->read(lengthBytes, 5); int32_t length = 0; std::memcpy(&length, lengthBytes, sizeof(int32_t)); length = ReverseBytes(length); int8_t compressionType = lengthBytes[4]; //Log::Trace("Loading chunk: Length: {}. Compression: {}", length, compressionType); char* chunkData = new char[length + 1]; chunkStream->read(chunkData, length); auto nbt = std::make_unique(chunkData, length); if (!*nbt) return nullptr; //std::cout << *nbt << "\n"; return std::make_unique(pos, std::move(nbt)); } };