diff --git a/src/utils/algorithms/calculateMMR.ts b/src/utils/algorithms/calculateMMR.ts index 5d394d5..c7aef1f 100644 --- a/src/utils/algorithms/calculateMMR.ts +++ b/src/utils/algorithms/calculateMMR.ts @@ -2,8 +2,10 @@ import { getQueueSettings, getUsersNeedingRoleUpdates, updatePlayerMmrAll, + countPlayerGames, + getLeaderboardPosition, } from '../queryDB' -import type { Matches, Queues, teamResults } from 'psqlDB' +import type { Queues, teamResults } from 'psqlDB' import { setUserQueueRole } from 'utils/queueHelpers' import { clamp } from 'lodash-es' @@ -130,7 +132,6 @@ export async function calculatePredictedMMR( export async function calculateNewMMR( queueId: number, - matchData: Matches, queueSettings: Queues, teamResults: teamResults, winningTeamId: number, @@ -143,8 +144,15 @@ export async function calculateNewMMR( queueSettings.default_elo, ) - const playerMMRChanges: Array<{ user_id: string; oldMMR: number; newMMR: number }> = [] + const playerMMRChanges: Array<{ + user_id: string + oldMMR: number + newMMR: number + oldRank: number | null + newRank: number | null + }> = [] const updatePromises: Promise[] = [] + let roleUpdateUsers: string[] = [] for (const ts of teamStats) { const isWinner = ts.isWinner @@ -152,17 +160,12 @@ export async function calculateNewMMR( for (const player of ts.team.players) { const oldMMR = player.elo ?? queueSettings.default_elo + const oldRank = await getLeaderboardPosition(queueId, player.user_id) const oldVolatility = player.volatility ?? 0 const newMMR = parseFloat((oldMMR + mmrChange).toFixed(1)) const newVolatility = Math.min(oldVolatility + 1, 10) - playerMMRChanges.push({ - user_id: player.user_id, - oldMMR, - newMMR, - }) - player.elo = clamp(newMMR, 0, 9999) player.elo_change = parseFloat(mmrChange.toFixed(1)) player.volatility = newVolatility @@ -170,6 +173,21 @@ export async function calculateNewMMR( updatePromises.push( updatePlayerMmrAll(queueId, player.user_id, newMMR, newVolatility), ) + + const newRank = await getLeaderboardPosition(queueId, player.user_id) + + playerMMRChanges.push({ + user_id: player.user_id, + oldMMR, + newMMR, + oldRank, + newRank, + }) + + const gamesPlayed = await countPlayerGames(queueId, player.user_id) + if (gamesPlayed === 1) { + roleUpdateUsers.push(player.user_id) + } } ts.team.score = isWinner ? 1 : 0 @@ -177,16 +195,17 @@ export async function calculateNewMMR( await Promise.all(updatePromises) - const usersNeedingRoleUpdate = await getUsersNeedingRoleUpdates( + // Get users who need role updates due to MMR threshold changes + let usersNeedingRoleUpdate = await getUsersNeedingRoleUpdates( queueId, playerMMRChanges, ) - if (usersNeedingRoleUpdate.length > 0) { + roleUpdateUsers = roleUpdateUsers.concat(usersNeedingRoleUpdate).flat() + + if (roleUpdateUsers.length > 0) { await Promise.all( - usersNeedingRoleUpdate.map((userId) => - setUserQueueRole(queueId, userId), - ), + roleUpdateUsers.map((userId) => setUserQueueRole(queueId, userId)), ) } diff --git a/src/utils/matchHelpers.ts b/src/utils/matchHelpers.ts index b1312fc..cf79232 100644 --- a/src/utils/matchHelpers.ts +++ b/src/utils/matchHelpers.ts @@ -760,7 +760,6 @@ export async function endMatch( teamResults = await calculateNewMMR( queueId, - matchData, queueSettings, teamResultsData, winningTeamId, diff --git a/src/utils/queryDB.ts b/src/utils/queryDB.ts index 8853d4c..1113081 100644 --- a/src/utils/queryDB.ts +++ b/src/utils/queryDB.ts @@ -13,7 +13,6 @@ import { } from 'psqlDB' import { client, getGuild } from '../client' import { QueryResult } from 'pg' -import { setUserQueueRole } from './queueHelpers' import { endMatch } from './matchHelpers' // Get the helper role @@ -281,6 +280,23 @@ export async function getAllQueueRoles( return res.rows } +// Count completed games for a user in a queue +export async function countPlayerGames( + queueId: number, + userId: string, +): Promise { + const res = await pool.query( + ` + SELECT COUNT(CASE WHEN m.winning_team IS NOT NULL THEN 1 END)::integer as games_played + FROM match_users mu + JOIN matches m ON m.id = mu.match_id + WHERE mu.user_id = $1 AND m.queue_id = $2 + `, + [userId, queueId], + ) + return res.rows[0]?.games_played ?? 0 +} + // get a users highest queue role export async function getUserQueueRole( queueId: number, @@ -306,7 +322,13 @@ export async function getUserQueueRole( export async function getUsersNeedingRoleUpdates( queueId: number, - players: Array<{ user_id: string; oldMMR: number; newMMR: number }>, + players: Array<{ + user_id: string + oldMMR: number + newMMR: number + oldRank: number | null + newRank: number | null + }>, ): Promise { if (players.length === 0) return [] @@ -317,6 +339,13 @@ export async function getUsersNeedingRoleUpdates( [queueId], ) + const leaderboardRoles = await pool.query( + `SELECT leaderboard_min, leaderboard_max FROM queue_roles + WHERE queue_id = $1 AND mmr_threshold IS NULL + ORDER BY leaderboard_min DESC`, + [queueId], + ) + const thresholds = roles.rows.map((r) => r.mmr_threshold) const usersToUpdate: string[] = [] @@ -327,6 +356,26 @@ export async function getUsersNeedingRoleUpdates( if (oldRole !== newRole) { usersToUpdate.push(player.user_id) } + + // Also handle leaderboard positions + if ( + player.oldRank !== null && + player.newRank !== null && + leaderboardRoles && + leaderboardRoles.rowCount !== 0 + ) { + const oldLeaderboardRole = leaderboardRoles.rows.find( + (r) => r.leaderboard_min <= player.oldRank!, + ) + const newLeaderboardRole = leaderboardRoles.rows.find( + (r) => r.leaderboard_min <= player.newRank!, + ) + + // Update leaderboard role if its not the same + if (oldLeaderboardRole !== newLeaderboardRole) { + usersToUpdate.push(player.user_id) + } + } } return usersToUpdate @@ -363,13 +412,15 @@ export async function getLeaderboardQueueRole( SELECT * FROM queue_roles WHERE queue_id = $1 - AND leaderboard_min >= $2 - AND leaderboard_max <= $2 + AND leaderboard_min <= $2 + AND leaderboard_max >= $2 LIMIT 1 `, [queueId, rank], ) + console.log(roleRes.rowCount) + if (roleRes.rowCount === 0) return null return roleRes.rows[0] }