From 505bbd739d07a29226c3dd032459dfb2ae3652a2 Mon Sep 17 00:00:00 2001 From: Kalane Date: Sun, 12 Sep 2021 22:55:46 +0200 Subject: [PATCH 1/3] feat: start basic-match serializer --- .../app/Serializers/BasicMatchSerializer.ts | 67 +++++++++++++++++ server-v2/app/Serializers/MatchSerializer.ts | 39 ++++++++++ server-v2/app/Serializers/SerializedTypes.ts | 75 +++++++++++++++++++ server-v2/app/Services/MatchService.ts | 7 +- 4 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 server-v2/app/Serializers/BasicMatchSerializer.ts create mode 100644 server-v2/app/Serializers/MatchSerializer.ts create mode 100644 server-v2/app/Serializers/SerializedTypes.ts diff --git a/server-v2/app/Serializers/BasicMatchSerializer.ts b/server-v2/app/Serializers/BasicMatchSerializer.ts new file mode 100644 index 0000000..f50f74c --- /dev/null +++ b/server-v2/app/Serializers/BasicMatchSerializer.ts @@ -0,0 +1,67 @@ +import { getSeasonNumber } from 'App/helpers' +import Match from 'App/Models/Match' +import MatchPlayer from 'App/Models/MatchPlayer' +import MatchSerializer from './MatchSerializer' +import { SerializedMatch, SerializedMatchChampion } 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, + } + } + + public async serializeOneMatch(match: Match, puuid: string) { + // : Promise + + const players = await match.related('players').query() + const identity = players.find((p) => p.summonerPuuid === puuid)! + + // TODO: keep going here + return { + allyTeam: null, + champion: this.getChampion(identity.championId), + date: match.date, + enemyTeam: null, + firstSum: identity.summoner1Id, + matchId: match.id, + gamemode: match.gamemode, + items: null, + level: identity.champLevel, + map: match.map, + name: identity.summonerName, + perks: null, + region: match.region, + result: null, + role: identity.teamPosition, + season: getSeasonNumber(match.date), + secondSum: identity.summoner2Id, + stats: null, + 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..e5df77c --- /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: SerializedMatchItem[] + 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 + 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..8b733bb 100644 --- a/server-v2/app/Services/MatchService.ts +++ b/server-v2/app/Services/MatchService.ts @@ -5,6 +5,7 @@ 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' class MatchService { /** @@ -102,9 +103,13 @@ 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 + ) } // Todo: Sort and return "matches" From 55a3fec13d0b3b284053249966c6dcf400ba75d3 Mon Sep 17 00:00:00 2001 From: Kalane Date: Mon, 13 Sep 2021 13:50:04 +0200 Subject: [PATCH 2/3] feat: serializer for basic matches should be good (can't test right now) --- server-v2/app/Models/MatchPlayer.ts | 2 +- .../app/Serializers/BasicMatchSerializer.ts | 117 +++++++++++++++--- server-v2/app/Serializers/SerializedTypes.ts | 4 +- .../migrations/1631392766690_match_players.ts | 2 +- 4 files changed, 107 insertions(+), 18 deletions(-) diff --git a/server-v2/app/Models/MatchPlayer.ts b/server-v2/app/Models/MatchPlayer.ts index 74b9c8d..a9a2918 100644 --- a/server-v2/app/Models/MatchPlayer.ts +++ b/server-v2/app/Models/MatchPlayer.ts @@ -16,7 +16,7 @@ export default class MatchPlayer extends BaseModel { public participantId: number @column() - public summonerId: number + public summonerId: string @column() public summonerPuuid: string diff --git a/server-v2/app/Serializers/BasicMatchSerializer.ts b/server-v2/app/Serializers/BasicMatchSerializer.ts index f50f74c..1ceea4e 100644 --- a/server-v2/app/Serializers/BasicMatchSerializer.ts +++ b/server-v2/app/Serializers/BasicMatchSerializer.ts @@ -2,7 +2,14 @@ import { getSeasonNumber } from 'App/helpers' import Match from 'App/Models/Match' import MatchPlayer from 'App/Models/MatchPlayer' import MatchSerializer from './MatchSerializer' -import { SerializedMatch, SerializedMatchChampion } from './SerializedTypes' +import { + SerializedMatch, + SerializedMatchChampion, + SerializedMatchItem, + SerializedMatchPerks, + SerializedMatchStats, + SerializedMatchTeamPlayer, +} from './SerializedTypes' class BasicMatchSerializer extends MatchSerializer { /** @@ -24,43 +31,125 @@ class BasicMatchSerializer extends MatchSerializer { } } - public async serializeOneMatch(match: Match, puuid: string) { - // : Promise + 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)) + } + + 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 async serializeOneMatch(match: Match, puuid: string): Promise { const players = await match.related('players').query() const identity = players.find((p) => p.summonerPuuid === puuid)! - // TODO: keep going here + const allyTeamColor = identity.team === 100 ? 'blueTeam' : 'redTeam' + // const enemyTeamColor = allyTeamColor === 'blueTeam' ? 'redTeam' : 'blueTeam' + + const allyTeam = await match.related(allyTeamColor).query().firstOrFail() + // const enemyTeam = await match.related(enemyTeamColor).query().firstOrFail() + + const allyPlayers: MatchPlayer[] = [] + const enemyPlayers: MatchPlayer[] = [] + + for (const p of players) { + // TODO: remove Number() when Lazar push the updated migration + p.team === Number(allyTeam.color) ? allyPlayers.push(p) : enemyPlayers.push(p) + } + return { - allyTeam: null, + allyTeam: this.getTeamSummary(allyPlayers), champion: this.getChampion(identity.championId), date: match.date, - enemyTeam: null, + enemyTeam: this.getTeamSummary(enemyPlayers), firstSum: identity.summoner1Id, matchId: match.id, gamemode: match.gamemode, - items: null, + items: this.getItems(identity), level: identity.champLevel, map: match.map, name: identity.summonerName, - perks: null, + perks: this.getPerks(identity), region: match.region, - result: null, + result: allyTeam.result.toString(), // TODO: remove toString() when Lazar push the updated migration role: identity.teamPosition, season: getSeasonNumber(match.date), secondSum: identity.summoner2Id, - stats: null, + stats: this.getStats(identity), summonerId: identity.summonerId, summonerPuuid: puuid, time: match.gameDuration, } } - public async serialize(matches: Match[], puuid: string) { - // : Promise - + public async serialize(matches: Match[], puuid: string): Promise { await super.getContext() - return matches.map((match) => this.serializeOneMatch(match, puuid)) + return Promise.all(matches.map((match) => this.serializeOneMatch(match, puuid))) } } diff --git a/server-v2/app/Serializers/SerializedTypes.ts b/server-v2/app/Serializers/SerializedTypes.ts index e5df77c..06d1116 100644 --- a/server-v2/app/Serializers/SerializedTypes.ts +++ b/server-v2/app/Serializers/SerializedTypes.ts @@ -6,7 +6,7 @@ export interface SerializedMatch { firstSum: number matchId: string gamemode: number - items: SerializedMatchItem[] + items: Array level: number map: number name: string @@ -60,7 +60,7 @@ export interface SerializedMatchStats { doubleKills: number gold: number heal: number - kda: number + kda: number | string killingSpree: number kills: number kp: number diff --git a/server-v2/database/migrations/1631392766690_match_players.ts b/server-v2/database/migrations/1631392766690_match_players.ts index 360ebd2..8cea6a7 100644 --- a/server-v2/database/migrations/1631392766690_match_players.ts +++ b/server-v2/database/migrations/1631392766690_match_players.ts @@ -9,7 +9,7 @@ export default class MatchPlayers extends BaseSchema { table.string('match_id', 15).notNullable() table.integer('participant_id').notNullable() - table.integer('summoner_id').notNullable() + table.string('summoner_id', 63).notNullable() table.string('summoner_puuid', 78).notNullable() table.string('summoner_name', 16).notNullable() From da3eea25f91796fc3338e2e052effd748c58e349 Mon Sep 17 00:00:00 2001 From: Kalane Date: Mon, 13 Sep 2021 22:00:04 +0200 Subject: [PATCH 3/3] feat: matchService + serializer --- .../app/Serializers/BasicMatchSerializer.ts | 18 +++++++---------- server-v2/app/Services/MatchService.ts | 20 ++++++++++++++----- server-v2/app/helpers.ts | 6 +++--- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/server-v2/app/Serializers/BasicMatchSerializer.ts b/server-v2/app/Serializers/BasicMatchSerializer.ts index 1ceea4e..c2f297d 100644 --- a/server-v2/app/Serializers/BasicMatchSerializer.ts +++ b/server-v2/app/Serializers/BasicMatchSerializer.ts @@ -1,4 +1,4 @@ -import { getSeasonNumber } from 'App/helpers' +import { getSeasonNumber, sortTeamByRole } from 'App/helpers' import Match from 'App/Models/Match' import MatchPlayer from 'App/Models/MatchPlayer' import MatchSerializer from './MatchSerializer' @@ -41,7 +41,7 @@ class BasicMatchSerializer extends MatchSerializer { } protected getTeamSummary(players: MatchPlayer[]): SerializedMatchTeamPlayer[] { - return players.map((p) => this.getPlayerSummary(p)) + return players.map((p) => this.getPlayerSummary(p)).sort(sortTeamByRole) } protected getItems(player: MatchPlayer): Array { @@ -104,20 +104,16 @@ class BasicMatchSerializer extends MatchSerializer { } } - public async serializeOneMatch(match: Match, puuid: string): Promise { - const players = await match.related('players').query() - const identity = players.find((p) => p.summonerPuuid === puuid)! + public serializeOneMatch(match: Match, puuid: string): SerializedMatch { + const identity = match.players.find((p) => p.summonerPuuid === puuid)! const allyTeamColor = identity.team === 100 ? 'blueTeam' : 'redTeam' - // const enemyTeamColor = allyTeamColor === 'blueTeam' ? 'redTeam' : 'blueTeam' - - const allyTeam = await match.related(allyTeamColor).query().firstOrFail() - // const enemyTeam = await match.related(enemyTeamColor).query().firstOrFail() + const allyTeam = match[allyTeamColor] const allyPlayers: MatchPlayer[] = [] const enemyPlayers: MatchPlayer[] = [] - for (const p of players) { + 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) } @@ -149,7 +145,7 @@ class BasicMatchSerializer extends MatchSerializer { public async serialize(matches: Match[], puuid: string): Promise { await super.getContext() - return Promise.all(matches.map((match) => this.serializeOneMatch(match, puuid))) + return matches.map((match) => this.serializeOneMatch(match, puuid)) } } diff --git a/server-v2/app/Services/MatchService.ts b/server-v2/app/Services/MatchService.ts index 8b733bb..acd2f42 100644 --- a/server-v2/app/Services/MatchService.ts +++ b/server-v2/app/Services/MatchService.ts @@ -6,6 +6,7 @@ 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 { /** @@ -77,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) } @@ -110,11 +118,13 @@ class MatchService { 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) }