add turns and control

This commit is contained in:
Elijah R 2024-02-02 19:40:54 -05:00 committed by Elijah R
parent 288edf6b3a
commit b4559a3841
5 changed files with 136 additions and 9 deletions

3
src/js/keyboard.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
declare function GetKeysym(keyCode : number, key : string, location : number) : number | undefined;
export default GetKeysym;

View File

@ -3,13 +3,10 @@
// shitty but it works so /shrug
// THIS SUCKS SO BAD AND I HATE IT PLEASE REWRITE ALL OF THIS
export default function GetKeysym(keyCode, keyIdentifier, key, location) {
export default function GetKeysym(keyCode, key, location) {
var keysym = keysym_from_key_identifier(key, location)
|| keysym_from_keycode(keyCode, location);
if (!keysym && key_identifier_sane(keyCode, keyIdentifier))
keysym = keysym_from_key_identifier(keyIdentifier, location);
return keysym;
}

View File

@ -22,6 +22,7 @@ const elements = {
changeUsernameBtn: document.getElementById("changeUsernameBtn") as HTMLButtonElement,
turnBtnText: document.getElementById("turnBtnText") as HTMLSpanElement,
turnstatus: document.getElementById("turnstatus") as HTMLParagraphElement,
takeTurnBtn: document.getElementById("takeTurnBtn") as HTMLButtonElement,
}
var expectedClose = false;
var turn = -1;
@ -32,6 +33,8 @@ const users : {
user : User,
element : HTMLTableRowElement
}[] = [];
var turnInterval : number | undefined = undefined;;
var turnTimer = 0;
// Active VM
var VM : CollabVMClient | null = null;
@ -166,8 +169,8 @@ function sortVMList() {
function sortUserList() {
users.sort((a, b) => {
if (a.user.username === w.username && (a.user.turn >= b.user.turn)) return -1;
if (b.user.username === w.username && (b.user.turn >= a.user.turn)) return 1;
if (a.user.username === w.username && (a.user.turn >= b.user.turn) && b.user.turn !== 0) return -1;
if (b.user.username === w.username && (b.user.turn >= a.user.turn) && a.user.turn !== 0) return 1;
if (a.user.turn === b.user.turn) return 0;
if (a.user.turn === -1) return 1;
if (b.user.turn === -1) return -1;
@ -269,6 +272,9 @@ function userRenamed(oldname : string, newname : string, selfrename : boolean) {
function turnUpdate(status : TurnStatus) {
// Clear all turn data
turn = -1;
VM!.canvas.classList.remove("focused", "waiting");
clearInterval(turnInterval);
turnTimer = 0;
for (const user of users) {
user.element.classList.remove("user-turn", "user-waiting");
user.element.setAttribute("data-cvm-turn", "-1");
@ -286,15 +292,36 @@ function turnUpdate(status : TurnStatus) {
}
if (status.user?.username === w.username) {
turn = 0;
turnTimer = status.turnTime! / 1000;
elements.turnBtnText.innerHTML = "End Turn";
VM!.canvas.classList.add("focused");
}
if (status.queue.some(u => u.username === w.username)) {
turn = status.queue.findIndex(u => u.username === w.username) + 1;
turnTimer = status.queueTime! / 1000;
elements.turnBtnText.innerHTML = "End Turn";
VM!.canvas.classList.add("waiting");
}
if (turn === -1) elements.turnstatus.innerText = "";
else {
turnInterval = setInterval(() => turnIntervalCb(), 1000);
setTurnStatus();
}
sortUserList();
}
function turnIntervalCb() {
turnTimer--;
setTurnStatus();
}
function setTurnStatus() {
if (turn === 0)
elements.turnstatus.innerText = `Turn expires in ${turnTimer} seconds`;
else
elements.turnstatus.innerText = `Waiting for turn in ${turnTimer} seconds`;
}
function sendChat() {
if (VM === null) return;
VM.chat(elements.chatinput.value);
@ -313,7 +340,10 @@ elements.changeUsernameBtn.addEventListener('click', () => {
var newname = prompt("Enter new username, or leave blank to be assigned a guest username", w.username);
if (newname === w.username) return;
VM?.rename(newname);
})
});
elements.takeTurnBtn.addEventListener('click', () => {
VM?.turn(turn === -1);
});
// Public API
w.collabvm = {

View File

@ -4,6 +4,8 @@ import VM from "./VM.js";
import { User } from "./User.js";
import { Rank } from "./Permissions.js";
import TurnStatus from "./TurnStatus.js";
import Mouse from "./mouse.js";
import GetKeysym from '../../js/keyboard';
export default class CollabVMClient {
// Fields
@ -14,6 +16,8 @@ export default class CollabVMClient {
private connectedToVM : boolean = false;
private users : User[] = [];
private username : string | null = null;
private mouse : Mouse = new Mouse();
private rank : Rank = Rank.Unregistered;
// events that are used internally and not exposed
private emitter;
// public events
@ -31,6 +35,50 @@ export default class CollabVMClient {
this.canvas.tabIndex = -1;
// Get the 2D context
this.ctx = this.canvas.getContext('2d')!;
// Bind canvas click
this.canvas.addEventListener('click', e => {
if (this.users.find(u => u.username === this.username)?.turn === -1)
this.turn(true);
});
// Bind keyboard and mouse
this.canvas.addEventListener('mousedown', (e : MouseEvent) => {
if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return;
this.mouse.processEvent(e, true);
this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask());
}, {
capture: true
});
this.canvas.addEventListener('mouseup', (e : MouseEvent) => {
if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return;
this.mouse.processEvent(e, false);
this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask());
}, {
capture: true
});
this.canvas.addEventListener('mousemove', (e : MouseEvent) => {
if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return;
this.mouse.processEvent(e, null);
this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask());
}, {
capture: true
});
this.canvas.addEventListener('keydown', (e : KeyboardEvent) => {
if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return;
var keysym = GetKeysym(e.keyCode, e.key, e.location);
if (keysym === undefined) return;
this.key(keysym, true);
}, {
capture: true
});
this.canvas.addEventListener('keyup', (e : KeyboardEvent) => {
if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return;
var keysym = GetKeysym(e.keyCode, e.key, e.location);
if (keysym === undefined) return;
this.key(keysym, false);
}, {
capture: true
});
this.canvas.addEventListener('contextmenu', e => e.preventDefault());
// Create the WebSocket
this.socket = new WebSocket(url, "guacamole");
// Add the event listeners
@ -61,7 +109,6 @@ export default class CollabVMClient {
}
case "list": {
// pass msgarr to the emitter for processing by list()
console.log("got list")
this.emitter.emit('list', msgArr.slice(1));
break;
}
@ -194,7 +241,6 @@ export default class CollabVMClient {
displayName: list[i + 1],
thumbnail: th,
});
console.log("pushed", list[i]);
}
res(vms);
});
@ -238,5 +284,20 @@ export default class CollabVMClient {
else this.send("rename");
}
// Take or drop turn
turn(taketurn : boolean) {
this.send("turn", taketurn ? "1" : "0");
}
// Send mouse instruction
sendmouse(x : number, y : number, mask : number) {
this.send("mouse", x.toString(), y.toString(), mask.toString());
}
// Send key
key(keysym : number, down : boolean) {
this.send("key", keysym.toString(), down ? "1" : "0");
}
on = (event : string | number, cb: (...args: any) => void) => this.publicEmitter.on(event, cb);
}

36
src/ts/protocol/mouse.ts Normal file
View File

@ -0,0 +1,36 @@
export default class Mouse {
left : boolean = false;
middle : boolean = false;
right : boolean = false;
scrolldown : boolean = false;
scrollup : boolean = false;
x : number = 0;
y : number = 0;
constructor() {}
makeMask() {
var mask = 0;
if (this.left) mask |= 1;
if (this.middle) mask |= 2;
if (this.right) mask |= 4;
if (this.scrollup) mask |= 8;
if (this.scrolldown) mask |= 16;
return mask;
}
processEvent(e : MouseEvent, down : boolean | null = null) {
if (down !== null) switch (e.button) {
case 0:
this.left = down;
break;
case 1:
this.middle = down;
break;
case 2:
this.right = down;
break;
}
this.x = e.offsetX;
this.y = e.offsetY;
}
}