import { sqlite } from './init.js' import { simpleSanitize } from './searcher.js' /** * This function checks if a player exists * * @param {string} player The player to check * @returns {boolean} Returns a boolean */ export function playerExists(player) { const exists = sqlite.prepare(`SELECT * FROM points WHERE player = ? LIMIT 1`).get(player) if(exists) return true else return false } /** * This function checks if a map exists * * @param {string} player The map to check * @returns {boolean} Returns a boolean */ export function mapExists(map) { const exists = sqlite.prepare(`SELECT * FROM maps WHERE map = ? LIMIT 1`).get(map) if(exists) return true else return false } /** * This function checks if a map category exists * * @param {string} player The category to check * @returns {boolean} Returns a boolean */ export function categoryExists(category) { const exists = sqlite.prepare(`SELECT category FROM maps WHERE category = ? LIMIT 1`).get(category) if(exists) return true else return false } /** * This function returns all data pertaining a certain player * * @param {string} player The player to fetch * @returns {object} An object containing the players data */ export function player(player) { /* Misc */ const firstFinish = sqlite.prepare(`SELECT map, time, date, server FROM race WHERE player = ? ORDER BY date ASC LIMIT 1`).get(player) if(!firstFinish) return undefined /* Points */ let points = {} let rank = {} const pointsData = sqlite.prepare(`SELECT * FROM points WHERE player = ?`) for (const pointsType of pointsData.iterate(player)) { rank[pointsType.type] = pointsType.rank } for (const pointsType of pointsData.iterate(player)) { points[pointsType.type] = pointsType.points } return { player, firstFinish, points, rank, } } /** * This function returns all data pertaining a certain map * * @param {string} map The map to fetch * @returns {object} An object containing map data */ export function map(map) { const a = sqlite.prepare(` SELECT * FROM maps WHERE map = ? `).get(map) return prettyifyMap(a) } export function mapCategory(category) { let output = [] const maps = sqlite.prepare(` SELECT * FROM maps WHERE category = ?`).all(category) if(!maps) return undefined for(const map of maps) { output.push(prettyifyMap(map)) } return output } /** * This function returns all data pertaining to all maps * @returns {array} An array contaning all map objects */ export function allMaps() { let output = [] const maps = sqlite.prepare(` SELECT * FROM maps`).all() for(const map of maps) { output.push(prettyifyMap(map)) } return output } /** * This function returns all data pertaining a certain map * * @param {string} map The map to fetch * @returns {object} An object containing map data */ export function prettyifyMap(a) { if(!a) return undefined let output = { map: a.map, category: a.category, points: a.points, stars: a.stars, release: a.release, mappers: a.mapper.split(" & "), times: { average: a.avgTime, median: a.medianTime, topTime: a.topTime, topTimeTeam: (a.topTeamTime != -1) ? a.topTeamTime : undefined, }, finishes: { total: a.finishesTotal, team: a.finishesTeam, unique: a.finishesUnique, } } return output } /** * This function returns the race leaderboard for a map * * @param {string} map The map to check * @param {number} start At which rank the leaderboard should begin * @param {number} end At which rank the leaderboard should end * @returns {array} An array containing the leaderboard */ export function leaderboardRace(map, start, end) { const leaderboard = sqlite.prepare(` SELECT rank, time, date, player, server FROM rankings WHERE map = ? LIMIT ?, ?`) .all(map, start -1, end) return leaderboard } /** * This function returns the teamrace leaderboard for a map * * @param {string} map The map to check * @param {number} start At which rank the leaderboard should begin * @param {number} end At which rank the leaderboard should end * @returns {array} An array containing the leaderboard */ export function leaderboardTeamrace(map, start, end) { // TODO: Optimize array creation of players let leaderboard = [] const a = sqlite.prepare(` SELECT teamrank, time, date, player, server FROM teamrankings WHERE map = ? AND teamrank >= ? AND teamrank <= ? GROUP BY teamrank`) for(const teamrank of a.iterate(map, start, end)) { let players = [] const b = sqlite.prepare(`SELECT player FROM teamrankings WHERE map = ? AND teamrank = ?`) if(!b) return undefined for(const player of b.iterate(map, teamrank.teamrank)) { players.push(player.player) } leaderboard.push({ teamrank: teamrank.teamrank, time: teamrank.time, date: teamrank.date, server: teamrank.server, players: players, }) } return leaderboard } /** * This function returns the points leaderboard for a specific type * (points, pointsRank, pointsTeam, pointsThisWeek, pointsThisMonth) * * @param {string} type Which type of points to fetch * @param {number} start At which rank the leaderboard should begin * @param {number} end At which rank the leaderboard should end * @returns {array} An array containing the leaderboard */ export function leaderboardPoints(map, start, end) { const leaderboard = sqlite.prepare(` SELECT rank, player, points FROM points WHERE type = ? AND rank >= ? AND rank <= ? ORDER BY rank`) .all(type, start, end) return leaderboard } /** * This function returns all finished maps by a specific player * togheter with their respective rank, teamrank, amount of finishes. * Finishes are grouped by map category (Novice, Brutal) * * @param {string} player The player to check * @returns {object} An object containing all finishes grouped by category */ export function finishedMaps(player) { const finishesStmt = sqlite.prepare( ` SELECT a.map, a.category, a.points, a.rank, b.teamrank, a.finishes FROM rankings AS a LEFT OUTER JOIN teamrankings AS b ON a.player = b.player AND a.category = b.category AND a.map = b.map WHERE a.player = ? `) if(!finishesStmt) return undefined let finishes = { Novice: [], Moderate: [], Brutal: [], Insane: [], Dummy: [], DDmaX: [], Oldschool: [], Solo: [], Race: [], Fun: [] } for (const finish of finishesStmt.iterate(player)) { finishes[finish.category].push(finish) } return finishes } /** * This function returns all unfinished maps by a specific player * togheter with category, points, finishTotal and medianTime. * Maps are grouped by the map category (Novice, Brutal) * * @param {string} player The player to check * @returns {object} An object containing all unfinished maps */ export function unfinishedMaps(player) { const maps = sqlite.prepare( ` SELECT a.map, a.category, a.points, b.finishesTotal, b.medianTime FROM (SELECT category, map, points FROM maps WHERE map NOT IN (SELECT map FROM rankings WHERE player = ? )) AS a JOIN maps AS b ON a.category = b.category AND a.map = b.map ORDER BY b.category ASC; `) if(!maps) return undefined let unfinished = { Novice: [], Moderate: [], Brutal: [], Insane: [], Dummy: [], DDmaX: [], Oldschool: [], Solo: [], Race: [], Fun: [] } for (const map of maps.iterate(player)) { unfinished[map.category].push(map) } return unfinished } export function graphMap(map) { const finishes = sqlite.prepare(`SELECT * FROM graphRecordCache WHERE map = ? ORDER BY date`) if(!finishes) return undefined let array = [] for (const record of finishes.iterate(map)) array.push({ t: record.date, y: record.time, player: record.player}) return array } /** * This searches the DB for maps * * @param {string} query The query to search for * @param {array} categories Which categories to be searched ["Novice", "Insane"] * @param {array} stars Only include maps with a specific amount of stars [1, 5] * @param {string} sortBy What to sort after * @param {string} order "ASC" | "DESC" * * @returns {array} Returns an array of all maps found */ export function searchMap(query, categories, stars, sortBy, order) { const categoriesJoined = categories.join(',') const starsJoined = stars.join(',') let output = [] const maps = sqlite.prepare(` SELECT * FROM maps WHERE map LIKE '%${query}%' AND ',' || ? || ',' LIKE '%,' || category || ',%' AND ',' || ? || ',' LIKE '%,' || stars || ',%' ORDER BY ? ${simpleSanitize(order)}`) .all(categoriesJoined, starsJoined, sortBy) for(const map of maps) { output.push(prettyifyMap(map)) } return output } export default { playerExists, finishedMaps, unfinishedMaps, player, map, graphMap, mapCategory, allMaps, mapExists, searchMap, leaderboardRace, leaderboardTeamrace, categoryExists, }