Fix channel bug and add cursor messages

This commit is contained in:
Hri7566 2023-09-10 06:07:52 -04:00
parent 28d9127059
commit 16219b504a
7 changed files with 212 additions and 49 deletions

View File

@ -1,13 +1,15 @@
import EventEmitter from "events";
import { Logger } from "../util/Logger"; import { Logger } from "../util/Logger";
import { loadConfig } from "../util/config"; import { loadConfig } from "../util/config";
import { import {
ChannelSettingValue, ChannelSettingValue,
ChannelSettings, ChannelSettings,
ClientEvents,
Participant Participant
} from "../util/types"; } from "../util/types";
import { Socket } from "../ws/Socket"; import { Socket } from "../ws/Socket";
import { app, findSocketByPartID } from "../ws/server";
import { validateChannelSettings } from "./settings"; import { validateChannelSettings } from "./settings";
import { socketsBySocketID } from "../ws/server";
interface ChannelConfig { interface ChannelConfig {
forceLoad: string[]; forceLoad: string[];
@ -41,7 +43,7 @@ export const config = loadConfig<ChannelConfig>("config/channels.yml", {
export const channelList = new Array<Channel>(); export const channelList = new Array<Channel>();
export class Channel { export class Channel extends EventEmitter {
private settings: Partial<ChannelSettings> = config.defaultSettings; private settings: Partial<ChannelSettings> = config.defaultSettings;
private ppl = new Array<Participant>(); private ppl = new Array<Participant>();
@ -50,6 +52,8 @@ export class Channel {
// TODO Add the crown // TODO Add the crown
constructor(private _id: string, set?: Partial<ChannelSettings>) { constructor(private _id: string, set?: Partial<ChannelSettings>) {
super();
this.logger = new Logger("Channel - " + _id); this.logger = new Logger("Channel - " + _id);
// Validate settings in set // Validate settings in set
@ -66,9 +70,10 @@ export class Channel {
} }
if (this.isLobby()) { if (this.isLobby()) {
this.logger.debug(config.lobbySettings);
this.settings = config.lobbySettings; this.settings = config.lobbySettings;
} }
this.bindEventListeners();
} }
public getID() { public getID() {
@ -113,37 +118,40 @@ export class Channel {
} }
public join(socket: Socket) { public join(socket: Socket) {
const part = socket.getParticipant(); const part = socket.getParticipant() as Participant;
// Unknown side-effects, but for type safety... // Unknown side-effects, but for type safety...
if (!part) return; // if (!part) return;
let hasChangedChannel = false; let hasChangedChannel = false;
let oldChannelID = socket.currentChannelID;
this.logger.debug("Has user?", this.hasUser(part._id)); // this.logger.debug("Has user?", this.hasUser(part._id));
// Is user in this channel? // Is user in this channel?
if (this.hasUser(part._id)) { if (this.hasUser(part._id)) {
// Alreay in channel, add this part ID to IDs list // Alreay in channel, don't add to list, but tell them they're here
// TODO hasChangedChannel = true;
this.ppl.push(part);
} else { } else {
// Are we full? // Are we full?
if (!this.isFull()) { if (!this.isFull()) {
// Add to channel // Add to channel
this.ppl.push(part);
hasChangedChannel = true; hasChangedChannel = true;
this.ppl.push(part);
} else { } else {
// Put us in full channel // Put us in full channel
socket.setChannel(config.fullChannel); return socket.setChannel(config.fullChannel);
} }
} }
if (hasChangedChannel) { if (hasChangedChannel) {
// Is user in any channel that isn't this one? if (socket.currentChannelID) {
for (const ch of channelList) { const ch = channelList.find(
if (ch == this) continue; ch => ch._id == socket.currentChannelID
if (ch.hasUser(part._id)) { );
ch.leave(socket); if (ch) {
ch?.leave(socket);
} }
} }
@ -160,29 +168,62 @@ export class Channel {
} }
]); ]);
// TODO Broadcast channel update const cursorPos = socket.getCursorPos();
// Broadcast participant update
this.sendArray([
{
m: "p",
_id: part._id,
name: part.name,
color: part.color,
id: part.id,
x: cursorPos.x,
y: cursorPos.y
}
]);
} }
public leave(socket: Socket) { public leave(socket: Socket) {
this.logger.debug("Leave called"); // this.logger.debug("Leave called");
const part = socket.getParticipant(); const part = socket.getParticipant() as Participant;
// Unknown side-effects, but for type safety... let dupeCount = 0;
if (!part) return; for (const s of socketsBySocketID.values()) {
if (s.getParticipantID() == part.id) {
if (this.hasUser(part._id)) { if (s.currentChannelID == this.getID()) {
this.ppl.splice(this.ppl.indexOf(part), 1); dupeCount++;
}
}
} }
// TODO Broadcast channel update // this.logger.debug("Dupes:", dupeCount);
if (dupeCount == 1) {
const p = this.ppl.find(p => p.id == socket.getParticipantID());
if (p) {
this.ppl.splice(this.ppl.indexOf(p), 1);
}
}
// Broadcast bye
this.sendArray([
{
m: "bye",
p: part.id
}
]);
this.emit("update");
} }
public isFull() { public isFull() {
// TODO Use limit setting // TODO Use limit setting
if (this.isLobby() && this.ppl.length >= 20) { // if (this.isLobby() && this.ppl.length >= 20) {
return true; // return true;
} // }
return false; return false;
} }
@ -209,6 +250,44 @@ export class Channel {
const foundPart = this.ppl.find(p => p.id == id); const foundPart = this.ppl.find(p => p.id == id);
return !!foundPart; return !!foundPart;
} }
public sendArray<EventID extends keyof ClientEvents>(
arr: ClientEvents[EventID][]
) {
let i = 0;
for (const socket of socketsBySocketID.values()) {
for (const p of this.ppl) {
if (p.id == socket.getParticipantID()) {
// this.logger.debug(
// `Sending to ${socket.getParticipant()?.name} [${i}]:`,
// arr
// );
socket.sendArray(arr);
i++;
}
}
}
}
private alreadyBound = false;
private bindEventListeners() {
if (this.alreadyBound) return;
this.alreadyBound = true;
this.on("update", () => {
for (const socket of socketsBySocketID.values()) {
for (const p of this.ppl) {
if (socket.getParticipantID() == p.id) {
socket.sendChannelUpdate(
this.getInfo(),
this.getParticipantList()
);
}
}
}
});
}
} }
// Forceloader // Forceloader

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

