diff --git a/.gitignore b/.gitignore index 869f36b..d5db0bf 100644 --- a/.gitignore +++ b/.gitignore @@ -10,9 +10,5 @@ cvmts/attic **/dist/ # Guac-rs -guac-rs/target -guac-rs/index.node - -# jpegturbo-rs -jpegturbo-rs/target -jpegturbo-rs/index.node +cvm-rs/target +cvm-rs/index.node diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..2333d22 --- /dev/null +++ b/Justfile @@ -0,0 +1,8 @@ +all: + yarn workspace @cvmts/cvm-rs run build + yarn workspace @cvmts/shared run build + yarn workspace @cvmts/qemu run build + yarn workspace @cvmts/cvmts run build + +pkg: + yarn diff --git a/README.md b/README.md index e723f55..d304e2b 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ This is a drop-in replacement for the dying CollabVM 1.2.11. Currently in beta **TODO**: These instructions are not finished for the refactor branch. 1. Copy config.example.toml to config.toml, and fill out fields -2. Install dependencies: `npm i` -3. Build it: `npm run build` -4. Run it: `npm run serve` +2. Install dependencies: `yarn` +3. Build it: `yarn build` +4. Run it: `yarn serve` ## FAQ ### When I try to access the admin panel, the server crashes! diff --git a/jpegturbo-rs/Cargo.lock b/cvm-rs/Cargo.lock similarity index 99% rename from jpegturbo-rs/Cargo.lock rename to cvm-rs/Cargo.lock index 60574c6..00d2143 100644 --- a/jpegturbo-rs/Cargo.lock +++ b/cvm-rs/Cargo.lock @@ -59,6 +59,16 @@ dependencies = [ "cc", ] +[[package]] +name = "cvm-rs" +version = "0.1.0" +dependencies = [ + "neon", + "once_cell", + "tokio", + "turbojpeg-sys", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -82,16 +92,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "jpegturbo-rs" -version = "0.1.0" -dependencies = [ - "neon", - "once_cell", - "tokio", - "turbojpeg-sys", -] - [[package]] name = "libc" version = "0.2.155" diff --git a/jpegturbo-rs/Cargo.toml b/cvm-rs/Cargo.toml similarity index 67% rename from jpegturbo-rs/Cargo.toml rename to cvm-rs/Cargo.toml index 5fc50ae..3b78185 100644 --- a/jpegturbo-rs/Cargo.toml +++ b/cvm-rs/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "jpegturbo-rs" -description = "Rust powered JPEGTurbo sex" +name = "cvm-rs" +description = "Rust utility library for cvmts. Runs all the high performance code" version = "0.1.0" edition = "2021" exclude = ["index.node"] @@ -10,6 +10,8 @@ crate-type = ["cdylib"] [dependencies] neon = "1" + +# Required for JPEG once_cell = "1.19.0" tokio = { version = "1.38.0", features = [ "rt", "rt-multi-thread" ] } turbojpeg-sys = "1.0.0" diff --git a/cvm-rs/index.d.ts b/cvm-rs/index.d.ts new file mode 100644 index 0000000..6032ed6 --- /dev/null +++ b/cvm-rs/index.d.ts @@ -0,0 +1,84 @@ +// + +// Guacamole Codec +export function guacDecode(input: string): string[]; +export function guacEncode(...items: string[]): string; + +interface JpegInputArgs { + width: number, + height: number, + stride: number, // The width of your input framebuffer OR your image width (if encoding a full image) + buffer: Buffer + + // TODO: Allow different formats, or export a boxed ffi object which can store a format + // (i.e: new JpegEncoder(FORMAT_xxx)). +} + +/// Performs JPEG encoding. +export function jpegEncode(input: JpegInputArgs) : Promise; + +// TODO: Version that can downscale? + + +/* remoting API? + +js side api: + + class RemotingClient extends EventEmitter { + constructor(uri: string) + + Connect(): Promise - connects to server. + + Disconnect(): void - disconnects from a server. + + get FullScreen(): Buffer - gets the full screen JPEG at a specific moment. This should only be called once + during some user-specific setup (for example: when a new user connects) + + get Thumbnail(): Buffer - gets JPEG thumbnail. + + KeyEvent(key: number, pressed: boolean) - sends a key event to the server. + MouseEvent(x: number, y: number, buttons: MouseButtonMask) - sends a mouse event (the button mask is semi-standardized for remoting, + the mask can be converted if not applicable for a given protocol) + + // explicit property setter APIs, maybe expose the semi-internal remotingSetProperty API if required? + set JpegQuality(q: number) - sets JPEG quality + + // events: + + on('open', cb: () => void) - on open + + //on('firstupdate', cb: (rect: RectWithJpeg) => void) - the first update of a resize is given here + // doesn't really matter + + on('resize', cb: (size: Size) => void) - when the server resizes we do too. + + on('update', cb: (rects: Array) => void) - gives screen frame update as jpeg rects + (pre-batched using existing batcher or a new invention or something) + on('close', cb: () => void) - on close + + on('cursor', cb: (b: CursorBitmap) => void) - cursor bitmap changed (always rgba8888) + + } + + binding side API: + + remotingNew("vnc://abc.def:1234") - creates a new remoting client which will use the given protocol in the URI + xxx for callbacks (they will get migrated to eventemitter or something on the JS side so it's more "idiomatic", depending on performance. + In all honesty however, remoting will take care of all the performance sensitive tasks, so it probably won't matter at all) + + remotingConnect(client) -> promise (throws rejection) - disconnects + remotingDisconnect(client) - disconnects + remotingGetBuffer(client) -> Buffer - gets the buffer used for the screen + + remotingSetProperty(client, propertyId, propertyValue) - sets property (e.g: jpeg quality) + e.g: server uri could be set after client creation + with remotingSetProperty(boxedClient, remoting.propertyServerUri, "vnc://another-server.org::2920") + + remotingGetThumbnail(client) - gets thumbnail, this is updated by remoting at about 5 fps + + remotingKeyEvent(client, key, pressed) - key event + remotingMouseEvent(client, x, y, buttons) - mouse event + + on the rust side a boxed client will contain an inner boxed `dyn RemotingProtocolClient` which will contain protocol specific dispatch, + upon parsing a remoting URI we will create a given client (e.g, for `vnc://` we'd make the VNC one) +*/ diff --git a/jpegturbo-rs/index.js b/cvm-rs/index.js similarity index 57% rename from jpegturbo-rs/index.js rename to cvm-rs/index.js index 5519fa2..fc02f40 100644 --- a/jpegturbo-rs/index.js +++ b/cvm-rs/index.js @@ -2,5 +2,5 @@ import { createRequire } from 'module'; const require = createRequire(import.meta.url); -export let {jpegEncode} = require('./index.node'); +export let {guacDecode, guacEncode, jpegEncode} = require('./index.node'); diff --git a/guac-rs/package.json b/cvm-rs/package.json similarity index 93% rename from guac-rs/package.json rename to cvm-rs/package.json index c939fc5..41d9dcb 100644 --- a/guac-rs/package.json +++ b/cvm-rs/package.json @@ -1,5 +1,5 @@ { - "name": "@cvmts/guac-rs", + "name": "@cvmts/cvm-rs", "version": "0.1.0", "packageManager": "yarn@4.1.1", "type": "module", diff --git a/guac-rs/src/guac.rs b/cvm-rs/src/guac.rs similarity index 100% rename from guac-rs/src/guac.rs rename to cvm-rs/src/guac.rs diff --git a/guac-rs/src/lib.rs b/cvm-rs/src/guac_js.rs similarity index 77% rename from guac-rs/src/lib.rs rename to cvm-rs/src/guac_js.rs index 2d7f623..9077333 100644 --- a/guac-rs/src/lib.rs +++ b/cvm-rs/src/guac_js.rs @@ -1,6 +1,5 @@ -mod guac; - use neon::prelude::*; +use crate::guac; fn guac_decode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsArray> { let input = cx.argument::(0)?.value(cx); @@ -40,17 +39,10 @@ fn guac_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsString> Ok(cx.string(guac::encode_instruction(&elements))) } -fn guac_decode(mut cx: FunctionContext) -> JsResult { +pub fn guac_decode(mut cx: FunctionContext) -> JsResult { guac_decode_impl(&mut cx) } -fn guac_encode(mut cx: FunctionContext) -> JsResult { +pub fn guac_encode(mut cx: FunctionContext) -> JsResult { guac_encode_impl(&mut cx) } - -#[neon::main] -fn main(mut cx: ModuleContext) -> NeonResult<()> { - cx.export_function("guacDecode", guac_decode)?; - cx.export_function("guacEncode", guac_encode)?; - Ok(()) -} diff --git a/jpegturbo-rs/src/jpeg_compressor.rs b/cvm-rs/src/jpeg_compressor.rs similarity index 100% rename from jpegturbo-rs/src/jpeg_compressor.rs rename to cvm-rs/src/jpeg_compressor.rs diff --git a/jpegturbo-rs/src/lib.rs b/cvm-rs/src/jpeg_js.rs similarity index 85% rename from jpegturbo-rs/src/lib.rs rename to cvm-rs/src/jpeg_js.rs index dc3f498..de87f9b 100644 --- a/jpegturbo-rs/src/lib.rs +++ b/cvm-rs/src/jpeg_js.rs @@ -8,8 +8,10 @@ use tokio::runtime::Runtime; use std::cell::RefCell; -mod jpeg_compressor; +use crate::jpeg_compressor::*; +/// Gives a static Tokio runtime. We should replace this with +/// rayon or something, but for now tokio works. fn runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static Runtime> { static RUNTIME: OnceCell = OnceCell::new(); @@ -19,7 +21,7 @@ fn runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static Runtime> { } thread_local! { - static COMPRESSOR: RefCell = RefCell::new(jpeg_compressor::JpegCompressor::new()); + static COMPRESSOR: RefCell = RefCell::new(JpegCompressor::new()); } fn jpeg_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsPromise> { @@ -52,7 +54,7 @@ fn jpeg_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsPromise> let clone = Arc::clone(©); let locked = clone.lock().unwrap(); - let image: jpeg_compressor::Image = jpeg_compressor::Image { + let image: Image = Image { buffer: locked.as_slice(), width: width as u32, height: height as u32, @@ -80,12 +82,6 @@ fn jpeg_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsPromise> Ok(promise) } -fn jpeg_encode(mut cx: FunctionContext) -> JsResult { +pub fn jpeg_encode(mut cx: FunctionContext) -> JsResult { jpeg_encode_impl(&mut cx) } - -#[neon::main] -fn main(mut cx: ModuleContext) -> NeonResult<()> { - cx.export_function("jpegEncode", jpeg_encode)?; - Ok(()) -} diff --git a/cvm-rs/src/lib.rs b/cvm-rs/src/lib.rs new file mode 100644 index 0000000..e6c6291 --- /dev/null +++ b/cvm-rs/src/lib.rs @@ -0,0 +1,19 @@ +mod guac; +mod guac_js; + +mod jpeg_compressor; +mod jpeg_js; + + +use neon::prelude::*; + + +#[neon::main] +fn main(mut cx: ModuleContext) -> NeonResult<()> { + // Mostly transitionary, later on API should change + cx.export_function("jpegEncode", jpeg_js::jpeg_encode)?; + + cx.export_function("guacDecode", guac_js::guac_decode)?; + cx.export_function("guacEncode", guac_js::guac_encode)?; + Ok(()) +} diff --git a/cvmts/package.json b/cvmts/package.json index 94754d5..496ede2 100644 --- a/cvmts/package.json +++ b/cvmts/package.json @@ -11,8 +11,7 @@ "author": "Elijah R, modeco80", "license": "GPL-3.0", "dependencies": { - "@cvmts/guac-rs": "*", - "@cvmts/jpegturbo-rs": "*", + "@cvmts/cvm-rs": "*", "@cvmts/qemu": "*", "execa": "^8.0.1", "mnemonist": "^0.39.5", diff --git a/cvmts/src/CollabVMServer.ts b/cvmts/src/CollabVMServer.ts index 5cb6f9b..e986557 100644 --- a/cvmts/src/CollabVMServer.ts +++ b/cvmts/src/CollabVMServer.ts @@ -1,7 +1,7 @@ import IConfig from './IConfig.js'; import * as Utilities from './Utilities.js'; import { User, Rank } from './User.js'; -import * as guac from '@cvmts/guac-rs'; +import * as cvm from '@cvmts/cvm-rs'; // I hate that you have to do it like this import CircularBuffer from 'mnemonist/circular-buffer.js'; import Queue from 'mnemonist/queue.js'; @@ -142,7 +142,7 @@ export default class CollabVMServer { user.socket.on('msg', (msg: string) => this.onMessage(user, msg)); user.socket.on('disconnect', () => this.connectionClosed(user)); if (this.Config.auth.enabled) { - user.sendMsg(guac.guacEncode('auth', this.Config.auth.apiEndpoint)); + user.sendMsg(cvm.guacEncode('auth', this.Config.auth.apiEndpoint)); } user.sendMsg(this.getAdduserMsg()); } @@ -171,25 +171,25 @@ export default class CollabVMServer { if (hadturn) this.nextTurn(); } - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('remuser', '1', user.username!))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('remuser', '1', user.username!))); } private async onMessage(client: User, message: string) { try { - var msgArr = guac.guacDecode(message); + var msgArr = cvm.guacDecode(message); if (msgArr.length < 1) return; switch (msgArr[0]) { case 'login': if (msgArr.length !== 2 || !this.Config.auth.enabled) return; if (!client.connectedToNode) { - client.sendMsg(guac.guacEncode('login', '0', 'You must connect to the VM before logging in.')); + client.sendMsg(cvm.guacEncode('login', '0', 'You must connect to the VM before logging in.')); return; } try { let res = await this.auth!.Authenticate(msgArr[1], client); if (res.clientSuccess) { this.logger.Info(`${client.IP.address} logged in as ${res.username}`); - client.sendMsg(guac.guacEncode('login', '1')); + client.sendMsg(cvm.guacEncode('login', '1')); let old = this.clients.find((c) => c.username === res.username); if (old) { // kick() doesnt wait until the user is actually removed from the list and itd be anal to make it do that @@ -202,13 +202,13 @@ export default class CollabVMServer { // Set rank client.rank = res.rank; if (client.rank === Rank.Admin) { - client.sendMsg(guac.guacEncode('admin', '0', '1')); + client.sendMsg(cvm.guacEncode('admin', '0', '1')); } else if (client.rank === Rank.Moderator) { - client.sendMsg(guac.guacEncode('admin', '0', '3', this.ModPerms.toString())); + client.sendMsg(cvm.guacEncode('admin', '0', '3', this.ModPerms.toString())); } - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('adduser', '1', client.username!, client.rank.toString()))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('adduser', '1', client.username!, client.rank.toString()))); } else { - client.sendMsg(guac.guacEncode('login', '0', res.error!)); + client.sendMsg(cvm.guacEncode('login', '0', res.error!)); if (res.error === 'You are banned') { client.kick(); } @@ -216,28 +216,28 @@ export default class CollabVMServer { } catch (err) { this.logger.Error(`Error authenticating client ${client.IP.address}: ${(err as Error).message}`); // for now? - client.sendMsg(guac.guacEncode('login', '0', 'There was an internal error while authenticating. Please let a staff member know as soon as possible')); + client.sendMsg(cvm.guacEncode('login', '0', 'There was an internal error while authenticating. Please let a staff member know as soon as possible')); } break; case 'list': - client.sendMsg(guac.guacEncode('list', this.Config.collabvm.node, this.Config.collabvm.displayname, this.screenHidden ? this.screenHiddenThumb : await this.getThumbnail())); + client.sendMsg(cvm.guacEncode('list', this.Config.collabvm.node, this.Config.collabvm.displayname, this.screenHidden ? this.screenHiddenThumb : await this.getThumbnail())); break; case 'connect': if (!client.username || msgArr.length !== 2 || msgArr[1] !== this.Config.collabvm.node) { - client.sendMsg(guac.guacEncode('connect', '0')); + client.sendMsg(cvm.guacEncode('connect', '0')); return; } client.connectedToNode = true; - client.sendMsg(guac.guacEncode('connect', '1', '1', this.VM.SnapshotsSupported() ? '1' : '0', '0')); + client.sendMsg(cvm.guacEncode('connect', '1', '1', this.VM.SnapshotsSupported() ? '1' : '0', '0')); if (this.ChatHistory.size !== 0) client.sendMsg(this.getChatHistoryMsg()); - if (this.Config.collabvm.motd) client.sendMsg(guac.guacEncode('chat', '', this.Config.collabvm.motd)); + if (this.Config.collabvm.motd) client.sendMsg(cvm.guacEncode('chat', '', this.Config.collabvm.motd)); if (this.screenHidden) { - client.sendMsg(guac.guacEncode('size', '0', '1024', '768')); - client.sendMsg(guac.guacEncode('png', '0', '0', '0', '0', this.screenHiddenImg)); + client.sendMsg(cvm.guacEncode('size', '0', '1024', '768')); + client.sendMsg(cvm.guacEncode('png', '0', '0', '0', '0', this.screenHiddenImg)); } else { await this.SendFullScreenWithSize(client); } - client.sendMsg(guac.guacEncode('sync', Date.now().toString())); + client.sendMsg(cvm.guacEncode('sync', Date.now().toString())); if (this.voteInProgress) this.sendVoteUpdate(client); this.sendTurnUpdate(client); break; @@ -245,7 +245,7 @@ export default class CollabVMServer { if (client.connectedToNode) return; if (client.username || msgArr.length !== 3 || msgArr[1] !== this.Config.collabvm.node) { // The use of connect here is intentional. - client.sendMsg(guac.guacEncode('connect', '0')); + client.sendMsg(cvm.guacEncode('connect', '0')); return; } @@ -257,22 +257,22 @@ export default class CollabVMServer { client.viewMode = 1; break; default: - client.sendMsg(guac.guacEncode('connect', '0')); + client.sendMsg(cvm.guacEncode('connect', '0')); return; } - client.sendMsg(guac.guacEncode('connect', '1', '1', this.VM.SnapshotsSupported() ? '1' : '0', '0')); + client.sendMsg(cvm.guacEncode('connect', '1', '1', this.VM.SnapshotsSupported() ? '1' : '0', '0')); if (this.ChatHistory.size !== 0) client.sendMsg(this.getChatHistoryMsg()); - if (this.Config.collabvm.motd) client.sendMsg(guac.guacEncode('chat', '', this.Config.collabvm.motd)); + if (this.Config.collabvm.motd) client.sendMsg(cvm.guacEncode('chat', '', this.Config.collabvm.motd)); if (client.viewMode == 1) { if (this.screenHidden) { - client.sendMsg(guac.guacEncode('size', '0', '1024', '768')); - client.sendMsg(guac.guacEncode('png', '0', '0', '0', '0', this.screenHiddenImg)); + client.sendMsg(cvm.guacEncode('size', '0', '1024', '768')); + client.sendMsg(cvm.guacEncode('png', '0', '0', '0', '0', this.screenHiddenImg)); } else { await this.SendFullScreenWithSize(client); } - client.sendMsg(guac.guacEncode('sync', Date.now().toString())); + client.sendMsg(cvm.guacEncode('sync', Date.now().toString())); } if (this.voteInProgress) this.sendVoteUpdate(client); @@ -282,12 +282,12 @@ export default class CollabVMServer { if (!client.RenameRateLimit.request()) return; if (client.connectedToNode && client.IP.muted) return; if (this.Config.auth.enabled && client.rank !== Rank.Unregistered) { - client.sendMsg(guac.guacEncode('chat', '', 'Go to your account settings to change your username.')); + client.sendMsg(cvm.guacEncode('chat', '', 'Go to your account settings to change your username.')); return; } if (this.Config.auth.enabled && msgArr[1] !== undefined) { // Don't send system message to a user without a username since it was likely an automated attempt by the webapp - if (client.username) client.sendMsg(guac.guacEncode('chat', '', 'You need to log in to do that.')); + if (client.username) client.sendMsg(cvm.guacEncode('chat', '', 'You need to log in to do that.')); if (client.rank !== Rank.Unregistered) return; this.renameUser(client, undefined); return; @@ -299,7 +299,7 @@ export default class CollabVMServer { if (client.IP.muted) return; if (msgArr.length !== 2) return; if (this.Config.auth.enabled && client.rank === Rank.Unregistered && !this.Config.auth.guestPermissions.chat) { - client.sendMsg(guac.guacEncode('chat', '', 'You need to login to do that.')); + client.sendMsg(cvm.guacEncode('chat', '', 'You need to login to do that.')); return; } var msg = Utilities.HTMLSanitize(msgArr[1]); @@ -307,14 +307,14 @@ export default class CollabVMServer { if (msg.length > this.Config.collabvm.maxChatLength) msg = msg.substring(0, this.Config.collabvm.maxChatLength); if (msg.trim().length < 1) return; - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('chat', client.username!, msg))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('chat', client.username!, msg))); this.ChatHistory.push({ user: client.username, msg: msg }); client.onMsgSent(); break; case 'turn': if ((!this.turnsAllowed || this.Config.collabvm.turnwhitelist) && client.rank !== Rank.Admin && client.rank !== Rank.Moderator && client.rank !== Rank.Turn) return; if (this.Config.auth.enabled && client.rank === Rank.Unregistered && !this.Config.auth.guestPermissions.turn) { - client.sendMsg(guac.guacEncode('chat', '', 'You need to login to do that.')); + client.sendMsg(cvm.guacEncode('chat', '', 'You need to login to do that.')); return; } if (!client.TurnRateLimit.request()) return; @@ -384,33 +384,33 @@ export default class CollabVMServer { case '1': if (!this.voteInProgress) { if (this.Config.auth.enabled && client.rank === Rank.Unregistered && !this.Config.auth.guestPermissions.callForReset) { - client.sendMsg(guac.guacEncode('chat', '', 'You need to login to do that.')); + client.sendMsg(cvm.guacEncode('chat', '', 'You need to login to do that.')); return; } if (this.voteCooldown !== 0) { - client.sendMsg(guac.guacEncode('vote', '3', this.voteCooldown.toString())); + client.sendMsg(cvm.guacEncode('vote', '3', this.voteCooldown.toString())); return; } this.startVote(); - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('chat', '', `${client.username} has started a vote to reset the VM.`))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('chat', '', `${client.username} has started a vote to reset the VM.`))); } if (this.Config.auth.enabled && client.rank === Rank.Unregistered && !this.Config.auth.guestPermissions.vote) { - client.sendMsg(guac.guacEncode('chat', '', 'You need to login to do that.')); + client.sendMsg(cvm.guacEncode('chat', '', 'You need to login to do that.')); return; } else if (client.IP.vote !== true) { - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('chat', '', `${client.username} has voted yes.`))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('chat', '', `${client.username} has voted yes.`))); } client.IP.vote = true; break; case '0': if (!this.voteInProgress) return; if (this.Config.auth.enabled && client.rank === Rank.Unregistered && !this.Config.auth.guestPermissions.vote) { - client.sendMsg(guac.guacEncode('chat', '', 'You need to login to do that.')); + client.sendMsg(cvm.guacEncode('chat', '', 'You need to login to do that.')); return; } if (client.IP.vote !== false) { - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('chat', '', `${client.username} has voted no.`))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('chat', '', `${client.username} has voted no.`))); } client.IP.vote = false; break; @@ -423,7 +423,7 @@ export default class CollabVMServer { case '2': // Login if (this.Config.auth.enabled) { - client.sendMsg(guac.guacEncode('chat', '', 'This server does not support staff passwords. Please log in to become staff.')); + client.sendMsg(cvm.guacEncode('chat', '', 'This server does not support staff passwords. Please log in to become staff.')); return; } if (!client.LoginRateLimit.request() || !client.username) return; @@ -434,37 +434,37 @@ export default class CollabVMServer { sha256.destroy(); if (pwdHash === this.Config.collabvm.adminpass) { client.rank = Rank.Admin; - client.sendMsg(guac.guacEncode('admin', '0', '1')); + client.sendMsg(cvm.guacEncode('admin', '0', '1')); } else if (this.Config.collabvm.moderatorEnabled && pwdHash === this.Config.collabvm.modpass) { client.rank = Rank.Moderator; - client.sendMsg(guac.guacEncode('admin', '0', '3', this.ModPerms.toString())); + client.sendMsg(cvm.guacEncode('admin', '0', '3', this.ModPerms.toString())); } else if (this.Config.collabvm.turnwhitelist && pwdHash === this.Config.collabvm.turnpass) { client.rank = Rank.Turn; - client.sendMsg(guac.guacEncode('chat', '', 'You may now take turns.')); + client.sendMsg(cvm.guacEncode('chat', '', 'You may now take turns.')); } else { - client.sendMsg(guac.guacEncode('admin', '0', '0')); + client.sendMsg(cvm.guacEncode('admin', '0', '0')); return; } if (this.screenHidden) { await this.SendFullScreenWithSize(client); - client.sendMsg(guac.guacEncode('sync', Date.now().toString())); + client.sendMsg(cvm.guacEncode('sync', Date.now().toString())); } - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('adduser', '1', client.username!, client.rank.toString()))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('adduser', '1', client.username!, client.rank.toString()))); break; case '5': // QEMU Monitor if (client.rank !== Rank.Admin) return; /* Surely there could be rudimentary processing to convert some qemu monitor syntax to [XYZ hypervisor] if possible if (!(this.VM instanceof QEMUVM)) { - client.sendMsg(guac.guacEncode("admin", "2", "This is not a QEMU VM and therefore QEMU monitor commands cannot be run.")); + client.sendMsg(cvm.guacEncode("admin", "2", "This is not a QEMU VM and therefore QEMU monitor commands cannot be run.")); return; } */ if (msgArr.length !== 4 || msgArr[2] !== this.Config.collabvm.node) return; var output = await this.VM.MonitorCommand(msgArr[3]); - client.sendMsg(guac.guacEncode('admin', '2', String(output))); + client.sendMsg(cvm.guacEncode('admin', '2', String(output))); break; case '8': // Restore @@ -541,7 +541,7 @@ export default class CollabVMServer { // Rename user if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.rename)) return; if (this.Config.auth.enabled) { - client.sendMsg(guac.guacEncode('chat', '', 'Cannot rename users on a server that uses authentication.')); + client.sendMsg(cvm.guacEncode('chat', '', 'Cannot rename users on a server that uses authentication.')); } if (msgArr.length !== 4) return; var user = this.clients.find((c) => c.username === msgArr[2]); @@ -554,7 +554,7 @@ export default class CollabVMServer { if (msgArr.length !== 3) return; var user = this.clients.find((c) => c.username === msgArr[2]); if (!user) return; - client.sendMsg(guac.guacEncode('admin', '19', msgArr[2], user.IP.address)); + client.sendMsg(cvm.guacEncode('admin', '19', msgArr[2], user.IP.address)); break; case '20': // Steal turn @@ -567,14 +567,14 @@ export default class CollabVMServer { if (msgArr.length !== 3) return; switch (client.rank) { case Rank.Admin: - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('chat', client.username!, msgArr[2]))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('chat', client.username!, msgArr[2]))); this.ChatHistory.push({ user: client.username!, msg: msgArr[2] }); break; case Rank.Moderator: - this.clients.filter((c) => c.rank !== Rank.Admin).forEach((c) => c.sendMsg(guac.guacEncode('chat', client.username!, msgArr[2]))); + this.clients.filter((c) => c.rank !== Rank.Admin).forEach((c) => c.sendMsg(cvm.guacEncode('chat', client.username!, msgArr[2]))); - this.clients.filter((c) => c.rank === Rank.Admin).forEach((c) => c.sendMsg(guac.guacEncode('chat', client.username!, Utilities.HTMLSanitize(msgArr[2])))); + this.clients.filter((c) => c.rank === Rank.Admin).forEach((c) => c.sendMsg(cvm.guacEncode('chat', client.username!, Utilities.HTMLSanitize(msgArr[2])))); break; } break; @@ -609,9 +609,9 @@ export default class CollabVMServer { this.clients .filter((c) => c.rank == Rank.Unregistered) .forEach((client) => { - client.sendMsg(guac.guacEncode('size', '0', '1024', '768')); - client.sendMsg(guac.guacEncode('png', '0', '0', '0', '0', this.screenHiddenImg)); - client.sendMsg(guac.guacEncode('sync', Date.now().toString())); + client.sendMsg(cvm.guacEncode('size', '0', '1024', '768')); + client.sendMsg(cvm.guacEncode('png', '0', '0', '0', '0', this.screenHiddenImg)); + client.sendMsg(cvm.guacEncode('sync', Date.now().toString())); }); break; case '1': @@ -626,16 +626,16 @@ export default class CollabVMServer { }); this.clients.forEach(async (client) => { - client.sendMsg(guac.guacEncode('size', '0', displaySize.width.toString(), displaySize.height.toString())); - client.sendMsg(guac.guacEncode('png', '0', '0', '0', '0', encoded)); - client.sendMsg(guac.guacEncode('sync', Date.now().toString())); + client.sendMsg(cvm.guacEncode('size', '0', displaySize.width.toString(), displaySize.height.toString())); + client.sendMsg(cvm.guacEncode('png', '0', '0', '0', '0', encoded)); + client.sendMsg(cvm.guacEncode('sync', Date.now().toString())); }); break; } break; case '25': if (client.rank !== Rank.Admin || msgArr.length !== 3) return; - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('chat', '', msgArr[2]))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('chat', '', msgArr[2]))); break; } break; @@ -665,7 +665,7 @@ export default class CollabVMServer { } else { newName = newName.trim(); if (hadName && newName === oldname) { - client.sendMsg(guac.guacEncode('rename', '0', '0', client.username!, client.rank.toString())); + client.sendMsg(cvm.guacEncode('rename', '0', '0', client.username!, client.rank.toString())); return; } if (this.getUsernameList().indexOf(newName) !== -1) { @@ -682,13 +682,13 @@ export default class CollabVMServer { } else client.username = newName; } - client.sendMsg(guac.guacEncode('rename', '0', status, client.username!, client.rank.toString())); + client.sendMsg(cvm.guacEncode('rename', '0', status, client.username!, client.rank.toString())); if (hadName) { this.logger.Info(`Rename ${client.IP.address} from ${oldname} to ${client.username}`); - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('rename', '1', oldname, client.username!, client.rank.toString()))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('rename', '1', oldname, client.username!, client.rank.toString()))); } else { this.logger.Info(`Rename ${client.IP.address} to ${client.username}`); - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('adduser', '1', client.username!, client.rank.toString()))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('adduser', '1', client.username!, client.rank.toString()))); } } @@ -696,13 +696,13 @@ export default class CollabVMServer { var arr: string[] = ['adduser', this.clients.filter((c) => c.username).length.toString()]; this.clients.filter((c) => c.username).forEach((c) => arr.push(c.username!, c.rank.toString())); - return guac.guacEncode(...arr); + return cvm.guacEncode(...arr); } getChatHistoryMsg(): string { var arr: string[] = ['chat']; this.ChatHistory.forEach((c) => arr.push(c.user, c.msg)); - return guac.guacEncode(...arr); + return cvm.guacEncode(...arr); } private sendTurnUpdate(client?: User) { @@ -715,7 +715,7 @@ export default class CollabVMServer { this.TurnQueue.forEach((c) => arr.push(c.username)); var currentTurningUser = this.TurnQueue.peek(); if (client) { - client.sendMsg(guac.guacEncode(...arr)); + client.sendMsg(cvm.guacEncode(...arr)); return; } this.clients @@ -725,12 +725,12 @@ export default class CollabVMServer { var time; if (this.indefiniteTurn === null) time = this.TurnTime * 1000 + (turnQueueArr.indexOf(c) - 1) * this.Config.collabvm.turnTime * 1000; else time = 9999999999; - c.sendMsg(guac.guacEncode(...arr, time.toString())); + c.sendMsg(cvm.guacEncode(...arr, time.toString())); } else { - c.sendMsg(guac.guacEncode(...arr)); + c.sendMsg(cvm.guacEncode(...arr)); } }); - if (currentTurningUser) currentTurningUser.sendMsg(guac.guacEncode(...arr)); + if (currentTurningUser) currentTurningUser.sendMsg(cvm.guacEncode(...arr)); } private nextTurn() { clearInterval(this.TurnInterval); @@ -777,8 +777,8 @@ export default class CollabVMServer { .filter((c) => c.connectedToNode || c.viewMode == 1) .forEach((c) => { if (this.screenHidden && c.rank == Rank.Unregistered) return; - c.sendMsg(guac.guacEncode('png', '0', '0', rect.x.toString(), rect.y.toString(), encodedb64)); - c.sendMsg(guac.guacEncode('sync', Date.now().toString())); + c.sendMsg(cvm.guacEncode('png', '0', '0', rect.x.toString(), rect.y.toString(), encodedb64)); + c.sendMsg(cvm.guacEncode('sync', Date.now().toString())); }); } @@ -787,7 +787,7 @@ export default class CollabVMServer { .filter((c) => c.connectedToNode || c.viewMode == 1) .forEach((c) => { if (this.screenHidden && c.rank == Rank.Unregistered) return; - c.sendMsg(guac.guacEncode('size', '0', size.width.toString(), size.height.toString())); + c.sendMsg(cvm.guacEncode('size', '0', size.width.toString(), size.height.toString())); }); } @@ -802,8 +802,8 @@ export default class CollabVMServer { height: displaySize.height }); - client.sendMsg(guac.guacEncode('size', '0', displaySize.width.toString(), displaySize.height.toString())); - client.sendMsg(guac.guacEncode('png', '0', '0', '0', '0', encoded)); + client.sendMsg(cvm.guacEncode('size', '0', displaySize.width.toString(), displaySize.height.toString())); + client.sendMsg(cvm.guacEncode('png', '0', '0', '0', '0', encoded)); } private async MakeRectData(rect: Rect) { @@ -828,7 +828,7 @@ export default class CollabVMServer { startVote() { if (this.voteInProgress) return; this.voteInProgress = true; - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('vote', '0'))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('vote', '0'))); this.voteTime = this.Config.collabvm.voteTime; this.voteInterval = setInterval(() => { this.voteTime--; @@ -843,12 +843,12 @@ export default class CollabVMServer { this.voteInProgress = false; clearInterval(this.voteInterval); var count = this.getVoteCounts(); - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('vote', '2'))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('vote', '2'))); if (result === true || (result === undefined && count.yes >= count.no)) { - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('chat', '', 'The vote to reset the VM has won.'))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('chat', '', 'The vote to reset the VM has won.'))); this.VM.Reset(); } else { - this.clients.forEach((c) => c.sendMsg(guac.guacEncode('chat', '', 'The vote to reset the VM has lost.'))); + this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('chat', '', 'The vote to reset the VM has lost.'))); } this.clients.forEach((c) => { c.IP.vote = null; @@ -863,7 +863,7 @@ export default class CollabVMServer { sendVoteUpdate(client?: User) { if (!this.voteInProgress) return; var count = this.getVoteCounts(); - var msg = guac.guacEncode('vote', '1', (this.voteTime * 1000).toString(), count.yes.toString(), count.no.toString()); + var msg = cvm.guacEncode('vote', '1', (this.voteTime * 1000).toString(), count.yes.toString(), count.no.toString()); if (client) client.sendMsg(msg); else this.clients.forEach((c) => c.sendMsg(msg)); } diff --git a/cvmts/src/JPEGEncoder.ts b/cvmts/src/JPEGEncoder.ts index 7e73243..a9a6f7a 100644 --- a/cvmts/src/JPEGEncoder.ts +++ b/cvmts/src/JPEGEncoder.ts @@ -1,6 +1,6 @@ import { Size, Rect } from '@cvmts/shared'; import sharp from 'sharp'; -import * as jpeg from '@cvmts/jpegturbo-rs'; +import * as cvm from '@cvmts/cvm-rs'; // A good balance. TODO: Configurable? let gJpegQuality = 35; @@ -28,7 +28,7 @@ export class JPEGEncoder { static async Encode(canvas: Buffer, displaySize: Size, rect: Rect): Promise { let offset = (rect.y * displaySize.width + rect.x) * 4; - return jpeg.jpegEncode({ + return cvm.jpegEncode({ width: rect.width, height: rect.height, stride: displaySize.width, @@ -42,7 +42,7 @@ export class JPEGEncoder { .raw() .toBuffer({ resolveWithObject: true }); - return jpeg.jpegEncode({ + return cvm.jpegEncode({ width: kThumbnailSize.width, height: kThumbnailSize.height, stride: kThumbnailSize.width, diff --git a/cvmts/src/User.ts b/cvmts/src/User.ts index 05d26b4..a434c2f 100644 --- a/cvmts/src/User.ts +++ b/cvmts/src/User.ts @@ -1,5 +1,5 @@ import * as Utilities from './Utilities.js'; -import * as guac from '@cvmts/guac-rs'; +import * as cvm from '@cvmts/cvm-rs'; import { IPData } from './IPData.js'; import IConfig from './IConfig.js'; import RateLimiter from './RateLimiter.js'; @@ -95,7 +95,7 @@ export class User { } closeConnection() { - this.socket.send(guac.guacEncode('disconnect')); + this.socket.send(cvm.guacEncode('disconnect')); this.socket.close(); } @@ -115,7 +115,7 @@ export class User { mute(permanent: boolean) { this.IP.muted = true; - this.sendMsg(guac.guacEncode('chat', '', `You have been muted${permanent ? '' : ` for ${this.Config.collabvm.tempMuteTime} seconds`}.`)); + this.sendMsg(cvm.guacEncode('chat', '', `You have been muted${permanent ? '' : ` for ${this.Config.collabvm.tempMuteTime} seconds`}.`)); if (!permanent) { clearTimeout(this.IP.tempMuteExpireTimeout); this.IP.tempMuteExpireTimeout = setTimeout(() => this.unmute(), this.Config.collabvm.tempMuteTime * 1000); @@ -124,7 +124,7 @@ export class User { unmute() { clearTimeout(this.IP.tempMuteExpireTimeout); this.IP.muted = false; - this.sendMsg(guac.guacEncode('chat', '', 'You are no longer muted.')); + this.sendMsg(cvm.guacEncode('chat', '', 'You are no longer muted.')); } private banCmdArgs(arg: string): string { diff --git a/cvmts/src/WebSocket/WSClient.ts b/cvmts/src/WebSocket/WSClient.ts index cf5e2b0..bf7daa4 100644 --- a/cvmts/src/WebSocket/WSClient.ts +++ b/cvmts/src/WebSocket/WSClient.ts @@ -49,7 +49,14 @@ export default class WSClient extends EventEmitter implements NetworkClient { } close(): void { - this.socket.close(); + if(this.isOpen()) { + // While this seems counterintutive, do note that the WebSocket protocol + // *sends* a data frame whilist closing a connection. Therefore, if the other end + // has forcibly hung up (closed) their connection, the best way to handle that + // is to just let the inner TCP socket propegate that, which `ws` will do for us. + // Otherwise, we'll try to send data to a closed client then SIGPIPE. + this.socket.close(); + } } } diff --git a/guac-rs/Cargo.lock b/guac-rs/Cargo.lock deleted file mode 100644 index 8f6bdab..0000000 --- a/guac-rs/Cargo.lock +++ /dev/null @@ -1,209 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "guac-rs" -version = "0.1.0" -dependencies = [ - "neon", -] - -[[package]] -name = "libc" -version = "0.2.155" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" - -[[package]] -name = "libloading" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" -dependencies = [ - "cfg-if", - "windows-targets", -] - -[[package]] -name = "neon" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d75440242411c87dc39847b0e33e961ec1f10326a9d8ecf9c1ea64a3b3c13dc" -dependencies = [ - "getrandom", - "libloading", - "neon-macros", - "once_cell", - "semver", - "send_wrapper", - "smallvec", -] - -[[package]] -name = "neon-macros" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6813fde79b646e47e7ad75f480aa80ef76a5d9599e2717407961531169ee38b" -dependencies = [ - "quote", - "syn", - "syn-mid", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "proc-macro2" -version = "1.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "syn" -version = "2.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn-mid" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5dc35bb08dd1ca3dfb09dce91fd2d13294d6711c88897d9a9d60acf39bce049" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "windows-targets" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/guac-rs/Cargo.toml b/guac-rs/Cargo.toml deleted file mode 100644 index 7fec5d6..0000000 --- a/guac-rs/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "guac-rs" -description = "Rust guacamole decoding :)" -version = "0.1.0" -edition = "2021" -exclude = ["index.node"] - -[lib] -crate-type = ["cdylib"] - -[dependencies] -neon = "1" diff --git a/guac-rs/index.d.ts b/guac-rs/index.d.ts deleted file mode 100644 index 8edbc8e..0000000 --- a/guac-rs/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ - -export function guacDecode(input: string): string[]; -export function guacEncode(...items: string[]): string; diff --git a/guac-rs/index.js b/guac-rs/index.js deleted file mode 100644 index 3d87ceb..0000000 --- a/guac-rs/index.js +++ /dev/null @@ -1,6 +0,0 @@ -// *sigh* -import { createRequire } from 'module'; -const require = createRequire(import.meta.url); - -export let {guacDecode, guacEncode} = require('./index.node'); - diff --git a/jpegturbo-rs/index.d.ts b/jpegturbo-rs/index.d.ts deleted file mode 100644 index 9c653e8..0000000 --- a/jpegturbo-rs/index.d.ts +++ /dev/null @@ -1,15 +0,0 @@ - -interface JpegInputArgs { - width: number, - height: number, - stride: number, // The width of your input framebuffer OR your image width (if encoding a full image) - buffer: Buffer - - // TODO: Allow different formats, or export a boxed ffi object which can store a format - // (i.e: new JpegEncoder(FORMAT_xxx)). -} - -/// Performs JPEG encoding. -export function jpegEncode(input: JpegInputArgs) : Promise; - -// TODO: Version that can downscale? diff --git a/jpegturbo-rs/package.json b/jpegturbo-rs/package.json deleted file mode 100644 index 58be5d6..0000000 --- a/jpegturbo-rs/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "@cvmts/jpegturbo-rs", - "version": "0.1.0", - "packageManager": "yarn@4.1.1", - "type": "module", - "main": "index.js", - "types": "index.d.ts", - "scripts": { - "build": "cargo-cp-artifact -nc index.node -- cargo build --release --message-format=json-render-diagnostics", - "install": "yarn build", - "test": "cargo test" - }, - "devDependencies": { - "cargo-cp-artifact": "^0.1" - } -} diff --git a/package.json b/package.json index 82387f8..76cfb71 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,7 @@ "name": "cvmts-repo", "workspaces": [ "shared", - "guac-rs", - "jpegturbo-rs", + "cvm-rs", "nodejs-rfb", "qemu", "cvmts" @@ -19,7 +18,7 @@ }, "packageManager": "yarn@4.1.1", "scripts": { - "build": "yarn && cd nodejs-rfb && yarn && yarn build && cd ../shared && yarn build && cd ../qemu && yarn build && cd ../cvmts && yarn build", + "build": "just", "serve": "node cvmts/dist/index.js" } } diff --git a/yarn.lock b/yarn.lock index 2c8b671..6e41293 100644 --- a/yarn.lock +++ b/yarn.lock @@ -47,12 +47,19 @@ __metadata: languageName: unknown linkType: soft +"@cvmts/cvm-rs@npm:*, @cvmts/cvm-rs@workspace:cvm-rs": + version: 0.0.0-use.local + resolution: "@cvmts/cvm-rs@workspace:cvm-rs" + dependencies: + cargo-cp-artifact: "npm:^0.1" + languageName: unknown + linkType: soft + "@cvmts/cvmts@workspace:cvmts": version: 0.0.0-use.local resolution: "@cvmts/cvmts@workspace:cvmts" dependencies: - "@cvmts/guac-rs": "npm:*" - "@cvmts/jpegturbo-rs": "npm:*" + "@cvmts/cvm-rs": "npm:*" "@cvmts/qemu": "npm:*" "@types/node": "npm:^20.12.5" "@types/ws": "npm:^8.5.5" @@ -66,22 +73,6 @@ __metadata: languageName: unknown linkType: soft -"@cvmts/guac-rs@npm:*, @cvmts/guac-rs@workspace:guac-rs": - version: 0.0.0-use.local - resolution: "@cvmts/guac-rs@workspace:guac-rs" - dependencies: - cargo-cp-artifact: "npm:^0.1" - languageName: unknown - linkType: soft - -"@cvmts/jpegturbo-rs@npm:*, @cvmts/jpegturbo-rs@workspace:jpegturbo-rs": - version: 0.0.0-use.local - resolution: "@cvmts/jpegturbo-rs@workspace:jpegturbo-rs" - dependencies: - cargo-cp-artifact: "npm:^0.1" - languageName: unknown - linkType: soft - "@cvmts/qemu@npm:*, @cvmts/qemu@workspace:qemu": version: 0.0.0-use.local resolution: "@cvmts/qemu@workspace:qemu"