]> git.localhorst.tv Git - blank.git/commitdiff
store players in world save
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Thu, 1 Oct 2015 13:22:31 +0000 (15:22 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Thu, 1 Oct 2015 15:33:45 +0000 (17:33 +0200)
14 files changed:
src/client/client.cpp
src/io/TokenStreamReader.hpp
src/io/WorldSave.cpp
src/io/WorldSave.hpp
src/io/token.cpp
src/rand/SimplexNoise.hpp
src/rand/noise.cpp
src/server/Server.hpp
src/server/ServerState.cpp
src/server/net.cpp
src/standalone/MasterState.cpp
src/standalone/MasterState.hpp
src/world/Generator.hpp
src/world/world.cpp

index 91d6aed6992e7327fc23116fcea59c9f902c8c00..e2702245a228fa1ba0d4e39c3e66c2d252dbc92b 100644 (file)
@@ -131,6 +131,9 @@ InteractiveState::InteractiveState(MasterState &master, uint32_t player_id)
        chunk_renderer.FogDensity(master.GetWorldConf().fog_density);
        skeletons.Load();
        loop_timer.Start();
+       if (save.Exists(player)) {
+               save.Read(player);
+       }
 }
 
 void InteractiveState::OnEnter() {
@@ -158,7 +161,7 @@ void InteractiveState::Handle(const SDL_Event &event) {
                        interface.Handle(event.wheel);
                        break;
                case SDL_QUIT:
-                       master.Quit();
+                       Exit();
                        break;
                default:
                        break;
@@ -273,6 +276,7 @@ void InteractiveState::SetDebug(bool b) {
 }
 
 void InteractiveState::Exit() {
+       save.Write(player);
        master.Quit();
 }
 
index caa620705179c3e8ef51e10d04144d1c8043e2e0..d4356aa3d91d0e7129ab8ea3353f545f1544d004 100644 (file)
@@ -37,6 +37,8 @@ public:
        void ReadVec(glm::ivec3 &);
        void ReadVec(glm::ivec4 &);
 
+       void ReadQuat(glm::quat &);
+
        bool GetBool();
        float GetFloat();
        int GetInt();
index 5cfb7f9d7fd7ec24bcdfc2cdccb18db18e049348..f45410b8d75b2eb0a2cbb145958584b2402d845c 100644 (file)
@@ -1,6 +1,7 @@
 #include "WorldSave.hpp"
 
 #include "filesystem.hpp"
+#include "TokenStreamReader.hpp"
 
 #include <cctype>
 #include <cstring>
@@ -9,6 +10,7 @@
 #include <limits>
 #include <stdexcept>
 #include <zlib.h>
+#include <glm/gtx/io.hpp>
 
 using namespace std;
 
@@ -19,6 +21,7 @@ WorldSave::WorldSave(const string &path)
 : root_path(path)
 , 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<int>::digits10)
 , chunk_buf(new char[chunk_bufsiz]) {
@@ -31,42 +34,26 @@ bool WorldSave::Exists() const noexcept {
 }
 
 
-// TODO: better implementation of config files
 void WorldSave::Read(World::Config &conf) const {
-       ifstream in(world_conf_path);
-       if (!in) {
+       ifstream is(world_conf_path);
+       if (!is) {
                throw runtime_error("failed to open world config");
        }
+       TokenStreamReader in(is);
 
-       constexpr char spaces[] = "\n\r\t ";
-
-       string line;
-       while (getline(in, line)) {
-               if (line.empty() || line[0] == '#') continue;
-               auto equals_pos = line.find_first_of('=');
-
-               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;
+       string name;
+       while (in.HasMore()) {
+               in.ReadIdentifier(name);
+               in.Skip(Token::EQUALS);
+               if (name == "spawn") {
+                       in.ReadVec(conf.spawn);
                }
-
-               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 (in.HasMore() && in.Peek().type == Token::SEMICOLON) {
+                       in.Skip(Token::SEMICOLON);
                }
-
-               string name(line, name_begin, name_end - name_begin + 1);
-               string value(line, value_begin, value_end - value_begin + 1);
-
-       //      if (name == "seed") {
-       //              conf.gen.seed = stoul(value);
-       //      } else {
-                       throw runtime_error("unknown world option: " + name);
-       //      }
        }
-       if (in.bad()) {
+
+       if (is.bad()) {
                throw runtime_error("IO error reading world config");
        }
 }
@@ -77,7 +64,7 @@ void WorldSave::Write(const World::Config &conf) const {
        }
 
        ofstream out(world_conf_path);
-       //out << "seed = " << conf.gen.seed << endl;
+       out << "spawn = " << conf.spawn << ';' << endl;
        out.close();
 
        if (!out) {
@@ -87,41 +74,26 @@ void WorldSave::Write(const World::Config &conf) const {
 
 
 void WorldSave::Read(Generator::Config &conf) const {
-       ifstream in(gen_conf_path);
-       if (!in) {
+       ifstream is(gen_conf_path);
+       if (!is) {
                throw runtime_error("failed to open generator config");
        }
+       TokenStreamReader in(is);
 
-       constexpr char spaces[] = "\n\r\t ";
-
-       string line;
-       while (getline(in, line)) {
-               if (line.empty() || line[0] == '#') continue;
-               auto equals_pos = line.find_first_of('=');
-
-               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;
-               }
-
-               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;
-               }
-
-               string name(line, name_begin, name_end - name_begin + 1);
-               string value(line, value_begin, value_end - value_begin + 1);
-
+       string name;
+       while (in.HasMore()) {
+               in.ReadIdentifier(name);
+               in.Skip(Token::EQUALS);
                if (name == "seed") {
-                       conf.seed = stoul(value);
-               } else {
-                       throw runtime_error("unknown generator 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");
        }
 }
 
@@ -131,7 +103,7 @@ void WorldSave::Write(const Generator::Config &conf) const {
        }
 
        ofstream out(gen_conf_path);
-       out << "seed = " << conf.seed << endl;
+       out << "seed = " << conf.seed << ';' << endl;
        out.close();
 
        if (!out) {
@@ -140,6 +112,55 @@ void WorldSave::Write(const Generator::Config &conf) const {
 }
 
 
+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.chunk_pos);
+               } else if (name == "position") {
+                       in.ReadVec(state.block_pos);
+               } else if (name == "orientation") {
+                       in.ReadQuat(state.orient);
+               } 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);
+}
+
+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.chunk_pos << ';' << endl;
+       out << "position = " << state.block_pos << ';' << endl;
+       out << "orientation = " << state.orient << ';' << 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 {
        return is_file(ChunkPath(pos));
 }
index 1796257fe748d62ecfbf88f2bb0e94c5e13c3abf..d12de952f986ba46fcadaf187f179991de44b5c0 100644 (file)
@@ -11,6 +11,8 @@
 
 namespace blank {
 
+class Player;
+
 class WorldSave {
 
 public:
@@ -24,17 +26,23 @@ public:
        void Read(Generator::Config &) const;
        void Write(const Generator::Config &) const;
 
+       // player
+       bool Exists(const Player &) const;
+       void Read(Player &) const;
+       void Write(const Player &) const;
+       std::string PlayerPath(const Player &) const;
+
        // single chunk
        bool Exists(const Chunk::Pos &) const noexcept;
        void Read(Chunk &) const;
        void Write(Chunk &) const;
-
        const char *ChunkPath(const Chunk::Pos &) const;
 
 private:
        std::string root_path;
        std::string world_conf_path;
        std::string gen_conf_path;
+       std::string player_path;
        std::string chunk_path;
        std::size_t chunk_bufsiz;
        std::unique_ptr<char[]> chunk_buf;
index 76a9c2f355e42a54f26f758b3bd76466160fc5aa..cf3ac3b97ec87e7af45f94252807fc2bfb8237cd 100644 (file)
@@ -4,6 +4,7 @@
 #include <cctype>
 #include <istream>
 #include <stdexcept>
+#include <glm/gtc/quaternion.hpp>
 
 using namespace std;
 
@@ -330,6 +331,18 @@ void TokenStreamReader::ReadVec(glm::ivec4 &v) {
        Skip(Token::BRACKET_CLOSE);
 }
 
+void TokenStreamReader::ReadQuat(glm::quat &q) {
+       Skip(Token::BRACKET_OPEN);
+       ReadNumber(q.w);
+       Skip(Token::COMMA);
+       ReadNumber(q.x);
+       Skip(Token::COMMA);
+       ReadNumber(q.y);
+       Skip(Token::COMMA);
+       ReadNumber(q.z);
+       Skip(Token::BRACKET_CLOSE);
+}
+
 
 bool TokenStreamReader::GetBool() {
        Next();
index 7a7242b05b59af021fbd741dc9a84202f96feea9..15789310a3243139f6e05d1e3854c1bcbd35ddec 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef BLANK_RAND_SIMPLEXNOISE_HPP_
 #define BLANK_RAND_SIMPLEXNOISE_HPP_
 
+#include <cstdint>
 #include <glm/glm.hpp>
 
 
@@ -10,7 +11,7 @@ namespace blank {
 class SimplexNoise {
 
 public:
-       explicit SimplexNoise(unsigned int seed) noexcept;
+       explicit SimplexNoise(std::uint64_t seed) noexcept;
 
        float operator ()(const glm::vec3 &) const noexcept;
 
index 664096dc36c5559bb87ae86021072cf2ee0779ab..e4ad3102e006795d3407b1576ceb4897fb35327f 100644 (file)
@@ -14,7 +14,7 @@ constexpr float one_sixth = 1.0f/6.0f;
 
 namespace blank {
 
-SimplexNoise::SimplexNoise(unsigned int seed) noexcept
+SimplexNoise::SimplexNoise(std::uint64_t seed) noexcept
 : grad({
        {  1.0f,  1.0f,  0.0f },
        { -1.0f,  1.0f,  0.0f },
index 1a4c0b4f44b9c84de2cda7530737db6c1acdc80d..372a947ef65fe0e14c1aa209d091036fcd47ed6b 100644 (file)
@@ -12,6 +12,7 @@ namespace blank {
 
 class CompositeModel;
 class World;
+class WorldSave;
 
 namespace server {
 
@@ -21,7 +22,7 @@ class Server
 : public WorldManipulator {
 
 public:
-       Server(const Config::Network &, World &);
+       Server(const Config::Network &, World &, const WorldSave &);
        ~Server();
 
        void Handle();
@@ -32,6 +33,7 @@ public:
        UDPpacket &GetPacket() noexcept { return serv_pack; }
 
        World &GetWorld() noexcept { return world; }
+       const WorldSave &GetWorldSave() noexcept { return save; }
 
        void SetPlayerModel(const CompositeModel &) noexcept;
        bool HasPlayerModel() const noexcept;
@@ -50,6 +52,7 @@ private:
        std::list<ClientConnection> clients;
 
        World &world;
+       const WorldSave &save;
        const CompositeModel *player_model;
 
 };
index 8657df08983e4efff57a086cfb0ba964b68853c4..cedae4ee9a8b4b4376e77f480645d45c64141217 100644 (file)
@@ -24,7 +24,7 @@ ServerState::ServerState(
 , chunk_loader(world.Chunks(), generator, ws)
 , skeletons()
 , spawner(world, skeletons, gc.seed)
-, server(config.net, world)
+, server(config.net, world, ws)
 , loop_timer(16) {
        TextureIndex tex_index;
        env.loader.LoadBlockTypes("default", block_types, tex_index);
index 8b4448e46d6b363051d755f8fe6f05c2d2c871ca..7cb07cdf02b9ae1e6f026493f20e0dd5aed65707 100644 (file)
@@ -3,6 +3,7 @@
 #include "Server.hpp"
 
 #include "../app/init.hpp"
+#include "../io/WorldSave.hpp"
 #include "../model/CompositeModel.hpp"
 #include "../world/ChunkIndex.hpp"
 #include "../world/Entity.hpp"
@@ -394,6 +395,9 @@ void ClientConnection::AttachPlayer(Player &player) {
        DetachPlayer();
        input.reset(new DirectInput(server.GetWorld(), player, server));
        PlayerEntity().Ref();
+       if (server.GetWorldSave().Exists(player)) {
+               server.GetWorldSave().Read(player);
+       }
 
        old_base = PlayerChunks().Base();
        Chunk::Pos begin = PlayerChunks().CoordsBegin();
@@ -415,6 +419,7 @@ void ClientConnection::AttachPlayer(Player &player) {
 void ClientConnection::DetachPlayer() {
        if (!HasPlayer()) return;
        cout << "player \"" << input->GetPlayer().Name() << "\" left" << endl;
+       server.GetWorldSave().Write(input->GetPlayer());
        PlayerEntity().Kill();
        PlayerEntity().UnRef();
        input.reset();
@@ -560,11 +565,12 @@ bool ClientConnection::ChunkInRange(const glm::ivec3 &pos) const noexcept {
 }
 
 
-Server::Server(const Config::Network &conf, World &world)
+Server::Server(const Config::Network &conf, World &world, const WorldSave &save)
 : serv_sock(nullptr)
 , serv_pack{ -1, nullptr, 0 }
 , clients()
 , world(world)
+, save(save)
 , player_model(nullptr) {
        serv_sock = SDLNet_UDP_Open(conf.port);
        if (!serv_sock) {
index 635f2733fc4590317e36cd365e3ef9a2e52ce109..98d3c839d8689dfc0950ba8ff9cb309d5c14eadf 100644 (file)
@@ -4,6 +4,7 @@
 #include "../app/Environment.hpp"
 #include "../app/init.hpp"
 #include "../app/TextureIndex.hpp"
+#include "../io/WorldSave.hpp"
 
 #include <SDL.h>
 
@@ -21,6 +22,7 @@ MasterState::MasterState(
 : config(config)
 , env(env)
 , block_types()
+, save(save)
 , world(block_types, wc)
 , player(*world.AddPlayer(config.player.name))
 , hud(env, config, player)
@@ -42,6 +44,9 @@ MasterState::MasterState(
        chunk_renderer.FogDensity(wc.fog_density);
        skeletons.Load();
        spawner.LimitSkeletons(0, skeletons.Size());
+       if (save.Exists(player)) {
+               save.Read(player);
+       }
 }
 
 
@@ -72,7 +77,7 @@ void MasterState::Handle(const SDL_Event &event) {
                        interface.Handle(event.wheel);
                        break;
                case SDL_QUIT:
-                       env.state.Switch(&unload);
+                       Exit();
                        break;
                default:
                        break;
@@ -151,7 +156,8 @@ void MasterState::SetDebug(bool b) {
 }
 
 void MasterState::Exit() {
-       env.state.Pop();
+       save.Write(player);
+       env.state.Switch(&unload);
 }
 
 }
index f2c702fb2033df38fbfe39df28e1ac930760e5f8..823e7f3a67ed781f1c274e5f598c004839bb8966 100644 (file)
@@ -60,6 +60,7 @@ private:
        Config &config;
        Environment &env;
        BlockTypeRegistry block_types;
+       const WorldSave &save;
        World world;
        Player &player;
        HUD hud;
index b3cb61f891e5bef8f8535f5a4a52980747f9145f..aadf2cfbeed4aaa2f0167f10e524d863c997ecb2 100644 (file)
@@ -5,6 +5,7 @@
 #include "../rand/SimplexNoise.hpp"
 #include "../rand/WorleyNoise.hpp"
 
+#include <cstdint>
 #include <vector>
 
 
@@ -16,7 +17,7 @@ class Generator {
 
 public:
        struct Config {
-               unsigned int seed = 0;
+               std::uint64_t seed = 0;
                float stretch = 64.0f;
                float solid_threshold = 0.5f;
        };
index e659322aa333c753fd953a718fc0c18b7eace276..f648baad45c082da5139066bf7fed8f68e6a1334 100644 (file)
@@ -167,7 +167,6 @@ Player *World::AddPlayer(const std::string &name) {
        }
        Entity &entity = AddEntity();
        entity.Name(name);
-       // TODO: load from save file here
        entity.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
        entity.WorldCollidable(true);
        entity.Position(config.spawn);
@@ -187,7 +186,6 @@ Player *World::AddPlayer(const std::string &name, std::uint32_t id) {
                return nullptr;
        }
        entity->Name(name);
-       // TODO: load from save file here
        entity->Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
        entity->WorldCollidable(true);
        entity->Position(config.spawn);