From f10b6df9894645a74c9f7391d404669712337b27 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 12 Nov 2023 15:32:54 +0200 Subject: [PATCH] Implemented messages sending and storing in database --- client/serverLobby/LobbyWindow.cpp | 46 ++++++++++++++++++++++-- client/serverLobby/LobbyWindow.h | 18 ++++++++-- config/widgets/lobbyWindow.json | 26 ++++++++------ lib/network/NetworkClient.h | 2 +- lib/network/NetworkConnection.cpp | 2 +- lobby/LobbyServer.cpp | 58 +++++++++++++++++++++++++++++- lobby/LobbyServer.h | 17 ++++++++- lobby/SQLiteConnection.cpp | 3 +- 8 files changed, 152 insertions(+), 20 deletions(-) diff --git a/client/serverLobby/LobbyWindow.cpp b/client/serverLobby/LobbyWindow.cpp index ce7a41e67..7f670b58d 100644 --- a/client/serverLobby/LobbyWindow.cpp +++ b/client/serverLobby/LobbyWindow.cpp @@ -13,9 +13,16 @@ #include "../gui/CGuiHandler.h" #include "../gui/WindowHandler.h" +#include "../widgets/TextControls.h" #include "../windows/InfoWindows.h" +LobbyClient::LobbyClient(LobbyWindow * window) + : window(window) +{ + +} + void LobbyClient::onPacketReceived(const std::vector & message) { @@ -33,22 +40,42 @@ void LobbyClient::onDisconnected() CInfoWindow::showInfoDialog("Connection to game lobby was lost!", {}); } -LobbyWidget::LobbyWidget() +void LobbyClient::sendMessage(const JsonNode & data) +{ + std::string payloadString = data.toJson(true); + + // FIXME: find better approach + uint8_t * payloadBegin = reinterpret_cast(payloadString.data()); + uint8_t * payloadEnd = payloadBegin + payloadString.size(); + + std::vector payloadBuffer(payloadBegin, payloadEnd); + + sendPacket(payloadBuffer); +} + +LobbyWidget::LobbyWidget(LobbyWindow * window) + : window(window) { addCallback("closeWindow", [](int) { GH.windows().popWindows(1); }); + addCallback("sendMessage", [this](int) { this->window->doSendChatMessage(); }); const JsonNode config(JsonPath::builtin("config/widgets/lobbyWindow.json")); build(config); } +std::shared_ptr LobbyWidget::getMessageInput() +{ + return widget("messageInput"); +} + LobbyWindow::LobbyWindow(): CWindowObject(BORDERED) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - widget = std::make_shared(); + widget = std::make_shared(this); pos = widget->pos; center(); - connection = std::make_shared(); + connection = std::make_shared(this); connection->start("127.0.0.1", 30303); @@ -59,3 +86,16 @@ void LobbyWindow::tick(uint32_t msPassed) { connection->poll(); } + +void LobbyWindow::doSendChatMessage() +{ + std::string messageText = widget->getMessageInput()->getText(); + + JsonNode toSend; + toSend["type"].String() = "sendChatMessage"; + toSend["messageText"].String() = messageText; + + connection->sendMessage(toSend); + + widget->getMessageInput()->setText(""); +} diff --git a/client/serverLobby/LobbyWindow.h b/client/serverLobby/LobbyWindow.h index d13c1d1a6..0aaf96b6a 100644 --- a/client/serverLobby/LobbyWindow.h +++ b/client/serverLobby/LobbyWindow.h @@ -14,19 +14,29 @@ #include "../../lib/network/NetworkClient.h" +class LobbyWindow; + class LobbyWidget : public InterfaceObjectConfigurable { + LobbyWindow * window; public: - LobbyWidget(); + LobbyWidget(LobbyWindow * window); + + std::shared_ptr getMessageInput(); }; class LobbyClient : public NetworkClient { + LobbyWindow * window; + void onPacketReceived(const std::vector & message) override; void onConnectionFailed(const std::string & errorMessage) override; void onDisconnected() override; + public: - LobbyClient() = default; + explicit LobbyClient(LobbyWindow * window); + + void sendMessage(const JsonNode & data); }; class LobbyWindow : public CWindowObject @@ -38,4 +48,8 @@ class LobbyWindow : public CWindowObject public: LobbyWindow(); + + void doSendChatMessage(); + + void onGameChatMessage(std::string sender, std::string message, std::string when); }; diff --git a/config/widgets/lobbyWindow.json b/config/widgets/lobbyWindow.json index 672cf34dd..42dba15c9 100644 --- a/config/widgets/lobbyWindow.json +++ b/config/widgets/lobbyWindow.json @@ -85,15 +85,25 @@ "position": {"x": 440, "y": 53}, "text" : "Game Chat" }, + { + "type": "textBox", + "name": "gameChat", + "font": "small", + "alignment": "left", + "color": "white", + "text": "[00:00]{Player 1}: Hello\n[00:01]{Player 2}: HI!", + "rect": {"x": 430, "y": 70, "w": 430, "h": 495} + }, { "type": "areaFilled", "rect": {"x": 430, "y": 565, "w": 395, "h": 25} }, { - "type": "labelTitle", - "position": {"x": 440, "y": 568}, - "text" : "Enter Message" + "name" : "messageInput", + "type": "textInput", + "alignment" : "left", + "rect": {"x": 440, "y": 568, "w": 375, "h": 20} }, { @@ -112,7 +122,7 @@ "image": "settingsWindow/button80", "help": "core.help.288", "callback": "closeWindow", - "hotkey": "globalReturn", + "hotkey": "globalCancel", "items": [ { @@ -130,8 +140,8 @@ "position": {"x": 828, "y": 565}, "image": "settingsWindow/button32", "help": "core.help.288", - "callback": "closeWindow", - "hotkey": "globalReturn", + "callback": "sendMessage", + "hotkey": "globalAccept", "items": [ { @@ -149,8 +159,6 @@ "position": {"x": 10, "y": 520}, "image": "settingsWindow/button190", "help": "core.help.288", - "callback": "closeWindow", - "hotkey": "globalReturn", "items": [ { @@ -168,8 +176,6 @@ "position": {"x": 10, "y": 555}, "image": "settingsWindow/button190", "help": "core.help.288", - "callback": "closeWindow", - "hotkey": "globalReturn", "items": [ { diff --git a/lib/network/NetworkClient.h b/lib/network/NetworkClient.h index 5cc87a1ec..60ee493f3 100644 --- a/lib/network/NetworkClient.h +++ b/lib/network/NetworkClient.h @@ -32,12 +32,12 @@ protected: virtual void onConnectionFailed(const std::string & errorMessage) = 0; virtual void onDisconnected() = 0; + void sendPacket(const std::vector & message); public: NetworkClient(); virtual ~NetworkClient() = default; void start(const std::string & host, uint16_t port); - void sendPacket(const std::vector & message); void run(); void poll(); }; diff --git a/lib/network/NetworkConnection.cpp b/lib/network/NetworkConnection.cpp index 4c0a26471..5cb567f94 100644 --- a/lib/network/NetworkConnection.cpp +++ b/lib/network/NetworkConnection.cpp @@ -77,7 +77,7 @@ void NetworkConnection::onPacketReceived(const boost::system::error_code & ec, u message.resize(expectedPacketSize); std::istream istream(&readBuffer); - istream.read(reinterpret_cast(message.data()), messageHeaderSize); + istream.read(reinterpret_cast(message.data()), expectedPacketSize); listener.onPacketReceived(shared_from_this(), message); diff --git a/lobby/LobbyServer.cpp b/lobby/LobbyServer.cpp index cdbc20a58..3aae7ba45 100644 --- a/lobby/LobbyServer.cpp +++ b/lobby/LobbyServer.cpp @@ -12,6 +12,8 @@ #include "SQLiteConnection.h" +#include "../lib/JsonNode.h" + #include #include @@ -20,6 +22,53 @@ static const int LISTENING_PORT = 30303; //static const std::string SERVER_NAME = GameConstants::VCMI_VERSION + " (server)"; //static const std::string SERVER_UUID = boost::uuids::to_string(boost::uuids::random_generator()()); +void LobbyDatabase::prepareStatements() +{ + static const std::string insertChatMessageText = R"( + INSERT INTO chatMessages(senderName, messageText) VALUES( ?, ?); + )"; + + insertChatMessageStatement = database->prepare(insertChatMessageText); +} + +void LobbyDatabase::createTableChatMessages() +{ + static const std::string statementText = R"( + CREATE TABLE IF NOT EXISTS chatMessages ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + senderName TEXT, + messageText TEXT, + sendTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL + ); + )"; + + auto statement = database->prepare(statementText); + statement->execute(); +} + +void LobbyDatabase::initializeDatabase() +{ + createTableChatMessages(); +} + +LobbyDatabase::LobbyDatabase() +{ + database = SQLiteInstance::open(DATABASE_PATH, true); + + if (!database) + throw std::runtime_error("Failed to open SQLite database!"); + + initializeDatabase(); + prepareStatements(); +} + +void LobbyDatabase::insertChatMessage(const std::string & sender, const std::string & messageText) +{ + insertChatMessageStatement->setBinds(sender, messageText); + insertChatMessageStatement->execute(); + insertChatMessageStatement->reset(); +} + void LobbyServer::onNewConnection(const std::shared_ptr &) { @@ -27,12 +76,19 @@ void LobbyServer::onNewConnection(const std::shared_ptr &) void LobbyServer::onPacketReceived(const std::shared_ptr &, const std::vector & message) { + // FIXME: find better approach + const char * payloadBegin = reinterpret_cast(message.data()); + JsonNode json(payloadBegin, message.size()); + if (json["type"].String() == "sendChatMessage") + { + database->insertChatMessage("Unknown", json["messageText"].String()); + } } LobbyServer::LobbyServer() + : database(new LobbyDatabase()) { - database = SQLiteInstance::open(DATABASE_PATH, true); } int main(int argc, const char * argv[]) diff --git a/lobby/LobbyServer.h b/lobby/LobbyServer.h index 329d831b5..f7114e6c4 100644 --- a/lobby/LobbyServer.h +++ b/lobby/LobbyServer.h @@ -12,10 +12,25 @@ #include "../lib/network/NetworkServer.h" class SQLiteInstance; +class SQLiteStatement; + +class LobbyDatabase +{ + std::unique_ptr database; + std::unique_ptr insertChatMessageStatement; + + void initializeDatabase(); + void prepareStatements(); + void createTableChatMessages(); +public: + LobbyDatabase(); + + void insertChatMessage(const std::string & sender, const std::string & messageText); +}; class LobbyServer : public NetworkServer { - std::unique_ptr database; + std::unique_ptr database; void onNewConnection(const std::shared_ptr &) override; void onPacketReceived(const std::shared_ptr &, const std::vector & message) override; diff --git a/lobby/SQLiteConnection.cpp b/lobby/SQLiteConnection.cpp index cac16ccb2..16cffb950 100644 --- a/lobby/SQLiteConnection.cpp +++ b/lobby/SQLiteConnection.cpp @@ -16,7 +16,8 @@ static void on_sqlite_error( sqlite3 * connection, [[maybe_unused]] int result ) { if ( result != SQLITE_OK ) { - printf( "sqlite error: %s\n", sqlite3_errmsg( connection ) ); + const char * message = sqlite3_errmsg( connection ); + printf( "sqlite error: %s\n", message ); } assert( result == SQLITE_OK );