fix everything

This commit is contained in:
Holly Stubbs 2023-09-10 12:59:22 +01:00
parent 148c2c341f
commit 3a07a892e1
Signed by: tgpholly
GPG key ID: B8583C4B7D18119E
63 changed files with 431 additions and 1032 deletions

2
.gitignore vendored
View file

@ -1,6 +1,6 @@
node_modules/ node_modules/
build/ build/
bundle/ combined.ts
tHMM.ds tHMM.ds
server-stats.log server-stats.log
config.json config.json

View file

@ -8,65 +8,54 @@ if (!existsSync("./config.json")) {
process.exit(1); process.exit(1);
} }
import { ChatHistory } from "./server/ChatHistory"; import ChatHistory from "./server/ChatHistory";
import { Config } from "./server/interfaces/Config"; import Config from "./server/interfaces/Config";
import compression from "compression"; import HandleRequest from "./server/BanchoServer";
import express from "express";
import { HandleRequest } from "./server/BanchoServer";
import { Shared } from "./server/objects/Shared";
import { Registry, collectDefaultMetrics } from "prom-client"; import { Registry, collectDefaultMetrics } from "prom-client";
import http from "http";
const config:Config = JSON.parse(readFileSync(__dirname + "/config.json").toString()) as Config; const config:Config = JSON.parse(readFileSync(__dirname + "/config.json").toString()) as Config;
const binatoApp:express.Application = express();
if (config["prometheus"]["enabled"]) { if (config["prometheus"]["enabled"]) {
const register:Registry = new Registry(); const register:Registry = new Registry();
register.setDefaultLabels({ app: "nodejs_binato" }); register.setDefaultLabels({ app: "nodejs_binato" });
collectDefaultMetrics({ register }); collectDefaultMetrics({ register });
const prometheusApp:express.Application = express(); const prometheusServer = http.createServer(async (req, res) => {
prometheusApp.get("/metrics", async (req, res) => { if (req.method === "GET") {
res.end(await register.metrics()); res.end(await register.metrics());
}
}); });
prometheusApp.listen(config["prometheus"]["port"], () => ConsoleHelper.printInfo(`Prometheus metrics listening at port ${config["prometheus"]["port"]}`)); prometheusServer.listen(config["prometheus"]["port"], () => ConsoleHelper.printInfo(`Prometheus metrics listening at port ${config["prometheus"]["port"]}`));
} else { } else {
ConsoleHelper.printWarn("Prometheus is disabled!"); ConsoleHelper.printWarn("Prometheus is disabled!");
} }
if (config["express"]["compression"]) {
binatoApp.use(compression());
ConsoleHelper.printInfo("Compression is enabled");
} else {
ConsoleHelper.printWarn("Compression is disabled");
}
const INDEX_PAGE:string = readFileSync("./web/serverPage.html").toString(); const INDEX_PAGE:string = readFileSync("./web/serverPage.html").toString();
binatoApp.use((req, res) => { const binatoServer = http.createServer((req, res) => {
let packet:Buffer = Buffer.alloc(0); let packet:Buffer = Buffer.alloc(0);
req.on("data", (chunk:Buffer) => packet = Buffer.concat([packet, chunk], packet.length + chunk.length)); req.on("data", (chunk:Buffer) => packet = Buffer.concat([packet, chunk], packet.length + chunk.length));
req.on("end", () => { req.on("end", () => {
switch (req.method) { switch (req.method) {
case "GET": case "GET":
if (req.url == "/" || req.url == "/index.html" || req.url == "/index") { if (req.url == "/" || req.url == "/index.html" || req.url == "/index") {
res.send(INDEX_PAGE); res.end(INDEX_PAGE);
} else if (req.url == "/chat") { } else if (req.url == "/chat") {
// I don't think this works?? // I don't think this works??
res.send(ChatHistory.GenerateForWeb()); res.end(ChatHistory.GenerateForWeb());
} }
break; break;
case "POST": case "POST":
HandleRequest(req, res, packet); HandleRequest(req, res, packet);
break; break;
default: default:
res.status(405).send("405 | Method not allowed!<hr>Binato"); res.writeHead(405);
break; res.end("Method not allowed");
break;
} }
}); });
}); });
binatoApp.listen(config.express.port, () => ConsoleHelper.printInfo(`Binato is up! Listening at port ${config.express.port}`)); binatoServer.listen(config.http.port, () => ConsoleHelper.printInfo(`Binato is up! Listening at port ${config.http.port}`));

View file

@ -1,4 +1,4 @@
export abstract class Constants { export default abstract class Constants {
public static readonly DEBUG = false; public static readonly DEBUG = false;
public static readonly PROTOCOL_VERSION = 19; public static readonly PROTOCOL_VERSION = 19;
} }

View file

