@@ -842,3 +842,107 @@ uint32_t DatabaseConnection::getBaseAccount(const std::set<uint32_t>& userIds, c
842842 Log::fatal () << " No base account found among user ids " << userIds << " ." ;
843843 throw std::runtime_error (" No base account. Cannot continue;" );
844844}
845+
846+ /* !
847+ */
848+ void DatabaseConnection::writePlayerRatings (
849+ gamemodes::GameMode gameMode,
850+ const Players &players,
851+ std::map<uint32_t , uint32_t > activeRanks,
852+ std::map<uint32_t , uint32_t > allTimeRanks)
853+ {
854+ try
855+ {
856+ // Check if expected columns exist.
857+ std::set<std::string> requiredColumns = {
858+ " user_id" , " ladder_id" , " rating" , " elo_rank" ,
859+ " alltime_rank" , " rated_games" , " active" , " created_at" , " updated_at"
860+ };
861+
862+ std::unique_ptr<sql::PreparedStatement> checkStmt (
863+ _connection->prepareStatement (R"SQL(
864+ SELECT COLUMN_NAME
865+ FROM information_schema.columns
866+ WHERE table_schema = DATABASE() AND table_name = 'user_ratings'
867+ )SQL" )
868+ );
869+
870+ std::unique_ptr<sql::ResultSet> columnResult (checkStmt->executeQuery ());
871+ std::set<std::string> actualColumns;
872+
873+ while (columnResult->next ())
874+ {
875+ actualColumns.insert (columnResult->getString (" COLUMN_NAME" ));
876+ }
877+
878+ for (const std::string &col : requiredColumns)
879+ {
880+ if (!actualColumns.contains (col))
881+ {
882+ Log::warning () << " Unable to write player ratings due to missing column '" << col << " ' in 'user_ratings'." ;
883+ return ;
884+ }
885+ }
886+
887+ // Clear table.
888+ std::unique_ptr<sql::PreparedStatement> truncateStmt (
889+ _connection->prepareStatement (" TRUNCATE TABLE user_ratings" )
890+ );
891+ truncateStmt->execute ();
892+ Log::info () << " Table 'user_ratings' truncated." ;
893+
894+ // Get ladder id.
895+ uint32_t ladderId = 0 ;
896+ std::unique_ptr<sql::PreparedStatement> ladderStmt (
897+ _connection->prepareStatement (" SELECT id FROM ladders WHERE abbreviation = ? LIMIT 1" )
898+ );
899+ ladderStmt->setString (1 , _ladder);
900+ std::unique_ptr<sql::ResultSet> ladderResult (ladderStmt->executeQuery ());
901+
902+ if (ladderResult->next ())
903+ {
904+ ladderId = ladderResult->getUInt (" id" );
905+ }
906+ else
907+ {
908+ Log::fatal () << " Ladder '" << _ladder << " ' not found in table 'ladders'." ;
909+ return ;
910+ }
911+
912+ // Prepare statement to save user data.
913+ std::unique_ptr<sql::PreparedStatement> insertStmt (
914+ _connection->prepareStatement (R"SQL(
915+ INSERT INTO user_ratings
916+ (user_id, ladder_id, rating, elo_rank, alltime_rank, rated_games, active, created_at, updated_at)
917+ VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
918+ )SQL" )
919+ );
920+
921+ for (uint32_t userId : players.userIds ())
922+ {
923+ const Player &player = players[userId];
924+ uint32_t gameCount = player.gameCount ();
925+ double elo = (gameMode == gamemodes::Blitz2v2) ? player.elo (factions::Combined) : player.maxRating (!player.isActive ());
926+ if (elo < 0.0 )
927+ {
928+ // Try to get any rating, even if the player was never active.
929+ elo = player.elo (factions::Combined);
930+ }
931+ insertStmt->setUInt (1 , userId);
932+ insertStmt->setUInt (2 , ladderId);
933+ insertStmt->setInt (3 , static_cast <int >(std::round (elo)));
934+ insertStmt->setUInt (4 , activeRanks.contains (userId) ? activeRanks[userId] : 0 );
935+ insertStmt->setUInt (5 , allTimeRanks.contains (userId) ? allTimeRanks[userId] : 0 );
936+ insertStmt->setUInt (6 , gameCount);
937+ insertStmt->setBoolean (7 , player.isActive ());
938+
939+ insertStmt->execute ();
940+ }
941+
942+ Log::info () << " Player ratings written to 'user_ratings'." ;
943+ }
944+ catch (sql::SQLException &e)
945+ {
946+ Log::fatal () << " Error while writing user ratings: " << e.what ();
947+ }
948+ }
0 commit comments