瀏覽代碼

Implemented loading of latest messages on joining chat

Ivan Savenko 1 年之前
父節點
當前提交
07fb313765
共有 5 個文件被更改,包括 123 次插入4 次删除
  1. 47 2
      client/serverLobby/LobbyWindow.cpp
  2. 4 1
      client/serverLobby/LobbyWindow.h
  3. 1 0
      lib/network/NetworkServer.cpp
  4. 56 1
      lobby/LobbyServer.cpp
  5. 15 0
      lobby/LobbyServer.h

+ 47 - 2
client/serverLobby/LobbyWindow.cpp

@@ -14,9 +14,10 @@
 #include "../gui/CGuiHandler.h"
 #include "../gui/WindowHandler.h"
 #include "../widgets/TextControls.h"
-
 #include "../windows/InfoWindows.h"
 
+#include "../../lib/MetaString.h"
+
 LobbyClient::LobbyClient(LobbyWindow * window)
 	: window(window)
 {
@@ -25,7 +26,33 @@ LobbyClient::LobbyClient(LobbyWindow * window)
 
 void LobbyClient::onPacketReceived(const std::vector<uint8_t> & message)
 {
-
+	// FIXME: find better approach
+	const char * payloadBegin = reinterpret_cast<const char*>(message.data());
+	JsonNode json(payloadBegin, message.size());
+
+	if (json["type"].String() == "chatHistory")
+	{
+		for (auto const & entry : json["messages"].Vector())
+		{
+			std::string senderName = entry["senderName"].String();
+			std::string messageText = entry["messageText"].String();
+			int ageSeconds = entry["ageSeconds"].Integer();
+
+			// FIXME: better/unified way to format date
+			auto timeNowChrono = std::chrono::system_clock::now();
+			timeNowChrono -= std::chrono::seconds(ageSeconds);
+
+			std::time_t timeNowC = std::chrono::system_clock::to_time_t(timeNowChrono);
+			std::tm timeNowTm = *std::localtime(&timeNowC);
+
+			MetaString dateFormatted;
+			dateFormatted.appendRawString("%d:%d");
+			dateFormatted.replaceNumber(timeNowTm.tm_hour);
+			dateFormatted.replaceNumber(timeNowTm.tm_min);
+
+			window->onGameChatMessage(senderName, messageText, dateFormatted.toString());
+		}
+	}
 }
 
 void LobbyClient::onConnectionFailed(const std::string & errorMessage)
@@ -68,6 +95,11 @@ std::shared_ptr<CTextInput> LobbyWidget::getMessageInput()
 	return widget<CTextInput>("messageInput");
 }
 
+std::shared_ptr<CTextBox> LobbyWidget::getGameChat()
+{
+	return widget<CTextBox>("gameChat");
+}
+
 LobbyWindow::LobbyWindow():
 	CWindowObject(BORDERED)
 {
@@ -99,3 +131,16 @@ void LobbyWindow::doSendChatMessage()
 
 	widget->getMessageInput()->setText("");
 }
+
+void LobbyWindow::onGameChatMessage(const std::string & sender, const std::string & message, const std::string & when)
+{
+	MetaString chatMessageFormatted;
+	chatMessageFormatted.appendRawString("[%s] {%s}: %s\n");
+	chatMessageFormatted.replaceRawString(when);
+	chatMessageFormatted.replaceRawString(sender);
+	chatMessageFormatted.replaceRawString(message);
+
+	chatHistory += chatMessageFormatted.toString();
+
+	widget->getGameChat()->setText(chatHistory);
+}

+ 4 - 1
client/serverLobby/LobbyWindow.h

@@ -23,6 +23,7 @@ public:
 	LobbyWidget(LobbyWindow * window);
 
 	std::shared_ptr<CTextInput> getMessageInput();
+	std::shared_ptr<CTextBox> getGameChat();
 };
 
 class LobbyClient : public NetworkClient
@@ -41,6 +42,8 @@ public:
 
 class LobbyWindow : public CWindowObject
 {
+	std::string chatHistory;
+
 	std::shared_ptr<LobbyWidget> widget;
 	std::shared_ptr<LobbyClient> connection;
 
@@ -51,5 +54,5 @@ public:
 
 	void doSendChatMessage();
 
-	void onGameChatMessage(std::string sender, std::string message, std::string when);
+	void onGameChatMessage(const std::string & sender, const std::string & message, const std::string & when);
 };

+ 1 - 0
lib/network/NetworkServer.cpp

@@ -43,6 +43,7 @@ void NetworkServer::connectionAccepted(std::shared_ptr<NetworkSocket> upcomingCo
 	auto connection = std::make_shared<NetworkConnection>(upcomingConnection, *this);
 	connections.insert(connection);
 	connection->start();
+	onNewConnection(connection);
 	startAsyncAccept();
 }
 