@ -1,9 +1,8 @@
import { OsuPacketWriter } from "./server/interfaces/OsuPacketWriter"; import OsuPacketWriter from "./server/interfaces/OsuPacketWriter";
// Dummy file
const nodeOsu = require("osu-packet"); const nodeOsu = require("osu-packet");
export abstract class osu { export default abstract class osu {
static Bancho = { static Bancho = {
Writer: function() : OsuPacketWriter { Writer: function() : OsuPacketWriter {
return new nodeOsu.Bancho.Writer(); return new nodeOsu.Bancho.Writer();

737
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -18,25 +18,21 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"aes256": "^1.1.0", "aes256": "^1.1.0",
"compression": "^1.7.4",
"dyetty": "^1.0.1", "dyetty": "^1.0.1",
"express": "^4.18.2", "mysql2": "^3.6.1",
"mysql2": "^3.6.0", "node-fetch": "^2.7.0",
"node-fetch": "^2.6.13",
"osu-packet": "^4.1.2", "osu-packet": "^4.1.2",
"prom-client": "^14.2.0", "prom-client": "^14.2.0",
"redis": "^4.6.7" "redis": "^4.6.8"
}, },
"devDependencies": { "devDependencies": {
"@types/compression": "^1.7.2", "@types/node": "^20.6.0",
"@types/express": "^4.17.17",
"@types/node": "^20.5.1",
"@types/node-fetch": "^2.6.4", "@types/node-fetch": "^2.6.4",
"check-outdated": "^2.12.0", "check-outdated": "^2.12.0",
"nodemon": "^3.0.1", "nodemon": "^3.0.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"ts-loader": "^9.4.4", "ts-loader": "^9.4.4",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^5.1.6" "typescript": "^5.2.2"
} }
} }

View file

@ -1,15 +1,15 @@
import Channel from "./objects/Channel";
import { ConsoleHelper } from "../ConsoleHelper"; import { ConsoleHelper } from "../ConsoleHelper";
import { Channel } from "./objects/Channel"; import Constants from "../Constants";
import { LatLng } from "./objects/LatLng"; import LoginProcess from "./LoginProcess";
import { LoginProcess } from "./LoginProcess"; import { IncomingMessage, ServerResponse } from "http";
import { Packets } from "./enums/Packets"; import { Packets } from "./enums/Packets";
import { RedisClientType, createClient } from "redis"; import { RedisClientType, createClient } from "redis";
import { Request, Response } from "express"; import MessageData from "./interfaces/MessageData";
import { SpectatorManager } from "./SpectatorManager"; import PrivateMessage from "./packets/PrivateMessage";
import { User } from "./objects/User"; import Shared from "./objects/Shared";
import { PrivateMessage } from "./packets/PrivateMessage"; import SpectatorManager from "./SpectatorManager";
import { MessageData } from "./interfaces/MessageData"; import osu from "../osuTyping";
import { Shared } from "./objects/Shared";
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.query("UPDATE mp_matches SET close_time = UNIX_TIMESTAMP() WHERE close_time IS NULL");
@ -55,19 +55,17 @@ if (shared.config.redis.enabled) {
} else ConsoleHelper.printWarn("Redis is disabled!"); } else ConsoleHelper.printWarn("Redis is disabled!");
// Import packets // Import packets
import { ChangeAction } from "./packets/ChangeAction"; import ChangeAction from "./packets/ChangeAction";
import { Logout } from "./packets/Logout"; import Logout from "./packets/Logout";
import { UserPresence } from "./packets/UserPresence"; import UserPresence from "./packets/UserPresence";
import { UserStatsRequest } from "./packets/UserStatsRequest"; import UserStatsRequest from "./packets/UserStatsRequest";
import { UserPresenceBundle } from "./packets/UserPresenceBundle"; import UserPresenceBundle from "./packets/UserPresenceBundle";
import { TourneyMatchSpecialInfo } from "./packets/TourneyMatchSpecialInfo"; import TourneyMatchSpecialInfo from "./packets/TourneyMatchSpecialInfo";
import { osu } from "../osuTyping"; import TourneyMatchJoinChannel from "./packets/TourneyJoinMatchChannel";
import { TourneyMatchJoinChannel } from "./packets/TourneyJoinMatchChannel"; import TourneyMatchLeaveChannel from "./packets/TourneyMatchLeaveChannel";
import { TourneyMatchLeaveChannel } from "./packets/TourneyMatchLeaveChannel"; import AddFriend from "./packets/AddFriend";
import { AddFriend } from "./packets/AddFriend"; import RemoveFriend from "./packets/RemoveFriend";
import { RemoveFriend } from "./packets/RemoveFriend"; import PrivateChannel from "./objects/PrivateChannel";
import { PrivateChannel } from "./objects/PrivateChannel";
import { Constants } from "../Constants";
// User timeout interval // User timeout interval
setInterval(() => { setInterval(() => {
@ -83,30 +81,23 @@ setInterval(() => {
const EMPTY_BUFFER = Buffer.alloc(0); const EMPTY_BUFFER = Buffer.alloc(0);
export async function HandleRequest(req:Request, res:Response, packet:Buffer) { export default async function HandleRequest(req:IncomingMessage, res:ServerResponse, packet:Buffer) {
// Get the client's token string and request data // Get the client's token string and request data
const requestTokenString:string | undefined = req.header("osu-token"); const requestTokenString = typeof(req.headers["osu-token"]) === "string" ? req.headers["osu-token"] : undefined;
// Check if the user is logged in // Check if the user is logged in
if (requestTokenString == null) { if (requestTokenString === undefined) {
// Only do this if we're absolutely sure that we're connected to the DB // Client doesn't have a token yet, let's auth them!
if (shared.database.connected) {
// 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.query("UPDATE osu_info SET value = ? WHERE name = 'online_now'", [shared.users.getLength() - 1]);
}
} else { } else {
let responseData:Buffer | string = EMPTY_BUFFER; let responseData = EMPTY_BUFFER;
// Remove headers we don't need for Bancho
res.removeHeader('X-Powered-By');
res.removeHeader('Date'); // This is not spec compilant
// Client has a token, let's see what they want. // Client has a token, let's see what they want.
try { try {
// Get the current user // Get the current user
const PacketUser:User | undefined = shared.users.getByToken(requestTokenString); const PacketUser = shared.users.getByToken(requestTokenString);
// Make sure the client's token isn't invalid // Make sure the client's token isn't invalid
if (PacketUser != null) { if (PacketUser != null) {
@ -298,12 +289,12 @@ export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
responseData = PacketUser.queue; responseData = PacketUser.queue;
PacketUser.clearQueue(); PacketUser.clearQueue();
} else { } else {
// Only do this if we're absolutely sure that we're connected to the DB // User's token is invlid, force a reconnect
if (shared.database.connected) { ConsoleHelper.printBancho(`Forced client re-login (Token is invalid)`);
// User's token is invlid, force a reconnect const osuPacketWriter = osu.Bancho.Writer();
ConsoleHelper.printBancho(`Forced client re-login (Token is invalid)`); osuPacketWriter.Announce("Reconnecting...");
responseData = "\u0005\u0000\u0000\u0004\u0000\u0000\u0000<30><30><EFBFBD><EFBFBD>\u0018\u0000\u0000\u0011\u0000\u0000\u0000\u000b\u000fReconnecting..."; osuPacketWriter.Restart(0);
} responseData = osuPacketWriter.toBuffer;
} }
} catch (e) { } catch (e) {
if (Constants.DEBUG) { if (Constants.DEBUG) {

View file

@ -1,16 +1,16 @@
import { ICommand } from "./interfaces/ICommand"; import ICommand from "./interfaces/ICommand";
import { Channel } from "./objects/Channel"; import Channel from "./objects/Channel";
import { Shared } from "./objects/Shared"; import Shared from "./objects/Shared";
import { User } from "./objects/User"; import User from "./objects/User";
// Commands // Commands
import { RankingCommand } from "./commands/Ranking"; import RankingCommand from "./commands/Ranking";
import { LockCommand } from "./commands/Lock"; import LockCommand from "./commands/Lock";
import { MultiplayerCommands } from "./commands/Multiplayer"; import MultiplayerCommands from "./commands/Multiplayer";
import { HelpCommand } from "./commands/Help"; import HelpCommand from "./commands/Help";
import { RollCommand } from "./commands/RollCommand"; import RollCommand from "./commands/RollCommand";
export class Bot { export default class Bot {
public user:User; public user:User;
private commands:{ [id: string]: ICommand } = {}; private commands:{ [id: string]: ICommand } = {};

View file

@ -1,6 +1,6 @@
import { readFileSync } from "fs"; import { readFileSync } from "fs";
export abstract class ChatHistory { export default abstract class ChatHistory {
private static _history:Array<string> = new Array<string>(); private static _history:Array<string> = new Array<string>();
private static _lastGeneratedPage:string; private static _lastGeneratedPage:string;
private static _hasChanged:boolean = true; private static _hasChanged:boolean = true;
@ -8,7 +8,7 @@ export abstract class ChatHistory {
private static readonly PAGE_TEMPLATE = readFileSync("./web/chatPageTemplate.html").toString(); private static readonly PAGE_TEMPLATE = readFileSync("./web/chatPageTemplate.html").toString();
public static AddMessage(message:string) : void { public static AddMessage(message:string) : void {
if (this._history.length === 10) { if (this._history.length === this.HISTORY_LENGTH) {
this._history.splice(0, 1); this._history.splice(0, 1);
} }
@ -17,14 +17,14 @@ export abstract class ChatHistory {
} }
public static GenerateForWeb() : string { public static GenerateForWeb() : string {
let lines:string = "", flip:boolean = false;
for (let i:number = Math.max(this._history.length - this.HISTORY_LENGTH, this.HISTORY_LENGTH); i < this._history.length; i++) {
lines += `<div class="line line${flip ? 1 : 0}">${this._history[i] == null ? "<hidden>blank</hidden>" : this._history[i]}</div>`
flip = !flip;
}
if (this._hasChanged) { if (this._hasChanged) {
let lines = "", flip = false;
for (let i = 0; i < this.HISTORY_LENGTH; i++) {
lines += `<div class="line line${flip ? 1 : 0}">${this._history[i] == null ? "<hidden>blank</hidden>" : this._history[i].replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\n", "<br>")}</div>`
flip = !flip;
}
this._lastGeneratedPage = this.PAGE_TEMPLATE.toString().replace("|content|", lines); this._lastGeneratedPage = this.PAGE_TEMPLATE.toString().replace("|content|", lines);
this._hasChanged = false; this._hasChanged = false;
} }

View file

@ -1,12 +1,12 @@
import { Channel } from "./objects/Channel"; import Channel from "./objects/Channel";
import { ConsoleHelper } from "../ConsoleHelper"; import { ConsoleHelper } from "../ConsoleHelper";
import { FunkyArray } from "./objects/FunkyArray"; import FunkyArray from "./objects/FunkyArray";
import { User } from "./objects/User"; import User from "./objects/User";
import { Shared } from "./objects/Shared"; import Shared from "./objects/Shared";
import { osu } from "../osuTyping"; import osu from "../osuTyping";
import { PrivateChannel } from "./objects/PrivateChannel"; import PrivateChannel from "./objects/PrivateChannel";
export class ChatManager { export default class ChatManager {
public chatChannels:FunkyArray<Channel> = new FunkyArray<Channel>(); public chatChannels:FunkyArray<Channel> = new FunkyArray<Channel>();
public forceJoinChannels:FunkyArray<Channel> = new FunkyArray<Channel>(); public forceJoinChannels:FunkyArray<Channel> = new FunkyArray<Channel>();
private readonly shared:Shared; private readonly shared:Shared;

View file

@ -254,7 +254,7 @@ enum CountryCodes {
const keys = Object.keys(CountryCodes); const keys = Object.keys(CountryCodes);
const values = Object.values(CountryCodes); const values = Object.values(CountryCodes);
export function getCountryID(code:string) : number { export default function getCountryID(code:string) : number {
// Get id of a country from a 2 char code // Get id of a country from a 2 char code
const upperCode:string = code.toUpperCase(); const upperCode:string = code.toUpperCase();
if (upperCode in CountryCodes) { if (upperCode in CountryCodes) {

View file

@ -1,19 +1,19 @@
import { ConsoleHelper } from "../ConsoleHelper"; import { ConsoleHelper } from "../ConsoleHelper";
import fetch from "node-fetch"; import fetch from "node-fetch";
import { getCountryID } from "./Country"; import getCountryID from "./Country";
import { generateSession } from "./Util"; import { generateSession } from "./Util";
import { LatLng } from "./objects/LatLng"; import LatLng from "./objects/LatLng";
import { LoginInfo } from "./objects/LoginInfo"; import LoginInfo from "./objects/LoginInfo";
import { Logout } from "./packets/Logout"; import Logout from "./packets/Logout";
import { pbkdf2 } from "crypto"; import { pbkdf2 } from "crypto";
import { Request, Response } from "express"; import User from "./objects/User";
import { User } from "./objects/User"; import UserPresenceBundle from "./packets/UserPresenceBundle";
import { UserPresenceBundle } from "./packets/UserPresenceBundle"; import UserPresence from "./packets/UserPresence";
import { UserPresence } from "./packets/UserPresence"; import StatusUpdate from "./packets/StatusUpdate";
import { StatusUpdate } from "./packets/StatusUpdate"; import Shared from "./objects/Shared";
import { Shared } from "./objects/Shared"; import osu from "../osuTyping";
import { osu } from "../osuTyping"; import IpZxqResponse from "./interfaces/IpZxqResponse";
import { IpZxqResponse } from "./interfaces/IpZxqResponse"; import { IncomingMessage, ServerResponse } from "http";
const { decrypt: aesDecrypt } = require("aes256"); const { decrypt: aesDecrypt } = require("aes256");
const incorrectLoginResponse:Buffer = osu.Bancho.Writer().LoginReply(-1).toBuffer; const incorrectLoginResponse:Buffer = osu.Bancho.Writer().LoginReply(-1).toBuffer;
@ -43,7 +43,6 @@ function TestLogin(loginInfo:LoginInfo, shared:Shared) {
// Make sure the username is the same as the login info // Make sure the username is the same as the login info
if (userDBData.username !== loginInfo.username) return resolve(LoginResult.INCORRECT); if (userDBData.username !== loginInfo.username) return resolve(LoginResult.INCORRECT);
console.log(userDBData.has_old_password);
switch (userDBData.has_old_password) { switch (userDBData.has_old_password) {
case LoginTypes.CURRENT: case LoginTypes.CURRENT:
pbkdf2(loginInfo.password, userDBData.password_salt, shared.config.database.pbkdf2.itterations, shared.config.database.pbkdf2.keylength, "sha512", (err, derivedKey) => { pbkdf2(loginInfo.password, userDBData.password_salt, shared.config.database.pbkdf2.itterations, shared.config.database.pbkdf2.keylength, "sha512", (err, derivedKey) => {
@ -58,11 +57,9 @@ function TestLogin(loginInfo:LoginInfo, shared:Shared) {
}); });
break; break;
case LoginTypes.OLD_AES: case LoginTypes.OLD_AES:
console.log("OLD AES");
if (aesDecrypt(shared.config.database.key, userDBData.password_hash) !== loginInfo.password) { if (aesDecrypt(shared.config.database.key, userDBData.password_hash) !== loginInfo.password) {
return resolve(LoginResult.INCORRECT); return resolve(LoginResult.INCORRECT);
} }
console.log("correct password");
return resolve(LoginResult.MIGRATION); return resolve(LoginResult.MIGRATION);
case LoginTypes.OLD_MD5: case LoginTypes.OLD_MD5:
if (userDBData.password_hash !== loginInfo.password) { if (userDBData.password_hash !== loginInfo.password) {
@ -73,7 +70,7 @@ function TestLogin(loginInfo:LoginInfo, shared:Shared) {
}); });
} }
export async function LoginProcess(req:Request, res:Response, packet:Buffer, shared:Shared) { export default async function LoginProcess(req:IncomingMessage, res:ServerResponse, packet:Buffer, shared:Shared) {
const loginStartTime = Date.now(); const loginStartTime = Date.now();
const loginInfo = LoginInfo.From(packet); const loginInfo = LoginInfo.From(packet);
@ -93,11 +90,11 @@ export async function LoginProcess(req:Request, res:Response, packet:Buffer, sha
// Get users IP for getting location // Get users IP for getting location
// Get cloudflare requestee IP first // Get cloudflare requestee IP first
let requestIP = req.get("cf-connecting-ip"); let requestIP = req.headers["cf-connecting-ip"];
// Get IP of requestee since we are probably behind a reverse proxy // Get IP of requestee since we are probably behind a reverse proxy
if (requestIP === undefined) { if (requestIP === undefined) {
requestIP = req.get("X-Real-IP"); requestIP = req.headers["X-Real-IP"];
} }
// Just get the requestee IP (we are not behind a reverse proxy) // Just get the requestee IP (we are not behind a reverse proxy)
@ -196,7 +193,7 @@ export async function LoginProcess(req:Request, res:Response, packet:Buffer, sha
osuPacketWriter.Announce(`Welcome back ${loginInfo.username}!`); osuPacketWriter.Announce(`Welcome back ${loginInfo.username}!`);
// TODO: Remove once merged into master // TODO: Remove once merged into master
osuPacketWriter.Announce("WARNING\nThis is a development test server made for the TypeScript rewrite.\nAnything could happen be it data loss, catastrophic crashes or otherwise.\nHere be dragons."); osuPacketWriter.Announce("Heads up!\nWhile the TypeScript server rewrite is mostly stable it still has some issues.");
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
@ -208,10 +205,10 @@ export async function LoginProcess(req:Request, res:Response, packet:Buffer, sha
const writerBuffer:Buffer = osuPacketWriter.toBuffer; const writerBuffer:Buffer = osuPacketWriter.toBuffer;
if (newUser === undefined) { if (newUser === undefined) {
res.writeHead(200, { res.writeHead(200, {
"cho-token": "no",
"Connection": "keep-alive", "Connection": "keep-alive",
"Keep-Alive": "timeout=5, max=100" "Keep-Alive": "timeout=5, max=100"
}); });
console.log(res.headersSent);
switch (loginResult) { switch (loginResult) {
case LoginResult.INCORRECT: case LoginResult.INCORRECT:
res.end(incorrectLoginResponse, () => { res.end(incorrectLoginResponse, () => {

View file

@ -1,18 +1,18 @@
import { Channel } from "./objects/Channel"; import Channel from "./objects/Channel";
import { Shared } from "./objects/Shared"; import Shared from "./objects/Shared";
import { SlotStatus } from "./enums/SlotStatus"; import { SlotStatus } from "./enums/SlotStatus";
import { DataStream } from "./objects/DataStream"; import DataStream from "./objects/DataStream";
import { Match } from "./objects/Match"; import Match from "./objects/Match";
import { User } from "./objects/User"; import User from "./objects/User";
import { StatusUpdate } from "./packets/StatusUpdate"; import StatusUpdate from "./packets/StatusUpdate";
import { UserPresence } from "./packets/UserPresence"; import UserPresence from "./packets/UserPresence";
import { UserPresenceBundle } from "./packets/UserPresenceBundle"; import UserPresenceBundle from "./packets/UserPresenceBundle";
import { MatchArray } from "./objects/MatchArray"; import MatchArray from "./objects/MatchArray";
import { MatchJoinData } from "./interfaces/MatchJoinData"; import MatchJoinData from "./interfaces/MatchJoinData";
import { MatchData } from "./interfaces/MatchData"; import MatchData from "./interfaces/MatchData";
import { osu } from "../osuTyping"; import osu from "../osuTyping";
export class MultiplayerManager { export default class MultiplayerManager {
private readonly shared:Shared; private readonly shared:Shared;
private matches:MatchArray = new MatchArray(); private matches:MatchArray = new MatchArray();
private readonly lobbyStream:DataStream; private readonly lobbyStream:DataStream;

View file

@ -1,12 +1,12 @@
import { Channel } from "./objects/Channel"; import Channel from "./objects/Channel";
import { ConsoleHelper } from "../ConsoleHelper"; import { ConsoleHelper } from "../ConsoleHelper";
import { FunkyArray } from "./objects/FunkyArray"; import FunkyArray from "./objects/FunkyArray";
import { User } from "./objects/User"; import User from "./objects/User";
import { Shared } from "./objects/Shared"; import Shared from "./objects/Shared";
import { osu } from "../osuTyping"; import osu from "../osuTyping";
import { PrivateChannel } from "./objects/PrivateChannel"; import PrivateChannel from "./objects/PrivateChannel";
export class PrivateChatManager { export default class PrivateChatManager {
public chatChannels:FunkyArray<PrivateChannel> = new FunkyArray<PrivateChannel>(); public chatChannels:FunkyArray<PrivateChannel> = new FunkyArray<PrivateChannel>();
private readonly shared:Shared; private readonly shared:Shared;

View file

@ -1,9 +1,9 @@
import { DataStream } from "./objects/DataStream"; import DataStream from "./objects/DataStream";
import { Shared } from "./objects/Shared"; import Shared from "./objects/Shared";
import { User } from "./objects/User"; import User from "./objects/User";
import { osu } from "../osuTyping"; import osu from "../osuTyping";
export class SpectatorManager { export default class SpectatorManager {
private shared:Shared; private shared:Shared;
public constructor(shared:Shared) { public constructor(shared:Shared) {

View file

@ -12,10 +12,6 @@ export function generateSession() : Promise<string> {
}); });
} }
export function generateSessionSync() : string {
return randomBytes(12).toString("hex");
}
export function hexlify(data:Buffer) : string { export function hexlify(data:Buffer) : string {
let out:string = ""; let out:string = "";
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {

View file

@ -1,9 +1,9 @@
import { ICommand } from "../interfaces/ICommand"; import ICommand from "../interfaces/ICommand";
import { Channel } from "../objects/Channel"; import Channel from "../objects/Channel";
import { Shared } from "../objects/Shared"; import Shared from "../objects/Shared";
import { User } from "../objects/User"; import User from "../objects/User";
export class BaseCommand implements ICommand { export default class BaseCommand implements ICommand {
public shared:Shared; public shared:Shared;
public readonly helpText:string = "No help page was found for that command"; public readonly helpText:string = "No help page was found for that command";
public readonly helpDescription:string = "Command has no description set"; public readonly helpDescription:string = "Command has no description set";

View file

@ -1,10 +1,10 @@
import { Channel } from "../objects/Channel"; import Channel from "../objects/Channel";
import { User } from "../objects/User"; import User from "../objects/User";
import { BaseCommand } from "./BaseCommand"; import BaseCommand from "./BaseCommand";
import { Shared } from "../objects/Shared"; import Shared from "../objects/Shared";
import { ICommand } from "../interfaces/ICommand"; import ICommand from "../interfaces/ICommand";
export class HelpCommand extends BaseCommand { export default class HelpCommand extends BaseCommand {
public readonly helpDescription:string = "Shows this message! :)"; public readonly helpDescription:string = "Shows this message! :)";
private readonly commandList:{ [id:string]: ICommand }; private readonly commandList:{ [id:string]: ICommand };

View file

@ -1,8 +1,8 @@
import { Channel } from "../objects/Channel"; import Channel from "../objects/Channel";
import { User } from "../objects/User"; import User from "../objects/User";
import { BaseCommand } from "./BaseCommand"; import BaseCommand from "./BaseCommand";
export class LockCommand extends BaseCommand { export default class LockCommand extends BaseCommand {
public readonly helpDescription:string = "Locks/Unlocks a channel and limits conversation to mods and above."; public readonly helpDescription:string = "Locks/Unlocks a channel and limits conversation to mods and above.";
public exec(channel:Channel, sender:User, args:Array<string>) { public exec(channel:Channel, sender:User, args:Array<string>) {

View file

@ -1,15 +1,13 @@
import { Channel } from "../objects/Channel"; import Channel from "../objects/Channel";
import { User } from "../objects/User"; import User from "../objects/User";
import { Match } from "../objects/Match"; import Match from "../objects/Match";
import { BaseCommand } from "./BaseCommand"; import BaseCommand from "./BaseCommand";
const helpText = `Multiplayer Subcommands: export default class MultiplayerCommands extends BaseCommand {
public readonly helpText:string = `Multiplayer Subcommands:
!mp start - Starts a multiplayer match with a delay (optional) !mp start - Starts a multiplayer match with a delay (optional)
!mp abort - Aborts the currently running round / countdown !mp abort - Aborts the currently running round / countdown
!mp obr - Toggles Battle Royale mode`; !mp obr - Toggles Battle Royale mode`;
export class MultiplayerCommands extends BaseCommand {
public readonly helpText:string = helpText;
public readonly helpDescription:string = "Command for use in multiplayer matches."; public readonly helpDescription:string = "Command for use in multiplayer matches.";
public readonly helpArguments:Array<string> = ["subCommand"]; public readonly helpArguments:Array<string> = ["subCommand"];

View file

@ -1,15 +1,13 @@
import { Channel } from "../objects/Channel"; import Channel from "../objects/Channel";
import { User } from "../objects/User"; import User from "../objects/User";
import { RankingModes } from "../enums/RankingModes"; import { RankingModes } from "../enums/RankingModes";
import { BaseCommand } from "./BaseCommand"; import BaseCommand from "./BaseCommand";
const helpText = `Ranking Modes: export default class RankingCommand extends BaseCommand {
public readonly helpText:string = `Ranking Modes:
!ranking pp - Sets your ranking mode to pp !ranking pp - Sets your ranking mode to pp
!ranking score - Sets your ranking mode to score !ranking score - Sets your ranking mode to score
!ranking acc - Sets your ranking mode to accuracy`; !ranking acc - Sets your ranking mode to accuracy`;
export class RankingCommand extends BaseCommand {
public readonly helpText:string = helpText;
public readonly helpDescription:string = "Sets your prefered ranking type"; public readonly helpDescription:string = "Sets your prefered ranking type";
public exec(channel:Channel, sender:User, args:Array<string>) { public exec(channel:Channel, sender:User, args:Array<string>) {

View file

@ -1,8 +1,8 @@
import { Channel } from "../objects/Channel"; import Channel from "../objects/Channel";
import { User } from "../objects/User"; import User from "../objects/User";
import { BaseCommand } from "./BaseCommand"; import BaseCommand from "./BaseCommand";
export class RollCommand extends BaseCommand { export default class RollCommand extends BaseCommand {
public readonly helpDescription:string = "Roll some dice and get a random number between 1 and a number (default 100)"; public readonly helpDescription:string = "Roll some dice and get a random number between 1 and a number (default 100)";
public readonly helpArguments:Array<string> = ["number"]; public readonly helpArguments:Array<string> = ["number"];

View file

@ -1,11 +1,11 @@
export interface Config { export default interface Config {
express:ExpressConfigSection, http:HttpConfigSection,
prometheus:PrometheusConfigSection, prometheus:PrometheusConfigSection,
redis:RedisConfigSection, redis:RedisConfigSection,
database:DatabaseConfigSection database:DatabaseConfigSection
} }
interface ExpressConfigSection { interface HttpConfigSection {
port:number, port:number,
compression:boolean compression:boolean
} }

View file

@ -1,8 +1,8 @@
import { Channel } from "../objects/Channel"; import Channel from "../objects/Channel";
import { Shared } from "../objects/Shared"; import Shared from "../objects/Shared";
import { User } from "../objects/User"; import User from "../objects/User";
export interface ICommand { export default interface ICommand {
shared:Shared, shared:Shared,
helpText:string, helpText:string,
helpDescription:string, helpDescription:string,

View file

@ -1,4 +1,4 @@
export interface IpZxqResponse { export default interface IpZxqResponse {
country: string, country: string,
loc: string loc: string
} }

View file

@ -1,6 +1,6 @@
import { MatchDataSlot } from "./MatchDataSlot"; import MatchDataSlot from "./MatchDataSlot";
export interface MatchData { export default interface MatchData {
matchId:number, matchId:number,
matchType:number, matchType:number,
activeMods:number, activeMods:number,

View file

@ -1,4 +1,4 @@
export interface MatchDataSlot { export default interface MatchDataSlot {
status:number, status:number,
team:number, team:number,
playerId:number, playerId:number,

View file

@ -1,4 +1,4 @@
export interface MatchJoinData { export default interface MatchJoinData {
matchId: number, matchId: number,
gamePassword: string gamePassword: string
} }

View file

@ -1,4 +1,4 @@
export interface MatchScoreData { export default interface MatchScoreData {
time:number, time:number,
id:number, id:number,
count300:number, count300:number,

View file

@ -1,4 +1,4 @@
export interface MatchStartSkipData { export default interface MatchStartSkipData {
playerId:number, playerId:number,
flag:boolean flag:boolean
} }

View file

@ -1,4 +1,4 @@
export interface MessageData { export default interface MessageData {
sendingClient: string, sendingClient: string,
message: string, message: string,
target: string, target: string,

View file

@ -1,7 +1,7 @@
import { MatchData } from "./MatchData" import MatchData from "./MatchData"
import { MessageData } from "./MessageData" import MessageData from "./MessageData"
export interface OsuPacketWriter { export default interface OsuPacketWriter {
// Functions // Functions
LoginReply(data:number) : OsuPacketWriter, LoginReply(data:number) : OsuPacketWriter,
CommandError() : OsuPacketWriter, CommandError() : OsuPacketWriter,

View file

@ -1,8 +1,8 @@
import { Slot } from "../objects/Slot"; import Slot from "../objects/Slot";
import { User } from "../objects/User"; import User from "../objects/User";
import { MatchScoreData } from "./MatchScoreData"; import MatchScoreData from "./MatchScoreData";
export interface PlayerScore { export default interface PlayerScore {
player:User, player:User,
slot:Slot, slot:Slot,
score:number, score:number,

View file

@ -1,10 +1,11 @@
import { osu } from "../../osuTyping"; import osu from "../../osuTyping";
import { Bot } from "../Bot"; import Bot from "../Bot";
import { Shared } from "../objects/Shared"; import ChatHistory from "../ChatHistory";
import { DataStream } from "./DataStream"; import Shared from "../objects/Shared";
import { User } from "./User"; import DataStream from "./DataStream";
import User from "./User";
export class Channel { export default class Channel {
public name:string; public name:string;
public description:string; public description:string;
public stream:DataStream; public stream:DataStream;
@ -40,6 +41,9 @@ export class Channel {
senderId: sender.id senderId: sender.id
}); });
this.stream.SendWithExclusion(osuPacketWriter.toBuffer, sender); this.stream.SendWithExclusion(osuPacketWriter.toBuffer, sender);
if (this.name === "#osu") {
ChatHistory.AddMessage(`${sender.username}: ${message}`);
}
} }
if (message[0] === "!") { if (message[0] === "!") {
@ -63,6 +67,10 @@ export class Channel {
} else { } else {
this.stream.Send(osuPacketWriter.toBuffer); this.stream.Send(osuPacketWriter.toBuffer);
} }
if (this.name === "#osu") {
ChatHistory.AddMessage(`${this.bot.user.username}: ${message}`);
}
} }
public SendSystemMessage(message:string, sendTo?:User) { public SendSystemMessage(message:string, sendTo?:User) {

View file

@ -1,13 +1,13 @@
import { ConsoleHelper } from "../../ConsoleHelper"; import { ConsoleHelper } from "../../ConsoleHelper";
import { Constants } from "../../Constants"; import Constants from "../../Constants";
import { DataStreamArray } from "./DataStreamArray"; import DataStreamArray from "./DataStreamArray";
import { User } from "./User"; import User from "./User";
import { UserArray } from "./UserArray"; import UserArray from "./UserArray";
import { hexlify } from "../Util"; import { hexlify } from "../Util";
type DeleteFunction = (dataStream:DataStream) => void; type DeleteFunction = (dataStream:DataStream) => void;
export class DataStream { export default class DataStream {
private users:UserArray = new UserArray(); private users:UserArray = new UserArray();
public readonly name:string; public readonly name:string;
private readonly parent:DataStreamArray; private readonly parent:DataStreamArray;

View file

@ -1,9 +1,9 @@
import { ConsoleHelper } from "../../ConsoleHelper"; import { ConsoleHelper } from "../../ConsoleHelper";
import { DataStream } from "./DataStream"; import DataStream from "./DataStream";
import { FunkyArray } from "./FunkyArray"; import FunkyArray from "./FunkyArray";
import { User } from "./User"; import User from "./User";
export class DataStreamArray extends FunkyArray<DataStream> { export default class DataStreamArray extends FunkyArray<DataStream> {
public CreateStream(name:string, removeWhenEmpty:boolean = true) : DataStream { public CreateStream(name:string, removeWhenEmpty:boolean = true) : DataStream {
const dataStream:DataStream = this.add(name, new DataStream(name, this, removeWhenEmpty)); const dataStream:DataStream = this.add(name, new DataStream(name, this, removeWhenEmpty));
ConsoleHelper.printStream(`Created stream [${name}]`); ConsoleHelper.printStream(`Created stream [${name}]`);

View file

@ -1,7 +1,7 @@
import { ConsoleHelper } from "../../ConsoleHelper"; import { ConsoleHelper } from "../../ConsoleHelper";
import { createPool, Pool } from "mysql2"; import { createPool, Pool } from "mysql2";
export class Database { export default class Database {
private connectionPool:Pool; private connectionPool:Pool;
private static readonly CONNECTION_LIMIT = 128; private static readonly CONNECTION_LIMIT = 128;

View file

@ -1,4 +1,4 @@
export class FunkyArray<T> { export default class FunkyArray<T> {
private items:any = {}; private items:any = {};
private itemKeys:Array<string> = Object.keys(this.items); private itemKeys:Array<string> = Object.keys(this.items);
private iterableArray:Array<T> = new Array<T>(); private iterableArray:Array<T> = new Array<T>();

View file

@ -1,4 +1,4 @@
export class LatLng { export default class LatLng {
public latitude:number; public latitude:number;
public longitude:number; public longitude:number;

View file

@ -1,4 +1,4 @@
export class LoginInfo { export default class LoginInfo {
public username:string; public username:string;
public password:string; public password:string;
public version:string; public version:string;
@ -19,8 +19,6 @@ export class LoginInfo {
data = data.toString(); data = data.toString();
} }
console.log(data);
const loginData:Array<string> = data.split("\n"); const loginData:Array<string> = data.split("\n");
const extraData:Array<string> = loginData[2].split("|"); const extraData:Array<string> = loginData[2].split("|");

View file

@ -1,19 +1,19 @@
import { Channel } from "./Channel"; import Channel from "./Channel";
import { Shared } from "../objects/Shared"; import Shared from "../objects/Shared";
import { DataStream } from "./DataStream"; import DataStream from "./DataStream";
import { Slot } from "./Slot"; import Slot from "./Slot";
import { User } from "./User"; import User from "./User";
import { StatusUpdate } from "../packets/StatusUpdate"; import StatusUpdate from "../packets/StatusUpdate";
import { SlotStatus } from "../enums/SlotStatus"; import { SlotStatus } from "../enums/SlotStatus";
import { MatchData } from "../interfaces/MatchData"; import MatchData from "../interfaces/MatchData";
import { Team } from "../enums/Team"; import { Team } from "../enums/Team";
import { MatchStartSkipData } from "../interfaces/MatchStartSkipData"; import MatchStartSkipData from "../interfaces/MatchStartSkipData";
import { Mods } from "../enums/Mods"; import { Mods } from "../enums/Mods";
import { PlayerScore } from "../interfaces/PlayerScore"; import PlayerScore from "../interfaces/PlayerScore";
import { MatchScoreData } from "../interfaces/MatchScoreData"; import MatchScoreData from "../interfaces/MatchScoreData";
import { osu } from "../../osuTyping"; import osu from "../../osuTyping";
export class Match { export default class Match {
// osu! Data // osu! Data
public matchId:number = -1; public matchId:number = -1;
public inProgress:boolean = false; public inProgress:boolean = false;
@ -236,8 +236,6 @@ export class Match {
// Update all users in the match with new match information // Update all users in the match with new match information
this.matchStream.Send(osuPacketWriter.toBuffer); this.matchStream.Send(osuPacketWriter.toBuffer);
console.log(this.slots);
} }
public moveToSlot(user:User, slotToMoveTo:number) { public moveToSlot(user:User, slotToMoveTo:number) {
@ -448,7 +446,6 @@ export class Match {
this.matchLoadSlots = new Array<MatchStartSkipData>(); this.matchLoadSlots = new Array<MatchStartSkipData>();
// Loop through all slots in the match // Loop through all slots in the match
console.log(this.slots);
for (let slot of this.slots) { for (let slot of this.slots) {
// Make sure the slot has a user in it // Make sure the slot has a user in it
if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) { if (slot.player === undefined || slot.status === SlotStatus.Empty || slot.status === SlotStatus.Locked) {

View file

@ -1,7 +1,7 @@
import { FunkyArray } from "./FunkyArray"; import FunkyArray from "./FunkyArray";
import { Match } from "./Match"; import Match from "./Match";
export class MatchArray extends FunkyArray<Match> { export default class MatchArray extends FunkyArray<Match> {
public getById(id:number) : Match | undefined { public getById(id:number) : Match | undefined {
for (let match of this.getIterableItems()) { for (let match of this.getIterableItems()) {
if (match.matchId === id) { if (match.matchId === id) {

View file

@ -1,10 +1,10 @@
import { osu } from "../../osuTyping"; import osu from "../../osuTyping";
import { Shared } from "../objects/Shared"; import Shared from "../objects/Shared";
import { Channel } from "./Channel"; import Channel from "./Channel";
import { DataStream } from "./DataStream"; import DataStream from "./DataStream";
import { User } from "./User"; import User from "./User";
export class PrivateChannel extends Channel { export default class PrivateChannel extends Channel {
private readonly user0:User; private readonly user0:User;
private readonly user1:User; private readonly user1:User;

View file

@ -1,16 +1,16 @@
import { ChatManager } from "../ChatManager"; import ChatManager from "../ChatManager";
import { Config } from "../interfaces/Config"; import Config from "../interfaces/Config";
import { Database } from "../objects/Database"; import Database from "../objects/Database";
import { DataStreamArray } from "../objects/DataStreamArray"; import DataStreamArray from "../objects/DataStreamArray";
import { MultiplayerManager } from "../MultiplayerManager"; import MultiplayerManager from "../MultiplayerManager";
import { PrivateChatManager } from "../PrivateChatManager"; import PrivateChatManager from "../PrivateChatManager";
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import { UserArray } from "../objects/UserArray"; import UserArray from "../objects/UserArray";
import { User } from "./User"; import User from "./User";
import { LatLng } from "./LatLng"; import LatLng from "./LatLng";
import { Bot } from "../Bot"; import Bot from "../Bot";
export class Shared { export default class Shared {
public readonly chatManager:ChatManager; public readonly chatManager:ChatManager;
public readonly config:Config; public readonly config:Config;
public readonly database:Database; public readonly database:Database;

View file

@ -1,8 +1,8 @@
import { Mods } from "../enums/Mods"; import { Mods } from "../enums/Mods";
import { SlotStatus } from "../enums/SlotStatus"; import { SlotStatus } from "../enums/SlotStatus";
import { User } from "./User"; import User from "./User";
export class Slot { export default class Slot {
public readonly slotId:number; public readonly slotId:number;
public status:SlotStatus; public status:SlotStatus;
public team:number; public team:number;

View file

@ -1,10 +1,10 @@
import { LatLng } from "./LatLng"; import LatLng from "./LatLng";
import { RankingModes } from "../enums/RankingModes"; import { RankingModes } from "../enums/RankingModes";
import { Match } from "./Match"; import Match from "./Match";
import { DataStream } from "./DataStream"; import DataStream from "./DataStream";
import { StatusUpdate } from "../packets/StatusUpdate"; import StatusUpdate from "../packets/StatusUpdate";
import { Shared } from "../objects/Shared"; import Shared from "../objects/Shared";
import { Slot } from "./Slot"; import Slot from "./Slot";
const rankingModes = [ const rankingModes = [
"pp_raw", "pp_raw",
@ -12,7 +12,7 @@ const rankingModes = [
"avg_accuracy" "avg_accuracy"
]; ];
export class User { export default class User {
private static readonly EMPTY_BUFFER = Buffer.alloc(0); private static readonly EMPTY_BUFFER = Buffer.alloc(0);
public shared:Shared; public shared:Shared;

View file

@ -1,7 +1,7 @@
import { FunkyArray } from "./FunkyArray"; import FunkyArray from "./FunkyArray";
import { User } from "./User"; import User from "./User";
export class UserArray extends FunkyArray<User> { export default class UserArray extends FunkyArray<User> {
public getById(id:number) : User | undefined { public getById(id:number) : User | undefined {
for (let user of this.getIterableItems()) { for (let user of this.getIterableItems()) {
if (user.id == id) if (user.id == id)

View file

@ -1,4 +1,4 @@
export class UserInfo { export default class UserInfo {
id:number = Number.MIN_VALUE; id:number = Number.MIN_VALUE;
username:string = ""; username:string = "";
username_safe:string = ""; username_safe:string = "";

View file

@ -1,6 +1,6 @@
import { User } from "../objects/User"; import User from "../objects/User";
export function AddFriend(user:User, friendId:number) { export default function AddFriend(user:User, friendId:number) {
user.shared.database.query("INSERT INTO friends (user, friendsWith) VALUES (?, ?)", [ user.shared.database.query("INSERT INTO friends (user, friendsWith) VALUES (?, ?)", [
user.id, friendId user.id, friendId
]); ]);

View file

@ -1,7 +1,7 @@
import { User } from "../objects/User"; import User from "../objects/User";
import { StatusUpdate } from "./StatusUpdate"; import StatusUpdate from "./StatusUpdate";
export function ChangeAction(user:User, data:any) { export default function ChangeAction(user:User, data:any) {
user.updatePresence(data); user.updatePresence(data);
if (user.spectatorStream != null) { if (user.spectatorStream != null) {

View file

@ -1,9 +1,9 @@
import { ConsoleHelper } from "../../ConsoleHelper"; import { ConsoleHelper } from "../../ConsoleHelper";
import { Database } from "../objects/Database"; import Database from "../objects/Database";
import { DataStreamArray } from "../objects/DataStreamArray"; import DataStreamArray from "../objects/DataStreamArray";
import { User } from "../objects/User"; import User from "../objects/User";
export async function Logout(user:User) { export default async function Logout(user:User) {
if (user.uuid === "bot") throw "Tried to log bot out, WTF???"; if (user.uuid === "bot") throw "Tried to log bot out, WTF???";
const logoutStartTime = Date.now(); const logoutStartTime = Date.now();

View file

@ -1,27 +1,23 @@
import { MessageData } from "../interfaces/MessageData"; import MessageData from "../interfaces/MessageData";
import { Shared } from "../objects/Shared"; import Shared from "../objects/Shared";
import { PrivateChannel } from "../objects/PrivateChannel"; import PrivateChannel from "../objects/PrivateChannel";
import { User } from "../objects/User"; import User from "../objects/User";
export function PrivateMessage(user:User, message:MessageData) { export default function PrivateMessage(user:User, message:MessageData) {
const shared:Shared = user.shared; const shared:Shared = user.shared;
const sendingTo = shared.users.getByUsername(message.target); const sendingTo = shared.users.getByUsername(message.target);
if (!(sendingTo instanceof User)) { if (!(sendingTo instanceof User)) {
console.log("Sending User invalid");
return; return;
} }
let channel = shared.privateChatManager.GetChannelByName(`${user.username}${sendingTo.username}`); let channel = shared.privateChatManager.GetChannelByName(`${user.username}${sendingTo.username}`);
if (!(channel instanceof PrivateChannel)) { if (!(channel instanceof PrivateChannel)) {
console.log("First find failed");
// Try it the other way around // Try it the other way around
channel = shared.privateChatManager.GetChannelByName(`${sendingTo.username}${user.username}`); channel = shared.privateChatManager.GetChannelByName(`${sendingTo.username}${user.username}`);
} }
if (!(channel instanceof PrivateChannel)) { if (!(channel instanceof PrivateChannel)) {
console.log("Second find failed, creating");
channel = shared.privateChatManager.AddChannel(user, sendingTo); channel = shared.privateChatManager.AddChannel(user, sendingTo);
} }
console.log("sending");
channel.SendMessage(user, message.message); channel.SendMessage(user, message.message);
} }

View file

@ -1,6 +1,6 @@
import { User } from "../objects/User"; import User from "../objects/User";
export function RemoveFriend(user:User, friendId:number) { export default function RemoveFriend(user:User, friendId:number) {
user.shared.database.query("DELETE FROM friends WHERE user = ? AND friendsWith = ? LIMIT 1", [ user.shared.database.query("DELETE FROM friends WHERE user = ? AND friendsWith = ? LIMIT 1", [
user.id, friendId user.id, friendId
]); ]);

View file

@ -1,9 +1,9 @@
import { Shared } from "../objects/Shared"; import Shared from "../objects/Shared";
import { RankingModes } from "../enums/RankingModes"; import { RankingModes } from "../enums/RankingModes";
import { User } from "../objects/User"; import User from "../objects/User";
import { osu } from "../../osuTyping"; import osu from "../../osuTyping";
export function StatusUpdate(arg0:User | Shared, id:number) { export default function StatusUpdate(arg0:User | Shared, id:number) {
if (id == 3) return; // Ignore Bot if (id == 3) return; // Ignore Bot
// Create new osu packet writer // Create new osu packet writer

View file

@ -1,7 +1,7 @@
import { osu } from "../../osuTyping"; import osu from "../../osuTyping";
import { User } from "../objects/User"; import User from "../objects/User";
export function TourneyMatchJoinChannel(user:User, matchId:number) { export default function TourneyMatchJoinChannel(user:User, matchId:number) {
const match = user.shared.multiplayerManager.GetMatchById(matchId); const match = user.shared.multiplayerManager.GetMatchById(matchId);
if (match === undefined) { if (match === undefined) {
return; return;

View file

@ -1,6 +1,6 @@
import { User } from "../objects/User"; import User from "../objects/User";
export function TourneyMatchLeaveChannel(user:User, matchId:number) { export default function TourneyMatchLeaveChannel(user:User, matchId:number) {
const match = user.shared.multiplayerManager.GetMatchById(matchId); const match = user.shared.multiplayerManager.GetMatchById(matchId);
if (match === undefined) { if (match === undefined) {
return; return;

View file

@ -1,10 +1,10 @@
import { osu } from "../../osuTyping"; import osu from "../../osuTyping";
import { Match } from "../objects/Match"; import Match from "../objects/Match";
import { User } from "../objects/User"; import User from "../objects/User";
import { StatusUpdate } from "./StatusUpdate"; import StatusUpdate from "./StatusUpdate";
import { UserPresence } from "./UserPresence"; import UserPresence from "./UserPresence";
export function TourneyMatchSpecialInfo(user:User, matchId:number) { export default function TourneyMatchSpecialInfo(user:User, matchId:number) {
const match = user.shared.multiplayerManager.GetMatchById(matchId); const match = user.shared.multiplayerManager.GetMatchById(matchId);
if (!(match instanceof Match)) { if (!(match instanceof Match)) {
return; return;

View file

@ -1,8 +1,8 @@
import { osu } from "../../osuTyping"; import osu from "../../osuTyping";
import { Shared } from "../objects/Shared"; import Shared from "../objects/Shared";
import { User } from "../objects/User"; import User from "../objects/User";
export function UserPresence(arg0:User | Shared, id:number) { export default function UserPresence(arg0:User | Shared, id:number) {
const osuPacketWriter = osu.Bancho.Writer(); const osuPacketWriter = osu.Bancho.Writer();
let shared:Shared; let shared:Shared;
if (arg0 instanceof User) { if (arg0 instanceof User) {

View file

@ -1,8 +1,8 @@
import { osu } from "../../osuTyping"; import osu from "../../osuTyping";
import { Shared } from "../objects/Shared"; import Shared from "../objects/Shared";
import { User } from "../objects/User"; import User from "../objects/User";
export function UserPresenceBundle(arg0:User | Shared) : Buffer { export default function UserPresenceBundle(arg0:User | Shared) : Buffer {
const osuPacketWriter = osu.Bancho.Writer(); const osuPacketWriter = osu.Bancho.Writer();
let shared:Shared; let shared:Shared;
if (arg0 instanceof User) { if (arg0 instanceof User) {

View file

@ -1,9 +1,9 @@
import { User } from "../objects/User"; import User from "../objects/User";
import { StatusUpdate } from "./StatusUpdate"; import StatusUpdate from "./StatusUpdate";
import { UserPresence } from "./UserPresence"; import UserPresence from "./UserPresence";
import { UserPresenceBundle } from "./UserPresenceBundle"; import UserPresenceBundle from "./UserPresenceBundle";
export function UserStatsRequest(user:User, data:Array<number>) { export default function UserStatsRequest(user:User, data:Array<number>) {
UserPresenceBundle(user); UserPresenceBundle(user);
for (let id of data) { for (let id of data) {

12
tooling/cleanup.ts Normal file
View file

@ -0,0 +1,12 @@
import { readdirSync, rmSync, renameSync } from "fs";
const libFiles = readdirSync("./build");
for (const file of libFiles) {
if (!file.startsWith("index.min.js")) {
rmSync(`./build/${file}`, { recursive: true });
}
}
//renameSync("./build/combined.js", "./build/index.js");
//renameSync("./build/combined.d.ts", "./build/index.d.ts");

67
tooling/fileSmasher.ts Normal file
View file

@ -0,0 +1,67 @@
// fileSmasher ~.~
// for when you're just too lazy to
// do it properly.
import { readdirSync, lstatSync, readFileSync, writeFileSync } from "fs";
let tsFileData:Array<string> = new Array<string>();
const tsFirstFileData:Array<string> = new Array<string>();
const tsLastFileData:Array<string> = new Array<string>();
const tsEverythingElse:Array<string> = new Array<string>();
function readDir(nam:string) {
const files = readdirSync(nam);
for (const file of files) {
if (nam == "./" && (file.startsWith(".") || file == "tooling" || file == "build" || file == "node_modules" || file == "combined.ts")) {
continue;
}
// This is a very dumb way of checking for folders
// protip: don't do this.
if (lstatSync(`${nam}/${file}`).isDirectory()) {
readDir(`${nam}/${file}`);
} else if (file.endsWith(".ts")) {
if (file == "Binato.ts") {
tsLastFileData.push(readFileSync((`${nam}/${file}`).replace("//", "/")).toString());
} else if (nam.includes("enum") || nam.includes("packets") || file.includes("FunkyArray") || file.includes("SpectatorManager") || file.includes("Shared")) {
tsFirstFileData.push(readFileSync((`${nam}/${file}`).replace("//", "/")).toString());
} else {
tsEverythingElse.push(readFileSync((`${nam}/${file}`).replace("//", "/")).toString());
}
}
}
}
readDir("./");
tsFileData = tsFileData.concat(tsFirstFileData).concat(tsEverythingElse).concat(tsLastFileData);
const combinedFiles = tsFileData.join("\n");
const splitLines = combinedFiles.split("\n");
const resultLines:Array<string> = new Array<string>();
// Insert allowed imports
resultLines.push(`import { IncomingMessage, ServerResponse } from "http";
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 * as dyetty from "dyetty";
import fetch from "node-fetch";
import http from "http";`);
// Let's process the file to make it usable
for (const line of splitLines) {
// Throw away imports as they aren't needed
// TODO: Add allow list for npm module imports
if (line.startsWith("import")) {
continue;
}
// Fix up classes, interfaces and such.
//resultLines.push(line);
resultLines.push(line.replace("export default", "").replace("export class", "class").replace("export interface", "interface").replace("export enum", "enum").replace("export type", "type"));
}
writeFileSync("./combined.ts", resultLines.join("\n"));

10
tooling/mangle.ts Normal file
View file

@ -0,0 +1,10 @@
import { readFileSync, writeFileSync } from "fs";
import { minify } from "terser";
(async () => {
const mangled = await minify(readFileSync("./build/combined.js").toString(), {
mangle: true,
toplevel: true,
});
writeFileSync("./build/index.min.js", `${mangled.code}`);
})();