diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 3516ffe03..aa3fd698b 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -249,6 +249,7 @@ ServerLobby::ServerLobby() : LobbyProtocol() m_game_mode.store(ServerConfig::m_server_mode); m_default_vote = new PeerVote(); m_player_reports_table_exists = false; + m_grand_prix_rowid = -1; initDatabase(); } // ServerLobby @@ -389,6 +390,51 @@ void ServerLobby::initServerStatsTable() ") WITHOUT ROWID;", country_table_name.c_str()); easySQLQuery(query); + // Extra default table _grandprixresults: + std::string grandprixresults_table_name = std::string("v") + + StringUtils::toString(ServerConfig::m_server_db_version) + "_" + + ServerConfig::m_server_uid + "_grandprixresults"; + query = StringUtils::insertValues( + "CREATE TABLE IF NOT EXISTS %s (\n" + " num_races INTEGER NOT NULL, -- Number of races completed\n" + " total_races INTEGER NOT NULL -- Total number of races\n" + ");", grandprixresults_table_name.c_str()); + if(easySQLQuery(query)) { + m_server_grandprixresults_table = grandprixresults_table_name; + } + + // Extra default table _raceresults: + std::string raceresults_table_name = std::string("v") + + StringUtils::toString(ServerConfig::m_server_db_version) + "_" + + ServerConfig::m_server_uid + "_raceresults"; + query = StringUtils::insertValues( + "CREATE TABLE IF NOT EXISTS %s (\n" + " race_finished TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Time when race was completed\n" + " grandprix_rowid INTEGER, -- ROWID of the grand prix this race is part of (optional)\n" + " track_name TEXT NOT NULL, -- Number of laps completed\n" + " num_laps INTEGER NOT NULL, -- Number of laps completed\n" + " total_laps INTEGER NOT NULL, -- Total number of laps\n" + " fastest_lap_time FLOAT NOT NULL, -- Fastest lap so far\n" + " fastest_lap_player TEXT NOT NULL -- Fastest lap so far\n" + ");", raceresults_table_name.c_str()); + if(easySQLQuery(query)) { + m_server_raceresults_table = raceresults_table_name; + } + + // Extra default table _raceresults: + std::string playerresults_table_name = std::string("v") + + StringUtils::toString(ServerConfig::m_server_db_version) + "_" + + ServerConfig::m_server_uid + "_playerresults"; + query = StringUtils::insertValues( + "CREATE TABLE IF NOT EXISTS %s (\n" + " race_rowid INTEGER NOT NULL, -- ROWID of the race\n" + " elapsed_time FLOAT NOT NULL, -- Total race time\n" + " player_name TEXT NOT NULL -- Player name\n" + ");", playerresults_table_name.c_str()); + if(easySQLQuery(query)) { + m_server_playerresults_table = playerresults_table_name; + } + // Default views: // _full_stats // Full stats with ip in human readable format and time played of each @@ -2884,6 +2930,7 @@ void ServerLobby::checkRaceFinished() RaceEventManager::get()->getProtocol()->requestTerminate(); GameProtocol::lock()->requestTerminate(); + // Save race result before delete the world m_result_ns->clear(); m_result_ns->addUInt8(LE_RACE_FINISHED); @@ -2895,10 +2942,13 @@ void ServerLobby::checkRaceFinished() m_result_ns->addUInt32(fastest_lap); m_result_ns->encodeString(static_cast(World::getWorld()) ->getFastestLapKartName()); + std::cout << static_cast(World::getWorld())->getFastestLapKartName().c_str() << std::endl; + Log::info("RaceResults", "Fastest lap by %s", StringUtils::wideToUtf8(static_cast(World::getWorld())->getFastestLapKartName()).c_str()); // all gp tracks m_result_ns->addUInt8((uint8_t)m_game_setup->getTotalGrandPrixTracks()) .addUInt8((uint8_t)m_game_setup->getAllTracks().size()); + Log::info("RaceResults", "Track number %d / %d", m_game_setup->getAllTracks().size(), m_game_setup->getTotalGrandPrixTracks()); for (const std::string& gp_track : m_game_setup->getAllTracks()) m_result_ns->encodeString(gp_track); @@ -2917,9 +2967,13 @@ void ServerLobby::checkRaceFinished() overall_time = overall_time + player->getOverallTime(); player->setScore(cur_score); player->setOverallTime(overall_time); + Log::info("RaceResults", "Score for %s from %d to %d (%f)", StringUtils::wideToUtf8(player->getName()).c_str(), last_score, cur_score, overall_time); + } + else { + Log::info("RaceResults", "Score for player %d from %d to %d (%f)", i, last_score, cur_score, overall_time); } m_result_ns->addUInt32(last_score).addUInt32(cur_score) - .addFloat(overall_time); + .addFloat(overall_time); } } else if (RaceManager::get()->modeHasLaps()) @@ -2936,6 +2990,8 @@ void ServerLobby::checkRaceFinished() ranking_changes_indication = 1; m_result_ns->addUInt8(ranking_changes_indication); + writeRaceResults(); + if (ServerConfig::m_ranked) { computeNewRankings(); @@ -2944,6 +3000,148 @@ void ServerLobby::checkRaceFinished() m_state.store(WAIT_FOR_RACE_STOPPED); } // checkRaceFinished +int ServerLobby::getLastRowID(const std::string &table) { + int rowid = -1; + std::string query = StringUtils::insertValues("SELECT ROWID from %s ORDER BY ROWID DESC LIMIT 1;", table); + + 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); + if (ret == SQLITE_ROW) + { + rowid = sqlite3_column_int(stmt, 0); + } + ret = sqlite3_finalize(stmt); + if (ret != SQLITE_OK) + { + Log::error("ServerLobby", + "Error rowid database for query %s: %s", + query.c_str(), sqlite3_errmsg(m_db)); + } + } + else + { + Log::error("ServerLobby", "Error preparing database for query %s: %s", + query.c_str(), sqlite3_errmsg(m_db)); + return -1; + } + return rowid; + +} + +//----------------------------------------------------------------------------- +/** Write the results of the race + */ +void ServerLobby::writeRaceResults() +{ +#ifdef ENABLE_SQLITE3 + std::string track_name = RaceManager::get()->getTrackName(); + bool track_reverse = RaceManager::get()->getReverseTrack(); + int player_count = RaceManager::get()->getNumPlayers(); + int laps_number = RaceManager::get()->getNumLaps(); + + if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE) { + std::string query; + if (m_game_setup->isGrandPrix()) { + int race_number = m_game_setup->getAllTracks().size(); + int total_races = m_game_setup->getTotalGrandPrixTracks(); + if(race_number == 1) { + query = StringUtils::insertValues( + "INSERT INTO %s " + "(num_races, total_races) " + "VALUES (%d, %d);", + m_server_grandprixresults_table, + race_number, + total_races + ); + if(easySQLQuery(query)) { + m_grand_prix_rowid = getLastRowID(m_server_grandprixresults_table); + } + } + else { + query = StringUtils::insertValues( + "UPDATE %s " + "SET num_races = %d " + "WHERE ROWID = %d;", + m_server_grandprixresults_table, + race_number, + m_grand_prix_rowid + ); + easySQLQuery(query); + } + } + + if(m_grand_prix_rowid >= 0) { + query = StringUtils::insertValues( + "INSERT INTO %s " + "(grandprix_rowid, track_name, num_laps, total_laps, fastest_lap_time, fastest_lap_player) " + "VALUES (%d, \"%s\", %d, %d, %f, \"%s\");", + m_server_raceresults_table, + m_grand_prix_rowid, + track_name, + laps_number, + 0, + 0., + "" + ); + } + else { + query = StringUtils::insertValues( + "INSERT INTO %s " + "(track_name, num_laps, total_laps, fastest_lap_time, fastest_lap_player) " + "VALUES (\"%s\", %d, %d, %f, \"%s\");", + m_server_raceresults_table, + track_name, + laps_number, + 0, + 0., + "" + ); + } + if(easySQLQuery(query)) { + int race_rowid = getLastRowID(m_server_raceresults_table); + Log::info("RaceResults", "Row ID: %d", race_rowid); + if(race_rowid >= 0) { + for (int i = 0; i < player_count; i++) { + double elapsed_time = RaceManager::get()->getKartRaceTime(i); + std::string player_name = StringUtils::wideToUtf8( + RaceManager::get()->getKartInfo(i).getPlayerName()); + //int grand_prix_rank = RaceManager::get()->getKartGPRank(i); + query = StringUtils::insertValues( + "INSERT INTO %s " + "(race_rowid, elapsed_time, player_name) " + "VALUES (%d, %f, ?);", + m_server_playerresults_table, + race_rowid, + elapsed_time + ); + easySQLQuery(query, [player_name](sqlite3_stmt* stmt) { + if (sqlite3_bind_text(stmt, 1, player_name.c_str(), + -1, SQLITE_TRANSIENT) != SQLITE_OK) + { + Log::error("easySQLQuery", "Failed to bind %s.", + player_name.c_str()); + } + }); + } + } + } + + if(m_game_setup->isGrandPrix()) { + int race_number = m_game_setup->getAllTracks().size(); + int total_races = m_game_setup->getTotalGrandPrixTracks(); + + if(race_number == total_races) { + m_grand_prix_rowid = -1; + } + } + } +#endif +} + + //----------------------------------------------------------------------------- /** Compute the new player's rankings used in ranked servers */ diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 53d3aceda..780ef2d4e 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -85,6 +85,14 @@ private: std::string m_server_stats_table; + std::string m_server_grandprixresults_table; + + std::string m_server_raceresults_table; + + std::string m_server_playerresults_table; + + int m_grand_prix_rowid; + bool m_ip_ban_table_exists; bool m_ipv6_ban_table_exists; @@ -377,6 +385,8 @@ private: void testBannedForOnlineId(STKPeer* peer, uint32_t online_id) const; void writeDisconnectInfoTable(STKPeer* peer); void writePlayerReport(Event* event); + int getLastRowID(const std::string &table); + void writeRaceResults(); bool supportsAI(); void updateAddons(); public: