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

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

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

View File

@ -1,7 +1,13 @@
import { createColor, createID, createUserID } from "../util/id";
import { decoder, encoder } from "../util/helpers";
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 { createUser, readUser } from "../data/user";
import { eventGroups } from "./events";
@ -43,8 +49,12 @@ export class Socket extends EventEmitter {
};
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();
this.ip = ws.remoteAddress; // Participant ID
@ -56,13 +66,15 @@ export class Socket extends EventEmitter {
let foundSocket;
for (const socket of socketsBySocketID.values()) {
if (socket == this) continue;
if (socket.socketID == this.socketID) continue;
if (socket.getUserID() == this.getUserID()) {
foundSocket = socket;
}
}
// logger.debug("Found socket?", foundSocket);
if (!foundSocket) {
// Use new session ID
this.id = createID();
@ -72,7 +84,6 @@ export class Socket extends EventEmitter {
}
this.loadUser();
this.bindEventListeners();
}
@ -202,18 +213,23 @@ export class Socket extends EventEmitter {
public destroy() {
// 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
try {
this.ws.close();
} catch (err) {}
if (this.currentChannelID) {
const foundCh = channelList.find(
ch => ch.getID() == this.currentChannelID
);
if (foundCh) foundCh.leave(this);
} catch (err) {
logger.warn("Problem closing socket:", err);
}
this.destroyed = true;
@ -222,4 +238,52 @@ export class Socket extends EventEmitter {
public isDestroyed() {
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(),
name: "Anonymous",
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 { devices } from "./handlers/devices";
import { ch } from "./handlers/ch";
import { m } from "./handlers/m";
EVENTGROUP_USER.add(hi);
EVENTGROUP_USER.add(devices);
EVENTGROUP_USER.add(ch);
EVENTGROUP_USER.add(m);
eventGroups.push(EVENTGROUP_USER);

View File

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