@ -274,8 +274,8 @@ declare interface ClientEvents {
m: { m: {
m: "m"; m: "m";
x: number; x: string;
y: number; y: string;
id: string; id: string;
}; };
@ -306,8 +306,8 @@ declare interface ClientEvents {
p: { p: {
m: "p"; m: "p";
x: number; x: number | string;
y: number; y: number | string;
} & Participant; } & Participant;
t: { t: {
@ -315,6 +315,11 @@ declare interface ClientEvents {
t: number; t: number;
e: number; e: number;
}; };
bye: {
m: "bye";
p: string;
};
} }
declare type ServerEventListener<EventID extends keyof ServerEvents> = { declare type ServerEventListener<EventID extends keyof ServerEvents> = {

View File

@ -1,7 +1,13 @@
import { createColor, createID, createUserID } from "../util/id"; import { createColor, createID, createUserID } from "../util/id";
import { decoder, encoder } from "../util/helpers"; import { decoder, encoder } from "../util/helpers";
import EventEmitter from "events"; import EventEmitter from "events";
import { ChannelSettings, ClientEvents, UserFlags } from "../util/types"; import {
ChannelInfo,
ChannelSettings,
ClientEvents,
Participant,
UserFlags
} from "../util/types";
import { User } from "@prisma/client"; import { User } from "@prisma/client";
import { createUser, readUser } from "../data/user"; import { createUser, readUser } from "../data/user";
import { eventGroups } from "./events"; import { eventGroups } from "./events";
@ -43,8 +49,12 @@ export class Socket extends EventEmitter {
}; };
public currentChannelID: string | undefined; public currentChannelID: string | undefined;
private cursorPos = {
x: "-10.00",
y: "-10.00"
};
constructor(private ws: ServerWebSocket<unknown>) { constructor(private ws: ServerWebSocket<unknown>, public socketID: string) {
super(); super();
this.ip = ws.remoteAddress; // Participant ID this.ip = ws.remoteAddress; // Participant ID
@ -56,13 +66,15 @@ export class Socket extends EventEmitter {
let foundSocket; let foundSocket;
for (const socket of socketsBySocketID.values()) { for (const socket of socketsBySocketID.values()) {
if (socket == this) continue; if (socket.socketID == this.socketID) continue;
if (socket.getUserID() == this.getUserID()) { if (socket.getUserID() == this.getUserID()) {
foundSocket = socket; foundSocket = socket;
} }
} }
// logger.debug("Found socket?", foundSocket);
if (!foundSocket) { if (!foundSocket) {
// Use new session ID // Use new session ID
this.id = createID(); this.id = createID();
@ -72,7 +84,6 @@ export class Socket extends EventEmitter {
} }
this.loadUser(); this.loadUser();
this.bindEventListeners(); this.bindEventListeners();
} }
@ -202,18 +213,23 @@ export class Socket extends EventEmitter {
public destroy() { public destroy() {
// Socket was closed or should be closed, clear data // Socket was closed or should be closed, clear data
// logger.debug("Destroying UID:", this._id);
const foundCh = channelList.find(
ch => ch.getID() === this.currentChannelID
);
// logger.debug("(Destroying) Found channel:", foundCh);
if (foundCh) {
foundCh.leave(this);
}
// Simulate closure // Simulate closure
try { try {
this.ws.close(); this.ws.close();
} catch (err) {} } catch (err) {
logger.warn("Problem closing socket:", err);
if (this.currentChannelID) {
const foundCh = channelList.find(
ch => ch.getID() == this.currentChannelID
);
if (foundCh) foundCh.leave(this);
} }
this.destroyed = true; this.destroyed = true;
@ -222,4 +238,52 @@ export class Socket extends EventEmitter {
public isDestroyed() { public isDestroyed() {
return this.destroyed == true; return this.destroyed == true;
} }
public getCursorPos() {
return this.cursorPos;
}
public setCursorPos(x: number | string, y: number | string) {
if (typeof x == "number") {
x = x.toFixed(2);
}
if (typeof y == "number") {
y = y.toFixed(2);
}
this.cursorPos.x = x;
this.cursorPos.y = y;
// Send through channel
const ch = this.getCurrentChannel();
if (!ch) return;
const part = this.getParticipant();
if (!part) return;
ch.sendArray([
{
m: "m",
id: part.id,
x: this.cursorPos.x,
y: this.cursorPos.y
}
]);
}
public getCurrentChannel() {
return channelList.find(ch => ch.getID() == this.currentChannelID);
}
public sendChannelUpdate(ch: ChannelInfo, ppl: Participant[]) {
this.sendArray([
{
m: "ch",
ch,
p: this.getParticipantID(),
ppl
}
]);
}
} }

View File

@ -12,7 +12,7 @@ export const hi: ServerEventListener<"hi"> = {
_id: socket.getUserID(), _id: socket.getUserID(),
name: "Anonymous", name: "Anonymous",
color: "#777", color: "#777",
id: socket.getUserID() id: ""
}; };
} }

View File

@ -0,0 +1,10 @@
import { ServerEventListener } from "../../../../util/types";
import { eventGroups } from "../../../events";
export const m: ServerEventListener<"m"> = {
id: "m",
callback: (msg, socket) => {
if (!msg.x || !msg.y) return;
socket.setCursorPos(msg.x, msg.y);
}
};

View File

@ -5,9 +5,11 @@ export const EVENTGROUP_USER = new EventGroup("user");
import { hi } from "./handlers/hi"; import { hi } from "./handlers/hi";
import { devices } from "./handlers/devices"; import { devices } from "./handlers/devices";
import { ch } from "./handlers/ch"; import { ch } from "./handlers/ch";
import { m } from "./handlers/m";
EVENTGROUP_USER.add(hi); EVENTGROUP_USER.add(hi);
EVENTGROUP_USER.add(devices); EVENTGROUP_USER.add(devices);
EVENTGROUP_USER.add(ch); EVENTGROUP_USER.add(ch);
EVENTGROUP_USER.add(m);
eventGroups.push(EVENTGROUP_USER); eventGroups.push(EVENTGROUP_USER);

View File

@ -20,7 +20,7 @@ export function findSocketByPartID(id: string) {
export function findSocketByUserID(_id: string) { export function findSocketByUserID(_id: string) {
for (const socket of socketsBySocketID.values()) { for (const socket of socketsBySocketID.values()) {
logger.debug("User ID:", socket.getUserID()); // logger.debug("User ID:", socket.getUserID());
if (socket.getUserID() == _id) return socket; if (socket.getUserID() == _id) return socket;
} }
} }
@ -35,6 +35,7 @@ export function findSocketByIP(ip: string) {
export const app = Bun.serve({ export const app = Bun.serve({
port: env.PORT, port: env.PORT,
hostname: "0.0.0.0",
fetch: (req, server) => { fetch: (req, server) => {
if (server.upgrade(req)) { if (server.upgrade(req)) {
return; return;
@ -64,11 +65,11 @@ export const app = Bun.serve({
}, },
websocket: { websocket: {
open: ws => { open: ws => {
const socket = new Socket(ws); const socket = new Socket(ws, createSocketID());
(ws as unknown as any).socket = socket; (ws as unknown as any).socket = socket;
logger.debug("Connection at " + socket.getIP()); // logger.debug("Connection at " + socket.getIP());
socketsBySocketID.set(createSocketID(), socket); socketsBySocketID.set(socket.socketID, socket);
}, },
message: (ws, message) => { message: (ws, message) => {
@ -77,7 +78,7 @@ export const app = Bun.serve({
}, },
close: (ws, code, message) => { close: (ws, code, message) => {
logger.debug("Close called"); // logger.debug("Close called");
const socket = (ws as unknown as any).socket as Socket; const socket = (ws as unknown as any).socket as Socket;
socket.destroy(); socket.destroy();
@ -91,3 +92,5 @@ export const app = Bun.serve({
} }
} }
}); });
logger.info("Listening on port", env.PORT);