changeset 154:614ac46dac3e

Client: add basic network support
author David Demelier <markand@malikania.fr>
date Wed, 13 Dec 2017 13:48:56 +0100
parents 43a26de57fe7
children f67c187bfceb
files libclient/CMakeLists.txt libclient/malikania/client/client.cpp libclient/malikania/client/client.hpp libclient/malikania/client/dispatcher.hpp libclient/malikania/client/state/login_state.cpp libclient/malikania/client/state/login_state.hpp server/CMakeLists.txt server/main.cpp
diffstat 8 files changed, 195 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/libclient/CMakeLists.txt	Wed Dec 13 13:46:51 2017 +0100
+++ b/libclient/CMakeLists.txt	Wed Dec 13 13:48:56 2017 +0100
@@ -43,7 +43,6 @@
     ${libmlk-client_SOURCE_DIR}/malikania/client/state/map_state.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/state/login_state.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/state/lobby_state.hpp
-    ${libmlk-client_SOURCE_DIR}/malikania/client/state/splashscreen_state.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/theme.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/unique_layout.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/widget.hpp
@@ -69,7 +68,6 @@
     ${libmlk-client_SOURCE_DIR}/malikania/client/state/map_state.cpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/state/login_state.cpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/state/lobby_state.cpp
-    ${libmlk-client_SOURCE_DIR}/malikania/client/state/splashscreen_state.cpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/theme.cpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/unique_layout.cpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/widget.cpp
--- a/libclient/malikania/client/client.cpp	Wed Dec 13 13:46:51 2017 +0100
+++ b/libclient/malikania/client/client.cpp	Wed Dec 13 13:48:56 2017 +0100
@@ -17,6 +17,7 @@
  */
 
 #include <iostream>
+#include <thread>
 
 #include "client.hpp"
 #include "connection.hpp"
