diff --git a/bun.lockb b/bun.lockb index 0816a0d..3da2746 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/src/channel/Channel.ts b/src/channel/Channel.ts index d359e40..9fc84e0 100644 --- a/src/channel/Channel.ts +++ b/src/channel/Channel.ts @@ -25,7 +25,7 @@ interface CachedKickban { export class Channel extends EventEmitter { private settings: Partial; - private ppl = new Array(); + private ppl = new Array(); public chatHistory = new Array(); private async loadChatHistory() { @@ -94,10 +94,13 @@ export class Channel extends EventEmitter { this.logger.info("Loaded Chat History."); this.on("update", () => { + this.logger.debug("-------- UPDATE START --------"); // Send updated info for (const socket of socketsBySocketID.values()) { for (const p of this.ppl) { - if (socket.getParticipantID() == p.id) { + //if (socket.getParticipantID() == p.id) { + if (p.uuids.includes(socket.getUUID())) { + this.logger.debug("sending to", socket.getUUID()) socket.sendChannelUpdate( this.getInfo(), this.getParticipantList() @@ -141,6 +144,10 @@ export class Channel extends EventEmitter { if (msg.message.startsWith("/")) { this.emit("command", msg, socket); } + + if (msg.message == "debug") { + this.logger.info(socket.getUUID(), socket.currentChannelID); + } } catch (err) { this.logger.error(err); } @@ -256,6 +263,7 @@ export class Channel extends EventEmitter { public join(socket: Socket): void { //! /!\ Players are forced to join the same channel on two different tabs! //? TODO Should this be a bug or a feature? + this.logger.debug("join triggered"); if (this.isDestroyed()) return; const part = socket.getParticipant() as Participant; @@ -269,26 +277,48 @@ export class Channel extends EventEmitter { // TODO Send notification for ban const chs = ChannelList.getList(); for (const ch of chs) { - if (ch.getID() == config.fullChannel) { - return ch.join(socket); + const chid = ch.getID(); + if (chid == config.fullChannel) { + return socket.setChannel(chid) } } } // Is user in this channel? if (this.hasUser(part._id)) { - // Alreay in channel, don't add to list, but tell them they're here + // Already in channel, don't add to list, but tell them they're here hasChangedChannel = true; - this.ppl.push(part); + + for (const p of this.ppl) { + if (p.id !== part.id) continue; + p.uuids.push(socket.getUUID()) + } + + socket.sendChannelUpdate(this.getInfo(), this.getParticipantList()); } else { // Are we full? if (!this.isFull()) { // Add to channel hasChangedChannel = true; - this.ppl.push(part); + this.ppl.push({ + _id: part._id, + name: part.name, + color: part.color, + id: part.id, + tag: part.tag, + uuids: [socket.getUUID()] + }); } else { - // Put them in full channel - return socket.setChannel(config.fullChannel); + if (socket.currentChannelID !== config.fullChannel) { + // Put them in full channel + const chs = ChannelList.getList(); + for (const ch of chs) { + const chid = ch.getID(); + if (chid == config.fullChannel) { + return socket.setChannel(chid) + } + } + } } } @@ -348,7 +378,7 @@ export class Channel extends EventEmitter { // Broadcast a channel update so everyone subscribed to the channel list can see us this.emit("update", this); - this.logger.debug("Settings:", this.settings); + //this.logger.debug("Settings:", this.settings); } /** @@ -386,6 +416,11 @@ export class Channel extends EventEmitter { ]); this.emit("update", this); + } else { + for (const p of this.ppl) { + if (!p.uuids.includes(socket.getUUID())) continue; + p.uuids.splice(p.uuids.indexOf(socket.getUUID()), 1); + } } } @@ -425,7 +460,13 @@ export class Channel extends EventEmitter { * @returns List of people */ public getParticipantList() { - return this.ppl; + return this.ppl.map(p => ({ + id: p.id, + _id: p._id, + name: p.name, + color: p.color, + tag: p.tag + })); } /** @@ -606,35 +647,60 @@ export class Channel extends EventEmitter { } /** - * Kickban a poor soul for t milliseconds. + * Kickban a participant for t milliseconds. * @param _id User ID to ban * @param t Time in millseconds to ban for **/ public kickban(_id: string, t: number = 1000 * 60 * 30) { const now = Date.now(); - if (!this.hasUser(_id)) return; - - const part = this.ppl.find(p => p._id == _id); - if (!part) return; - - this.bans.push({ - userId: _id, - startTime: now, - endTime: now + t - }); - - const socket = findSocketByPartID(part.id); - if (!socket) return; + let shouldUpdate = false; const banChannel = ChannelList.getList().find( ch => ch.getID() == config.fullChannel ); if (!banChannel) return; - banChannel.join(socket); - this.emit("update", this); + let isBanned = this.bans.map(b => b.userId).includes(_id); + let overwrite = false; + + if (isBanned) { + overwrite = true; + } + + let uuidsToKick: string[] = []; + + for (const part of this.ppl) { + if (part._id !== _id) continue; + + if (!overwrite) { + this.bans.push({ + userId: _id, + startTime: now, + endTime: now + t + }); + } else { + for (const ban of this.bans) { + if (ban.userId !== _id) continue; + ban.startTime = now; + ban.endTime = now + t; + } + } + + uuidsToKick = [...uuidsToKick, ...part.uuids]; + + shouldUpdate = true; + } + + for (const socket of socketsBySocketID.values()) { + if (uuidsToKick.includes(socket.getUUID())) { + socket.setChannel(banChannel.getID()); + } + } + + if (shouldUpdate) + this.emit("update", this); } public isBanned(_id: string) { diff --git a/src/data/history.ts b/src/data/history.ts index bab7aad..b769e27 100644 --- a/src/data/history.ts +++ b/src/data/history.ts @@ -16,10 +16,14 @@ export async function saveChatHistory(_id: string, chatHistory: object) { } export async function getChatHistory(_id: string) { - const history = await prisma.chatHistory.findFirst({ where: { id: _id } }); - if (!history) { + try { + const history = await prisma.chatHistory.findFirst({ where: { id: _id } }); + if (!history) { + return []; + } else { + return JSON.parse(history.messages); + } + } catch (err) { return []; - } else { - return JSON.parse(history.messages); } } diff --git a/src/data/user.ts b/src/data/user.ts index 5e523c1..b2bc60a 100644 --- a/src/data/user.ts +++ b/src/data/user.ts @@ -27,20 +27,24 @@ export async function deleteUser(_id: string) { } export async function readUser(_id: string) { - const data = await prisma.user.findUnique({ - where: { id: _id } - }); + try { + const data = await prisma.user.findUnique({ + where: { id: _id } + }); - if (!data) return null; + if (!data) return null; - // return { - // _id: data.id, - // name: data.name, - // color: data.color, - // flags: data.flags - // }; + // return { + // _id: data.id, + // name: data.name, + // color: data.color, + // flags: data.flags + // }; - return data; + return data; + } catch (err) { + return createUser(_id); + } } export async function updateUser( diff --git a/src/ws/Socket.ts b/src/ws/Socket.ts index 67e4c78..a13c0fe 100644 --- a/src/ws/Socket.ts +++ b/src/ws/Socket.ts @@ -37,6 +37,7 @@ export class Socket extends EventEmitter { private id: string; private _id: string; private ip: string; + private uuid: string; private user: User | null = null; public gateway = new Gateway(); @@ -48,9 +49,9 @@ export class Socket extends EventEmitter { _id: string | undefined; set: Partial | undefined; } = { - _id: undefined, - set: {} - }; + _id: undefined, + set: {} + }; public currentChannelID: string | undefined; private cursorPos: Vector2 = { x: 200, y: 100 }; @@ -64,19 +65,26 @@ export class Socket extends EventEmitter { // User ID this._id = createUserID(this.getIP()); + this.uuid = crypto.randomUUID(); // Check if we're already connected // We need to skip ourselves, so we loop here instead of using a helper let foundSocket; + let count = 0; for (const socket of socketsBySocketID.values()) { - if (socket.socketID == this.socketID) continue; + if (socket.socketID == this.socketID || socket.ws.readyState !== 1) continue; if (socket.getUserID() == this.getUserID()) { foundSocket = socket; + count++; } } + if (count >= 4) { + this.destroy(); + } + // logger.debug("Found socket?", foundSocket); if (!foundSocket) { @@ -84,11 +92,11 @@ export class Socket extends EventEmitter { this.id = createID(); } else { // Use original session ID - // this.id = foundSocket.id; + this.id = foundSocket.id; // Break us off - this.id = "broken"; - this.destroy(); + //this.id = "broken"; + //this.destroy(); } (async () => { @@ -460,6 +468,10 @@ export class Socket extends EventEmitter { channel.kickban(_id, ms); } } + + public getUUID() { + return this.uuid; + } } export const socketsBySocketID = new Map(); diff --git a/src/ws/events/admin/handlers/color.ts b/src/ws/events/admin/handlers/color.ts index 6c8a77e..4d620c5 100644 --- a/src/ws/events/admin/handlers/color.ts +++ b/src/ws/events/admin/handlers/color.ts @@ -6,6 +6,7 @@ export const color: ServerEventListener<"color"> = { id: "color", callback: async (msg, socket) => { // I'm not allowed to use this feature on MPP.net for a really stupid reason + // Nevermind, fishing bot has color again const id = msg._id; const color = msg.color; diff --git a/src/ws/events/user/handlers/chown.ts b/src/ws/events/user/handlers/chown.ts new file mode 100644 index 0000000..9954cd9 --- /dev/null +++ b/src/ws/events/user/handlers/chown.ts @@ -0,0 +1,29 @@ +import { Logger } from "../../../../util/Logger"; +import { ServerEventListener } from "../../../../util/types"; + +const logger = new Logger("chown"); + +export const chown: ServerEventListener<"chown"> = { + id: "chown", + callback: (msg, socket) => { + // Change channel ownership + if (typeof msg.id == "undefined") return; + + + const ch = socket.getCurrentChannel(); + if (!ch) return; + + if (!socket.isOwner()) return; + + if (!ch.crown) { + // TODO Crown admin stuff + } else { + if (!ch.crown.canBeSetBy(socket)) return; + + const heir = ch.getParticipantList().find(p => p.id == msg.id); + if (!heir) return; + + ch.chown(heir); + } + } +}; diff --git a/src/ws/events/user/handlers/kickban.ts b/src/ws/events/user/handlers/kickban.ts index 331399a..9168f6b 100644 --- a/src/ws/events/user/handlers/kickban.ts +++ b/src/ws/events/user/handlers/kickban.ts @@ -3,9 +3,9 @@ import { ServerEventListener } from "../../../../util/types"; export const kickban: ServerEventListener<"kickban"> = { id: "kickban", callback: (msg, socket) => { - // Kickban asshole from channel - if (!msg._id) return; - if (!msg.ms) return; + // Kickbanning some asshole from channel + if (typeof msg._id !== "string") return; + if (typeof msg.ms !== "number") return; socket.kickban(msg._id, msg.ms); } }; diff --git a/src/ws/events/user/index.ts b/src/ws/events/user/index.ts index 081e42f..9f257d0 100644 --- a/src/ws/events/user/index.ts +++ b/src/ws/events/user/index.ts @@ -15,6 +15,7 @@ import { admin_message } from "./handlers/admin_message"; import { chset } from "./handlers/chset"; import { kickban } from "./handlers/kickban"; import { bye } from "./handlers/bye"; +import { chown } from "./handlers/chown"; // Imagine not having an "addMany" function... @@ -44,7 +45,8 @@ EVENTGROUP_USER.addMany( admin_message, chset, kickban, - bye + bye, + chown ); eventGroups.push(EVENTGROUP_USER);