Add toggle for ghost turning

This commit is contained in:
Elijah R 2024-06-16 13:50:44 -04:00
parent 00da6db08d
commit f0c09b2e1b
6 changed files with 38 additions and 7 deletions

View File

@ -257,6 +257,7 @@
<button class="btn btn-secondary" id="bypassTurnBtn"><i class="fa-solid fa-forward"></i> <span id="bypassTurnBtnText"></span></button> <button class="btn btn-secondary" id="bypassTurnBtn"><i class="fa-solid fa-forward"></i> <span id="bypassTurnBtnText"></span></button>
<button class="btn btn-secondary" id="endTurnBtn"><i class="fa-solid fa-ban"></i> <span id="endTurnBtnText"></span></button> <button class="btn btn-secondary" id="endTurnBtn"><i class="fa-solid fa-ban"></i> <span id="endTurnBtnText"></span></button>
<button class="btn btn-secondary" id="indefTurnBtn"><i class="fa-solid fa-infinity"></i> <span id="indefTurnBtnText"></span></button> <button class="btn btn-secondary" id="indefTurnBtn"><i class="fa-solid fa-infinity"></i> <span id="indefTurnBtnText"></span></button>
<button class="btn btn-secondary" id="ghostTurnBtn"><i class="fa-solid fa-ghost"></i> <span id="ghostTurnBtnText"></span></button>
<button class="btn btn-secondary" id="qemuMonitorBtn" data-bs-toggle="modal" data-bs-target="#qemuMonitorModal"><i class="fa-solid fa-terminal"></i> <span id="qemuMonitorBtnText"></span></button> <button class="btn btn-secondary" id="qemuMonitorBtn" data-bs-toggle="modal" data-bs-target="#qemuMonitorModal"><i class="fa-solid fa-terminal"></i> <span id="qemuMonitorBtnText"></span></button>
</div> </div>
</div> </div>

View File

