diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 29ddd1a61..391f108fc 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -252,15 +252,148 @@ void ServerLobby::initDatabase() #endif } // initDatabase +//----------------------------------------------------------------------------- +void ServerLobby::initServerStatsTable() +{ +#ifdef ENABLE_SQLITE3 + if (!ServerConfig::m_sql_management || !m_db) + return; + std::string table_name = std::string("v") + + StringUtils::toString(ServerConfig::m_server_db_version) + "_" + + ServerConfig::m_server_uid + "_stats"; + + std::string query = StringUtils::insertValues( + "CREATE TABLE IF NOT EXISTS %s (\n" + " host_id INTEGER UNSIGNED NOT NULL PRIMARY KEY, -- Unique host id in STKHost of each connection session for a STKPeer\n" + " ip INTEGER UNSIGNED NOT NULL, -- IP decimal of host\n" + " port INTEGER UNSIGNED NOT NULL, -- Port of host\n" + " online_id INTEGER UNSIGNED NOT NULL, -- Online if of the host (0 for offline account)\n" + " username TEXT NOT NULL, -- First player name in the host (if the host has splitscreen player)\n" + " player_num INEGER UNSIGNED NOT NULL, -- Number of player(s) from the host, more than 1 if it has splitscreen player\n" + " country_id TEXT NULL DEFAULT NULL, -- Country id of the host\n" + " version TEXT NOT NULL, -- SuperTuxKart version of the host (with OS info)\n" + " connected_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Time when connected\n" + " disconnected_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Time when disconnected (saved when disconnected)\n" + " ping INEGER UNSIGNED NOT NULL DEFAULT 0 -- Ping of the host\n" + ") WITHOUT ROWID;", table_name.c_str()); + sqlite3_stmt* stmt = NULL; + int ret = sqlite3_prepare_v2(m_db, query.c_str(), -1, &stmt, 0); + if (ret == SQLITE_OK) + { + ret = sqlite3_step(stmt); + ret = sqlite3_finalize(stmt); + if (ret == SQLITE_OK) + m_server_stats_table = table_name; + else + { + Log::error("ServerLobby", "Error finalize database: %s", + sqlite3_errmsg(m_db)); + } + } + else + { + Log::error("ServerLobby", "Error preparing database: %s", + sqlite3_errmsg(m_db)); + } + if (m_server_stats_table.empty()) + return; + + uint32_t last_host_id = 0; + query = StringUtils::insertValues("SELECT MAX(host_id) FROM %s;", + m_server_stats_table.c_str()); + ret = sqlite3_prepare_v2(m_db, query.c_str(), -1, &stmt, 0); + if (ret == SQLITE_OK) + { + ret = sqlite3_step(stmt); + if (ret == SQLITE_ROW && sqlite3_column_type(stmt, 0) != SQLITE_NULL) + { + last_host_id = (unsigned)sqlite3_column_int64(stmt, 0); + Log::info("ServerLobby", "%u was last server session max host id.", + last_host_id); + } + ret = sqlite3_finalize(stmt); + if (ret != SQLITE_OK) + { + Log::error("ServerLobby", "Error finalize database: %s", + sqlite3_errmsg(m_db)); + m_server_stats_table = ""; + } + } + else + { + Log::error("ServerLobby", "Error preparing database: %s", + sqlite3_errmsg(m_db)); + m_server_stats_table = ""; + } + STKHost::get()->setNextHostId(last_host_id); + + // Update disconnected time (if stk crashed it will not be written) + query = StringUtils::insertValues( + "UPDATE %s SET disconnected_time = datetime('now')" + "WHERE connected_time = disconnected_time;", + m_server_stats_table.c_str()); + ret = sqlite3_prepare_v2(m_db, query.c_str(), -1, &stmt, 0); + if (ret == SQLITE_OK) + { + ret = sqlite3_step(stmt); + ret = sqlite3_finalize(stmt); + if (ret != SQLITE_OK) + { + Log::error("ServerLobby", "Error finalize database: %s", + sqlite3_errmsg(m_db)); + } + } + else + { + Log::error("ServerLobby", "Error preparing database: %s", + sqlite3_errmsg(m_db)); + } +#endif +} // initServerStatsTable + //----------------------------------------------------------------------------- void ServerLobby::destroyDatabase() { #ifdef ENABLE_SQLITE3 + auto peers = STKHost::get()->getPeers(); + for (auto& peer : peers) + writeDisconnectInfoTable(peer.get()); if (m_db != NULL) sqlite3_close(m_db); #endif } // destroyDatabase +//----------------------------------------------------------------------------- +void ServerLobby::writeDisconnectInfoTable(STKPeer* peer) +{ +#ifdef ENABLE_SQLITE3 + if (m_server_stats_table.empty()) + return; + std::string query = StringUtils::insertValues( + "UPDATE %s SET disconnected_time = datetime('now'), ping = %d " + "WHERE host_id = %u;", m_server_stats_table.c_str(), + peer->getAveragePing(), peer->getHostId()); + + sqlite3_stmt* stmt = NULL; + int ret = sqlite3_prepare_v2(m_db, query.c_str(), -1, &stmt, 0); + if (ret == SQLITE_OK) + { + ret = sqlite3_step(stmt); + ret = sqlite3_finalize(stmt); + if (ret != SQLITE_OK) + { + Log::error("ServerLobby", "Error finalize database: %s", + sqlite3_errmsg(m_db)); + } + } + else + { + Log::error("ServerLobby", "Error preparing database: %s", + sqlite3_errmsg(m_db)); + } +#endif +} // writeDisconnectInfoTable + //----------------------------------------------------------------------------- /** Called whenever server is reset or game mode is changed. */ @@ -2151,6 +2284,8 @@ void ServerLobby::clientDisconnected(Event* event) }, msg); updatePlayerList(); delete msg; + + writeDisconnectInfoTable(event->getPeer()); } // clientDisconnected //----------------------------------------------------------------------------- @@ -2235,7 +2370,7 @@ void ServerLobby::connectionRequested(Event* event) NetworkString *message = getNetworkString(2); message->setSynchronous(true); message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_BUSY); - // send only to the peer that made the request and disconect it now + // send only to the peer that made the request and disconnect it now peer->sendPacket(message, true/*reliable*/, false/*encrypted*/); peer->reset(); delete message; @@ -2553,6 +2688,37 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr peer, getRankingForPlayer(peer->getPlayerProfiles()[0]); } } +#ifdef ENABLE_SQLITE3 + if (m_server_stats_table.empty()) + return; + std::string query = StringUtils::insertValues( + "INSERT INTO %s " + "(host_id, ip, port, online_id, username, player_num, version, ping) " + "VALUES (%u, %u, %u, %u, \"%s\", %u, \"%s\", %u);", + m_server_stats_table.c_str(), peer->getHostId(), + peer->getAddress().getIP(), peer->getAddress().getPort(), online_id, + StringUtils::wideToUtf8( + peer->getPlayerProfiles()[0]->getName()).c_str(), player_count, + peer->getUserVersion().c_str(), peer->getAveragePing()); + + sqlite3_stmt* stmt = NULL; + int ret = sqlite3_prepare_v2(m_db, query.c_str(), -1, &stmt, 0); + if (ret == SQLITE_OK) + { + ret = sqlite3_step(stmt); + ret = sqlite3_finalize(stmt); + if (ret != SQLITE_OK) + { + Log::error("ServerLobby", "Error finalize database: %s", + sqlite3_errmsg(m_db)); + } + } + else + { + Log::error("ServerLobby", "Error preparing database: %s", + sqlite3_errmsg(m_db)); + } +#endif } // handleUnencryptedConnection //----------------------------------------------------------------------------- diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 8ea79fcf7..345ed3b18 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -73,6 +73,8 @@ private: #ifdef ENABLE_SQLITE3 sqlite3* m_db; + std::string m_server_stats_table; + bool m_ip_ban_table_exists; bool m_online_id_ban_table_exists; @@ -315,6 +317,7 @@ private: void kickPlayerWithReason(STKPeer* peer, const char* reason) const; void testBannedForIP(STKPeer* peer) const; void testBannedForOnlineId(STKPeer* peer, uint32_t online_id) const; + void writeDisconnectInfoTable(STKPeer* peer); public: ServerLobby(); virtual ~ServerLobby(); @@ -344,6 +347,7 @@ public: void saveInitialItems(); void saveIPBanTable(const TransportAddress& addr); void listBanTable(); + void initServerStatsTable(); }; // class ServerLobby #endif // SERVER_LOBBY_HPP diff --git a/src/network/server_config.cpp b/src/network/server_config.cpp index 8ba52b05e..418996d72 100644 --- a/src/network/server_config.cpp +++ b/src/network/server_config.cpp @@ -46,6 +46,7 @@ namespace ServerConfig { // ============================================================================ std::string g_server_config_path; +std::string m_server_uid; // ============================================================================ FloatServerConfigParam::FloatServerConfigParam(float default_value, const char* param_name, @@ -121,6 +122,8 @@ void loadServerConfig(const std::string& path) g_server_config_path = file_manager->getFileSystem() ->getAbsolutePath(path.c_str()).c_str(); } + m_server_uid = StringUtils::removeExtension( + StringUtils::getBasename(g_server_config_path)); const XMLNode* root = file_manager->createXMLTree(g_server_config_path); loadServerConfigXML(root, default_config); } // loadServerConfig diff --git a/src/network/server_config.hpp b/src/network/server_config.hpp index d01c7c770..22d52a86a 100644 --- a/src/network/server_config.hpp +++ b/src/network/server_config.hpp @@ -349,6 +349,13 @@ namespace ServerConfig /** Server version, will be advanced if there are protocol changes. */ static const uint32_t m_server_version = 6; // ======================================================================== + /** Server database version, will be advanced if there are protocol + * changes. */ + static const uint32_t m_server_db_version = 1; + // ======================================================================== + /** Server uid, extracted from server_config.xml file with .xml removed. */ + extern std::string m_server_uid; + // ======================================================================== void loadServerConfig(const std::string& path = ""); // ------------------------------------------------------------------------ void loadServerConfigXML(const XMLNode* root, bool default_config = false); diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 40d6dd863..cf30ab36b 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -79,8 +79,11 @@ std::shared_ptr STKHost::create(SeparateProcess* p) std::shared_ptr lp; if (NetworkConfig::get()->isServer()) { - lp = LobbyProtocol::create(); + std::shared_ptr sl = + LobbyProtocol::create(); m_stk_host = new STKHost(true/*server*/); + sl->initServerStatsTable(); + lp = sl; } else { @@ -929,8 +932,9 @@ void STKHost::mainLoop() Event* stk_event = NULL; if (event.type == ENET_EVENT_TYPE_CONNECT) { + // ++m_next_unique_host_id for unique host id for database auto stk_peer = std::make_shared - (event.peer, this, m_next_unique_host_id++); + (event.peer, this, ++m_next_unique_host_id); std::unique_lock lock(m_peers_mutex); m_peers[event.peer] = stk_peer; lock.unlock(); diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index 567db630f..5afd044f3 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -315,6 +315,8 @@ public: return m_next_unique_host_id; } // ------------------------------------------------------------------------ + void setNextHostId(uint32_t id) { m_next_unique_host_id = id; } + // ------------------------------------------------------------------------ /** Returns the number of currently connected peers. */ unsigned int getPeerCount() const {