From 9973a310e3a26a7420fc4c9a9ac003b2377b9ea5 Mon Sep 17 00:00:00 2001 From: Jefferson Turpin Date: Thu, 16 Oct 2025 16:47:31 -0600 Subject: [PATCH 1/7] Potential fix for LOSS 0.0 MMR Hopefully this works --- src/utils/algorithms/calculateMMR.ts | 1 + src/utils/matchHelpers.ts | 14 +++++++------- src/utils/queueHelpers.ts | 26 ++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/utils/algorithms/calculateMMR.ts b/src/utils/algorithms/calculateMMR.ts index 2b0b2cc..7e5904f 100644 --- a/src/utils/algorithms/calculateMMR.ts +++ b/src/utils/algorithms/calculateMMR.ts @@ -129,6 +129,7 @@ export async function calculatePredictedMMR( } } + export async function calculateNewMMR( queueId: number, matchId: number, diff --git a/src/utils/matchHelpers.ts b/src/utils/matchHelpers.ts index f05c541..1d396ab 100644 --- a/src/utils/matchHelpers.ts +++ b/src/utils/matchHelpers.ts @@ -644,18 +644,18 @@ export async function endMatch( teamResults = await calculateNewMMR(queueId, matchId, teamResultsData) // Save elo_change and winstreak to database + // IMPORTANT: Always update elo_change to prevent race conditions const updatePromises = teamResults.teams.flatMap((team) => team.players.map(async (player) => { // Update win streak await updatePlayerWinStreak(player.user_id, queueId, team.score == 1) - // Update elo change if it exists - if (player.elo_change !== undefined && player.elo_change !== null) { - await pool.query( - `UPDATE match_users SET elo_change = $1 WHERE match_id = $2 AND user_id = $3`, - [player.elo_change, matchId, player.user_id], - ) - } + // Always update elo_change - use 0 as fallback if calculation failed + const eloChange = player.elo_change ?? 0 + await pool.query( + `UPDATE match_users SET elo_change = $1 WHERE match_id = $2 AND user_id = $3`, + [eloChange, matchId, player.user_id], + ) }), ) diff --git a/src/utils/queueHelpers.ts b/src/utils/queueHelpers.ts index c28f94d..8ae5bb9 100644 --- a/src/utils/queueHelpers.ts +++ b/src/utils/queueHelpers.ts @@ -523,6 +523,7 @@ export async function createMatch( ) // Insert match_users (queue_join_time is already NULL from matchUpGames) + // First, insert all users to create the match_users records for (const userId of userIds) { await pool.query( `INSERT INTO match_users (user_id, match_id, team) @@ -543,6 +544,31 @@ export async function createMatch( } catch (err) {} } + // Calculate and store initial ELO changes for all players + // This prevents the race condition where elo_change is 0 + try { + const { getTeamsInMatch } = await import('./matchHelpers') + const { calculatePredictedMMR } = await import('./algorithms/calculateMMR') + + const teamData = await getTeamsInMatch(matchId) + if (teamData.teams.length > 0) { + // Calculate ELO changes assuming team 1 wins (as a baseline) + const winningTeamId = teamData.teams[0]?.id ?? 1 + const eloChanges = await calculatePredictedMMR(queueId, teamData, winningTeamId) + + // Store the calculated ELO changes in match_users table + for (const [userId, eloChange] of eloChanges.entries()) { + await pool.query( + `UPDATE match_users SET elo_change = $1 WHERE match_id = $2 AND user_id = $3`, + [eloChange, matchId, userId], + ) + } + } + } catch (err) { + console.error('Error calculating initial ELO changes:', err) + // Continue even if ELO calculation fails - it will be recalculated at match end + } + await updateQueueMessage() // Wait 2 seconds for channel to fully propagate in Discord's API From 225f1602009b6d99e2135b50f31b8d5e62b363c3 Mon Sep 17 00:00:00 2001 From: Jefferson Turpin Date: Thu, 16 Oct 2025 17:11:14 -0600 Subject: [PATCH 2/7] Revert "Potential fix for LOSS 0.0 MMR" This reverts commit 9973a310e3a26a7420fc4c9a9ac003b2377b9ea5. --- src/utils/algorithms/calculateMMR.ts | 1 - src/utils/matchHelpers.ts | 14 +++++++------- src/utils/queueHelpers.ts | 26 -------------------------- 3 files changed, 7 insertions(+), 34 deletions(-) diff --git a/src/utils/algorithms/calculateMMR.ts b/src/utils/algorithms/calculateMMR.ts index 7e5904f..2b0b2cc 100644 --- a/src/utils/algorithms/calculateMMR.ts +++ b/src/utils/algorithms/calculateMMR.ts @@ -129,7 +129,6 @@ export async function calculatePredictedMMR( } } - export async function calculateNewMMR( queueId: number, matchId: number, diff --git a/src/utils/matchHelpers.ts b/src/utils/matchHelpers.ts index 1d396ab..f05c541 100644 --- a/src/utils/matchHelpers.ts +++ b/src/utils/matchHelpers.ts @@ -644,18 +644,18 @@ export async function endMatch( teamResults = await calculateNewMMR(queueId, matchId, teamResultsData) // Save elo_change and winstreak to database - // IMPORTANT: Always update elo_change to prevent race conditions const updatePromises = teamResults.teams.flatMap((team) => team.players.map(async (player) => { // Update win streak await updatePlayerWinStreak(player.user_id, queueId, team.score == 1) - // Always update elo_change - use 0 as fallback if calculation failed - const eloChange = player.elo_change ?? 0 - await pool.query( - `UPDATE match_users SET elo_change = $1 WHERE match_id = $2 AND user_id = $3`, - [eloChange, matchId, player.user_id], - ) + // Update elo change if it exists + if (player.elo_change !== undefined && player.elo_change !== null) { + await pool.query( + `UPDATE match_users SET elo_change = $1 WHERE match_id = $2 AND user_id = $3`, + [player.elo_change, matchId, player.user_id], + ) + } }), ) diff --git a/src/utils/queueHelpers.ts b/src/utils/queueHelpers.ts index 8ae5bb9..c28f94d 100644 --- a/src/utils/queueHelpers.ts +++ b/src/utils/queueHelpers.ts @@ -523,7 +523,6 @@ export async function createMatch( ) // Insert match_users (queue_join_time is already NULL from matchUpGames) - // First, insert all users to create the match_users records for (const userId of userIds) { await pool.query( `INSERT INTO match_users (user_id, match_id, team) @@ -544,31 +543,6 @@ export async function createMatch( } catch (err) {} } - // Calculate and store initial ELO changes for all players - // This prevents the race condition where elo_change is 0 - try { - const { getTeamsInMatch } = await import('./matchHelpers') - const { calculatePredictedMMR } = await import('./algorithms/calculateMMR') - - const teamData = await getTeamsInMatch(matchId) - if (teamData.teams.length > 0) { - // Calculate ELO changes assuming team 1 wins (as a baseline) - const winningTeamId = teamData.teams[0]?.id ?? 1 - const eloChanges = await calculatePredictedMMR(queueId, teamData, winningTeamId) - - // Store the calculated ELO changes in match_users table - for (const [userId, eloChange] of eloChanges.entries()) { - await pool.query( - `UPDATE match_users SET elo_change = $1 WHERE match_id = $2 AND user_id = $3`, - [eloChange, matchId, userId], - ) - } - } - } catch (err) { - console.error('Error calculating initial ELO changes:', err) - // Continue even if ELO calculation fails - it will be recalculated at match end - } - await updateQueueMessage() // Wait 2 seconds for channel to fully propagate in Discord's API From 904c720c4df3f9d7d0a3a8571ed485baea8acd72 Mon Sep 17 00:00:00 2001 From: Jefferson Turpin Date: Thu, 16 Oct 2025 17:15:06 -0600 Subject: [PATCH 3/7] Added logging --- src/events/interactionCreate.ts | 5 +++++ src/utils/matchHelpers.ts | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts index 549c115..3c247b8 100644 --- a/src/events/interactionCreate.ts +++ b/src/events/interactionCreate.ts @@ -225,6 +225,7 @@ export default { const customSelId = interaction.values[0] const matchDataParts: string[] = customSelId.split('_') const matchId = parseInt(matchDataParts[1]) + console.log(`Finishing vote for ${matchId}, winner ${winner}`) // Check if this match is Best of 3 or 5 const matchDataObj = await getMatchData(matchId) @@ -279,6 +280,10 @@ export default { winningTeam = 2 } + console.log( + `Winning team variable for ${matchId}: ${winningTeam}`, + ) + if (winningTeam) { await setWinningTeam(matchId, winningTeam) await endMatch(matchId) diff --git a/src/utils/matchHelpers.ts b/src/utils/matchHelpers.ts index f05c541..595d5b5 100644 --- a/src/utils/matchHelpers.ts +++ b/src/utils/matchHelpers.ts @@ -605,6 +605,8 @@ export async function endMatch( ) } + if (!cancelled) console.log(`Match ${matchId} ended`) + // build results button row const resultsButtonRow: ActionRowBuilder = new ActionRowBuilder().addComponents( @@ -643,6 +645,8 @@ export async function endMatch( teamResults = await calculateNewMMR(queueId, matchId, teamResultsData) + console.log(`${matchId} results: ${teamResults}`) + // Save elo_change and winstreak to database const updatePromises = teamResults.teams.flatMap((team) => team.players.map(async (player) => { @@ -661,6 +665,8 @@ export async function endMatch( await Promise.all(updatePromises) + console.log(`Updated elo_change and win_streak for match ${matchId}`) + // build results embed const resultsEmbed = new EmbedBuilder() .setTitle(`🏆 ${queueSettings.queue_name} Match #${matchId} 🏆`) @@ -771,6 +777,8 @@ export async function endMatch( return false } + console.log(`Sending results to ${resultsChannel.id}`) + await resultsChannel.send({ embeds: [resultsEmbed], components: [resultsButtonRow], From 34d660523e98cfdabb300921cd488605cbc08141 Mon Sep 17 00:00:00 2001 From: Jefferson Turpin Date: Thu, 16 Oct 2025 17:33:17 -0600 Subject: [PATCH 4/7] More logging, reshuffled delete match channel --- src/events/interactionCreate.ts | 3 ++ src/utils/matchHelpers.ts | 82 +++++++++++++++++++-------------- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts index 3c247b8..e6d06ed 100644 --- a/src/events/interactionCreate.ts +++ b/src/events/interactionCreate.ts @@ -221,6 +221,9 @@ export default { await handleTwoPlayerMatchVoting(interaction, { participants: matchUsersArray, onComplete: async (interaction, winner) => { + console.log( + `Starting finish vote from vote from ${interaction.user.id}`, + ) try { const customSelId = interaction.values[0] const matchDataParts: string[] = customSelId.split('_') diff --git a/src/utils/matchHelpers.ts b/src/utils/matchHelpers.ts index 595d5b5..65072fc 100644 --- a/src/utils/matchHelpers.ts +++ b/src/utils/matchHelpers.ts @@ -566,47 +566,17 @@ export async function endMatch( matchId: number, cancelled = false, ): Promise { - try { - // close match in DB - await closeMatch(matchId) - - // get log file using glob library - // const pattern = path - // .join(__dirname, '..', 'logs', `match-${matchId}_*.log`) - // .replace(/\\/g, '/') - // const files = await glob(pattern) - // const file: string | null = files[0] ?? null - - // TODO: Re-add this and send it to the website - // if (file) { - // // format and send transcript - // const logContent = fs.readFileSync(file, 'utf8') - // const logLines = logContent - // .split('\n') - // .filter((line) => line.trim() !== '') - // const parsedLogLines = await parseLogLines(logLines) - // console.log(parsedLogLines) // json body - // - // // delete the log file after transcript is sent - // fs.unlinkSync(file) - // } + console.log(`Ending match ${matchId}, cancelled: ${cancelled}`) - // delete match channel + if (cancelled) { + console.log(`Match ${matchId} cancelled.`) const wasSuccessfullyDeleted = await deleteMatchChannel(matchId) if (!wasSuccessfullyDeleted) { console.log(`Channel id not found / failed to delete match ${matchId}`) } - - if (cancelled) return true - } catch (err) { - console.error( - `Error in file formatting or channel deletion for match ${matchId}:`, - err, - ) + return true } - if (!cancelled) console.log(`Match ${matchId} ended`) - // build results button row const resultsButtonRow: ActionRowBuilder = new ActionRowBuilder().addComponents( @@ -630,8 +600,11 @@ export async function endMatch( } const queueId = await getQueueIdFromMatch(matchId) + console.log(`Queue ID for match ${matchId}: ${queueId}`) const queueSettings = await getQueueSettings(queueId, ['queue_name', 'color']) + console.log(`Queue settings for match ${matchId}:`, queueSettings) const matchData = await getMatchData(matchId) + console.log(`Match data for match ${matchId}:`, matchData) let teamResults: teamResults | null // create our teamResults object here @@ -650,6 +623,7 @@ export async function endMatch( // Save elo_change and winstreak to database const updatePromises = teamResults.teams.flatMap((team) => team.players.map(async (player) => { + console.log(`Team ${team} player ${player} in ${matchId}`) // Update win streak await updatePlayerWinStreak(player.user_id, queueId, team.score == 1) @@ -667,6 +641,46 @@ export async function endMatch( console.log(`Updated elo_change and win_streak for match ${matchId}`) + try { + // close match in DB + console.log(`Ending match ${matchId}, cancelled: ${cancelled}`) + await closeMatch(matchId) + + // get log file using glob library + // const pattern = path + // .join(__dirname, '..', 'logs', `match-${matchId}_*.log`) + // .replace(/\\/g, '/') + // const files = await glob(pattern) + // const file: string | null = files[0] ?? null + + // TODO: Re-add this and send it to the website + // if (file) { + // // format and send transcript + // const logContent = fs.readFileSync(file, 'utf8') + // const logLines = logContent + // .split('\n') + // .filter((line) => line.trim() !== '') + // const parsedLogLines = await parseLogLines(logLines) + // console.log(parsedLogLines) // json body + // + // // delete the log file after transcript is sent + // fs.unlinkSync(file) + // } + + // delete match channel + const wasSuccessfullyDeleted = await deleteMatchChannel(matchId) + if (!wasSuccessfullyDeleted) { + console.log(`Channel id not found / failed to delete match ${matchId}`) + } + + if (cancelled) return true + } catch (err) { + console.error( + `Error in file formatting or channel deletion for match ${matchId}:`, + err, + ) + } + // build results embed const resultsEmbed = new EmbedBuilder() .setTitle(`🏆 ${queueSettings.queue_name} Match #${matchId} 🏆`) From 7e36961f90ce9bc06a4fa3a03e7ad8273e2c935b Mon Sep 17 00:00:00 2001 From: Jefferson Turpin Date: Thu, 16 Oct 2025 17:41:28 -0600 Subject: [PATCH 5/7] Even more logging --- src/events/interactionCreate.ts | 2 +- src/utils/matchHelpers.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts index e6d06ed..8b5b49d 100644 --- a/src/events/interactionCreate.ts +++ b/src/events/interactionCreate.ts @@ -222,7 +222,7 @@ export default { participants: matchUsersArray, onComplete: async (interaction, winner) => { console.log( - `Starting finish vote from vote from ${interaction.user.id}`, + `Starting finish vote from vote from ${interaction.user.id} with winner ${winner}`, ) try { const customSelId = interaction.values[0] diff --git a/src/utils/matchHelpers.ts b/src/utils/matchHelpers.ts index 65072fc..5e170b0 100644 --- a/src/utils/matchHelpers.ts +++ b/src/utils/matchHelpers.ts @@ -791,7 +791,7 @@ export async function endMatch( return false } - console.log(`Sending results to ${resultsChannel.id}`) + console.log(`Sending results to ${resultsChannel.id} on match ${matchId}`) await resultsChannel.send({ embeds: [resultsEmbed], From f17dbde0b8bf7196fd33c4cba8e762753ec9729e Mon Sep 17 00:00:00 2001 From: Jefferson Turpin Date: Thu, 16 Oct 2025 17:46:39 -0600 Subject: [PATCH 6/7] LOGGING --- src/events/interactionCreate.ts | 6 ++++-- src/utils/matchHelpers.ts | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts index 8b5b49d..e658fea 100644 --- a/src/events/interactionCreate.ts +++ b/src/events/interactionCreate.ts @@ -228,7 +228,9 @@ export default { const customSelId = interaction.values[0] const matchDataParts: string[] = customSelId.split('_') const matchId = parseInt(matchDataParts[1]) - console.log(`Finishing vote for ${matchId}, winner ${winner}`) + console.log( + `Finishing vote for match ${matchId}, winner ${winner}`, + ) // Check if this match is Best of 3 or 5 const matchDataObj = await getMatchData(matchId) @@ -284,7 +286,7 @@ export default { } console.log( - `Winning team variable for ${matchId}: ${winningTeam}`, + `Winning team variable for match ${matchId}: ${winningTeam}`, ) if (winningTeam) { diff --git a/src/utils/matchHelpers.ts b/src/utils/matchHelpers.ts index 5e170b0..5da3835 100644 --- a/src/utils/matchHelpers.ts +++ b/src/utils/matchHelpers.ts @@ -618,12 +618,12 @@ export async function endMatch( teamResults = await calculateNewMMR(queueId, matchId, teamResultsData) - console.log(`${matchId} results: ${teamResults}`) + console.log(`match ${matchId} results: ${teamResults}`) // Save elo_change and winstreak to database const updatePromises = teamResults.teams.flatMap((team) => team.players.map(async (player) => { - console.log(`Team ${team} player ${player} in ${matchId}`) + console.log(`Team ${team} player ${player} in match ${matchId}`) // Update win streak await updatePlayerWinStreak(player.user_id, queueId, team.score == 1) From 61f46391f107b63dc7087806a2292e0a49361ba4 Mon Sep 17 00:00:00 2001 From: Jefferson Turpin Date: Thu, 16 Oct 2025 19:00:33 -0600 Subject: [PATCH 7/7] Update matchHelpers.ts --- src/utils/matchHelpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/matchHelpers.ts b/src/utils/matchHelpers.ts index 5da3835..5b61b88 100644 --- a/src/utils/matchHelpers.ts +++ b/src/utils/matchHelpers.ts @@ -618,7 +618,7 @@ export async function endMatch( teamResults = await calculateNewMMR(queueId, matchId, teamResultsData) - console.log(`match ${matchId} results: ${teamResults}`) + console.log(`match ${matchId} results: ${teamResults.teams}`) // Save elo_change and winstreak to database const updatePromises = teamResults.teams.flatMap((team) =>