Merge branch 'master' of https://git.froggi.es/frogcraft/FeatherMC into master
This commit is contained in:
commit
2d1403548c
|
@ -1,9 +1,12 @@
|
|||
# common meson build dirs
|
||||
/build
|
||||
/build.32
|
||||
/build.64
|
||||
/build.cross
|
||||
# /build
|
||||
# /build.32
|
||||
# /build.64
|
||||
# /build.cross
|
||||
|
||||
# vscode per-user stuff
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/settings.json
|
||||
|
||||
# Visual Studio
|
||||
.vs
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,6 @@
|
|||
project('FeatherMC', ['c', 'cpp'], version : '0.0', meson_version : '>= 0.55', default_options : [
|
||||
'warning_level=2',
|
||||
'backend_startup_project=FeatherMC'
|
||||
])
|
||||
|
||||
add_project_arguments('-DNOMINMAX', language : 'cpp')
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
#include "DedicatedServer.h"
|
||||
#include "config/ServerProperties.h"
|
||||
#include "data/Registry.h"
|
||||
|
||||
#include "PacketReader.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
DedicatedServer::DedicatedServer(ServerProperties* properties) :
|
||||
|
@ -15,7 +18,9 @@ namespace Feather
|
|||
{
|
||||
m_status.descriptionText = properties->motd.GetValue();
|
||||
m_status.maxPlayers = properties->maxPlayers;
|
||||
|
||||
|
||||
Registry::Init();
|
||||
|
||||
m_worldManager.LoadWorld(m_properties->levelName);
|
||||
|
||||
while (1)
|
||||
|
@ -189,7 +194,7 @@ namespace Feather
|
|||
Play::ClientboundPlayerPositionAndLook playerPos =
|
||||
{
|
||||
.x = 0,
|
||||
.y = 64,
|
||||
.y = 70,
|
||||
.z = 0,
|
||||
.xRot = 0,
|
||||
.yRot = 0,
|
||||
|
@ -198,6 +203,92 @@ namespace Feather
|
|||
};
|
||||
|
||||
client.SendMessage(playerPos);
|
||||
|
||||
|
||||
NetworkMessage chunkData(5KB);
|
||||
// Packet ID
|
||||
chunkData.WriteVarInt(0x22);
|
||||
|
||||
// Chunk X, Z
|
||||
chunkData.Write<int32_t>(-10);
|
||||
chunkData.Write<int32_t>(-12);
|
||||
|
||||
// Full chunk
|
||||
chunkData.Write(true);
|
||||
|
||||
|
||||
Chunk* chunk = m_worldManager.GetOverworld()->m_chunk;
|
||||
|
||||
NBT::CompoundTag* chunkNBT = m_worldManager.GetOverworld()->m_chunkNBT;
|
||||
|
||||
int32_t sectionsBits = 0;
|
||||
|
||||
NetworkMessage sections(3KB);
|
||||
|
||||
for (int i = 0; i < Chunk::NUM_SECTIONS; i++)
|
||||
{
|
||||
ChunkSection sect = chunk->sections[i];
|
||||
|
||||
Log::Trace("Chunk: Got Section {} with local palette size {}", i, sect.localPalette.Size());
|
||||
|
||||
sectionsBits |= 1 << i;
|
||||
|
||||
// Block Count: fudge
|
||||
sections.Write<uint16_t>(1024);
|
||||
|
||||
// Bits Per Block
|
||||
sections.Write<uint8_t>(4);
|
||||
|
||||
// Palette
|
||||
|
||||
NetworkMessage paletteData(512);
|
||||
|
||||
for (int i = 0; i < sect.localPalette.Size(); i++)
|
||||
{
|
||||
int id = sect.localPalette.ByID(i);
|
||||
Log::Trace("Palette: {} -> {}", i, id);
|
||||
|
||||
paletteData.WriteVarInt(id);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
paletteData.Finalize();
|
||||
sections.WriteData(paletteData.GetData(), paletteData.GetDataSize());
|
||||
|
||||
// Block Data
|
||||
//NBT::LongArrayTag blocks = sect.Get<NBT::LongArrayTag>("BlockStates");
|
||||
|
||||
sections.WriteVarInt(ChunkSection::NUM_LONGS);
|
||||
sections.WriteData(sect.blockStates, ChunkSection::NUM_LONGS * sizeof(int64_t));
|
||||
}
|
||||
|
||||
//Log::Trace("Section bits: {:#b}", sectionsBits);
|
||||
|
||||
chunkData.WriteVarInt(sectionsBits);
|
||||
|
||||
// Heightmaps
|
||||
// TODO: Use chunk->heightmaps
|
||||
NBT::DataBuffer buf = chunkNBT->Get<NBT::CompoundTag>("Heightmaps").GetData();
|
||||
chunkData.WriteData(buf.data, buf.length);
|
||||
|
||||
// Biomes
|
||||
for (int i = 0; i < 1024; i++)
|
||||
chunkData.Write<int32_t>(5);
|
||||
//NBT::IntArrayTag biomes = chunk->Get<NBT::IntArrayTag>("Biomes");
|
||||
//NBT::DataBuffer buf1 = biomes.GetData();
|
||||
//chunkData.WriteData(buf1.data, buf1.length);
|
||||
|
||||
sections.Finalize();
|
||||
chunkData.WriteVarInt(sections.GetDataSize());
|
||||
chunkData.WriteData(sections.GetData(), sections.GetDataSize());
|
||||
|
||||
// Block Ents
|
||||
chunkData.WriteVarInt(0);
|
||||
|
||||
chunkData.Finalize();
|
||||
client.SendMessage(chunkData);
|
||||
|
||||
}
|
||||
|
||||
template <>
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace Feather
|
|||
ChunkPos(BlockPos pos) : x(pos.x >> 4), z(pos.z >> 4) {}
|
||||
|
||||
// Decode from packed int64
|
||||
ChunkPos(int64_t packed) : x(packed), z(packed >> 32) {}
|
||||
ChunkPos(int64_t packed) : x((int32_t)packed), z(packed >> 32) {}
|
||||
|
||||
// Pack into one int64
|
||||
inline int64_t Encode()
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <functional>
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
class BlockState
|
||||
{
|
||||
// PLACEHOLDER
|
||||
std::string_view m_name;
|
||||
public:
|
||||
BlockState(std::string_view name) : m_name(name) {}
|
||||
|
||||
inline const std::string_view GetName() const { return m_name; }
|
||||
|
||||
inline bool operator ==(const BlockState that) const
|
||||
{
|
||||
return m_name == that.m_name;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
struct std::hash<Feather::BlockState>
|
||||
{
|
||||
size_t operator()(const Feather::BlockState& state) const
|
||||
{
|
||||
using std::hash;
|
||||
using std::string_view;
|
||||
|
||||
return hash<string_view>()(state.GetName());
|
||||
}
|
||||
};
|
|
@ -32,6 +32,9 @@ namespace Feather
|
|||
}
|
||||
catch (const std::out_of_range& err)
|
||||
{
|
||||
// ignore unreferenced local var
|
||||
(void)err;
|
||||
|
||||
// process default value from constructor
|
||||
SetValue(value);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
class IDMapper
|
||||
{
|
||||
std::vector<T> m_IDtoValue;
|
||||
std::unordered_map<T, int> m_ValueToID;
|
||||
|
||||
int m_NextID = 0;
|
||||
|
||||
public:
|
||||
IDMapper(int size = 512)
|
||||
{
|
||||
m_IDtoValue.reserve(size);
|
||||
m_ValueToID.reserve(size);
|
||||
}
|
||||
|
||||
inline void AddMapping(int id, T& value)
|
||||
{
|
||||
m_IDtoValue.insert(m_IDtoValue.begin() + id, value);
|
||||
m_ValueToID.insert({ value, id });
|
||||
|
||||
if (m_NextID <= id) {
|
||||
m_NextID = id + 1;
|
||||
}
|
||||
}
|
||||
|
||||
inline void Add(T& value)
|
||||
{
|
||||
AddMapping(m_NextID, value);
|
||||
}
|
||||
|
||||
inline int GetID(T& value) const
|
||||
{
|
||||
return m_ValueToID.at(value);
|
||||
}
|
||||
|
||||
inline const T& ByID(int id) const
|
||||
{
|
||||
return m_IDtoValue.at(id);
|
||||
}
|
||||
|
||||
inline int Size() const
|
||||
{
|
||||
return m_IDtoValue.size();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
#include "Common.h"
|
||||
#include "Registry.h"
|
||||
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/filereadstream.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
#include "rapidjson/writer.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
IDMapper<BlockState> Registry::BLOCK_STATES;
|
||||
|
||||
void Registry::Init()
|
||||
{
|
||||
static bool initialized = false;
|
||||
if (initialized) return;
|
||||
|
||||
Log::Info("Loading data/blocks.json...");
|
||||
{
|
||||
using namespace rapidjson;
|
||||
|
||||
std::string text;
|
||||
FILE* fp = fopen("data/blocks.json", "rb");
|
||||
if (!fp) {
|
||||
Log::Error("Failed to read data/blocks.json");
|
||||
return;
|
||||
}
|
||||
fseek(fp, 0, SEEK_END);
|
||||
text.resize(ftell(fp));
|
||||
rewind(fp);
|
||||
fread(&text[0], 1, text.size(), fp);
|
||||
fclose(fp);
|
||||
|
||||
Document doc;
|
||||
doc.Parse(text.c_str());
|
||||
|
||||
|
||||
for (auto& block : doc.GetObject())
|
||||
{
|
||||
//Log::Info("{}: {} states", m.name.GetString(), m.value["states"].Size());
|
||||
for (auto& stateData : block.value["states"].GetArray())
|
||||
{
|
||||
std::string* name = new std::string(block.name.GetString());
|
||||
BlockState state(*name);
|
||||
BLOCK_STATES.AddMapping(stateData["id"].GetInt(), state);
|
||||
}
|
||||
}
|
||||
|
||||
Log::Info("Loaded {} block states.", BLOCK_STATES.Size());
|
||||
|
||||
//StringBuffer buf;
|
||||
//Writer<StringBuffer> writer(buf);
|
||||
//doc.Accept(writer);
|
||||
|
||||
// FIXME: why does this crash
|
||||
//Log::Info(text);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "IDMapper.h"
|
||||
#include "block/state/BlockState.h"
|
||||
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
class Registry
|
||||
{
|
||||
public:
|
||||
static IDMapper<BlockState> BLOCK_STATES;
|
||||
|
||||
static void Init();
|
||||
};
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
//#define FEATHER_Log::COLUMNS
|
||||
//#define FEATHER_LOG_COLUMNS
|
||||
|
||||
namespace Feather::Log
|
||||
{
|
||||
|
@ -105,7 +105,7 @@ namespace Feather::Log
|
|||
{
|
||||
std::string_view levelString = GetLevelStringView(level);
|
||||
std::string_view channelString = m_channels[channel].GetName();
|
||||
#ifdef FEATHER_Log::COLUMNS
|
||||
#ifdef FEATHER_LOG_COLUMNS
|
||||
std::array<char, MaxLevelLength + 1> levelSpaces = {};
|
||||
std::fill_n(levelSpaces.data(), MaxLevelLength - levelString.size(), ' ');
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ feather_src = [
|
|||
|
||||
'nbt/NBT.cpp',
|
||||
|
||||
'data/Registry.cpp',
|
||||
|
||||
'network/NetworkManager.cpp',
|
||||
'network/TCPListener.cpp',
|
||||
'network/TCPClient.cpp',
|
||||
|
@ -18,6 +20,7 @@ feather_src = [
|
|||
'config/Properties.cpp',
|
||||
'config/ServerProperties.cpp',
|
||||
|
||||
'world/Chunk.cpp',
|
||||
'world/World.cpp',
|
||||
'world/WorldManager.cpp',
|
||||
'world/RegionFile.cpp',
|
||||
|
|
|
@ -41,7 +41,8 @@ namespace NBT
|
|||
DataBuffer::~DataBuffer()
|
||||
{
|
||||
// equivalent to buffer_free in cNBT buffer.h
|
||||
free(data);
|
||||
// FIXME
|
||||
//free(data);
|
||||
}
|
||||
|
||||
Tag Tag::CreateTag(nbt_node *node)
|
||||
|
@ -137,6 +138,11 @@ namespace NBT
|
|||
return nbt_dump_ascii(m_node);
|
||||
}
|
||||
|
||||
Tag::operator bool() const
|
||||
{
|
||||
return m_node != NULL && m_node->type != TAG_INVALID;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, const Tag& tag)
|
||||
{
|
||||
return stream << tag.ToString();
|
||||
|
@ -155,13 +161,19 @@ namespace NBT
|
|||
//==================================================
|
||||
|
||||
CompoundTag::CompoundTag(const char *filename)
|
||||
: CompoundTag(nbt_parse_path(filename))
|
||||
: CompoundTag(nbt_parse_path(filename), true) // m_isRoot = true, this calls malloc
|
||||
{
|
||||
}
|
||||
|
||||
CompoundTag::CompoundTag(const void* data, size_t length)
|
||||
: CompoundTag(nbt_parse_compressed(data, length), true) // m_isRoot = true, this calls malloc
|
||||
{
|
||||
}
|
||||
|
||||
CompoundTag::~CompoundTag()
|
||||
{
|
||||
nbt_free(m_node);
|
||||
if (m_isRoot)
|
||||
nbt_free(m_node);
|
||||
}
|
||||
|
||||
const Tag CompoundTag::operator[](const char *name) const
|
||||
|
|
|
@ -45,6 +45,8 @@ namespace NBT
|
|||
const DataBuffer GetData() const;
|
||||
|
||||
const char* ToString() const;
|
||||
|
||||
operator bool() const;
|
||||
};
|
||||
|
||||
// Operator overload for ostream
|
||||
|
@ -79,6 +81,8 @@ namespace NBT
|
|||
|
||||
const T* GetValue() const;
|
||||
|
||||
inline const T operator[](int32_t index) const { return GetValue()[index]; }
|
||||
|
||||
// Methods for range-based for loops
|
||||
// TODO: check if this works for IntArrayTag and LongArrayTag
|
||||
const T* begin() { return GetValue(); }
|
||||
|
@ -131,10 +135,19 @@ namespace NBT
|
|||
// A Compound Tag is an unordered list of named tags
|
||||
class CompoundTag : public Tag
|
||||
{
|
||||
// only true if the tree was allocated by constructor
|
||||
const bool m_isRoot;
|
||||
|
||||
CompoundTag(nbt_node* node, bool isRoot) : Tag(node), m_isRoot(false) {}
|
||||
public:
|
||||
CompoundTag(nbt_node* node) : Tag(node) {}
|
||||
CompoundTag(nbt_node* node) : CompoundTag(node, false) {}
|
||||
|
||||
// Read tree from file
|
||||
CompoundTag(const char* filename);
|
||||
|
||||
// Read tree from compressed data
|
||||
CompoundTag(const void* data, size_t length);
|
||||
|
||||
CompoundTag(const char *filename);
|
||||
~CompoundTag();
|
||||
|
||||
template <typename T>
|
||||
|
@ -143,6 +156,8 @@ namespace NBT
|
|||
template <typename T>
|
||||
const T Get(const char* name) const;
|
||||
|
||||
inline const CompoundTag GetCompound(const char* name) const { return Get<CompoundTag>(name); }
|
||||
|
||||
const Tag operator[](const char* name) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -194,6 +194,30 @@
|
|||
id: int64
|
||||
}
|
||||
}
|
||||
|
||||
/*ChunkData:
|
||||
{
|
||||
id: 0x22
|
||||
vars: {
|
||||
chunkX: int32
|
||||
chunkZ: int32
|
||||
fullChunk: bool
|
||||
// bitmask for each 16x16x16 chunk section
|
||||
// LSB: section at y=0, bits for up to y=15
|
||||
chunkSections: varint
|
||||
heightmaps: nbt
|
||||
|
||||
// only if fullChunk == true
|
||||
biomes: int32[1024]
|
||||
|
||||
dataSize: varint
|
||||
data: byte[dataSize]
|
||||
|
||||
numBlockEntities: varint
|
||||
blockEntities: nbt[numBlockEntities]
|
||||
|
||||
}
|
||||
}*/
|
||||
|
||||
PlayerPositionAndLook:
|
||||
{
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
#include "Chunk.h"
|
||||
#include "World.h"
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
Chunk::Chunk(ChunkPos& pos, NBT::CompoundTag& tag) :
|
||||
pos(pos)
|
||||
{
|
||||
using namespace NBT;
|
||||
CompoundTag level = tag.GetCompound("Level");
|
||||
|
||||
Log::Trace("Loading chunk ({}, {}) from NBT", pos.x, pos.z);
|
||||
|
||||
if (pos.x != level.Get<int>("xPos") || pos.z != level.Get<int>("zPos"))
|
||||
{
|
||||
Log::Error("Tried to load chunk ({}, {}) from NBT but NBT says chunk pos is ({}, {})",
|
||||
pos.x, pos.z,
|
||||
level.Get<int>("xPos"), level.Get<int>("zPos")
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Heightmaps
|
||||
|
||||
ListTag<CompoundTag> sectionsList = level.GetList<CompoundTag>("Sections");
|
||||
|
||||
int n = 0;
|
||||
for (CompoundTag& sectData : sectionsList)
|
||||
{
|
||||
//std::cout << sectData << "\n";
|
||||
|
||||
|
||||
int8_t y = sectData.Get<int8_t>("Y");
|
||||
if (y < 0 || y >= NUM_SECTIONS) {
|
||||
Log::Trace("Skipping section {}", y);
|
||||
continue;
|
||||
}
|
||||
|
||||
Log::Trace("Loading section {}/{} at Y {}", n + 1, NUM_SECTIONS, y);
|
||||
|
||||
if (n >= NUM_SECTIONS)
|
||||
{
|
||||
Log::Error("Chunk ({}, {}) has more than {} sections!", pos.x, pos.z, NUM_SECTIONS);
|
||||
break;
|
||||
}
|
||||
|
||||
ChunkSection* section = §ions[y];
|
||||
|
||||
// Palette
|
||||
|
||||
ListTag<CompoundTag> palette = sectData.GetList<CompoundTag>("Palette");
|
||||
|
||||
for (int i = 0; i < palette.GetLength(); i++)
|
||||
{
|
||||
CompoundTag state = palette[i];
|
||||
const char* name = state.Get<StringTag>("Name").GetValue();
|
||||
|
||||
BlockState stateLookup(name);
|
||||
int id;
|
||||
|
||||
// TODO: handle this in IdMapper
|
||||
try {
|
||||
id = World::GLOBAL_PALETTE.GetID(stateLookup);
|
||||
}
|
||||
catch (std::out_of_range& err) {
|
||||
(void)err;
|
||||
Log::Warn("Chunk Section Palette maps id {} to invalid block state with name '{}'", i, name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: could use Add()
|
||||
section->localPalette.AddMapping(i, id);
|
||||
|
||||
Log::Trace(" {} -> {} {}", i, id, name);
|
||||
}
|
||||
|
||||
// Block States
|
||||
|
||||
LongArrayTag blockStates = sectData.Get<LongArrayTag>("BlockStates");
|
||||
|
||||
if (!blockStates) {
|
||||
Log::Warn("Skipping section with no block states.");
|
||||
continue;
|
||||
}
|
||||
|
||||
int len = blockStates.GetLength();
|
||||
if (len != ChunkSection::NUM_LONGS) {
|
||||
Log::Warn("Section {} has {} longs, expected {}", y, len, ChunkSection::NUM_LONGS);
|
||||
}
|
||||
|
||||
for (int i = 0; i < blockStates.GetLength() && i < ChunkSection::NUM_LONGS; i++)
|
||||
{
|
||||
section->blockStates[i] = blockStates[i];
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#include "nbt/NBT.h"
|
||||
#include "PalettedContainer.h"
|
||||
#include "block/state/BlockState.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
// 16x16x16 sections of a chunk
|
||||
class ChunkSection
|
||||
{
|
||||
public:
|
||||
// 4 bits per block
|
||||
// 16^3 = 4096 blocks total
|
||||
// 4096 * 4 = 16384 bits = 256x int64
|
||||
// 16 blocks per int64
|
||||
static constexpr int NUM_LONGS = 256;
|
||||
|
||||
int64_t blockStates[NUM_LONGS];
|
||||
|
||||
// TODO: this should eventually be Palette<BlockState>
|
||||
// Palette of local indexs to indexs in the global palette
|
||||
IDMapper<int> localPalette;
|
||||
};
|
||||
|
||||
// Chunk: 16x256x16 world chunk
|
||||
class Chunk
|
||||
{
|
||||
public:
|
||||
static constexpr int NUM_SECTIONS = 16;
|
||||
|
||||
// Each chunk is made of up 16 ChunkSections
|
||||
ChunkSection sections[NUM_SECTIONS];
|
||||
|
||||
ChunkPos pos;
|
||||
|
||||
// TODO: save heightmaps here
|
||||
// UNSERIALIZED
|
||||
//const NBT::CompoundTag* heightmaps;
|
||||
|
||||
Chunk(ChunkPos& pos, NBT::CompoundTag& tag);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include "data/IDMapper.h"
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
template <typename T>
|
||||
class Palette
|
||||
{
|
||||
protected:
|
||||
const IDMapper<T>& m_registry;
|
||||
public:
|
||||
Palette(const IDMapper<T>& registry) :
|
||||
m_registry(registry)
|
||||
{}
|
||||
|
||||
virtual int GetID(T& value) const = 0;
|
||||
|
||||
virtual const T& ByID(int id) const = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class GlobalPalette : public Palette<T>
|
||||
{
|
||||
const T* m_defaultValue;
|
||||
public:
|
||||
GlobalPalette(const IDMapper<T>& registry, const T* defaultValue) :
|
||||
Palette(registry),
|
||||
m_defaultValue(defaultValue)
|
||||
{}
|
||||
|
||||
inline virtual int GetID(T& value) const final { return m_registry.GetID(value); }
|
||||
inline virtual const T& ByID(int id) const final { return m_registry.ByID(id); }
|
||||
};
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "Palette.h"
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
// A container that maps values from a local palette to a
|
||||
template <typename T>
|
||||
class PalettedContainer
|
||||
{
|
||||
const Palette<T>& m_globalPalette;
|
||||
};
|
||||
}
|
|
@ -8,15 +8,16 @@ using std::string;
|
|||
namespace Feather
|
||||
{
|
||||
RegionFile::RegionFile(string path)
|
||||
: m_path(path)
|
||||
{
|
||||
std::ifstream stream(path);
|
||||
stream.read(m_header, 8KB);
|
||||
m_stream = new std::ifstream(path, std::ifstream::binary);
|
||||
m_stream->read(m_header, 8KB);
|
||||
|
||||
// Populate m_offsets
|
||||
for (int i = 0; i < 1KB; i++)
|
||||
for (int i = 0; i < 1024; i++)
|
||||
{
|
||||
int32_t readInt = 0;
|
||||
std::memcpy(&readInt, &m_header[i * 4], sizeof(int32_t));
|
||||
std::memcpy(&readInt, &m_header[i * sizeof(int32_t)], sizeof(int32_t));
|
||||
|
||||
// Read big endian ints.
|
||||
readInt = ReverseBytes(readInt);
|
||||
|
@ -26,6 +27,7 @@ namespace Feather
|
|||
|
||||
//int sector = GetSectorNumber(readInt);
|
||||
//int sectorCount = GetSectorCount(readInt);
|
||||
//Log::Trace("{}: Sector [{:x}] Count:{} Num:{}", i, readInt, sectorCount, sector);
|
||||
// We can record which sectors are used by this region here
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
|
@ -10,6 +13,11 @@ namespace Feather
|
|||
char m_header[8KB];
|
||||
int32_t m_offsets[1KB];
|
||||
|
||||
// TEMP
|
||||
std::ifstream* m_stream;
|
||||
|
||||
std::string m_path;
|
||||
|
||||
inline int GetOffsetIndex(RegionLocalPos pos) { return pos.x + pos.z * 32; }
|
||||
public:
|
||||
RegionFile(std::string path);
|
||||
|
@ -20,8 +28,14 @@ namespace Feather
|
|||
// Contains data for this chunk?
|
||||
inline bool HasChunk(ChunkPos pos) { return GetOffset(RegionLocalPos(pos)) != 0; }
|
||||
|
||||
// TEMP
|
||||
std::ifstream* GetChunkStream(RegionLocalPos pos) {
|
||||
m_stream->seekg(GetSectorNumber(GetOffset(pos)));
|
||||
return m_stream;
|
||||
}
|
||||
|
||||
// Unpack sector num from result of GetOffset()
|
||||
static int GetSectorNumber(int packedOffset) { return packedOffset >> 8; }
|
||||
static int GetSectorNumber(int packedOffset) { return (packedOffset >> 8) * 4096; }
|
||||
|
||||
// Unpack sector count from result of GetOffset()
|
||||
static int GetSectorCount(int packedOffset) { return packedOffset & 0xFF; }
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "World.h"
|
||||
#include "nbt/NBT.h"
|
||||
#include "RegionFile.h"
|
||||
#include "data/Registry.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
@ -15,6 +16,11 @@ using std::stringstream;
|
|||
|
||||
namespace Feather
|
||||
{
|
||||
const GlobalPalette<BlockState> World::GLOBAL_PALETTE = GlobalPalette<BlockState>(
|
||||
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)))
|
||||
|
@ -50,19 +56,55 @@ namespace Feather
|
|||
m_levelData.spawnY = levelDat.Get<int32_t>("SpawnY");
|
||||
m_levelData.spawnZ = levelDat.Get<int32_t>("SpawnZ");
|
||||
|
||||
ChunkPos spawnChunk(BlockPos(m_levelData.spawnX, m_levelData.spawnY, m_levelData.spawnZ));
|
||||
BlockPos spawnPos(m_levelData.spawnX, m_levelData.spawnY, m_levelData.spawnZ);
|
||||
ChunkPos spawnChunk(spawnPos);
|
||||
RegionPos regionPos(spawnChunk);
|
||||
RegionLocalPos regionChunkPos(spawnChunk);
|
||||
|
||||
stringstream regionFile;
|
||||
regionFile << "r." << regionPos.x << "." << regionPos.z << ".mca";
|
||||
|
||||
fs::path mcaFile = regionsPath / regionFile.str();
|
||||
if (!CheckPath(mcaFile)) return;
|
||||
Log::Trace("Spawn Chunk: Block({} {} {}) -> Chunk({} {}) -> Region({} {}) -> RegionChunk({} {})",
|
||||
spawnPos.x, spawnPos.y, spawnPos.z,
|
||||
spawnChunk.x, spawnChunk.z,
|
||||
regionPos.x, regionPos.z,
|
||||
regionChunkPos.x, regionChunkPos.z
|
||||
);
|
||||
|
||||
|
||||
//Log_Info("Spawn Chunk Region File: %s", mcaFile.string().c_str());
|
||||
|
||||
RegionFile region(mcaFile.string());
|
||||
|
||||
//int regionOffset = region.GetOffset(regionChunkPos);
|
||||
|
||||
//std::cout << region << "\n";
|
||||
//int sectorNum = RegionFile::GetSectorNumber(regionOffset);
|
||||
//Log::Trace("Loading chunk from offset: [{}] = {}", regionChunkPos.x + regionChunkPos.z * 32, sectorNum);
|
||||
|
||||
std::ifstream* chunkStream = region.GetChunkStream(regionChunkPos);
|
||||
|
||||
Log::Trace("{}", chunkStream->good());
|
||||
|
||||
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);
|
||||
|
||||
m_chunkNBT = new NBT::CompoundTag(chunkData, length);
|
||||
|
||||
std::cout << *m_chunkNBT << "\n";
|
||||
|
||||
m_chunk = new Chunk(spawnChunk, *m_chunkNBT);
|
||||
|
||||
/*for (auto& f : fs::directory_iterator(regionsPath))
|
||||
{
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "nbt/NBT.h"
|
||||
#include "Chunk.h"
|
||||
#include "Palette.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
|
@ -9,6 +13,13 @@ namespace Feather
|
|||
class World
|
||||
{
|
||||
public:
|
||||
// global palette of all block states
|
||||
static const GlobalPalette<BlockState> GLOBAL_PALETTE;
|
||||
|
||||
// TEMP
|
||||
NBT::CompoundTag* m_chunkNBT;
|
||||
Chunk* m_chunk;
|
||||
|
||||
struct LevelData
|
||||
{
|
||||
int32_t spawnX, spawnY, spawnZ;
|
||||
|
|
|
@ -14,5 +14,7 @@ namespace Feather
|
|||
World* world;
|
||||
public:
|
||||
void LoadWorld(string name);
|
||||
|
||||
inline World* GetOverworld() { return world; }
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue