diff --git a/server-v2/app/Serializers/BasicMatchSerializer.ts b/server-v2/app/Serializers/BasicMatchSerializer.ts new file mode 100644 index 0000000..c2f297d --- /dev/null +++ b/server-v2/app/Serializers/BasicMatchSerializer.ts @@ -0,0 +1,152 @@ +import { getSeasonNumber, sortTeamByRole } from 'App/helpers' +import Match from 'App/Models/Match' +import MatchPlayer from 'App/Models/MatchPlayer' +import MatchSerializer from './MatchSerializer' +import { + SerializedMatch, + SerializedMatchChampion, + SerializedMatchItem, + SerializedMatchPerks, + SerializedMatchStats, + SerializedMatchTeamPlayer, +} from './SerializedTypes' + +class BasicMatchSerializer extends MatchSerializer { + /** + * Get champion specific data + * @param id of the champion + */ + protected getChampion(id: number): SerializedMatchChampion { + const originalChampionData = this.champions.find((c) => c.id === id) + const icon = + 'https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/' + + originalChampionData!.squarePortraitPath.split('/assets/')[1].toLowerCase() + + return { + icon, + id: originalChampionData!.id, + name: originalChampionData!.name, + alias: originalChampionData!.alias, + roles: originalChampionData!.roles, + } + } + + protected getPlayerSummary(player: MatchPlayer): SerializedMatchTeamPlayer { + return { + puuid: player.summonerPuuid, + champion: this.getChampion(player.championId), + name: player.summonerName, + role: player.teamPosition, + } + } + + protected getTeamSummary(players: MatchPlayer[]): SerializedMatchTeamPlayer[] { + return players.map((p) => this.getPlayerSummary(p)).sort(sortTeamByRole) + } + + protected getItems(player: MatchPlayer): Array { + const items: (SerializedMatchItem | null)[] = [] + for (let i = 0; i < 6; i++) { + const id = player['item' + i] + if (id === 0) { + items.push(null) + continue + } + + const item = this.items.find((i) => i.id === id) + if (!item) { + items.push(null) + continue + } + + const itemUrl = item.iconPath.split('/assets/')[1].toLowerCase() + items.push({ + image: `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/${itemUrl}`, + name: item.name, + description: item.description, + price: item.priceTotal, + }) + } + return items + } + + protected getPerks(player: MatchPlayer): SerializedMatchPerks { + return { + primaryStyle: player.perksPrimaryStyle, + secondaryStyle: player.perksSecondaryStyle, + selected: player.perksSelected, + } + } + + protected getStats(player: MatchPlayer): SerializedMatchStats { + return { + kills: player.kills, + deaths: player.deaths, + assists: player.assists, + minions: player.minions, + vision: player.visionScore, + gold: player.gold, + dmgChamp: player.damageDealtChampions, + dmgObj: player.damageDealtObjectives, + dmgTaken: player.damageTaken, + kp: player.kp, + kda: player.kills + player.assists !== 0 && player.deaths === 0 ? '∞' : player.kda, + realKda: player.kda, + criticalStrike: player.criticalStrike, + killingSpree: player.killingSpree, + doubleKills: player.doubleKills, + tripleKills: player.tripleKills, + quadraKills: player.quadraKills, + pentaKills: player.pentaKills, + heal: player.heal, + towers: player.turretKills, + longestLiving: player.timeSpentLiving, + } + } + + public serializeOneMatch(match: Match, puuid: string): SerializedMatch { + const identity = match.players.find((p) => p.summonerPuuid === puuid)! + + const allyTeamColor = identity.team === 100 ? 'blueTeam' : 'redTeam' + const allyTeam = match[allyTeamColor] + + const allyPlayers: MatchPlayer[] = [] + const enemyPlayers: MatchPlayer[] = [] + + for (const p of match.players) { + // TODO: remove Number() when Lazar push the updated migration + p.team === Number(allyTeam.color) ? allyPlayers.push(p) : enemyPlayers.push(p) + } + + return { + allyTeam: this.getTeamSummary(allyPlayers), + champion: this.getChampion(identity.championId), + date: match.date, + enemyTeam: this.getTeamSummary(enemyPlayers), + firstSum: identity.summoner1Id, + matchId: match.id, + gamemode: match.gamemode, + items: this.getItems(identity), + level: identity.champLevel, + map: match.map, + name: identity.summonerName, + perks: this.getPerks(identity), + region: match.region, + result: allyTeam.result.toString(), // TODO: remove toString() when Lazar push the updated migration + role: identity.teamPosition, + season: getSeasonNumber(match.date), + secondSum: identity.summoner2Id, + stats: this.getStats(identity), + summonerId: identity.summonerId, + summonerPuuid: puuid, + time: match.gameDuration, + } + } + public async serialize(matches: Match[], puuid: string): Promise { + await super.getContext() + + return matches.map((match) => this.serializeOneMatch(match, puuid)) + } +} + +export default new BasicMatchSerializer() diff --git a/server-v2/app/Serializers/MatchSerializer.ts b/server-v2/app/Serializers/MatchSerializer.ts new file mode 100644 index 0000000..71e7052 --- /dev/null +++ b/server-v2/app/Serializers/MatchSerializer.ts @@ -0,0 +1,39 @@ +import Jax from 'App/Services/Jax' +import { + ChampionDTO, + ItemDTO, + PerkDTO, + PerkStyleDTO, + SummonerSpellDTO, +} from 'App/Services/Jax/src/Endpoints/CDragonEndpoint' +import RoleIdentificationService, { + ChampionsPlayRate, +} from 'App/Services/RoleIdentificationService' + +export default abstract class MatchSerializer { + protected champions: ChampionDTO[] + protected items: ItemDTO[] + protected perks: PerkDTO[] + protected perkstyles: PerkStyleDTO[] + protected summonerSpells: SummonerSpellDTO[] + protected championRoles: ChampionsPlayRate + + /** + * Get global Context with CDragon Data + */ + public async getContext() { + const items = await Jax.CDragon.items() + const champions = await Jax.CDragon.champions() + const perks = await Jax.CDragon.perks() + const perkstyles = await Jax.CDragon.perkstyles() + const summonerSpells = await Jax.CDragon.summonerSpells() + const championRoles = await RoleIdentificationService.pullData().catch(() => {}) + + this.champions = champions + this.items = items + this.perks = perks + this.perkstyles = perkstyles.styles + this.summonerSpells = summonerSpells + this.championRoles = championRoles as ChampionsPlayRate + } +} diff --git a/server-v2/app/Serializers/SerializedTypes.ts b/server-v2/app/Serializers/SerializedTypes.ts new file mode 100644 index 0000000..06d1116 --- /dev/null +++ b/server-v2/app/Serializers/SerializedTypes.ts @@ -0,0 +1,75 @@ +export interface SerializedMatch { + allyTeam: SerializedMatchTeamPlayer[] + champion: SerializedMatchChampion + date: number + enemyTeam: SerializedMatchTeamPlayer[] + firstSum: number + matchId: string + gamemode: number + items: Array + level: number + map: number + name: string + perks: SerializedMatchPerks + region: string + result: string + role: string + season: number + secondSum: number + stats: SerializedMatchStats + summonerId: string + summonerPuuid: string + time: number +} + +export interface SerializedMatchTeamPlayer { + puuid: string + champion: SerializedMatchChampion + name: string + role: string +} + +export interface SerializedMatchChampion { + alias: string + icon: string + id: number + name: string + roles: string[] +} + +export interface SerializedMatchItem { + description: string + image: string + name: string + price: number +} + +export interface SerializedMatchPerks { + primaryStyle: number + secondaryStyle: number + selected: number[] +} + +export interface SerializedMatchStats { + assists: number + criticalStrike: number + deaths: number + dmgChamp: number + dmgObj: number + dmgTaken: number + doubleKills: number + gold: number + heal: number + kda: number | string + killingSpree: number + kills: number + kp: number + longestLiving: number + minions: number + pentaKills: number + quadraKills: number + realKda: number + towers: number + tripleKills: number + vision: number +} diff --git a/server-v2/app/Services/MatchService.ts b/server-v2/app/Services/MatchService.ts index b54302c..acd2f42 100644 --- a/server-v2/app/Services/MatchService.ts +++ b/server-v2/app/Services/MatchService.ts @@ -5,6 +5,8 @@ import Summoner from 'App/Models/Summoner' import Database from '@ioc:Adonis/Lucid/Database' import SummonerMatchlist from 'App/Models/SummonerMatchlist' import MatchParser from 'App/Parsers/MatchParser' +import BasicMatchSerializer from 'App/Serializers/BasicMatchSerializer' +import { SerializedMatch } from 'App/Serializers/SerializedTypes' class MatchService { /** @@ -76,21 +78,28 @@ class MatchService { /** * Fetch list of matches for a specific Summoner */ - public async getMatches(region: string, matchList: SummonerMatchlist[], summonerDB: Summoner) { + public async getMatches( + region: string, + matchList: SummonerMatchlist[], + summonerDB: Summoner + ): Promise { console.time('getMatches') - let matches: any[] = [] // Todo: add type of serialized matches here + let matches: SerializedMatch[] = [] const matchesToGetFromRiot: MatchlistDto = [] for (let i = 0; i < matchList.length; ++i) { const matchSaved = await summonerDB .related('matches') .query() .where('matchId', matchList[i].matchId) - .preload('match') + .preload('match', (preloader) => { + preloader.preload('blueTeam').preload('redTeam').preload('players') + }) .first() if (matchSaved) { // TODO: Serialize match from DB + put it in Redis + push it in "matches" + matches.push(BasicMatchSerializer.serializeOneMatch(matchSaved.match, summonerDB.puuid)) } else { matchesToGetFromRiot.push(matchList[i].matchId) } @@ -102,14 +111,20 @@ class MatchService { /* If we have to store some matches in the db */ if (matchesFromApi.length !== 0) { // Transform raw matches data - const parsedMatches = await MatchParser.parse(matchesFromApi) + const parsedMatches: any = await MatchParser.parse(matchesFromApi) // TODO: Serialize match from DB + put it in Redis + push it in "matches" + const serializedMatches = await BasicMatchSerializer.serialize( + parsedMatches, + summonerDB.puuid + ) + matches = [...matches, ...serializedMatches] } - // Todo: Sort and return "matches" - + // Todo: check if we need to sort here + matches.sort((a, b) => (a.date < b.date ? 1 : -1)) console.timeEnd('getMatches') + return matches } } diff --git a/server-v2/app/helpers.ts b/server-v2/app/helpers.ts index 1a637f8..e6602b5 100644 --- a/server-v2/app/helpers.ts +++ b/server-v2/app/helpers.ts @@ -1,4 +1,4 @@ -import MatchPlayer from './Models/MatchPlayer' +import { SerializedMatchTeamPlayer } from './Serializers/SerializedTypes' /** * All League of Legends regions used in Riot API @@ -104,7 +104,7 @@ export function getCurrentSeason(): number { * @param a first player * @param b second player */ -export function sortTeamByRole(a: MatchPlayer, b: MatchPlayer) { +export function sortTeamByRole(a: SerializedMatchTeamPlayer, b: SerializedMatchTeamPlayer) { const sortingArr = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'SUPPORT'] - return sortingArr.indexOf(a.teamPosition) - sortingArr.indexOf(b.teamPosition) + return sortingArr.indexOf(a.role) - sortingArr.indexOf(b.role) }