]> git.localhorst.tv Git - blank.git/commitdiff
reorganized client state
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 4 Sep 2015 08:42:45 +0000 (10:42 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 4 Sep 2015 08:42:45 +0000 (10:42 +0200)
16 files changed:
src/app/ClientState.cpp [deleted file]
src/app/ClientState.hpp [deleted file]
src/app/Environment.hpp
src/app/Runtime.hpp
src/app/StateControl.hpp
src/app/app.cpp
src/app/runtime.cpp
src/client/InitialState.hpp [new file with mode: 0644]
src/client/InteractiveState.hpp [new file with mode: 0644]
src/client/MasterState.hpp [new file with mode: 0644]
src/client/client.cpp [new file with mode: 0644]
src/net/Client.hpp
src/net/Connection.hpp
src/net/Packet.hpp
src/net/PacketHandler.hpp [new file with mode: 0644]
src/net/net.cpp

diff --git a/src/app/ClientState.cpp b/src/app/ClientState.cpp
deleted file mode 100644 (file)
index b4ed3f8..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-#include "ClientState.hpp"
-
-#include "Environment.hpp"
-#include "init.hpp"
-#include "TextureIndex.hpp"
-
-namespace blank {
-
-ClientState::ClientState(
-       Environment &env,
-       const World::Config &wc,
-       const WorldSave &ws,
-       const Interface::Config &ic,
-       const Client::Config &cc
-)
-: env(env)
-, block_types()
-, world(block_types, wc, ws)
-, chunk_renderer(world, wc.load.load_dist)
-, interface(ic, env, world)
-, client(cc, world) {
-       TextureIndex tex_index;
-       env.loader.LoadBlockTypes("default", block_types, tex_index);
-       chunk_renderer.LoadTextures(env.loader, tex_index);
-       chunk_renderer.FogDensity(wc.fog_density);
-       // TODO: better solution for initializing HUD
-       interface.SelectNext();
-       client.SendLogin(ic.player_name);
-}
-
-
-void ClientState::OnEnter() {
-       env.window.GrabMouse();
-}
-
-
-void ClientState::Handle(const SDL_Event &event) {
-       switch (event.type) {
-               case SDL_KEYDOWN:
-                       interface.HandlePress(event.key);
-                       break;
-               case SDL_KEYUP:
-                       interface.HandleRelease(event.key);
-                       break;
-               case SDL_MOUSEBUTTONDOWN:
-                       interface.HandlePress(event.button);
-                       break;
-               case SDL_MOUSEBUTTONUP:
-                       interface.HandleRelease(event.button);
-                       break;
-               case SDL_MOUSEMOTION:
-                       interface.Handle(event.motion);
-                       break;
-               case SDL_MOUSEWHEEL:
-                       interface.Handle(event.wheel);
-                       break;
-               case SDL_QUIT:
-                       env.state.Pop();
-                       break;
-               default:
-                       break;
-       }
-}
-
-
-void ClientState::Update(int dt) {
-       client.Handle();
-       client.Update(dt);
-       if (client.TimedOut()) {
-               env.state.Pop();
-       }
-
-       interface.Update(dt);
-       world.Update(dt);
-       chunk_renderer.Rebase(interface.Player().ChunkCoords());
-       chunk_renderer.Update(dt);
-
-       glm::mat4 trans = interface.Player().Transform(interface.Player().ChunkCoords());
-       glm::vec3 dir(trans * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
-       glm::vec3 up(trans * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
-       env.audio.Position(interface.Player().Position());
-       env.audio.Velocity(interface.Player().Velocity());
-       env.audio.Orientation(dir, up);
-}
-
-
-void ClientState::Render(Viewport &viewport) {
-       viewport.WorldPosition(interface.Player().Transform(interface.Player().ChunkCoords()));
-       chunk_renderer.Render(viewport);
-       world.Render(viewport);
-       interface.Render(viewport);
-}
-
-}
diff --git a/src/app/ClientState.hpp b/src/app/ClientState.hpp
deleted file mode 100644 (file)
index 8868c98..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef BLANK_APP_CLIENTSTATE_HPP_
-#define BLANK_APP_CLIENTSTATE_HPP_
-
-#include "State.hpp"
-#include "../net/Client.hpp"
-#include "../ui/Interface.hpp"
-#include "../world/BlockTypeRegistry.hpp"
-#include "../world/ChunkRenderer.hpp"
-#include "../world/World.hpp"
-
-
-namespace blank {
-
-class Environment;
-
-class ClientState
-: public State {
-
-public:
-       ClientState(
-               Environment &,
-               const World::Config &,
-               const WorldSave &,
-               const Interface::Config &,
-               const Client::Config &
-       );
-
-       void OnEnter() override;
-
-       void Handle(const SDL_Event &) override;
-       void Update(int dt) override;
-       void Render(Viewport &) override;
-
-private:
-       Environment &env;
-       BlockTypeRegistry block_types;
-       World world;
-       ChunkRenderer chunk_renderer;
-       Interface interface;
-       Client client;
-
-};
-
-}
-
-#endif
index 95813fc12dd7ad43f5e6352924939a80c26040e2..97f4a7ec54434e839dfaba76c66095a80bb17cf7 100644 (file)
@@ -17,6 +17,19 @@ class Window;
 
 struct HeadlessEnvironment {
 
+       struct Config {
+               std::string asset_path;
+               std::string save_path;
+
+               std::string GetWorldPath(
+                       const std::string &world_name
+               ) const;
+               std::string GetWorldPath(
+                       const std::string &world_name,
+                       const std::string &hostname
+               ) const;
+       } config;
+
        AssetLoader loader;
 
        FrameCounter counter;
@@ -24,7 +37,7 @@ struct HeadlessEnvironment {
        StateControl state;
 
 
-       explicit HeadlessEnvironment(const std::string &asset_path);
+       explicit HeadlessEnvironment(const Config &);
 
 };
 
@@ -41,7 +54,7 @@ struct Environment
        Keymap keymap;
 
 
-       Environment(Window &win, const std::string &asset_path);
+       Environment(Window &win, const Config &);
 
 };
 
index 904431b021bf2d346fac78e9dff5d2586ea05414..10a0e55cb559365366c29c090c2dc29bfb67778f 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef BLANK_RUNTIME_HPP_
 #define BLANK_RUNTIME_HPP_
 
+#include "Environment.hpp"
 #include "../net/Client.hpp"
 #include "../net/Server.hpp"
 #include "../ui/Interface.hpp"
@@ -42,10 +43,8 @@ public:
                bool doublebuf = true;
                int multisampling = 1;
 
-               std::string asset_path;
-               std::string save_path;
-
                Client::Config client = Client::Config();
+               HeadlessEnvironment::Config env = HeadlessEnvironment::Config();
                Interface::Config interface = Interface::Config();
                Server::Config server = Server::Config();
                World::Config world = World::Config();
index c072040bc12f57de2a31abc6a9465fbfa1e3a551..d679a53ba211c3ad5fd498aeaf84443a51750ef2 100644 (file)
@@ -12,22 +12,37 @@ class State;
 class StateControl {
 
 public:
+       // add state to the front
        void Push(State *s) {
                cue.emplace(PUSH, s);
        }
 
+       // swap state at the front
        void Switch(State *s) {
                cue.emplace(SWITCH, s);
        }
 
+       // remove state at the front
        void Pop() {
                cue.emplace(POP);
        }
 
+       // remove all states
+       // application will exit if nothing is pushed after this
        void PopAll() {
                cue.emplace(POP_ALL);
        }
 
+       // pop states until this one is on top
+       void PopAfter(State *s) {
+               cue.emplace(POP_AFTER, s);
+       }
+
+       // pop states until this one is removed
+       void PopUntil(State *s) {
+               cue.emplace(POP_UNTIL, s);
+       }
+
 
        void Commit(HeadlessApplication &);
 
@@ -37,6 +52,8 @@ private:
                SWITCH,
                POP,
                POP_ALL,
+               POP_AFTER,
+               POP_UNTIL,
        };
        struct Memo {
                State *state;
index 48a59502ad974dac9d98eed82d49eaaeaa13487b..4368022eb59dbddc5c981f9e5e172af4af8f56cf 100644 (file)
@@ -270,6 +270,17 @@ void StateControl::Commit(HeadlessApplication &app) {
                                        app.PopState();
                                }
                                break;
+                       case POP_AFTER:
+                               while (app.HasState() && &app.GetState() != m.state) {
+                                       app.PopState();
+                               }
+                               break;
+                       case POP_UNTIL:
+                               while (app.HasState()) {
+                                       if (app.PopState() == m.state) {
+                                               break;
+                                       }
+                               }
                }
        }
 }
index e551e9d9309811f539f36d65800560ade736e8fd..e169a7bb5eb1d73efd4efa997ddc4f902c119ca9 100644 (file)
@@ -1,11 +1,11 @@
 #include "Application.hpp"
-#include "ClientState.hpp"
 #include "Environment.hpp"
 #include "Runtime.hpp"
 #include "ServerState.hpp"
 #include "WorldState.hpp"
 
 #include "init.hpp"
+#include "../client/MasterState.hpp"
 #include "../io/filesystem.hpp"
 #include "../io/WorldSave.hpp"
 
@@ -47,15 +47,24 @@ string default_save_path() {
 
 namespace blank {
 
-HeadlessEnvironment::HeadlessEnvironment(const string &asset_path)
-: loader(asset_path)
+HeadlessEnvironment::HeadlessEnvironment(const Config &config)
+: config(config)
+, loader(config.asset_path)
 , counter()
 , state() {
 
 }
 
-Environment::Environment(Window &win, const string &asset_path)
-: HeadlessEnvironment(asset_path)
+string HeadlessEnvironment::Config::GetWorldPath(const string &world_name) const {
+       return save_path + "worlds/" + world_name + '/';
+}
+
+string HeadlessEnvironment::Config::GetWorldPath(const string &world_name, const string &host_name) const {
+       return save_path + "cache/" + host_name + '/' + world_name + '/';
+}
+
+Environment::Environment(Window &win, const Config &config)
+: HeadlessEnvironment(config)
 , assets(loader)
 , audio()
 , viewport()
@@ -64,6 +73,15 @@ Environment::Environment(Window &win, const string &asset_path)
        viewport.Clear();
        window.Flip();
        keymap.LoadDefault();
+
+       string keys_path = config.save_path + "keys.conf";
+       if (!is_file(keys_path)) {
+               std::ofstream file(keys_path);
+               keymap.Save(file);
+       } else {
+               std::ifstream file(keys_path);
+               keymap.Load(file);
+       }
 }
 
 
@@ -123,7 +141,7 @@ void Runtime::ReadArgs(int argc, const char *const *argv) {
                                                        cerr << "missing argument to --asset-path" << endl;
                                                        error = true;
                                                } else {
-                                                       config.asset_path = argv[i];
+                                                       config.env.asset_path = argv[i];
                                                }
                                        } else if (strcmp(param, "host") == 0) {
                                                ++i;
@@ -156,7 +174,7 @@ void Runtime::ReadArgs(int argc, const char *const *argv) {
                                                        cerr << "missing argument to --save-path" << endl;
                                                        error = true;
                                                } else {
-                                                       config.save_path = argv[i];
+                                                       config.env.save_path = argv[i];
                                                }
                                        } else if (strcmp(param, "world-name") == 0) {
                                                ++i;
@@ -237,21 +255,21 @@ void Runtime::ReadArgs(int argc, const char *const *argv) {
                return;
        }
 
-       if (config.asset_path.empty()) {
-               config.asset_path = default_asset_path();
+       if (config.env.asset_path.empty()) {
+               config.env.asset_path = default_asset_path();
        } else if (
-               config.asset_path[config.asset_path.size() - 1] != '/' &&
-               config.asset_path[config.asset_path.size() - 1] != '\\'
+               config.env.asset_path[config.env.asset_path.size() - 1] != '/' &&
+               config.env.asset_path[config.env.asset_path.size() - 1] != '\\'
        ) {
-               config.asset_path += '/';
+               config.env.asset_path += '/';
        }
-       if (config.save_path.empty()) {
-               config.save_path = default_save_path();
+       if (config.env.save_path.empty()) {
+               config.env.save_path = default_save_path();
        } else if (
-               config.save_path[config.save_path.size() - 1] != '/' &&
-               config.save_path[config.save_path.size() - 1] != '\\'
+               config.env.save_path[config.env.save_path.size() - 1] != '/' &&
+               config.env.save_path[config.env.save_path.size() - 1] != '\\'
        ) {
-               config.save_path += '/';
+               config.env.save_path += '/';
        }
 
        if (n > 0) {
@@ -293,25 +311,16 @@ int Runtime::Execute() {
 void Runtime::RunStandalone() {
        Init init(config.doublebuf, config.multisampling);
 
-       Environment env(init.window, config.asset_path);
+       Environment env(init.window, config.env);
        env.viewport.VSync(config.vsync);
 
-       WorldSave save(config.save_path + config.world.name + '/');
+       WorldSave save(config.env.GetWorldPath(config.world.name));
        if (save.Exists()) {
                save.Read(config.world);
        } else {
                save.Write(config.world);
        }
 
-       std::string keys_path = config.save_path + "keys.conf";
-       if (!is_file(keys_path)) {
-               std::ofstream file(keys_path);
-               env.keymap.Save(file);
-       } else {
-               std::ifstream file(keys_path);
-               env.keymap.Load(file);
-       }
-
        Application app(env);
        WorldState world_state(env, config.interface, config.world, save);
        app.PushState(&world_state);
@@ -319,9 +328,9 @@ void Runtime::RunStandalone() {
 }
 
 void Runtime::RunServer() {
-       HeadlessEnvironment env(config.asset_path);
+       HeadlessEnvironment env(config.env);
 
-       WorldSave save(config.save_path + config.world.name + '/');
+       WorldSave save(config.env.GetWorldPath(config.world.name));
        if (save.Exists()) {
                save.Read(config.world);
        } else {
@@ -337,27 +346,11 @@ void Runtime::RunServer() {
 void Runtime::RunClient() {
        Init init(config.doublebuf, config.multisampling);
 
-       Environment env(init.window, config.asset_path);
+       Environment env(init.window, config.env);
        env.viewport.VSync(config.vsync);
 
-       WorldSave save(config.save_path + config.world.name + '/');
-       if (save.Exists()) {
-               save.Read(config.world);
-       } else {
-               save.Write(config.world);
-       }
-
-       std::string keys_path = config.save_path + "keys.conf";
-       if (!is_file(keys_path)) {
-               std::ofstream file(keys_path);
-               env.keymap.Save(file);
-       } else {
-               std::ifstream file(keys_path);
-               env.keymap.Load(file);
-       }
-
        Application app(env);
-       ClientState client_state(env, config.world, save, config.interface, config.client);
+       client::MasterState client_state(env, config.world, config.interface, config.client);
        app.PushState(&client_state);
        Run(app);
 }
diff --git a/src/client/InitialState.hpp b/src/client/InitialState.hpp
new file mode 100644 (file)
index 0000000..05e457d
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef BLANK_CLIENT_INITIALSTATE_HPP_
+#define BLANK_CLIENT_INITIALSTATE_HPP_
+
+#include "../app/State.hpp"
+#include "../ui/FixedText.hpp"
+
+
+namespace blank {
+namespace client {
+
+class MasterState;
+
+class InitialState
+: public State {
+
+public:
+       explicit InitialState(MasterState &);
+
+       void OnEnter() override;
+
+       void Handle(const SDL_Event &) override;
+       void Update(int dt) override;
+       void Render(Viewport &) override;
+
+private:
+       MasterState &master;
+       FixedText message;
+
+};
+
+}
+}
+
+#endif
diff --git a/src/client/InteractiveState.hpp b/src/client/InteractiveState.hpp
new file mode 100644 (file)
index 0000000..e679eb2
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef BLANK_CLIENT_INTERACTIVESTATE_HPP_
+#define BLANK_CLIENT_INTERACTIVESTATE_HPP_
+
+#include "../app/State.hpp"
+#include "../io/WorldSave.hpp"
+#include "../ui/Interface.hpp"
+#include "../world/BlockTypeRegistry.hpp"
+#include "../world/ChunkRenderer.hpp"
+#include "../world/World.hpp"
+
+
+namespace blank {
+
+class Environment;
+
+namespace client {
+
+class MasterState;
+
+class InteractiveState
+: public State {
+
+public:
+       explicit InteractiveState(MasterState &);
+
+       World &GetWorld() noexcept { return world; }
+       Interface &GetInterface() noexcept { return interface; }
+
+       void OnEnter() override;
+
+       void Handle(const SDL_Event &) override;
+       void Update(int dt) override;
+       void Render(Viewport &) override;
+
+private:
+       MasterState &master;
+       BlockTypeRegistry block_types;
+       WorldSave save;
+       World world;
+       ChunkRenderer chunk_renderer;
+       Interface interface;
+
+};
+
+}
+}
+
+#endif
diff --git a/src/client/MasterState.hpp b/src/client/MasterState.hpp
new file mode 100644 (file)
index 0000000..af2e6aa
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef BLANK_CLIENT_CLIENTSTATE_HPP_
+#define BLANK_CLIENT_CLIENTSTATE_HPP_
+
+#include "InitialState.hpp"
+#include "InteractiveState.hpp"
+#include "../app/State.hpp"
+#include "../net/Client.hpp"
+#include "../net/PacketHandler.hpp"
+
+#include <memory>
+
+
+namespace blank {
+
+class Environment;
+
+namespace client {
+
+class InteractiveState;
+
+class MasterState
+: public State
+, public PacketHandler {
+
+public:
+       MasterState(
+               Environment &,
+               const World::Config &,
+               const Interface::Config &,
+               const Client::Config &
+       );
+
+       Client &GetClient() noexcept { return client; }
+       Environment &GetEnv() noexcept { return env; }
+
+       World::Config &GetWorldConf() noexcept { return world_conf; }
+       const World::Config &GetWorldConf() const noexcept { return world_conf; }
+       const Interface::Config &GetInterfaceConf() const noexcept { return intf_conf; }
+       const Client::Config &GetClientConf() const noexcept { return client_conf; }
+
+       void Quit();
+
+       void OnEnter() override;
+
+       void Handle(const SDL_Event &) override;
+       void Update(int dt) override;
+       void Render(Viewport &) override;
+
+       void On(const Packet::Join &) override;
+       void On(const Packet::Part &) override;
+
+private:
+       Environment &env;
+       World::Config world_conf;
+       const Interface::Config &intf_conf;
+       const Client::Config &client_conf;
+       std::unique_ptr<InteractiveState> state;
+       Client client;
+
+       InitialState init_state;
+
+};
+
+}
+}
+
+#endif
diff --git a/src/client/client.cpp b/src/client/client.cpp
new file mode 100644 (file)
index 0000000..ac682bb
--- /dev/null
@@ -0,0 +1,188 @@
+#include "InitialState.hpp"
+#include "InteractiveState.hpp"
+#include "MasterState.hpp"
+
+#include "../app/Environment.hpp"
+#include "../app/init.hpp"
+#include "../app/TextureIndex.hpp"
+
+#include <iostream>
+
+
+namespace blank {
+namespace client {
+
+InitialState::InitialState(MasterState &master)
+: master(master)
+, message() {
+       message.Position(glm::vec3(0.0f), Gravity::CENTER);
+       message.Set(master.GetEnv().assets.large_ui_font, "logging in");
+}
+
+void InitialState::OnEnter() {
+
+}
+
+void InitialState::Handle(const SDL_Event &evt) {
+       if (evt.type == SDL_QUIT) {
+               master.Quit();
+       }
+}
+
+void InitialState::Update(int dt) {
+       master.Update(dt);
+}
+
+void InitialState::Render(Viewport &viewport) {
+       message.Render(viewport);
+}
+
+
+InteractiveState::InteractiveState(MasterState &master)
+: master(master)
+, block_types()
+, save(master.GetEnv().config.GetWorldPath(master.GetWorldConf().name, master.GetClientConf().host))
+, world(block_types, master.GetWorldConf(), save)
+, chunk_renderer(world, master.GetWorldConf().load.load_dist)
+, interface(master.GetInterfaceConf(), master.GetEnv(), world) {
+       TextureIndex tex_index;
+       master.GetEnv().loader.LoadBlockTypes("default", block_types, tex_index);
+       chunk_renderer.LoadTextures(master.GetEnv().loader, tex_index);
+       chunk_renderer.FogDensity(master.GetWorldConf().fog_density);
+       // TODO: better solution for initializing HUD
+       interface.SelectNext();
+}
+
+void InteractiveState::OnEnter() {
+       master.GetEnv().window.GrabMouse();
+}
+
+void InteractiveState::Handle(const SDL_Event &event) {
+       switch (event.type) {
+               case SDL_KEYDOWN:
+                       interface.HandlePress(event.key);
+                       break;
+               case SDL_KEYUP:
+                       interface.HandleRelease(event.key);
+                       break;
+               case SDL_MOUSEBUTTONDOWN:
+                       interface.HandlePress(event.button);
+                       break;
+               case SDL_MOUSEBUTTONUP:
+                       interface.HandleRelease(event.button);
+                       break;
+               case SDL_MOUSEMOTION:
+                       interface.Handle(event.motion);
+                       break;
+               case SDL_MOUSEWHEEL:
+                       interface.Handle(event.wheel);
+                       break;
+               case SDL_QUIT:
+                       master.Quit();
+                       break;
+               default:
+                       break;
+       }
+}
+
+void InteractiveState::Update(int dt) {
+       master.Update(dt);
+
+       interface.Update(dt);
+       world.Update(dt);
+       chunk_renderer.Rebase(interface.Player().ChunkCoords());
+       chunk_renderer.Update(dt);
+
+       glm::mat4 trans = interface.Player().Transform(interface.Player().ChunkCoords());
+       glm::vec3 dir(trans * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
+       glm::vec3 up(trans * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
+       master.GetEnv().audio.Position(interface.Player().Position());
+       master.GetEnv().audio.Velocity(interface.Player().Velocity());
+       master.GetEnv().audio.Orientation(dir, up);
+}
+
+void InteractiveState::Render(Viewport &viewport) {
+       viewport.WorldPosition(interface.Player().Transform(interface.Player().ChunkCoords()));
+       chunk_renderer.Render(viewport);
+       world.Render(viewport);
+       interface.Render(viewport);
+}
+
+
+MasterState::MasterState(
+       Environment &env,
+       const World::Config &wc,
+       const Interface::Config &ic,
+       const Client::Config &cc)
+: env(env)
+, world_conf(wc)
+, intf_conf(ic)
+, client_conf(cc)
+, state()
+, client(cc)
+, init_state(*this) {
+       client.GetConnection().SetHandler(this);
+}
+
+void MasterState::Quit() {
+       env.state.PopUntil(this);
+}
+
+
+void MasterState::OnEnter() {
+       client.SendLogin(intf_conf.player_name);
+       env.state.Push(&init_state);
+}
+
+
+void MasterState::Handle(const SDL_Event &event) {
+
+}
+
+
+void MasterState::Update(int dt) {
+       client.Handle();
+       client.Update(dt);
+       if (client.GetConnection().Closed()) {
+               Quit();
+               // TODO: push disconnected message
+       }
+}
+
+
+void MasterState::Render(Viewport &) {
+
+}
+
+
+void MasterState::On(const Packet::Join &pack) {
+       pack.ReadWorldName(world_conf.name);
+
+       if (state) {
+               // changing worlds
+               std::cout << "server changing worlds" << std::endl;
+       } else {
+               // joining game
+               std::cout << "joined game" << std::endl;
+       }
+       state.reset(new InteractiveState(*this));
+
+       pack.ReadPlayer(state->GetInterface().Player());
+
+       env.state.PopAfter(this);
+       env.state.Push(state.get());
+}
+
+void MasterState::On(const Packet::Part &pack) {
+       if (state) {
+               // kicked
+               std::cout << "kicked by server" << std::endl;
+       } else {
+               // join refused
+               std::cout << "login refused by server" << std::endl;
+       }
+       Quit();
+}
+
+}
+}
index 5b83061ae48abc917e5bc5af5130a77a89a5d01f..3960e845c9fed551ae15b2046013bab7504af3a2 100644 (file)
@@ -20,14 +20,15 @@ public:
        };
 
 public:
-       Client(const Config &, World &);
+       explicit Client(const Config &);
        ~Client();
 
        void Handle();
 
        void Update(int dt);
 
-       bool TimedOut() { return conn.TimedOut(); }
+       Connection &GetConnection() noexcept { return conn; }
+       const Connection &GetConnection() const noexcept { return conn; }
 
        void SendPing();
        void SendLogin(const std::string &);
@@ -36,7 +37,6 @@ private:
        void HandlePacket(const UDPpacket &);
 
 private:
-       World &world;
        Connection conn;
        UDPsocket client_sock;
        UDPpacket client_pack;
index cc13ef60915df48dc676228535c50101f0eccc9d..58f315d4d58e0358033cc74dee6e4e1162cd1a14 100644 (file)
 
 namespace blank {
 
+class PacketHandler;
+
 class Connection {
 
 public:
        explicit Connection(const IPaddress &);
 
+       void SetHandler(PacketHandler *h) noexcept { handler = h; }
+       void RemoveHandler() noexcept { handler = nullptr; }
+       bool HasHandler() const noexcept { return handler; }
+       PacketHandler &Handler() noexcept { return *handler; }
+
        const IPaddress &Address() const noexcept { return addr; }
 
        bool Matches(const IPaddress &) const noexcept;
@@ -37,6 +44,7 @@ private:
        void FlagRecv() noexcept;
 
 private:
+       PacketHandler *handler;
        IPaddress addr;
        IntervalTimer send_timer;
        IntervalTimer recv_timer;
index 0d99fee2992c74c61a1979dfe251813c69e607ca..bd9dd86436a68ff0c08c548b8dd8b2031aa06ee2 100644 (file)
@@ -4,6 +4,7 @@
 #include <cstdint>
 #include <ostream>
 #include <string>
+#include <SDL_net.h>
 
 
 namespace blank {
@@ -14,14 +15,7 @@ struct Packet {
 
        static constexpr std::uint32_t TAG = 0xFB1AB1AF;
 
-       enum Type {
-               PING = 0,
-               LOGIN = 1,
-               JOIN = 2,
-               PART = 3,
-       };
-
-       static const char *Type2String(Type) noexcept;
+       static const char *Type2String(std::uint8_t) noexcept;
 
        struct TControl {
                std::uint16_t seq;
@@ -35,23 +29,91 @@ struct Packet {
                std::uint8_t type;
        } header;
 
-       std::uint8_t payload[500 - sizeof(Header)];
+       static constexpr std::size_t MAX_PAYLOAD_LEN = 500 - sizeof(Header);
 
+       std::uint8_t payload[MAX_PAYLOAD_LEN];
 
-       Type GetType() const noexcept { return Type(header.type); }
 
-       void Tag() noexcept;
+       void Tag() noexcept { header.tag = TAG; }
 
-       std::size_t MakePing() noexcept;
-       std::size_t MakeLogin(const std::string &name) noexcept;
-       std::size_t MakeJoin(const Entity &player, const std::string &world_name) noexcept;
-       std::size_t MakePart() noexcept;
+       void Type(std::uint8_t t) noexcept { header.type = t; }
+       std::uint8_t Type() const noexcept { return header.type; }
+       const char *TypeString() const noexcept { return Type2String(Type()); }
 
-};
 
-inline std::ostream &operator <<(std::ostream &out, Packet::Type t) {
-       return out << Packet::Type2String(t);
-}
+       struct Payload {
+               std::size_t length;
+               std::uint8_t *data;
+
+               template<class T>
+               void Write(const T &, size_t off) noexcept;
+               template<class T>
+               void Read(T &, size_t off) const noexcept;
+
+               void WriteString(const std::string &src, std::size_t off, std::size_t maxlen) noexcept;
+               void ReadString(std::string &dst, std::size_t off, std::size_t maxlen) const noexcept;
+       };
+
+       struct Ping : public Payload {
+               static constexpr std::uint8_t TYPE = 0;
+               static constexpr std::size_t MAX_LEN = 0;
+       };
+
+       struct Login : public Payload {
+               static constexpr std::uint8_t TYPE = 1;
+               static constexpr std::size_t MAX_LEN = 32;
+
+               void WritePlayerName(const std::string &) noexcept;
+               void ReadPlayerName(std::string &) const noexcept;
+       };
+
+       struct Join : public Payload {
+               static constexpr std::uint8_t TYPE = 2;
+               static constexpr std::size_t MAX_LEN = 100;
+
+               void WritePlayer(const Entity &) noexcept;
+               void ReadPlayer(Entity &) const noexcept;
+               void WriteWorldName(const std::string &) noexcept;
+               void ReadWorldName(std::string &) const noexcept;
+       };
+
+       struct Part : public Payload {
+               static constexpr std::uint8_t TYPE = 3;
+               static constexpr std::size_t MAX_LEN = 0;
+       };
+
+
+       template<class PayloadType>
+       PayloadType As() {
+               PayloadType result;
+               result.length = PayloadType::MAX_LEN;
+               result.data = &payload[0];
+               return result;
+       }
+
+       template<class PayloadType>
+       static PayloadType As(const UDPpacket &pack) {
+               PayloadType result;
+               result.length = std::min(pack.len - sizeof(Header), PayloadType::MAX_LEN);
+               result.data = pack.data + sizeof(Header);
+               return result;
+       }
+
+       template<class PayloadType>
+       static PayloadType Make(UDPpacket &udp_pack) {
+               Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
+               pack.Tag();
+               pack.Type(PayloadType::TYPE);
+
+               udp_pack.len = sizeof(Header) + PayloadType::TYPE;
+
+               PayloadType result;
+               result.length = PayloadType::MAX_LEN;
+               result.data = pack.payload;
+               return result;
+       }
+
+};
 
 }
 
diff --git a/src/net/PacketHandler.hpp b/src/net/PacketHandler.hpp
new file mode 100644 (file)
index 0000000..0ea9b57
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef BLANK_NET_PACKETHANDLER_HPP_
+#define BLANK_NET_PACKETHANDLER_HPP_
+
+#include "Packet.hpp"
+
+#include <SDL_net.h>
+
+
+namespace blank {
+
+class PacketHandler {
+
+public:
+       void Handle(const UDPpacket &);
+
+private:
+       virtual void On(const Packet::Ping &) { }
+       virtual void On(const Packet::Login &) { }
+       virtual void On(const Packet::Join &) { }
+       virtual void On(const Packet::Part &) { }
+
+};
+
+}
+
+#endif
index 9c29c1e09edeb96d9c7de52d433ad4e2f15ec341..1be712b9fd51b0985da0b0924c29ae3c8cf0d248 100644 (file)
@@ -2,6 +2,7 @@
 #include "Connection.hpp"
 #include "io.hpp"
 #include "Packet.hpp"
+#include "PacketHandler.hpp"
 #include "Server.hpp"
 
 #include "../app/init.hpp"
@@ -35,9 +36,8 @@ IPaddress client_resolve(const char *host, Uint16 port) {
 
 }
 
-Client::Client(const Config &conf, World &world)
-: world(world)
-, conn(client_resolve(conf.host.c_str(), conf.port))
+Client::Client(const Config &conf)
+: conn(client_resolve(conf.host.c_str(), conf.port))
 , client_sock(client_bind(0))
 , client_pack{ -1, nullptr, 0 } {
        client_pack.data = new Uint8[sizeof(Packet)];
@@ -92,14 +92,15 @@ void Client::SendPing() {
 }
 
 void Client::SendLogin(const string &name) {
-       Packet &pack = *reinterpret_cast<Packet *>(client_pack.data);
-       client_pack.len = pack.MakeLogin(name);
+       auto pack = Packet::Make<Packet::Login>(client_pack);
+       pack.WritePlayerName(name);
        conn.Send(client_pack, client_sock);
 }
 
 
 Connection::Connection(const IPaddress &addr)
-: addr(addr)
+: handler(nullptr)
+, addr(addr)
 , send_timer(3000)
 , recv_timer(10000)
 , ctrl{ 0, 0xFFFF, 0xFFFF }
@@ -137,8 +138,9 @@ void Connection::Update(int dt) {
 void Connection::Send(UDPpacket &udp_pack, UDPsocket sock) {
        Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
        pack.header.ctrl = ctrl;
+       ++ctrl.seq;
 
-       cout << "sending " << pack.GetType() << " to " << Address() << endl;
+       cout << "sending " << pack.TypeString() << " to " << Address() << endl;
 
        udp_pack.address = addr;
        if (SDLNet_UDP_Send(sock, -1, &udp_pack) == 0) {
@@ -151,7 +153,7 @@ void Connection::Send(UDPpacket &udp_pack, UDPsocket sock) {
 void Connection::Received(const UDPpacket &udp_pack) {
        Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
 
-       cout << "received " << pack.GetType() << " from " << Address() << endl;
+       cout << "received " << pack.TypeString() << " from " << Address() << endl;
 
        int diff = std::int16_t(pack.header.ctrl.seq) - std::int16_t(ctrl.ack);
 
@@ -182,12 +184,15 @@ void Connection::Received(const UDPpacket &udp_pack) {
 
        ctrl.ack = pack.header.ctrl.seq;
 
+       if (HasHandler()) {
+               Handler().Handle(udp_pack);
+       }
+
        FlagRecv();
 }
 
 void Connection::SendPing(UDPpacket &udp_pack, UDPsocket sock) {
-       Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
-       udp_pack.len = pack.MakePing();
+       Packet::Make<Packet::Ping>(udp_pack);
        Send(udp_pack, sock);
 }
 
@@ -205,85 +210,127 @@ ostream &operator <<(ostream &out, const IPaddress &addr) {
 }
 
 
-const char *Packet::Type2String(Type t) noexcept {
+const char *Packet::Type2String(uint8_t t) noexcept {
        switch (t) {
-               case PING:
-                       return "PING";
-               case LOGIN:
-                       return "LOGIN";
-               case JOIN:
-                       return "JOIN";
-               case PART:
-                       return "PART";
+               case Ping::TYPE:
+                       return "Ping";
+               case Login::TYPE:
+                       return "Login";
+               case Join::TYPE:
+                       return "Join";
+               case Part::TYPE:
+                       return "Part";
                default:
-                       return "UNKNOWN";
+                       return "Unknown";
        }
 }
 
-void Packet::Tag() noexcept {
-       header.tag = TAG;
+template<class T>
+void Packet::Payload::Write(const T &src, size_t off) noexcept {
+       if ((length - off) < sizeof(T)) {
+               // dismiss out of bounds write
+               return;
+       }
+       *reinterpret_cast<T *>(&data[off]) = src;
 }
 
-size_t Packet::MakePing() noexcept {
-       Tag();
-       header.type = PING;
-       return sizeof(Header);
+template<class T>
+void Packet::Payload::Read(T &dst, size_t off) const noexcept {
+       if ((length - off) < sizeof(T)) {
+               // dismiss out of bounds read
+               return;
+       }
+       dst = *reinterpret_cast<T *>(&data[off]);
 }
 
-size_t Packet::MakeLogin(const string &name) noexcept {
-       constexpr size_t maxname = 32;
-
-       Tag();
-       header.type = LOGIN;
-       if (name.size() < maxname) {
-               memset(payload, '\0', maxname);
-               memcpy(payload, name.c_str(), name.size());
+void Packet::Payload::WriteString(const string &src, size_t off, size_t maxlen) noexcept {
+       uint8_t *dst = &data[off];
+       size_t len = min(maxlen, length - off);
+       if (src.size() < len) {
+               memset(dst, '\0', len);
+               memcpy(dst, src.c_str(), src.size());
        } else {
-               memcpy(payload, name.c_str(), maxname);
+               memcpy(dst, src.c_str(), len);
        }
-       return sizeof(Header) + maxname;
 }
 
-size_t Packet::MakeJoin(const Entity &player, const string &world_name) noexcept {
-       constexpr size_t maxname = 32;
+void Packet::Payload::ReadString(string &dst, size_t off, size_t maxlen) const noexcept {
+       size_t len = min(maxlen, length - off);
+       dst.clear();
+       dst.reserve(len);
+       for (size_t i = 0; i < len && data[off + i] != '\0'; ++i) {
+               dst.push_back(data[off + i]);
+       }
+}
 
-       Tag();
-       header.type = JOIN;
 
-       uint8_t *cursor = &payload[0];
+void Packet::Login::WritePlayerName(const string &name) noexcept {
+       WriteString(name, 0, 32);
+}
 
+void Packet::Login::ReadPlayerName(string &name) const noexcept {
+       ReadString(name, 0, 32);
+}
+
+void Packet::Join::WritePlayer(const Entity &player) noexcept {
        // TODO: generate entity IDs
-       *reinterpret_cast<uint32_t *>(cursor) = 1;
-       cursor += 4;
+       Write(uint32_t(1), 0);
+       Write(player.ChunkCoords(), 4);
+       Write(player.Position(), 16);
+       Write(player.Velocity(), 28);
+       Write(player.Orientation(), 40);
+       Write(player.AngularVelocity(), 56);
+}
 
-       *reinterpret_cast<glm::ivec3 *>(cursor) = player.ChunkCoords();
-       cursor += 12;
+void Packet::Join::ReadPlayer(Entity &player) const noexcept {
+       uint32_t id = 0;
+       glm::ivec3 chunk_coords(0);
+       glm::vec3 pos;
+       glm::vec3 vel;
+       glm::quat rot;
+       glm::vec3 ang;
 
-       *reinterpret_cast<glm::vec3 *>(cursor) = player.Position();
-       cursor += 12;
-       *reinterpret_cast<glm::vec3 *>(cursor) = player.Velocity();
-       cursor += 12;
+       Read(id, 0);
+       Read(chunk_coords, 4);
+       Read(pos, 16);
+       Read(vel, 28);
+       Read(rot, 40);
+       Read(ang, 56);
 
-       *reinterpret_cast<glm::quat *>(cursor) = player.Orientation();
-       cursor += 16;
-       *reinterpret_cast<glm::vec3 *>(cursor) = player.AngularVelocity();
-       cursor += 12;
+       player.Position(chunk_coords, pos);
+       player.Velocity(vel);
+       player.Orientation(rot);
+       player.AngularVelocity(ang);
+}
 
-       if (world_name.size() < maxname) {
-               memset(cursor, '\0', maxname);
-               memcpy(cursor, world_name.c_str(), world_name.size());
-       } else {
-               memcpy(cursor, world_name.c_str(), maxname);
-       }
-       cursor += maxname;
+void Packet::Join::WriteWorldName(const string &name) noexcept {
+       WriteString(name, 68, 32);
+}
 
-       return sizeof(Header) + (cursor - &payload[0]);
+void Packet::Join::ReadWorldName(string &name) const noexcept {
+       ReadString(name, 68, 32);
 }
 
-size_t Packet::MakePart() noexcept {
-       Tag();
-       header.type = PART;
-       return sizeof(Header);
+
+void PacketHandler::Handle(const UDPpacket &udp_pack) {
+       const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
+       switch (pack.Type()) {
+               case Packet::Ping::TYPE:
+                       On(Packet::As<Packet::Ping>(udp_pack));
+                       break;
+               case Packet::Login::TYPE:
+                       On(Packet::As<Packet::Login>(udp_pack));
+                       break;
+               case Packet::Join::TYPE:
+                       On(Packet::As<Packet::Join>(udp_pack));
+                       break;
+               case Packet::Part::TYPE:
+                       On(Packet::As<Packet::Part>(udp_pack));
+                       break;
+               default:
+                       // drop unknown or unhandled packets
+                       break;
+       }
 }
 
 
@@ -334,10 +381,10 @@ void Server::HandlePacket(const UDPpacket &udp_pack) {
        client.Received(udp_pack);
 
        switch (pack.header.type) {
-               case Packet::LOGIN:
+               case Packet::Login::TYPE:
                        HandleLogin(client, udp_pack);
                        break;
-               case Packet::PART:
+               case Packet::Part::TYPE:
                        HandlePart(client, udp_pack);
                        break;
                default:
@@ -384,26 +431,24 @@ void Server::OnDisconnect(Connection &client) {
 
 
 void Server::HandleLogin(Connection &client, const UDPpacket &udp_pack) {
-       const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
-       size_t maxlen = min(udp_pack.len - int(sizeof(Packet::Header)), 32);
+       auto pack = Packet::As<Packet::Login>(udp_pack);
+
        string name;
-       name.reserve(maxlen);
-       for (size_t i = 0; i < maxlen && pack.payload[i] != '\0'; ++i) {
-               name.push_back(pack.payload[i]);
-       }
+       pack.ReadPlayerName(name);
 
        Entity *player = world.AddPlayer(name);
-       Packet &response = *reinterpret_cast<Packet *>(serv_pack.data);
 
        if (player) {
                // success!
                cout << "accepted login from player \"" << name << '"' << endl;
-               response.MakeJoin(*player, world.Name());
+               auto response = Packet::Make<Packet::Join>(serv_pack);
+               response.WritePlayer(*player);
+               response.WriteWorldName(world.Name());
                client.Send(serv_pack, serv_sock);
        } else {
                // aw no :(
                cout << "rejected login from player \"" << name << '"' << endl;
-               response.MakePart();
+               Packet::Make<Packet::Part>(serv_pack);
                client.Send(serv_pack, serv_sock);
                client.Close();
        }