@ -61,6 +61,8 @@ const fallbackLanguage : Language = {
"kAdminVMButtons_ClearTurnQueue": "Clear Turn Queue", "kAdminVMButtons_ClearTurnQueue": "Clear Turn Queue",
"kAdminVMButtons_BypassTurn": "Bypass Turn", "kAdminVMButtons_BypassTurn": "Bypass Turn",
"kAdminVMButtons_IndefiniteTurn": "Indefinite Turn", "kAdminVMButtons_IndefiniteTurn": "Indefinite Turn",
"kAdminVMButtons_GhostTurnOn": "Ghost Turn (On)",
"kAdminVMButtons_GhostTurnOff": "Ghost Turn (Off)",
"kAdminVMButtons_Ban": "Ban", "kAdminVMButtons_Ban": "Ban",
"kAdminVMButtons_Kick": "Kick", "kAdminVMButtons_Kick": "Kick",

View File

@ -62,6 +62,8 @@ export enum I18nStringKey {
kAdminVMButtons_ClearTurnQueue = 'kAdminVMButtons_ClearTurnQueue', kAdminVMButtons_ClearTurnQueue = 'kAdminVMButtons_ClearTurnQueue',
kAdminVMButtons_BypassTurn = 'kAdminVMButtons_BypassTurn', kAdminVMButtons_BypassTurn = 'kAdminVMButtons_BypassTurn',
kAdminVMButtons_IndefiniteTurn = 'kAdminVMButtons_IndefiniteTurn', kAdminVMButtons_IndefiniteTurn = 'kAdminVMButtons_IndefiniteTurn',
kAdminVMButtons_GhostTurnOn = 'kAdminVMButtons_GhostTurnOn',
kAdminVMButtons_GhostTurnOff = 'kAdminVMButtons_GhostTurnOff',
kAdminVMButtons_Ban = 'kAdminVMButtons_Ban', kAdminVMButtons_Ban = 'kAdminVMButtons_Ban',
kAdminVMButtons_Kick = 'kAdminVMButtons_Kick', kAdminVMButtons_Kick = 'kAdminVMButtons_Kick',
@ -272,6 +274,7 @@ export class I18n {
clearQueueBtnText: I18nStringKey.kAdminVMButtons_ClearTurnQueue, clearQueueBtnText: I18nStringKey.kAdminVMButtons_ClearTurnQueue,
bypassTurnBtnText: I18nStringKey.kAdminVMButtons_BypassTurn, bypassTurnBtnText: I18nStringKey.kAdminVMButtons_BypassTurn,
indefTurnBtnText: I18nStringKey.kAdminVMButtons_IndefiniteTurn, indefTurnBtnText: I18nStringKey.kAdminVMButtons_IndefiniteTurn,
ghostTurnBtnText: I18nStringKey.kAdminVMButtons_GhostTurnOff,
// Account modal // Account modal
accountLoginUsernameLabel: I18nStringKey.kGeneric_Username, accountLoginUsernameLabel: I18nStringKey.kGeneric_Username,

View File

@ -70,6 +70,8 @@ const elements = {
forceVoteYesBtn: document.getElementById('forceVoteYesBtn') as HTMLButtonElement, forceVoteYesBtn: document.getElementById('forceVoteYesBtn') as HTMLButtonElement,
forceVoteNoBtn: document.getElementById('forceVoteNoBtn') as HTMLButtonElement, forceVoteNoBtn: document.getElementById('forceVoteNoBtn') as HTMLButtonElement,
indefTurnBtn: document.getElementById('indefTurnBtn') as HTMLButtonElement, indefTurnBtn: document.getElementById('indefTurnBtn') as HTMLButtonElement,
ghostTurnBtn: document.getElementById('ghostTurnBtn') as HTMLButtonElement,
ghostTurnBtnText: document.getElementById('ghostTurnBtnText') as HTMLSpanElement,
qemuMonitorInput: document.getElementById('qemuMonitorInput') as HTMLInputElement, qemuMonitorInput: document.getElementById('qemuMonitorInput') as HTMLInputElement,
qemuMonitorSendBtn: document.getElementById('qemuMonitorSendBtn') as HTMLButtonElement, qemuMonitorSendBtn: document.getElementById('qemuMonitorSendBtn') as HTMLButtonElement,
qemuMonitorOutput: document.getElementById('qemuMonitorOutput') as HTMLTextAreaElement, qemuMonitorOutput: document.getElementById('qemuMonitorOutput') as HTMLTextAreaElement,
@ -482,6 +484,7 @@ function closeVM() {
elements.clearQueueBtn.style.display = 'none'; elements.clearQueueBtn.style.display = 'none';
elements.qemuMonitorBtn.style.display = 'none'; elements.qemuMonitorBtn.style.display = 'none';
elements.indefTurnBtn.style.display = 'none'; elements.indefTurnBtn.style.display = 'none';
elements.ghostTurnBtn.style.display = 'none';
elements.xssCheckboxContainer.style.display = 'none'; elements.xssCheckboxContainer.style.display = 'none';
elements.forceVotePanel.style.display = 'none'; elements.forceVotePanel.style.display = 'none';
elements.voteResetPanel.style.display = 'none'; elements.voteResetPanel.style.display = 'none';
@ -813,6 +816,7 @@ function onLogin(_rank: Rank, _perms: Permissions) {
if (_rank === Rank.Admin) { if (_rank === Rank.Admin) {
elements.qemuMonitorBtn.style.display = 'inline-block'; elements.qemuMonitorBtn.style.display = 'inline-block';
elements.indefTurnBtn.style.display = 'inline-block'; elements.indefTurnBtn.style.display = 'inline-block';
elements.ghostTurnBtn.style.display = 'inline-block';
} }
if (_perms.xss) elements.xssCheckboxContainer.style.display = 'inline-block'; if (_perms.xss) elements.xssCheckboxContainer.style.display = 'inline-block';
if (_perms.forcevote) elements.forceVotePanel.style.display = 'block'; if (_perms.forcevote) elements.forceVotePanel.style.display = 'block';
@ -876,6 +880,15 @@ elements.forceVoteNoBtn.addEventListener('click', () => VM?.forceVote(false));
elements.forceVoteYesBtn.addEventListener('click', () => VM?.forceVote(true)); elements.forceVoteYesBtn.addEventListener('click', () => VM?.forceVote(true));
elements.indefTurnBtn.addEventListener('click', () => VM?.indefiniteTurn()); elements.indefTurnBtn.addEventListener('click', () => VM?.indefiniteTurn());
elements.ghostTurnBtn.addEventListener('click', () => {
w.collabvm.ghostTurn = !w.collabvm.ghostTurn;
if (w.collabvm.ghostTurn)
elements.ghostTurnBtnText.innerText = TheI18n.GetString(I18nStringKey.kAdminVMButtons_GhostTurnOn);
else
elements.ghostTurnBtnText.innerText = TheI18n.GetString(I18nStringKey.kAdminVMButtons_GhostTurnOff);
});
async function sendQEMUCommand() { async function sendQEMUCommand() {
if (!elements.qemuMonitorInput.value) return; if (!elements.qemuMonitorInput.value) return;
let cmd = elements.qemuMonitorInput.value; let cmd = elements.qemuMonitorInput.value;
@ -1212,7 +1225,8 @@ w.collabvm = {
closeVM: closeVM, closeVM: closeVM,
loadList: loadList, loadList: loadList,
multicollab: multicollab, multicollab: multicollab,
getVM: () => VM getVM: () => VM,
ghostTurn: false,
}; };
// Multicollab will stay in the global scope for backwards compatibility // Multicollab will stay in the global scope for backwards compatibility
w.multicollab = multicollab; w.multicollab = multicollab;
@ -1276,6 +1290,10 @@ document.addEventListener('DOMContentLoaded', async () => {
if (darkTheme) elements.toggleThemeBtnText.innerHTML = TheI18n.GetString(I18nStringKey.kSiteButtons_LightMode); if (darkTheme) elements.toggleThemeBtnText.innerHTML = TheI18n.GetString(I18nStringKey.kSiteButtons_LightMode);
else elements.toggleThemeBtnText.innerHTML = TheI18n.GetString(I18nStringKey.kSiteButtons_DarkMode); else elements.toggleThemeBtnText.innerHTML = TheI18n.GetString(I18nStringKey.kSiteButtons_DarkMode);
if (w.collabvm.ghostTurn)
elements.ghostTurnBtnText.innerText = TheI18n.GetString(I18nStringKey.kAdminVMButtons_GhostTurnOn);
else
elements.ghostTurnBtnText.innerText = TheI18n.GetString(I18nStringKey.kAdminVMButtons_GhostTurnOff);
}); });
// Load theme // Load theme
var _darktheme : boolean; var _darktheme : boolean;

View File

@ -9,6 +9,7 @@ import GetKeysym from '../keyboard.js';
import VoteStatus from './VoteStatus.js'; import VoteStatus from './VoteStatus.js';
import MuteState from './MuteState.js'; import MuteState from './MuteState.js';
import { StringLike } from '../StringLike.js'; import { StringLike } from '../StringLike.js';
const w = window as any;
export interface CollabVMClientEvents { export interface CollabVMClientEvents {
//open: () => void; //open: () => void;
@ -98,7 +99,7 @@ export default class CollabVMClient {
this.canvas.addEventListener( this.canvas.addEventListener(
'mousedown', 'mousedown',
(e: MouseEvent) => { (e: MouseEvent) => {
if (this.users.find((u) => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; if (!this.shouldSendInput()) return;
this.mouse.initFromMouseEvent(e); this.mouse.initFromMouseEvent(e);
this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask()); this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask());
}, },
@ -110,7 +111,7 @@ export default class CollabVMClient {
this.canvas.addEventListener( this.canvas.addEventListener(
'mouseup', 'mouseup',
(e: MouseEvent) => { (e: MouseEvent) => {
if (this.users.find((u) => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; if (!this.shouldSendInput()) return;
this.mouse.initFromMouseEvent(e); this.mouse.initFromMouseEvent(e);
this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask()); this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask());
}, },
@ -122,7 +123,7 @@ export default class CollabVMClient {
this.canvas.addEventListener( this.canvas.addEventListener(
'mousemove', 'mousemove',
(e: MouseEvent) => { (e: MouseEvent) => {
if (this.users.find((u) => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; if (!this.shouldSendInput()) return;
this.mouse.initFromMouseEvent(e); this.mouse.initFromMouseEvent(e);
this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask()); this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask());
}, },
@ -135,7 +136,7 @@ export default class CollabVMClient {
'keydown', 'keydown',
(e: KeyboardEvent) => { (e: KeyboardEvent) => {
e.preventDefault(); e.preventDefault();
if (this.users.find((u) => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; if (!this.shouldSendInput()) return;
let keysym = GetKeysym(e.keyCode, e.key, e.location); let keysym = GetKeysym(e.keyCode, e.key, e.location);
if (keysym === null) return; if (keysym === null) return;
this.key(keysym, true); this.key(keysym, true);
@ -149,7 +150,7 @@ export default class CollabVMClient {
'keyup', 'keyup',
(e: KeyboardEvent) => { (e: KeyboardEvent) => {
e.preventDefault(); e.preventDefault();
if (this.users.find((u) => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; if (!this.shouldSendInput()) return;
let keysym = GetKeysym(e.keyCode, e.key, e.location); let keysym = GetKeysym(e.keyCode, e.key, e.location);
if (keysym === null) return; if (keysym === null) return;
this.key(keysym, false); this.key(keysym, false);
@ -163,7 +164,7 @@ export default class CollabVMClient {
'wheel', 'wheel',
(ev: WheelEvent) => { (ev: WheelEvent) => {
ev.preventDefault(); ev.preventDefault();
if (this.users.find((u) => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; if (!this.shouldSendInput()) return;
this.mouse.initFromWheelEvent(ev); this.mouse.initFromWheelEvent(ev);
this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask()); this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask());
@ -677,6 +678,10 @@ export default class CollabVMClient {
return this.internalEmitter.on(event, callback); return this.internalEmitter.on(event, callback);
} }
private shouldSendInput() {
return this.users.find(u => u.username === this.username)?.turn === 0 || (w.collabvm.ghostTurn && this.rank === Rank.Admin);
}
on<E extends keyof CollabVMClientEvents>(event: E, callback: CollabVMClientEvents[E]): Unsubscribe { on<E extends keyof CollabVMClientEvents>(event: E, callback: CollabVMClientEvents[E]): Unsubscribe {
let unsub = this.publicEmitter.on(event, callback); let unsub = this.publicEmitter.on(event, callback);
this.unsubscribeCallbacks.push(unsub); this.unsubscribeCallbacks.push(unsub);

View File

@ -60,6 +60,8 @@
"kAdminVMButtons_ClearTurnQueue": "Clear Turn Queue", "kAdminVMButtons_ClearTurnQueue": "Clear Turn Queue",
"kAdminVMButtons_BypassTurn": "Bypass Turn", "kAdminVMButtons_BypassTurn": "Bypass Turn",
"kAdminVMButtons_IndefiniteTurn": "Indefinite Turn", "kAdminVMButtons_IndefiniteTurn": "Indefinite Turn",
"kAdminVMButtons_GhostTurnOn": "Ghost Turn (On)",
"kAdminVMButtons_GhostTurnOff": "Ghost Turn (Off)",
"kAdminVMButtons_Ban": "Ban", "kAdminVMButtons_Ban": "Ban",
"kAdminVMButtons_Kick": "Kick", "kAdminVMButtons_Kick": "Kick",