Add note and time messages

This commit is contained in:
Hri7566 2023-09-12 01:55:15 -04:00
parent e9407a5fee
commit 66c7560b54
11 changed files with 157 additions and 25 deletions

View File

@ -2,3 +2,4 @@ defaultName: "Anonymous"
defaultFlags: defaultFlags:
volume: 100 volume: 100
enableColorChanging: false enableColorChanging: false
enableCustomNoteData: true

View File

@ -103,6 +103,7 @@ export class Channel extends EventEmitter {
set: Partial<ChannelSettings>, set: Partial<ChannelSettings>,
admin: boolean = false admin: boolean = false
) { ) {
if (this.isDestroyed()) return;
if (!admin) { if (!admin) {
if (set.lobby) set.lobby = undefined; if (set.lobby) set.lobby = undefined;
if (set.owner_id) set.owner_id = undefined; if (set.owner_id) set.owner_id = undefined;
@ -125,6 +126,7 @@ export class Channel extends EventEmitter {
} }
public join(socket: Socket) { public join(socket: Socket) {
if (this.isDestroyed()) return;
const part = socket.getParticipant() as Participant; const part = socket.getParticipant() as Participant;
// Unknown side-effects, but for type safety... // Unknown side-effects, but for type safety...
@ -289,6 +291,7 @@ export class Channel extends EventEmitter {
this.alreadyBound = true; this.alreadyBound = true;
this.on("update", () => { this.on("update", () => {
// Send updated info
for (const socket of socketsBySocketID.values()) { for (const socket of socketsBySocketID.values()) {
for (const p of this.ppl) { for (const p of this.ppl) {
if (socket.getParticipantID() == p.id) { if (socket.getParticipantID() == p.id) {
@ -299,6 +302,10 @@ export class Channel extends EventEmitter {
} }
} }
} }
if (this.ppl.length == 0) {
this.destroy();
}
}); });
this.on("message", (msg: ServerEvents["a"], socket: Socket) => { this.on("message", (msg: ServerEvents["a"], socket: Socket) => {
@ -315,6 +322,53 @@ export class Channel extends EventEmitter {
this.chatHistory.push(outgoing); this.chatHistory.push(outgoing);
}); });
} }
public playNotes(msg: ServerEvents["n"], socket: Socket) {
if (this.isDestroyed()) return;
const part = socket.getParticipant();
if (!part) return;
let clientMsg: ClientEvents["n"] = {
m: "n",
n: msg.n,
t: msg.t,
p: part.id
};
let sentSocketIDs = new Array<string>();
for (const p of this.ppl) {
socketLoop: for (const socket of socketsBySocketID.values()) {
if (socket.isDestroyed()) continue socketLoop;
if (socket.getParticipantID() != p.id) continue socketLoop;
if (socket.getParticipantID() == part.id) continue socketLoop;
if (sentSocketIDs.includes(socket.socketID))
continue socketLoop;
socket.sendArray([clientMsg]);
sentSocketIDs.push(socket.socketID);
}
}
}
private destroyed = false;
public destroy() {
if (this.destroyed) return;
this.destroyed = true;
if (this.ppl.length > 0) {
for (const socket of socketsBySocketID.values()) {
if (socket.currentChannelID !== this.getID()) continue;
socket.setChannel(config.fullChannel);
}
}
channelList.splice(channelList.indexOf(this), 1);
}
public isDestroyed() {
return this.destroyed == true;
}
} }
// Forceloader // Forceloader

View File

@ -1,11 +1,10 @@
import YAML from "yaml"; import YAML from "yaml";
import { readFileSync } from "fs"; import fs from "fs";
export function loadConfig<T>(filepath: string, def: T) { export function loadConfig<T>(filepath: string, def: T) {
try { try {
const data = readFileSync(filepath).toString(); const data = fs.readFileSync(filepath).toString();
const parsed = YAML.parse(data); const parsed = YAML.parse(data);
return parsed as T; return parsed as T;
} catch (err) { } catch (err) {
console.error("Unable to load config:", err); console.error("Unable to load config:", err);

2
src/util/types.d.ts vendored
View File

@ -313,7 +313,7 @@ declare interface ClientEvents {
t: { t: {
m: "t"; m: "t";
t: number; t: number;
e: number; e: number | undefined;
}; };
bye: { bye: {

View File

@ -1,4 +1,5 @@
export class Gateway { export class Gateway {
public hasProcessedHi: boolean = false; public hasProcessedHi: boolean = false;
public hasSentDevices: boolean = false; public hasSentDevices: boolean = false;
public lastPing: number = Date.now();
} }

View File

@ -5,12 +5,12 @@ import {
ChannelSettings, ChannelSettings,
ClientEvents, ClientEvents,
Participant, Participant,
ServerEvents,
UserFlags UserFlags
} from "../util/types"; } from "../util/types";
import { User } from "@prisma/client"; import { User } from "@prisma/client";
import { createUser, readUser, updateUser } from "../data/user"; import { createUser, readUser, updateUser } from "../data/user";
import { eventGroups } from "./events"; import { eventGroups } from "./events";
import { loadConfig } from "../util/config";
import { Gateway } from "./Gateway"; import { Gateway } from "./Gateway";
import { Channel, channelList } from "../channel/Channel"; import { Channel, channelList } from "../channel/Channel";
import { ServerWebSocket } from "bun"; import { ServerWebSocket } from "bun";
@ -20,20 +20,7 @@ import { RateLimitConstructorList, RateLimitList } from "./ratelimit/config";
import { adminLimits } from "./ratelimit/limits/admin"; import { adminLimits } from "./ratelimit/limits/admin";
import { userLimits } from "./ratelimit/limits/user"; import { userLimits } from "./ratelimit/limits/user";
import { NoteQuota } from "./ratelimit/NoteQuota"; import { NoteQuota } from "./ratelimit/NoteQuota";
import { config } from "./usersConfig";
interface UsersConfig {
defaultName: string;
defaultFlags: UserFlags;
enableColorChanging: boolean;
}
const usersConfig = loadConfig<UsersConfig>("config/users.yml", {
defaultName: "Anonymous",
defaultFlags: {
volume: 100
},
enableColorChanging: false
});
const logger = new Logger("Sockets"); const logger = new Logger("Sockets");
@ -46,7 +33,7 @@ export class Socket extends EventEmitter {
public gateway = new Gateway(); public gateway = new Gateway();
public rateLimits: RateLimitList | undefined; public rateLimits: RateLimitList | undefined;
public noteQuota = new NoteQuota(this.onQuota); public noteQuota = new NoteQuota();
public desiredChannel: { public desiredChannel: {
_id: string | undefined; _id: string | undefined;
@ -176,9 +163,9 @@ export class Socket extends EventEmitter {
if (!user) { if (!user) {
await createUser( await createUser(
this._id, this._id,
usersConfig.defaultName, config.defaultName,
createColor(this.ip), createColor(this.ip),
usersConfig.defaultFlags config.defaultFlags
); );
user = await readUser(this._id); user = await readUser(this._id);
@ -316,7 +303,7 @@ export class Socket extends EventEmitter {
public async userset(name?: string, color?: string) { public async userset(name?: string, color?: string) {
let isColor = false; let isColor = false;
if (color && usersConfig.enableColorChanging) { if (color && config.enableColorChanging) {
isColor = isColor =
typeof color === "string" && !!color.match(/^#[0-9a-f]{6}$/i); typeof color === "string" && !!color.match(/^#[0-9a-f]{6}$/i);
} }
@ -373,5 +360,9 @@ export class Socket extends EventEmitter {
]); ]);
} }
public onQuota(points: number) {} public playNotes(msg: ServerEvents["n"]) {
const ch = this.getCurrentChannel();
if (!ch) return;
ch.playNotes(msg, this);
}
} }

View File

@ -0,0 +1,39 @@
import { ServerEventListener } from "../../../../util/types";
import { config } from "../../../usersConfig";
export const n: ServerEventListener<"n"> = {
id: "n",
callback: (msg, socket) => {
// Piano note
if (!Array.isArray(msg.n)) return;
if (typeof msg.t !== "number") return;
// Check note properties
for (const n of msg.n) {
if (typeof n.n != "string") return;
// TODO Check for config.enableCustomNoteData here
// For whatever reason, Bun likes to crash when we access that config object
continue;
if (n.s) {
if (typeof n.s !== "number") return;
if (n.s == 1) {
if (typeof n.d !== "number") return;
if (n.d < 0 || n.d > 200) return;
if (typeof n.v !== "number") return;
if (n.v < 0) n.v = 0;
if (n.v > 1) n.v = 1;
}
}
}
let amount = msg.n.length;
// TODO Check crownsolo
if (socket.noteQuota.spend(amount)) {
socket.playNotes(msg);
}
}
};

View File

@ -0,0 +1,19 @@
import { ServerEventListener } from "../../../../util/types";
export const t: ServerEventListener<"t"> = {
id: "t",
callback: (msg, socket) => {
// Ping
if (msg.e) {
if (typeof msg.e !== "number") return;
}
socket.sendArray([
{
m: "t",
t: Date.now(),
e: typeof msg.e == "number" ? msg.e : undefined
}
]);
}
};

View File

@ -8,6 +8,7 @@ import { ch } from "./handlers/ch";
import { m } from "./handlers/m"; import { m } from "./handlers/m";
import { a } from "./handlers/a"; import { a } from "./handlers/a";
import { userset } from "./handlers/userset"; import { userset } from "./handlers/userset";
import { n } from "./handlers/n";
EVENTGROUP_USER.add(hi); EVENTGROUP_USER.add(hi);
EVENTGROUP_USER.add(devices); EVENTGROUP_USER.add(devices);
@ -15,5 +16,6 @@ EVENTGROUP_USER.add(ch);
EVENTGROUP_USER.add(m); EVENTGROUP_USER.add(m);
EVENTGROUP_USER.add(a); EVENTGROUP_USER.add(a);
EVENTGROUP_USER.add(userset); EVENTGROUP_USER.add(userset);
EVENTGROUP_USER.add(n);
eventGroups.push(EVENTGROUP_USER); eventGroups.push(EVENTGROUP_USER);

View File

@ -14,7 +14,7 @@ export class NoteQuota {
maxHistLen: 3 maxHistLen: 3
}; };
constructor(public cb: (points: number) => void) { constructor(public cb?: (points: number) => void) {
this.setParams(); this.setParams();
this.resetPoints(); this.resetPoints();
} }

26
src/ws/usersConfig.ts Normal file
View File

@ -0,0 +1,26 @@
import { loadConfig } from "../util/config";
import { UserFlags } from "../util/types";
export interface UsersConfig {
defaultName: string;
defaultFlags: UserFlags;
enableColorChanging: boolean;
enableCustomNoteData: boolean;
}
export const usersConfigPath = "config/users.yml";
export const defaultUsersConfig = {
defaultName: "Anonymous",
defaultFlags: {
volume: 100
},
enableColorChanging: false,
enableCustomNoteData: true
};
// Importing this elsewhere causes bun to segfault
export const config = loadConfig<UsersConfig>(
usersConfigPath,
defaultUsersConfig
);