Merge branch 'computernewb:master' into patch-1

This commit is contained in:
sporb 2023-01-26 20:25:46 -05:00 committed by GitHub
commit 6b563d6d4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 208 additions and 44 deletions

7
dist/index.html vendored
View file

@ -55,6 +55,13 @@
<button class="btn btn-secondary" id="changeUsernameBtn">Change Username</button>
<button class="btn btn-secondary" id="voteResetButton">Vote for Reset</button>
<button class="btn btn-secondary" id="screenshotButton">Screenshot</button>
<div id="staffbtns">
<button class="btn btn-secondary" id="restoreBtn">Restore</button>
<button class="btn btn-secondary" id="rebootBtn">Reboot</button>
<button class="btn btn-secondary" id="clearQueueBtn">Clear Turn Queue</button>
<button class="btn btn-secondary" id="bypassTurnBtn">Bypass Turn</button>
<button class="btn btn-secondary" id="endTurnBtn">End Turn</button>
</div>
</div>
<div class="row">
<div class="col-md-4">

26
dist/style.css vendored
View file

@ -19,6 +19,7 @@
display: block;
margin-bottom: 10px;
}
#vmlist > div.row > div {
padding-bottom: 10px;
}
@ -26,9 +27,11 @@
cursor: pointer;
border-color: rgb(8, 121, 250);
}
.vmtile > img {
margin-bottom: 2px;
}*/
}
.chat-table, .username-table {
overflow-y: auto;
border: 1px solid #575757;
@ -36,6 +39,7 @@
.chat-table {
height: 30vh;
}
.username-table {
max-height: 30vh;
}
@ -43,9 +47,29 @@
position: sticky;
top: 0;
}
#turnstatus {
text-align: center;
}
#voteResetPanel {
text-align: center;
}
.focused {
box-shadow: 0 0 9px 0 rgba(45,213,255,.75);
-moz-box-shadow: 0 0 9px 0 rgba(45,213,255,.75);
-webkit-box-shadow: 0 0 9px 0 rgba(45,213,255,.75)
}
.waiting {
box-shadow: 0 0 9px 0 rgba(242,255,63,.75);
-moz-box-shadow: 0 0 9px 0 rgba(242,255,63,.75);
-webkit-box-shadow: 0 0 9px 0 rgba(242,255,63,.75)
}
#staffbtns {
display: none;
}
#staffbtns > button {
display: none;
}

View file

@ -9,6 +9,7 @@
"author": "Elijah R",
"license": "GPL-3.0",
"dependencies": {
"nanoevents": "^7.0.1",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1"
}

View file

