Add note and time messages
This commit is contained in:
parent
e9407a5fee
commit
66c7560b54
|
@ -2,3 +2,4 @@ defaultName: "Anonymous"
|
|||
defaultFlags:
|
||||
volume: 100
|
||||
enableColorChanging: false
|
||||
enableCustomNoteData: true
|
||||
|
|
|
@ -103,6 +103,7 @@ export class Channel extends EventEmitter {
|
|||
set: Partial<ChannelSettings>,
|
||||
admin: boolean = false
|
||||
) {
|
||||
if (this.isDestroyed()) return;
|
||||
if (!admin) {
|
||||
if (set.lobby) set.lobby = undefined;
|
||||
if (set.owner_id) set.owner_id = undefined;
|
||||
|
@ -125,6 +126,7 @@ export class Channel extends EventEmitter {
|
|||
}
|
||||
|
||||
public join(socket: Socket) {
|
||||
if (this.isDestroyed()) return;
|
||||
const part = socket.getParticipant() as Participant;
|
||||
|
||||
// Unknown side-effects, but for type safety...
|
||||
|
@ -289,6 +291,7 @@ export class Channel extends EventEmitter {
|
|||
this.alreadyBound = true;
|
||||
|
||||
this.on("update", () => {
|
||||
// Send updated info
|
||||
for (const socket of socketsBySocketID.values()) {
|
||||
for (const p of this.ppl) {
|
||||
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) => {
|
||||
|
@ -315,6 +322,53 @@ export class Channel extends EventEmitter {
|
|||
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
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import YAML from "yaml";
|
||||
import { readFileSync } from "fs";
|
||||
import fs from "fs";
|
||||
|
||||
export function loadConfig<T>(filepath: string, def: T) {
|
||||
try {
|
||||
const data = readFileSync(filepath).toString();
|
||||
const data = fs.readFileSync(filepath).toString();
|
||||
const parsed = YAML.parse(data);
|
||||
|
||||
return parsed as T;
|
||||
} catch (err) {
|
||||
console.error("Unable to load config:", err);
|
||||
|
|
|
@ -313,7 +313,7 @@ declare interface ClientEvents {
|
|||
t: {
|
||||
m: "t";
|
||||
t: number;
|
||||
e: number;
|
||||
e: number | undefined;
|
||||
};
|
||||
|
||||
bye: {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export class Gateway {
|
||||
public hasProcessedHi: boolean = false;
|
||||
public hasSentDevices: boolean = false;
|
||||
public lastPing: number = Date.now();
|
||||
}
|
||||
|
|
|
@ -5,12 +5,12 @@ import {
|
|||
ChannelSettings,
|
||||
ClientEvents,
|
||||
Participant,
|
||||
ServerEvents,
|
||||
UserFlags
|
||||
} from "../util/types";
|
||||
import { User } from "@prisma/client";
|
||||
import { createUser, readUser, updateUser } from "../data/user";
|
||||
import { eventGroups } from "./events";
|
||||
import { loadConfig } from "../util/config";
|
||||
import { Gateway } from "./Gateway";
|
||||
import { Channel, channelList } from "../channel/Channel";
|
||||
import { ServerWebSocket } from "bun";
|
||||
|
@ -20,20 +20,7 @@ import { RateLimitConstructorList, RateLimitList } from "./ratelimit/config";
|
|||
import { adminLimits } from "./ratelimit/limits/admin";
|
||||
import { userLimits } from "./ratelimit/limits/user";
|
||||
import { NoteQuota } from "./ratelimit/NoteQuota";
|
||||
|
||||
interface UsersConfig {
|
||||
defaultName: string;
|
||||
defaultFlags: UserFlags;
|
||||
enableColorChanging: boolean;
|
||||
}
|
||||
|
||||
const usersConfig = loadConfig<UsersConfig>("config/users.yml", {
|
||||
defaultName: "Anonymous",
|
||||
defaultFlags: {
|
||||
volume: 100
|
||||
},
|
||||
enableColorChanging: false
|
||||
});
|
||||
import { config } from "./usersConfig";
|
||||
|
||||
const logger = new Logger("Sockets");
|
||||
|
||||
|
@ -46,7 +33,7 @@ export class Socket extends EventEmitter {
|
|||
public gateway = new Gateway();
|
||||
|
||||
public rateLimits: RateLimitList | undefined;
|
||||
public noteQuota = new NoteQuota(this.onQuota);
|
||||
public noteQuota = new NoteQuota();
|
||||
|
||||
public desiredChannel: {
|
||||
_id: string | undefined;
|
||||
|
@ -176,9 +163,9 @@ export class Socket extends EventEmitter {
|
|||
if (!user) {
|
||||
await createUser(
|
||||
this._id,
|
||||
usersConfig.defaultName,
|
||||
config.defaultName,
|
||||
createColor(this.ip),
|
||||
usersConfig.defaultFlags
|
||||
config.defaultFlags
|
||||
);
|
||||
|
||||
user = await readUser(this._id);
|
||||
|
@ -316,7 +303,7 @@ export class Socket extends EventEmitter {
|
|||
public async userset(name?: string, color?: string) {
|
||||
let isColor = false;
|
||||
|
||||
if (color && usersConfig.enableColorChanging) {
|
||||
if (color && config.enableColorChanging) {
|
||||
isColor =
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -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
|
||||
}
|
||||
]);
|
||||
}
|
||||
};
|
|
@ -8,6 +8,7 @@ import { ch } from "./handlers/ch";
|
|||
import { m } from "./handlers/m";
|
||||
import { a } from "./handlers/a";
|
||||
import { userset } from "./handlers/userset";
|
||||
import { n } from "./handlers/n";
|
||||
|
||||
EVENTGROUP_USER.add(hi);
|
||||
EVENTGROUP_USER.add(devices);
|
||||
|
@ -15,5 +16,6 @@ EVENTGROUP_USER.add(ch);
|
|||
EVENTGROUP_USER.add(m);
|
||||
EVENTGROUP_USER.add(a);
|
||||
EVENTGROUP_USER.add(userset);
|
||||
EVENTGROUP_USER.add(n);
|
||||
|
||||
eventGroups.push(EVENTGROUP_USER);
|
||||
|
|
|
@ -14,7 +14,7 @@ export class NoteQuota {
|
|||
maxHistLen: 3
|
||||
};
|
||||
|
||||
constructor(public cb: (points: number) => void) {
|
||||
constructor(public cb?: (points: number) => void) {
|
||||
this.setParams();
|
||||
this.resetPoints();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
Loading…
Reference in New Issue