From 9d53d8299799155adad5a19df4d6a1931972bb3e Mon Sep 17 00:00:00 2001 From: Holly Date: Sat, 7 Oct 2023 13:09:10 +0100 Subject: [PATCH] DB access cleanup --- server/LoginProcess.ts | 4 +- server/commands/Ranking.ts | 8 +-- server/enums/Mode.ts | 7 ++ .../enums/{RankingModes.ts => RankingMode.ts} | 2 +- server/objects/Shared.ts | 3 + server/objects/User.ts | 33 +++------ server/objects/database/UserModeInfo.ts | 24 +++++++ server/packets/StatusUpdate.ts | 13 ++-- server/repos/UserInfoRepository.ts | 12 ++-- server/repos/UserModesInfoRepository.ts | 72 +++++++++++++++++++ 10 files changed, 138 insertions(+), 40 deletions(-) create mode 100644 server/enums/Mode.ts rename server/enums/{RankingModes.ts => RankingMode.ts} (56%) create mode 100644 server/objects/database/UserModeInfo.ts create mode 100644 server/repos/UserModesInfoRepository.ts diff --git a/server/LoginProcess.ts b/server/LoginProcess.ts index 8767656..6a729be 100644 --- a/server/LoginProcess.ts +++ b/server/LoginProcess.ts @@ -37,7 +37,7 @@ enum LoginResult { function TestLogin(loginInfo:LoginInfo, shared:Shared) { return new Promise(async (resolve, reject) => { - const userDBData = await shared.userInfoRepository.getByUsername(loginInfo.username); + const userDBData = await shared.userInfoRepository.selectByUsername(loginInfo.username); // Make sure a user was found in the database if (userDBData == null) return resolve(LoginResult.INCORRECT); @@ -123,7 +123,7 @@ export default async function LoginProcess(req:IncomingMessage, res:ServerRespon } // Get information about the user from the database - const userInfo = await shared.userInfoRepository.getByUsername(loginInfo.username); + const userInfo = await shared.userInfoRepository.selectByUsername(loginInfo.username); if (userInfo == null) { return; } diff --git a/server/commands/Ranking.ts b/server/commands/Ranking.ts index f71742c..947c951 100644 --- a/server/commands/Ranking.ts +++ b/server/commands/Ranking.ts @@ -1,6 +1,6 @@ import Channel from "../objects/Channel"; import User from "../objects/User"; -import { RankingModes } from "../enums/RankingModes"; +import { RankingMode } from "../enums/RankingMode"; import BaseCommand from "./BaseCommand"; export default class RankingCommand extends BaseCommand { @@ -18,15 +18,15 @@ export default class RankingCommand extends BaseCommand { switch (args[0].toLowerCase()) { case "pp": - sender.rankingMode = RankingModes.PP; + sender.rankingMode = RankingMode.PP; channel.SendBotMessage("Set ranking mode to pp."); break; case "score": - sender.rankingMode = RankingModes.RANKED_SCORE; + sender.rankingMode = RankingMode.RANKED_SCORE; channel.SendBotMessage("Set ranking mode to score."); break; case "acc": - sender.rankingMode = RankingModes.AVG_ACCURACY; + sender.rankingMode = RankingMode.AVG_ACCURACY; channel.SendBotMessage("Set ranking mode to accuracy."); break; } diff --git a/server/enums/Mode.ts b/server/enums/Mode.ts new file mode 100644 index 0000000..45b7ceb --- /dev/null +++ b/server/enums/Mode.ts @@ -0,0 +1,7 @@ +export enum Mode { + Unknown = -1, + Osu, + Taiko, + Catch, + Mania +} \ No newline at end of file diff --git a/server/enums/RankingModes.ts b/server/enums/RankingMode.ts similarity index 56% rename from server/enums/RankingModes.ts rename to server/enums/RankingMode.ts index 740b6e4..192bcfe 100644 --- a/server/enums/RankingModes.ts +++ b/server/enums/RankingMode.ts @@ -1,4 +1,4 @@ -export enum RankingModes { +export enum RankingMode { PP, RANKED_SCORE, AVG_ACCURACY diff --git a/server/objects/Shared.ts b/server/objects/Shared.ts index 8703fa5..7f1f4ca 100644 --- a/server/objects/Shared.ts +++ b/server/objects/Shared.ts @@ -12,6 +12,7 @@ import Bot from "../Bot"; import { ConsoleHelper } from "../../ConsoleHelper"; import UserInfoRepository from "../repos/UserInfoRepository"; import { Permissions } from "../enums/Permissions"; +import UserModesInfoRepository from "../repos/UserModesInfoRepository"; export default class Shared { public readonly chatManager:ChatManager; @@ -24,6 +25,7 @@ export default class Shared { public readonly bot:Bot; public readonly userInfoRepository:UserInfoRepository; + public readonly userModesInfoRepository:UserModesInfoRepository; public constructor() { if (!existsSync("./config.json")) { @@ -53,5 +55,6 @@ export default class Shared { // DB Repos this.userInfoRepository = new UserInfoRepository(this); + this.userModesInfoRepository = new UserModesInfoRepository(this); } } \ No newline at end of file diff --git a/server/objects/User.ts b/server/objects/User.ts index 7fe2cf8..9fabd2b 100644 --- a/server/objects/User.ts +++ b/server/objects/User.ts @@ -1,5 +1,5 @@ import LatLng from "./LatLng"; -import { RankingModes } from "../enums/RankingModes"; +import { RankingMode } from "../enums/RankingMode"; import Match from "./Match"; import DataStream from "./DataStream"; import StatusUpdate from "../packets/StatusUpdate"; @@ -9,12 +9,6 @@ import Channel from "./Channel"; import PresenceData from "../interfaces/packetTypes/PresenceData"; import { Permissions } from "../enums/Permissions"; -const rankingModes = [ - "pp_raw", - "ranked_score", - "avg_accuracy" -]; - export default class User { public shared:Shared; @@ -26,7 +20,7 @@ export default class User { public queue:Buffer = Buffer.allocUnsafe(0); // Binato data - public rankingMode:RankingModes = RankingModes.PP; + public rankingMode:RankingMode = RankingMode.PP; public spectatorStream?:DataStream; public spectatingUser?:User; public permissions:Permissions; @@ -101,26 +95,27 @@ export default class User { // Gets the user's score information from the database and caches it async updateUserInfo(forceUpdate:boolean = false) { - const userScoreDB = await this.shared.database.querySingle("SELECT * FROM users_modes_info WHERE user_id = ? AND mode_id = ? LIMIT 1", [this.id, this.playMode]); - const mappedRankingMode = rankingModes[this.rankingMode]; - const userRankDB = await this.shared.database.query(`SELECT user_id, ${mappedRankingMode} FROM users_modes_info WHERE mode_id = ? ORDER BY ${mappedRankingMode} DESC`, [this.playMode]); + const userScoreDB = await this.shared.userModesInfoRepository.selectByUserIdModeId(this.id, this.playMode); + const userRank = await this.shared.userModesInfoRepository.selectRankByIdModeIdRankingMode(this.id, this.playMode, this.rankingMode); - if (userScoreDB == null || userRankDB == null) throw "fuck"; + if (userScoreDB == null || userRank == null) throw "fuck"; + + this.rank = userRank; // Handle "if we should update" checks for each rankingMode let userScoreUpdate = false; switch (this.rankingMode) { - case RankingModes.PP: + case RankingMode.PP: if (this.pp != userScoreDB.pp_raw) userScoreUpdate = true; break; - case RankingModes.RANKED_SCORE: + case RankingMode.RANKED_SCORE: if (this.rankedScore != userScoreDB.ranked_score) userScoreUpdate = true; break; - case RankingModes.AVG_ACCURACY: + case RankingMode.AVG_ACCURACY: if (this.accuracy != userScoreDB.avg_accuracy) userScoreUpdate = true; break; @@ -131,14 +126,6 @@ export default class User { this.accuracy = userScoreDB.avg_accuracy; this.playCount = userScoreDB.playcount; - // Fetch rank - for (let i = 0; i < userRankDB.length; i++) { - if (userRankDB[i]["user_id"] == this.id) { - this.rank = i + 1; - break; - } - } - // Set PP to none if ranking mode is not PP if (this.rankingMode == 0) this.pp = userScoreDB.pp_raw; else this.pp = 0; diff --git a/server/objects/database/UserModeInfo.ts b/server/objects/database/UserModeInfo.ts new file mode 100644 index 0000000..4aa6db9 --- /dev/null +++ b/server/objects/database/UserModeInfo.ts @@ -0,0 +1,24 @@ +import { Mode } from "../../enums/Mode"; + +export default class UserModeInfo { + n:number = Number.MIN_VALUE; + user_id:number = Number.MIN_VALUE; + mode_id:Mode = Mode.Unknown; + count300:number = Number.MIN_VALUE; + count100:number = Number.MIN_VALUE; + count50:number = Number.MIN_VALUE; + countmiss:number = Number.MIN_VALUE; + playcount:number = Number.MIN_VALUE; + total_score:number = Number.MIN_VALUE; + ranked_score:number = Number.MIN_VALUE; + pp_rank:number = Number.MIN_VALUE; + pp_raw:number = Number.MIN_VALUE; + count_rank_ss:number = Number.MIN_VALUE; + count_rank_s:number = Number.MIN_VALUE; + count_rank_a:number = Number.MIN_VALUE; + pp_country_rank:number = Number.MIN_VALUE; + playtime:number = Number.MIN_VALUE; + avg_accuracy:number = Number.MIN_VALUE; + level:number = Number.MIN_VALUE; + is_deleted:boolean = false; +} \ No newline at end of file diff --git a/server/packets/StatusUpdate.ts b/server/packets/StatusUpdate.ts index 5fc6e77..6e45175 100644 --- a/server/packets/StatusUpdate.ts +++ b/server/packets/StatusUpdate.ts @@ -1,10 +1,13 @@ import Shared from "../objects/Shared"; -import { RankingModes } from "../enums/RankingModes"; +import { RankingMode } from "../enums/RankingMode"; import User from "../objects/User"; import osu from "../../osuTyping"; export default function StatusUpdate(arg0:User | Shared, id:number) { - if (id == 3) return; // Ignore Bot + // Ignore Bot + if (id == 3) { + return Buffer.allocUnsafe(0); + } // Create new osu packet writer const osuPacketWriter = osu.Bancho.Writer(); @@ -18,7 +21,9 @@ export default function StatusUpdate(arg0:User | Shared, id:number) { // Get user's class const userData = shared.users.getById(id); - if (userData == null) return; + if (userData == null) { + return Buffer.allocUnsafe(0); + } osuPacketWriter.HandleOsuUpdate({ userId: userData.id, @@ -33,7 +38,7 @@ export default function StatusUpdate(arg0:User | Shared, id:number) { playCount: userData.playCount, totalScore: userData.totalScore, rank: userData.rank, - performance: (userData.rankingMode == RankingModes.PP ? userData.pp : 0) + performance: (userData.rankingMode == RankingMode.PP ? userData.pp : 0) }); // Send data to user's queue diff --git a/server/repos/UserInfoRepository.ts b/server/repos/UserInfoRepository.ts index 0021db5..2bccf1d 100644 --- a/server/repos/UserInfoRepository.ts +++ b/server/repos/UserInfoRepository.ts @@ -9,11 +9,11 @@ export default class UserInfoRepository { this.database = shared.database; } - public async getById(id:number) { - const query = await this.database.query("SELECT * FROM users_info WHERE id = ? AND is_deleted = 0 LIMIT 1", [id]); + public async selectById(id:number) { + const query = await this.database.query("CALL SelectUserInfoById(?)", [id]); if (query != null) { const userInfo = new UserInfo(); - populateUserInfoFromRowDataPacket(userInfo, query[0]); + populateUserInfoFromRowDataPacket(userInfo, query[0][0]); return userInfo; } @@ -21,11 +21,11 @@ export default class UserInfoRepository { return null; } - public async getByUsername(username:string) { - const query = await this.database.query("SELECT * FROM users_info WHERE username = ? AND is_deleted = 0 LIMIT 1", [username]); + public async selectByUsername(username:string) { + const query = await this.database.query("CALL SelectUserInfoByUsername(?)", [username]); if (query != null) { const userInfo = new UserInfo(); - populateUserInfoFromRowDataPacket(userInfo, query[0]); + populateUserInfoFromRowDataPacket(userInfo, query[0][0]); return userInfo; } diff --git a/server/repos/UserModesInfoRepository.ts b/server/repos/UserModesInfoRepository.ts new file mode 100644 index 0000000..60d27f2 --- /dev/null +++ b/server/repos/UserModesInfoRepository.ts @@ -0,0 +1,72 @@ +import { RowDataPacket } from "mysql2"; +import Database from "../objects/Database"; +import Shared from "../objects/Shared"; +import UserModeInfo from "../objects/database/UserModeInfo"; +import { Mode } from "fs"; +import { RankingMode } from "../enums/RankingMode"; + +export default class UserModesInfoRepository { + private database:Database; + public constructor(shared:Shared) { + this.database = shared.database; + } + + public async selectByUserIdModeId(id:number, mode:Mode) { + const query = await this.database.query("CALL SelectUserModesInfoByUserIdModeId(?,?)", [id, mode]); + if (query != null) { + const userModeInfo = new UserModeInfo(); + populateUserModeInfoFromRowDataPacket(userModeInfo, query[0][0]); + + return userModeInfo; + } + + return null; + } + + public async selectRankByIdModeIdRankingMode(id:number, mode:Mode, rankingMode:RankingMode) : Promise { + let query:RowDataPacket[] | undefined; + switch (rankingMode) { + case RankingMode.RANKED_SCORE: + query = await this.database.query("CALL SelectUserScoreRankByIdModeId(?,?)", [id, mode]); + break; + + case RankingMode.AVG_ACCURACY: + query = await this.database.query("CALL SelectUserAccRankByIdModeId(?,?)", [id, mode]); + break; + + case RankingMode.PP: + default: + query = await this.database.query("CALL SelectUserPPRankByIdModeId(?,?)", [id, mode]); + break; + } + + if (query != null && query.length != 0) { + return query[0][0].rank; + } + + return null; + } +} + +function populateUserModeInfoFromRowDataPacket(userModeInfo:UserModeInfo, rowDataPacket:RowDataPacket) { + userModeInfo.n = rowDataPacket["n"]; + userModeInfo.user_id = rowDataPacket["user_id"]; + userModeInfo.mode_id = rowDataPacket["mode_id"]; + userModeInfo.count300 = rowDataPacket["count300"]; + userModeInfo.count100 = rowDataPacket["count100"]; + userModeInfo.count50 = rowDataPacket["count50"]; + userModeInfo.countmiss = rowDataPacket["countmiss"]; + userModeInfo.playcount = rowDataPacket["playcount"]; + userModeInfo.total_score = rowDataPacket["total_score"]; + userModeInfo.ranked_score = rowDataPacket["ranked_score"]; + userModeInfo.pp_rank = rowDataPacket["pp_rank"]; + userModeInfo.pp_raw = rowDataPacket["pp_raw"]; + userModeInfo.count_rank_ss = rowDataPacket["count_rank_ss"]; + userModeInfo.count_rank_s = rowDataPacket["count_rank_s"]; + userModeInfo.count_rank_a = rowDataPacket["count_rank_a"]; + userModeInfo.pp_country_rank = rowDataPacket["pp_country_rank"]; + userModeInfo.playtime = rowDataPacket["playtime"]; + userModeInfo.avg_accuracy = rowDataPacket["avg_accuracy"]; + userModeInfo.level = rowDataPacket["level"]; + userModeInfo.is_deleted = rowDataPacket["is_deleted"]; +} \ No newline at end of file