diff --git a/server/BanchoServer.ts b/server/BanchoServer.ts index 2988bc9..3f570eb 100644 --- a/server/BanchoServer.ts +++ b/server/BanchoServer.ts @@ -81,8 +81,6 @@ setInterval(() => { } }, 10000); -const EMPTY_BUFFER = Buffer.alloc(0); - export default async function HandleRequest(req:IncomingMessage, res:ServerResponse, packet:Buffer) { // Get the client's token string and request data const requestTokenString = typeof(req.headers["osu-token"]) === "string" ? req.headers["osu-token"] : undefined; @@ -94,7 +92,7 @@ export default async function HandleRequest(req:IncomingMessage, res:ServerRespo await LoginProcess(req, res, packet, shared); shared.database.query("UPDATE osu_info SET value = ? WHERE name = 'online_now'", [shared.users.getLength() - 1]); } else { - let responseData = EMPTY_BUFFER; + let responseData = Buffer.allocUnsafe(0); // Client has a token, let's see what they want. try { diff --git a/server/Country.ts b/server/Country.ts index dc5c28b..3c9f294 100644 --- a/server/Country.ts +++ b/server/Country.ts @@ -1,269 +1,261 @@ -enum CountryCodes { - LV = 132, - AD = 3, - LT = 130, - KM = 116, - QA = 182, - VA = 0, - PK = 173, - KI = 115, - SS = 0, - KH = 114, - NZ = 166, - TO = 215, - KZ = 122, - BW = 35, - GA = 76, - AX = 247, - GE = 79, - UA = 222, - CR = 50, - AE = 0, - NE = 157, - ZA = 240, - SK = 196, - BV = 34, - SH = 0, - PT = 179, - SC = 189, - CO = 49, - GP = 86, - GY = 93, - CM = 47, - TJ = 211, - AF = 5, - IE = 101, - AL = 8, - BG = 24, - JO = 110, - MU = 149, - PM = 0, - LA = 0, - IO = 104, - KY = 121, - SA = 187, - KN = 0, - OM = 167, - CY = 54, - BQ = 0, - BT = 33, - WS = 236, - ES = 67, - LR = 128, - RW = 186, - AQ = 12, - PW = 180, - JE = 250, - TN = 214, - ZW = 243, - JP = 111, - BB = 20, - VN = 233, - HN = 96, - KP = 0, - WF = 235, - EC = 62, - HU = 99, - GF = 80, - GQ = 87, - TW = 220, - MC = 135, - BE = 22, - PN = 176, - SZ = 205, - CZ = 55, - LY = 0, - IN = 103, - FM = 0, - PY = 181, - PH = 172, - MN = 142, - GG = 248, - CC = 39, - ME = 242, - DO = 60, - KR = 0, - PL = 174, - MT = 148, - MM = 141, - AW = 17, - MV = 150, - BD = 21, - NR = 164, - AT = 15, - GW = 92, - FR = 74, - LI = 126, - CF = 41, - DZ = 61, - MA = 134, - VG = 0, - NC = 156, - IQ = 105, - BN = 0, - BF = 23, - BO = 30, - GB = 77, - CU = 51, - LU = 131, - YT = 238, - NO = 162, - SM = 198, - GL = 83, - IS = 107, - AO = 11, - MH = 138, - SE = 191, - ZM = 241, - FJ = 70, - SL = 197, - CH = 43, - RU = 0, - CW = 0, - CX = 53, - TF = 208, - NL = 161, - AU = 16, - FI = 69, - MS = 147, - GH = 81, - BY = 36, - IL = 102, - VC = 0, - NG = 159, - HT = 98, - LS = 129, - MR = 146, - YE = 237, - MP = 144, - SX = 0, - RE = 183, - RO = 184, - NP = 163, - CG = 0, - FO = 73, - CI = 0, - TH = 210, - HK = 94, - TK = 212, - XK = 0, - DM = 59, - LC = 0, - ID = 100, - MG = 137, - JM = 109, - IT = 108, - CA = 38, - TZ = 221, - GI = 82, - KG = 113, - NU = 165, - TV = 219, - LB = 124, - SY = 0, - PR = 177, - NI = 160, - KE = 112, - MO = 0, - SR = 201, - VI = 0, - SV = 203, - HM = 0, - CD = 0, - BI = 26, - BM = 28, - MW = 151, - TM = 213, - GT = 90, - AG = 0, - UM = 0, - US = 225, - AR = 13, - DJ = 57, - KW = 120, - MY = 153, - FK = 71, - EG = 64, - BA = 0, - CN = 48, - GN = 85, - PS = 178, - SO = 200, - IM = 249, - GS = 0, - BR = 31, - GM = 84, - PF = 170, - PA = 168, - PG = 171, - BH = 25, - TG = 209, - GU = 91, - CK = 45, - MF = 252, - VE = 230, - CL = 46, - TR = 217, - UG = 223, - GD = 78, - TT = 218, - TL = 0, - MD = 0, - MK = 0, - ST = 202, - CV = 52, - MQ = 145, - GR = 88, - HR = 97, - BZ = 37, - UZ = 227, - DK = 58, - SN = 199, - ET = 68, - VU = 234, - ER = 66, - BJ = 27, - LK = 127, - NA = 155, - AS = 14, - SG = 192, - PE = 169, - IR = 0, - MX = 152, - TD = 207, - AZ = 18, - AM = 9, - BL = 0, - SJ = 195, - SB = 188, - NF = 158, - RS = 239, - DE = 56, - EH = 65, - EE = 63, - SD = 190, - ML = 140, - TC = 206, - MZ = 154, - BS = 32, - UY = 226, - SI = 194, - AI = 7 -} +const countryCodes:{ [id: string]: number } = { + "LV": 132, + "AD": 3, + "LT": 130, + "KM": 116, + "QA": 182, + "VA": 0, + "PK": 173, + "KI": 115, + "SS": 0, + "KH": 114, + "NZ": 166, + "TO": 215, + "KZ": 122, + "BW": 35, + "GA": 76, + "AX": 247, + "GE": 79, + "UA": 222, + "CR": 50, + "AE": 0, + "NE": 157, + "ZA": 240, + "SK": 196, + "BV": 34, + "SH": 0, + "PT": 179, + "SC": 189, + "CO": 49, + "GP": 86, + "GY": 93, + "CM": 47, + "TJ": 211, + "AF": 5, + "IE": 101, + "AL": 8, + "BG": 24, + "JO": 110, + "MU": 149, + "PM": 0, + "LA": 0, + "IO": 104, + "KY": 121, + "SA": 187, + "KN": 0, + "OM": 167, + "CY": 54, + "BQ": 0, + "BT": 33, + "WS": 236, + "ES": 67, + "LR": 128, + "RW": 186, + "AQ": 12, + "PW": 180, + "JE": 250, + "TN": 214, + "ZW": 243, + "JP": 111, + "BB": 20, + "VN": 233, + "HN": 96, + "KP": 0, + "WF": 235, + "EC": 62, + "HU": 99, + "GF": 80, + "GQ": 87, + "TW": 220, + "MC": 135, + "BE": 22, + "PN": 176, + "SZ": 205, + "CZ": 55, + "LY": 0, + "IN": 103, + "FM": 0, + "PY": 181, + "PH": 172, + "MN": 142, + "GG": 248, + "CC": 39, + "ME": 242, + "DO": 60, + "KR": 0, + "PL": 174, + "MT": 148, + "MM": 141, + "AW": 17, + "MV": 150, + "BD": 21, + "NR": 164, + "AT": 15, + "GW": 92, + "FR": 74, + "LI": 126, + "CF": 41, + "DZ": 61, + "MA": 134, + "VG": 0, + "NC": 156, + "IQ": 105, + "BN": 0, + "BF": 23, + "BO": 30, + "GB": 77, + "CU": 51, + "LU": 131, + "YT": 238, + "NO": 162, + "SM": 198, + "GL": 83, + "IS": 107, + "AO": 11, + "MH": 138, + "SE": 191, + "ZM": 241, + "FJ": 70, + "SL": 197, + "CH": 43, + "RU": 0, + "CW": 0, + "CX": 53, + "TF": 208, + "NL": 161, + "AU": 16, + "FI": 69, + "MS": 147, + "GH": 81, + "BY": 36, + "IL": 102, + "VC": 0, + "NG": 159, + "HT": 98, + "LS": 129, + "MR": 146, + "YE": 237, + "MP": 144, + "SX": 0, + "RE": 183, + "RO": 184, + "NP": 163, + "CG": 0, + "FO": 73, + "CI": 0, + "TH": 210, + "HK": 94, + "TK": 212, + "XK": 0, + "DM": 59, + "LC": 0, + "ID": 100, + "MG": 137, + "JM": 109, + "IT": 108, + "CA": 38, + "TZ": 221, + "GI": 82, + "KG": 113, + "NU": 165, + "TV": 219, + "LB": 124, + "SY": 0, + "PR": 177, + "NI": 160, + "KE": 112, + "MO": 0, + "SR": 201, + "VI": 0, + "SV": 203, + "HM": 0, + "CD": 0, + "BI": 26, + "BM": 28, + "MW": 151, + "TM": 213, + "GT": 90, + "AG": 0, + "UM": 0, + "US": 225, + "AR": 13, + "DJ": 57, + "KW": 120, + "MY": 153, + "FK": 71, + "EG": 64, + "BA": 0, + "CN": 48, + "GN": 85, + "PS": 178, + "SO": 200, + "IM": 249, + "GS": 0, + "BR": 31, + "GM": 84, + "PF": 170, + "PA": 168, + "PG": 171, + "BH": 25, + "TG": 209, + "GU": 91, + "CK": 45, + "MF": 252, + "VE": 230, + "CL": 46, + "TR": 217, + "UG": 223, + "GD": 78, + "TT": 218, + "TL": 0, + "MD": 0, + "MK": 0, + "ST": 202, + "CV": 52, + "MQ": 145, + "GR": 88, + "HR": 97, + "BZ": 37, + "UZ": 227, + "DK": 58, + "SN": 199, + "ET": 68, + "VU": 234, + "ER": 66, + "BJ": 27, + "LK": 127, + "NA": 155, + "AS": 14, + "SG": 192, + "PE": 169, + "IR": 0, + "MX": 152, + "TD": 207, + "AZ": 18, + "AM": 9, + "BL": 0, + "SJ": 195, + "SB": 188, + "NF": 158, + "RS": 239, + "DE": 56, + "EH": 65, + "EE": 63, + "SD": 190, + "ML": 140, + "TC": 206, + "MZ": 154, + "BS": 32, + "UY": 226, + "SI": 194, + "AI": 7 +}; -const keys = Object.keys(CountryCodes); -const values = Object.values(CountryCodes); - -export default function getCountryID(code:string) : number { - // Get id of a country from a 2 char code - const upperCode:string = code.toUpperCase(); - if (upperCode in CountryCodes) { - const code = values[keys.indexOf(upperCode)]; - if (typeof(code) === "string") { - return 0; - } - - return code; +// Get id of a country from a 2 char code +export function getCountryID(code:string) : number { + const upperCode = code.toUpperCase(); + if (upperCode in countryCodes) { + return countryCodes[upperCode]; } return 0; diff --git a/server/LoginProcess.ts b/server/LoginProcess.ts index a8ab24e..053f711 100644 --- a/server/LoginProcess.ts +++ b/server/LoginProcess.ts @@ -1,6 +1,6 @@ import { ConsoleHelper } from "../ConsoleHelper"; import fetch from "node-fetch"; -import getCountryID from "./Country"; +import { getCountryID } from "./Country"; import { generateSession } from "./Util"; import LatLng from "./objects/LatLng"; import LoginInfo from "./objects/LoginInfo"; @@ -14,6 +14,7 @@ import Shared from "./objects/Shared"; import osu from "../osuTyping"; import IpZxqResponse from "./interfaces/IpZxqResponse"; import { IncomingMessage, ServerResponse } from "http"; +import UserInfo from "./objects/database/UserInfo"; const { decrypt: aesDecrypt } = require("aes256"); const incorrectLoginResponse:Buffer = osu.Bancho.Writer().LoginReply(-1).toBuffer; @@ -36,7 +37,7 @@ enum LoginResult { function TestLogin(loginInfo:LoginInfo, shared:Shared) { return new Promise(async (resolve, reject) => { - const userDBData:any = await shared.database.query("SELECT * FROM users_info WHERE username = ? LIMIT 1", [loginInfo.username]); + const userDBData:UserInfo = await shared.database.query("SELECT * FROM users_info WHERE username = ? LIMIT 1", [loginInfo.username]); // Make sure a user was found in the database if (userDBData == null) return resolve(LoginResult.INCORRECT); @@ -122,7 +123,7 @@ export default async function LoginProcess(req:IncomingMessage, res:ServerRespon } // Get information about the user from the database - const userDB = await shared.database.query("SELECT id FROM users_info WHERE username = ? LIMIT 1", [loginInfo.username]); + const userId:number = (await shared.database.query("SELECT id FROM users_info WHERE username = ? LIMIT 1", [loginInfo.username])).id; // Create a token for the client const newClientToken:string = await generateSession(); @@ -135,7 +136,7 @@ export default async function LoginProcess(req:IncomingMessage, res:ServerRespon } // Retreive the newly created user - newUser = shared.users.add(newClientToken, new User(userDB.id, loginInfo.username, newClientToken, shared)); + newUser = shared.users.add(newClientToken, new User(userId, loginInfo.username, newClientToken, shared)); // Set tourney client flag newUser.isTourneyUser = isTourneyClient; newUser.location = userLocation; @@ -203,7 +204,7 @@ export default async function LoginProcess(req:IncomingMessage, res:ServerRespon const writerBuffer:Buffer = osuPacketWriter.toBuffer; if (newUser === undefined) { res.writeHead(200, { - "cho-token": "no", + "cho-token": "no", // NOTE: You have to specify a token even if it's an incorrect login for some reason. "Connection": "keep-alive", "Keep-Alive": "timeout=5, max=100" }); diff --git a/server/commands/BaseCommand.ts b/server/commands/BaseCommand.ts index 709998f..4cc4460 100644 --- a/server/commands/BaseCommand.ts +++ b/server/commands/BaseCommand.ts @@ -14,6 +14,6 @@ export default class BaseCommand implements ICommand { } public exec(channel:Channel, sender:User, args:Array) { - + channel.SendBotMessage(`Sorry ${sender.username}! This command has no functionality yet. Args: ["${args.join('", "')}"]`); } } \ No newline at end of file diff --git a/server/commands/Multiplayer.ts b/server/commands/Multiplayer.ts index 75fe862..c411fe2 100644 --- a/server/commands/Multiplayer.ts +++ b/server/commands/Multiplayer.ts @@ -6,8 +6,7 @@ import BaseCommand from "./BaseCommand"; export default class MultiplayerCommands extends BaseCommand { public readonly helpText:string = `Multiplayer Subcommands: !mp start - Starts a multiplayer match with a delay (optional) -!mp abort - Aborts the currently running round / countdown -!mp obr - Toggles Battle Royale mode`; +!mp abort - Aborts the currently running round / countdown`; public readonly helpDescription:string = "Command for use in multiplayer matches."; public readonly helpArguments:Array = ["subCommand"]; @@ -36,9 +35,6 @@ export default class MultiplayerCommands extends BaseCommand { case "abort": return mpAbort(channel, sender.match); - - case "obr": - return mpOBR(channel, sender.match); } } } @@ -89,8 +85,4 @@ function mpAbort(channel:Channel, match:Match) { match.finishMatch(); channel.SendBotMessage("Aborted current round"); } -} - -function mpOBR(channel:Channel, match:Match) { - } \ No newline at end of file diff --git a/server/interfaces/OsuPacketWriter.ts b/server/interfaces/OsuPacketWriter.ts index 14c9639..7d64f4a 100644 --- a/server/interfaces/OsuPacketWriter.ts +++ b/server/interfaces/OsuPacketWriter.ts @@ -1,5 +1,7 @@ import MatchData from "./MatchData" +import MatchScoreData from "./MatchScoreData" import MessageData from "./MessageData" +import StatusUpdateData from "./StatusUpdateData" export default interface OsuPacketWriter { // Functions @@ -9,7 +11,7 @@ export default interface OsuPacketWriter { Ping() : OsuPacketWriter, HandleIrcChangeUsername(data:any) : OsuPacketWriter, HandleIrcQuit() : OsuPacketWriter, - HandleOsuUpdate(data:any) : OsuPacketWriter, + HandleOsuUpdate(data:StatusUpdateData) : OsuPacketWriter, HandleUserQuit(data:any) : OsuPacketWriter, SpectatorJoined(data:any) : OsuPacketWriter, SpectatorLeft(data:any) : OsuPacketWriter, @@ -19,14 +21,14 @@ export default interface OsuPacketWriter { GetAttention() : OsuPacketWriter, Announce(data:string) : OsuPacketWriter, MatchUpdate(data:MatchData) : OsuPacketWriter, - MatchNew(data:any) : OsuPacketWriter, + MatchNew(data:MatchData) : OsuPacketWriter, MatchDisband(data:any) : OsuPacketWriter, - MatchJoinSuccess(data:any) : OsuPacketWriter, + MatchJoinSuccess(data:MatchData) : OsuPacketWriter, MatchJoinFail() : OsuPacketWriter, - FellowSpectatorJoined(data:any) : OsuPacketWriter, - FellowSpectatorLeft(data:any) : OsuPacketWriter, - MatchStart(data:any) : OsuPacketWriter, - MatchScoreUpdate(data:any) : OsuPacketWriter, + FellowSpectatorJoined(data:number) : OsuPacketWriter, + FellowSpectatorLeft(data:number) : OsuPacketWriter, + MatchStart(data:MatchData) : OsuPacketWriter, + MatchScoreUpdate(data:MatchScoreData) : OsuPacketWriter, MatchTransferHost(data:any) : OsuPacketWriter, MatchAllPlayersLoaded() : OsuPacketWriter, MatchPlayerFailed(data:any) : OsuPacketWriter, @@ -43,9 +45,9 @@ export default interface OsuPacketWriter { ProtocolNegotiation(data:number) : OsuPacketWriter, TitleUpdate(data:string) : OsuPacketWriter, Monitor() : OsuPacketWriter, - MatchPlayerSkipped(data:any) : OsuPacketWriter, + MatchPlayerSkipped(data:number) : OsuPacketWriter, UserPresence(data:any) : OsuPacketWriter, - Restart(data:any) : OsuPacketWriter, + Restart(data:number) : OsuPacketWriter, Invite(data:any) : OsuPacketWriter, ChannelListingComplete() : OsuPacketWriter, MatchChangePassword(data:any) : OsuPacketWriter, @@ -58,7 +60,7 @@ export default interface OsuPacketWriter { VersionUpdateForced() : OsuPacketWriter, SwitchServer(data:any) : OsuPacketWriter, AccountRestricted() : OsuPacketWriter, - RTX(data:any) : OsuPacketWriter, + RTX(data:string) : OsuPacketWriter, SwitchTourneyServer(data:any) : OsuPacketWriter toBuffer : Buffer diff --git a/server/interfaces/PresenceData.ts b/server/interfaces/PresenceData.ts new file mode 100644 index 0000000..d520f69 --- /dev/null +++ b/server/interfaces/PresenceData.ts @@ -0,0 +1,8 @@ +export default interface PresenceData { + status: number, + statusText: string, + beatmapId: number, + beatmapChecksum: string, + currentMods: number, + playMode: number, +} \ No newline at end of file diff --git a/server/interfaces/StatusUpdateData.ts b/server/interfaces/StatusUpdateData.ts new file mode 100644 index 0000000..58c48ba --- /dev/null +++ b/server/interfaces/StatusUpdateData.ts @@ -0,0 +1,15 @@ +export default interface StatusUpdateData { + userId: number, + status: number, + statusText: string, + beatmapChecksum: string, + currentMods: number, + playMode: number, + beatmapId: number, + rankedScore: number, + accuracy: number, + playCount: number, + totalScore: number, + rank: number, + performance: number +} \ No newline at end of file diff --git a/server/objects/FunkyArray.ts b/server/objects/FunkyArray.ts index cf2127c..83623ca 100644 --- a/server/objects/FunkyArray.ts +++ b/server/objects/FunkyArray.ts @@ -1,5 +1,5 @@ export default class FunkyArray { - private items:any = {}; + private items:{ [id: string]: T } = {}; private itemKeys:Array = Object.keys(this.items); private iterableArray:Array = new Array(); diff --git a/server/objects/Match.ts b/server/objects/Match.ts index 619fad1..9d343b9 100644 --- a/server/objects/Match.ts +++ b/server/objects/Match.ts @@ -397,11 +397,16 @@ export default class Match { allSkipped = false; } + const slotId = user.matchSlot?.slotId ?? Number.MIN_VALUE; + if (slotId === Number.MIN_VALUE) { + return; + } + // All players have finished playing, finish the match if (allSkipped) { const osuPacketWriter = osu.Bancho.Writer(); - osuPacketWriter.MatchPlayerSkipped(user.id); + osuPacketWriter.MatchPlayerSkipped(slotId); osuPacketWriter.MatchSkip(); this.matchStream.Send(osuPacketWriter.toBuffer); @@ -410,7 +415,7 @@ export default class Match { } else { const osuPacketWriter = osu.Bancho.Writer(); - osuPacketWriter.MatchPlayerSkipped(user.id); + osuPacketWriter.MatchPlayerSkipped(slotId); this.matchStream.Send(osuPacketWriter.toBuffer); } @@ -593,7 +598,7 @@ export default class Match { const osuPacketWriter = osu.Bancho.Writer(); - let queryData:Array = [ + const queryData:Array = [ this.matchId, this.roundId++, this.playMode, diff --git a/server/objects/User.ts b/server/objects/User.ts index 112e366..9e549c1 100644 --- a/server/objects/User.ts +++ b/server/objects/User.ts @@ -6,6 +6,7 @@ import StatusUpdate from "../packets/StatusUpdate"; import Shared from "../objects/Shared"; import Slot from "./Slot"; import Channel from "./Channel"; +import PresenceData from "../interfaces/PresenceData"; const rankingModes = [ "pp_raw", @@ -14,8 +15,6 @@ const rankingModes = [ ]; export default class User { - private static readonly EMPTY_BUFFER = Buffer.alloc(0); - public shared:Shared; public id:number; @@ -23,7 +22,7 @@ export default class User { public uuid:string; public readonly connectTime:number = Date.now(); public timeoutTime:number = Date.now() + 30000; - public queue:Buffer = User.EMPTY_BUFFER; + public queue:Buffer = Buffer.allocUnsafe(0); // Binato data public rankingMode:RankingModes = RankingModes.PP; @@ -80,11 +79,11 @@ export default class User { } clearQueue() { - this.queue = User.EMPTY_BUFFER; + this.queue = Buffer.allocUnsafe(0); } // Updates the user's current action - updatePresence(action:any) { + updatePresence(action:PresenceData) { this.actionID = action.status; this.actionText = action.statusText; this.beatmapChecksum = action.beatmapChecksum; diff --git a/server/packets/ChangeAction.ts b/server/packets/ChangeAction.ts index 30a4dd6..40149e9 100644 --- a/server/packets/ChangeAction.ts +++ b/server/packets/ChangeAction.ts @@ -1,7 +1,8 @@ +import PresenceData from "../interfaces/PresenceData"; import User from "../objects/User"; import StatusUpdate from "./StatusUpdate"; -export default function ChangeAction(user:User, data:any) { +export default function ChangeAction(user:User, data:PresenceData) { user.updatePresence(data); if (user.spectatorStream != null) {