Kickstart the big db rewrite
This commit is contained in:
parent
686e6001b2
commit
4b90031294
15 changed files with 140 additions and 47 deletions
|
@ -12,8 +12,8 @@ import SpectatorManager from "./SpectatorManager";
|
|||
import osu from "../osuTyping";
|
||||
|
||||
const shared:Shared = new Shared();
|
||||
shared.database.query("UPDATE mp_matches SET close_time = UNIX_TIMESTAMP() WHERE close_time IS NULL");
|
||||
shared.database.query("UPDATE osu_info SET value = 0 WHERE name = 'online_now'");
|
||||
shared.database.execute("UPDATE mp_matches SET close_time = UNIX_TIMESTAMP() WHERE close_time IS NULL");
|
||||
shared.database.execute("UPDATE osu_info SET value = 0 WHERE name = 'online_now'");
|
||||
|
||||
// Server Setup
|
||||
const spectatorManager:SpectatorManager = new SpectatorManager(shared);
|
||||
|
@ -90,7 +90,7 @@ export default async function HandleRequest(req:IncomingMessage, res:ServerRespo
|
|||
// Client doesn't have a token yet, let's auth them!
|
||||
|
||||
await LoginProcess(req, res, packet, shared);
|
||||
shared.database.query("UPDATE osu_info SET value = ? WHERE name = 'online_now'", [shared.users.getLength() - 1]);
|
||||
shared.database.execute("UPDATE osu_info SET value = ? WHERE name = 'online_now'", [shared.users.getLength() - 1]);
|
||||
} else {
|
||||
let responseData = Buffer.allocUnsafe(0);
|
||||
|
||||
|
@ -240,11 +240,11 @@ export default async function HandleRequest(req:IncomingMessage, res:ServerRespo
|
|||
break;
|
||||
|
||||
case Packets.Client_FriendAdd:
|
||||
AddFriend(user, packet.data);
|
||||
await AddFriend(user, packet.data);
|
||||
break;
|
||||
|
||||
case Packets.Client_FriendRemove:
|
||||
RemoveFriend(user, packet.data);
|
||||
await RemoveFriend(user, packet.data);
|
||||
break;
|
||||
|
||||
case Packets.Client_UserStatsRequest:
|
||||
|
|
|
@ -37,7 +37,7 @@ enum LoginResult {
|
|||
|
||||
function TestLogin(loginInfo:LoginInfo, shared:Shared) {
|
||||
return new Promise<LoginResult>(async (resolve, reject) => {
|
||||
const userDBData:UserInfo = await shared.database.query("SELECT * FROM users_info WHERE username = ? LIMIT 1", [loginInfo.username]);
|
||||
const userDBData = await shared.userInfoRepository.getByUsername(loginInfo.username);
|
||||
|
||||
// Make sure a user was found in the database
|
||||
if (userDBData == null) return resolve(LoginResult.INCORRECT);
|
||||
|
@ -123,7 +123,10 @@ export default async function LoginProcess(req:IncomingMessage, res:ServerRespon
|
|||
}
|
||||
|
||||
// Get information about the user from the database
|
||||
const userId:number = (await shared.database.query("SELECT id FROM users_info WHERE username = ? LIMIT 1", [loginInfo.username])).id;
|
||||
const userInfo = await shared.userInfoRepository.getByUsername(loginInfo.username);
|
||||
if (userInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a token for the client
|
||||
const newClientToken:string = await generateSession();
|
||||
|
@ -136,7 +139,7 @@ export default async function LoginProcess(req:IncomingMessage, res:ServerRespon
|
|||
}
|
||||
|
||||
// Retreive the newly created user
|
||||
newUser = shared.users.add(newClientToken, new User(userId, loginInfo.username, newClientToken, shared));
|
||||
newUser = shared.users.add(newClientToken, new User(userInfo.id, loginInfo.username, newClientToken, shared));
|
||||
// Set tourney client flag
|
||||
newUser.isTourneyUser = isTourneyClient;
|
||||
newUser.location = userLocation;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export enum Permissions {
|
||||
None = 0,
|
||||
BAT = 2,
|
||||
Supporter = 4,
|
||||
Friend = 8,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { ConsoleHelper } from "../../ConsoleHelper";
|
||||
import { createPool, Pool } from "mysql2";
|
||||
import { createPool, Pool, RowDataPacket } from "mysql2";
|
||||
import { DBInDataType } from "../types/DBTypes";
|
||||
|
||||
export default class Database {
|
||||
private connectionPool:Pool;
|
||||
|
@ -20,50 +21,70 @@ export default class Database {
|
|||
ConsoleHelper.printInfo(`Connected DB connection pool. MAX_CONNECTIONS = ${Database.CONNECTION_LIMIT}`);
|
||||
}
|
||||
|
||||
public query(query = "", data?:Array<any>) {
|
||||
const limited = query.includes("LIMIT 1");
|
||||
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
public execute(query:string, data?:Array<DBInDataType>) {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
this.connectionPool.getConnection((err, connection) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
try {
|
||||
connection.release();
|
||||
} catch (e) {
|
||||
ConsoleHelper.printError("Failed to release mysql connection\n" + err);
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
connection.execute(query, data, (err, result) => {
|
||||
if (err) {
|
||||
connection.release();
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(result !== undefined);
|
||||
});
|
||||
} else {
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public query(query:string, data?:Array<DBInDataType>) {
|
||||
return new Promise<RowDataPacket[]>((resolve, reject) => {
|
||||
this.connectionPool.getConnection((err, connection) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
} else {
|
||||
// Use old query
|
||||
if (data == null) {
|
||||
connection.query(query, (err, data) => {
|
||||
connection.query<RowDataPacket[]>(query, (err, rows) => {
|
||||
connection.release();
|
||||
if (err) {
|
||||
reject(err);
|
||||
connection.release();
|
||||
} else {
|
||||
dataReceived(resolve, data, limited);
|
||||
connection.release();
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(rows);
|
||||
connection.release();
|
||||
});
|
||||
}
|
||||
// Use new prepared statements w/ placeholders
|
||||
else {
|
||||
connection.execute(query, data, (err, data) => {
|
||||
connection.execute<RowDataPacket[]>(query, data, (err, rows) => {
|
||||
connection.release();
|
||||
if (err) {
|
||||
reject(err);
|
||||
connection.release();
|
||||
} else {
|
||||
dataReceived(resolve, data, limited);
|
||||
connection.release();
|
||||
return reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function dataReceived(resolveCallback:(value:unknown) => void, data:any, limited:boolean = false) : void {
|
||||
if (limited) resolveCallback(data[0]);
|
||||
else resolveCallback(data);
|
||||
resolve(rows);
|
||||
connection.release();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async querySingle(query:string, data?:Array<DBInDataType>) {
|
||||
const dbData = await this.query(query, data);
|
||||
if (dbData != null && dbData.length > 0) {
|
||||
return dbData[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ export default class FunkyArray<T> {
|
|||
}
|
||||
|
||||
public regenerateIterableArray() : void {
|
||||
this.iterableArray = new Array();
|
||||
this.iterableArray = new Array<T>();
|
||||
for (const itemKey of this.itemKeys) {
|
||||
this.iterableArray.push(this.items[itemKey]);
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ export default class FunkyArray<T> {
|
|||
return this.itemKeys;
|
||||
}
|
||||
|
||||
public getItems() : any {
|
||||
public getItems() : { [id: string]: T } {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
|
|
|
@ -242,7 +242,7 @@ export default class Match {
|
|||
}
|
||||
queryData.push(this.matchId);
|
||||
|
||||
await this.shared.database.query(
|
||||
await this.shared.database.execute(
|
||||
`UPDATE mp_matches SET ${gameNameChanged ? `name = ?${gameSeedChanged ? ", " : ""}` : ""}${gameSeedChanged ? `seed = ?` : ""} WHERE id = ?`,
|
||||
queryData
|
||||
);
|
||||
|
@ -632,7 +632,7 @@ export default class Match {
|
|||
slot.status = SlotStatus.NotReady;
|
||||
}
|
||||
|
||||
await this.shared.database.query("INSERT INTO mp_match_rounds (id, match_id, round_id, round_mode, match_type, round_scoring_type, round_team_type, round_mods, beatmap_md5, freemod, player0, player1, player2, player3, player4, player5, player6, player7, player8, player9, player10, player11, player12, player13, player14, player15) VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", queryData);
|
||||
await this.shared.database.execute("INSERT INTO mp_match_rounds (id, match_id, round_id, round_mode, match_type, round_scoring_type, round_team_type, round_mods, beatmap_md5, freemod, player0, player1, player2, player3, player4, player5, player6, player7, player8, player9, player10, player11, player12, player13, player14, player15) VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", queryData);
|
||||
|
||||
osuPacketWriter.MatchComplete();
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import User from "./User";
|
|||
import LatLng from "./LatLng";
|
||||
import Bot from "../Bot";
|
||||
import { ConsoleHelper } from "../../ConsoleHelper";
|
||||
import UserInfoRepository from "../repos/UserInfoRepository";
|
||||
|
||||
export default class Shared {
|
||||
public readonly chatManager:ChatManager;
|
||||
|
@ -21,6 +22,8 @@ export default class Shared {
|
|||
public readonly users:UserArray;
|
||||
public readonly bot:Bot;
|
||||
|
||||
public readonly userInfoRepository:UserInfoRepository;
|
||||
|
||||
public constructor() {
|
||||
if (!existsSync("./config.json")) {
|
||||
ConsoleHelper.printError("Config file missing!");
|
||||
|
@ -46,5 +49,8 @@ export default class Shared {
|
|||
|
||||
this.multiplayerManager = new MultiplayerManager(this);
|
||||
this.privateChatManager = new PrivateChatManager(this);
|
||||
|
||||
// DB Repos
|
||||
this.userInfoRepository = new UserInfoRepository(this);
|
||||
}
|
||||
}
|
|
@ -98,7 +98,7 @@ 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.query("SELECT * FROM users_modes_info WHERE user_id = ? AND mode_id = ? LIMIT 1", [this.id, this.playMode]);
|
||||
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]);
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { Permissions } from "../../enums/Permissions";
|
||||
|
||||
export default class UserInfo {
|
||||
id:number = Number.MIN_VALUE;
|
||||
username:string = "";
|
||||
|
@ -10,7 +12,7 @@ export default class UserInfo {
|
|||
last_login_date:Date = new Date(0);
|
||||
last_played_mode:number = Number.MIN_VALUE;
|
||||
online_now:boolean = false;
|
||||
tags:number = Number.MIN_VALUE;
|
||||
tags:Permissions = Permissions.None;
|
||||
supporter:boolean = false;
|
||||
web_session:string = "";
|
||||
verification_needed:boolean = false;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import User from "../objects/User";
|
||||
|
||||
export default function AddFriend(user:User, friendId:number) {
|
||||
user.shared.database.query("INSERT INTO friends (user, friendsWith) VALUES (?, ?)", [
|
||||
export default async function AddFriend(user:User, friendId:number) {
|
||||
await user.shared.database.execute("INSERT INTO friends (user, friendsWith) VALUES (?, ?)", [
|
||||
user.id, friendId
|
||||
]);
|
||||
}
|
|
@ -13,7 +13,7 @@ export default async function Logout(user:User) {
|
|||
// Remove user from user list
|
||||
user.shared.users.remove(user.uuid);
|
||||
|
||||
await user.shared.database.query("UPDATE osu_info SET value = ? WHERE name = 'online_now'", [user.shared.users.getLength() - 1]);
|
||||
await user.shared.database.execute("UPDATE osu_info SET value = ? WHERE name = 'online_now'", [user.shared.users.getLength() - 1]);
|
||||
|
||||
ConsoleHelper.printBancho(`User logged out, took ${Date.now() - logoutStartTime}ms. [User: ${user.username}]`);
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import User from "../objects/User";
|
||||
|
||||
export default function RemoveFriend(user:User, friendId:number) {
|
||||
user.shared.database.query("DELETE FROM friends WHERE user = ? AND friendsWith = ? LIMIT 1", [
|
||||
export default async function RemoveFriend(user:User, friendId:number) {
|
||||
await user.shared.database.execute("DELETE FROM friends WHERE user = ? AND friendsWith = ? LIMIT 1", [
|
||||
user.id, friendId
|
||||
]);
|
||||
}
|
59
server/repos/UserInfoRepository.ts
Normal file
59
server/repos/UserInfoRepository.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { RowDataPacket } from "mysql2";
|
||||
import Database from "../objects/Database";
|
||||
import Shared from "../objects/Shared";
|
||||
import UserInfo from "../objects/database/UserInfo";
|
||||
|
||||
export default class UserInfoRepository {
|
||||
private database:Database;
|
||||
public constructor(shared:Shared) {
|
||||
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]);
|
||||
if (query != null) {
|
||||
const userInfo = new UserInfo();
|
||||
populateUserInfoFromRowDataPacket(userInfo, query[0]);
|
||||
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
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]);
|
||||
if (query != null) {
|
||||
const userInfo = new UserInfo();
|
||||
populateUserInfoFromRowDataPacket(userInfo, query[0]);
|
||||
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function populateUserInfoFromRowDataPacket(userInfo:UserInfo, rowDataPacket:RowDataPacket) {
|
||||
userInfo.id = rowDataPacket["id"];
|
||||
userInfo.username = rowDataPacket["username"];
|
||||
userInfo.username_safe = rowDataPacket["username_safe"];
|
||||
userInfo.password_hash = rowDataPacket["password_hash"];
|
||||
userInfo.password_salt = rowDataPacket["password_salt"];
|
||||
userInfo.email = rowDataPacket["email"];
|
||||
userInfo.country = rowDataPacket["country"];
|
||||
userInfo.reg_date = rowDataPacket["reg_date"];
|
||||
userInfo.last_login_date = rowDataPacket["last_login_date"];
|
||||
userInfo.last_played_mode = rowDataPacket["last_played_mode"];
|
||||
userInfo.online_now = rowDataPacket["online_now"];
|
||||
userInfo.tags = rowDataPacket["tags"];
|
||||
userInfo.supporter = rowDataPacket["supporter"];
|
||||
userInfo.web_session = rowDataPacket["web_session"];
|
||||
userInfo.verification_needed = rowDataPacket["verification_needed"];
|
||||
userInfo.password_change_required = rowDataPacket["password_change_required"];
|
||||
userInfo.has_old_password = rowDataPacket["has_old_password"];
|
||||
userInfo.password_reset_key = rowDataPacket["password_reset_key"];
|
||||
userInfo.away_message = rowDataPacket["away_message"];
|
||||
userInfo.last_modified_time = rowDataPacket["last_modified_time"];
|
||||
userInfo.is_deleted = rowDataPacket["is_deleted"];
|
||||
}
|
1
server/types/DBTypes.ts
Normal file
1
server/types/DBTypes.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export type DBInDataType = string | number | null | undefined;
|
|
@ -53,7 +53,7 @@ import { Registry, collectDefaultMetrics } from "prom-client";
|
|||
import { RedisClientType, createClient } from "redis";
|
||||
import { readFileSync, existsSync } from "fs";
|
||||
import { randomBytes, pbkdf2 } from "crypto";
|
||||
import { createPool, Pool } from "mysql2";
|
||||
import { createPool, Pool, RowDataPacket } from "mysql2";
|
||||
import * as dyetty from "dyetty";
|
||||
import fetch from "node-fetch";
|
||||
import http from "http";`);
|
||||
|
|
Loading…
Reference in a new issue