diff --git a/src/client/process.ts b/src/client/process.ts index bdb2b71..6df4ecc 100644 --- a/src/client/process.ts +++ b/src/client/process.ts @@ -1,15 +1,8 @@ import db from '../db/db' -import { - processWeaponReport, - populateWeaponSet, - processWeaponList -} from './process/processWeapon' -import { - populatePlayerSet, - processPlayerList, - processPlayerReport -} from './process/processPlayer' -import { getWeaponSet, getPlayerSet } from '../cache/cacheUtils' +const { count, max, sum } = db.fn +import client from '../cache/redis' +import { createWeaponJson } from './process/processWeapon' +import { createPlayerJson } from './process/processPlayer' /** * Starts the process of populating the REDIS database globally @@ -18,33 +11,12 @@ import { getWeaponSet, getPlayerSet } from '../cache/cacheUtils' async function processAll() { console.log('Starting data calculation...') const timeStart = new Date() - // Iterate through all servers - const servers = await db.selectFrom('server').selectAll().execute() - await populateAllSets() - const promises: Promise[] = [] - servers.forEach((server) => { - promises.push(processServer(server.id)) - }) - //Iterate through all weapons - const weapons = await getWeaponSet() - weapons.forEach((weapon) => { - promises.push(processWeapon(weapon)) - }) - //Iterate through all players - - const players = await getPlayerSet() - players.forEach((player) => { - promises.push(processPlayer(player)) - }) - - await Promise.all(promises) - //Generate lists now that we have all data - await processAllLists() + await Promise.all([processGlobalStats(), processServerStats()]) console.log( 'Data calculation finished. Took + ' + - Math.abs(new Date().getTime() - timeStart.getTime()) / 1000 + - ' seconds' + Math.abs(new Date().getTime() - timeStart.getTime()) / 1000 + + ' seconds' ) if (process.env.ENVIRONMENT == 'production') { return @@ -52,157 +24,204 @@ async function processAll() { setTimeout(processAll, 3600000) } -/** - * Populte redis of server lists and reports - * @param server - */ -async function processServer(server: number) { - /*const promises: Promise[] = [] - //TODO TO REMOVE OR RENAME - await processServerPlayers(server) - - //Iterate through all weapons present on this server - await populateWeaponSet(server) - const weapons = await cache.SMEMBERS(`servers:${server}:weapons`) - weapons.forEach((weapon) => { - promises.push(processWeaponReport(weapon, server)) - }) - - //Iterate through all players present on this server (and do more stuff on those players) - const players = await cache.SMEMBERS(`servers:${server}:players`) - players.forEach((player) => { - promises.push(processPlayer({ server, player })) - }) - await Promise.all(promises) - await processWeaponList(server)*/ -} - -/** - * Populate redis of player lists and reports - * @param param0 - */ -async function processPlayer(player: string) { - const promises: Promise[] = [] - //iterate through all servers - const servers = await db.selectFrom('server').selectAll().execute() - servers.forEach((server) => { - promises.push( - processPlayerReport(player, server.id), - //Iterate through all players on servers - getWeaponSet(server.id, player).then((weapons) => { - weapons.forEach((weapon) => { - promises.push(processPlayerReport(player, server.id, weapon)) - }) +async function processGlobalStats() { + let promises: Promise[] = [] + //Global kills + promises.push(db.selectFrom('kill') + .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance')]) + .execute().then(data => { + let transaction = client.multi() + data.forEach(({ kills, max_distance, total_distance }) => { + transaction = processGlobalData({ kills, max_distance, total_distance }, transaction) }) - ) - }) - //iterate through all weapons outside of servers - const weapons = await getWeaponSet(undefined, player) - weapons.forEach((weapon) => { - promises.push(processPlayerReport(player, undefined, weapon)) - }) - //global weapon data - promises.push(processPlayerReport(player)) - await Promise.all(promises) -} + return transaction.exec() + })) -async function processWeapon(weapon: string) { - const promises: Promise[] = [] - //iterate through all servers - const servers = await db.selectFrom('server').selectAll().execute() - servers.forEach((server) => { - promises.push( - processWeaponReport(weapon, server.id), - //Iterate through all players on servers - getPlayerSet(server.id, weapon).then((players) => { - players.forEach((player) => { - promises.push(processWeaponReport(weapon, server.id, player)) - }) + //Global weapon kill + promises.push(db.selectFrom('kill') + .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'cause_of_death']) + .groupBy('cause_of_death').execute().then(async (data) => { + let transaction = client.multi() + data.forEach(({ kills, max_distance, total_distance, cause_of_death }) => { + transaction = processWeaponData({ kills, max_distance, total_distance, cause_of_death }, transaction) }) - ) - }) - //iterate through all players outside of servers - const players = await getPlayerSet(undefined, weapon) - players.forEach((player) => { - promises.push(processWeaponReport(weapon, undefined, player)) - }) - //global weapon data - promises.push(processWeaponReport(weapon)) - await Promise.all(promises) -} + await transaction.exec() + })) -async function populateAllSets() { - const servers = await db.selectFrom('server').selectAll().execute() - await Promise.all([populatePlayerSet(), populateWeaponSet()]) - await (async () => { - const promises: Promise[] = [] - await getWeaponSet().then((weaponSet) => { - weaponSet.forEach((weapon: string) => { - promises.push(populatePlayerSet(undefined, weapon)) + //Player weapon kills + promises.push(db.selectFrom('kill') + .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'cause_of_death', 'attacker_id']) + .groupBy(['cause_of_death', 'attacker_id']).execute().then(async (data) => { + let transaction = client.multi() + data.forEach(({ kills, max_distance, total_distance, cause_of_death, attacker_id }) => { + transaction = processWeaponData({ kills, max_distance, total_distance, cause_of_death, attacker_id }, transaction) }) - }) - - await getPlayerSet().then((playerSet) => { - playerSet.forEach((player) => { - promises.push(populateWeaponSet(undefined, player)) + await transaction.exec() + })) + //Player weapon deaths + promises.push(db.selectFrom('kill').select([count('id').as('deaths'), 'victim_id', 'cause_of_death']) + .groupBy(['victim_id', 'cause_of_death']).execute().then(data => { + let transaction = client.multi() + data.forEach(({ deaths, victim_id, cause_of_death }) => { + transaction = transaction.HSET(`weapons:${cause_of_death}:players:${victim_id}`, "deaths", deaths.toString()) }) - }) - return Promise.all(promises) - })() + return transaction.exec() + })) - await (async () => { - const promises: Promise[] = servers.map((server) => - (async () => { - await Promise.all([ - populatePlayerSet(server.id), - populateWeaponSet(server.id) - ]) - const promises: Promise[] = [] - await getWeaponSet(server.id).then((weaponSet) => { - weaponSet.forEach((weapon: string) => { - promises.push(populatePlayerSet(server.id, weapon)) - }) - }) - await getPlayerSet(server.id).then((playerSet) => { - playerSet.forEach((player) => { - promises.push(populateWeaponSet(server.id, player)) - }) - }) - return Promise.all(promises) - })() - ) - await Promise.all(promises) - })() -} - -async function processAllLists() { - const promises: Promise[] = [] - promises.push(processPlayerList(), processWeaponList()) - promises.push( - ...(await getPlayerSet()).map((player) => - processWeaponList(undefined, player) - ), - ...(await ( - await getWeaponSet() - ).map((weapon) => processPlayerList(undefined, weapon))) - ) - const servers = await db.selectFrom('server').selectAll().execute() - promises.push( - ...servers.map(async ({ id }) => { - const promises: Promise[] = [] - promises.push( - processWeaponList(id), - processPlayerList(id), - ...(await getPlayerSet()).map((player) => - processWeaponList(id, player) - ), - ...(await ( - await getWeaponSet() - ).map((weapon) => processPlayerList(id, weapon))) - ) - await Promise.all(promises) + //Global player kills + promises.push(db.selectFrom('kill') + .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'attacker_id']) + .groupBy('attacker_id').execute().then(data => { + let transaction = client.multi() + data.forEach(({ kills, max_distance, total_distance, attacker_id }) => { + processPlayerData({ kills, max_distance, total_distance, attacker_id }, transaction) + }) + return transaction.exec() }) ) + //Global player deaths + promises.push(db.selectFrom('kill').select([count('id').as('deaths'), 'victim_id']) + .groupBy('victim_id').execute().then(data => { + let transaction = client.multi() + data.forEach(({ deaths, victim_id }) => { + transaction = transaction.HSET('players:' + victim_id, "deaths", deaths.toString()) + }) + return transaction.exec() + })) + + await Promise.all(promises) + promises = []; + promises.push(createWeaponJson()); + promises.push(createPlayerJson()); + (await client.sMembers('players')).forEach((player: string) => { + promises.push(createWeaponJson(undefined, player)) + }); + (await client.sMembers('weapons')).forEach((weapon: string) => { + promises.push(createPlayerJson(undefined, weapon)) + }); await Promise.all(promises) } + +async function processServerStats() { + let promises: Promise[] = [] + //Server global kills + promises.push(db.selectFrom('kill') + .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'server']) + .groupBy(['server']).execute().then(data => { + let transaction = client.multi() + data.forEach(({ kills, max_distance, total_distance, server }) => { + transaction = processGlobalData({ kills, max_distance, total_distance, server }, transaction) + }) + return transaction.exec() + })) + //Server weapon kills + promises.push(db.selectFrom('kill') + .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'cause_of_death', 'server']) + .groupBy(['cause_of_death', 'server']).execute().then(data => { + let transaction = client.multi() + data.forEach(({ kills, max_distance, total_distance, cause_of_death, server }) => { + transaction = processWeaponData({ kills, max_distance, total_distance, cause_of_death, server }, transaction) + }) + return transaction.exec() + })) + + //Server Player weapon kills + promises.push(db.selectFrom('kill') + .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'cause_of_death', 'attacker_id', 'server']) + .groupBy(['cause_of_death', 'attacker_id', 'server']).execute().then(data => { + let transaction = client.multi() + data.forEach(({ kills, max_distance, total_distance, cause_of_death, attacker_id, server }) => { + transaction = processWeaponData({ kills, max_distance, total_distance, cause_of_death, attacker_id, server }, transaction) + }) + return transaction.exec() + })) + //Server Player weapon deaths + promises.push(db.selectFrom('kill').select([count('id').as('deaths'), 'victim_id', 'cause_of_death', 'server']) + .groupBy(['victim_id', 'cause_of_death', 'server']).execute().then(data => { + let transaction = client.multi() + data.forEach(({ deaths, victim_id, cause_of_death, server }) => { + transaction = transaction.HSET(`servers:${server}:weapons:${cause_of_death}:players:${victim_id}`, "deaths", deaths.toString()) + }) + return transaction.exec() + })) + + //server player kills + promises.push(db.selectFrom('kill') + .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'attacker_id', 'server']) + .groupBy(['attacker_id', 'server']).execute().then(data => { + let transaction = client.multi() + data.forEach(({ kills, max_distance, total_distance, attacker_id, server }) => { + transaction = processPlayerData({ kills, max_distance, total_distance, attacker_id, server }, transaction) + }) + return transaction.exec() + }) + ) + + //server player deaths + promises.push(db.selectFrom('kill').select([count('id').as('deaths'), 'victim_id', 'server']) + .groupBy(['victim_id', 'server']).execute().then(data => { + let transaction = client.multi() + data.forEach(({ deaths, victim_id, server }) => { + transaction = transaction.HSET('servers:' + server + ':players:' + victim_id, "deaths", deaths.toString()) + }) + return transaction.exec() + })) + + await Promise.all(promises) + promises = []; + ((await client.sMembers('servers')).forEach((server: string) => { + if (isNaN(Number(server))) return + promises.push(createWeaponJson(Number(server))); + promises.push(createPlayerJson(Number(server))); + promises.push(client.sMembers('servers:' + server + ':players').then(data => { + data.forEach((player: string) => { + promises.push(createWeaponJson(Number(server), player)) + }) + })) + promises.push(client.sMembers('servers:' + server + 'weapons').then(data => { + data.forEach((weapon: string) => { + promises.push(createPlayerJson(Number(server), weapon)) + }) + })) + })); + + await Promise.all(promises) +} + +function processGlobalData({ kills, max_distance, total_distance, server }: { kills: string | number | bigint, max_distance: number, total_distance: string | number | bigint, server?: number }, transaction: any) { + const serverPrefix = server ? `servers:${server}:` : '' + const cacheLocation = serverPrefix + 'global' + transaction = transaction.HSET(cacheLocation, "total_distance", total_distance.toString()) + transaction = transaction.HSET(cacheLocation, "max_distance", max_distance.toString()) + transaction = transaction.HSET(cacheLocation, "kills", kills.toString()) + if (server) { + transaction = transaction.SADD('servers', server.toString()) + } + + return transaction +} + +function processWeaponData({ kills, max_distance, total_distance, cause_of_death, attacker_id, server }: { kills: string | number | bigint, max_distance: number, total_distance: string | number | bigint, cause_of_death: string, attacker_id?: string, server?: number }, transaction: any) { + const serverPrefix = server ? `servers:${server}:` : '' + const playerPrefix = attacker_id ? `players:${attacker_id}:` : '' + const cacheLocation = serverPrefix + playerPrefix + `weapons:` + cause_of_death + transaction = transaction.HSET(cacheLocation, "total_distance", total_distance.toString()) + transaction = transaction.HSET(cacheLocation, "max_distance", max_distance.toString()) + transaction = transaction.HSET(cacheLocation, "kills", kills.toString()) + transaction = transaction.SADD(serverPrefix + playerPrefix + `weapons`, cause_of_death.toString()) + return transaction +} + +function processPlayerData({ kills, max_distance, total_distance, attacker_id, cause_of_death, server }: { kills: string | number | bigint, max_distance: number, total_distance: string | number | bigint, attacker_id: string, server?: number, cause_of_death?: string }, transaction: any) { + const serverPrefix = server ? `servers:${server}:` : '' + const weaponPrefix = cause_of_death ? `weapons:${cause_of_death}:` : '' + const cacheLocation = serverPrefix + weaponPrefix + `players:` + attacker_id + + transaction = transaction.HSET(cacheLocation, "total_distance", total_distance.toString()) + transaction = transaction.HSET(cacheLocation, "max_distance", max_distance.toString()) + transaction = transaction.HSET(cacheLocation, "kills", kills.toString()) + transaction = transaction.SADD(serverPrefix + weaponPrefix + `players`, attacker_id.toString()) + return transaction +} + export default processAll diff --git a/src/client/process/processPlayer.ts b/src/client/process/processPlayer.ts index 94d5039..4e8c92c 100644 --- a/src/client/process/processPlayer.ts +++ b/src/client/process/processPlayer.ts @@ -1,208 +1,8 @@ /** * This file contains all functions related to population of the REDIS database for players. */ -import db from '../../db/db' import cache from '../../cache/redis' import { getPlayerReport, getPlayerSet } from '../../cache/cacheUtils' -import { sql } from 'kysely' -const { count, max, avg } = db.fn - -/** - * Populates the set of players (just a list with player IDS) - * - * Inputs `POSTGRESQL` - * - * Outputs `SET` `[servers:{serverId}:][weapons:{weaponId}:]players` - * @param server optional server filter - * @param weapon optional weapon filter - * @returns - */ -export async function populatePlayerSet(server?: number, weapon?: string) { - const serverPrefix = !isNaN(Number(server?.toString())) - ? `servers:${server}:` - : '' - const weaponPrefix = weapon ? `weapons:${weapon}:` : '' - const cacheLocation = serverPrefix + weaponPrefix + `players` - const last_entry = Number(await cache.GET(cacheLocation + ':last_entry')) || 0 - - //processKills - let query = db - .selectFrom('kill') - .select([ - 'attacker_id', - db - .selectFrom('kill') - .select(max('kill.id').as('last_entry')) - .as('last_entry') - ]) - .groupBy('attacker_id') - .where('kill.id', '>', last_entry) - if (server) { - query = query.where('server', '=', server) - } - if (weapon) { - query = query.where('cause_of_death', '=', weapon) - } - let newData = await query.execute() - if (newData.length == 0) { - return - } - const promises: Promise[] = [] - newData.forEach(({ attacker_id }) => { - if (attacker_id == 'last_entry' || attacker_id == 'processedList') { - console.error('attacker_id cannot be ' + attacker_id) - return - } - promises.push(cache.SADD(cacheLocation, attacker_id)) - }) - - //processDeaths - let query2 = db - .selectFrom('kill') - .select([ - sql`DISTINCT(victim_id)`.as('victim_id'), - db - .selectFrom('kill') - .select(max('kill.id').as('last_entry')) - .as('last_entry') - ]) - .where('kill.id', '>', last_entry) - if (server) { - query2 = query2.where('server', '=', server) - } - if (weapon) { - query2 = query2.where('cause_of_death', '=', weapon) - } - const newData2 = await query2.execute() - newData2.forEach(({ victim_id }) => { - if (victim_id == 'last_entry' || victim_id == 'processedList') { - console.error('victim_id cannot be ' + victim_id) - return - } - promises.push(cache.SADD(cacheLocation, victim_id)) - }) - newData = await query.execute() - promises.push( - cache.SET(cacheLocation + ':last_entry', newData2[0].last_entry) - ) - await Promise.all(promises) -} - -/** - * Processes player kills with optional server and/or weapon filter. - * Does not processes deaths from weapons. - * - * Inputs `POSTGRESQL` - * - * Outputs `HASH` `[servers:{serverId}:][weapon:{weaponId}:]players:{playerId}` - * @param player weapon to generate the report for - * @param server optional server filter - * @param weapon optional player filter - */ - -export async function processPlayerReport( - player: string, - server?: number, - weapon?: string -) { - if (player == 'last_entry' || player == 'processedList') { - console.error('Player cannot be ' + player) - return - } - const serverPrefix = server ? `servers:${server}:` : '' - const weaponPrefix = weapon ? `weapons:${weapon}:` : '' - const cacheLocation = serverPrefix + weaponPrefix + `players:` + player - const last_entry = Number(await cache.HGET(cacheLocation, 'last_entry')) || 0 - let query = db - .selectFrom('kill') - .select([ - sql`percentile_disc(0) WITHIN GROUP (ORDER BY attacker_name) FILTER (where attacker_id = ${player})`.as( - 'username' - ), - count('id') - .filterWhere('attacker_id', '=', player) - .filterWhereRef('attacker_id', '!=', 'victim_id') - .as('kills'), - count('id').filterWhere('victim_id', '=', player).as('deaths'), - avg('kill.distance') - .filterWhere('attacker_id', '=', player) - .as('avg_kill_distance'), - max('kill.distance') - .filterWhere('attacker_id', '=', player) - .as('max_kill_distance'), - db - .selectFrom('kill') - .select(max('kill.id').as('last_entry')) - .as('last_entry') - ]) - .where((qb) => - qb.where('attacker_id', '=', player).orWhere('victim_id', '=', player) - ) - .where('kill.id', '>', last_entry) - if (server) { - query = query.where('server', '=', server) - } - if (weapon) { - query = query.where('cause_of_death', '=', weapon) - } - const newData = await query.execute() - if (newData.length == 0) { - return - } - const promises: Promise[] = [] - newData.forEach( - ({ kills, avg_kill_distance, max_kill_distance, deaths, username }) => { - promises.push( - (async () => { - if (!username) { - await cache.HSET( - cacheLocation, - 'username', - (await cache.HGET(`players:` + player, 'username')) || '' - ) - } else { - await cache.HSET(cacheLocation, 'username', username) - } - })() - ) - promises.push( - processAvg(cacheLocation, 'avg_kill_distance', { - newkills: kills || 0, - newavg: avg_kill_distance || 0 - }).then((e) => cache.HINCRBY(cacheLocation, 'kills', kills || 0)), - cache.HINCRBY(cacheLocation, 'deaths', deaths || 0), - - processMax(cacheLocation, 'max_kill_distance', max_kill_distance) - ) - } - ) - - promises.push(cache.HSET(cacheLocation, 'last_entry', newData[0].last_entry)) - await Promise.all(promises) -} - -async function processAvg( - cacheLocation: string, - key: 'avg_kill_distance', - { newkills, newavg }: { newkills: number; newavg: number } -) { - const oldKills = Number(await cache.HGET(cacheLocation, 'kills')) || 0 - const oldaverage = Number(await cache.HGET(cacheLocation, key)) || 0 - const totalkills = Number(oldKills) + Number(newkills) - const newAverage = - (oldKills / totalkills) * oldaverage + (newkills / totalkills) * newavg || 0 - await cache.HSET(cacheLocation, key, newAverage) -} - -async function processMax( - cacheLocation: string, - key: 'max_kill_distance', - newmax: number -) { - const oldMax = Number(await cache.HGET(cacheLocation, key)) || 0 - if (oldMax < newmax || oldMax == 0) - await cache.HSET(cacheLocation, key, newmax || 0) -} /** * Combines player reports in a readable json string. @@ -216,7 +16,7 @@ async function processMax( * @param weapon optional player filter * @returns */ -export async function processPlayerList(server?: number, weapon?: string) { +export async function createPlayerJson(server?: number, weapon?: string) { const serverPrefix = server ? `servers:${server}:` : '' const weaponPrefix = weapon ? `weapons:${weapon}:` : '' const cacheLocation = serverPrefix + weaponPrefix + `players` diff --git a/src/client/process/processWeapon.ts b/src/client/process/processWeapon.ts index 0a88773..2cf87d8 100644 --- a/src/client/process/processWeapon.ts +++ b/src/client/process/processWeapon.ts @@ -1,149 +1,8 @@ /** * This file contains all functions related to population of the REDIS database for weapons. */ -import db from '../../db/db' import cache from '../../cache/redis' import { getWeaponReport } from '../../cache/cacheUtils' -const { count, max, avg } = db.fn - -/** - * Populates the set of weapons (just a list with weapon names) - * - * Inputs `POSTGRESQL` - * - * Outputs `SET` `[servers:{serverId}:][players:{playerId}:]weapons` - * @param server optional server filter - * @param player optional player filter - * @returns - */ -export async function populateWeaponSet(server?: number, player?: string) { - const serverPrefix = server ? `servers:${server}:` : '' - const playerPrefix = player ? `players:${player}:` : '' - const cacheLocation = serverPrefix + playerPrefix + `weapons` - const last_entry = Number(await cache.GET(cacheLocation + ':last_entry')) || 0 - let query = db - .selectFrom('kill') - .select([ - 'cause_of_death', - db - .selectFrom('kill') - .select(max('kill.id').as('last_entry')) - .as('last_entry') - ]) - .groupBy('cause_of_death') - .where('kill.id', '>', last_entry) - if (server) { - query = query.where('server', '=', server) - } - if (player) { - query = query - .where('attacker_id', '=', player) - .orWhere('victim_id', '=', player) - } - const newData = await query.execute() - if (newData.length == 0) { - return - } - const promises: Promise[] = [] - newData.forEach(({ cause_of_death }) => { - if (cause_of_death == 'last_entry' || cause_of_death == 'processedList') { - console.error('cause_of_death cannot be ' + cause_of_death) - return - } - promises.push(cache.SADD(cacheLocation, cause_of_death)) - }) - promises.push(cache.SET(cacheLocation + ':last_entry', newData[0].last_entry)) - await Promise.all(promises) -} - -/** - * Processes weapon kills with optional server and/or player filter. - * Does not processes deaths from weapons. - * - * Inputs `POSTGRESQL` - * - * Outputs `HASH` `[servers:{serverId}:][players:{playerId}:]weapons:{weaponId}` - * @param weapon weapon to generate the report for - * @param server optional server filter - * @param player optional player filter - */ - -export async function processWeaponReport( - weapon: string, - server?: number, - player?: string -) { - if (weapon == 'last_entry' || weapon == 'processedList') { - console.error('Weapon cannot be ' + weapon) - return - } - const serverPrefix = server ? `servers:${server}:` : '' - const playerPrefix = player ? `players:${player}:` : '' - const cacheLocation = serverPrefix + playerPrefix + `weapons:` + weapon - const last_entry = Number(await cache.HGET(cacheLocation, 'last_entry')) || 0 - let query = db - .selectFrom('kill') - .select([ - count('id').as('kills'), - avg('kill.distance').as('avg_kill_distance'), - max('kill.distance').as('max_kill_distance'), - db - .selectFrom('kill') - .select(max('kill.id').as('last_entry')) - .as('last_entry') - ]) - .where('cause_of_death', '=', weapon) - .whereRef('attacker_id', '!=', 'victim_id') - .where('kill.id', '>', last_entry) - .groupBy('cause_of_death') - if (server) { - query = query.where('server', '=', server) - } - if (player) { - query = query.where('attacker_id', '=', player) - } - const newData = await query.execute() - - if (newData.length == 0) { - return - } - const promises: Promise[] = [] - newData.forEach(({ kills, avg_kill_distance, max_kill_distance }) => { - promises.push( - processAvg(cacheLocation, 'avg_kill_distance', { - newkills: kills, - newavg: avg_kill_distance - }).then((e) => cache.HINCRBY(cacheLocation, 'kills', kills)), - processMax(cacheLocation, 'max_kill_distance', max_kill_distance) - ) - }) - promises.push(cache.HSET(cacheLocation, 'last_entry', newData[0].last_entry)) - await Promise.all(promises) -} - -async function processAvg( - cacheLocation: string, - key: 'avg_kill_distance', - { newkills, newavg }: { newkills: number; newavg: number } -) { - const oldKills = Number(await cache.HGET(cacheLocation, 'kills')) || 0 - const oldaverage = Number(await cache.HGET(cacheLocation, key)) | 0 - const totalkills = oldKills + newkills - const newAverage = - (oldKills / totalkills) * oldaverage + (newkills / totalkills) * newavg - await cache.HSET(cacheLocation, key, newAverage) -} - -async function processMax( - cacheLocation: string, - key: 'max_kill_distance', - newmax: number -) { - const oldMax = Number(await cache.HGET(cacheLocation, key)) || -1 - newmax = newmax || 0 - if (!oldMax || !newmax) console.log(oldMax, newmax) - if (oldMax < newmax) await cache.HSET(cacheLocation, key, newmax) -} /** * Combines weapons reports in a readable json string. @@ -157,7 +16,7 @@ async function processMax( * @param player optional player filter * @returns */ -export async function processWeaponList(server?: number, player?: string) { +export async function createWeaponJson(server?: number, player?: string) { const serverPrefix = server ? `servers:${server}:` : '' const playerPrefix = player ? `players:${player}:` : '' const cacheLocation = serverPrefix + playerPrefix + `weapons`