X-Git-Url: http://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fio%2FWorldSave.cpp;h=529d0d91a5784da2f1cf2cea55ae62a1fc87b75c;hb=c0a5ece0f6bacea1b85157a908d710070fb0affd;hp=bd041f2e89cf20aba12487b1db2352ea010ebfac;hpb=ad7cf72ed47c39640d5588ba53386e090289b4d1;p=blank.git diff --git a/src/io/WorldSave.cpp b/src/io/WorldSave.cpp index bd041f2..529d0d9 100644 --- a/src/io/WorldSave.cpp +++ b/src/io/WorldSave.cpp @@ -1,6 +1,8 @@ #include "WorldSave.hpp" #include "filesystem.hpp" +#include "TokenStreamReader.hpp" +#include "../app/error.hpp" #include #include @@ -9,6 +11,7 @@ #include #include #include +#include using namespace std; @@ -17,7 +20,9 @@ namespace blank { WorldSave::WorldSave(const string &path) : root_path(path) -, conf_path(path + "world.conf") +, world_conf_path(path + "world.conf") +, gen_conf_path(path + "gen.conf") +, player_path(path + "player/") , chunk_path(path + "chunks/%d/%d/%d.gz") , chunk_bufsiz(chunk_path.length() + 3 * std::numeric_limits::digits10) , chunk_buf(new char[chunk_bufsiz]) { @@ -26,65 +31,145 @@ WorldSave::WorldSave(const string &path) bool WorldSave::Exists() const noexcept { - return is_dir(root_path) && is_file(conf_path); + return is_dir(root_path) && is_file(world_conf_path); } void WorldSave::Read(World::Config &conf) const { - ifstream in(conf_path); - if (!in) { + ifstream is(world_conf_path); + if (!is) { throw runtime_error("failed to open world config"); } + TokenStreamReader in(is); + + string name; + while (in.HasMore()) { + in.ReadIdentifier(name); + in.Skip(Token::EQUALS); + if (name == "spawn") { + in.ReadVec(conf.spawn); + } + if (in.HasMore() && in.Peek().type == Token::SEMICOLON) { + in.Skip(Token::SEMICOLON); + } + } - constexpr char spaces[] = "\n\r\t "; + if (is.bad()) { + throw runtime_error("IO error reading world config"); + } +} - string line; - while (getline(in, line)) { - if (line.empty() || line[0] == '#') continue; - auto equals_pos = line.find_first_of('='); +void WorldSave::Write(const World::Config &conf) const { + if (!make_dirs(root_path)) { + throw runtime_error("failed to create world save directory"); + } - auto name_begin = line.find_first_not_of(spaces, 0, sizeof(spaces)); - auto name_end = equals_pos - 1; - while (name_end > name_begin && isspace(line[name_end])) { - --name_end; - } + ofstream out(world_conf_path); + out << "spawn = " << conf.spawn << ';' << endl; + out.close(); - auto value_begin = line.find_first_not_of(spaces, equals_pos + 1, sizeof(spaces)); - auto value_end = line.length() - 1; - while (value_end > value_begin && isspace(line[value_end])) { - --value_end; - } + if (!out) { + throw runtime_error("failed to write world config"); + } +} - string name(line, name_begin, name_end - name_begin + 1); - string value(line, value_begin, value_end - value_begin + 1); +void WorldSave::Read(Generator::Config &conf) const { + ifstream is(gen_conf_path); + if (!is) { + throw runtime_error("failed to open generator config"); + } + TokenStreamReader in(is); + + string name; + while (in.HasMore()) { + in.ReadIdentifier(name); + in.Skip(Token::EQUALS); if (name == "seed") { - conf.gen.seed = stoul(value); - } else { - throw runtime_error("unknown world option: " + name); + in.ReadNumber(conf.seed); + } + if (in.HasMore() && in.Peek().type == Token::SEMICOLON) { + in.Skip(Token::SEMICOLON); } } - if (in.bad()) { - throw runtime_error("IO error reading world config"); + + if (is.bad()) { + throw runtime_error("IO error reading generator config"); } } -void WorldSave::Write(const World::Config &conf) const { +void WorldSave::Write(const Generator::Config &conf) const { if (!make_dirs(root_path)) { throw runtime_error("failed to create world save directory"); } - ofstream out(conf_path); - out << "seed = " << conf.gen.seed << endl; + ofstream out(gen_conf_path); + out << "seed = " << conf.seed << ';' << endl; out.close(); if (!out) { - throw runtime_error("failed to write world config"); + throw runtime_error("failed to write generator config"); + } +} + + +bool WorldSave::Exists(const Player &player) const { + return is_file(PlayerPath(player)); +} + +void WorldSave::Read(Player &player) const { + ifstream is(PlayerPath(player)); + TokenStreamReader in(is); + string name; + EntityState state; + while (in.HasMore()) { + in.ReadIdentifier(name); + in.Skip(Token::EQUALS); + if (name == "chunk") { + in.ReadVec(state.pos.chunk); + } else if (name == "position") { + in.ReadVec(state.pos.block); + } else if (name == "orientation") { + in.ReadQuat(state.orient); + } else if (name == "pitch") { + state.pitch = in.GetFloat(); + } else if (name == "yaw") { + state.yaw = in.GetFloat(); + } else if (name == "slot") { + int slot; + in.ReadNumber(slot); + player.SetInventorySlot(slot); + } + if (in.HasMore() && in.Peek().type == Token::SEMICOLON) { + in.Skip(Token::SEMICOLON); + } } + player.GetEntity().SetState(state); + player.Update(0); +} + +void WorldSave::Write(const Player &player) const { + if (!make_dirs(player_path)) { + throw runtime_error("failed to create player save directory"); + } + const EntityState &state = player.GetEntity().GetState(); + ofstream out(PlayerPath(player)); + out << "chunk = " << state.pos.chunk << ';' << endl; + out << "position = " << state.pos.block << ';' << endl; + out << "orientation = " << state.orient << ';' << endl; + out << "pitch = " << state.pitch << ';' << endl; + out << "yaw = " << state.yaw << ';' << endl; + out << "slot = " << player.GetInventorySlot() << ';' << endl; +} + +string WorldSave::PlayerPath(const Player &player) const { + // TODO: this is potentially dangerous, server and client should + // provide a sanitized name for storage + return player_path + player.Name(); } -bool WorldSave::Exists(const Chunk::Pos &pos) const noexcept { +bool WorldSave::Exists(const ExactLocation::Coarse &pos) const noexcept { return is_file(ChunkPath(pos)); } @@ -100,7 +185,8 @@ void WorldSave::Read(Chunk &chunk) const { if (gzclose(file) != Z_OK) { throw runtime_error("failed to read chunk file"); } - chunk.InvalidateModel(); + chunk.ScanActive(); + chunk.InvalidateMesh(); chunk.ClearSave(); } @@ -111,7 +197,7 @@ void WorldSave::Write(Chunk &chunk) const { // check if it's because of a missing path component if (errno != ENOENT) { // nope, fatal - throw runtime_error(strerror(errno)); + throw SysError(); } string dir_path(path); dir_path.erase(dir_path.find_last_of("\\/")); @@ -134,7 +220,7 @@ void WorldSave::Write(Chunk &chunk) const { } -const char *WorldSave::ChunkPath(const Chunk::Pos &pos) const { +const char *WorldSave::ChunkPath(const ExactLocation::Coarse &pos) const { snprintf(chunk_buf.get(), chunk_bufsiz, chunk_path.c_str(), pos.x, pos.y, pos.z); return chunk_buf.get(); }