diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..72446f4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/bun.lockb b/bun.lockb index e61fd76..12f607a 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 1c6cdae..55bf8e0 100644 --- a/package.json +++ b/package.json @@ -12,23 +12,23 @@ "author": "Hri7566", "license": "ISC", "dependencies": { - "@prisma/client": "5.2.0", + "@prisma/client": "5.7.0", "@t3-oss/env-core": "^0.6.1", "bun": "^1.0.0", "bun-types": "^1.0.1", + "commander": "^11.1.0", "date-holidays": "^3.21.5", "dotenv": "^8.6.0", "events": "^3.3.0", "fancy-text-converter": "^1.0.9", "keccak": "^2.1.0", - "mppclone-client": "^1.1.3", "unique-names-generator": "^4.7.1", "yaml": "^2.3.2", "zod": "^3.22.2" }, "devDependencies": { "@types/node": "^20.5.9", - "prisma": "^5.2.0", + "prisma": "5.7.0", "typescript": "^5.2.2" } } diff --git a/src/channel/Channel.ts b/src/channel/Channel.ts index 9c66671..930da6f 100644 --- a/src/channel/Channel.ts +++ b/src/channel/Channel.ts @@ -3,20 +3,21 @@ import { Logger } from "../util/Logger"; import { loadConfig } from "../util/config"; import { ChannelSettingValue, - ChannelSettings, + IChannelSettings, ClientEvents, Participant, - ServerEvents + ServerEvents, + IChannelInfo } from "../util/types"; import { Socket } from "../ws/Socket"; import { validateChannelSettings } from "./settings"; -import { socketsBySocketID } from "../ws/server"; +import { findSocketByPartID, socketsBySocketID } from "../ws/server"; import Crown from "./Crown"; interface ChannelConfig { forceLoad: string[]; - lobbySettings: Partial; - defaultSettings: Partial; + lobbySettings: Partial; + defaultSettings: Partial; lobbyRegexes: string[]; lobbyBackdoor: string; fullChannel: string; @@ -48,7 +49,7 @@ export const config = loadConfig("config/channels.yml", { export const channelList = new Array(); export class Channel extends EventEmitter { - private settings: Partial = config.defaultSettings; + private settings: Partial = config.defaultSettings; private ppl = new Array(); public logger: Logger; @@ -59,7 +60,7 @@ export class Channel extends EventEmitter { constructor( private _id: string, - set?: Partial, + set?: Partial, creator?: Socket, owner_id?: string ) { @@ -193,7 +194,7 @@ export class Channel extends EventEmitter { * @returns undefined */ public changeSettings( - set: Partial, + set: Partial, admin: boolean = false ) { if (this.isDestroyed()) return; @@ -223,7 +224,7 @@ export class Channel extends EventEmitter { * @param setting Channel setting to get * @returns Value of setting */ - public getSetting(setting: keyof ChannelSettings) { + public getSetting(setting: keyof IChannelSettings) { return this.settings[setting]; } @@ -369,8 +370,10 @@ export class Channel extends EventEmitter { id: this.getID(), count: this.ppl.length, settings: this.settings, - crown: JSON.parse(JSON.stringify(this.crown)) - }; + crown: this.crown + ? JSON.parse(JSON.stringify(this.crown)) + : undefined + } as IChannelInfo; } /** @@ -520,8 +523,34 @@ export class Channel extends EventEmitter { */ public dropCrown() { if (this.crown) { - delete this.crown.participantId; this.crown.time = Date.now(); + + let socket; + if (this.crown.participantId) + socket = findSocketByPartID(this.crown.participantId); + + let x = Math.random() * 100; + let y1 = Math.random() * 100; + let y2 = y1 + Math.random() * (100 - y1); + + if (socket) { + const cursorPos = socket.getCursorPos(); + + let cursorX = cursorPos.x; + if (typeof cursorPos.x == "string") + cursorX = parseInt(cursorPos.x); + + let cursorY = cursorPos.y; + if (typeof cursorPos.y == "string") + cursorY = parseInt(cursorPos.y); + } + + // Screen positions + this.crown.startPos = { x, y: y1 }; + this.crown.endPos = { x, y: y2 }; + + delete this.crown.participantId; + this.emit("update"); } } diff --git a/src/channel/Crown.ts b/src/channel/Crown.ts index e27434a..41368bc 100644 --- a/src/channel/Crown.ts +++ b/src/channel/Crown.ts @@ -1,4 +1,4 @@ -import { Participant } from "../util/types"; +import { Participant, Vector2 } from "../util/types"; import { Socket } from "../ws/Socket"; export class Crown { @@ -6,6 +6,16 @@ export class Crown { public participantId: string | undefined; public time: number = Date.now(); + public startPos: Vector2 = { + x: 50, + y: 50 + }; + + public endPos: Vector2 = { + x: 50, + y: 50 + }; + public canBeSetBy(socket: Socket) { // can claim, drop, or give if... const flags = socket.getUserFlags(); diff --git a/src/util/types.d.ts b/src/util/types.d.ts index 4d08470..9cabef3 100644 --- a/src/util/types.d.ts +++ b/src/util/types.d.ts @@ -29,7 +29,7 @@ declare interface Participant extends User { id: string; // participant id (same as user id on mppclone) } -declare type ChannelSettings = { +declare type IChannelSettings = { color: string; crownsolo: boolean; chat: boolean; @@ -87,14 +87,8 @@ declare interface Crown { userId: string; partcipantId?: string; time: number; - startPos: { - x: number; - y: number; - }; - endPos: { - x: number; - y: number; - }; + startPos: Vector2; + endPos: Vector2; } declare interface ChannelInfo { @@ -103,7 +97,7 @@ declare interface ChannelInfo { id: string; _id: string; crown?: Crown; - settings: Partial; + settings: Partial; } // Events copied from Hri7566/mppclone-client typedefs @@ -120,7 +114,7 @@ declare interface ServerEvents { ch: { m: "ch"; _id: string; - set: ChannelSettings; + set: IChannelSettings; }; chown: { @@ -130,7 +124,7 @@ declare interface ServerEvents { chset: { m: "chset"; - set: ChannelSettings; + set: IChannelSettings; }; custom: { @@ -328,3 +322,29 @@ declare type ServerEventListener = { id: EventID; callback: (msg: ServerEvents[EventID], socket: Socket) => void; }; + +declare type Vector2 = { + x: T; + y: T; +}; + +declare interface ICrown { + // User who had the crown (remove participantId if there is none, no userId if there hasn't been one) + userId?: string; + participantId?: string; + + // Crown position when dropped (beginning and end of slide animation) + startPos: Vector2; + endPos: Vector2; + + // Timestamp from the latest crown update + time: number; +} + +declare interface IChannelInfo { + _id: string; + id: string; + count: number; + settings: Partial; + crown?: ICrown; +} diff --git a/src/ws/Socket.ts b/src/ws/Socket.ts index a03ba32..09e17b8 100644 --- a/src/ws/Socket.ts +++ b/src/ws/Socket.ts @@ -1,6 +1,6 @@ /** * Socket connection module - * + * * Represents user connections */ @@ -8,17 +8,18 @@ import { createColor, createID, createUserID } from "../util/id"; import EventEmitter from "events"; import { ChannelInfo, - ChannelSettings, + IChannelSettings, ClientEvents, Participant, ServerEvents, - UserFlags + UserFlags, + Vector2 } from "../util/types"; import { User } from "@prisma/client"; import { createUser, readUser, updateUser } from "../data/user"; import { eventGroups } from "./events"; import { Gateway } from "./Gateway"; -import { Channel, channelList } from "../channel/Channel"; +import { channelList, Channel } from "../channel/Channel"; import { ServerWebSocket } from "bun"; import { socketsBySocketID } from "./server"; import { Logger } from "../util/Logger"; @@ -30,6 +31,8 @@ import { config } from "./usersConfig"; const logger = new Logger("Sockets"); +type CursorValue = string | number; + export class Socket extends EventEmitter { private id: string; private _id: string; @@ -43,19 +46,14 @@ export class Socket extends EventEmitter { public desiredChannel: { _id: string | undefined; - set: Partial | undefined; + set: Partial | undefined; } = { _id: undefined, set: {} }; public currentChannelID: string | undefined; - private cursorPos: - | { - x: string | number | undefined; - y: string | number | undefined; - } - | undefined; + private cursorPos: Vector2 = { x: 200, y: 100 }; constructor(private ws: ServerWebSocket, public socketID: string) { super(); @@ -110,7 +108,7 @@ export class Socket extends EventEmitter { return this.id; } - public setChannel(_id: string, set?: Partial) { + public setChannel(_id: string, set?: Partial) { if (this.isDestroyed()) return; this.desiredChannel._id = _id; @@ -136,7 +134,8 @@ export class Socket extends EventEmitter { // Doesn't exist, create channel = new Channel( this.desiredChannel._id, - this.desiredChannel.set + this.desiredChannel.set, + this ); channel.join(this); @@ -255,13 +254,13 @@ export class Socket extends EventEmitter { public getCursorPos() { if (!this.cursorPos) this.cursorPos = { - x: undefined, - y: undefined + x: "-10.00", + y: "-10.00" }; return this.cursorPos; } - public setCursorPos(x: number | string, y: number | string) { + public setCursorPos(x: CursorValue, y: CursorValue) { if (typeof x == "number") { x = x.toFixed(2); } diff --git a/src/ws/events.inc.ts b/src/ws/events.inc.ts index 26c35a1..4f15916 100644 --- a/src/ws/events.inc.ts +++ b/src/ws/events.inc.ts @@ -1,3 +1,4 @@ -// Bun hoists import, but not require? +// Bun hoists import so we are kinda forced to use require here... +// Maybe bun should have a setting for that :/ require("./events/user"); require("./events/admin"); diff --git a/src/ws/events/user/handlers/m.ts b/src/ws/events/user/handlers/m.ts index 156ef2d..06362f4 100644 --- a/src/ws/events/user/handlers/m.ts +++ b/src/ws/events/user/handlers/m.ts @@ -6,6 +6,13 @@ export const m: ServerEventListener<"m"> = { // Cursor movement if (!socket.rateLimits?.normal.m.attempt()) return; if (!msg.x || !msg.y) return; - socket.setCursorPos(msg.x, msg.y); + + let x = msg.x; + let y = msg.y; + + if (typeof msg.x == "string") x = parseFloat(msg.x); + if (typeof msg.y == "string") y = parseFloat(msg.y); + + socket.setCursorPos(x, y); } };