From 5cd0837815cde4411f6d5ddd35ebc275568a7c92 Mon Sep 17 00:00:00 2001 From: Kalane Date: Wed, 11 Aug 2021 00:12:17 +0200 Subject: [PATCH] feat: ugly working version: need to work with old matchLists too --- client/src/store/modules/summoner.js | 7 +- .../app/Controllers/Http/MatchesController.ts | 7 +- .../Jax/src/Endpoints/MatchEndpoint.ts | 4 +- .../Jax/src/Endpoints/MatchlistEndpoint.ts | 4 +- server/app/Services/MatchService.ts | 20 ++-- .../app/Transformers/BasicMatchTransformer.ts | 25 ++--- .../Transformers/DetailedMatchTransformer.ts | 40 +++---- server/app/Transformers/MatchTransformer.ts | 104 +++++++++--------- .../app/Validators/MatchesIndexValidator.ts | 2 +- server/app/helpers.ts | 52 ++++++--- 10 files changed, 145 insertions(+), 120 deletions(-) diff --git a/client/src/store/modules/summoner.js b/client/src/store/modules/summoner.js index 2b59d45..7c3e840 100644 --- a/client/src/store/modules/summoner.js +++ b/client/src/store/modules/summoner.js @@ -180,7 +180,12 @@ export const actions = { const gameIds = getters.filteredMatchList .slice(state.overview.matchIndex, state.overview.matchIndex + 10) - .map(({ gameId }) => gameId) + .map((gameId) => { + if(typeof gameId == 'string') { + return gameId + } + return gameId.gameId.toString() + }) const resp = await axios(({ url: 'match', diff --git a/server/app/Controllers/Http/MatchesController.ts b/server/app/Controllers/Http/MatchesController.ts index a493b8a..98249b2 100644 --- a/server/app/Controllers/Http/MatchesController.ts +++ b/server/app/Controllers/Http/MatchesController.ts @@ -40,7 +40,9 @@ export default class MatchesController { if (!summonerDB) { return response.json(null) } - const matches = await MatchService.getMatches(puuid, accountId, region, gameIds, summonerDB) + + const matchesId = /^\d/.test(gameIds[0]) ? gameIds.map(id => `${region.toUpperCase()}_${id}`) : gameIds + const matches = await MatchService.getMatches(puuid, accountId, region, matchesId, summonerDB) await summonerDB.save() @@ -67,7 +69,8 @@ export default class MatchesController { console.log('MATCH DETAILS ALREADY SAVED') matchDetails = alreadySaved } else { - const match = await Jax.Match.get(gameId, region) + const matchId = `${region.toUpperCase()}_${gameId}` + const match = await Jax.Match.get(matchId, region) matchDetails = await DetailedMatchTransformer.transform(match) await DetailedMatch.create(matchDetails) } diff --git a/server/app/Services/Jax/src/Endpoints/MatchEndpoint.ts b/server/app/Services/Jax/src/Endpoints/MatchEndpoint.ts index bd82f7b..6d69dcf 100644 --- a/server/app/Services/Jax/src/Endpoints/MatchEndpoint.ts +++ b/server/app/Services/Jax/src/Endpoints/MatchEndpoint.ts @@ -1,5 +1,5 @@ // import { RiotRateLimiter } from '@fightmegg/riot-rate-limiter' -import { getV5Region } from 'App/helpers' +import { getRiotRegion } from 'App/helpers' import RiotRateLimiter from 'riot-ratelimiter' import { JaxConfig } from '../../JaxConfig' import JaxRequest from '../JaxRequest' @@ -423,7 +423,7 @@ export default class MatchEndpoint { public get (matchID: string, region: string): Promise { return new JaxRequest( - getV5Region(region), + getRiotRegion(region), this.config, `match/v5/matches/${matchID}`, this.limiter, diff --git a/server/app/Services/Jax/src/Endpoints/MatchlistEndpoint.ts b/server/app/Services/Jax/src/Endpoints/MatchlistEndpoint.ts index 418f12e..160ab35 100644 --- a/server/app/Services/Jax/src/Endpoints/MatchlistEndpoint.ts +++ b/server/app/Services/Jax/src/Endpoints/MatchlistEndpoint.ts @@ -1,5 +1,5 @@ // import { RiotRateLimiter } from '@fightmegg/riot-rate-limiter' -import { getV5Region } from 'App/helpers' +import { getRiotRegion } from 'App/helpers' import RiotRateLimiter from 'riot-ratelimiter' import { JaxConfig } from '../../JaxConfig' import JaxRequest from '../JaxRequest' @@ -44,7 +44,7 @@ export default class MatchlistEndpoint { public puuid (puuid: string, region: string, beginIndex = 0, count = 100): Promise { return new JaxRequest( - getV5Region(region), + getRiotRegion(region), this.config, `match/v5/matches/by-puuid/${puuid}/ids?start=${beginIndex}&count=${count}`, this.limiter, diff --git a/server/app/Services/MatchService.ts b/server/app/Services/MatchService.ts index 0cf203a..2c87895 100644 --- a/server/app/Services/MatchService.ts +++ b/server/app/Services/MatchService.ts @@ -29,7 +29,7 @@ class MatchService { matchList = [...matchList, ...newMatchList] alreadyIn = newMatchList.length === 0 || stopFetching(newMatchList) // If the match is made in another region : we stop fetching - // if (matchList[matchList.length - 1].platformId.toLowerCase() !== account.region) { // TODO: check this... + // if (matchList[matchList.length - 1].platformId.toLowerCase() !== account.region) { // TODO: check if region has changed // alreadyIn = true // } index += 100 @@ -92,11 +92,11 @@ class MatchService { * @param gameIds * @param summonerDB */ - public async getMatches (puuid: string, accountId: string, region: string, matchIds: string[], summonerDB: SummonerModel) { + public async getMatches (puuid: string, accountId: string, region: string, matchIds: MatchlistDto, summonerDB: SummonerModel) { console.time('getMatches') let matchesDetails: MatchModel[] = [] - const matchesToGetFromRiot: string[] = [] + const matchesToGetFromRiot: MatchlistDto = [] for (let i = 0; i < matchIds.length; ++i) { const matchSaved = await Match.findOne({ summoner_puuid: puuid, @@ -122,16 +122,14 @@ class MatchService { }) // Transform raw matches data - // const transformedMatches = await BasicMatchTransformer.transform(matchesFromApi, { puuid, accountId }) + const transformedMatches = await BasicMatchTransformer.transform(matchesFromApi, { puuid, accountId }) /* Save all matches from Riot Api in db */ - // for (const match of transformedMatches) { - // // await Match.create(match) - // match.newMatch = true - // } - // matchesDetails = [...matchesDetails, ...transformedMatches] - - matchesDetails = [...matchesDetails, ...matchesFromApi as unknown as MatchModel[]] + for (const match of transformedMatches) { + await Match.create(match) + match.newMatch = true + } + matchesDetails = [...matchesDetails, ...transformedMatches] } /* Sort matches */ diff --git a/server/app/Transformers/BasicMatchTransformer.ts b/server/app/Transformers/BasicMatchTransformer.ts index b2a376d..3377104 100644 --- a/server/app/Transformers/BasicMatchTransformer.ts +++ b/server/app/Transformers/BasicMatchTransformer.ts @@ -13,13 +13,12 @@ class BasicMatchTransformer extends MatchTransformer { // Global data about the match const globalInfos = super.getGameInfos(match) - const identity = match.participantIdentities.find((p) => p.player.currentAccountId === accountId) - const player = match.participants[identity!.participantId - 1] + const player = match.info.participants.find(p => p.puuid === puuid)! - let win = match.teams.find((t) => t.teamId === player.teamId)!.win + let win = match.info.teams.find((t) => t.teamId === player.teamId)!.win ? 'Win' : 'Fail' // Match less than 5min - if (match.gameDuration < 300) { + if (match.info.gameDuration < 300) { win = 'Remake' } @@ -29,16 +28,15 @@ class BasicMatchTransformer extends MatchTransformer { // Teams data const allyTeam: ParticipantBasic[] = [] const enemyTeam: ParticipantBasic[] = [] - for (let summoner of match.participantIdentities) { - const allData = match.participants[summoner.participantId - 1] + for (let summoner of match.info.participants) { const playerInfos = { - account_id: summoner.player.currentAccountId, - name: summoner.player.summonerName, - role: super.getRoleName(allData.timeline, match.queueId), - champion: super.getChampion(allData.championId), + account_id: summoner.puuid, // TODO: switch to puuid + name: summoner.summonerName, + role: super.getRoleName(summoner.teamPosition, match.info.queueId), + champion: super.getChampion(summoner.championId), } - if (allData.teamId === player.teamId) { + if (summoner.teamId === player.teamId) { allyTeam.push(playerInfos) } else { enemyTeam.push(playerInfos) @@ -49,9 +47,10 @@ class BasicMatchTransformer extends MatchTransformer { super.getMatchRoles(match, allyTeam, enemyTeam, player.teamId, playerData) return { - account_id: identity!.player.currentAccountId, + account_id: accountId, summoner_puuid: puuid, - gameId: match.gameId, + gameId: match.info.gameId, + matchId: match.metadata.matchId, result: win, allyTeam, enemyTeam, diff --git a/server/app/Transformers/DetailedMatchTransformer.ts b/server/app/Transformers/DetailedMatchTransformer.ts index ad73be0..8123f3a 100644 --- a/server/app/Transformers/DetailedMatchTransformer.ts +++ b/server/app/Transformers/DetailedMatchTransformer.ts @@ -1,5 +1,5 @@ import { Ban, DetailedMatchModel } from 'App/Models/DetailedMatch' -import { MatchDto, TeamStatsDto } from 'App/Services/Jax/src/Endpoints/MatchEndpoint' +import { MatchDto, TeamDto } from 'App/Services/Jax/src/Endpoints/MatchEndpoint' import MatchTransformer from './MatchTransformer' /** @@ -13,22 +13,22 @@ class DetailedMatchTransformer extends MatchTransformer { * @param match raw match data from Riot API * @param team raw team data from Riot API */ - private getTeamData (match: MatchDto, team: TeamStatsDto) { - let win = team.win - if (match.gameDuration < 300) { + private getTeamData (match: MatchDto, team: TeamDto) { + let win = team.win ? 'Win' : 'Fail' + if (match.info.gameDuration < 300) { win = 'Remake' } // Global stats of the team - const teamPlayers = match.participants.filter(p => p.teamId === team.teamId) + const teamPlayers = match.info.participants.filter(p => p.teamId === team.teamId) const teamStats = teamPlayers.reduce((prev, cur) => { - prev.kills += cur.stats.kills - prev.deaths += cur.stats.deaths - prev.assists += cur.stats.assists - prev.gold += cur.stats.goldEarned - prev.dmgChamp += cur.stats.totalDamageDealtToChampions - prev.dmgObj += cur.stats.damageDealtToObjectives - prev.dmgTaken += cur.stats.totalDamageTaken + prev.kills += cur.kills + prev.deaths += cur.deaths + prev.assists += cur.assists + prev.gold += cur.goldEarned + prev.dmgChamp += cur.totalDamageDealtToChampions + prev.dmgObj += cur.damageDealtToObjectives + prev.dmgTaken += cur.totalDamageTaken return prev }, { kills: 0, deaths: 0, assists: 0, gold: 0, dmgChamp: 0, dmgObj: 0, dmgTaken: 0 }) @@ -57,15 +57,15 @@ class DetailedMatchTransformer extends MatchTransformer { return { bans, - barons: team.baronKills, + barons: team.objectives.baron.kills, color: team.teamId === 100 ? 'Blue' : 'Red', - dragons: team.dragonKills, - inhibitors: team.inhibitorKills, + dragons: team.objectives.dragon.kills, + inhibitors: team.objectives.inhibitor.kills, players, result: win, - riftHerald: team.riftHeraldKills, + riftHerald: team.objectives.riftHerald.kills, teamStats, - towers: team.towerKills, + towers: team.objectives.tower.kills, } } @@ -80,14 +80,14 @@ class DetailedMatchTransformer extends MatchTransformer { const globalInfos = super.getGameInfos(match) // Teams - const firstTeam = this.getTeamData(match, match.teams[0]) - const secondTeam = this.getTeamData(match, match.teams[1]) + const firstTeam = this.getTeamData(match, match.info.teams[0]) + const secondTeam = this.getTeamData(match, match.info.teams[1]) // Roles super.getMatchRoles(match, firstTeam.players, secondTeam.players) return { - gameId: match.gameId, + gameId: match.info.gameId, blueTeam: firstTeam.color === 'Blue' ? firstTeam : secondTeam, redTeam: firstTeam.color === 'Blue' ? secondTeam : firstTeam, ...globalInfos, diff --git a/server/app/Transformers/MatchTransformer.ts b/server/app/Transformers/MatchTransformer.ts index 4a3b5e8..eb9422b 100644 --- a/server/app/Transformers/MatchTransformer.ts +++ b/server/app/Transformers/MatchTransformer.ts @@ -7,6 +7,7 @@ import { TeamStats } from 'App/Models/DetailedMatch' import { MatchDto, ParticipantDto, + PerksDto, } from 'App/Services/Jax/src/Endpoints/MatchEndpoint' export interface PlayerRole { @@ -83,23 +84,22 @@ export default abstract class MatchTransformer { * @param teamStats : if detailed, the teamStats argument is mandatory */ public getPlayerData (match: MatchDto, player: ParticipantDto, detailed: boolean, teamStats?: TeamStats) { - const identity = match.participantIdentities.find(p => p.participantId === player.participantId) - const name = identity!.player.summonerName + const name = player.summonerName const champion = this.getChampion(player.championId) - const role = this.getRoleName(player.timeline, match.queueId) - const level = player.stats.champLevel + const role = this.getRoleName(player.teamPosition, match.info.queueId) + const level = player.champLevel // Regular stats / Full match stats const stats: Stats = { - kills: player.stats.kills, - deaths: player.stats.deaths, - assists: player.stats.assists, - minions: player.stats.totalMinionsKilled + player.stats.neutralMinionsKilled, - vision: player.stats.visionScore, - gold: player.stats.goldEarned, - dmgChamp: player.stats.totalDamageDealtToChampions, - dmgObj: player.stats.damageDealtToObjectives, - dmgTaken: player.stats.totalDamageTaken, + kills: player.kills, + deaths: player.deaths, + assists: player.assists, + minions: player.totalMinionsKilled + player.neutralMinionsKilled, + vision: player.visionScore, + gold: player.goldEarned, + dmgChamp: player.totalDamageDealtToChampions, + dmgObj: player.damageDealtToObjectives, + dmgTaken: player.totalDamageTaken, kp: 0, kda: 0, realKda: 0, @@ -118,44 +118,48 @@ export default abstract class MatchTransformer { if (detailed) { teamStats = teamStats! percentStats = { - minions: +(stats.minions / (match.gameDuration / 60)).toFixed(2), - vision: +(stats.vision / (match.gameDuration / 60)).toFixed(2), - gold: +(player.stats.goldEarned * 100 / teamStats.gold).toFixed(1) + '%', - dmgChamp: +(player.stats.totalDamageDealtToChampions * 100 / teamStats.dmgChamp).toFixed(1) + '%', - dmgObj: +(teamStats.dmgObj ? player.stats.damageDealtToObjectives * 100 / teamStats.dmgObj : 0).toFixed(1) + '%', - dmgTaken: +(player.stats.totalDamageTaken * 100 / teamStats.dmgTaken).toFixed(1) + '%', + minions: +(stats.minions / (match.info.gameDuration / 60)).toFixed(2), + vision: +(stats.vision / (match.info.gameDuration / 60)).toFixed(2), + gold: +(player.goldEarned * 100 / teamStats.gold).toFixed(1) + '%', + dmgChamp: +(player.totalDamageDealtToChampions * 100 / teamStats.dmgChamp).toFixed(1) + '%', + dmgObj: +(teamStats.dmgObj ? player.damageDealtToObjectives * 100 / teamStats.dmgObj : 0).toFixed(1) + '%', + dmgTaken: +(player.totalDamageTaken * 100 / teamStats.dmgTaken).toFixed(1) + '%', } stats.kp = teamStats.kills === 0 ? '0%' : +((stats.kills + stats.assists) * 100 / teamStats.kills).toFixed(1) + '%' } else { - const totalKills = match.participants.reduce((prev, current) => { + const totalKills = match.info.participants.reduce((prev, current) => { if (current.teamId !== player.teamId) { return prev } - return prev + current.stats.kills + return prev + current.kills }, 0) - stats.criticalStrike = player.stats.largestCriticalStrike - stats.killingSpree = player.stats.largestKillingSpree - stats.doubleKills = player.stats.doubleKills - stats.tripleKills = player.stats.tripleKills - stats.quadraKills = player.stats.quadraKills - stats.pentaKills = player.stats.pentaKills - stats.heal = player.stats.totalHeal - stats.towers = player.stats.turretKills - stats.longestLiving = player.stats.longestTimeSpentLiving + stats.criticalStrike = player.largestCriticalStrike + stats.killingSpree = player.largestKillingSpree + stats.doubleKills = player.doubleKills + stats.tripleKills = player.tripleKills + stats.quadraKills = player.quadraKills + stats.pentaKills = player.pentaKills + stats.heal = player.totalHeal + stats.towers = player.turretKills + stats.longestLiving = player.longestTimeSpentLiving stats.kp = totalKills === 0 ? 0 : +((stats.kills + stats.assists) * 100 / totalKills).toFixed(1) } let primaryRune: string | null = null let secondaryRune: string | null = null - if (player.stats.perkPrimaryStyle) { - ({ primaryRune, secondaryRune } = this.getPerksImages(player.stats.perk0, player.stats.perkSubStyle)) + + const primaryStyle = player.perks.styles.find(s => s.description === 'primaryStyle') + const subStyle = player.perks.styles.find(s => s.description === 'subStyle') + + if (primaryStyle && subStyle && primaryStyle.selections.length) { + ({ primaryRune, secondaryRune } = this.getPerksImages(primaryStyle.selections[0].perk, subStyle.style)) } const items: (Item | null)[] = [] for (let i = 0; i < 6; i++) { - const id = player.stats['item' + i] + const id = player['item' + i] if (id === 0) { items.push(null) continue @@ -182,7 +186,7 @@ export default abstract class MatchTransformer { const playerData: ParticipantDetails = { name, - summonerId: identity!.player.summonerId, + summonerId: player.summonerId, champion, role, primaryRune, @@ -197,25 +201,25 @@ export default abstract class MatchTransformer { playerData.percentStats = percentStats! } - playerData.perks = this.getFullPerks(player.stats) + playerData.perks = this.getFullPerks(player.perks) return playerData } - public getFullPerks (stats: ParticipantStatsDto) { + public getFullPerks (perksDto: PerksDto) { const perks: Perks = { - primaryStyle: stats.perkPrimaryStyle, - secondaryStyle: stats.perkSubStyle, + primaryStyle: perksDto.styles.find(s => s.description === 'primaryStyle')!.style, + secondaryStyle: perksDto.styles.find(s => s.description === 'subStyle')!.style, selected: [], } - for (let i = 0; i < 6; i++) { - perks.selected.push(stats[`perk${i}`]) + for (const styles of perksDto.styles) { + for (const perk of styles.selections) { + perks.selected.push(perk.perk) + } } - for (let i = 0; i < 3; i++) { - perks.selected.push(stats[`statPerk${i}`]) - } + perks.selected.concat(Object.values(perksDto.statPerks)) return perks } @@ -245,16 +249,16 @@ export default abstract class MatchTransformer { * @param timeline from Riot Api * @param gamemode of the match to check if a role is needed */ - public getRoleName (timeline: ParticipantTimelineDto, gamemode: number) { - if (!queuesWithRole.includes(gamemode)) { + public getRoleName (teamPosition: string, gamemode: number) { + if (!queuesWithRole.includes(gamemode) || !teamPosition) { return 'NONE' } - if (timeline.lane === 'BOTTOM' && timeline.role.includes('SUPPORT')) { + if (teamPosition === 'UTILITY') { return 'SUPPORT' } - return timeline.lane + return teamPosition } /** @@ -317,11 +321,11 @@ export default abstract class MatchTransformer { let allyChamps: PlayerRole[] = [] let enemyChamps: PlayerRole[] = [] - match.participants.map(p => { - const items = [p.stats.item0, p.stats.item1, p.stats.item2, p.stats.item3, p.stats.item4, p.stats.item5] + match.info.participants.map(p => { + const items = [p.item0, p.item1, p.item2, p.item3, p.item4, p.item5] const playerRole = { champion: p.championId, - jungle: p.spell1Id === 11 || p.spell2Id === 11, + jungle: p.summoner1Id === 11 || p.summoner2Id === 11, support: supportItems.some(suppItem => items.includes(suppItem)), } p.teamId === allyTeamId ? allyChamps.push(playerRole) : enemyChamps.push(playerRole) diff --git a/server/app/Validators/MatchesIndexValidator.ts b/server/app/Validators/MatchesIndexValidator.ts index adc0d9f..c7f31dc 100644 --- a/server/app/Validators/MatchesIndexValidator.ts +++ b/server/app/Validators/MatchesIndexValidator.ts @@ -29,7 +29,7 @@ export default class MatchesIndexValidator { accountId: schema.string(), region: schema.string(), gameIds: schema.array().members( - schema.number() + schema.string() ), season: schema.number.optional(), }) diff --git a/server/app/helpers.ts b/server/app/helpers.ts index 21b7e15..db8adef 100644 --- a/server/app/helpers.ts +++ b/server/app/helpers.ts @@ -3,34 +3,50 @@ import { ParticipantBasic, ParticipantDetails } from './Models/Match' /** * All League of Legends regions used in Riot API */ -export type Region = 'br1' | 'eun1' | 'euw1' | 'jp1' | 'kr' | 'la1' | 'la2' | 'na1' | 'oc1' | 'tr1' | 'ru' +export enum LeagueRegion { + BRAZIL = 'br1', + EUROPE_NORTHEAST = 'eun1', + EUROPE_WEST = 'euw1', + KOREA = 'kr', + LATIN_AMERICA_NORTH = 'la1', + LATIN_AMERICA_SOUTH = 'la2', + NORTH_AMERICA = 'na1', + OCEANIA = 'oc1', + RUSSIA = 'ru', + TURKEY = 'tr1', + JAPAN = 'jp1', +} /** * New regions used in Riot API >= v5 */ -export type V5Region = 'americas' | 'asia' | 'europe' +export enum RiotRegion { + AMERICAS = 'americas', + ASIA = 'asia', + EUROPE = 'europe', +} /** * Map old Riot API regions to new ones * @param region : old region * @returns new region name */ -export function getV5Region (region: string): V5Region { - switch (region as Region) { // TODO: remove cast when region is typed to "Region" everywhere instead of string - case 'na1': - case 'br1': - case 'la1': - case 'la2': - case 'oc1': - return 'americas' - case 'kr': - case 'jp1': - return 'asia' - case 'eun1': - case 'euw1': - case 'tr1': - case 'ru': - return 'europe' +export function getRiotRegion (region: string): RiotRegion { + switch (region as LeagueRegion) { // TODO: remove cast when region is typed to "Region" everywhere instead of string + case LeagueRegion.NORTH_AMERICA: + case LeagueRegion.BRAZIL: + case LeagueRegion.LATIN_AMERICA_NORTH: + case LeagueRegion.LATIN_AMERICA_SOUTH: + case LeagueRegion.OCEANIA: + return RiotRegion.AMERICAS + case LeagueRegion.KOREA: + case LeagueRegion.JAPAN: + return RiotRegion.ASIA + case LeagueRegion.EUROPE_NORTHEAST: + case LeagueRegion.EUROPE_WEST: + case LeagueRegion.TURKEY: + case LeagueRegion.RUSSIA: + return RiotRegion.EUROPE } }