+ 56 - 1
lobby/LobbyServer.cpp

@@ -28,7 +28,16 @@ void LobbyDatabase::prepareStatements()
 		INSERT INTO chatMessages(senderName, messageText) VALUES( ?, ?);
 	)";
 
+	static const std::string getRecentMessageHistoryText = R"(
+		SELECT senderName, messageText, strftime('%s',CURRENT_TIMESTAMP)- strftime('%s',sendTime)  AS secondsElapsed
+		FROM chatMessages
+		WHERE secondsElapsed < 60*60*24
+		ORDER BY sendTime DESC
+		LIMIT 100
+	)";
+
 	insertChatMessageStatement = database->prepare(insertChatMessageText);
+	getRecentMessageHistoryStatement = database->prepare(getRecentMessageHistoryText);
 }
 
 void LobbyDatabase::createTableChatMessages()
@@ -69,9 +78,55 @@ void LobbyDatabase::insertChatMessage(const std::string & sender, const std::str
 	insertChatMessageStatement->reset();
 }
 
-void LobbyServer::onNewConnection(const std::shared_ptr<NetworkConnection> &)
+std::vector<LobbyDatabase::ChatMessage> LobbyDatabase::getRecentMessageHistory()
+{
+	std::vector<LobbyDatabase::ChatMessage> result;
+
+	while(getRecentMessageHistoryStatement->execute())
+	{
+		LobbyDatabase::ChatMessage message;
+		getRecentMessageHistoryStatement->getColumns(message.sender, message.messageText, message.messageAgeSeconds);
+		result.push_back(message);
+	}
+	getRecentMessageHistoryStatement->reset();
+
+	return result;
+}
+
+void LobbyServer::sendMessage(const std::shared_ptr<NetworkConnection> & target, const JsonNode & json)
+{
+	//FIXME: copy-paste from LobbyClient::sendMessage
+	std::string payloadString = json.toJson(true);
+
+	// FIXME: find better approach
+	uint8_t * payloadBegin = reinterpret_cast<uint8_t*>(payloadString.data());
+	uint8_t * payloadEnd = payloadBegin + payloadString.size();
+
+	std::vector<uint8_t> payloadBuffer(payloadBegin, payloadEnd);
+
+	sendPacket(target, payloadBuffer);
+}
+
+void LobbyServer::onNewConnection(const std::shared_ptr<NetworkConnection> & connection)
 {
+	// FIXME: move to authorization message reply
+	auto history = database->getRecentMessageHistory();
+
+	JsonNode json;
+	json["type"].String() = "chatHistory";
+
+	for (auto const & message : boost::adaptors::reverse(history))
+	{
+		JsonNode jsonEntry;
+
+		jsonEntry["messageText"].String() = message.messageText;
+		jsonEntry["senderName"].String() = message.sender;
+		jsonEntry["ageSeconds"].Integer() = message.messageAgeSeconds;
+
+		json["messages"].Vector().push_back(jsonEntry);
+	}
 
+	sendMessage(connection, json);
 }
 
 void LobbyServer::onPacketReceived(const std::shared_ptr<NetworkConnection> &, const std::vector<uint8_t> & message)

+ 15 - 0
lobby/LobbyServer.h

@@ -11,6 +11,10 @@
 
 #include "../lib/network/NetworkServer.h"
 
+VCMI_LIB_NAMESPACE_BEGIN
+class JsonNode;
+VCMI_LIB_NAMESPACE_END
+
 class SQLiteInstance;
 class SQLiteStatement;
 
@@ -18,14 +22,23 @@ class LobbyDatabase
 {
 	std::unique_ptr<SQLiteInstance> database;
 	std::unique_ptr<SQLiteStatement> insertChatMessageStatement;
+	std::unique_ptr<SQLiteStatement> getRecentMessageHistoryStatement;
 
 	void initializeDatabase();
 	void prepareStatements();
 	void createTableChatMessages();
 public:
+	struct ChatMessage
+	{
+		std::string sender;
+		std::string messageText;
+		int messageAgeSeconds;
+	};
+
 	LobbyDatabase();
 
 	void insertChatMessage(const std::string & sender, const std::string & messageText);
+	std::vector<ChatMessage> getRecentMessageHistory();
 };
 
 class LobbyServer : public NetworkServer
@@ -34,6 +47,8 @@ class LobbyServer : public NetworkServer
 
 	void onNewConnection(const std::shared_ptr<NetworkConnection> &) override;
 	void onPacketReceived(const std::shared_ptr<NetworkConnection> &, const std::vector<uint8_t> & message) override;
+
+	void sendMessage(const std::shared_ptr<NetworkConnection> & target, const JsonNode & json);
 public:
 	LobbyServer();
 };