kopasdkopsdaokp
This commit is contained in:
parent
4ebf9ee0e6
commit
53a12461ce
16 changed files with 694 additions and 114 deletions
33
Binato.ts
33
Binato.ts
|
@ -1,12 +1,13 @@
|
|||
import { Application } from "express";
|
||||
import { ChatHistory } from "./server/ChatHistory";
|
||||
import compression from "compression";
|
||||
import config from "./config.json";
|
||||
import { ConsoleHelper } from "./ConsoleHelper";
|
||||
import express from "express";
|
||||
import { readFile } from "fs";
|
||||
import { HandleRequest } from "./server/BanchoServer";
|
||||
import { readFileSync } from "fs";
|
||||
import { Registry, collectDefaultMetrics } from "prom-client";
|
||||
|
||||
const binatoApp:Application = express();
|
||||
const config = require("./config.json");
|
||||
const binatoApp:express.Application = express();
|
||||
|
||||
if (config["prometheus"]["enabled"]) {
|
||||
const register:Registry = new Registry();
|
||||
|
@ -14,7 +15,7 @@ if (config["prometheus"]["enabled"]) {
|
|||
|
||||
collectDefaultMetrics({ register });
|
||||
|
||||
const prometheusApp:Application = express();
|
||||
const prometheusApp:express.Application = express();
|
||||
prometheusApp.get("/metrics", async (req, res) => {
|
||||
res.end(await register.metrics());
|
||||
});
|
||||
|
@ -31,6 +32,8 @@ if (config["express"]["compression"]) {
|
|||
ConsoleHelper.printWarn("Compression is disabled");
|
||||
}
|
||||
|
||||
const INDEX_PAGE:string = readFileSync("./web/serverPage.html").toString();
|
||||
|
||||
binatoApp.use((req, res) => {
|
||||
let packet:Buffer = Buffer.alloc(0);
|
||||
req.on("data", (chunk:Buffer) => packet = Buffer.concat([packet, chunk], packet.length + chunk.length));
|
||||
|
@ -38,21 +41,9 @@ binatoApp.use((req, res) => {
|
|||
switch (req.method) {
|
||||
case "GET":
|
||||
if (req.url == "/" || req.url == "/index.html" || req.url == "/index") {
|
||||
res.sendFile(`${__dirname}/web/serverPage.html`);
|
||||
res.send(INDEX_PAGE);
|
||||
} else if (req.url == "/chat") {
|
||||
readFile("./web/chatPageTemplate.html", (err, data) => {
|
||||
if (err) throw err;
|
||||
|
||||
let lines = "", flip = false;
|
||||
const limit = global.chatHistory.length < 10 ? 10 : global.chatHistory.length;
|
||||
for (let i = global.chatHistory.length - 10; i < limit; i++) {
|
||||
if (i < 0) i = 0;
|
||||
lines += `<div class="line line${flip ? 1 : 0}">${global.chatHistory[i] == null ? "<hidden>blank</hidden>" : global.chatHistory[i]}</div>`
|
||||
flip = !flip;
|
||||
}
|
||||
|
||||
res.send(data.toString().replace("|content|", lines));
|
||||
});
|
||||
res.send(ChatHistory.GenerateForWeb());
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -60,8 +51,8 @@ binatoApp.use((req, res) => {
|
|||
// Make sure this address should respond to bancho requests
|
||||
// Bancho addresses: c, c1, c2, c3, c4, c5, c6, ce
|
||||
// Just looking for the first character being "c" *should* be enough
|
||||
if (req.headers["host"].split(".")[0][0] == "c")
|
||||
serverHandler(req, res);
|
||||
if (req.headers.host != null && req.headers.host.split(".")[0][0] == "c")
|
||||
HandleRequest(req, res, packet);
|
||||
else
|
||||
res.status(400).send("400 | Bad Request!<br>Binato only accepts POST requests on Bancho subdomains.<hr>Binato");
|
||||
break;
|
||||
|
|
165
package-lock.json
generated
165
package-lock.json
generated
|
@ -9,6 +9,7 @@
|
|||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/node": "^18.11.9",
|
||||
"chalk": "^4.1.0",
|
||||
|
@ -16,7 +17,8 @@
|
|||
"express": "^4.18.2",
|
||||
"mysql2": "^2.3.3",
|
||||
"osu-packet": "^4.1.2",
|
||||
"prom-client": "^14.1.0"
|
||||
"prom-client": "^14.1.0",
|
||||
"redis": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^2.0.20",
|
||||
|
@ -60,6 +62,59 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/bloom": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.1.0.tgz",
|
||||
"integrity": "sha512-9QovlxmpRtvxVbN0UBcv8WfdSMudNZZTFqCsnBszcQXqaZb/TVe30ScgGEO7u1EAIacTPAo7/oCYjYAxiHLanQ==",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/client": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.4.0.tgz",
|
||||
"integrity": "sha512-1gEj1AkyXPlkcC/9/T5xpDcQF8ntERURjLBgEWMTdUZqe181zfI9BY3jc2OzjTLkvZh5GV7VT4ktoJG2fV2ufw==",
|
||||
"dependencies": {
|
||||
"cluster-key-slot": "1.1.1",
|
||||
"generic-pool": "3.9.0",
|
||||
"yallist": "4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/graph": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz",
|
||||
"integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/json": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz",
|
||||
"integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/search": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.0.tgz",
|
||||
"integrity": "sha512-NyFZEVnxIJEybpy+YskjgOJRNsfTYqaPbK/Buv6W2kmFNaRk85JiqjJZA5QkRmWvGbyQYwoO5QfDi2wHskKrQQ==",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/time-series": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz",
|
||||
"integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
|
@ -93,6 +148,14 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/compression": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.2.tgz",
|
||||
"integrity": "sha512-lwEL4M/uAGWngWFLSG87ZDr2kLrbuR8p7X+QZB1OQlT+qkHsCPDVFnHPyXf4Vyl4yDDorNY+mAhosxkCvppatg==",
|
||||
"dependencies": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/connect": {
|
||||
"version": "3.4.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
||||
|
@ -374,6 +437,14 @@
|
|||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/cluster-key-slot": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz",
|
||||
"integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
|
@ -656,6 +727,14 @@
|
|||
"is-property": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/generic-pool": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
|
||||
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
|
||||
|
@ -1171,6 +1250,19 @@
|
|||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/redis": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-4.5.0.tgz",
|
||||
"integrity": "sha512-oZGAmOKG+RPnHo0UxM5GGjJ0dBd/Vi4fs3MYwM1p2baDoXC0wpm0yOdpxVS9K+0hM84ycdysp2eHg2xGoQ4FEw==",
|
||||
"dependencies": {
|
||||
"@redis/bloom": "1.1.0",
|
||||
"@redis/client": "1.4.0",
|
||||
"@redis/graph": "1.1.0",
|
||||
"@redis/json": "1.0.4",
|
||||
"@redis/search": "1.1.0",
|
||||
"@redis/time-series": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
|
@ -1510,6 +1602,46 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"@redis/bloom": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.1.0.tgz",
|
||||
"integrity": "sha512-9QovlxmpRtvxVbN0UBcv8WfdSMudNZZTFqCsnBszcQXqaZb/TVe30ScgGEO7u1EAIacTPAo7/oCYjYAxiHLanQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"@redis/client": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.4.0.tgz",
|
||||
"integrity": "sha512-1gEj1AkyXPlkcC/9/T5xpDcQF8ntERURjLBgEWMTdUZqe181zfI9BY3jc2OzjTLkvZh5GV7VT4ktoJG2fV2ufw==",
|
||||
"requires": {
|
||||
"cluster-key-slot": "1.1.1",
|
||||
"generic-pool": "3.9.0",
|
||||
"yallist": "4.0.0"
|
||||
}
|
||||
},
|
||||
"@redis/graph": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz",
|
||||
"integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@redis/json": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz",
|
||||
"integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@redis/search": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.0.tgz",
|
||||
"integrity": "sha512-NyFZEVnxIJEybpy+YskjgOJRNsfTYqaPbK/Buv6W2kmFNaRk85JiqjJZA5QkRmWvGbyQYwoO5QfDi2wHskKrQQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"@redis/time-series": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz",
|
||||
"integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
|
@ -1543,6 +1675,14 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/compression": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.2.tgz",
|
||||
"integrity": "sha512-lwEL4M/uAGWngWFLSG87ZDr2kLrbuR8p7X+QZB1OQlT+qkHsCPDVFnHPyXf4Vyl4yDDorNY+mAhosxkCvppatg==",
|
||||
"requires": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
||||
|
@ -1766,6 +1906,11 @@
|
|||
"readdirp": "~3.6.0"
|
||||
}
|
||||
},
|
||||
"cluster-key-slot": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz",
|
||||
"integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw=="
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
|
@ -1988,6 +2133,11 @@
|
|||
"is-property": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"generic-pool": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
|
||||
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g=="
|
||||
},
|
||||
"get-intrinsic": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
|
||||
|
@ -2377,6 +2527,19 @@
|
|||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"redis": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-4.5.0.tgz",
|
||||
"integrity": "sha512-oZGAmOKG+RPnHo0UxM5GGjJ0dBd/Vi4fs3MYwM1p2baDoXC0wpm0yOdpxVS9K+0hM84ycdysp2eHg2xGoQ4FEw==",
|
||||
"requires": {
|
||||
"@redis/bloom": "1.1.0",
|
||||
"@redis/client": "1.4.0",
|
||||
"@redis/graph": "1.1.0",
|
||||
"@redis/json": "1.0.4",
|
||||
"@redis/search": "1.1.0",
|
||||
"@redis/time-series": "1.0.4"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/node": "^18.11.9",
|
||||
"chalk": "^4.1.0",
|
||||
|
@ -17,7 +18,8 @@
|
|||
"express": "^4.18.2",
|
||||
"mysql2": "^2.3.3",
|
||||
"osu-packet": "^4.1.2",
|
||||
"prom-client": "^14.1.0"
|
||||
"prom-client": "^14.1.0",
|
||||
"redis": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^2.0.20",
|
||||
|
|
|
@ -1,71 +1,76 @@
|
|||
import * as osu from "osu-packet";
|
||||
import config from "../config.json";
|
||||
import { ConsoleHelper } from "../ConsoleHelper";
|
||||
import { Database } from "./objects/Database";
|
||||
import { UserArray } from "./objects/UserArray";
|
||||
import { LatLng } from "./objects/LatLng";
|
||||
import { Packets } from "./enums/Packets";
|
||||
import { RedisClientType, createClient } from "redis";
|
||||
import { replaceAll } from "./Util";
|
||||
import { Request, Response } from "express";
|
||||
import { User } from "./objects/User";
|
||||
import * as osu from "osu-packet";
|
||||
|
||||
const
|
||||
/*const
|
||||
loginHandler = require("./loginHandler.js"),
|
||||
parseUserData = require("./util/parseUserData.js"),
|
||||
User = require("./User.js"),
|
||||
getUserFromToken = require("./util/getUserByToken.js"),
|
||||
getUserById = require("./util/getUserById.js"),
|
||||
bakedResponses = require("./bakedResponses.js"),
|
||||
Streams = require("./Streams.js"),
|
||||
DatabaseHelperClass = require("./DatabaseHelper.js"),
|
||||
funkyArray = require("./util/funkyArray.js"),
|
||||
config = require("../config.json");
|
||||
Streams = require("./Streams.js");*/
|
||||
|
||||
// Users funkyArray for session storage
|
||||
global.users = new funkyArray();
|
||||
|
||||
// Add the bot user
|
||||
global.botUser = global.users.add("bot", new User(3, "SillyBot", "bot"));
|
||||
// Set the bot's position on the map
|
||||
global.botUser.location[0] = 50;
|
||||
global.botUser.location[1] = -32;
|
||||
|
||||
global.DatabaseHelper = new DatabaseHelperClass(config.database.address, config.database.port, config.database.username, config.database.password, config.database.name, async () => {
|
||||
const DB:Database = new Database(config.database.address, config.database.port, config.database.username, config.database.password, config.database.name, async () => {
|
||||
// Close any unclosed db matches on startup
|
||||
global.DatabaseHelper.query("UPDATE mp_matches SET close_time = UNIX_TIMESTAMP() WHERE close_time IS NULL");
|
||||
global.DatabaseHelper.query("UPDATE osu_info SET value = 0 WHERE name = 'online_now'");
|
||||
DB.query("UPDATE mp_matches SET close_time = UNIX_TIMESTAMP() WHERE close_time IS NULL");
|
||||
DB.query("UPDATE osu_info SET value = 0 WHERE name = 'online_now'");
|
||||
});
|
||||
|
||||
async function subscribeToChannel(channelName = "", callback = function(message = "") {}) {
|
||||
// Users funkyArray for session storage
|
||||
const users = new UserArray();
|
||||
|
||||
// Add the bot user
|
||||
const botUser:User = users.add("bot", new User(3, "SillyBot", "bot", DB));
|
||||
// Set the bot's position on the map
|
||||
botUser.location = new LatLng(50, -32);
|
||||
|
||||
let redisClient:RedisClientType;
|
||||
|
||||
async function subscribeToChannel(channelName:string, callback:(message:string) => void) {
|
||||
// Dup and connect new client for channel subscription (required)
|
||||
const subscriptionClient = global.promClient.duplicate();
|
||||
const subscriptionClient:RedisClientType = redisClient.duplicate();
|
||||
await subscriptionClient.connect();
|
||||
// Subscribe to channel
|
||||
await subscriptionClient.subscribe(channelName, callback);
|
||||
ConsoleHelper.printRedis(`Subscribed to ${channelName} channel`);
|
||||
}
|
||||
|
||||
// Do redis if it's enabled
|
||||
if (config.redis.enabled) {
|
||||
(async () => {
|
||||
const { createClient } = require("redis");
|
||||
global.promClient = createClient({
|
||||
url: `redis://${config.redis.password.replaceAll(" ", "") == "" ? "" : `${config.redis.password}@`}${config.redis.address}:${config.redis.port}/${config.redis.database}`
|
||||
redisClient = createClient({
|
||||
url: `redis://${replaceAll(config.redis.password, " ", "") == "" ? "" : `${config.redis.password}@`}${config.redis.address}:${config.redis.port}/${config.redis.database}`
|
||||
});
|
||||
|
||||
global.promClient.on('error', e => consoleHelper.printRedis(e));
|
||||
redisClient.on('error', e => ConsoleHelper.printRedis(e));
|
||||
|
||||
const connectionStartTime = Date.now();
|
||||
await global.promClient.connect();
|
||||
consoleHelper.printRedis(`Connected to redis server. Took ${Date.now() - connectionStartTime}ms`);
|
||||
await redisClient.connect();
|
||||
ConsoleHelper.printRedis(`Connected to redis server. Took ${Date.now() - connectionStartTime}ms`);
|
||||
|
||||
// Score submit update channel
|
||||
subscribeToChannel("binato:update_user_stats", (message) => {
|
||||
const user = getUserById(parseInt(message));
|
||||
if (typeof(message) === "string") {
|
||||
const user = users.getById(parseInt(message));
|
||||
// Update user info
|
||||
user.updateUserInfo(true);
|
||||
|
||||
consoleHelper.printRedis(`Score submission stats update request received for ${user.username}`);
|
||||
ConsoleHelper.printRedis(`Score submission stats update request received for ${user.username}`);
|
||||
}
|
||||
});
|
||||
})();
|
||||
} else consoleHelper.printWarn("Redis is disabled!");
|
||||
} else ConsoleHelper.printWarn("Redis is disabled!");
|
||||
|
||||
// User timeout interval
|
||||
setInterval(() => {
|
||||
for (let User of global.users.getIterableItems()) {
|
||||
for (let User of users.getIterableItems()) {
|
||||
if (User.id == 3) continue; // Ignore the bot
|
||||
// Bot: :(
|
||||
|
||||
|
@ -75,36 +80,25 @@ setInterval(() => {
|
|||
}
|
||||
}, 10000);
|
||||
|
||||
// An array containing the last 15 messages in chat
|
||||
global.chatHistory = [];
|
||||
global.addChatMessage = function(msg) {
|
||||
if (global.chatHistory.length == 15) {
|
||||
global.chatHistory.splice(0, 1);
|
||||
global.chatHistory.push(msg);
|
||||
} else {
|
||||
global.chatHistory.push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Init stream class
|
||||
Streams.init();
|
||||
//Streams.init();
|
||||
|
||||
// An array containing all chat channels
|
||||
global.channels = [
|
||||
/*global.channels = [
|
||||
{ channelName:"#osu", channelTopic:"The main channel", channelUserCount: 0, locked: false },
|
||||
{ channelName:"#userlog", channelTopic:"Log about stuff doing go on yes very", channelUserCount: 0, locked: false },
|
||||
{ channelName:"#lobby", channelTopic:"Talk about multiplayer stuff", channelUserCount: 0, locked: false },
|
||||
{ channelName:"#english", channelTopic:"Talk in exclusively English", channelUserCount: 0, locked: false },
|
||||
{ channelName:"#japanese", channelTopic:"Talk in exclusively Japanese", channelUserCount: 0, locked: false },
|
||||
];
|
||||
];*/
|
||||
|
||||
// Create a stream for each chat channel
|
||||
for (let i = 0; i < global.channels.length; i++) {
|
||||
/*for (let i = 0; i < global.channels.length; i++) {
|
||||
Streams.addStream(global.channels[i].channelName, false);
|
||||
}
|
||||
}*/
|
||||
|
||||
// Add a stream for the multiplayer lobby
|
||||
Streams.addStream("multiplayer_lobby", false);
|
||||
//Streams.addStream("multiplayer_lobby", false);
|
||||
|
||||
// Include packets
|
||||
const ChangeAction = require("./Packets/ChangeAction.js"),
|
||||
|
@ -127,11 +121,11 @@ const ChangeAction = require("./Packets/ChangeAction.js"),
|
|||
TourneyMatchLeaveChannel = require("./Packets/TourneyLeaveMatchChannel.js");
|
||||
|
||||
// A class for managing everything multiplayer
|
||||
global.MultiplayerManager = new MultiplayerManager();
|
||||
const multiplayerManager:MultiplayerManager = new MultiplayerManager();
|
||||
|
||||
module.exports = async function(req, res, packet:Buffer) {
|
||||
export async function HandleRequest(req:Request, res:Response, packet:Buffer) {
|
||||
// Get the client's token string and request data
|
||||
const requestTokenString:string = req.header("osu-token"),
|
||||
const requestTokenString:string | undefined = req.header("osu-token"),
|
||||
requestData:Buffer = packet;
|
||||
|
||||
// Server's response
|
||||
|
@ -147,7 +141,7 @@ module.exports = async function(req, res, packet:Buffer) {
|
|||
// Client has a token, let's see what they want.
|
||||
try {
|
||||
// Get the current user
|
||||
const PacketUser:User = getUserFromToken(requestTokenString);
|
||||
const PacketUser:User = users.getByToken(requestTokenString);
|
||||
|
||||
// Make sure the client's token isn't invalid
|
||||
if (PacketUser != null) {
|
||||
|
@ -160,7 +154,7 @@ module.exports = async function(req, res, packet:Buffer) {
|
|||
const PacketData = osuPacketReader.Parse();
|
||||
|
||||
// Go through each packet sent by the client
|
||||
for (CurrentPacket of PacketData) {
|
||||
for (let CurrentPacket of PacketData) {
|
||||
switch (CurrentPacket.id) {
|
||||
case Packets.Client_ChangeAction:
|
||||
ChangeAction(PacketUser, CurrentPacket.data);
|
||||
|
@ -190,136 +184,136 @@ module.exports = async function(req, res, packet:Buffer) {
|
|||
Spectator.stopSpectatingUser(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_sendPrivateMessage:
|
||||
case Packets.Client_sendPrivateMessage:
|
||||
SendPrivateMessage(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_joinLobby:
|
||||
case Packets.Client_joinLobby:
|
||||
global.MultiplayerManager.userEnterLobby(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_partLobby:
|
||||
case Packets.Client_partLobby:
|
||||
global.MultiplayerManager.userLeaveLobby(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_createMatch:
|
||||
case Packets.Client_createMatch:
|
||||
await global.MultiplayerManager.createMultiplayerMatch(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_joinMatch:
|
||||
case Packets.Client_joinMatch:
|
||||
global.MultiplayerManager.joinMultiplayerMatch(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_matchChangeSlot:
|
||||
case Packets.Client_matchChangeSlot:
|
||||
PacketUser.currentMatch.moveToSlot(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_matchReady:
|
||||
case Packets.Client_matchReady:
|
||||
PacketUser.currentMatch.setStateReady(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_matchChangeSettings:
|
||||
case Packets.Client_matchChangeSettings:
|
||||
await PacketUser.currentMatch.updateMatch(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_matchNotReady:
|
||||
case Packets.Client_matchNotReady:
|
||||
PacketUser.currentMatch.setStateNotReady(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_partMatch:
|
||||
case Packets.Client_partMatch:
|
||||
await global.MultiplayerManager.leaveMultiplayerMatch(PacketUser);
|
||||
break;
|
||||
|
||||
// Also handles user kick if the slot has a user
|
||||
case Packets.client_matchLock:
|
||||
case Packets.Client_matchLock:
|
||||
PacketUser.currentMatch.lockMatchSlot(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_matchNoBeatmap:
|
||||
case Packets.Client_matchNoBeatmap:
|
||||
PacketUser.currentMatch.missingBeatmap(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_matchSkipRequest:
|
||||
case Packets.Client_matchSkipRequest:
|
||||
PacketUser.currentMatch.matchSkip(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_matchHasBeatmap:
|
||||
case Packets.Client_matchHasBeatmap:
|
||||
PacketUser.currentMatch.notMissingBeatmap(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_matchTransferHost:
|
||||
case Packets.Client_matchTransferHost:
|
||||
PacketUser.currentMatch.transferHost(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_matchChangeMods:
|
||||
case Packets.Client_matchChangeMods:
|
||||
PacketUser.currentMatch.updateMods(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_matchStart:
|
||||
case Packets.Client_matchStart:
|
||||
PacketUser.currentMatch.startMatch();
|
||||
break;
|
||||
|
||||
case Packets.client_matchLoadComplete:
|
||||
case Packets.Client_matchLoadComplete:
|
||||
PacketUser.currentMatch.matchPlayerLoaded(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_matchComplete:
|
||||
case Packets.Client_matchComplete:
|
||||
await PacketUser.currentMatch.onPlayerFinishMatch(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_matchScoreUpdate:
|
||||
case Packets.Client_matchScoreUpdate:
|
||||
PacketUser.currentMatch.updatePlayerScore(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_matchFailed:
|
||||
case Packets.Client_matchFailed:
|
||||
PacketUser.currentMatch.matchFailed(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_matchChangeTeam:
|
||||
case Packets.Client_matchChangeTeam:
|
||||
PacketUser.currentMatch.changeTeam(PacketUser);
|
||||
break;
|
||||
|
||||
case Packets.client_channelJoin:
|
||||
case Packets.Client_channelJoin:
|
||||
ChannelJoin(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_channelPart:
|
||||
case Packets.Client_channelPart:
|
||||
ChannelPart(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_setAwayMessage:
|
||||
case Packets.Client_setAwayMessage:
|
||||
SetAwayMessage(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_friendAdd:
|
||||
case Packets.Client_friendAdd:
|
||||
AddFriend(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_friendRemove:
|
||||
case Packets.Client_friendRemove:
|
||||
RemoveFriend(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_userStatsRequest:
|
||||
case Packets.Client_userStatsRequest:
|
||||
UserStatsRequest(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_specialMatchInfoRequest:
|
||||
case Packets.Client_specialMatchInfoRequest:
|
||||
TourneyMatchSpecialInfo(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_specialJoinMatchChannel:
|
||||
case Packets.Client_specialJoinMatchChannel:
|
||||
TourneyMatchJoinChannel(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_specialLeaveMatchChannel:
|
||||
case Packets.Client_specialLeaveMatchChannel:
|
||||
TourneyMatchLeaveChannel(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_invite:
|
||||
case Packets.Client_invite:
|
||||
MultiplayerInvite(PacketUser, CurrentPacket.data);
|
||||
break;
|
||||
|
||||
case Packets.client_userPresenceRequest:
|
||||
case Packets.Client_userPresenceRequest:
|
||||
UserPresence(PacketUser, PacketUser.id); // Can't really think of a way to generalize this?
|
||||
break;
|
||||
|
||||
|
|
34
server/ChatHistory.ts
Normal file
34
server/ChatHistory.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { readFileSync } from "fs";
|
||||
|
||||
export abstract class ChatHistory {
|
||||
private static _history:Array<string> = new Array<string>();
|
||||
private static _lastGeneratedPage:string;
|
||||
private static _hasChanged:boolean = true;
|
||||
private static readonly HISTORY_LENGTH = 10;
|
||||
private static readonly PAGE_TEMPLATE = readFileSync("./web/chatPageTemplate.html").toString();
|
||||
|
||||
public static AddMessage(message:string) : void {
|
||||
if (this._history.length === 10) {
|
||||
this._history.splice(0, 1);
|
||||
}
|
||||
|
||||
this._history.push(message);
|
||||
this._hasChanged = true;
|
||||
}
|
||||
|
||||
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) {
|
||||
this._lastGeneratedPage = this.PAGE_TEMPLATE.toString().replace("|content|", lines);
|
||||
this._hasChanged = false;
|
||||
}
|
||||
|
||||
return this._lastGeneratedPage;
|
||||
}
|
||||
}
|
8
server/Util.ts
Normal file
8
server/Util.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
// Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
|
||||
function escapeRegExp(string:string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
export function replaceAll(inputString:string, toReplace:string, toReplaceWith:string) {
|
||||
return inputString.replace(`/:${escapeRegExp(toReplace)}:/g`, toReplaceWith);
|
||||
}
|
|
@ -56,7 +56,7 @@ export enum Packets {
|
|||
Client_MatchNoBeatmap,
|
||||
Client_MatchNotReady,
|
||||
Client_MatchFailed,
|
||||
Server_MatchComplete,
|
||||
Server_MatchComplete = 58,
|
||||
Client_MatchHasBeatmap,
|
||||
Client_MatchSkipRequest,
|
||||
Server_MatchSkip,
|
||||
|
@ -109,5 +109,5 @@ export enum Packets {
|
|||
// NOTE: Tournament client only
|
||||
Client_SpecialJoinMatchChannel,
|
||||
// NOTE: Tournament client only
|
||||
Client_SpecialLeaveMatchChanne,
|
||||
Client_SpecialLeaveMatchChannel,
|
||||
}
|
5
server/enums/RankingModes.ts
Normal file
5
server/enums/RankingModes.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export enum RankingModes {
|
||||
PP,
|
||||
RANKED_SCORE,
|
||||
AVG_ACCURACY
|
||||
};
|
77
server/objects/Database.ts
Normal file
77
server/objects/Database.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import { ConsoleHelper } from "../../ConsoleHelper";
|
||||
import { createPool, Pool } from "mysql2";
|
||||
|
||||
export class Database {
|
||||
private connectionPool:Pool;
|
||||
private static readonly CONNECTION_LIMIT = 128;
|
||||
|
||||
public constructor(databaseAddress:string, databasePort:number = 3306, databaseUsername:string, databasePassword:string, databaseName:string, connectedCallback:Function) {
|
||||
this.connectionPool = createPool({
|
||||
connectionLimit: Database.CONNECTION_LIMIT,
|
||||
host: databaseAddress,
|
||||
port: databasePort,
|
||||
user: databaseUsername,
|
||||
password: databasePassword,
|
||||
database: databaseName
|
||||
});
|
||||
|
||||
const classCreationTime:number = Date.now();
|
||||
const connectionCheckInterval = setInterval(() => {
|
||||
this.query("SELECT name FROM osu_info LIMIT 1")
|
||||
.then(data => {
|
||||
ConsoleHelper.printBancho(`Connected to database. Took ${Date.now() - classCreationTime}ms`);
|
||||
clearInterval(connectionCheckInterval);
|
||||
|
||||
connectedCallback();
|
||||
})
|
||||
.catch(err => {});
|
||||
}, 17); // Roughly 6 times per sec
|
||||
}
|
||||
|
||||
public query(query = "", data?:Array<any>) {
|
||||
const limited = query.includes("LIMIT 1");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.connectionPool.getConnection((err, connection) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
try {
|
||||
connection.release();
|
||||
} catch (e) {
|
||||
ConsoleHelper.printError("Failed to release mysql connection\n" + err);
|
||||
}
|
||||
} else {
|
||||
// Use old query
|
||||
if (data == null) {
|
||||
connection.query(query, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
connection.release();
|
||||
} else {
|
||||
dataReceived(resolve, data, limited);
|
||||
connection.release();
|
||||
}
|
||||
});
|
||||
}
|
||||
// Use new prepared statements w/ placeholders
|
||||
else {
|
||||
connection.execute(query, data, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
connection.release();
|
||||
} else {
|
||||
dataReceived(resolve, data, limited);
|
||||
connection.release();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function dataReceived(resolveCallback:(value:unknown) => void, data:any, limited:boolean = false) : void {
|
||||
if (limited) resolveCallback(data[0]);
|
||||
else resolveCallback(data);
|
||||
}
|
74
server/objects/FunkyArray.ts
Normal file
74
server/objects/FunkyArray.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
export class FunkyArray<T> {
|
||||
private items:any = {};
|
||||
private itemKeys:Array<string> = Object.keys(this.items);
|
||||
private iterableArray:Array<T> = new Array<T>();
|
||||
|
||||
public add(uuid:string, item:T, regenerate:boolean = true) : T {
|
||||
this.items[uuid] = item;
|
||||
|
||||
if (regenerate) {
|
||||
this.itemKeys = Object.keys(this.items);
|
||||
this.regenerateIterableArray();
|
||||
}
|
||||
|
||||
return this.items[uuid];
|
||||
}
|
||||
|
||||
public remove(uuid:string, regenerate:boolean = true) {
|
||||
delete this.items[uuid];
|
||||
if (regenerate) {
|
||||
this.itemKeys = Object.keys(this.items);
|
||||
this.regenerateIterableArray();
|
||||
}
|
||||
}
|
||||
|
||||
public removeFirstItem(regenerate:boolean = true) : void {
|
||||
delete this.items[this.itemKeys[0]];
|
||||
this.itemKeys = Object.keys(this.items);
|
||||
if (regenerate) this.regenerateIterableArray();
|
||||
}
|
||||
|
||||
public regenerateIterableArray() : void {
|
||||
this.iterableArray = new Array();
|
||||
for (let itemKey of this.itemKeys) {
|
||||
this.iterableArray.push(this.items[itemKey]);
|
||||
}
|
||||
this.itemKeys = Object.keys(this.items);
|
||||
}
|
||||
|
||||
public getFirstItem() : T {
|
||||
return this.items[this.itemKeys[0]];
|
||||
}
|
||||
|
||||
public getLength() : number {
|
||||
return this.itemKeys.length;
|
||||
}
|
||||
|
||||
public getKeyById(id:number) : string {
|
||||
return this.itemKeys[id];
|
||||
}
|
||||
|
||||
public getById(id:number) : T | undefined {
|
||||
return this.items[this.itemKeys[id]];
|
||||
}
|
||||
|
||||
public getByKey(key:string) : T | undefined {
|
||||
if (key in this.items) {
|
||||
return this.items[key];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public getKeys() : Array<string> {
|
||||
return this.itemKeys;
|
||||
}
|
||||
|
||||
public getItems() : any {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
public getIterableItems() : Array<T> {
|
||||
return this.iterableArray;
|
||||
}
|
||||
}
|
9
server/objects/LatLng.ts
Normal file
9
server/objects/LatLng.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
export class LatLng {
|
||||
public latitude:number;
|
||||
public longitude:number;
|
||||
|
||||
public constructor(latitude:number, longitude:number) {
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
import { Database } from "./Database";
|
||||
import { LatLng } from "./LatLng";
|
||||
import { RankingModes } from "../enums/RankingModes";
|
||||
const StatusUpdate = require("./Packets/StatusUpdate.js");
|
||||
|
||||
const rankingModes = [
|
||||
"pp_raw",
|
||||
"ranked_score",
|
||||
"avg_accuracy"
|
||||
];
|
||||
|
||||
export class User {
|
||||
private static readonly EMPTY_BUFFER = Buffer.alloc(0);
|
||||
|
||||
public id:number;
|
||||
public username:string;
|
||||
public uuid:string;
|
||||
public readonly connectTime:number = Date.now();
|
||||
public timeoutTime:number = Date.now() + 30000;
|
||||
public queue:Buffer = User.EMPTY_BUFFER;
|
||||
|
||||
// Binato data
|
||||
public rankingMode:RankingModes = RankingModes.PP;
|
||||
|
||||
// osu! data
|
||||
public playMode:number = 0;
|
||||
public countryID:number = 0;
|
||||
//public spectators:Array; // TODO: Figure out if this was ever needed
|
||||
public spectating:number = -1;
|
||||
public location:LatLng = new LatLng(0, 0);
|
||||
public joinedChannels:Array<string> = new Array<string>();
|
||||
|
||||
// Presence data
|
||||
public actionID:number = 0;
|
||||
public actionText:string = "";
|
||||
public actionMods:number = 0;
|
||||
public beatmapChecksum:string = "";
|
||||
public beatmapID:number = 0;
|
||||
public currentMods:number = 0;
|
||||
|
||||
// Cached db data
|
||||
public rankedScore:number = 0;
|
||||
public accuracy:number = 0;
|
||||
public playCount:number = 0;
|
||||
public totalScore:number = 0;
|
||||
public rank:number = 0;
|
||||
public pp:number = 0;
|
||||
|
||||
// Multiplayer data
|
||||
public currentMatch = null;
|
||||
public matchSlotId:number = -1;
|
||||
public inMatch:boolean = false;
|
||||
|
||||
// Tournament client flag
|
||||
public isTourneyUser:boolean = false;
|
||||
|
||||
public dbConnection:Database;
|
||||
|
||||
public constructor(id:number, username:string, uuid:string, dbConnection:Database) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
|
||||
this.dbConnection = dbConnection;
|
||||
}
|
||||
|
||||
// Concats new actions to the user's queue
|
||||
public addActionToQueue(newData:Buffer) {
|
||||
this.queue = Buffer.concat([this.queue, newData], this.queue.length + newData.length);
|
||||
}
|
||||
|
||||
clearQueue() {
|
||||
this.queue = User.EMPTY_BUFFER;
|
||||
}
|
||||
|
||||
// Updates the user's current action
|
||||
updatePresence(action:any) : void {
|
||||
this.actionID = action.status;
|
||||
this.actionText = action.statusText;
|
||||
this.beatmapChecksum = action.beatmapChecksum;
|
||||
this.currentMods = action.currentMods;
|
||||
this.actionMods = action.currentMods;
|
||||
if (action.playMode != this.playMode) {
|
||||
this.updateUserInfo(true);
|
||||
this.playMode = action.playMode;
|
||||
}
|
||||
this.beatmapID = action.beatmapId;
|
||||
}
|
||||
|
||||
// Gets the user's score information from the database and caches it
|
||||
async updateUserInfo(forceUpdate:boolean = false) : Promise<void> {
|
||||
const userScoreDB:any = await this.dbConnection.query("SELECT * FROM users_modes_info WHERE user_id = ? AND mode_id = ? LIMIT 1", [this.id, this.playMode]);
|
||||
const mappedRankingMode = rankingModes[this.rankingMode];
|
||||
const userRankDB:any = await this.dbConnection.query(`SELECT user_id, ${mappedRankingMode} FROM users_modes_info WHERE mode_id = ? ORDER BY ${mappedRankingMode} DESC`, [this.playMode]);
|
||||
|
||||
if (userScoreDB == null || userRankDB == null) throw "fuck";
|
||||
|
||||
// Handle "if we should update" checks for each rankingMode
|
||||
let userScoreUpdate = false;
|
||||
switch (this.rankingMode) {
|
||||
case RankingModes.PP:
|
||||
if (this.pp != userScoreDB.pp_raw)
|
||||
userScoreUpdate = true;
|
||||
break;
|
||||
|
||||
case RankingModes.RANKED_SCORE:
|
||||
if (this.rankedScore != userScoreDB.ranked_score)
|
||||
userScoreUpdate = true;
|
||||
break;
|
||||
|
||||
case RankingModes.AVG_ACCURACY:
|
||||
if (this.accuracy != userScoreDB.avg_accuracy)
|
||||
userScoreUpdate = true;
|
||||
break;
|
||||
}
|
||||
|
||||
this.rankedScore = userScoreDB.ranked_score;
|
||||
this.totalScore = userScoreDB.total_score;
|
||||
this.accuracy = userScoreDB.avg_accuracy;
|
||||
this.playCount = userScoreDB.playcount;
|
||||
|
||||
// Fetch rank
|
||||
for (let i = 0; i < userRankDB.length; i++) {
|
||||
if (userRankDB[i]["user_id"] == this.id) {
|
||||
this.rank = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set PP to none if ranking mode is not PP
|
||||
if (this.rankingMode == 0) this.pp = userScoreDB.pp_raw;
|
||||
else this.pp = 0;
|
||||
|
||||
if (userScoreUpdate || forceUpdate) {
|
||||
StatusUpdate(this, this.id);
|
||||
}
|
||||
}
|
||||
}
|
26
server/objects/UserArray.ts
Normal file
26
server/objects/UserArray.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { FunkyArray } from "./FunkyArray";
|
||||
import { User } from "./User";
|
||||
|
||||
export class UserArray extends FunkyArray<User> {
|
||||
public getById(id:number) : User | undefined {
|
||||
for (let user of this.getIterableItems()) {
|
||||
if (user.id == id)
|
||||
return user;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public getByUsername(username:string) : User | undefined {
|
||||
for (let user of this.getIterableItems()) {
|
||||
if (user.username === username)
|
||||
return user;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public getByToken(token:string) : User | undefined {
|
||||
return this.getByKey(token);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"rootDir": "./",
|
||||
"outDir": "./build",
|
||||
"strict": true
|
||||
|
|
39
web/chatPageTemplate.html
Normal file
39
web/chatPageTemplate.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 482px;
|
||||
height: 165px;
|
||||
}
|
||||
|
||||
hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.line {
|
||||
padding: 2px;
|
||||
font-size: 8pt;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.line0 {
|
||||
background-color: #edebfa;
|
||||
}
|
||||
|
||||
.line1 {
|
||||
background-color: #e3e1fa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|content|
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
19
web/serverPage.html
Normal file
19
web/serverPage.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Binato</title>
|
||||
</head>
|
||||
<body>
|
||||
<pre style="border-style:double;border-width:4px;width:376px;">
|
||||
|
||||
. o ..
|
||||
o . o o.o
|
||||
...oo
|
||||
__[]__ <b>Binato</b>
|
||||
__|_o_o_o\__ A custom osu!Bancho
|
||||
\""""""""""/
|
||||
\. .. . / <a href="https://binato.eusv.ml">Website</a> | <a href="https://github.com/tgpethan/Binato">Github</a>
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue