Fix channel bug and add cursor messages
This commit is contained in:
parent
28d9127059
commit
16219b504a
|
@ -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
|
||||
|
|
|
@ -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> = {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export const hi: ServerEventListener<"hi"> = {
|
|||
_id: socket.getUserID(),
|
||||
name: "Anonymous",
|
||||
color: "#777",
|
||||
id: socket.getUserID()
|
||||
id: ""
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue