forked from Hri7566/mpp-server-dev2
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 { 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
|
||||||
|
|
|
@ -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> = {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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: ""
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 { 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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue