diff --git a/src/DedicatedServer.cpp b/src/DedicatedServer.cpp index 2cce798..bbd0b0f 100644 --- a/src/DedicatedServer.cpp +++ b/src/DedicatedServer.cpp @@ -4,6 +4,7 @@ #include "PacketReader.h" #include +#include namespace Feather { @@ -13,7 +14,9 @@ namespace Feather m_status() { m_status.descriptionText = properties->motd.GetValue(); - m_status.maxPlayers = properties->maxPlayers.GetValue(); + m_status.maxPlayers = properties->maxPlayers; + + m_worldManager.LoadWorld(m_properties->levelName); while (1) { diff --git a/src/DedicatedServer.h b/src/DedicatedServer.h index 23b2f7d..c329c9a 100644 --- a/src/DedicatedServer.h +++ b/src/DedicatedServer.h @@ -5,8 +5,11 @@ #include "protocol/Protocol.h" #include "PacketReader.h" +#include "world/WorldManager.h" + #include "network/IListenerInterface.h" + namespace Feather { class ServerProperties; @@ -33,5 +36,8 @@ namespace Feather ServerStatus m_status; LockableList m_clients; + + WorldManager m_worldManager; + }; } diff --git a/src/Types.h b/src/Types.h index 0e7f809..ba5112e 100644 --- a/src/Types.h +++ b/src/Types.h @@ -23,7 +23,7 @@ namespace Feather struct BlockPos { - int32_t x, y, z; + const int32_t x, y, z; BlockPos(int32_t x, int32_t y, int32_t z) : x(x), y(y), z(z) {} @@ -52,4 +52,37 @@ namespace Feather static const int Z_OFFSET = PACKED_Y_LENGTH; static const int X_OFFSET = PACKED_Y_LENGTH + PACKED_Z_LENGTH; }; + + struct ChunkPos + { + const int32_t x, z; + + ChunkPos(int32_t x, int32_t z) : x(x), z(z) {} + + // Coordinate of the 16x16 chunk containing this block + ChunkPos(BlockPos pos) : x(pos.x >> 4), z(pos.z >> 4) {} + + // Decode from packed int64 + ChunkPos(int64_t packed) : x(packed), z(packed >> 32) {} + + // Pack into one int64 + inline int64_t Encode() + { + return ((int64_t)x & 0xFFFFFFFF) | (((int64_t)z & 0xFFFFFFFF) << 32); + } + }; + + // Which 32x32 chunk region file (r...mca) the chunk is stored in + struct RegionPos + { + const int32_t x, z; + RegionPos(ChunkPos pos) : x(pos.x >> 5), z(pos.z >> 5) {} + }; + + // Position of chunk relative to its region + struct RegionLocalPos + { + const int32_t x, z; + RegionLocalPos(ChunkPos pos) : x(pos.x & 0x1F), z(pos.z & 0x1F) {} + }; } \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index d16335a..ef88f83 100644 --- a/src/meson.build +++ b/src/meson.build @@ -17,6 +17,10 @@ feather_src = [ 'config/Properties.cpp', 'config/ServerProperties.cpp', + + 'world/World.cpp', + 'world/WorldManager.cpp', + 'world/RegionFile.cpp', ] subdir('protocol') diff --git a/src/world/RegionFile.cpp b/src/world/RegionFile.cpp new file mode 100644 index 0000000..1400124 --- /dev/null +++ b/src/world/RegionFile.cpp @@ -0,0 +1,32 @@ +#include "RegionFile.h" +#include "util/ByteUtil.h" + +#include + +using std::string; + +namespace Feather +{ + RegionFile::RegionFile(string path) + { + std::ifstream stream(path); + stream.read(m_header, 8KB); + + // Populate m_offsets + for (int i = 0; i < 1KB; i++) + { + int32_t readInt = 0; + std::memcpy(&readInt, &m_header[i * 4], sizeof(int32_t)); + + // Read big endian ints. + readInt = ReverseBytes(readInt); + m_offsets[i] = readInt; + + if (readInt == 0) continue; + + //int sector = GetSectorNumber(readInt); + //int sectorCount = GetSectorCount(readInt); + // We can record which sectors are used by this region here + } + } +} \ No newline at end of file diff --git a/src/world/RegionFile.h b/src/world/RegionFile.h new file mode 100644 index 0000000..e956054 --- /dev/null +++ b/src/world/RegionFile.h @@ -0,0 +1,29 @@ +#pragma once +#include "Common.h" +#include "Types.h" +#include + +namespace Feather +{ + class RegionFile + { + char m_header[8KB]; + int32_t m_offsets[1KB]; + + inline int GetOffsetIndex(RegionLocalPos pos) { return pos.x + pos.z * 32; } + public: + RegionFile(std::string path); + + // Packed data about the offset into the region file to parse chunk data from + inline int GetOffset(RegionLocalPos pos) { return m_offsets[GetOffsetIndex(pos)]; } + + // Contains data for this chunk? + inline bool HasChunk(ChunkPos pos) { return GetOffset(RegionLocalPos(pos)) != 0; } + + // Unpack sector num from result of GetOffset() + static int GetSectorNumber(int packedOffset) { return packedOffset >> 8; } + + // Unpack sector count from result of GetOffset() + static int GetSectorCount(int packedOffset) { return packedOffset & 0xFF; } + }; +} \ No newline at end of file diff --git a/src/world/World.cpp b/src/world/World.cpp new file mode 100644 index 0000000..fb7a4b9 --- /dev/null +++ b/src/world/World.cpp @@ -0,0 +1,72 @@ +#include "Common.h" +#include "Types.h" +#include "World.h" +#include "nbt/NBT.h" +#include "RegionFile.h" + +#include +#include +#include +#include + +namespace fs = std::filesystem; +using std::string; +using std::stringstream; + +namespace Feather +{ + 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 \"%s\"", path.string().c_str()); + else Log_Error("Cannot find file \"%s\"", path.string().c_str()); + + return false; + } + + return true; + } + + World::World(string name) + { + fs::path worldPath = fs::path(name); + if (!CheckPath(worldPath)) return; + + fs::path regionsPath = worldPath/"region"; + if (!CheckPath(regionsPath)) return; + + fs::path levelDatPath = worldPath/"level.dat"; + if (!CheckPath(levelDatPath)) return; + + + Log_Info("Loading world \"%s\"", 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"); + + ChunkPos spawnChunk(BlockPos(m_levelData.spawnX, m_levelData.spawnY, m_levelData.spawnZ)); + RegionPos regionPos(spawnChunk); + + stringstream regionFile; + regionFile << "r." << regionPos.x << "." << regionPos.z << ".mca"; + + fs::path mcaFile = regionsPath / regionFile.str(); + if (!CheckPath(mcaFile)) return; + //Log_Info("Spawn Chunk Region File: %s", mcaFile.string().c_str()); + + RegionFile region(mcaFile.string()); + + //std::cout << region << "\n"; + + /*for (auto& f : fs::directory_iterator(regionsPath)) + { + std::cout << f.path() << "\n"; + }*/ + } +}; \ No newline at end of file diff --git a/src/world/World.h b/src/world/World.h new file mode 100644 index 0000000..98d0281 --- /dev/null +++ b/src/world/World.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Common.h" +#include +#include + +namespace Feather +{ + class World + { + public: + struct LevelData + { + int32_t spawnX, spawnY, spawnZ; + } m_levelData; + + World(std::string name); + }; +} \ No newline at end of file diff --git a/src/world/WorldManager.cpp b/src/world/WorldManager.cpp new file mode 100644 index 0000000..aadfb7b --- /dev/null +++ b/src/world/WorldManager.cpp @@ -0,0 +1,16 @@ +#include "Common.h" + +#include "WorldManager.h" + +#include +#include + +namespace fs = std::filesystem; + +namespace Feather +{ + void WorldManager::LoadWorld(string name) + { + world = new World(name); + } +} \ No newline at end of file diff --git a/src/world/WorldManager.h b/src/world/WorldManager.h new file mode 100644 index 0000000..c1969dc --- /dev/null +++ b/src/world/WorldManager.h @@ -0,0 +1,18 @@ +#pragma once + +#include "World.h" + +#include + +using std::string; + +namespace Feather +{ + // TODO: this will manage different dimensions and worlds + class WorldManager + { + World* world; + public: + void LoadWorld(string name); + }; +} \ No newline at end of file