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";
|
import osu from "../osuTyping";
|
||||||
|
|
||||||
const shared:Shared = new Shared();
|
const shared:Shared = new Shared();
|
||||||
shared.database.query("UPDATE mp_matches SET close_time = UNIX_TIMESTAMP() WHERE close_time IS NULL");
|
shared.database.execute("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 osu_info SET value = 0 WHERE name = 'online_now'");
|
||||||
|
|
||||||
// Server Setup
|
// Server Setup
|
||||||
const spectatorManager:SpectatorManager = new SpectatorManager(shared);
|
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!
|
// Client doesn't have a token yet, let's auth them!
|
||||||
|
|
||||||
await LoginProcess(req, res, packet, shared);
|
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 {
|
} else {
|
||||||
let responseData = Buffer.allocUnsafe(0);
|
let responseData = Buffer.allocUnsafe(0);
|
||||||
|
|
||||||
|
@ -240,11 +240,11 @@ export default async function HandleRequest(req:IncomingMessage, res:ServerRespo
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_FriendAdd:
|
case Packets.Client_FriendAdd:
|
||||||
AddFriend(user, packet.data);
|
await AddFriend(user, packet.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_FriendRemove:
|
case Packets.Client_FriendRemove:
|
||||||
RemoveFriend(user, packet.data);
|
await RemoveFriend(user, packet.data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packets.Client_UserStatsRequest:
|
case Packets.Client_UserStatsRequest:
|
||||||
|
|
|
@ -37,7 +37,7 @@ enum LoginResult {
|
||||||
|
|
||||||
function TestLogin(loginInfo:LoginInfo, shared:Shared) {
|
function TestLogin(loginInfo:LoginInfo, shared:Shared) {
|
||||||
return new Promise<LoginResult>(async (resolve, reject) => {
|
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
|
// Make sure a user was found in the database
|
||||||
if (userDBData == null) return resolve(LoginResult.INCORRECT);
|
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
|
// 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
|
// Create a token for the client
|
||||||
const newClientToken:string = await generateSession();
|
const newClientToken:string = await generateSession();
|
||||||
|
@ -136,7 +139,7 @@ export default async function LoginProcess(req:IncomingMessage, res:ServerRespon
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retreive the newly created user
|
// 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
|
// Set tourney client flag
|
||||||
newUser.isTourneyUser = isTourneyClient;
|
newUser.isTourneyUser = isTourneyClient;
|
||||||
newUser.location = userLocation;
|
newUser.location = userLocation;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export enum Permissions {
|
export enum Permissions {
|
||||||
|
None = 0,
|
||||||
BAT = 2,
|
BAT = 2,
|
||||||
Supporter = 4,
|
Supporter = 4,
|
||||||
Friend = 8,
|
Friend = 8,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { ConsoleHelper } from "../../ConsoleHelper";
|
import { ConsoleHelper } from "../../ConsoleHelper";
|
||||||
import { createPool, Pool } from "mysql2";
|
import { createPool, Pool, RowDataPacket } from "mysql2";
|
||||||
|
import { DBInDataType } from "../types/DBTypes";
|
||||||
|
|
||||||
export default class Database {
|
export default class Database {
|
||||||
private connectionPool:Pool;
|
private connectionPool:Pool;
|
||||||
|
@ -20,50 +21,70 @@ export default class Database {
|
||||||
ConsoleHelper.printInfo(`Connected DB connection pool. MAX_CONNECTIONS = ${Database.CONNECTION_LIMIT}`);
|
ConsoleHelper.printInfo(`Connected DB connection pool. MAX_CONNECTIONS = ${Database.CONNECTION_LIMIT}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public query(query = "", data?:Array<any>) {
|
public execute(query:string, data?:Array<DBInDataType>) {
|
||||||
const limited = query.includes("LIMIT 1");
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
|
|
||||||
return new Promise<any>((resolve, reject) => {
|
|
||||||
this.connectionPool.getConnection((err, connection) => {
|
this.connectionPool.getConnection((err, connection) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
return reject(err);
|
||||||
try {
|
}
|
||||||
connection.release();
|
|
||||||
} catch (e) {
|
if (data == null) {
|
||||||
ConsoleHelper.printError("Failed to release mysql connection\n" + err);
|
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 {
|
} else {
|
||||||
// Use old query
|
// Use old query
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
connection.query(query, (err, data) => {
|
connection.query<RowDataPacket[]>(query, (err, rows) => {
|
||||||
|
connection.release();
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
return reject(err);
|
||||||
connection.release();
|
|
||||||
} else {
|
|
||||||
dataReceived(resolve, data, limited);
|
|
||||||
connection.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolve(rows);
|
||||||
|
connection.release();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Use new prepared statements w/ placeholders
|
// Use new prepared statements w/ placeholders
|
||||||
else {
|
else {
|
||||||
connection.execute(query, data, (err, data) => {
|
connection.execute<RowDataPacket[]>(query, data, (err, rows) => {
|
||||||
|
connection.release();
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
return reject(err);
|
||||||
connection.release();
|
|
||||||
} else {
|
|
||||||
dataReceived(resolve, data, limited);
|
|
||||||
connection.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolve(rows);
|
||||||
|
connection.release();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function dataReceived(resolveCallback:(value:unknown) => void, data:any, limited:boolean = false) : void {
|
public async querySingle(query:string, data?:Array<DBInDataType>) {
|
||||||
if (limited) resolveCallback(data[0]);
|
const dbData = await this.query(query, data);
|
||||||
else resolveCallback(data);
|
if (dbData != null && dbData.length > 0) {
|
||||||
|
return dbData[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -29,7 +29,7 @@ export default class FunkyArray<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public regenerateIterableArray() : void {
|
public regenerateIterableArray() : void {
|
||||||
this.iterableArray = new Array();
|
this.iterableArray = new Array<T>();
|
||||||
for (const itemKey of this.itemKeys) {
|
for (const itemKey of this.itemKeys) {
|
||||||
this.iterableArray.push(this.items[itemKey]);
|
this.iterableArray.push(this.items[itemKey]);
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ export default class FunkyArray<T> {
|
||||||
return this.itemKeys;
|
return this.itemKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getItems() : any {
|
public getItems() : { [id: string]: T } {
|
||||||
return this.items;
|
return this.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -242,7 +242,7 @@ export default class Match {
|
||||||
}
|
}
|
||||||
queryData.push(this.matchId);
|
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 = ?`,
|
`UPDATE mp_matches SET ${gameNameChanged ? `name = ?${gameSeedChanged ? ", " : ""}` : ""}${gameSeedChanged ? `seed = ?` : ""} WHERE id = ?`,
|
||||||
queryData
|
queryData
|
||||||
);
|
);
|
||||||
|
@ -632,7 +632,7 @@ export default class Match {
|
||||||
slot.status = SlotStatus.NotReady;
|
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();
|
osuPacketWriter.MatchComplete();
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import User from "./User";
|
||||||
import LatLng from "./LatLng";
|
import LatLng from "./LatLng";
|
||||||
import Bot from "../Bot";
|
import Bot from "../Bot";
|
||||||
import { ConsoleHelper } from "../../ConsoleHelper";
|
import { ConsoleHelper } from "../../ConsoleHelper";
|
||||||
|
import UserInfoRepository from "../repos/UserInfoRepository";
|
||||||
|
|
||||||
export default class Shared {
|
export default class Shared {
|
||||||
public readonly chatManager:ChatManager;
|
public readonly chatManager:ChatManager;
|
||||||
|
@ -21,6 +22,8 @@ export default class Shared {
|
||||||
public readonly users:UserArray;
|
public readonly users:UserArray;
|
||||||
public readonly bot:Bot;
|
public readonly bot:Bot;
|
||||||
|
|
||||||
|
public readonly userInfoRepository:UserInfoRepository;
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
if (!existsSync("./config.json")) {
|
if (!existsSync("./config.json")) {
|
||||||
ConsoleHelper.printError("Config file missing!");
|
ConsoleHelper.printError("Config file missing!");
|
||||||
|
@ -46,5 +49,8 @@ export default class Shared {
|
||||||
|
|
||||||
this.multiplayerManager = new MultiplayerManager(this);
|
this.multiplayerManager = new MultiplayerManager(this);
|
||||||
this.privateChatManager = new PrivateChatManager(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
|
// Gets the user's score information from the database and caches it
|
||||||
async updateUserInfo(forceUpdate:boolean = false) {
|
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 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 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 {
|
export default class UserInfo {
|
||||||
id:number = Number.MIN_VALUE;
|
id:number = Number.MIN_VALUE;
|
||||||
username:string = "";
|
username:string = "";
|
||||||
|
@ -10,7 +12,7 @@ export default class UserInfo {
|
||||||
last_login_date:Date = new Date(0);
|
last_login_date:Date = new Date(0);
|
||||||
last_played_mode:number = Number.MIN_VALUE;
|
last_played_mode:number = Number.MIN_VALUE;
|
||||||
online_now:boolean = false;
|
online_now:boolean = false;
|
||||||
tags:number = Number.MIN_VALUE;
|
tags:Permissions = Permissions.None;
|
||||||
supporter:boolean = false;
|
supporter:boolean = false;
|
||||||
web_session:string = "";
|
web_session:string = "";
|
||||||
verification_needed:boolean = false;
|
verification_needed:boolean = false;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import User from "../objects/User";
|
import User from "../objects/User";
|
||||||
|
|
||||||
export default function AddFriend(user:User, friendId:number) {
|
export default async function AddFriend(user:User, friendId:number) {
|
||||||
user.shared.database.query("INSERT INTO friends (user, friendsWith) VALUES (?, ?)", [
|
await user.shared.database.execute("INSERT INTO friends (user, friendsWith) VALUES (?, ?)", [
|
||||||
user.id, friendId
|
user.id, friendId
|
||||||
]);
|
]);
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@ export default async function Logout(user:User) {
|
||||||
// Remove user from user list
|
// Remove user from user list
|
||||||
user.shared.users.remove(user.uuid);
|
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}]`);
|
ConsoleHelper.printBancho(`User logged out, took ${Date.now() - logoutStartTime}ms. [User: ${user.username}]`);
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import User from "../objects/User";
|
import User from "../objects/User";
|
||||||
|
|
||||||
export default function RemoveFriend(user:User, friendId:number) {
|
export default async function RemoveFriend(user:User, friendId:number) {
|
||||||
user.shared.database.query("DELETE FROM friends WHERE user = ? AND friendsWith = ? LIMIT 1", [
|
await user.shared.database.execute("DELETE FROM friends WHERE user = ? AND friendsWith = ? LIMIT 1", [
|
||||||
user.id, friendId
|
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 { RedisClientType, createClient } from "redis";
|
||||||
import { readFileSync, existsSync } from "fs";
|
import { readFileSync, existsSync } from "fs";
|
||||||
import { randomBytes, pbkdf2 } from "crypto";
|
import { randomBytes, pbkdf2 } from "crypto";
|
||||||
import { createPool, Pool } from "mysql2";
|
import { createPool, Pool, RowDataPacket } from "mysql2";
|
||||||
import * as dyetty from "dyetty";
|
import * as dyetty from "dyetty";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import http from "http";`);
|
import http from "http";`);
|
||||||
|
|
Loading…
Reference in a new issue