@ -1,11 +1,14 @@
import { guacutils } from "./protocol";
import { config } from "./common";
import { GetKeysym } from "./keyboard";
import { createNanoEvents } from "nanoevents";
import { makeperms } from "./permissions";
// None = -1
// Has turn = 0
// In queue = <queue position>
var turn = -1;
var perms = 0;
var perms = makeperms(0);
var rank = 0;
var connected = false;
const vms = [];
const users = [];
@ -13,7 +16,13 @@ const buttons = {
takeTurn: window.document.getElementById("takeTurnBtn"),
changeUsername: window.document.getElementById("changeUsernameBtn"),
voteReset: window.document.getElementById("voteResetButton"),
screenshot: window.document.getElementById("screenshotButton")
screenshot: window.document.getElementById("screenshotButton"),
// Staff
restore: window.document.getElementById("restoreBtn"),
reboot: window.document.getElementById("rebootBtn"),
clearQueue: window.document.getElementById("clearQueueBtn"),
bypassTurn: window.document.getElementById("bypassTurnBtn"),
endTurn: window.document.getElementById("endTurnBtn"),
}
var hasTurn = false;
var vm;
@ -37,11 +46,14 @@ const votenobtn = document.getElementById("voteNoBtn");
const voteyeslabel = document.getElementById("voteYesLabel");
const votenolabel = document.getElementById("voteNoLabel");
const votetime = document.getElementById("votetime");
const staffbtns = document.getElementById("staffbtns");
// needed to scroll to bottom
const chatListDiv = document.querySelector(".chat-table");
class CollabVMClient {
eventemitter = createNanoEvents();
socket;
node;
#url;
constructor(url) {
this.#url = url;
@ -63,26 +75,23 @@ class CollabVMClient {
}
connectToVM(node) {
return new Promise((res, rej) => {
this.node = node;
var savedUsername = window.localStorage.getItem("username");
if (savedUsername === null)
this.socket.send(guacutils.encode(["rename"]));
else this.socket.send(guacutils.encode(["rename", savedUsername]));
var f = (e) => {
var msgArr = guacutils.decode(e.data);
if (msgArr[0] == "connect") {
switch (msgArr[1]) {
case "0":
rej("Failed to connect to the node");
break;
case "1":
var unbind = this.eventemitter.on('connect', () => {
unbind();
res();
break;
}
this.socket.removeEventListener("message", f);
}
}
this.socket.addEventListener("message", f);
});
var failunbind = this.eventemitter.on('connectfail', () => {
failunbind();
rej();
});
this.socket.send(guacutils.encode(["connect", node]));
var pass = window.localStorage.getItem("password_"+this.#url);
if (pass)
this.admin.login(pass);
});
}
async #onMessage(event) {
@ -91,6 +100,16 @@ class CollabVMClient {
case "nop":
this.socket.send("3.nop;");
break;
case "connect":
switch (msgArr[1]) {
case "0":
this.eventemitter.emit('connectfail');
break;
case "1":
this.eventemitter.emit('connect');
break;
}
break;
case "chat":
if (!connected) return;
for (var i = 1; i < msgArr.length; i += 2) {
@ -99,6 +118,19 @@ class CollabVMClient {
chatsound.play();
chatListDiv.scrollTop = chatListDiv.scrollHeight;
break;
case "list":
var list = [];
for (var i = 1; i < msgArr.length; i+=3) {
list.push({
url: this.#url,
id: msgArr[i],
name: msgArr[i+1],
thumb: msgArr[i+2],
});
}
this.eventemitter.emit('list', list);
break;
case "size":
if (!connected || msgArr[1] !== "0") return;
display.width = msgArr[2];
@ -171,6 +203,7 @@ class CollabVMClient {
buttons.takeTurn.innerText = "Take Turn";
turn = -1;
turnstatus.innerText = "";
display.className = "";
// Get the number of users queued for a turn
var queuedUsers = Number(msgArr[2]);
if (queuedUsers === 0) return;
@ -182,6 +215,7 @@ class CollabVMClient {
if (currentTurnUsername === window.username) {
turn = 0;
turnstatus.innerText = "You have the turn.";
display.className = "focused";
}
// Highlight all waiting users and set their status
if (queuedUsers > 1) {
@ -189,6 +223,7 @@ class CollabVMClient {
if (window.username === msgArr[i+3]) {
turn = i;
turnstatus.innerText = "Waiting for turn";
display.className = "waiting";
};
var user = users.find(u => u.username === msgArr[i+3]);
user.turn = i;
@ -234,6 +269,50 @@ class CollabVMClient {
break;
}
break;
case "admin":
switch (msgArr[1]) {
case "0":
// Login
switch (msgArr[2]) {
case "0":
this.eventemitter.emit('login', {error: 'badpassword'});
return;
break;
case "1":
perms = makeperms(65535);
rank = 2;
break;
case "3":
rank = 3;
perms = makeperms(parseInt(msgArr[3]))
}
this.eventemitter.emit('login', {perms: perms, rank: rank});
usernameSpan.classList.remove("text-light");
switch (rank) {
case 2:
usernameSpan.classList.add("text-danger");
break;
case 3:
usernameSpan.classList.add("text-success");
break;
}
// Disabled for now until we figure out the issue of uservm
//window.localStorage.setItem("password_"+this.#url, password);
staffbtns.style.display = "block";
if (perms.restore) buttons.restore.style.display = "inline-block";
if (perms.reboot) buttons.reboot.style.display = "inline-block";
if (perms.bypassturn) {
buttons.bypassTurn.style.display = "inline-block";
buttons.clearQueue.style.display = "inline-block";
buttons.endTurn.style.display = "inline-block";
}
break;
}
break;
default:
window.cvmEvents.emit(msgArr[0], msgArr.slice(1));
break;
}
}
reloadUsers() {
@ -252,24 +331,10 @@ class CollabVMClient {
}
async list() {
return new Promise((res, rej) => {
var h = (e) => {
var msgArr = guacutils.decode(e.data);
if (msgArr[0] === "list") {
var list = [];
for (var i = 1; i < msgArr.length; i+=3) {
list.push({
url: this.#url,
id: msgArr[i],
name: msgArr[i+1],
thumb: msgArr[i+2],
});
}
this.socket.removeEventListener("message", h);
res(list);
}
};
this.socket.addEventListener("message", h);
var unbind = this.eventemitter.on('list', (e) => {
unbind();
res(e);
})
this.socket.send("4.list;");
});
}
@ -309,6 +374,28 @@ class CollabVMClient {
voteReset(reset) {
this.socket.send(guacutils.encode(["vote", reset ? "1" : "0"]));
}
admin = {
login: (password) => {
return new Promise((res, rej) => {
var unbind = this.eventemitter.on('login', (args) => {
unbind();
if (args.error) rej(error);
res(args);
})
this.socket.send(guacutils.encode(["admin", "2", password]));
});
},
adminInstruction: (...args) => { // Compatibility
args.unshift("admin");
console.log(args);
this.socket.send(guacutils.encode(args));
},
restore: () => this.socket.send(guacutils.encode(["admin", "8", this.node])),
reboot: () => this.socket.send(guacutils.encode(["admin", "10", this.node])),
clearQueue: () => this.socket.send(guacutils.encode(["admin", "17", this.node])),
bypassTurn: () => this.socket.send(guacutils.encode(["admin", "20"])),
endTurn: () => this.socket.send(guacutils.encode(["admin", "16", users[0].username])),
}
}
function multicollab(url) {
return new Promise(async (res, rej) => {
@ -364,15 +451,15 @@ async function openVM(url, node) {
await vm.connectToVM(node);
vmlist.style.display = "none";
vmview.style.display = "block";
display.addEventListener('mousemove', (e) => vm.mouseevent(e))
display.addEventListener('mousedown', (e) => vm.mouseevent(e));
display.addEventListener('mouseup', (e) => vm.mouseevent(e));
display.addEventListener('mousemove', (e) => vm.mouseevent(e), {capture: true})
display.addEventListener('mousedown', (e) => vm.mouseevent(e), {capture: true});
display.addEventListener('mouseup', (e) => vm.mouseevent(e), {capture: true});
display.addEventListener('contextmenu', (e) => e.preventDefault());
display.addEventListener('click', () => {
if (turn === -1) vm.turn();
});
display.addEventListener('keydown', (e) => vm.keyevent(e, true));
display.addEventListener('keyup', (e) => vm.keyevent(e, false));
}, {capture: true});
display.addEventListener('keydown', (e) => vm.keyevent(e, true), {capture: true});
display.addEventListener('keyup', (e) => vm.keyevent(e, false), {capture: true});
}
function screenshotVM() {
return new Promise((res, rej) => {
@ -406,9 +493,31 @@ buttons.takeTurn.addEventListener('click', () => vm.turn());
buttons.voteReset.addEventListener('click', () => vm.voteReset(true));
voteyesbtn.addEventListener('click', () => vm.voteReset(true));
votenobtn.addEventListener('click', () => vm.voteReset(false));
// Staff buttons
buttons.restore.addEventListener('click', () => vm.admin.restore());
buttons.reboot.addEventListener('click', () => vm.admin.reboot());
buttons.clearQueue.addEventListener('click', () => vm.admin.clearQueue());
buttons.bypassTurn.addEventListener('click', () => vm.admin.bypassTurn());
buttons.endTurn.addEventListener('click', () => vm.admin.endTurn());
// Login
var usernameClick = false;
usernameSpan.addEventListener('click', () => {
if (!usernameClick) {
usernameClick = true;
setInterval(() => {usernameClick = false;}, 1000);
return;
}
var pass = window.prompt("🔑");
if (!pass) return;
vm.admin.login(pass);
});
// Load all vms
config.serverAddresses.forEach(multicollab);
// Export some stuff
window.screenshotVM = screenshotVM;
window.multicollab = multicollab;
window.getPerms = () => perms;
window.getRank = () => rank;
window.GetAdmin = () => vm.admin;
window.cvmEvents = createNanoEvents();

23
src/permissions.js Normal file
View file

@ -0,0 +1,23 @@
export function makeperms(mask) {
const perms = {
restore: false,
reboot: false,
ban: false,
forcevote: false,
mute: false,
kick: false,
bypassturn: false,
rename: false,
grabip: false
};
if ((mask & 1) !== 0) perms.restore = true;
if ((mask & 2) !== 0) perms.reboot = true;
if ((mask & 4) !== 0) perms.ban = true;
if ((mask & 8) !== 0) perms.forcevote = true;
if ((mask & 16) !== 0) perms.mute = true;
if ((mask & 32) !== 0) perms.kick = true;
if ((mask & 64) !== 0) perms.bypassturn = true;
if ((mask & 128) !== 0) perms.rename = true;
if ((mask & 256) !== 0) perms.grabip = true;
return perms;
}