ddstats-server/libs/database/wrapper.js

490 lines
12 KiB
JavaScript

import { sqlite, skinDB } 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 {string} region Which region should be included (Global, Europe, Asia, SA, NA, Africa, ME, OLD, Other)
* @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(type, region, start, end) {
let output = []
let leaderboard
if(region == "Global") {
leaderboard = sqlite.prepare(`
SELECT rank, region, player, points FROM points WHERE type = ? AND rank >= ? AND rank <= ? ORDER BY rank`)
.all(type, start, end)
}
else {
leaderboard = sqlite.prepare(`
SELECT rank, region, player, points FROM points WHERE type = ? AND region = ? ORDER BY rank ASC LIMIT ?, ${end}`)
.all(type, region, start - 1)
}
let rank = 1
for (const entry of leaderboard) {
let flag = skinDB.prepare(`SELECT flag FROM skindata WHERE player = ?`).get(entry.player)?.flag ?? "default"
output.push({ rank: rank, global: entry.rank, player: entry.player, points: entry.points, region: entry.region, flag: flag })
++i
}
return output
}
/**
* 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
}
/*
Europe
/ RUS - Russia
/ GER - Germany
/ NLD - Netherlands
/ TUR - Turkey
/ POL - Poland
/ FRA - France
Asia
/ IND - INDIA
/ SGP - Singapore
/ CHN - China
/ JAP - Japan
/ KOR - South Korea
South America
/ COL - Columbia
/ ARG - Argentina
/ CHL - Chile
/ BRA - Brazil
North America
/ CAN - Cananda
/ USA - USA
Africa
/ ZAF - South Africa
Middle east
/ KSA/UAE/IRN
Others
/ CRI - Costa Rica
/ AUS - Australia
/ OLD ranks (NA/EU)
/ UNK - Some bugged ranks
*/
export function mapToRegion(srv) {
if(srv == "RUS" || srv == "GER" || srv == "NLD" || srv == "TUR" || srv == "POL" || srv == "FRA")
return "Europe"
if(srv == "IND" || srv == "SGP" || srv == "CHN" || srv == "JAP" || srv == "KOR")
return "Asia"
if(srv == "COL" || srv == "ARG" || srv == "CHL" || srv == "BRA")
return "SA"
if(srv == "CAN" || srv == "USA")
return "NA"
if(srv == "ZAF")
return "Africa"
/* Middle east */
if(srv == "KSA" || srv == "UAE" || srv == "IRN")
return "ME"
if(srv == "")
return "OLD"
return "Other"
}
export function playerServer(player) {
/* This can be undefined if a player has changed their name */
const server = sqlite.prepare(
`SELECT server, COUNT(server) as a FROM rankings WHERE player = ? GROUP BY server ORDER BY a DESC`)
.get(player)?.server ?? ""
return server
}
export default {
playerExists,
finishedMaps,
unfinishedMaps,
player,
leaderboardPoints,
map,
graphMap,
mapCategory,
allMaps,
mapExists,
searchMap,
leaderboardRace,
leaderboardTeamrace,
categoryExists,
mapToRegion,
playerServer,
}