Fix memory issues and insert unhinged comments

This commit is contained in:
Hri7566 2024-08-02 23:22:23 -04:00
parent 8ab74266e2
commit 828443cb93
10 changed files with 201 additions and 31 deletions

View File

@ -7,6 +7,11 @@ import { ChannelList } from "./ChannelList";
const logger = new Logger("Channel Forceloader");
/**
* Forceloads a channel
* @param id The channel ID
* @returns Whether the channel was loaded
*/
export function forceloadChannel(id: string) {
try {
logger.info("Forceloading", id);
@ -17,6 +22,11 @@ export function forceloadChannel(id: string) {
}
}
/**
* Unforceloads a channel
* @param id The channel ID
* @returns Whether the channel was unloaded
*/
export function unforceloadChannel(id: string) {
const ch = ChannelList.getList().find(ch => ch.getID() == id);
if (!ch) return false;
@ -27,6 +37,10 @@ export function unforceloadChannel(id: string) {
return true;
}
/**
* Forceloads all forceload-configured channels
* This is meant to be called on startup
**/
export function loadForcedStartupChannels() {
let hasFullChannel = false;

View File

@ -4,15 +4,47 @@
* by Hri7566
*/
// There are a lot of unhinged bs comments in this repo
// Pay no attention to the ones that cuss you out
// If you don't load the server first, bun will literally segfault
import "./ws/server";
import { loadForcedStartupChannels } from "./channel/forceLoad";
import { Logger } from "./util/Logger";
// Let's construct an entire object just for one thing to be printed
// and then keep it in memory for the entirety of runtime
const logger = new Logger("Main");
logger.info("Forceloading startup channels...");
loadForcedStartupChannels();
// This literally breaks editors and they stick all the imports here instead of at the top
import "./util/readline";
import { Socket, socketsBySocketID } from "./ws/Socket";
import { createSocketID } from "./util/id";
// Nevermind we use it twice
logger.info("Ready");
// Connect 5 sockets
const sockets = [];
for (let i = 0; i < 5; i++) {
setTimeout(() => {
const socket = new Socket(undefined, createSocketID());
socket.on("ready", () => {
logger.info(`Socket ${i} is ready`);
});
socket.setChannel("lobby");
sockets.push(socket);
if (socket.socketID == undefined) {
socket.socketID = createSocketID();
}
socketsBySocketID.set(socket.socketID, socket);
}, i * 5000);
}

View File

@ -1,11 +1,15 @@
import { padNum, unimportant } from "./helpers";
/**
* A logger that doesn't fuck with the readline prompt
* timestamps are likely wrong because of js timezones
**/
export class Logger {
private static log(method: string, ...args: any[]) {
// Clear current line
// Un-fuck the readline prompt
process.stdout.write("\x1b[2K\r");
// Log our stuff
// Log
(console as unknown as Record<string, (..._args: any[]) => any>)[
method
](
@ -14,7 +18,7 @@ export class Logger {
...args
);
// Fix the readline prompt (spooky cringe code)
// Re-print the readline prompt (spooky cringe global variable)
if ((globalThis as unknown as any).rl)
(globalThis as unknown as any).rl.prompt();
}
@ -38,7 +42,7 @@ export class Logger {
return new Date().toISOString().split("T")[0];
}
constructor(public id: string) {}
constructor(public id: string) { }
public info(...args: any[]) {
Logger.log("log", `[${this.id}]`, `\x1b[34m[info]\x1b[0m`, ...args);

View File

@ -1,9 +1,20 @@
// Gray console text
/**
* Gray console text maker
* @param str String to gray
* @returns Gray string to put in console
**/
export function unimportant(str: string) {
return `\x1b[90m${str}\x1b[0m`;
}
// Pad time strings
/**
* Pad time strings
* @param num Number to pad
* @param padAmount Amount of padding
* @param padChar Character to pad with
* @param left Whether to pad left or right
* @returns Padded string
**/
export function padNum(
num: number,
padAmount: number,
@ -21,6 +32,9 @@ export const encoder = new TextEncoder();
/**
* Check if an object has a property
* @param obj Object to check
* @param property Property to check
* @returns Whether the object has the property
**/
export function hasOwn(obj: any, property: string | number | Symbol) {
return (Object as unknown as any).hasOwn(obj, property);
@ -47,7 +61,15 @@ export function darken(color: string, amount = 0x40) {
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
}
// Brandon made this literally eons ago and it's fucking hilarious
// spooky.jsaurus
// NOT the same as poop_text
/**
* Make text spoopy
* @param message Message to spoop
* @returns Spooped message
**/
export function spoop_text(message: string) {
var old = message;
message = "";
@ -64,6 +86,11 @@ export function spoop_text(message: string) {
return message;
}
/**
* Mix two objects
* @param obj1 Object to mix into
* @param obj2 Object to mix
**/
export function mixin(obj1: any, obj2: any) {
for (const key of Object.keys(obj2)) {
obj1[key] = obj2[key];

View File

@ -75,3 +75,49 @@ Command.addCommand(
}
})
);
Command.addCommand(
new Command(["js", "eval"], "js <code>", async msg => {
function roughSizeOfObject(object: any) {
const objectList: any[] = [];
const stack = [object];
let bytes = 0;
while (stack.length) {
const value = stack.pop();
switch (typeof value) {
case 'boolean':
bytes += 4;
break;
case 'string':
bytes += value.length * 2;
break;
case 'number':
bytes += 8;
break;
case 'object':
if (!objectList.includes(value)) {
objectList.push(value);
for (const prop in value) {
if (value.hasOwnProperty(prop)) {
stack.push(value[prop]);
}
}
}
break;
}
}
return bytes;
}
if (msg.args.length > 1) {
try {
const output = eval(msg.args[1]);
return output;
} catch (err) {
return err;
}
}
})
);

View File

@ -36,6 +36,11 @@ const logger = new Logger("Sockets");
type CursorValue = string | number;
/**
* Extended websocket thing
* Most poeple call this "Client" but it's not on the client...
* This is likely the source of my memory leaks
**/
export class Socket extends EventEmitter {
private id: string;
private _id: string;
@ -65,6 +70,7 @@ export class Socket extends EventEmitter {
) {
super();
// real user?
if (ws) {
// Real user
this.ip = ws.data.ip;
@ -82,13 +88,18 @@ export class Socket extends EventEmitter {
let foundSocket;
let count = 0;
// big boi loop
for (const socket of socketsBySocketID.values()) {
// Skip us
if (socket.socketID == this.socketID) continue;
// Are they real?
if (socket.ws) {
// Are they connected?
if (socket.ws.readyState !== 1) continue;
}
// Same user ID?
if (socket.getUserID() == this.getUserID()) {
foundSocket = socket;
count++;
@ -96,11 +107,14 @@ export class Socket extends EventEmitter {
}
if (count >= 4) {
// Too many go away
this.destroy();
}
// logger.debug("Found socket?", foundSocket);
// If there is another socket, use their ID for some reason I forgot
// otherwise, make a new one
if (!foundSocket) {
// Use new session ID
this.id = createID();
@ -109,18 +123,24 @@ export class Socket extends EventEmitter {
this.id = foundSocket.id;
// Break us off
// didn't work nvm
//this.id = "broken";
//this.destroy();
}
// Load stuff
(async () => {
// Load our user data
await this.loadUser();
// Set our rate limits
this.resetRateLimits();
this.setNoteQuota(NoteQuota.PARAMS_RIDICULOUS);
// Bind a bunch of our event listeners so we do stuff when told to
this.bindEventListeners();
// Send a challenge to the browser for MPP.net frontends
if (config.browserChallenge == "basic") {
this.sendArray([{
m: "b",
@ -131,6 +151,7 @@ export class Socket extends EventEmitter {
}
})();
// all done
this.emit("ready");
}
@ -143,7 +164,7 @@ export class Socket extends EventEmitter {
}
/**
* Get the user ID of this socket
* Get the user ID (_id) of this socket
* @returns User ID
**/
public getUserID() {
@ -151,7 +172,7 @@ export class Socket extends EventEmitter {
}
/**
* Get the participant ID of this socket
* Get the participant ID (id) of this socket
* @returns Participant ID
**/
public getParticipantID() {
@ -744,6 +765,9 @@ export class Socket extends EventEmitter {
/**
* Ban this socket's user for doing bad things
* this doesn't actually ban the user, it just sends a notification right now FIXME
* @param duration Duration of the ban in milliseconds
* @param reason Reason for the ban
**/
public ban(duration: number, reason: string) {
// TODO cleaner ban system
@ -763,13 +787,26 @@ export class Socket extends EventEmitter {
}
export const socketsBySocketID = new Map<string, Socket>();
(globalThis as any).socketsBySocketID = socketsBySocketID;
/**
* Find a socket by their participant ID
* bad don't use for unique sockets
* @param id Participant ID to find
* @returns Socket object
**/
export function findSocketByPartID(id: string) {
for (const socket of socketsBySocketID.values()) {
if (socket.getParticipantID() == id) return socket;
}
}
/**
* Find all sockets by their user ID
* also not unique
* @param _id User ID to find
* @returns Socket objects
**/
export function findSocketsByUserID(_id: string) {
const sockets = [];
@ -781,6 +818,12 @@ export function findSocketsByUserID(_id: string) {
return sockets;
}
/**
* Find a socket by their IP
* probably not unique if they're on different tabs
* @param ip IP to find
* @returns Socket object
**/
export function findSocketByIP(ip: string) {
for (const socket of socketsBySocketID.values()) {
if (socket.getIP() == ip) {

View File

@ -22,18 +22,18 @@ export const adminLimits: RateLimitConstructorList = {
chains: {
userset: () =>
new RateLimitChain(
config.admin.chains.userset.interval,
config.admin.chains.userset.num
config.admin.chains.userset.num,
config.admin.chains.userset.interval
),
chset: () =>
new RateLimitChain(
config.admin.chains.chset.interval,
config.admin.chains.userset.num
config.admin.chains.chset.num,
config.admin.chains.userset.interval
),
n: () =>
new RateLimitChain(
config.admin.chains.n.interval,
config.admin.chains.userset.num
config.admin.chains.n.num,
config.admin.chains.userset.interval
)
}
};

View File

@ -22,18 +22,18 @@ export const crownLimits: RateLimitConstructorList = {
chains: {
userset: () =>
new RateLimitChain(
config.crown.chains.userset.interval,
config.crown.chains.userset.num
config.crown.chains.userset.num,
config.crown.chains.userset.interval
),
chset: () =>
new RateLimitChain(
config.crown.chains.chset.interval,
config.crown.chains.userset.num
config.crown.chains.chset.num,
config.crown.chains.userset.interval
),
n: () =>
new RateLimitChain(
config.crown.chains.n.interval,
config.crown.chains.userset.num
config.crown.chains.n.num,
config.crown.chains.userset.interval
)
}
};

View File

@ -22,18 +22,18 @@ export const userLimits: RateLimitConstructorList = {
chains: {
userset: () =>
new RateLimitChain(
config.user.chains.userset.interval,
config.user.chains.userset.num
config.user.chains.userset.num,
config.user.chains.userset.interval
),
chset: () =>
new RateLimitChain(
config.user.chains.chset.interval,
config.user.chains.userset.num
config.user.chains.chset.num,
config.user.chains.userset.interval
),
n: () =>
new RateLimitChain(
config.user.chains.n.interval,
config.user.chains.userset.num
config.user.chains.n.num,
config.user.chains.userset.interval
)
}
};

View File

@ -19,6 +19,8 @@ async function getIndex() {
// nobody realistically uses templates in 2024 and documents
// it well enough to say what library they used
// I totally forget if this even works
const index = Bun.file("./public/index.html");
const rendered = nunjucks.renderString(await index.text(), {
@ -91,11 +93,15 @@ export const app = Bun.serve<{ ip: string }>({
// logger.debug("Connection at " + socket.getIP());
// Let's put it in the dinner bucket.
socketsBySocketID.set((socket.socketID as any), socket);
if (socket.socketID == undefined) {
socket.socketID = createSocketID();
}
socketsBySocketID.set(socket.socketID, socket);
},
message: (ws, message) => {
// "Let's make it binary" said all websocket developers for some reason
// Fucking string
const msg = message.toString();
// Let's find out wtf they even sent
@ -103,8 +109,6 @@ export const app = Bun.serve<{ ip: string }>({
},
close: (ws, code, message) => {
// logger.debug("Close called");
// This usually gets called when someone leaves,
// but it's also used internally just in case
// some dickhead can't close their tab like a