@@ -27,18 +28,52 @@
 
 namespace client {
 
-client::client(boost::asio::io_service&,
+client::client(boost::asio::io_service& service,
                mlk::client::connection& connection,
                mlk::client::window& window) noexcept
-    : connection_(connection)
+    : service_(service)
+    , connection_(connection)
     , window_(window)
 {
 }
 
 client::~client() noexcept = default;
 
-void client::connect(std::string, std::uint16_t)
+void client::recv()
+{
+    // Network thread space.
+    connection_.recv([this] (auto code, auto message) {
+        if (!code)
+            recv();
+
+        std::lock_guard<std::mutex> lock(nmutex_);
+
+        nqueue_.push_back(std::bind(&client::handle_message, this, code, message));
+    });
+}
+
+void client::connect(std::string host, std::uint16_t port)
 {
+    service_.post([this, host, port] () {
+        connection_.connect(host, port, [this] (auto code) {
+            // Start receiveing on success.
+            if (!code)
+                recv();
+
+            // Push the result to the main loop queue.
+            std::lock_guard<std::mutex> lock(nmutex_);
+
+            nqueue_.push_back(std::bind(&client::handle_connect, this, code));
+        });
+    });
+}
+
+void client::send(nlohmann::json message)
+{
+    service_.post([this, message] () {
+        // TODO: error checking.
+        connection_.send(std::move(message), nullptr);
+    });
 }
 
 void client::set_state(std::unique_ptr<state> state)
@@ -46,6 +81,18 @@
     state_next_ = std::move(state);
 }
 
+void client::handle_connect(const boost::system::error_code& code)
+{
+    if (state_)
+        state_->handle_connect(code);
+}
+
+void client::handle_message(const boost::system::error_code& code, const nlohmann::json& msg)
+{
+    if (state_)
+        state_->handle_message(code, msg);
+}
+
 void client::handle_key_down(const key_event& ev)
 {
     window_.handle_key_down(ev);
@@ -97,25 +144,43 @@
 void client::handle_quit()
 {
     window_.handle_quit();
+    service_.stop();
 }
 
 void client::run()
 {
-    while (window_.is_open())
-        run_one();
-}
+    std::thread t([this] () {
+        // TODO: change to an atomic member variable.
+        while (window_.is_open()) {
+            service_.reset();
+            service_.run();
+        }
+    });
+
+    while (window_.is_open()) {
+        // Network events.
+        {
+            std::lock_guard<std::mutex> lock(nmutex_);
+
+            for (auto& ev : nqueue_)
+                ev();
 
-void client::run_one()
-{
-    if (state_next_)
-        state_ = std::move(state_next_);
+            nqueue_.clear();
+        }
+
+        // State and input events.
+        if (state_next_)
+            state_ = std::move(state_next_);
 
-    window_.poll(*this);
+        window_.poll(*this);
 
-    if (state_) {
-        state_->update(*this);
-        state_->draw(*this);
+        if (state_) {
+            state_->update(*this);
+            state_->draw(*this);
+        }
     }
+
+    t.join();
 }
 
 } // !client
--- a/libclient/malikania/client/client.hpp	Wed Dec 13 13:46:51 2017 +0100
+++ b/libclient/malikania/client/client.hpp	Wed Dec 13 13:48:56 2017 +0100
@@ -24,7 +24,10 @@
  * \brief Main client game class.
  */
 
+#include <functional>
 #include <memory>
+#include <mutex>
+#include <vector>
 
 #include <boost/asio.hpp>
 
@@ -45,10 +48,17 @@
  */
 class client : public dispatcher {
 private:
+    boost::asio::io_service& service_;
+
     mlk::client::connection& connection_;
     mlk::client::window& window_;
+
     std::unique_ptr<state> state_;
     std::unique_ptr<state> state_next_;
+    std::mutex nmutex_;
+    std::vector<std::function<void ()>> nqueue_;
+
+    void recv();
 
 public:
     /**
@@ -67,6 +77,8 @@
      */
     virtual ~client() noexcept;
 
+#if 0
+
     /**
      * Get the connection object.
      *
@@ -86,6 +98,7 @@
     {
         return connection_;
     }
+#endif
 
     /**
      * Get the window.
@@ -118,6 +131,13 @@
     void connect(std::string host, std::uint16_t port);
 
     /**
+     * Send a message.
+     *
+     * \param message the message
+     */
+    void send(nlohmann::json message);
+
+    /**
      * Set the client state.
      *
      * \param state the state
@@ -130,9 +150,14 @@
     void run();
 
     /**
-     * Same as run except that it does not block.
+     * \copydoc dispatcher::handle_connect
      */
-    void run_one();
+    void handle_connect(const boost::system::error_code& code) override;
+
+    /**
+     * \copydoc dispatcher::handle_message
+     */
+    void handle_message(const boost::system::error_code& code, const nlohmann::json& msg) override;
 
     /**
      * \copydoc dispatcher::handle_key_down
--- a/libclient/malikania/client/dispatcher.hpp	Wed Dec 13 13:46:51 2017 +0100
+++ b/libclient/malikania/client/dispatcher.hpp	Wed Dec 13 13:48:56 2017 +0100
@@ -24,6 +24,10 @@
  * \brief Client event dispatcher.
  */
 
+#include <boost/system/error_code.hpp>
+
+#include <json.hpp>
+
 #include "mouse.hpp"
 #include "key.hpp"
 #include "point.hpp"
@@ -91,6 +95,28 @@
     virtual ~dispatcher() noexcept = default;
 
     /**
+     * Connection event.
+     *
+     * \param code the result code
+     */
+    virtual void handle_connect(const boost::system::error_code& code)
+    {
+        (void)code;
+    }
+
+    /**
+     * Message event.
+     *
+     * \param code the result code
+     * \param msg the network message
+     */
+    virtual void handle_message(const boost::system::error_code& code, const nlohmann::json& msg)
+    {
+        (void)code;
+        (void)msg;
+    }
+
+    /**
      * Key down event.
      *
      * \param ev the event
--- a/libclient/malikania/client/state/login_state.cpp	Wed Dec 13 13:46:51 2017 +0100
+++ b/libclient/malikania/client/state/login_state.cpp	Wed Dec 13 13:48:56 2017 +0100
@@ -28,6 +28,24 @@
 
 namespace client {
 
+void login_state::do_connect()
+{
+    state_ = state_t::connecting;
+    status_ = "Connecting...";
+    client_.connect("localhost", 3320);
+}
+
+void login_state::do_auth()
+{
+    status_ = "Authenticating";
+    state_ = state_t::authenticating;
+    client_.send({
+        { "command",    "auth"                      },
+        { "login",      unicode::to_utf8(login_)    },
+        { "password",   unicode::to_utf8(password_) }
+    });
+}
+
 login_state::login_state(client& clt)
     : client_(clt)
 {
@@ -46,6 +64,22 @@
 {
 }
 
+void login_state::handle_connect(const boost::system::error_code& code)
+{
+    if (code)
+        status_ = code.message();
+    else
+        do_auth();
+}
+
+void login_state::handle_message(const boost::system::error_code& code, const nlohmann::json& msg)
+{
+    if (code)
+        status_ = code.message();
+    else
+        status_ = msg["error"];
+}
+
 void login_state::handle_key_down(const key_event& ev)
 {
     switch (ev.key) {
@@ -62,8 +96,8 @@
             password_.pop_back();
         break;
     case key::enter:
-        if (index_ == 1)
-            client_.set_state(std::make_unique<lobby_state>(client_));
+        if (index_ == 1 && state_ == state_t::disconnected)
+            do_connect();
         break;
     default:
         break;
@@ -86,6 +120,8 @@
         win.draw_text(unicode::to_utf8(login_), font, {70, 50});
     if (!password_.empty())
         win.draw_text(std::string(password_.length(), '*'), font, {70, 70});
+    if (!status_.empty())
+        win.draw_text(status_, font, {70, 90});
 
     win.present();
 }
--- a/libclient/malikania/client/state/login_state.hpp	Wed Dec 13 13:46:51 2017 +0100
+++ b/libclient/malikania/client/state/login_state.hpp	Wed Dec 13 13:48:56 2017 +0100
@@ -24,7 +24,7 @@
  * \brief Login state.
  */
 
-#include "state.hpp"
+#include <malikania/client/state.hpp>
 
 namespace mlk {
 
@@ -35,16 +35,29 @@
  */
 class login_state : public state {
 private:
+    enum class state_t {
+        disconnected,
+        connecting,
+        authenticating
+    };
+
     client& client_;
+    state_t state_{state_t::disconnected};
     std::size_t index_{0};
     std::u32string login_;
     std::u32string password_;
+    std::string status_;
+
+    void do_connect();
+    void do_auth();
 
 public:
     login_state(client& clt);
 
     void update(client&) override;
     void draw(client& clt) override;
+    void handle_connect(const boost::system::error_code& code) override;
+    void handle_message(const boost::system::error_code& code, const nlohmann::json& msg) override;
     void handle_key_down(const key_event& ev) override;
     void handle_text(const text_event& ev) override;
 };
--- a/server/CMakeLists.txt	Wed Dec 13 13:46:51 2017 +0100
+++ b/server/CMakeLists.txt	Wed Dec 13 13:48:56 2017 +0100
@@ -24,4 +24,5 @@
         ${mlk-server_SOURCE_DIR}/main.cpp
     LIBRARIES
         libmlk-server
+        libmlk-db-sqlite
 )
--- a/server/main.cpp	Wed Dec 13 13:46:51 2017 +0100
+++ b/server/main.cpp	Wed Dec 13 13:48:56 2017 +0100
@@ -16,35 +16,22 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <iostream>
+#include <cstdio>
 
-#include <malikania/server/db/dynlib_database.hpp>
+#include <malikania/server/db/sqlite_database.hpp>
+
 #include <malikania/server/server.hpp>
 
 int main()
 {
-#if 0
-    mlk::server::settings sv_params{
-        3320,
-        "/home/markand/null/server.crt",
-        "/home/markand/null/server.key",
-    };
+    std::remove("server.db");
 
-    mlk::server::dynlib_database db({
-        { "type", "sqlite" },
-        { "path", "/home/markand/kingdom.db" }
-    });
+    boost::asio::io_service io;
 
-    boost::asio::io_service service;
-
-    try {
-        mlk::server::server server(service, db, sv_params);
+    mlk::server::sqlite_database db("server.db");
+    mlk::server::settings settings(3320U, "server.crt", "server.key");
+    mlk::server::server server(io, db, settings);
 
-        for (;;) {
-            service.run();
-        }
-    } catch (const std::exception& ex) {
-        std::cerr << "fatal: " << ex.what() << std::endl;
-    }
-#endif
+    for (;;)
+        io.run();
 }