move flag and rename to protocol layer
This means that 'turn' is now the only thing not sent by the protocol layer.
This commit is contained in:
@@ -18,7 +18,7 @@ import { ReaderModel } from '@maxmind/geoip2-node';
|
|||||||
import { Size, Rect } from './Utilities.js';
|
import { Size, Rect } from './Utilities.js';
|
||||||
import pino from 'pino';
|
import pino from 'pino';
|
||||||
import { BanManager } from './BanManager.js';
|
import { BanManager } from './BanManager.js';
|
||||||
import { IProtocolHandlers, ListEntry, ProtocolAddUser, TheProtocolManager } from './Protocol.js';
|
import { IProtocolHandlers, ListEntry, ProtocolAddUser, ProtocolFlag, ProtocolRenameStatus, ProtocolUpgradeCapability, TheProtocolManager } from './Protocol.js';
|
||||||
|
|
||||||
// Instead of strange hacks we can just use nodejs provided
|
// Instead of strange hacks we can just use nodejs provided
|
||||||
// import.meta properties, which have existed since LTS if not before
|
// import.meta properties, which have existed since LTS if not before
|
||||||
@@ -193,9 +193,11 @@ export default class CollabVMServer implements IProtocolHandlers {
|
|||||||
user.protocol.sendAuth(this.Config.auth.apiEndpoint);
|
user.protocol.sendAuth(this.Config.auth.apiEndpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert these to proto
|
user.protocol.sendAddUser(this.getAddUser());
|
||||||
user.protocol.sendAddUser(this.getAdduserMsg());
|
if (this.Config.geoip.enabled) {
|
||||||
if (this.Config.geoip.enabled) user.sendMsg(this.getFlagMsg());
|
let flags = this.getFlags();
|
||||||
|
user.protocol.sendFlag(flags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private connectionClosed(user: User) {
|
private connectionClosed(user: User) {
|
||||||
@@ -246,7 +248,7 @@ export default class CollabVMServer implements IProtocolHandlers {
|
|||||||
if (!this.Config.auth.enabled) return;
|
if (!this.Config.auth.enabled) return;
|
||||||
|
|
||||||
if (!user.connectedToNode) {
|
if (!user.connectedToNode) {
|
||||||
user.sendMsg(cvm.guacEncode('login', '0', 'You must connect to the VM before logging in.'));
|
user.protocol.sendLoginResponse(false, 'You must connect to the VM before logging in.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,7 +270,7 @@ export default class CollabVMServer implements IProtocolHandlers {
|
|||||||
if (user.countryCode !== null && user.noFlag) {
|
if (user.countryCode !== null && user.noFlag) {
|
||||||
// privacy
|
// privacy
|
||||||
for (let cl of this.clients.filter((c) => c !== user)) {
|
for (let cl of this.clients.filter((c) => c !== user)) {
|
||||||
cl.sendMsg(cvm.guacEncode('remuser', '1', user.username!));
|
cl.protocol.sendRemUser([user.username!]);
|
||||||
}
|
}
|
||||||
this.renameUser(user, res.username, false);
|
this.renameUser(user, res.username, false);
|
||||||
} else this.renameUser(user, res.username, true);
|
} else this.renameUser(user, res.username, true);
|
||||||
@@ -279,7 +281,14 @@ export default class CollabVMServer implements IProtocolHandlers {
|
|||||||
} else if (user.rank === Rank.Moderator) {
|
} else if (user.rank === Rank.Moderator) {
|
||||||
user.protocol.sendAdminLoginResponse(true, this.ModPerms);
|
user.protocol.sendAdminLoginResponse(true, this.ModPerms);
|
||||||
}
|
}
|
||||||
this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('adduser', '1', user.username!, user.rank.toString())));
|
this.clients.forEach((c) =>
|
||||||
|
c.protocol.sendAddUser([
|
||||||
|
{
|
||||||
|
username: user.username!,
|
||||||
|
rank: user.rank
|
||||||
|
}
|
||||||
|
])
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
user.protocol.sendLoginResponse(false, res.error!);
|
user.protocol.sendLoginResponse(false, res.error!);
|
||||||
if (res.error === 'You are banned') {
|
if (res.error === 'You are banned') {
|
||||||
@@ -302,10 +311,13 @@ export default class CollabVMServer implements IProtocolHandlers {
|
|||||||
onCapabilityUpgrade(user: User, capability: String[]): boolean {
|
onCapabilityUpgrade(user: User, capability: String[]): boolean {
|
||||||
if (user.connectedToNode) return false;
|
if (user.connectedToNode) return false;
|
||||||
|
|
||||||
|
let enabledCaps = [];
|
||||||
|
|
||||||
for (let cap of capability) {
|
for (let cap of capability) {
|
||||||
switch (cap) {
|
switch (cap) {
|
||||||
// binary 1.0 (msgpack rects)
|
// binary 1.0 (msgpack rects)
|
||||||
case 'bin':
|
case ProtocolUpgradeCapability.BinRects:
|
||||||
|
enabledCaps.push(cap as ProtocolUpgradeCapability);
|
||||||
user.Capabilities.bin = true;
|
user.Capabilities.bin = true;
|
||||||
user.protocol.dispose();
|
user.protocol.dispose();
|
||||||
user.protocol = TheProtocolManager.createProtocol('binary1', user);
|
user.protocol = TheProtocolManager.createProtocol('binary1', user);
|
||||||
@@ -315,6 +327,8 @@ export default class CollabVMServer implements IProtocolHandlers {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user.protocol.sendCapabilities(enabledCaps);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,47 +720,64 @@ export default class CollabVMServer implements IProtocolHandlers {
|
|||||||
|
|
||||||
renameUser(client: User, newName?: string, announce: boolean = true) {
|
renameUser(client: User, newName?: string, announce: boolean = true) {
|
||||||
// This shouldn't need a ternary but it does for some reason
|
// This shouldn't need a ternary but it does for some reason
|
||||||
var hadName: boolean = client.username ? true : false;
|
let hadName = client.username ? true : false;
|
||||||
var oldname: any;
|
let oldname: any;
|
||||||
if (hadName) oldname = client.username;
|
if (hadName) oldname = client.username;
|
||||||
var status = '0';
|
|
||||||
if (!newName) {
|
if (!newName) {
|
||||||
client.assignGuestName(this.getUsernameList());
|
client.assignGuestName(this.getUsernameList());
|
||||||
} else {
|
} else {
|
||||||
newName = newName.trim();
|
newName = newName.trim();
|
||||||
if (hadName && newName === oldname) {
|
if (hadName && newName === oldname) {
|
||||||
client.sendMsg(cvm.guacEncode('rename', '0', '0', client.username!, client.rank.toString()));
|
client.protocol.sendSelfRename(ProtocolRenameStatus.Ok, client.username!, client.rank);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let status = ProtocolRenameStatus.Ok;
|
||||||
|
|
||||||
if (this.getUsernameList().indexOf(newName) !== -1) {
|
if (this.getUsernameList().indexOf(newName) !== -1) {
|
||||||
client.assignGuestName(this.getUsernameList());
|
client.assignGuestName(this.getUsernameList());
|
||||||
if (client.connectedToNode) {
|
if (client.connectedToNode) {
|
||||||
status = '1';
|
status = ProtocolRenameStatus.UsernameTaken;
|
||||||
}
|
}
|
||||||
} else if (!/^[a-zA-Z0-9\ \-\_\.]+$/.test(newName) || newName.length > 20 || newName.length < 3) {
|
} else if (!/^[a-zA-Z0-9\ \-\_\.]+$/.test(newName) || newName.length > 20 || newName.length < 3) {
|
||||||
client.assignGuestName(this.getUsernameList());
|
client.assignGuestName(this.getUsernameList());
|
||||||
status = '2';
|
status = ProtocolRenameStatus.UsernameInvalid;
|
||||||
} else if (this.Config.collabvm.usernameblacklist.indexOf(newName) !== -1) {
|
} else if (this.Config.collabvm.usernameblacklist.indexOf(newName) !== -1) {
|
||||||
client.assignGuestName(this.getUsernameList());
|
client.assignGuestName(this.getUsernameList());
|
||||||
status = '3';
|
status = ProtocolRenameStatus.UsernameNotAllowed;
|
||||||
} else client.username = newName;
|
} else client.username = newName;
|
||||||
|
|
||||||
|
client.protocol.sendSelfRename(status, client.username!, client.rank);
|
||||||
}
|
}
|
||||||
|
|
||||||
client.sendMsg(cvm.guacEncode('rename', '0', status, client.username!, client.rank.toString()));
|
|
||||||
if (hadName) {
|
if (hadName) {
|
||||||
this.logger.info(`Rename ${client.IP.address} from ${oldname} to ${client.username}`);
|
this.logger.info(`Rename ${client.IP.address} from ${oldname} to ${client.username}`);
|
||||||
if (announce) this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('rename', '1', oldname, client.username!, client.rank.toString())));
|
if (announce) this.clients.forEach((c) => c.protocol.sendRename(oldname, client.username!, client.rank));
|
||||||
} else {
|
} else {
|
||||||
this.logger.info(`Rename ${client.IP.address} to ${client.username}`);
|
this.logger.info(`Rename ${client.IP.address} to ${client.username}`);
|
||||||
if (announce)
|
if (announce)
|
||||||
this.clients.forEach((c) => {
|
this.clients.forEach((c) => {
|
||||||
c.sendMsg(cvm.guacEncode('adduser', '1', client.username!, client.rank.toString()));
|
c.protocol.sendAddUser([
|
||||||
if (client.countryCode !== null) c.sendMsg(cvm.guacEncode('flag', client.username!, client.countryCode));
|
{
|
||||||
|
username: client.username!,
|
||||||
|
rank: client.rank
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (client.countryCode !== null) {
|
||||||
|
c.protocol.sendFlag([
|
||||||
|
{
|
||||||
|
username: client.username!,
|
||||||
|
countryCode: client.countryCode
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAdduserMsg(): ProtocolAddUser[] {
|
private getAddUser(): ProtocolAddUser[] {
|
||||||
return this.clients
|
return this.clients
|
||||||
.filter((c) => c.username)
|
.filter((c) => c.username)
|
||||||
.map((c) => {
|
.map((c) => {
|
||||||
@@ -757,12 +788,15 @@ export default class CollabVMServer implements IProtocolHandlers {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getFlagMsg(): string {
|
private getFlags(): ProtocolFlag[] {
|
||||||
var arr = ['flag'];
|
let arr = [];
|
||||||
for (let c of this.clients.filter((cl) => cl.countryCode !== null && cl.username && (!cl.noFlag || cl.rank === Rank.Unregistered))) {
|
for (let c of this.clients.filter((cl) => cl.countryCode !== null && cl.username && (!cl.noFlag || cl.rank === Rank.Unregistered))) {
|
||||||
arr.push(c.username!, c.countryCode!);
|
arr.push({
|
||||||
|
username: c.username!,
|
||||||
|
countryCode: c.countryCode!
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return cvm.guacEncode(...arr);
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendTurnUpdate(client?: User) {
|
private sendTurnUpdate(client?: User) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import pino from 'pino';
|
import pino from 'pino';
|
||||||
import { IProtocol, IProtocolHandlers, ListEntry, ProtocolAddUser, ProtocolBase, ProtocolChatHistory, ScreenRect } from './Protocol.js';
|
import { IProtocol, IProtocolHandlers, ListEntry, ProtocolAddUser, ProtocolBase, ProtocolChatHistory, ProtocolFlag, ProtocolRenameStatus, ProtocolUpgradeCapability, ScreenRect } from './Protocol.js';
|
||||||
import { User } from './User';
|
import { Rank, User } from './User';
|
||||||
|
|
||||||
import * as cvm from '@cvmts/cvm-rs';
|
import * as cvm from '@cvmts/cvm-rs';
|
||||||
|
|
||||||
@@ -209,6 +209,11 @@ export class GuacamoleProtocol extends ProtocolBase implements IProtocol {
|
|||||||
this.user?.sendMsg(cvm.guacEncode('sync', now.toString()));
|
this.user?.sendMsg(cvm.guacEncode('sync', now.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendCapabilities(caps: ProtocolUpgradeCapability[]): void {
|
||||||
|
let arr = ['cap', ...caps];
|
||||||
|
this?.user?.sendMsg(cvm.guacEncode(...arr));
|
||||||
|
}
|
||||||
|
|
||||||
sendConnectFailResponse(): void {
|
sendConnectFailResponse(): void {
|
||||||
this.user?.sendMsg(cvm.guacEncode('connect', '0'));
|
this.user?.sendMsg(cvm.guacEncode('connect', '0'));
|
||||||
}
|
}
|
||||||
@@ -253,8 +258,7 @@ export class GuacamoleProtocol extends ProtocolBase implements IProtocol {
|
|||||||
sendChatHistoryMessage(history: ProtocolChatHistory[]): void {
|
sendChatHistoryMessage(history: ProtocolChatHistory[]): void {
|
||||||
let arr = ['chat'];
|
let arr = ['chat'];
|
||||||
for (let a of history) {
|
for (let a of history) {
|
||||||
arr.push(a.user);
|
arr.push(a.user, a.msg);
|
||||||
arr.push(a.msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.user?.sendMsg(cvm.guacEncode(...arr));
|
this.user?.sendMsg(cvm.guacEncode(...arr));
|
||||||
@@ -280,6 +284,21 @@ export class GuacamoleProtocol extends ProtocolBase implements IProtocol {
|
|||||||
this.user?.sendMsg(cvm.guacEncode(...arr));
|
this.user?.sendMsg(cvm.guacEncode(...arr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendFlag(flag: ProtocolFlag[]): void {
|
||||||
|
// Basically this does the same as the above manual for of things
|
||||||
|
// but in one line of code
|
||||||
|
let arr = ['flag', ...flag.flatMap((flag) => [flag.username, flag.countryCode])];
|
||||||
|
this.user?.sendMsg(cvm.guacEncode(...arr));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendSelfRename(status: ProtocolRenameStatus, newUsername: string, rank: Rank): void {
|
||||||
|
this.user?.sendMsg(cvm.guacEncode('rename', '0', status.toString(), newUsername, rank.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRename(oldUsername: string, newUsername: string, rank: Rank): void {
|
||||||
|
this.user?.sendMsg(cvm.guacEncode('rename', '1', oldUsername, newUsername, rank.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
sendListResponse(list: ListEntry[]): void {
|
sendListResponse(list: ListEntry[]): void {
|
||||||
let arr = ['list'];
|
let arr = ['list'];
|
||||||
for (let node of list) {
|
for (let node of list) {
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
import { Rank, User } from './User';
|
import { Rank, User } from './User';
|
||||||
|
|
||||||
// We should probably put this in the binproto repository or something
|
// We should probably put this in the binproto repository or something
|
||||||
enum UpgradeCapability {
|
export enum ProtocolUpgradeCapability {
|
||||||
Binary = 'bin'
|
BinRects = 'bin'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProtocolRenameStatus {
|
||||||
|
Ok = 0,
|
||||||
|
UsernameTaken = 1,
|
||||||
|
UsernameInvalid = 2,
|
||||||
|
UsernameNotAllowed = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ScreenRect {
|
export interface ScreenRect {
|
||||||
@@ -27,6 +34,11 @@ export interface ProtocolAddUser {
|
|||||||
rank: Rank;
|
rank: Rank;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProtocolFlag {
|
||||||
|
username: string;
|
||||||
|
countryCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Protocol handlers. This is implemented by a layer that wants to listen to CollabVM protocol messages.
|
// Protocol handlers. This is implemented by a layer that wants to listen to CollabVM protocol messages.
|
||||||
export interface IProtocolHandlers {
|
export interface IProtocolHandlers {
|
||||||
onNop(user: User): void;
|
onNop(user: User): void;
|
||||||
@@ -74,7 +86,9 @@ export interface IProtocolHandlers {
|
|||||||
onMouse(user: User, x: number, y: number, buttonMask: number): void;
|
onMouse(user: User, x: number, y: number, buttonMask: number): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abstracts away all of the CollabVM protocol details
|
// Abstracts away all of the protocol details from the CollabVM server,
|
||||||
|
// allowing it to be protocol-independent (as long as the client and server
|
||||||
|
// are able to speak the same protocol.)
|
||||||
export interface IProtocol {
|
export interface IProtocol {
|
||||||
// don't implement this yourself, extend from ProtocolBase
|
// don't implement this yourself, extend from ProtocolBase
|
||||||
init(u: User): void;
|
init(u: User): void;
|
||||||
@@ -85,9 +99,11 @@ export interface IProtocol {
|
|||||||
|
|
||||||
// Protocol implementation stuff
|
// Protocol implementation stuff
|
||||||
|
|
||||||
// Parses a single CollabVM protocol message and fires the given handler.
|
// Parses a single message and fires the given handler with deserialized arguments.
|
||||||
// This function does not catch any thrown errors; it is the caller's responsibility
|
// This function does not catch any thrown errors; it is the caller's responsibility
|
||||||
// to handle errors. It should, however, catch invalid parameters without failing.
|
// to handle errors. It should, however, catch invalid parameters without failing.
|
||||||
|
//
|
||||||
|
// This function will perform conversion to text if it is required.
|
||||||
processMessage(buffer: Buffer): boolean;
|
processMessage(buffer: Buffer): boolean;
|
||||||
|
|
||||||
// Senders
|
// Senders
|
||||||
@@ -97,6 +113,8 @@ export interface IProtocol {
|
|||||||
|
|
||||||
sendAuth(authServer: string): void;
|
sendAuth(authServer: string): void;
|
||||||
|
|
||||||
|
sendCapabilities(caps: ProtocolUpgradeCapability[]): void;
|
||||||
|
|
||||||
sendConnectFailResponse(): void;
|
sendConnectFailResponse(): void;
|
||||||
sendConnectOKResponse(votes: boolean): void;
|
sendConnectOKResponse(votes: boolean): void;
|
||||||
|
|
||||||
@@ -111,7 +129,11 @@ export interface IProtocol {
|
|||||||
|
|
||||||
sendAddUser(users: ProtocolAddUser[]): void;
|
sendAddUser(users: ProtocolAddUser[]): void;
|
||||||
sendRemUser(users: string[]): void;
|
sendRemUser(users: string[]): void;
|
||||||
|
sendFlag(flag: ProtocolFlag[]): void;
|
||||||
|
|
||||||
|
sendSelfRename(status: ProtocolRenameStatus, newUsername: string, rank: Rank): void;
|
||||||
|
sendRename(oldUsername: string, newUsername: string, rank: Rank): void;
|
||||||
|
|
||||||
sendListResponse(list: ListEntry[]): void;
|
sendListResponse(list: ListEntry[]): void;
|
||||||
|
|
||||||
sendVoteStarted(): void;
|
sendVoteStarted(): void;
|
||||||
@@ -125,7 +147,7 @@ export interface IProtocol {
|
|||||||
sendScreenUpdate(rect: ScreenRect): void;
|
sendScreenUpdate(rect: ScreenRect): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// base mixin for all protocols to use
|
// Base mixin for all concrete protocols to use. Inherit from this!
|
||||||
export class ProtocolBase {
|
export class ProtocolBase {
|
||||||
protected handlers: IProtocolHandlers | null = null;
|
protected handlers: IProtocolHandlers | null = null;
|
||||||
protected user: User | null = null;
|
protected user: User | null = null;
|
||||||
@@ -144,7 +166,9 @@ export class ProtocolBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Holds protocol factories.
|
// The protocol manager. Holds protocol factories, and provides the ability
|
||||||
|
// to create a protocol by name. Avoids direct dependency on a given list of protocols,
|
||||||
|
// and allows (relatively simple) expansion.
|
||||||
export class ProtocolManager {
|
export class ProtocolManager {
|
||||||
private protocols = new Map<String, () => IProtocol>();
|
private protocols = new Map<String, () => IProtocol>();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user